diff --git a/.github/workflows/.net-build-microservices.yml b/.github/workflows/.net-build-microservices.yml index dc1092fd5..a5293a3c1 100644 --- a/.github/workflows/.net-build-microservices.yml +++ b/.github/workflows/.net-build-microservices.yml @@ -36,9 +36,6 @@ jobs: - name: Publish Identity.Api run: dotnet publish Services.Identity/ClassifiedAds.Services.Identity.Api/*.csproj --configuration Release - - name: Publish Identity.AuthServer - run: dotnet publish Services.Identity/ClassifiedAds.Services.Identity.AuthServer/*.csproj --configuration Release - - name: Publish Identity.Grpc run: dotnet publish Services.Identity/ClassifiedAds.Services.Identity.Grpc/*.csproj --configuration Release @@ -78,12 +75,6 @@ jobs: name: ClassifiedAds.Services.Identity.Api path: src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.Api/bin/Release/net6.0/publish - - name: Upload ClassifiedAds.Services.Identity.AuthServer - uses: actions/upload-artifact@v1.0.0 - with: - name: ClassifiedAds.Services.Identity.AuthServer - path: src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/bin/Release/net6.0/publish - - name: Upload ClassifiedAds.Services.Identity.Grpc uses: actions/upload-artifact@v1.0.0 with: diff --git a/.github/workflows/.net-build-modularmonolith.yml b/.github/workflows/.net-build-modularmonolith.yml index 97a599391..ac67aabab 100644 --- a/.github/workflows/.net-build-modularmonolith.yml +++ b/.github/workflows/.net-build-modularmonolith.yml @@ -30,9 +30,6 @@ jobs: - name: Publish ClassifiedAds.BackgroundServer run: dotnet publish ClassifiedAds.BackgroundServer/*.csproj --configuration Release - - name: Publish ClassifiedAds.IdentityServer - run: dotnet publish ClassifiedAds.IdentityServer/*.csproj --configuration Release - - name: Publish ClassifiedAds.Migrator run: dotnet publish ClassifiedAds.Migrator/*.csproj --configuration Release @@ -45,12 +42,6 @@ jobs: name: ClassifiedAds.BackgroundServer path: src/ModularMonolith/ClassifiedAds.BackgroundServer/bin/Release/net6.0/publish - - name: Upload ClassifiedAds.IdentityServer - uses: actions/upload-artifact@v1.0.0 - with: - name: ClassifiedAds.IdentityServer - path: src/ModularMonolith/ClassifiedAds.IdentityServer/bin/Release/net6.0/publish - - name: Upload ClassifiedAds.Migrator uses: actions/upload-artifact@v1.0.0 with: diff --git a/.github/workflows/.net-build-monolith.yml b/.github/workflows/.net-build-monolith.yml index 069457320..0b5155e3b 100644 --- a/.github/workflows/.net-build-monolith.yml +++ b/.github/workflows/.net-build-monolith.yml @@ -39,9 +39,6 @@ jobs: - name: Publish ClassifiedAds.WebAPI run: dotnet publish ClassifiedAds.WebAPI/*.csproj --configuration Release - - name: Publish ClassifiedAds.IdentityServer - run: dotnet publish ClassifiedAds.IdentityServer/*.csproj --configuration Release - - name: Publish ClassifiedAds.WebMVC run: dotnet publish ClassifiedAds.WebMVC/*.csproj --configuration Release @@ -74,12 +71,6 @@ jobs: with: name: ClassifiedAds.WebAPI path: src/Monolith/ClassifiedAds.WebAPI/bin/Release/net6.0/publish - - - name: Upload ClassifiedAds.IdentityServer - uses: actions/upload-artifact@v1.0.0 - with: - name: ClassifiedAds.IdentityServer - path: src/Monolith/ClassifiedAds.IdentityServer/bin/Release/net6.0/publish - name: Upload ClassifiedAds.WebMVC uses: actions/upload-artifact@v1.0.0 diff --git a/.gitignore b/.gitignore index e8a33ba34..6d1f4d5c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ ## Ignore the Migrations folder for now since this repo is for learning and testing purposes ## We don't want to mess things up with lot of migration files anytime we make changes to the database structure. -/src/Monolith/ClassifiedAds.Persistence/Migrations/ +/src/IdentityServer/ClassifiedAds.Migrator/Migrations/ /src/Monolith/ClassifiedAds.Migrator/Migrations/ /src/ModularMonolith/ClassifiedAds.Migrator/Migrations/ /src/Microservices/Services.AuditLog/ClassifiedAds.Services.AuditLog.Api/Migrations/ diff --git a/src/Monolith/ClassifiedAds.IdentityServer/.editorconfig b/src/IdentityServer/ClassifiedAds.Application/.editorconfig similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/.editorconfig rename to src/IdentityServer/ClassifiedAds.Application/.editorconfig diff --git a/src/IdentityServer/ClassifiedAds.Application/ApplicationServicesExtensions.cs b/src/IdentityServer/ClassifiedAds.Application/ApplicationServicesExtensions.cs new file mode 100644 index 000000000..be9ee20cf --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/ApplicationServicesExtensions.cs @@ -0,0 +1,97 @@ +using ClassifiedAds.Application; +using ClassifiedAds.Application.EmailMessages.Services; +using ClassifiedAds.Application.EventLogs; +using ClassifiedAds.Application.Products.Services; +using ClassifiedAds.Application.SmsMessages.Services; +using ClassifiedAds.Application.Users.Services; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using System; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ApplicationServicesExtensions + { + public static IServiceCollection AddApplicationServices(this IServiceCollection services, Action configureInterceptor = null) + { + DomainEvents.RegisterHandlers(Assembly.GetExecutingAssembly(), services); + + services + .AddScoped() + .AddScoped(typeof(ICrudService<>), typeof(CrudService<>)) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + + if (configureInterceptor != null) + { + var aggregateRootTypes = typeof(IAggregateRoot).Assembly.GetTypes().Where(x => x.BaseType == typeof(Entity) && x.GetInterfaces().Contains(typeof(IAggregateRoot))).ToList(); + foreach (var type in aggregateRootTypes) + { + configureInterceptor(typeof(ICrudService<>).MakeGenericType(type), typeof(CrudService<>).MakeGenericType(type), ServiceLifetime.Scoped); + } + + configureInterceptor(typeof(IUserService), typeof(UserService), ServiceLifetime.Scoped); + configureInterceptor(typeof(IProductService), typeof(ProductService), ServiceLifetime.Scoped); + } + + return services; + } + + public static IServiceCollection AddMessageHandlers(this IServiceCollection services) + { + services.AddScoped(); + + var assemblyTypes = Assembly.GetExecutingAssembly().GetTypes(); + + foreach (var type in assemblyTypes) + { + var handlerInterfaces = type.GetInterfaces() + .Where(Utils.IsHandlerInterface) + .ToList(); + + if (!handlerInterfaces.Any()) + { + continue; + } + + var handlerFactory = new HandlerFactory(type); + foreach (var interfaceType in handlerInterfaces) + { + services.AddTransient(interfaceType, provider => handlerFactory.Create(provider, interfaceType)); + } + } + + var aggregateRootTypes = typeof(IAggregateRoot).Assembly.GetTypes().Where(x => x.BaseType == typeof(Entity) && x.GetInterfaces().Contains(typeof(IAggregateRoot))).ToList(); + + var genericHandlerTypes = new[] + { + typeof(GetEntititesQueryHandler<>), + typeof(GetEntityByIdQueryHandler<>), + typeof(AddOrUpdateEntityCommandHandler<>), + typeof(DeleteEntityCommandHandler<>), + }; + + foreach (var aggregateRootType in aggregateRootTypes) + { + foreach (var genericHandlerType in genericHandlerTypes) + { + var handlerType = genericHandlerType.MakeGenericType(aggregateRootType); + var handlerInterfaces = handlerType.GetInterfaces(); + + var handlerFactory = new HandlerFactory(handlerType); + foreach (var interfaceType in handlerInterfaces) + { + services.AddTransient(interfaceType, provider => handlerFactory.Create(provider, interfaceType)); + } + } + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/DTOs/AuditLogEntryDTO.cs b/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/DTOs/AuditLogEntryDTO.cs new file mode 100644 index 000000000..45cd53d53 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/DTOs/AuditLogEntryDTO.cs @@ -0,0 +1,21 @@ +using System; + +namespace ClassifiedAds.Application.AuditLogEntries.DTOs +{ + public class AuditLogEntryDTO + { + public Guid Id { get; set; } + + public Guid UserId { get; set; } + + public string UserName { get; set; } + + public string Action { get; set; } + + public string ObjectId { get; set; } + + public string Log { get; set; } + + public DateTimeOffset CreatedDateTime { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/Queries/GetAuditEntriesQuery.cs b/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/Queries/GetAuditEntriesQuery.cs new file mode 100644 index 000000000..53f62e507 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/Queries/GetAuditEntriesQuery.cs @@ -0,0 +1,45 @@ +using ClassifiedAds.Application.AuditLogEntries.DTOs; +using ClassifiedAds.Domain.Repositories; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.AuditLogEntries.Queries +{ + public class GetAuditEntriesQuery : AuditLogEntryQueryOptions, IQuery> + { + } + + internal class GetAuditEntriesQueryHandler : IQueryHandler> + { + private readonly IAuditLogEntryRepository _auditLogEntryRepository; + private readonly IUserRepository _userRepository; + + public GetAuditEntriesQueryHandler(IAuditLogEntryRepository auditLogEntryRepository, IUserRepository userRepository) + { + _auditLogEntryRepository = auditLogEntryRepository; + _userRepository = userRepository; + } + + public async Task> HandleAsync(GetAuditEntriesQuery query, CancellationToken cancellationToken = default) + { + var auditLogs = _auditLogEntryRepository.Get(query); + var users = _userRepository.GetAll(); + + var rs = auditLogs.Join(users, x => x.UserId, y => y.Id, + (x, y) => new AuditLogEntryDTO + { + Id = x.Id, + UserId = x.UserId, + Action = x.Action, + ObjectId = x.ObjectId, + Log = x.Log, + CreatedDateTime = x.CreatedDateTime, + UserName = y.UserName, + }); + + return await _userRepository.ToListAsync(rs.OrderByDescending(x => x.CreatedDateTime)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/Queries/GetPagedAuditEntriesQuery.cs b/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/Queries/GetPagedAuditEntriesQuery.cs new file mode 100644 index 000000000..dd3d7baed --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/AuditLogEntries/Queries/GetPagedAuditEntriesQuery.cs @@ -0,0 +1,59 @@ +using ClassifiedAds.Application.AuditLogEntries.DTOs; +using ClassifiedAds.Application.Common.DTOs; +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Repositories; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.AuditLogEntries.Queries +{ + public class GetPagedAuditEntriesQuery : AuditLogEntryQueryOptions, IQuery> + { + public int Page { get; set; } + + public int PageSize { get; set; } + } + + internal class GetPagedAuditEntriesQueryHandler : IQueryHandler> + { + private readonly IAuditLogEntryRepository _auditLogEntryRepository; + private readonly IUserRepository _userRepository; + + public GetPagedAuditEntriesQueryHandler(IAuditLogEntryRepository auditLogEntryRepository, IUserRepository userRepository) + { + _auditLogEntryRepository = auditLogEntryRepository; + _userRepository = userRepository; + } + + public async Task> HandleAsync(GetPagedAuditEntriesQuery queryOptions, CancellationToken cancellationToken = default) + { + var query = _auditLogEntryRepository.Get(queryOptions); + var users = _userRepository.GetAll(); + + var result = new Paged + { + TotalItems = query.Count(), + }; + + var auditLogs = query.OrderByDescending(x => x.CreatedDateTime) + .Paged(queryOptions.Page, queryOptions.PageSize); + + var rs = auditLogs.Join(users, x => x.UserId, y => y.Id, + (x, y) => new AuditLogEntryDTO + { + Id = x.Id, + UserId = x.UserId, + Action = x.Action, + ObjectId = x.ObjectId, + Log = x.Log, + CreatedDateTime = x.CreatedDateTime, + UserName = y.UserName, + }); + + result.Items = await _userRepository.ToListAsync(rs); + + return result; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/ClassifiedAds.Application.csproj b/src/IdentityServer/ClassifiedAds.Application/ClassifiedAds.Application.csproj new file mode 100644 index 000000000..954c4bb8a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/ClassifiedAds.Application.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + Recommended + All + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Commands/AddEntityCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/AddEntityCommand.cs new file mode 100644 index 000000000..c5ead5e59 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/AddEntityCommand.cs @@ -0,0 +1,34 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class AddEntityCommand : ICommand + where TEntity : Entity, IAggregateRoot + { + public AddEntityCommand(TEntity entity) + { + Entity = entity; + } + + public TEntity Entity { get; set; } + } + + internal class AddEntityCommandHandler : ICommandHandler> + where TEntity : Entity, IAggregateRoot + { + private readonly ICrudService _crudService; + + public AddEntityCommandHandler(ICrudService crudService) + { + _crudService = crudService; + } + + public async Task HandleAsync(AddEntityCommand command, CancellationToken cancellationToken = default) + { + await _crudService.AddAsync(command.Entity); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Commands/AddOrUpdateEntityCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/AddOrUpdateEntityCommand.cs new file mode 100644 index 000000000..f7094193d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/AddOrUpdateEntityCommand.cs @@ -0,0 +1,34 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class AddOrUpdateEntityCommand : ICommand + where TEntity : Entity, IAggregateRoot + { + public AddOrUpdateEntityCommand(TEntity entity) + { + Entity = entity; + } + + public TEntity Entity { get; set; } + } + + internal class AddOrUpdateEntityCommandHandler : ICommandHandler> + where TEntity : Entity, IAggregateRoot + { + private readonly ICrudService _crudService; + + public AddOrUpdateEntityCommandHandler(ICrudService crudService) + { + _crudService = crudService; + } + + public async Task HandleAsync(AddOrUpdateEntityCommand command, CancellationToken cancellationToken = default) + { + await _crudService.AddOrUpdateAsync(command.Entity); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Commands/DeleteEntityCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/DeleteEntityCommand.cs new file mode 100644 index 000000000..7c3d69123 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/DeleteEntityCommand.cs @@ -0,0 +1,29 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class DeleteEntityCommand : ICommand + where TEntity : Entity, IAggregateRoot + { + public TEntity Entity { get; set; } + } + + internal class DeleteEntityCommandHandler : ICommandHandler> + where TEntity : Entity, IAggregateRoot + { + private readonly ICrudService _crudService; + + public DeleteEntityCommandHandler(ICrudService crudService) + { + _crudService = crudService; + } + + public async Task HandleAsync(DeleteEntityCommand command, CancellationToken cancellationToken = default) + { + await _crudService.DeleteAsync(command.Entity); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Commands/ICommand.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/ICommand.cs new file mode 100644 index 000000000..b07b1f0ac --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/ICommand.cs @@ -0,0 +1,6 @@ +namespace ClassifiedAds.Application +{ + public interface ICommand + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Commands/ICommandHandler.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/ICommandHandler.cs new file mode 100644 index 000000000..bc28c0f60 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/ICommandHandler.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public interface ICommandHandler + where TCommand : ICommand + { + Task HandleAsync(TCommand command, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Commands/UpdateEntityCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/UpdateEntityCommand.cs new file mode 100644 index 000000000..41ad3dd7e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Commands/UpdateEntityCommand.cs @@ -0,0 +1,34 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class UpdateEntityCommand : ICommand + where TEntity : Entity, IAggregateRoot + { + public UpdateEntityCommand(TEntity entity) + { + Entity = entity; + } + + public TEntity Entity { get; set; } + } + + internal class UpdateEntityCommandHandler : ICommandHandler> + where TEntity : Entity, IAggregateRoot + { + private readonly ICrudService _crudService; + + public UpdateEntityCommandHandler(ICrudService crudService) + { + _crudService = crudService; + } + + public async Task HandleAsync(UpdateEntityCommand command, CancellationToken cancellationToken = default) + { + await _crudService.UpdateAsync(command.Entity); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/DTOs/Paged.cs b/src/IdentityServer/ClassifiedAds.Application/Common/DTOs/Paged.cs new file mode 100644 index 000000000..6d9274d76 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/DTOs/Paged.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Application.Common.DTOs +{ + public class Paged + { + public long TotalItems { get; set; } + + public List Items { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Dispatcher.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Dispatcher.cs new file mode 100644 index 000000000..367e76635 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Dispatcher.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class Dispatcher + { + private readonly IServiceProvider _provider; + + public Dispatcher(IServiceProvider provider) + { + _provider = provider; + } + + public async Task DispatchAsync(ICommand command, CancellationToken cancellationToken = default) + { + Type type = typeof(ICommandHandler<>); + Type[] typeArgs = { command.GetType() }; + Type handlerType = type.MakeGenericType(typeArgs); + + dynamic handler = _provider.GetService(handlerType); + await handler.HandleAsync((dynamic)command, cancellationToken); + } + + public async Task DispatchAsync(IQuery query, CancellationToken cancellationToken = default) + { + Type type = typeof(IQueryHandler<,>); + Type[] typeArgs = { query.GetType(), typeof(T) }; + Type handlerType = type.MakeGenericType(typeArgs); + + dynamic handler = _provider.GetService(handlerType); + Task result = handler.HandleAsync((dynamic)query, cancellationToken); + + return await result; + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/HandlerFactory.cs b/src/IdentityServer/ClassifiedAds.Application/Common/HandlerFactory.cs new file mode 100644 index 000000000..27aac16fa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/HandlerFactory.cs @@ -0,0 +1,100 @@ +using ClassifiedAds.Application.Decorators; +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace ClassifiedAds.Application +{ + internal class HandlerFactory + { + private readonly List> _handlerFactoriesPipeline = new List>(); + + public HandlerFactory(Type type) + { + AddHandlerFactory(type); + AddDecoratedFactories(type); + } + + public object Create(IServiceProvider provider, Type handlerInterfaceType) + { + object currentHandler = null; + foreach (var handlerFactory in _handlerFactoriesPipeline) + { + currentHandler = handlerFactory(currentHandler, handlerInterfaceType, provider); + } + + return currentHandler; + } + + private void AddDecoratedFactories(Type type) + { + var attributes = type.GetCustomAttributes(inherit: false); + + for (var i = attributes.Length - 1; i >= 0; i--) + { + var attribute = attributes[i]; + var attributeType = attribute.GetType(); + Type decoratorHandlerType = null; + var hasDecoratorHandler = (type.HasInterface(typeof(ICommandHandler<>)) && Mappings.AttributeToCommandHandler.TryGetValue(attributeType, out decoratorHandlerType)) + || (type.HasInterface(typeof(IQueryHandler<,>)) && Mappings.AttributeToQueryHandler.TryGetValue(attributeType, out decoratorHandlerType)); + + if (!hasDecoratorHandler) + { + continue; + } + + AddHandlerFactory(decoratorHandlerType, attribute); + } + } + + private void AddHandlerFactory(Type handlerType, object attribute = null) + { + _handlerFactoriesPipeline.Add(CreateHandler); + + object CreateHandler(object decoratingHandler, Type interfaceType, IServiceProvider provider) + { + var ctor = handlerType + .MakeGenericTypeSafe(interfaceType.GenericTypeArguments) + .GetConstructors() + .Single(); + + var parameterInfos = ctor.GetParameters(); + var parameters = GetParameters(parameterInfos, decoratingHandler, attribute, provider); + + var handler = ctor.Invoke(parameters); + + return handler; + } + } + + private static object[] GetParameters(IEnumerable parameterInfos, object current, object attribute, IServiceProvider provider) + { + return parameterInfos.Select(GetParameter).ToArray(); + + object GetParameter(ParameterInfo parameterInfo) + { + var parameterType = parameterInfo.ParameterType; + + if (Utils.IsHandlerInterface(parameterType)) + { + return current; + } + + if (parameterType == attribute?.GetType()) + { + return attribute; + } + + var service = provider.GetService(parameterType); + if (service != null) + { + return service; + } + + throw new ArgumentException($"Type {parameterType} not found"); + } + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Queries/GetEntititesQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/GetEntititesQuery.cs new file mode 100644 index 000000000..957bb8dd2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/GetEntititesQuery.cs @@ -0,0 +1,31 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class GetEntititesQuery : IQuery> + where TEntity : Entity, IAggregateRoot + { + } + + internal class GetEntititesQueryHandler : IQueryHandler, List> + where TEntity : Entity, IAggregateRoot + { + private readonly IRepository _repository; + + public GetEntititesQueryHandler(IRepository repository) + { + _repository = repository; + } + + public async Task> HandleAsync(GetEntititesQuery query, CancellationToken cancellationToken = default) + { + return await _repository.ToListAsync(_repository.GetAll()); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Queries/GetEntityByIdQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/GetEntityByIdQuery.cs new file mode 100644 index 000000000..bd1e48f81 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/GetEntityByIdQuery.cs @@ -0,0 +1,40 @@ +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class GetEntityByIdQuery : IQuery + where TEntity : Entity, IAggregateRoot + { + public Guid Id { get; set; } + public bool ThrowNotFoundIfNull { get; set; } + } + + internal class GetEntityByIdQueryHandler : IQueryHandler, TEntity> + where TEntity : Entity, IAggregateRoot + { + private readonly IRepository _repository; + + public GetEntityByIdQueryHandler(IRepository repository) + { + _repository = repository; + } + + public async Task HandleAsync(GetEntityByIdQuery query, CancellationToken cancellationToken = default) + { + var entity = await _repository.FirstOrDefaultAsync(_repository.GetAll().Where(x => x.Id == query.Id)); + + if (query.ThrowNotFoundIfNull && entity == null) + { + throw new NotFoundException($"Entity {query.Id} not found."); + } + + return entity; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Queries/IQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/IQuery.cs new file mode 100644 index 000000000..36f768af6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/IQuery.cs @@ -0,0 +1,6 @@ +namespace ClassifiedAds.Application +{ + public interface IQuery + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Queries/IQueryHandler.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/IQueryHandler.cs new file mode 100644 index 000000000..821fbe070 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Queries/IQueryHandler.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public interface IQueryHandler + where TQuery : IQuery + { + Task HandleAsync(TQuery query, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Services/CrudService.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Services/CrudService.cs new file mode 100644 index 000000000..a87060eaf --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Services/CrudService.cs @@ -0,0 +1,71 @@ +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public class CrudService : ICrudService + where T : Entity, IAggregateRoot + { + private readonly IUnitOfWork _unitOfWork; + protected readonly IRepository _repository; + private readonly IDomainEvents _domainEvents; + + public CrudService(IRepository repository, IDomainEvents domainEvents) + { + _unitOfWork = repository.UnitOfWork; + _repository = repository; + _domainEvents = domainEvents; + } + + public Task> GetAsync(CancellationToken cancellationToken = default) + { + return _repository.ToListAsync(_repository.GetAll()); + } + + public Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default) + { + ValidationException.Requires(id != Guid.Empty, "Invalid Id"); + return _repository.FirstOrDefaultAsync(_repository.GetAll().Where(x => x.Id == id)); + } + + public async Task AddOrUpdateAsync(T entity, CancellationToken cancellationToken = default) + { + if (entity.Id.Equals(default)) + { + await AddAsync(entity, cancellationToken); + } + else + { + await UpdateAsync(entity, cancellationToken); + } + } + + public async Task AddAsync(T entity, CancellationToken cancellationToken = default) + { + await _repository.AddAsync(entity, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + await _domainEvents.DispatchAsync(new EntityCreatedEvent(entity, DateTime.UtcNow), cancellationToken); + } + + public async Task UpdateAsync(T entity, CancellationToken cancellationToken = default) + { + await _repository.UpdateAsync(entity, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + await _domainEvents.DispatchAsync(new EntityUpdatedEvent(entity, DateTime.UtcNow), cancellationToken); + } + + public async Task DeleteAsync(T entity, CancellationToken cancellationToken = default) + { + _repository.Delete(entity); + await _unitOfWork.SaveChangesAsync(cancellationToken); + await _domainEvents.DispatchAsync(new EntityDeletedEvent(entity, DateTime.UtcNow), cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Services/ICrudService.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Services/ICrudService.cs new file mode 100644 index 000000000..bf01b375d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Services/ICrudService.cs @@ -0,0 +1,24 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application +{ + public interface ICrudService + where T : Entity, IAggregateRoot + { + Task> GetAsync(CancellationToken cancellationToken = default); + + Task GetByIdAsync(Guid id, CancellationToken cancellationToken = default); + + Task AddOrUpdateAsync(T entity, CancellationToken cancellationToken = default); + + Task AddAsync(T entity, CancellationToken cancellationToken = default); + + Task UpdateAsync(T entity, CancellationToken cancellationToken = default); + + Task DeleteAsync(T entity, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Common/Utils.cs b/src/IdentityServer/ClassifiedAds.Application/Common/Utils.cs new file mode 100644 index 000000000..8ba48e017 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Common/Utils.cs @@ -0,0 +1,20 @@ +using System; + +namespace ClassifiedAds.Application +{ + internal static class Utils + { + public static bool IsHandlerInterface(Type type) + { + if (!type.IsGenericType) + { + return false; + } + + var typeDefinition = type.GetGenericTypeDefinition(); + + return typeDefinition == typeof(ICommandHandler<>) + || typeDefinition == typeof(IQueryHandler<,>); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogAttribute.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogAttribute.cs new file mode 100644 index 000000000..7fa379a29 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace ClassifiedAds.Application.Decorators.AuditLog +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] + public sealed class AuditLogAttribute : Attribute + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogCommandDecorator.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogCommandDecorator.cs new file mode 100644 index 000000000..91e47f012 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogCommandDecorator.cs @@ -0,0 +1,26 @@ +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Decorators.AuditLog +{ + [Mapping(Type = typeof(AuditLogAttribute))] + public class AuditLogCommandDecorator : ICommandHandler + where TCommand : ICommand + { + private readonly ICommandHandler _handler; + + public AuditLogCommandDecorator(ICommandHandler handler) + { + _handler = handler; + } + + public async Task HandleAsync(TCommand command, CancellationToken cancellationToken = default) + { + var commandJson = JsonSerializer.Serialize(command); + Console.WriteLine($"Command of type {command.GetType().Name}: {commandJson}"); + await _handler.HandleAsync(command, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogQueryDecorator.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogQueryDecorator.cs new file mode 100644 index 000000000..554e10316 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/AuditLog/AuditLogQueryDecorator.cs @@ -0,0 +1,26 @@ +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Decorators.AuditLog +{ + [Mapping(Type = typeof(AuditLogAttribute))] + public class AuditLogQueryDecorator : IQueryHandler + where TQuery : IQuery + { + private readonly IQueryHandler _handler; + + public AuditLogQueryDecorator(IQueryHandler handler) + { + _handler = handler; + } + + public Task HandleAsync(TQuery query, CancellationToken cancellationToken = default) + { + string queryJson = JsonSerializer.Serialize(query); + Console.WriteLine($"Query of type {query.GetType().Name}: {queryJson}"); + return _handler.HandleAsync(query, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryAttribute.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryAttribute.cs new file mode 100644 index 000000000..64c966f47 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryAttribute.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Application.Decorators.DatabaseRetry +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] + public sealed class DatabaseRetryAttribute : Attribute + { + public int RetryTimes { get; } + + public DatabaseRetryAttribute(int retryTimes = 3) + { + RetryTimes = retryTimes; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryCommandDecorator.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryCommandDecorator.cs new file mode 100644 index 000000000..dba343719 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryCommandDecorator.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Decorators.DatabaseRetry +{ + [Mapping(Type = typeof(DatabaseRetryAttribute))] + public class DatabaseRetryCommandDecorator : DatabaseRetryDecoratorBase, ICommandHandler + where TCommand : ICommand + { + private readonly ICommandHandler _handler; + + public DatabaseRetryCommandDecorator(ICommandHandler handler, DatabaseRetryAttribute options) + { + DatabaseRetryOptions = options; + _handler = handler; + } + + public async Task HandleAsync(TCommand command, CancellationToken cancellationToken = default) + { + await WrapExecutionAsync(() => _handler.HandleAsync(command, cancellationToken)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryDecoratorBase.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryDecoratorBase.cs new file mode 100644 index 000000000..54147b9e5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryDecoratorBase.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Decorators.DatabaseRetry +{ + public abstract class DatabaseRetryDecoratorBase + { + protected DatabaseRetryAttribute DatabaseRetryOptions { get; set; } + + protected void WrapExecution(Action action) + { + int executedTimes = 0; + + while (true) + { + try + { + executedTimes++; + action(); + return; + } + catch (Exception ex) + { + if (executedTimes >= DatabaseRetryOptions.RetryTimes || !IsDatabaseException(ex)) + { + throw; + } + } + } + } + + protected async Task WrapExecutionAsync(Func action) + { + int executedTimes = 0; + + while (true) + { + try + { + executedTimes++; + await action(); + return; + } + catch (Exception ex) + { + if (executedTimes >= DatabaseRetryOptions.RetryTimes || !IsDatabaseException(ex)) + { + throw; + } + } + } + } + + private static bool IsDatabaseException(Exception exception) + { + string message = exception.InnerException?.Message; + + if (message == null) + { + return false; + } + + return message.Contains("The connection is broken and recovery is not possible") + || message.Contains("error occurred while establishing a connection"); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryQueryDecorator.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryQueryDecorator.cs new file mode 100644 index 000000000..0cf32c8ce --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/DatabaseRetry/DatabaseRetryQueryDecorator.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Decorators.DatabaseRetry +{ + [Mapping(Type = typeof(DatabaseRetryAttribute))] + public class DatabaseRetryQueryDecorator : DatabaseRetryDecoratorBase, IQueryHandler + where TQuery : IQuery + { + private readonly IQueryHandler _handler; + + public DatabaseRetryQueryDecorator(IQueryHandler handler, DatabaseRetryAttribute options) + { + DatabaseRetryOptions = options; + _handler = handler; + } + + public async Task HandleAsync(TQuery query, CancellationToken cancellationToken = default) + { + Task result = default; + await WrapExecutionAsync(() => result = _handler.HandleAsync(query, cancellationToken)); + return await result; + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/Decorators/Mappings.cs b/src/IdentityServer/ClassifiedAds.Application/Decorators/Mappings.cs new file mode 100644 index 000000000..6c569bd38 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Decorators/Mappings.cs @@ -0,0 +1,47 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace ClassifiedAds.Application.Decorators +{ + internal static class Mappings + { + static Mappings() + { + var decorators = Assembly.GetExecutingAssembly().GetTypes(); + foreach (var type in decorators) + { + if (type.HasInterface(typeof(ICommandHandler<>))) + { + var decoratorAttribute = (MappingAttribute)type.GetCustomAttributes(false).FirstOrDefault(x => x.GetType() == typeof(MappingAttribute)); + + if (decoratorAttribute != null) + { + AttributeToCommandHandler[decoratorAttribute.Type] = type; + } + } + else if (type.HasInterface(typeof(IQueryHandler<,>))) + { + var decoratorAttribute = (MappingAttribute)type.GetCustomAttributes(false).FirstOrDefault(x => x.GetType() == typeof(MappingAttribute)); + + if (decoratorAttribute != null) + { + AttributeToQueryHandler[decoratorAttribute.Type] = type; + } + } + } + } + + public static readonly Dictionary AttributeToCommandHandler = new Dictionary(); + + public static readonly Dictionary AttributeToQueryHandler = new Dictionary(); + } + + [AttributeUsage(AttributeTargets.Class)] + public sealed class MappingAttribute : Attribute + { + public Type Type { get; set; } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Application/EmailMessages/Services/EmailMessageService.cs b/src/IdentityServer/ClassifiedAds.Application/EmailMessages/Services/EmailMessageService.cs new file mode 100644 index 000000000..0b5c543f9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/EmailMessages/Services/EmailMessageService.cs @@ -0,0 +1,111 @@ +using ClassifiedAds.CrossCuttingConcerns.CircuitBreakers; +using ClassifiedAds.CrossCuttingConcerns.Locks; +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Notification; +using ClassifiedAds.Domain.Repositories; +using Microsoft.Extensions.Logging; +using System; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.EmailMessages.Services +{ + public class EmailMessageService + { + private readonly ILogger _logger; + private readonly IEmailMessageRepository _repository; + private readonly IEmailNotification _emailNotification; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly ICircuitBreakerManager _circuitBreakerManager; + private readonly IDistributedLock _distributedLock; + + public EmailMessageService(ILogger logger, + IEmailMessageRepository repository, + IEmailNotification emailNotification, + IDateTimeProvider dateTimeProvider, + ICircuitBreakerManager circuitBreakerManager, + IDistributedLock distributedLock) + { + _logger = logger; + _repository = repository; + _emailNotification = emailNotification; + _dateTimeProvider = dateTimeProvider; + _circuitBreakerManager = circuitBreakerManager; + _distributedLock = distributedLock; + } + + public async Task SendEmailMessagesAsync() + { + var circuit = _circuitBreakerManager.GetCircuitBreaker("EmailService", TimeSpan.FromMinutes(1)); + circuit.EnsureOkStatus(); + + var deplayedTimes = new[] + { + TimeSpan.FromMinutes(1), + TimeSpan.FromMinutes(2), + TimeSpan.FromMinutes(3), + TimeSpan.FromMinutes(5), + TimeSpan.FromMinutes(8), + TimeSpan.FromMinutes(13), + TimeSpan.FromMinutes(21), + TimeSpan.FromMinutes(34), + TimeSpan.FromMinutes(55), + TimeSpan.FromMinutes(89), + }; + + var dateTime = _dateTimeProvider.OffsetNow; + var defaultAttemptCount = 5; + + var messages = _repository.GetAll() + .Where(x => x.SentDateTime == null) + .Where(x => x.ExpiredDateTime == null || x.ExpiredDateTime > dateTime) + .Where(x => (x.MaxAttemptCount == 0 && x.AttemptCount < defaultAttemptCount) || x.AttemptCount < x.MaxAttemptCount) + .Where(x => x.NextAttemptDateTime == null || x.NextAttemptDateTime <= dateTime) + .ToList(); + + if (messages.Any()) + { + foreach (var email in messages) + { + string log = Environment.NewLine + Environment.NewLine + + $"[{_dateTimeProvider.OffsetNow.ToString(CultureInfo.InvariantCulture)}] "; + try + { + await _emailNotification.SendAsync(email); + email.SentDateTime = _dateTimeProvider.OffsetNow; + email.Log += log + "Succeed."; + + _circuitBreakerManager.LogSuccess(circuit); + } + catch (Exception ex) + { + email.Log += log + ex.ToString(); + email.NextAttemptDateTime = _dateTimeProvider.OffsetNow + deplayedTimes[email.AttemptCount]; + + _circuitBreakerManager.LogFailure(circuit, 5, TimeSpan.FromMinutes(5)); + } + + email.AttemptCount += 1; + email.Log = email.Log.Trim(); + email.UpdatedDateTime = _dateTimeProvider.OffsetNow; + + if (email.MaxAttemptCount == 0) + { + email.MaxAttemptCount = defaultAttemptCount; + } + + await _repository.UnitOfWork.SaveChangesAsync(); + + circuit.EnsureOkStatus(); + } + } + else + { + _logger.LogInformation("No email to send."); + } + + return messages.Count; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/EventLogs/PublishEventService.cs b/src/IdentityServer/ClassifiedAds.Application/EventLogs/PublishEventService.cs new file mode 100644 index 000000000..617d9c9ef --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/EventLogs/PublishEventService.cs @@ -0,0 +1,65 @@ +using ClassifiedAds.Application.FileEntries.DTOs; +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using ClassifiedAds.Domain.Repositories; +using Microsoft.Extensions.Logging; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.EventLogs +{ + public class PublishEventService + { + private readonly ILogger _logger; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IRepository _outboxEventRepository; + private readonly IMessageSender _fileUploadedEventSender; + private readonly IMessageSender _fileDeletedEventSender; + + public PublishEventService(ILogger logger, + IDateTimeProvider dateTimeProvider, + IRepository outboxEventRepository, + IMessageSender fileUploadedEventSender, + IMessageSender fileDeletedEventSender) + { + _logger = logger; + _dateTimeProvider = dateTimeProvider; + _outboxEventRepository = outboxEventRepository; + _fileUploadedEventSender = fileUploadedEventSender; + _fileDeletedEventSender = fileDeletedEventSender; + } + + public async Task PublishEvents() + { + var events = _outboxEventRepository.GetAll() + .Where(x => !x.Published) + .OrderBy(x => x.CreatedDateTime) + .Take(50) + .ToList(); + + foreach (var eventLog in events) + { + if (eventLog.EventType == "FILEENTRY_CREATED") + { + await _fileUploadedEventSender.SendAsync(new FileUploadedEvent { FileEntry = JsonSerializer.Deserialize(eventLog.Message) }); + } + else if (eventLog.EventType == "FILEENTRY_DELETED") + { + await _fileDeletedEventSender.SendAsync(new FileDeletedEvent { FileEntry = JsonSerializer.Deserialize(eventLog.Message) }); + } + else + { + // TODO: Take Note + } + + eventLog.Published = true; + eventLog.UpdatedDateTime = _dateTimeProvider.OffsetNow; + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(); + } + + return events.Count; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/FileEntries/DTOs/FileDeletedEvent.cs b/src/IdentityServer/ClassifiedAds.Application/FileEntries/DTOs/FileDeletedEvent.cs new file mode 100644 index 000000000..90c64ec21 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/FileEntries/DTOs/FileDeletedEvent.cs @@ -0,0 +1,9 @@ +using ClassifiedAds.Domain.Entities; + +namespace ClassifiedAds.Application.FileEntries.DTOs +{ + public class FileDeletedEvent + { + public FileEntry FileEntry { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/FileEntries/DTOs/FileUploadedEvent.cs b/src/IdentityServer/ClassifiedAds.Application/FileEntries/DTOs/FileUploadedEvent.cs new file mode 100644 index 000000000..67683a01c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/FileEntries/DTOs/FileUploadedEvent.cs @@ -0,0 +1,9 @@ +using ClassifiedAds.Domain.Entities; + +namespace ClassifiedAds.Application.FileEntries.DTOs +{ + public class FileUploadedEvent + { + public FileEntry FileEntry { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryCreatedEventHandler.cs b/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryCreatedEventHandler.cs new file mode 100644 index 000000000..0308e33b5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryCreatedEventHandler.cs @@ -0,0 +1,51 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.FileEntries.EventHandlers +{ + public class FileEntryCreatedEventHandler : IDomainEventHandler> + { + private readonly ICrudService _auditSerivce; + private readonly ICurrentUser _currentUser; + private readonly IRepository _outboxEventRepository; + + public FileEntryCreatedEventHandler(ICrudService auditSerivce, + ICurrentUser currentUser, + IRepository outboxEventRepository) + { + _auditSerivce = auditSerivce; + _currentUser = currentUser; + _outboxEventRepository = outboxEventRepository; + } + + public async Task HandleAsync(EntityCreatedEvent domainEvent, CancellationToken cancellationToken = default) + { + await _auditSerivce.AddOrUpdateAsync(new AuditLogEntry + { + UserId = _currentUser.IsAuthenticated ? _currentUser.UserId : Guid.Empty, + CreatedDateTime = domainEvent.EventDateTime, + Action = "CREATED_FILEENTRY", + ObjectId = domainEvent.Entity.Id.ToString(), + Log = domainEvent.Entity.AsJsonString(), + }); + + await _outboxEventRepository.AddOrUpdateAsync(new OutboxEvent + { + EventType = "FILEENTRY_CREATED", + TriggeredById = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + ObjectId = domainEvent.Entity.Id.ToString(), + Message = domainEvent.Entity.AsJsonString(), + Published = false, + }, cancellationToken); + + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryDeletedEventHandler.cs b/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryDeletedEventHandler.cs new file mode 100644 index 000000000..935cd3262 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryDeletedEventHandler.cs @@ -0,0 +1,51 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.FileEntries.EventHandlers +{ + public class FileEntryDeletedEventHandler : IDomainEventHandler> + { + private readonly ICrudService _auditSerivce; + private readonly ICurrentUser _currentUser; + private readonly IRepository _outboxEventRepository; + + public FileEntryDeletedEventHandler(ICrudService auditSerivce, + ICurrentUser currentUser, + IRepository outboxEventRepository) + { + _auditSerivce = auditSerivce; + _currentUser = currentUser; + _outboxEventRepository = outboxEventRepository; + } + + public async Task HandleAsync(EntityDeletedEvent domainEvent, CancellationToken cancellationToken = default) + { + await _auditSerivce.AddOrUpdateAsync(new AuditLogEntry + { + UserId = _currentUser.IsAuthenticated ? _currentUser.UserId : Guid.Empty, + CreatedDateTime = domainEvent.EventDateTime, + Action = "DELETE_FILEENTRY", + ObjectId = domainEvent.Entity.Id.ToString(), + Log = domainEvent.Entity.AsJsonString(), + }); + + await _outboxEventRepository.AddOrUpdateAsync(new OutboxEvent + { + EventType = "FILEENTRY_DELETED", + TriggeredById = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + ObjectId = domainEvent.Entity.Id.ToString(), + Message = domainEvent.Entity.AsJsonString(), + Published = false, + }, cancellationToken); + + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryUpdatedEventHandler.cs b/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryUpdatedEventHandler.cs new file mode 100644 index 000000000..0c6e6ca88 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/FileEntries/EventHandlers/FileEntryUpdatedEventHandler.cs @@ -0,0 +1,51 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.FileEntries.EventHandlers +{ + public class FileEntryUpdatedEventHandler : IDomainEventHandler> + { + private readonly ICrudService _auditSerivce; + private readonly ICurrentUser _currentUser; + private readonly IRepository _outboxEventRepository; + + public FileEntryUpdatedEventHandler(ICrudService auditSerivce, + ICurrentUser currentUser, + IRepository outboxEventRepository) + { + _auditSerivce = auditSerivce; + _currentUser = currentUser; + _outboxEventRepository = outboxEventRepository; + } + + public async Task HandleAsync(EntityUpdatedEvent domainEvent, CancellationToken cancellationToken = default) + { + await _auditSerivce.AddOrUpdateAsync(new AuditLogEntry + { + UserId = _currentUser.IsAuthenticated ? _currentUser.UserId : Guid.Empty, + CreatedDateTime = domainEvent.EventDateTime, + Action = "UPDATED_FILEENTRY", + ObjectId = domainEvent.Entity.Id.ToString(), + Log = domainEvent.Entity.AsJsonString(), + }); + + await _outboxEventRepository.AddOrUpdateAsync(new OutboxEvent + { + EventType = "FILEENTRY_UPDATED", + TriggeredById = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + ObjectId = domainEvent.Entity.Id.ToString(), + Message = domainEvent.Entity.AsJsonString(), + Published = false, + }, cancellationToken); + + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/Commands/AddUpdateProductCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Products/Commands/AddUpdateProductCommand.cs new file mode 100644 index 000000000..71f563f8c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/Commands/AddUpdateProductCommand.cs @@ -0,0 +1,34 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.Commands +{ + public class AddUpdateProductCommand : ICommand + { + public Product Product { get; set; } + } + + internal class AddUpdateProductCommandHandler : ICommandHandler + { + private readonly ICrudService _productService; + private readonly IUnitOfWork _unitOfWork; + + public AddUpdateProductCommandHandler(ICrudService productService, IUnitOfWork unitOfWork) + { + _productService = productService; + _unitOfWork = unitOfWork; + } + + public async Task HandleAsync(AddUpdateProductCommand command, CancellationToken cancellationToken = default) + { + using (await _unitOfWork.BeginTransactionAsync(System.Data.IsolationLevel.ReadCommitted, cancellationToken)) + { + await _productService.AddOrUpdateAsync(command.Product); + + await _unitOfWork.CommitTransactionAsync(cancellationToken); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/Commands/DeleteProductCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Products/Commands/DeleteProductCommand.cs new file mode 100644 index 000000000..6c5f58f5b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/Commands/DeleteProductCommand.cs @@ -0,0 +1,26 @@ +using ClassifiedAds.Domain.Entities; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.Commands +{ + public class DeleteProductCommand : ICommand + { + public Product Product { get; set; } + } + + internal class DeleteProductCommandHandler : ICommandHandler + { + private readonly ICrudService _productService; + + public DeleteProductCommandHandler(ICrudService productService) + { + _productService = productService; + } + + public async Task HandleAsync(DeleteProductCommand command, CancellationToken cancellationToken = default) + { + await _productService.DeleteAsync(command.Product); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/DTOs/ProductDTO.cs b/src/IdentityServer/ClassifiedAds.Application/Products/DTOs/ProductDTO.cs new file mode 100644 index 000000000..07e6384fa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/DTOs/ProductDTO.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Application.Products.DTOs +{ + public class ProductDTO + { + public Guid Id { get; set; } + + public string Code { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductCreatedEventHandler.cs b/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductCreatedEventHandler.cs new file mode 100644 index 000000000..f38c8cbae --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductCreatedEventHandler.cs @@ -0,0 +1,51 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.EventHandlers +{ + public class ProductCreatedEventHandler : IDomainEventHandler> + { + private readonly ICrudService _auditSerivce; + private readonly ICurrentUser _currentUser; + private readonly IRepository _outboxEventRepository; + + public ProductCreatedEventHandler(ICrudService auditSerivce, + ICurrentUser currentUser, + IRepository outboxEventRepository) + { + _auditSerivce = auditSerivce; + _currentUser = currentUser; + _outboxEventRepository = outboxEventRepository; + } + + public async Task HandleAsync(EntityCreatedEvent domainEvent, CancellationToken cancellationToken = default) + { + await _auditSerivce.AddOrUpdateAsync(new AuditLogEntry + { + UserId = _currentUser.IsAuthenticated ? _currentUser.UserId : Guid.Empty, + CreatedDateTime = domainEvent.EventDateTime, + Action = "CREATED_PRODUCT", + ObjectId = domainEvent.Entity.Id.ToString(), + Log = domainEvent.Entity.AsJsonString(), + }); + + await _outboxEventRepository.AddOrUpdateAsync(new OutboxEvent + { + EventType = "PRODUCT_CREATED", + TriggeredById = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + ObjectId = domainEvent.Entity.Id.ToString(), + Message = domainEvent.Entity.AsJsonString(), + Published = false, + }, cancellationToken); + + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductDeletedEventHandler.cs b/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductDeletedEventHandler.cs new file mode 100644 index 000000000..2b629ccfb --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductDeletedEventHandler.cs @@ -0,0 +1,50 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.EventHandlers +{ + public class ProductDeletedEventHandler : IDomainEventHandler> + { + private readonly ICrudService _auditSerivce; + private readonly ICurrentUser _currentUser; + private readonly IRepository _outboxEventRepository; + + public ProductDeletedEventHandler(ICrudService auditSerivce, + ICurrentUser currentUser, + IRepository outboxEventRepository) + { + _auditSerivce = auditSerivce; + _currentUser = currentUser; + _outboxEventRepository = outboxEventRepository; + } + + public async Task HandleAsync(EntityDeletedEvent domainEvent, CancellationToken cancellationToken = default) + { + await _auditSerivce.AddOrUpdateAsync(new AuditLogEntry + { + UserId = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + Action = "DELETED_PRODUCT", + ObjectId = domainEvent.Entity.Id.ToString(), + Log = domainEvent.Entity.AsJsonString(), + }); + + await _outboxEventRepository.AddOrUpdateAsync(new OutboxEvent + { + EventType = "PRODUCT_DELETED", + TriggeredById = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + ObjectId = domainEvent.Entity.Id.ToString(), + Message = domainEvent.Entity.AsJsonString(), + Published = false, + }, cancellationToken); + + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductUpdatedEventHandler.cs b/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductUpdatedEventHandler.cs new file mode 100644 index 000000000..c80926310 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/EventHandlers/ProductUpdatedEventHandler.cs @@ -0,0 +1,50 @@ +using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.EventHandlers +{ + public class ProductUpdatedEventHandler : IDomainEventHandler> + { + private readonly ICrudService _auditSerivce; + private readonly ICurrentUser _currentUser; + private readonly IRepository _outboxEventRepository; + + public ProductUpdatedEventHandler(ICrudService auditSerivce, + ICurrentUser currentUser, + IRepository outboxEventRepository) + { + _auditSerivce = auditSerivce; + _currentUser = currentUser; + _outboxEventRepository = outboxEventRepository; + } + + public async Task HandleAsync(EntityUpdatedEvent domainEvent, CancellationToken cancellationToken = default) + { + await _auditSerivce.AddOrUpdateAsync(new AuditLogEntry + { + UserId = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + Action = "UPDATED_PRODUCT", + ObjectId = domainEvent.Entity.Id.ToString(), + Log = domainEvent.Entity.AsJsonString(), + }); + + await _outboxEventRepository.AddOrUpdateAsync(new OutboxEvent + { + EventType = "PRODUCT_UPDATED", + TriggeredById = _currentUser.UserId, + CreatedDateTime = domainEvent.EventDateTime, + ObjectId = domainEvent.Entity.Id.ToString(), + Message = domainEvent.Entity.AsJsonString(), + Published = false, + }, cancellationToken); + + await _outboxEventRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/Queries/GetProductQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Products/Queries/GetProductQuery.cs new file mode 100644 index 000000000..4c48d5546 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/Queries/GetProductQuery.cs @@ -0,0 +1,38 @@ +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.Queries +{ + public class GetProductQuery : IQuery + { + public Guid Id { get; set; } + public bool ThrowNotFoundIfNull { get; set; } + } + + internal class GetProductQueryHandler : IQueryHandler + { + private readonly IRepository _productRepository; + + public GetProductQueryHandler(IRepository productRepository) + { + _productRepository = productRepository; + } + + public async Task HandleAsync(GetProductQuery query, CancellationToken cancellationToken = default) + { + var product = await _productRepository.FirstOrDefaultAsync(_productRepository.GetAll().Where(x => x.Id == query.Id)); + + if (query.ThrowNotFoundIfNull && product == null) + { + throw new NotFoundException($"Product {query.Id} not found."); + } + + return product; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/Queries/GetProductsQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Products/Queries/GetProductsQuery.cs new file mode 100644 index 000000000..eb0cba8af --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/Queries/GetProductsQuery.cs @@ -0,0 +1,33 @@ +using ClassifiedAds.Application.Decorators.AuditLog; +using ClassifiedAds.Application.Decorators.DatabaseRetry; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Products.Queries +{ + public class GetProductsQuery : IQuery> + { + } + + [AuditLog] + [DatabaseRetry] + internal class GetProductsQueryHandler : IQueryHandler> + { + private readonly IRepository _productRepository; + + public GetProductsQueryHandler(IRepository productRepository) + { + _productRepository = productRepository; + } + + public async Task> HandleAsync(GetProductsQuery query, CancellationToken cancellationToken = default) + { + return await _productRepository.ToListAsync(_productRepository.GetAll()); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/Services/IProductService.cs b/src/IdentityServer/ClassifiedAds.Application/Products/Services/IProductService.cs new file mode 100644 index 000000000..0bb7e8e52 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/Services/IProductService.cs @@ -0,0 +1,8 @@ +using ClassifiedAds.Domain.Entities; + +namespace ClassifiedAds.Application.Products.Services +{ + public interface IProductService : ICrudService + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Products/Services/ProductService.cs b/src/IdentityServer/ClassifiedAds.Application/Products/Services/ProductService.cs new file mode 100644 index 000000000..361a71963 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Products/Services/ProductService.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Domain.Repositories; +using System; + +namespace ClassifiedAds.Application.Products.Services +{ + public class ProductService : CrudService, IProductService + { + public ProductService(IRepository productRepository, IDomainEvents domainEvents, ICurrentUser currentUser) + : base(productRepository, domainEvents) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/AddClaimCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/AddClaimCommand.cs new file mode 100644 index 000000000..173db10c2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/AddClaimCommand.cs @@ -0,0 +1,29 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Roles.Commands +{ + public class AddClaimCommand : ICommand + { + public Role Role { get; set; } + public RoleClaim Claim { get; set; } + } + + internal class AddClaimCommandHandler : ICommandHandler + { + private readonly IRoleRepository _roleRepository; + + public AddClaimCommandHandler(IRoleRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public async Task HandleAsync(AddClaimCommand command, CancellationToken cancellationToken = default) + { + command.Role.Claims.Add(command.Claim); + await _roleRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/AddUpdateRoleCommand .cs b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/AddUpdateRoleCommand .cs new file mode 100644 index 000000000..1446e29a0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/AddUpdateRoleCommand .cs @@ -0,0 +1,28 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Roles.Commands +{ + public class AddUpdateRoleCommand : ICommand + { + public Role Role { get; set; } + } + + internal class AddUpdateRoleCommandHandler : ICommandHandler + { + private readonly IRoleRepository _roleRepository; + + public AddUpdateRoleCommandHandler(IRoleRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public async Task HandleAsync(AddUpdateRoleCommand command, CancellationToken cancellationToken = default) + { + await _roleRepository.AddOrUpdateAsync(command.Role); + await _roleRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/DeleteClaimCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/DeleteClaimCommand.cs new file mode 100644 index 000000000..b960cc95c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/DeleteClaimCommand.cs @@ -0,0 +1,29 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Roles.Commands +{ + public class DeleteClaimCommand : ICommand + { + public Role Role { get; set; } + public RoleClaim Claim { get; set; } + } + + internal class DeleteClaimCommandHandler : ICommandHandler + { + private readonly IRoleRepository _roleRepository; + + public DeleteClaimCommandHandler(IRoleRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public async Task HandleAsync(DeleteClaimCommand command, CancellationToken cancellationToken = default) + { + command.Role.Claims.Remove(command.Claim); + await _roleRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/DeleteRoleCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/DeleteRoleCommand.cs new file mode 100644 index 000000000..630ab34b9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Roles/Commands/DeleteRoleCommand.cs @@ -0,0 +1,28 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Roles.Commands +{ + public class DeleteRoleCommand : ICommand + { + public Role Role { get; set; } + } + + internal class DeleteRoleCommandHandler : ICommandHandler + { + private readonly IRoleRepository _roleRepository; + + public DeleteRoleCommandHandler(IRoleRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public async Task HandleAsync(DeleteRoleCommand command, CancellationToken cancellationToken = default) + { + _roleRepository.Delete(command.Role); + await _roleRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Roles/Queries/GetRoleQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Roles/Queries/GetRoleQuery.cs new file mode 100644 index 000000000..f3e4b3715 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Roles/Queries/GetRoleQuery.cs @@ -0,0 +1,41 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Roles.Queries +{ + public class GetRoleQuery : IQuery + { + public Guid Id { get; set; } + public bool IncludeClaims { get; set; } + public bool IncludeUserRoles { get; set; } + public bool IncludeUsers { get; set; } + public bool AsNoTracking { get; set; } + } + + internal class GetRoleQueryHandler : IQueryHandler + { + private readonly IRoleRepository _roleRepository; + + public GetRoleQueryHandler(IRoleRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public Task HandleAsync(GetRoleQuery query, CancellationToken cancellationToken = default) + { + var db = _roleRepository.Get(new RoleQueryOptions + { + IncludeClaims = query.IncludeClaims, + IncludeUserRoles = query.IncludeUserRoles, + IncludeUsers = query.IncludeUsers, + AsNoTracking = query.AsNoTracking, + }); + + return _roleRepository.FirstOrDefaultAsync(db.Where(x => x.Id == query.Id)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Roles/Queries/GetRolesQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Roles/Queries/GetRolesQuery.cs new file mode 100644 index 000000000..a3f049784 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Roles/Queries/GetRolesQuery.cs @@ -0,0 +1,38 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Roles.Queries +{ + public class GetRolesQuery : IQuery> + { + public bool IncludeClaims { get; set; } + public bool IncludeUserRoles { get; set; } + public bool AsNoTracking { get; set; } + } + + internal class GetRolesQueryHandler : IQueryHandler> + { + private readonly IRoleRepository _roleRepository; + + public GetRolesQueryHandler(IRoleRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public async Task> HandleAsync(GetRolesQuery query, CancellationToken cancellationToken = default) + { + var db = _roleRepository.Get(new RoleQueryOptions + { + IncludeClaims = query.IncludeClaims, + IncludeUserRoles = query.IncludeUserRoles, + AsNoTracking = query.AsNoTracking, + }); + + return await _roleRepository.ToListAsync(db); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/SmsMessages/Services/SmsMessageService.cs b/src/IdentityServer/ClassifiedAds.Application/SmsMessages/Services/SmsMessageService.cs new file mode 100644 index 000000000..de19f7aa1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/SmsMessages/Services/SmsMessageService.cs @@ -0,0 +1,111 @@ +using ClassifiedAds.CrossCuttingConcerns.CircuitBreakers; +using ClassifiedAds.CrossCuttingConcerns.Locks; +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Notification; +using ClassifiedAds.Domain.Repositories; +using Microsoft.Extensions.Logging; +using System; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.SmsMessages.Services +{ + public class SmsMessageService + { + private readonly ILogger _logger; + private readonly ISmsMessageRepository _repository; + private readonly ISmsNotification _smsNotification; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly ICircuitBreakerManager _circuitBreakerManager; + private readonly IDistributedLock _distributedLock; + + public SmsMessageService(ILogger logger, + ISmsMessageRepository repository, + ISmsNotification smsNotification, + IDateTimeProvider dateTimeProvider, + ICircuitBreakerManager circuitBreakerManager, + IDistributedLock distributedLock) + { + _logger = logger; + _repository = repository; + _smsNotification = smsNotification; + _dateTimeProvider = dateTimeProvider; + _circuitBreakerManager = circuitBreakerManager; + _distributedLock = distributedLock; + } + + public async Task SendSmsMessagesAsync() + { + var circuit = _circuitBreakerManager.GetCircuitBreaker("SmsService", TimeSpan.FromMinutes(1)); + circuit.EnsureOkStatus(); + + var deplayedTimes = new[] + { + TimeSpan.FromMinutes(1), + TimeSpan.FromMinutes(2), + TimeSpan.FromMinutes(3), + TimeSpan.FromMinutes(5), + TimeSpan.FromMinutes(8), + TimeSpan.FromMinutes(13), + TimeSpan.FromMinutes(21), + TimeSpan.FromMinutes(34), + TimeSpan.FromMinutes(55), + TimeSpan.FromMinutes(89), + }; + + var dateTime = _dateTimeProvider.OffsetNow; + var defaultAttemptCount = 5; + + var messages = _repository.GetAll() + .Where(x => x.SentDateTime == null) + .Where(x => x.ExpiredDateTime == null || x.ExpiredDateTime > dateTime) + .Where(x => (x.MaxAttemptCount == 0 && x.AttemptCount < defaultAttemptCount) || x.AttemptCount < x.MaxAttemptCount) + .Where(x => x.NextAttemptDateTime == null || x.NextAttemptDateTime <= dateTime) + .ToList(); + + if (messages.Any()) + { + foreach (var sms in messages) + { + string log = Environment.NewLine + Environment.NewLine + + $"[{_dateTimeProvider.OffsetNow.ToString(CultureInfo.InvariantCulture)}] "; + try + { + await _smsNotification.SendAsync(sms); + sms.SentDateTime = _dateTimeProvider.OffsetNow; + sms.Log += log + "Succeed."; + + _circuitBreakerManager.LogSuccess(circuit); + } + catch (Exception ex) + { + sms.Log += log + ex.ToString(); + sms.NextAttemptDateTime = _dateTimeProvider.OffsetNow + deplayedTimes[sms.AttemptCount]; + + _circuitBreakerManager.LogFailure(circuit, 5, TimeSpan.FromMinutes(5)); + } + + sms.AttemptCount += 1; + sms.Log = sms.Log.Trim(); + sms.UpdatedDateTime = _dateTimeProvider.OffsetNow; + + if (sms.MaxAttemptCount == 0) + { + sms.MaxAttemptCount = defaultAttemptCount; + } + + await _repository.UnitOfWork.SaveChangesAsync(); + + circuit.EnsureOkStatus(); + } + } + else + { + _logger.LogInformation("No SMS to send."); + } + + return messages.Count; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Commands/AddClaimCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/AddClaimCommand.cs new file mode 100644 index 000000000..828819618 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/AddClaimCommand.cs @@ -0,0 +1,30 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Commands +{ + public class AddClaimCommand : ICommand + { + public User User { get; set; } + public UserClaim Claim { get; set; } + } + + internal class AddClaimCommandHandler : ICommandHandler + { + private readonly IUserRepository _userRepository; + + public AddClaimCommandHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task HandleAsync(AddClaimCommand command, CancellationToken cancellationToken = default) + { + command.User.Claims.Add(command.Claim); + await _userRepository.AddOrUpdateAsync(command.User); + await _userRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Commands/AddRoleCommand .cs b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/AddRoleCommand .cs new file mode 100644 index 000000000..852574eb5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/AddRoleCommand .cs @@ -0,0 +1,30 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Commands +{ + public class AddRoleCommand : ICommand + { + public User User { get; set; } + public UserRole Role { get; set; } + } + + internal class AddRoleCommandHandler : ICommandHandler + { + private readonly IUserRepository _userRepository; + + public AddRoleCommandHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task HandleAsync(AddRoleCommand command, CancellationToken cancellationToken = default) + { + command.User.UserRoles.Add(command.Role); + await _userRepository.AddOrUpdateAsync(command.User); + await _userRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteClaimCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteClaimCommand.cs new file mode 100644 index 000000000..d38f7b51c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteClaimCommand.cs @@ -0,0 +1,30 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Commands +{ + public class DeleteClaimCommand : ICommand + { + public User User { get; set; } + public UserClaim Claim { get; set; } + } + + internal class DeleteClaimCommandHandler : ICommandHandler + { + private readonly IUserRepository _userRepository; + + public DeleteClaimCommandHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task HandleAsync(DeleteClaimCommand command, CancellationToken cancellationToken = default) + { + command.User.Claims.Remove(command.Claim); + await _userRepository.AddOrUpdateAsync(command.User); + await _userRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteRoleCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteRoleCommand.cs new file mode 100644 index 000000000..189c4db8e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteRoleCommand.cs @@ -0,0 +1,30 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Commands +{ + public class DeleteRoleCommand : ICommand + { + public User User { get; set; } + public UserRole Role { get; set; } + } + + internal class DeleteRoleCommandHandler : ICommandHandler + { + private readonly IUserRepository _userRepository; + + public DeleteRoleCommandHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task HandleAsync(DeleteRoleCommand command, CancellationToken cancellationToken = default) + { + command.User.UserRoles.Remove(command.Role); + await _userRepository.AddOrUpdateAsync(command.User); + await _userRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteUserCommand.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteUserCommand.cs new file mode 100644 index 000000000..2865eba0a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Commands/DeleteUserCommand.cs @@ -0,0 +1,28 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Commands +{ + public class DeleteUserCommand : ICommand + { + public User User { get; set; } + } + + internal class DeleteUserCommandHandler : ICommandHandler + { + private readonly IUserRepository _userRepository; + + public DeleteUserCommandHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task HandleAsync(DeleteUserCommand command, CancellationToken cancellationToken = default) + { + _userRepository.Delete(command.User); + await _userRepository.UnitOfWork.SaveChangesAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/DTOs/UserDTO.cs b/src/IdentityServer/ClassifiedAds.Application/Users/DTOs/UserDTO.cs new file mode 100644 index 000000000..37b94bcd9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/DTOs/UserDTO.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Application.Users.DTOs +{ + public class UserDTO + { + public string UserName { get; set; } + + public string NormalizedUserName { get; set; } + + public string Email { get; set; } + + public string NormalizedEmail { get; set; } + + public bool EmailConfirmed { get; set; } + + public string PhoneNumber { get; set; } + + public bool PhoneNumberConfirmed { get; set; } + + public bool TwoFactorEnabled { get; set; } + + public string ConcurrencyStamp { get; set; } + + public string SecurityStamp { get; set; } + + public bool LockoutEnabled { get; set; } + + public DateTimeOffset? LockoutEnd { get; set; } + + public int AccessFailedCount { get; set; } + + // public IList Tokens { get; set; } + + // public IList Claims { get; set; } + + // public IList UserRoles { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Queries/GetUserQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Queries/GetUserQuery.cs new file mode 100644 index 000000000..753aa3fd8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Queries/GetUserQuery.cs @@ -0,0 +1,41 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Queries +{ + public class GetUserQuery : IQuery + { + public Guid Id { get; set; } + public bool IncludeClaims { get; set; } + public bool IncludeUserRoles { get; set; } + public bool IncludeRoles { get; set; } + public bool AsNoTracking { get; set; } + } + + internal class GetUserQueryHandler : IQueryHandler + { + private readonly IUserRepository _userRepository; + + public GetUserQueryHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public Task HandleAsync(GetUserQuery query, CancellationToken cancellationToken = default) + { + var db = _userRepository.Get(new UserQueryOptions + { + IncludeClaims = query.IncludeClaims, + IncludeUserRoles = query.IncludeUserRoles, + IncludeRoles = query.IncludeRoles, + AsNoTracking = query.AsNoTracking, + }); + + return _userRepository.FirstOrDefaultAsync(db.Where(x => x.Id == query.Id)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Queries/GetUsersQuery.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Queries/GetUsersQuery.cs new file mode 100644 index 000000000..ff65d861a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Queries/GetUsersQuery.cs @@ -0,0 +1,39 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Application.Users.Queries +{ + public class GetUsersQuery : IQuery> + { + public bool IncludeClaims { get; set; } + public bool IncludeUserRoles { get; set; } + public bool IncludeRoles { get; set; } + public bool AsNoTracking { get; set; } + } + + internal class GetUsersQueryHandler : IQueryHandler> + { + private readonly IUserRepository _userRepository; + + public GetUsersQueryHandler(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task> HandleAsync(GetUsersQuery query, CancellationToken cancellationToken = default) + { + var db = _userRepository.Get(new UserQueryOptions + { + IncludeClaims = query.IncludeClaims, + IncludeUserRoles = query.IncludeUserRoles, + IncludeRoles = query.IncludeRoles, + AsNoTracking = query.AsNoTracking, + }); + + return await _userRepository.ToListAsync(db); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Services/IUserService.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Services/IUserService.cs new file mode 100644 index 000000000..800fa5bb8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Services/IUserService.cs @@ -0,0 +1,8 @@ +using ClassifiedAds.Domain.Entities; + +namespace ClassifiedAds.Application.Users.Services +{ + public interface IUserService : ICrudService + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Application/Users/Services/UserService.cs b/src/IdentityServer/ClassifiedAds.Application/Users/Services/UserService.cs new file mode 100644 index 000000000..1ccce06b2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Application/Users/Services/UserService.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Events; +using ClassifiedAds.Domain.Repositories; +using System; + +namespace ClassifiedAds.Application.Users.Services +{ + public class UserService : CrudService, IUserService + { + public UserService(IRepository userRepository, IDomainEvents domainEvents) + : base(userRepository, domainEvents) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/.editorconfig b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/.editorconfig new file mode 100644 index 000000000..43d5b5702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/.editorconfig @@ -0,0 +1,224 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +# insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = false +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + +############################### +# StyleCop # +############################### +[*.cs] + +# SA1027: Use tabs correctly +dotnet_diagnostic.SA1027.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +dotnet_diagnostic.SA1111.severity = none + +# SA1113: Comma should be on the same line as previous parameter +dotnet_diagnostic.SA1113.severity = none + +# SA1115: Parameter should follow comma +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1208: System using directives should be placed before other using directives +dotnet_diagnostic.SA1208.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +dotnet_diagnostic.SA1310.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# SA1610: Property documentation should have value text +dotnet_diagnostic.SA1610.severity = none + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = suggestion diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Caching/ICache.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Caching/ICache.cs new file mode 100644 index 000000000..7ab709613 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Caching/ICache.cs @@ -0,0 +1,13 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Caching +{ + public interface ICache + { + void Add(string key, object item, TimeSpan timeSpan); + + void Remove(string key); + + object Get(string key); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/CircuitBreakerOpenException.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/CircuitBreakerOpenException.cs new file mode 100644 index 000000000..3890a4df2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/CircuitBreakerOpenException.cs @@ -0,0 +1,8 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.CircuitBreakers +{ + public class CircuitBreakerOpenException : Exception + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/ICircuitBreaker.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/ICircuitBreaker.cs new file mode 100644 index 000000000..949461d73 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/ICircuitBreaker.cs @@ -0,0 +1,22 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.CircuitBreakers +{ + public interface ICircuitBreaker + { + public string Name { get; set; } + + public CircuitStatus Status { get; set; } + + public DateTimeOffset LastStatusUpdated { get; set; } + + void EnsureOkStatus(); + } + + public enum CircuitStatus + { + Closed = 1, + Open = 2, + HalfOpen = 3, + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/ICircuitBreakerManager.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/ICircuitBreakerManager.cs new file mode 100644 index 000000000..6afa96006 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/CircuitBreakers/ICircuitBreakerManager.cs @@ -0,0 +1,13 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.CircuitBreakers +{ + public interface ICircuitBreakerManager + { + ICircuitBreaker GetCircuitBreaker(string name, TimeSpan openTime); + + void LogSuccess(ICircuitBreaker circuitBreaker); + + void LogFailure(ICircuitBreaker circuitBreaker, int maximumNumberOfFailures, TimeSpan period); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ClassifiedAds.CrossCuttingConcerns.csproj b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ClassifiedAds.CrossCuttingConcerns.csproj new file mode 100644 index 000000000..70a4f36ff --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ClassifiedAds.CrossCuttingConcerns.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + Recommended + All + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Csv/ICsvReader.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Csv/ICsvReader.cs new file mode 100644 index 000000000..6395e7fb9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Csv/ICsvReader.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.IO; + +namespace ClassifiedAds.CrossCuttingConcerns.Csv +{ + public interface ICsvReader + { + IEnumerable Read(Stream stream); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Csv/ICsvWriter.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Csv/ICsvWriter.cs new file mode 100644 index 000000000..56b709123 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Csv/ICsvWriter.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using System.IO; + +namespace ClassifiedAds.CrossCuttingConcerns.Csv +{ + public interface ICsvWriter + { + void Write(IEnumerable collection, Stream stream); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Excel/IExcelReader.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Excel/IExcelReader.cs new file mode 100644 index 000000000..86153236b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Excel/IExcelReader.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace ClassifiedAds.CrossCuttingConcerns.Excel +{ + public interface IExcelReader + { + T Read(Stream stream); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Excel/IExcelWriter.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Excel/IExcelWriter.cs new file mode 100644 index 000000000..0f955d934 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Excel/IExcelWriter.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace ClassifiedAds.CrossCuttingConcerns.Excel +{ + public interface IExcelWriter + { + void Write(T data, Stream stream); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Exceptions/NotFoundException.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Exceptions/NotFoundException.cs new file mode 100644 index 000000000..be8453b67 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Exceptions/NotFoundException.cs @@ -0,0 +1,17 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Exceptions +{ + public class NotFoundException : Exception + { + public NotFoundException() + : base() + { + } + + public NotFoundException(string message) + : base(message) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Exceptions/ValidationException.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Exceptions/ValidationException.cs new file mode 100644 index 000000000..f29861d1d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Exceptions/ValidationException.cs @@ -0,0 +1,25 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Exceptions +{ + public class ValidationException : Exception + { + public static void Requires(bool expected, string errorMessage) + { + if (!expected) + { + throw new ValidationException(errorMessage); + } + } + + public ValidationException(string message) + : base(message) + { + } + + public ValidationException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/CloningExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/CloningExtensions.cs new file mode 100644 index 000000000..4410be804 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/CloningExtensions.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Text.Json; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + /// + /// https://stackoverflow.com/questions/78536/deep-cloning-objects + /// + public static class CloningExtensions + { + public static T ShallowClone(this T source) + { + var methodInfo = typeof(T).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic); + return (T)methodInfo.Invoke(source, null); + } + + /// + /// Perform a deep Copy of the object, using Json as a serialisation method. NOTE: Private members are not cloned using this method. + /// + /// The type of object being copied. + /// The object instance to copy. + /// The copied object. + public static T DeepCloneJson(this T source) + { + // Don't serialize a null object, simply return the default for that object + if (ReferenceEquals(source, null)) + { + return default(T); + } + + return JsonSerializer.Deserialize(JsonSerializer.Serialize(source)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/DateTimeExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/DateTimeExtensions.cs new file mode 100644 index 000000000..e0e4beb54 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/DateTimeExtensions.cs @@ -0,0 +1,17 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class DateTimeExtensions + { + public static DateTime FirstDayOfMonth(this DateTime date) + { + return new DateTime(date.Year, date.Month, 1); + } + + public static DateTime LastDayOfMonth(this DateTime date) + { + return new DateTime(date.Year, date.Month, 1).AddMonths(1).AddDays(-1); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/DecimalExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/DecimalExtensions.cs new file mode 100644 index 000000000..961abde2d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/DecimalExtensions.cs @@ -0,0 +1,15 @@ +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class DecimalExtensions + { + public static decimal Negate(this decimal d) + { + return decimal.Negate(d); + } + + public static decimal? Negate(this decimal? d) + { + return d.HasValue ? decimal.Negate(d.Value) : (decimal?)null; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/GuidExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/GuidExtensions.cs new file mode 100644 index 000000000..ec89aa55a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/GuidExtensions.cs @@ -0,0 +1,12 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class GuidExtensions + { + public static bool IsNullOrEmpty(this Guid? guid) + { + return guid == null || guid == Guid.Empty; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs new file mode 100644 index 000000000..baf05ee67 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpClientExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class HttpClientExtensions + { + public static void UseBasicAuthentication(this HttpClient client, string userName, string password) + { + var byteArray = Encoding.UTF8.GetBytes(userName + ":" + password); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } + + public static void UseBearerToken(this HttpClient client, string token) + { + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpContentExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpContentExtensions.cs new file mode 100644 index 000000000..3b6b4c707 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/HttpContentExtensions.cs @@ -0,0 +1,37 @@ +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class HttpContentExtensions + { + public static async Task ReadAs(this HttpContent httpContent) + { + var json = await httpContent.ReadAsStringAsync(); + + if (string.IsNullOrWhiteSpace(json)) + { + return default; + } + + return JsonSerializer.Deserialize(json, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + }); + } + + public static StringContent AsStringContent(this object obj, string contentType) + { + var content = new StringContent(JsonSerializer.Serialize(obj)); + content.Headers.ContentType = new MediaTypeHeaderValue(contentType); + return content; + } + + public static StringContent AsJsonContent(this object obj) + { + return obj.AsStringContent("application/json"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/IQueryableExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/IQueryableExtensions.cs new file mode 100644 index 000000000..1f88a140e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/IQueryableExtensions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class IQueryableExtensions + { + public static IQueryable Paged(this IQueryable source, int page, int pageSize) + { + return source.Skip((page - 1) * pageSize).Take(pageSize); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ListExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ListExtensions.cs new file mode 100644 index 000000000..8b63b5319 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ListExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class ListExtensions + { + public static List Combines(this List collection1, List collection2) + { + collection1.AddRange(collection2); + return collection1; + } + + public static ICollection Combines(this ICollection collection1, ICollection collection2) + { + var list = new List(); + list.AddRange(collection1); + list.AddRange(collection2); + return list; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs new file mode 100644 index 000000000..ce61cd046 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/ObjectExtensions.cs @@ -0,0 +1,19 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class ObjectExtensions + { + public static string AsJsonString(this object obj) + { + var content = JsonSerializer.Serialize(obj, new JsonSerializerOptions + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + ReferenceHandler = ReferenceHandler.IgnoreCycles, + }); + return content; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/StringExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/StringExtensions.cs new file mode 100644 index 000000000..51c46edd8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/StringExtensions.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class StringExtensions + { + public static string Left(this string value, int length) + { + length = Math.Abs(length); + return string.IsNullOrEmpty(value) ? value : value.Substring(0, Math.Min(value.Length, length)); + } + + public static string Right(this string value, int length) + { + length = Math.Abs(length); + return string.IsNullOrEmpty(value) ? value : value.Substring(value.Length - Math.Min(value.Length, length)); + } + + public static bool In(this string value, List list) + { + return list.Contains(value, StringComparer.OrdinalIgnoreCase); + } + + public static bool NotIn(this string value, List list) + { + return !In(value, list); + } + + public static bool EqualsIgnoreCase(this string source, string toCheck) + { + return string.Equals(source, toCheck, StringComparison.OrdinalIgnoreCase); + } + + public static string ToBase64(this string src) + { + byte[] b = Encoding.UTF8.GetBytes(src); + return Convert.ToBase64String(b); + } + + public static string ToBase64(this string src, Encoding encoding) + { + byte[] b = encoding.GetBytes(src); + return Convert.ToBase64String(b); + } + + public static string FromBase64String(this string src) + { + byte[] b = Convert.FromBase64String(src); + return Encoding.UTF8.GetString(b); + } + + public static string FromBase64String(this string src, Encoding encoding) + { + byte[] b = Convert.FromBase64String(src); + return encoding.GetString(b); + } + + public static string Remove(this string source, params string[] removedValues) + { + removedValues.ToList().ForEach(x => source = source.Replace(x, string.Empty)); + return source; + } + + public static string ToUpperInvariantCulture(this string source) + { + return source.ToUpper(CultureInfo.InvariantCulture); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs new file mode 100644 index 000000000..0e6f3f98a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/ExtensionMethods/TypeExtensions.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Text; + +namespace ClassifiedAds.CrossCuttingConcerns.ExtensionMethods +{ + public static class TypeExtensions + { + public static bool HasInterface(this Type type, Type interfaceType) + { + return type.GetInterfacesOf(interfaceType).Length > 0; + } + + public static Type[] GetInterfacesOf(this Type type, Type interfaceType) + { + return type.FindInterfaces((i, _) => i.GetGenericTypeDefinitionSafe() == interfaceType, null); + } + + public static Type GetGenericTypeDefinitionSafe(this Type type) + { + return type.IsGenericType + ? type.GetGenericTypeDefinition() + : type; + } + + public static Type MakeGenericTypeSafe(this Type type, params Type[] typeArguments) + { + return type.IsGenericType && !type.GenericTypeArguments.Any() + ? type.MakeGenericType(typeArguments) + : type; + } + + public static string GenerateMappingCode(this Type type) + { + var names = type.GetProperties().Select(x => x.Name); + + var text1 = new StringBuilder(); + var text2 = new StringBuilder(); + var text3 = new StringBuilder(); + var text4 = new StringBuilder(); + + foreach (var name in names) + { + text1.Append($"a.{name} = {name};{Environment.NewLine}"); + text2.Append($"{name} = b.{name};{Environment.NewLine}"); + text3.Append($"{name} = b.{name},{Environment.NewLine}"); + text4.Append($"a.{name} = b.{name};{Environment.NewLine}"); + } + + return text1.ToString() + + "--------------------------------------" + Environment.NewLine + + text2.ToString() + + "--------------------------------------" + Environment.NewLine + + text3.ToString() + + "--------------------------------------" + Environment.NewLine + + text4.ToString(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/HtmlGenerator/IHtmlGenerator.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/HtmlGenerator/IHtmlGenerator.cs new file mode 100644 index 000000000..94670be95 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/HtmlGenerator/IHtmlGenerator.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace ClassifiedAds.CrossCuttingConcerns.HtmlGenerator +{ + public interface IHtmlGenerator + { + Task GenerateAsync(string template, object model); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/CouldNotAcquireLockException.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/CouldNotAcquireLockException.cs new file mode 100644 index 000000000..7f60533bb --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/CouldNotAcquireLockException.cs @@ -0,0 +1,21 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Locks +{ + public class CouldNotAcquireLockException : Exception + { + public CouldNotAcquireLockException() + { + } + + public CouldNotAcquireLockException(string message) + : base(message) + { + } + + public CouldNotAcquireLockException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/IDistributedLock.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/IDistributedLock.cs new file mode 100644 index 000000000..f54333dca --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/IDistributedLock.cs @@ -0,0 +1,11 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Locks +{ + public interface IDistributedLock : IDisposable + { + IDistributedLockScope Acquire(string lockName); + + IDistributedLockScope TryAcquire(string lockName); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/IDistributedLockScope.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/IDistributedLockScope.cs new file mode 100644 index 000000000..11f258a00 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/IDistributedLockScope.cs @@ -0,0 +1,9 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Locks +{ + public interface IDistributedLockScope : IDisposable + { + bool StillHoldingLock(); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/ILockManager.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/ILockManager.cs new file mode 100644 index 000000000..1fd92e1ed --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Locks/ILockManager.cs @@ -0,0 +1,19 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.Locks +{ + public interface ILockManager + { + bool AcquireLock(string entityName, string entityId, string ownerId, TimeSpan expirationIn); + + bool ExtendLock(string entityName, string entityId, string ownerId, TimeSpan expirationIn); + + bool ReleaseLock(string entityName, string entityId, string ownerId); + + bool ReleaseLocks(string ownerId); + + bool ReleaseExpiredLocks(); + + void EnsureAcquiringLock(string entityName, string entityId, string ownerId, TimeSpan expirationIn); + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Logging/ILogger.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Logging/ILogger.cs new file mode 100644 index 000000000..96e76cff9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Logging/ILogger.cs @@ -0,0 +1,6 @@ +namespace ClassifiedAds.CrossCuttingConcerns.Logging +{ + public interface ILogger + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/OS/IDateTimeProvider.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/OS/IDateTimeProvider.cs new file mode 100644 index 000000000..6db527e4b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/OS/IDateTimeProvider.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.CrossCuttingConcerns.OS +{ + public interface IDateTimeProvider + { + DateTime Now { get; } + + DateTime UtcNow { get; } + + DateTimeOffset OffsetNow { get; } + + DateTimeOffset OffsetUtcNow { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/PdfConverter/IPdfConverter.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/PdfConverter/IPdfConverter.cs new file mode 100644 index 000000000..e26b6de57 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/PdfConverter/IPdfConverter.cs @@ -0,0 +1,16 @@ +using System.IO; +using System.Threading.Tasks; + +namespace ClassifiedAds.CrossCuttingConcerns.PdfConverter +{ + public interface IPdfConverter + { + Stream Convert(string html, PdfOptions pdfOptions = null); + + Task ConvertAsync(string html, PdfOptions pdfOptions = null); + } + + public class PdfOptions + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Tenants/IConnectionStringResolver.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Tenants/IConnectionStringResolver.cs new file mode 100644 index 000000000..0694486c3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Tenants/IConnectionStringResolver.cs @@ -0,0 +1,9 @@ +namespace ClassifiedAds.CrossCuttingConcerns.Tenants +{ + public interface IConnectionStringResolver + { + string ConnectionString { get; } + + string MigrationsAssembly { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Tenants/ITenantResolver.cs b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Tenants/ITenantResolver.cs new file mode 100644 index 000000000..45561b0fc --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.CrossCuttingConcerns/Tenants/ITenantResolver.cs @@ -0,0 +1,14 @@ +namespace ClassifiedAds.CrossCuttingConcerns.Tenants +{ + public interface ITenantResolver + { + Tenant Tenant { get; } + } + + public class Tenant + { + public string Id { get; } + public string Name { get; } + public string ConnectionString { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/.editorconfig b/src/IdentityServer/ClassifiedAds.Domain/.editorconfig new file mode 100644 index 000000000..43d5b5702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/.editorconfig @@ -0,0 +1,224 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +# insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = false +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + +############################### +# StyleCop # +############################### +[*.cs] + +# SA1027: Use tabs correctly +dotnet_diagnostic.SA1027.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +dotnet_diagnostic.SA1111.severity = none + +# SA1113: Comma should be on the same line as previous parameter +dotnet_diagnostic.SA1113.severity = none + +# SA1115: Parameter should follow comma +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1208: System using directives should be placed before other using directives +dotnet_diagnostic.SA1208.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +dotnet_diagnostic.SA1310.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# SA1610: Property documentation should have value text +dotnet_diagnostic.SA1610.severity = none + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = suggestion diff --git a/src/IdentityServer/ClassifiedAds.Domain/ClassifiedAds.Domain.csproj b/src/IdentityServer/ClassifiedAds.Domain/ClassifiedAds.Domain.csproj new file mode 100644 index 000000000..74cf72bdd --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/ClassifiedAds.Domain.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + Recommended + All + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/IdentityServer/ClassifiedAds.Domain/DomainServicesCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Domain/DomainServicesCollectionExtensions.cs new file mode 100644 index 000000000..965e0d758 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/DomainServicesCollectionExtensions.cs @@ -0,0 +1,13 @@ +using ClassifiedAds.Domain.Services; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class DomainServicesCollectionExtensions + { + public static IServiceCollection AddDomainServices(this IServiceCollection services) + { + services.AddScoped(); + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/AuditLogEntry.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/AuditLogEntry.cs new file mode 100644 index 000000000..6810010cb --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/AuditLogEntry.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class AuditLogEntry : Entity, IAggregateRoot + { + public Guid UserId { get; set; } + + public string Action { get; set; } + + public string ObjectId { get; set; } + + public string Log { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/ConfigurationEntry.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/ConfigurationEntry.cs new file mode 100644 index 000000000..2e72408af --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/ConfigurationEntry.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class ConfigurationEntry : Entity, IAggregateRoot + { + public string Key { get; set; } + + public string Value { get; set; } + + public string Description { get; set; } + + public bool IsSensitive { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/CustomMigrationHistory.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/CustomMigrationHistory.cs new file mode 100644 index 000000000..ca6c124cb --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/CustomMigrationHistory.cs @@ -0,0 +1,9 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class CustomMigrationHistory : Entity + { + public string MigrationName { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/EmailMessage.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/EmailMessage.cs new file mode 100644 index 000000000..4135bb5f8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/EmailMessage.cs @@ -0,0 +1,37 @@ +using ClassifiedAds.Domain.Notification; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Domain.Entities +{ + public class EmailMessage : Entity, IAggregateRoot, IEmailMessage + { + public string From { get; set; } + + public string Tos { get; set; } + + public string CCs { get; set; } + + public string BCCs { get; set; } + + public string Subject { get; set; } + + public string Body { get; set; } + + public int AttemptCount { get; set; } + + public int MaxAttemptCount { get; set; } + + public DateTimeOffset? NextAttemptDateTime { get; set; } + + public DateTimeOffset? ExpiredDateTime { get; set; } + + public string Log { get; set; } + + public DateTimeOffset? SentDateTime { get; set; } + + public Guid? CopyFromId { get; set; } + + public ICollection EmailMessageAttachments { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/EmailMessageAttachment.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/EmailMessageAttachment.cs new file mode 100644 index 000000000..de5f5f9a9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/EmailMessageAttachment.cs @@ -0,0 +1,17 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class EmailMessageAttachment : Entity + { + public Guid EmailMessageId { get; set; } + + public Guid FileEntryId { get; set; } + + public string Name { get; set; } + + public EmailMessage EmailMessage { get; set; } + + public FileEntry FileEntry { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/Entity.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/Entity.cs new file mode 100644 index 000000000..3fa5c1bc6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/Entity.cs @@ -0,0 +1,17 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace ClassifiedAds.Domain.Entities +{ + public abstract class Entity : IHasKey, ITrackable + { + public TKey Id { get; set; } + + [Timestamp] + public byte[] RowVersion { get; set; } + + public DateTimeOffset CreatedDateTime { get; set; } + + public DateTimeOffset? UpdatedDateTime { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/FileEntry.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/FileEntry.cs new file mode 100644 index 000000000..0d090f483 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/FileEntry.cs @@ -0,0 +1,26 @@ +using ClassifiedAds.Domain.Infrastructure.Storages; +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class FileEntry : Entity, IAggregateRoot, IFileEntry + { + public string Name { get; set; } + + public string Description { get; set; } + + public long Size { get; set; } + + public DateTimeOffset UploadedTime { get; set; } + + public string FileName { get; set; } + + public string FileLocation { get; set; } + + public bool Encrypted { get; set; } + + public string EncryptionKey { get; set; } + + public string EncryptionIV { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/IAggregateRoot.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/IAggregateRoot.cs new file mode 100644 index 000000000..ca07e790d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/IAggregateRoot.cs @@ -0,0 +1,6 @@ +namespace ClassifiedAds.Domain.Entities +{ + public interface IAggregateRoot + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/IHasKey.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/IHasKey.cs new file mode 100644 index 000000000..dca9d6566 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/IHasKey.cs @@ -0,0 +1,7 @@ +namespace ClassifiedAds.Domain.Entities +{ + public interface IHasKey + { + T Id { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/ITrackable.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/ITrackable.cs new file mode 100644 index 000000000..e44ac0f44 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/ITrackable.cs @@ -0,0 +1,13 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public interface ITrackable + { + byte[] RowVersion { get; set; } + + DateTimeOffset CreatedDateTime { get; set; } + + DateTimeOffset? UpdatedDateTime { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/LocalizationEntry.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/LocalizationEntry.cs new file mode 100644 index 000000000..b74a69eb5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/LocalizationEntry.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class LocalizationEntry : Entity, IAggregateRoot + { + public string Name { get; set; } + + public string Value { get; set; } + + public string Culture { get; set; } + + public string Description { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/Lock.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/Lock.cs new file mode 100644 index 000000000..3b5ccf66e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/Lock.cs @@ -0,0 +1,17 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class Lock + { + public string EntityId { get; set; } + + public string EntityName { get; set; } + + public string OwnerId { get; set; } + + public DateTimeOffset? AcquiredDateTime { get; set; } + + public DateTimeOffset? ExpiredDateTime { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/OutboxEvent.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/OutboxEvent.cs new file mode 100644 index 000000000..30b3ee454 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/OutboxEvent.cs @@ -0,0 +1,17 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class OutboxEvent : Entity, IAggregateRoot + { + public string EventType { get; set; } + + public Guid TriggeredById { get; set; } + + public string ObjectId { get; set; } + + public string Message { get; set; } + + public bool Published { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/PasswordHistory.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/PasswordHistory.cs new file mode 100644 index 000000000..b22ea7112 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/PasswordHistory.cs @@ -0,0 +1,13 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class PasswordHistory : Entity + { + public Guid UserId { get; set; } + + public string PasswordHash { get; set; } + + public virtual User User { get; set; } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/Product.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/Product.cs new file mode 100644 index 000000000..e903a35cd --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/Product.cs @@ -0,0 +1,13 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class Product : Entity, IAggregateRoot + { + public string Code { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/Role.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/Role.cs new file mode 100644 index 000000000..8aea58cec --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/Role.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Domain.Entities +{ + public class Role : Entity, IAggregateRoot + { + public virtual string Name { get; set; } + + public virtual string NormalizedName { get; set; } + + public virtual string ConcurrencyStamp { get; set; } + + public IList Claims { get; set; } + + public IList UserRoles { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/RoleClaim.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/RoleClaim.cs new file mode 100644 index 000000000..aff2cb420 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/RoleClaim.cs @@ -0,0 +1,12 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class RoleClaim : Entity + { + public string Type { get; set; } + public string Value { get; set; } + + public Role Role { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/SmsMessage.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/SmsMessage.cs new file mode 100644 index 000000000..277bec5fa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/SmsMessage.cs @@ -0,0 +1,26 @@ +using ClassifiedAds.Domain.Notification; +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class SmsMessage : Entity, IAggregateRoot, ISmsMessage + { + public string Message { get; set; } + + public string PhoneNumber { get; set; } + + public int AttemptCount { get; set; } + + public int MaxAttemptCount { get; set; } + + public DateTimeOffset? NextAttemptDateTime { get; set; } + + public DateTimeOffset? ExpiredDateTime { get; set; } + + public string Log { get; set; } + + public DateTimeOffset? SentDateTime { get; set; } + + public Guid? CopyFromId { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/User.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/User.cs new file mode 100644 index 000000000..11d8ea3d9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/User.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Domain.Entities +{ + public class User : Entity, IAggregateRoot + { + public string UserName { get; set; } + + public string NormalizedUserName { get; set; } + + public string Email { get; set; } + + public string NormalizedEmail { get; set; } + + public bool EmailConfirmed { get; set; } + + public string PasswordHash { get; set; } + + public string PhoneNumber { get; set; } + + public bool PhoneNumberConfirmed { get; set; } + + public bool TwoFactorEnabled { get; set; } + + public string ConcurrencyStamp { get; set; } + + public string SecurityStamp { get; set; } + + public bool LockoutEnabled { get; set; } + + public DateTimeOffset? LockoutEnd { get; set; } + + public int AccessFailedCount { get; set; } + + public IList Tokens { get; set; } + + public IList Claims { get; set; } + + public IList UserRoles { get; set; } + + public IList PasswordHistories { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/UserClaim.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/UserClaim.cs new file mode 100644 index 000000000..86f595b87 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/UserClaim.cs @@ -0,0 +1,12 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class UserClaim : Entity + { + public string Type { get; set; } + public string Value { get; set; } + + public User User { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/UserRole.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/UserRole.cs new file mode 100644 index 000000000..fb810607b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/UserRole.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class UserRole : Entity + { + public Guid UserId { get; set; } + + public Guid RoleId { get; set; } + + public User User { get; set; } + + public Role Role { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Entities/UserToken.cs b/src/IdentityServer/ClassifiedAds.Domain/Entities/UserToken.cs new file mode 100644 index 000000000..9f2337439 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Entities/UserToken.cs @@ -0,0 +1,15 @@ +using System; + +namespace ClassifiedAds.Domain.Entities +{ + public class UserToken : Entity + { + public Guid UserId { get; set; } + + public string LoginProvider { get; set; } + + public string TokenName { get; set; } + + public string TokenValue { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/DomainEvents.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/DomainEvents.cs new file mode 100644 index 000000000..f785ad8e3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/DomainEvents.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Events +{ + public class DomainEvents : IDomainEvents + { + private static List _handlers = new List(); + private IServiceProvider _serviceProvider; + + public static void RegisterHandlers(Assembly assembly, IServiceCollection services) + { + var types = assembly.GetTypes() + .Where(x => x.GetInterfaces().Any(y => y.IsGenericType && y.GetGenericTypeDefinition() == typeof(IDomainEventHandler<>))) + .ToList(); + + foreach (var type in types) + { + services.AddTransient(type); + } + + _handlers.AddRange(types); + } + + public DomainEvents(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public async Task DispatchAsync(IDomainEvent domainEvent, CancellationToken cancellationToken = default) + { + foreach (Type handlerType in _handlers) + { + bool canHandleEvent = handlerType.GetInterfaces() + .Any(x => x.IsGenericType + && x.GetGenericTypeDefinition() == typeof(IDomainEventHandler<>) + && x.GenericTypeArguments[0] == domainEvent.GetType()); + + if (canHandleEvent) + { + dynamic handler = _serviceProvider.GetService(handlerType); + await handler.HandleAsync((dynamic)domainEvent, cancellationToken); + } + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/EntityCreatedEvent.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/EntityCreatedEvent.cs new file mode 100644 index 000000000..dac4362d4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/EntityCreatedEvent.cs @@ -0,0 +1,19 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.Domain.Events +{ + public class EntityCreatedEvent : IDomainEvent + where T : Entity + { + public EntityCreatedEvent(T entity, DateTime eventDateTime) + { + Entity = entity; + EventDateTime = eventDateTime; + } + + public T Entity { get; } + + public DateTime EventDateTime { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/EntityDeletedEvent.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/EntityDeletedEvent.cs new file mode 100644 index 000000000..f18210580 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/EntityDeletedEvent.cs @@ -0,0 +1,19 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.Domain.Events +{ + public class EntityDeletedEvent : IDomainEvent + where T : Entity + { + public EntityDeletedEvent(T entity, DateTime eventDateTime) + { + Entity = entity; + EventDateTime = eventDateTime; + } + + public T Entity { get; } + + public DateTime EventDateTime { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/EntityUpdatedEvent.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/EntityUpdatedEvent.cs new file mode 100644 index 000000000..c99d8482e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/EntityUpdatedEvent.cs @@ -0,0 +1,19 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.Domain.Events +{ + public class EntityUpdatedEvent : IDomainEvent + where T : Entity + { + public EntityUpdatedEvent(T entity, DateTime eventDateTime) + { + Entity = entity; + EventDateTime = eventDateTime; + } + + public T Entity { get; } + + public DateTime EventDateTime { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEvent.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEvent.cs new file mode 100644 index 000000000..70f75ff3a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEvent.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ClassifiedAds.Domain.Events +{ + public interface IDomainEvent + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEventHandler.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEventHandler.cs new file mode 100644 index 000000000..1f9936290 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEventHandler.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Events +{ + public interface IDomainEventHandler + where T : IDomainEvent + { + Task HandleAsync(T domainEvent, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEvents.cs b/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEvents.cs new file mode 100644 index 000000000..38bcf6a9b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Events/IDomainEvents.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Events +{ + public interface IDomainEvents + { + Task DispatchAsync(IDomainEvent domainEvent, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Identity/ICurrentUser.cs b/src/IdentityServer/ClassifiedAds.Domain/Identity/ICurrentUser.cs new file mode 100644 index 000000000..6a6f3bcfa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Identity/ICurrentUser.cs @@ -0,0 +1,11 @@ +using System; + +namespace ClassifiedAds.Domain.Identity +{ + public interface ICurrentUser + { + bool IsAuthenticated { get; } + + Guid UserId { get; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Identity/IPasswordHasher.cs b/src/IdentityServer/ClassifiedAds.Domain/Identity/IPasswordHasher.cs new file mode 100644 index 000000000..cc06dc03a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Identity/IPasswordHasher.cs @@ -0,0 +1,9 @@ +using ClassifiedAds.Domain.Entities; + +namespace ClassifiedAds.Domain.Identity +{ + public interface IPasswordHasher + { + bool VerifyHashedPassword(User user, string hashedPassword, string providedPassword); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/IMessageReceiver.cs b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/IMessageReceiver.cs new file mode 100644 index 000000000..83e3a937b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/IMessageReceiver.cs @@ -0,0 +1,9 @@ +using System; + +namespace ClassifiedAds.Domain.Infrastructure.MessageBrokers +{ + public interface IMessageReceiver + { + void Receive(Action action); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/IMessageSender.cs b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/IMessageSender.cs new file mode 100644 index 000000000..899691d11 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/IMessageSender.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Infrastructure.MessageBrokers +{ + public interface IMessageSender + { + Task SendAsync(T message, MetaData metaData = null, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/Message.cs b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/Message.cs new file mode 100644 index 000000000..33f456c0c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/Message.cs @@ -0,0 +1,22 @@ +using System.Text; +using System.Text.Json; + +namespace ClassifiedAds.Domain.Infrastructure.MessageBrokers +{ + public class Message + { + public MetaData MetaData { get; set; } + + public T Data { get; set; } + + public string SerializeObject() + { + return JsonSerializer.Serialize(this); + } + + public byte[] GetBytes() + { + return Encoding.UTF8.GetBytes(SerializeObject()); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/MetaData.cs b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/MetaData.cs new file mode 100644 index 000000000..d798c6a5b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/MessageBrokers/MetaData.cs @@ -0,0 +1,19 @@ +using System; + +namespace ClassifiedAds.Domain.Infrastructure.MessageBrokers +{ + public class MetaData + { + public string MessageId { get; set; } + + public string MessageVersion { get; set; } + + public string CorrelationId { get; set; } + + public DateTimeOffset? CreationDateTime { get; set; } + + public DateTimeOffset? EnqueuedDateTime { get; set; } + + public string AccessToken { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/Networking/IFileDownloader.cs b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/Networking/IFileDownloader.cs new file mode 100644 index 000000000..69eb0bfe4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/Networking/IFileDownloader.cs @@ -0,0 +1,12 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Infrastructure.Networking +{ + public interface IFileDownloader + { + void DownloadFile(string url, string path); + + Task DownloadFileAsync(string url, string path, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/Storages/IFileStorageManager.cs b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/Storages/IFileStorageManager.cs new file mode 100644 index 000000000..4e68db615 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Infrastructure/Storages/IFileStorageManager.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Infrastructure.Storages +{ + public interface IFileStorageManager + { + Task CreateAsync(IFileEntry fileEntry, Stream stream, CancellationToken cancellationToken = default); + + Task ReadAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default); + + Task DeleteAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default); + + Task ArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default); + + Task UnArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default); + } + + public interface IFileEntry + { + public Guid Id { get; set; } + + public string FileName { get; set; } + + public string FileLocation { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Notification/IEmailNotification.cs b/src/IdentityServer/ClassifiedAds.Domain/Notification/IEmailNotification.cs new file mode 100644 index 000000000..f729dec9d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Notification/IEmailNotification.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Notification +{ + public interface IEmailNotification + { + Task SendAsync(IEmailMessage emailMessage, CancellationToken cancellationToken = default); + } + + public interface IEmailMessage + { + public string From { get; set; } + + public string Tos { get; set; } + + public string CCs { get; set; } + + public string BCCs { get; set; } + + public string Subject { get; set; } + + public string Body { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Notification/ISmsNotification.cs b/src/IdentityServer/ClassifiedAds.Domain/Notification/ISmsNotification.cs new file mode 100644 index 000000000..ac2a700ec --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Notification/ISmsNotification.cs @@ -0,0 +1,17 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Notification +{ + public interface ISmsNotification + { + Task SendAsync(ISmsMessage smsMessage, CancellationToken cancellationToken = default); + } + + public interface ISmsMessage + { + public string Message { get; set; } + + public string PhoneNumber { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Notification/IWebNotification.cs b/src/IdentityServer/ClassifiedAds.Domain/Notification/IWebNotification.cs new file mode 100644 index 000000000..157d614a4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Notification/IWebNotification.cs @@ -0,0 +1,10 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Notification +{ + public interface IWebNotification + { + Task SendAsync(T message, CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IAuditLogEntryRepository.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IAuditLogEntryRepository.cs new file mode 100644 index 000000000..8098145fa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IAuditLogEntryRepository.cs @@ -0,0 +1,20 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Linq; + +namespace ClassifiedAds.Domain.Repositories +{ + public class AuditLogEntryQueryOptions + { + public Guid UserId { get; set; } + + public string ObjectId { get; set; } + + public bool AsNoTracking { get; set; } + } + + public interface IAuditLogEntryRepository : IRepository + { + IQueryable Get(AuditLogEntryQueryOptions queryOptions); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IConcurrencyHandler.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IConcurrencyHandler.cs new file mode 100644 index 000000000..f3eac4bfc --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IConcurrencyHandler.cs @@ -0,0 +1,11 @@ +using System; + +namespace ClassifiedAds.Domain.Repositories +{ + public interface IConcurrencyHandler + { + void SetRowVersion(TEntity entity, byte[] version); + + bool IsDbUpdateConcurrencyException(Exception ex); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IEmailMessageRepository.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IEmailMessageRepository.cs new file mode 100644 index 000000000..9e363587c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IEmailMessageRepository.cs @@ -0,0 +1,9 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.Domain.Repositories +{ + public interface IEmailMessageRepository : IRepository + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IRepository.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IRepository.cs new file mode 100644 index 000000000..22eea4138 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IRepository.cs @@ -0,0 +1,42 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Repositories +{ + public interface IRepository : IConcurrencyHandler + where TEntity : Entity, IAggregateRoot + { + IUnitOfWork UnitOfWork { get; } + + IQueryable GetAll(); + + Task AddOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + + Task AddAsync(TEntity entity, CancellationToken cancellationToken = default); + + Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + + void Delete(TEntity entity); + + Task FirstOrDefaultAsync(IQueryable query); + + Task SingleOrDefaultAsync(IQueryable query); + + Task> ToListAsync(IQueryable query); + + void BulkInsert(IEnumerable entities); + + void BulkInsert(IEnumerable entities, Expression> columnNamesSelector); + + void BulkUpdate(IEnumerable entities, Expression> columnNamesSelector); + + void BulkMerge(IEnumerable entities, Expression> idSelector, Expression> updateColumnNamesSelector, Expression> insertColumnNamesSelector); + + void BulkDelete(IEnumerable entities); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IRoleRepository.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IRoleRepository.cs new file mode 100644 index 000000000..3150db1d6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IRoleRepository.cs @@ -0,0 +1,19 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Linq; + +namespace ClassifiedAds.Domain.Repositories +{ + public class RoleQueryOptions + { + public bool IncludeClaims { get; set; } + public bool IncludeUserRoles { get; set; } + public bool IncludeUsers { get; set; } + public bool AsNoTracking { get; set; } + } + + public interface IRoleRepository : IRepository + { + IQueryable Get(RoleQueryOptions queryOptions); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/ISmsMessageRepository.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/ISmsMessageRepository.cs new file mode 100644 index 000000000..4d26b1d54 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/ISmsMessageRepository.cs @@ -0,0 +1,9 @@ +using ClassifiedAds.Domain.Entities; +using System; + +namespace ClassifiedAds.Domain.Repositories +{ + public interface ISmsMessageRepository : IRepository + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IUnitOfWork.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IUnitOfWork.cs new file mode 100644 index 000000000..311f436cf --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IUnitOfWork.cs @@ -0,0 +1,18 @@ +using System; +using System.Data; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Domain.Repositories +{ + public interface IUnitOfWork + { + Task SaveChangesAsync(CancellationToken cancellationToken = default); + + Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, CancellationToken cancellationToken = default); + + Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, string lockName = null, CancellationToken cancellationToken = default); + + Task CommitTransactionAsync(CancellationToken cancellationToken = default); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Repositories/IUserRepository.cs b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IUserRepository.cs new file mode 100644 index 000000000..dfcccd30f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Repositories/IUserRepository.cs @@ -0,0 +1,21 @@ +using ClassifiedAds.Domain.Entities; +using System; +using System.Linq; + +namespace ClassifiedAds.Domain.Repositories +{ + public class UserQueryOptions + { + public bool IncludePasswordHistories { get; set; } + public bool IncludeClaims { get; set; } + public bool IncludeUserRoles { get; set; } + public bool IncludeRoles { get; set; } + public bool IncludeTokens { get; set; } + public bool AsNoTracking { get; set; } + } + + public interface IUserRepository : IRepository + { + IQueryable Get(UserQueryOptions queryOptions); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Services/ProductService.cs b/src/IdentityServer/ClassifiedAds.Domain/Services/ProductService.cs new file mode 100644 index 000000000..37f85c710 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Services/ProductService.cs @@ -0,0 +1,6 @@ +namespace ClassifiedAds.Domain.Services +{ + public class ProductService + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/Specifications/Specification.cs b/src/IdentityServer/ClassifiedAds.Domain/Specifications/Specification.cs new file mode 100644 index 000000000..c79ca7372 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/Specifications/Specification.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace ClassifiedAds.Domain.Specifications +{ + internal sealed class IdentitySpecification : Specification + { + public override Expression> ToExpression() + { + return x => true; + } + } + + public abstract class Specification + { + public static readonly Specification All = new IdentitySpecification(); + + public bool IsSatisfiedBy(T entity) + { + Func predicate = ToExpression().Compile(); + return predicate(entity); + } + + public abstract Expression> ToExpression(); + + public Specification And(Specification specification) + { + if (this == All) + { + return specification; + } + + if (specification == All) + { + return this; + } + + return new AndSpecification(this, specification); + } + + public Specification Or(Specification specification) + { + if (this == All || specification == All) + { + return All; + } + + return new OrSpecification(this, specification); + } + + public Specification Not() + { + return new NotSpecification(this); + } + } + + internal sealed class AndSpecification : Specification + { + private readonly Specification _left; + private readonly Specification _right; + + public AndSpecification(Specification left, Specification right) + { + _left = left; + _right = right; + } + + public override Expression> ToExpression() + { + Expression> leftExpression = _left.ToExpression(); + Expression> rightExpression = _right.ToExpression(); + + BinaryExpression andExpression = Expression.AndAlso(leftExpression.Body, rightExpression.Body); + + return Expression.Lambda>(andExpression, leftExpression.Parameters.Single()); + } + } + + internal sealed class OrSpecification : Specification + { + private readonly Specification _left; + private readonly Specification _right; + + public OrSpecification(Specification left, Specification right) + { + _left = left; + _right = right; + } + + public override Expression> ToExpression() + { + Expression> leftExpression = _left.ToExpression(); + Expression> rightExpression = _right.ToExpression(); + + BinaryExpression orExpression = Expression.OrElse(leftExpression.Body, rightExpression.Body); + + return Expression.Lambda>(orExpression, leftExpression.Parameters.Single()); + } + } + + internal sealed class NotSpecification : Specification + { + private readonly Specification _specification; + + public NotSpecification(Specification specification) + { + _specification = specification; + } + + public override Expression> ToExpression() + { + Expression> expression = _specification.ToExpression(); + UnaryExpression notExpression = Expression.Not(expression.Body); + + return Expression.Lambda>(notExpression, expression.Parameters.Single()); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/Address.cs b/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/Address.cs new file mode 100644 index 000000000..85e292d49 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/Address.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Domain.ValueObjects +{ + public class Address : ValueObject + { + public string Street { get; private set; } + public string City { get; private set; } + public string ZipCode { get; private set; } + + private Address() + { + } + + public Address(string street, string city, string zipCode) + { + Street = street; + City = city; + ZipCode = zipCode; + } + + protected override IEnumerable GetEqualityComponents() + { + yield return Street; + yield return City; + yield return ZipCode; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/Money.cs b/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/Money.cs new file mode 100644 index 000000000..c27e775fd --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/Money.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Domain.ValueObjects +{ + public class Money : ValueObject + { + public decimal Amount { get; } + + public string Currency { get; } + + private Money() + { + } + + public Money(decimal amount, string currency) + { + Amount = amount; + Currency = currency; + } + + protected override IEnumerable GetEqualityComponents() + { + yield return Amount; + yield return Currency; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/ValueObject.cs b/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/ValueObject.cs new file mode 100644 index 000000000..ab8ec5393 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Domain/ValueObjects/ValueObject.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.Domain.ValueObjects +{ + public abstract class ValueObject + { + protected abstract IEnumerable GetEqualityComponents(); + + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + + if (GetType() != obj.GetType()) + { + throw new ArgumentException($"Invalid comparison of Value Objects of different types: {GetType()} and {obj.GetType()}"); + } + + var valueObject = (ValueObject)obj; + + return GetEqualityComponents().SequenceEqual(valueObject.GetEqualityComponents()); + } + + public override int GetHashCode() + { + return GetEqualityComponents() + .Aggregate(1, (current, obj) => + { + unchecked + { + return (current * 23) + (obj?.GetHashCode() ?? 0); + } + }); + } + + public static bool operator ==(ValueObject a, ValueObject b) + { + if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) + { + return true; + } + + if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) + { + return false; + } + + return a.Equals(b); + } + + public static bool operator !=(ValueObject a, ValueObject b) + { + return !(a == b); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.IdentityServer.sln b/src/IdentityServer/ClassifiedAds.IdentityServer.sln new file mode 100644 index 000000000..5b76cade2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.IdentityServer.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33122.133 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.IdentityServer", "ClassifiedAds.IdentityServer\ClassifiedAds.IdentityServer.csproj", "{EEC55F91-2586-449B-8EFE-E2E8BF4E8423}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Domain", "ClassifiedAds.Domain\ClassifiedAds.Domain.csproj", "{DBD8C790-85B1-4187-B5C2-F46622E2392C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.CrossCuttingConcerns", "ClassifiedAds.CrossCuttingConcerns\ClassifiedAds.CrossCuttingConcerns.csproj", "{D050C6F0-B301-408D-A8DA-41C180B0D134}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Application", "ClassifiedAds.Application\ClassifiedAds.Application.csproj", "{1F4FBEC9-2AF5-408D-9DA4-BBE687132369}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Infrastructure", "ClassifiedAds.Infrastructure\ClassifiedAds.Infrastructure.csproj", "{04C1021F-3734-48F1-96BA-FEFE7AC08C3A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Persistence", "ClassifiedAds.Persistence\ClassifiedAds.Persistence.csproj", "{6F6B4208-3B1E-4B9A-B02B-401D9C03C014}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Migrator", "ClassifiedAds.Migrator\ClassifiedAds.Migrator.csproj", "{BF9210E2-EA74-4B01-A5E5-A7A87894FD5F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EEC55F91-2586-449B-8EFE-E2E8BF4E8423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEC55F91-2586-449B-8EFE-E2E8BF4E8423}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEC55F91-2586-449B-8EFE-E2E8BF4E8423}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEC55F91-2586-449B-8EFE-E2E8BF4E8423}.Release|Any CPU.Build.0 = Release|Any CPU + {DBD8C790-85B1-4187-B5C2-F46622E2392C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBD8C790-85B1-4187-B5C2-F46622E2392C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBD8C790-85B1-4187-B5C2-F46622E2392C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBD8C790-85B1-4187-B5C2-F46622E2392C}.Release|Any CPU.Build.0 = Release|Any CPU + {D050C6F0-B301-408D-A8DA-41C180B0D134}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D050C6F0-B301-408D-A8DA-41C180B0D134}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D050C6F0-B301-408D-A8DA-41C180B0D134}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D050C6F0-B301-408D-A8DA-41C180B0D134}.Release|Any CPU.Build.0 = Release|Any CPU + {1F4FBEC9-2AF5-408D-9DA4-BBE687132369}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F4FBEC9-2AF5-408D-9DA4-BBE687132369}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F4FBEC9-2AF5-408D-9DA4-BBE687132369}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F4FBEC9-2AF5-408D-9DA4-BBE687132369}.Release|Any CPU.Build.0 = Release|Any CPU + {04C1021F-3734-48F1-96BA-FEFE7AC08C3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04C1021F-3734-48F1-96BA-FEFE7AC08C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04C1021F-3734-48F1-96BA-FEFE7AC08C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04C1021F-3734-48F1-96BA-FEFE7AC08C3A}.Release|Any CPU.Build.0 = Release|Any CPU + {6F6B4208-3B1E-4B9A-B02B-401D9C03C014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F6B4208-3B1E-4B9A-B02B-401D9C03C014}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F6B4208-3B1E-4B9A-B02B-401D9C03C014}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F6B4208-3B1E-4B9A-B02B-401D9C03C014}.Release|Any CPU.Build.0 = Release|Any CPU + {BF9210E2-EA74-4B01-A5E5-A7A87894FD5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF9210E2-EA74-4B01-A5E5-A7A87894FD5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF9210E2-EA74-4B01-A5E5-A7A87894FD5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF9210E2-EA74-4B01-A5E5-A7A87894FD5F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B636E435-8EE7-4305-8C16-A3FB2D0D9358} + EndGlobalSection +EndGlobal diff --git a/src/IdentityServer/ClassifiedAds.IdentityServer/.editorconfig b/src/IdentityServer/ClassifiedAds.IdentityServer/.editorconfig new file mode 100644 index 000000000..43d5b5702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.IdentityServer/.editorconfig @@ -0,0 +1,224 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +# insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = false +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + +############################### +# StyleCop # +############################### +[*.cs] + +# SA1027: Use tabs correctly +dotnet_diagnostic.SA1027.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +dotnet_diagnostic.SA1111.severity = none + +# SA1113: Comma should be on the same line as previous parameter +dotnet_diagnostic.SA1113.severity = none + +# SA1115: Parameter should follow comma +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1208: System using directives should be placed before other using directives +dotnet_diagnostic.SA1208.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +dotnet_diagnostic.SA1310.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# SA1610: Property documentation should have value text +dotnet_diagnostic.SA1610.severity = none + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = suggestion diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/IdentityHostingStartup.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/IdentityHostingStartup.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/IdentityHostingStartup.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/IdentityHostingStartup.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/AccessDenied.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/AccessDenied.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Lockout.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Lockout.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Login.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Login.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Logout.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Logout.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Email.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Email.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Register.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Register.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/_ViewImports.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_ViewImports.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/_ViewImports.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_ViewImports.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Error.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Error.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Error.cshtml.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Error.cshtml.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ViewStart.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewStart.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ViewStart.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewStart.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Certs/CreateSelfSignedCertificate.ps1 b/src/IdentityServer/ClassifiedAds.IdentityServer/Certs/CreateSelfSignedCertificate.ps1 similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Certs/CreateSelfSignedCertificate.ps1 rename to src/IdentityServer/ClassifiedAds.IdentityServer/Certs/CreateSelfSignedCertificate.ps1 diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Certs/classifiedads.identityserver.pfx b/src/IdentityServer/ClassifiedAds.IdentityServer/Certs/classifiedads.identityserver.pfx similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Certs/classifiedads.identityserver.pfx rename to src/IdentityServer/ClassifiedAds.IdentityServer/Certs/classifiedads.identityserver.pfx diff --git a/src/Monolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj b/src/IdentityServer/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj rename to src/IdentityServer/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.nuspec b/src/IdentityServer/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.nuspec similarity index 100% rename from src/ModularMonolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.nuspec rename to src/IdentityServer/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.nuspec diff --git a/src/Monolith/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ConnectionStrings.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ConnectionStrings.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ConnectionStrings.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ConnectionStrings.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/CookiePolicyOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/CookiePolicyOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/CookiePolicyOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/CookiePolicyOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/IdentityServerOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/IdentityServerOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/IdentityServerOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/ConfigurationOptions/IdentityServerOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/ApiResourceController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/ApiResourceController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/ApiResourceController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/ApiResourceController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/ApiScopeController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/ApiScopeController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/ApiScopeController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/ApiScopeController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/ClientController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/ClientController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/ClientController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/ClientController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/GrantController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/GrantController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/GrantController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/GrantController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/IdentityResourceController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/IdentityResourceController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/IdentityResourceController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/IdentityResourceController.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Controllers/RoleController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/RoleController.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Controllers/RoleController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/RoleController.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Controllers/UserController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/UserController.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Controllers/UserController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Controllers/UserController.cs diff --git a/src/IdentityServer/ClassifiedAds.IdentityServer/Dockerfile b/src/IdentityServer/ClassifiedAds.IdentityServer/Dockerfile new file mode 100644 index 000000000..f9108b69f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.IdentityServer/Dockerfile @@ -0,0 +1,24 @@ +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env +WORKDIR /ClassifiedAds.IdentityServer + +# Copy csproj and restore as distinct layers +COPY ./ClassifiedAds.Application/*.csproj ./ClassifiedAds.Application/ +COPY ./ClassifiedAds.CrossCuttingConcerns/*.csproj ./ClassifiedAds.CrossCuttingConcerns/ +COPY ./ClassifiedAds.Domain/*.csproj ./ClassifiedAds.Domain/ +COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ +COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ +COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ +COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ +COPY ./ClassifiedAds.IdentityServer.sln . +RUN dotnet restore + +# Copy everything else and build +COPY . ./ +RUN dotnet publish ./ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj -c Release -o out + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +WORKDIR /ClassifiedAds.IdentityServer +COPY --from=build-env /ClassifiedAds.IdentityServer/out . + +ENTRYPOINT ["dotnet", "ClassifiedAds.IdentityServer.dll"] \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ApiResourceModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourceModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ApiResourceModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourceModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/PropertiesModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/PropertiesModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/PropertiesModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/PropertiesModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ScopeModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopeModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ScopeModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopeModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ScopesModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopesModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/ScopesModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopesModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/SecretModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/SecretModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/SecretsModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiResourceModels/SecretsModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiScopeModels/ApiScopeModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiScopeModels/ApiScopeModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ApiScopeModels/ApiScopeModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ApiScopeModels/ApiScopeModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/ClaimModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/ClaimModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/ClaimsModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/ClaimsModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/ClientModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/ClientModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/PropertiesModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/PropertiesModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/PropertyModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/PropertyModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/SecretModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/SecretModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/SecretsModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ClientModels/SecretsModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ForgotPasswordModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ForgotPasswordModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ForgotPasswordModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ForgotPasswordModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/IdentityResourceModels/IdentityResourceModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourceModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/IdentityResourceModels/IdentityResourceModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourceModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/IdentityResourceModels/PropertiesModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/PropertiesModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/IdentityResourceModels/PropertiesModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/PropertiesModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RegisterModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/RegisterModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RegisterModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/RegisterModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ResetPasswordModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/ResetPasswordModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/ResetPasswordModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/ResetPasswordModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/UserModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/UserModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Program.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Program.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Program.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Program.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Properties/launchSettings.json b/src/IdentityServer/ClassifiedAds.IdentityServer/Properties/launchSettings.json similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Properties/launchSettings.json rename to src/IdentityServer/ClassifiedAds.IdentityServer/Properties/launchSettings.json diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/AccountOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/AccountOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/AccountOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/AccountOptions.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/ExternalProvider.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalProvider.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/ExternalProvider.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalProvider.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LoggedOutViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LoggedOutViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LoggedOutViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LoggedOutViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LoginInputModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LoginInputModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LoginInputModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LoginInputModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LoginViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LoginViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LoginViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LoginViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LogoutInputModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutInputModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LogoutInputModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutInputModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LogoutViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/LogoutViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/RedirectViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/RedirectViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/RedirectViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/RedirectViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/SendCodeViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/SendCodeViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/SendCodeViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/SendCodeViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/VerifyCodeViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/VerifyCodeViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/VerifyCodeViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Account/VerifyCodeViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentInputModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentInputModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentInputModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentInputModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentOptions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentOptions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentOptions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentOptions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ConsentViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ProcessConsentResult.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ProcessConsentResult.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ProcessConsentResult.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ProcessConsentResult.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ScopeViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ScopeViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Consent/ScopeViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Consent/ScopeViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Device/DeviceAuthorizationInputModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Device/DeviceAuthorizationInputModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Device/DeviceAuthorizationViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Device/DeviceAuthorizationViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Device/DeviceController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Device/DeviceController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Diagnostics/DiagnosticsController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Diagnostics/DiagnosticsController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Extensions.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Extensions.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Extensions.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Extensions.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Grants/GrantsController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Grants/GrantsController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Grants/GrantsViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Grants/GrantsViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Home/ErrorViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Home/ErrorViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Home/ErrorViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Home/ErrorViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Home/HomeController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Home/HomeController.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Home/HomeController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Home/HomeController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/AddPhoneNumberViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/AddPhoneNumberViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/AddPhoneNumberViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/AddPhoneNumberViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ChangePasswordViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ChangePasswordViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ChangePasswordViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ChangePasswordViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/FactorViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/FactorViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/FactorViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/FactorViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/IndexViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/IndexViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/IndexViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/IndexViewModel.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ManageLoginsViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageLoginsViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ManageLoginsViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageLoginsViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/RemoveLoginViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/RemoveLoginViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/RemoveLoginViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/RemoveLoginViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/SetPasswordViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/SetPasswordViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/SetPasswordViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/SetPasswordViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/SecurityHeadersAttribute.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/SecurityHeadersAttribute.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/SecurityHeadersAttribute.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/SecurityHeadersAttribute.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/TestUsers.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/TestUsers.cs similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/TestUsers.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Quickstart/TestUsers.cs diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Startup.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/Startup.cs similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Startup.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/Startup.cs diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/TagHelpers/PickerTagHelper.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/TagHelpers/PickerTagHelper.cs similarity index 100% rename from src/ModularMonolith/ClassifiedAds.IdentityServer/TagHelpers/PickerTagHelper.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/TagHelpers/PickerTagHelper.cs diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs b/src/IdentityServer/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs similarity index 100% rename from src/ModularMonolith/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs rename to src/IdentityServer/ClassifiedAds.IdentityServer/TagHelpers/SwitchTagHelper.cs diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/AccessDenied.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/AccessDenied.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/AccessDenied.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/AccessDenied.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/ForgotPassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/ForgotPassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/LoggedOut.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/LoggedOut.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/LoggedOut.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/LoggedOut.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Login.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Login.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Logout.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Logout.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Logout.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Logout.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Register.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Register.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Register.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Register.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/ResetPassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/ResetPassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/ResetPassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/ResetPassword.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/SendCode.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/SendCode.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/SendCode.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/SendCode.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Success.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Success.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/Success.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/Success.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/VerifyCode.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/VerifyCode.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Account/VerifyCode.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Account/VerifyCode.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Delete.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Delete.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Delete.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Delete.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/DeleteProperty.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteProperty.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/DeleteProperty.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteProperty.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/DeleteScope.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteScope.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/DeleteScope.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteScope.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/DeleteSecret.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteSecret.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/DeleteSecret.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteSecret.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Edit.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Edit.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Edit.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Edit.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Properties.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Properties.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Properties.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Properties.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Scopes.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Scopes.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Scopes.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Scopes.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Secrets.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Secrets.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/Secrets.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/Secrets.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/_Label.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/_Label.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiResource/_Label.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiResource/_Label.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiScope/Edit.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiScope/Edit.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/ApiScope/Edit.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/ApiScope/Edit.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Claims.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Claims.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Clone.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Clone.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Delete.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Delete.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/DeleteClaim.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/DeleteClaim.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/DeleteProperty.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/DeleteProperty.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/DeleteSecret.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/DeleteSecret.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Edit.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Edit.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Properties.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Properties.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Secrets.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/Secrets.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Actions.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Actions.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Authentication.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Authentication.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Basics.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Basics.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Consent.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Consent.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_DeviceFlow.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_DeviceFlow.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Label.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Label.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Label.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Label.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Name.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Name.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Token.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Client/_Token.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Consent/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Consent/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Consent/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Consent/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Device/Success.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Device/Success.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Device/Success.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Device/Success.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Device/UserCodeCapture.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Device/UserCodeCapture.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Device/UserCodeCapture.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Device/UserCodeCapture.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Device/UserCodeConfirmation.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Device/UserCodeConfirmation.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Device/UserCodeConfirmation.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Device/UserCodeConfirmation.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Diagnostics/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Diagnostics/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Diagnostics/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Diagnostics/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Grants/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Grants/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Grants/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Grants/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Home/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Home/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Home/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Home/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Delete.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Delete.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Delete.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Delete.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/DeleteProperty.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/DeleteProperty.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/DeleteProperty.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/DeleteProperty.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Edit.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Edit.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Properties.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Properties.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/Properties.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/Properties.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/_Label.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/IdentityResource/_Label.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/AddPhoneNumber.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/AddPhoneNumber.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/AddPhoneNumber.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/AddPhoneNumber.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/ChangePassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/ChangePassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/ChangePassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/ChangePassword.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/DisplayRecoveryCodes.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/DisplayRecoveryCodes.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/DisplayRecoveryCodes.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/DisplayRecoveryCodes.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/Index.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/ManageLogins.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/ManageLogins.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/ManageLogins.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/ManageLogins.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/SetPassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/SetPassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/SetPassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/SetPassword.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/VerifyPhoneNumber.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/VerifyPhoneNumber.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Manage/VerifyPhoneNumber.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Manage/VerifyPhoneNumber.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Claims.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Claims.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/DeleteClaim.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/DeleteClaim.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Users.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Users.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/_Label.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/_Label.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/Error.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/Error.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/Error.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/Error.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/Redirect.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/Redirect.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/Redirect.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/Redirect.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_Layout.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_Layout.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_ScopeListItem.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_ScopeListItem.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_ScopeListItem.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_ScopeListItem.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_ValidationScriptsPartial.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_ValidationScriptsPartial.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_ValidationScriptsPartial.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_ValidationScriptsPartial.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_ValidationSummary.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_ValidationSummary.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_ValidationSummary.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/Shared/_ValidationSummary.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/ChangePassword.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/ChangePassword.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Claims.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Claims.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/DeleteClaim.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/DeleteClaim.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/DeleteRole.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/DeleteRole.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/User/Index.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Index.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/User/Index.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Index.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Roles.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Roles.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/_Label.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/_Label.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/_ViewImports.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/_ViewImports.cshtml similarity index 100% rename from src/ModularMonolith/ClassifiedAds.IdentityServer/Views/_ViewImports.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/_ViewImports.cshtml diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/_ViewStart.cshtml b/src/IdentityServer/ClassifiedAds.IdentityServer/Views/_ViewStart.cshtml similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/_ViewStart.cshtml rename to src/IdentityServer/ClassifiedAds.IdentityServer/Views/_ViewStart.cshtml diff --git a/src/Monolith/ClassifiedAds.IdentityServer/appsettings.Development.json b/src/IdentityServer/ClassifiedAds.IdentityServer/appsettings.Development.json similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/appsettings.Development.json rename to src/IdentityServer/ClassifiedAds.IdentityServer/appsettings.Development.json diff --git a/src/Monolith/ClassifiedAds.IdentityServer/appsettings.json b/src/IdentityServer/ClassifiedAds.IdentityServer/appsettings.json similarity index 100% rename from src/Monolith/ClassifiedAds.IdentityServer/appsettings.json rename to src/IdentityServer/ClassifiedAds.IdentityServer/appsettings.json diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/bundle.min.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/bundle.min.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/bundle.min.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/bundle.min.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/site.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/site.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/site.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/site.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/site.less b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/site.less similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/site.less rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/site.less diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/site.min.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/site.min.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/site.min.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/site.min.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/web.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/web.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/web.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/web.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/web.min.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/web.min.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/css/web.min.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/css/web.min.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/favicon.ico b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/favicon.ico similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/favicon.ico rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/favicon.ico diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/FontAwesome.otf b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/FontAwesome.otf similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/FontAwesome.otf rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/FontAwesome.otf diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.eot b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.eot similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.eot rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.eot diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.svg b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.svg similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.svg rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.svg diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.ttf b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.ttf similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.ttf rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.ttf diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.woff b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.woff rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.woff2 b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff2 similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/fontawesome-webfont.woff2 rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff2 diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.eot b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.eot similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.eot rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.eot diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.otf b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.otf similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.otf rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.otf diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.svg b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.svg similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.svg rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.svg diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.ttf b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.ttf similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.ttf rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.ttf diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.woff b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.woff similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/fonts/open-iconic.woff rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.woff diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/icon.jpg b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/icon.jpg similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/icon.jpg rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/icon.jpg diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/icon.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/icon.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/icon.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/icon.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/device-client.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/device-client.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/device-client.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/device-client.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/empty-client.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/empty-client.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/empty-client.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/empty-client.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/native-client.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/native-client.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/native-client.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/native-client.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/server-client.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/server-client.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/server-client.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/server-client.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/spa-client.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/spa-client.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/spa-client.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/spa-client.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/web-client.png b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/web-client.png similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/icons/web-client.png rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/icons/web-client.png diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/loading.gif b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/loading.gif similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/images/loading.gif rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/images/loading.gif diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/js/bundle.min.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/js/bundle.min.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/js/bundle.min.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/js/bundle.min.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/js/signin-redirect.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/js/signin-redirect.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/js/signin-redirect.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/js/signin-redirect.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/js/signout-redirect.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/js/signout-redirect.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/js/signout-redirect.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/js/signout-redirect.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/css/bootstrap.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/css/bootstrap.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/css/bootstrap.css.map b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/css/bootstrap.css.map rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/css/bootstrap.min.css b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.min.css similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/css/bootstrap.min.css rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.min.css diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.svg b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.svg similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.svg rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.svg diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/js/bootstrap.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/js/bootstrap.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/js/bootstrap.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/js/bootstrap.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/js/bootstrap.min.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/js/bootstrap.min.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/bootstrap/js/bootstrap.min.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/js/bootstrap.min.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/jquery/jquery.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/jquery/jquery.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/jquery/jquery.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/jquery/jquery.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/jquery/jquery.min.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/jquery/jquery.min.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/jquery/jquery.min.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/jquery/jquery.min.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/jquery/jquery.min.map b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/jquery/jquery.min.map similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/jquery/jquery.min.map rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/jquery/jquery.min.map diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/qrcode.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/qrcode.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/qrcode.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/qrcode.js diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/qrcode.min.js b/src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/qrcode.min.js similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/wwwroot/lib/qrcode.min.js rename to src/IdentityServer/ClassifiedAds.IdentityServer/wwwroot/lib/qrcode.min.js diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/.editorconfig b/src/IdentityServer/ClassifiedAds.Infrastructure/.editorconfig new file mode 100644 index 000000000..43d5b5702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/.editorconfig @@ -0,0 +1,224 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +# insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = false +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + +############################### +# StyleCop # +############################### +[*.cs] + +# SA1027: Use tabs correctly +dotnet_diagnostic.SA1027.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +dotnet_diagnostic.SA1111.severity = none + +# SA1113: Comma should be on the same line as previous parameter +dotnet_diagnostic.SA1113.severity = none + +# SA1115: Parameter should follow comma +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1208: System using directives should be placed before other using directives +dotnet_diagnostic.SA1208.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +dotnet_diagnostic.SA1310.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# SA1610: Property documentation should have value text +dotnet_diagnostic.SA1610.severity = none + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = suggestion diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Caching/CachingOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Caching/CachingOptions.cs new file mode 100644 index 000000000..67ef160ec --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Caching/CachingOptions.cs @@ -0,0 +1,41 @@ +namespace ClassifiedAds.Infrastructure.Caching +{ + public class CachingOptions + { + public InMemoryCacheOptions InMemory { get; set; } + + public DistributedCacheOptions Distributed { get; set; } + } + + public class InMemoryCacheOptions + { + public long? SizeLimit { get; set; } + } + + public class DistributedCacheOptions + { + public string Provider { get; set; } + + public InMemoryCacheOptions InMemory { get; set; } + + public RedisOptions Redis { get; set; } + + public SqlServerOptions SqlServer { get; set; } + } + + public class RedisOptions + { + public string Configuration { get; set; } + + public string InstanceName { get; set; } + } + + public class SqlServerOptions + { + public string ConnectionString { get; set; } + + public string SchemaName { get; set; } + + public string TableName { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Caching/CachingServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Caching/CachingServiceCollectionExtensions.cs new file mode 100644 index 000000000..3805a42a7 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Caching/CachingServiceCollectionExtensions.cs @@ -0,0 +1,44 @@ +using ClassifiedAds.Infrastructure.Caching; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class CachingServiceCollectionExtensions + { + public static IServiceCollection AddCaches(this IServiceCollection services, CachingOptions options = null) + { + services.AddMemoryCache(opt => + { + opt.SizeLimit = options?.InMemory?.SizeLimit; + }); + + var distributedProvider = options?.Distributed?.Provider; + + if (distributedProvider == "InMemory") + { + services.AddDistributedMemoryCache(opt => + { + opt.SizeLimit = options?.Distributed?.InMemory?.SizeLimit; + }); + } + else if (distributedProvider == "Redis") + { + services.AddDistributedRedisCache(opt => + { + opt.Configuration = options.Distributed.Redis.Configuration; + opt.InstanceName = options.Distributed.Redis.InstanceName; + }); + } + else if (distributedProvider == "SqlServer") + { + services.AddDistributedSqlServerCache(opt => + { + opt.ConnectionString = options.Distributed.SqlServer.ConnectionString; + opt.SchemaName = options.Distributed.SqlServer.SchemaName; + opt.TableName = options.Distributed.SqlServer.TableName; + }); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj b/src/IdentityServer/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj new file mode 100644 index 000000000..ed71ced70 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj @@ -0,0 +1,86 @@ + + + + net6.0 + Recommended + All + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + PreserveNewest + libwkhtmltox.dll + + + PreserveNewest + libwkhtmltox.so + + + + diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/ConfigurationCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/ConfigurationCollectionExtensions.cs new file mode 100644 index 000000000..0d3c75204 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/ConfigurationCollectionExtensions.cs @@ -0,0 +1,37 @@ +using Azure.Extensions.AspNetCore.Configuration.Secrets; +using Azure.Identity; +using Azure.Security.KeyVault.Secrets; +using Microsoft.Extensions.Configuration; +using System; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public static class ConfigurationCollectionExtensions + { + public static IConfigurationBuilder AddAppConfiguration(this IConfigurationBuilder configurationBuilder, ConfigurationProviders options) + { + if (options?.SqlServer?.IsEnabled ?? false) + { + configurationBuilder.AddSqlServer(options.SqlServer); + } + + if (options?.AzureAppConfiguration?.IsEnabled ?? false) + { + configurationBuilder.AddAzureAppConfiguration(options.AzureAppConfiguration.ConnectionString); + } + + if (options?.AzureKeyVault?.IsEnabled ?? false) + { + var secretClient = new SecretClient(new Uri(options.AzureKeyVault.VaultName), new DefaultAzureCredential()); + configurationBuilder.AddAzureKeyVault(secretClient, new KeyVaultSecretManager()); + } + + if (options?.HashiCorpVault?.IsEnabled ?? false) + { + configurationBuilder.AddHashiCorpVault(options.HashiCorpVault); + } + + return configurationBuilder; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/ConfigurationProviders.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/ConfigurationProviders.cs new file mode 100644 index 000000000..99dfad973 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/ConfigurationProviders.cs @@ -0,0 +1,74 @@ +using CryptographyHelper.Certificates; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public class ConfigurationProviders + { + public SqlServerOptions SqlServer { get; set; } + + public AzureAppConfigurationOptions AzureAppConfiguration { get; set; } + + public AzureKeyVaultOptions AzureKeyVault { get; set; } + + public HashiCorpVaultOptions HashiCorpVault { get; set; } + } + + public class SqlServerOptions + { + public bool IsEnabled { get; set; } + + public string ConnectionString { get; set; } + + public string SqlQuery { get; set; } + + public CertificateOption Certificate { get; set; } + } + + public class AzureKeyVaultOptions + { + public bool IsEnabled { get; set; } + + public string VaultName { get; set; } + } + + public class AzureAppConfigurationOptions + { + public bool IsEnabled { get; set; } + + public string ConnectionString { get; set; } + } + + public class HashiCorpVaultOptions + { + public bool IsEnabled { get; set; } + + public string Address { get; set; } + + public string SecretEnginePath { get; set; } + + public string SecretPath { get; set; } + + public string AuthMethod { get; set; } + + public HashiCorpVaultAuthOptions Auth { get; set; } + } + + public class HashiCorpVaultAuthOptions + { + public HashiCorpVaultTokenAuthOptions Token { get; set; } + + public HashiCorpVaultUserPassAuthOptions UserPass { get; set; } + } + + public class HashiCorpVaultUserPassAuthOptions + { + public string UserName { get; set; } + + public string Password { get; set; } + } + + public class HashiCorpVaultTokenAuthOptions + { + public string Token { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationExtensions.cs new file mode 100644 index 000000000..e5779afef --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Configuration; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public static class HashiCorpVaultConfigurationExtensions + { + public static IConfigurationBuilder AddHashiCorpVault(this IConfigurationBuilder builder, HashiCorpVaultOptions options) + { + return builder.Add(new HashiCorpVaultConfigurationSource(options)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationProvider.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationProvider.cs new file mode 100644 index 000000000..2b5ddd6a1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationProvider.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.Configuration; +using System.Linq; +using VaultSharp; +using VaultSharp.V1.AuthMethods; +using VaultSharp.V1.AuthMethods.Token; +using VaultSharp.V1.AuthMethods.UserPass; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + internal class HashiCorpVaultConfigurationProvider : ConfigurationProvider + { + private readonly HashiCorpVaultOptions _options; + + public HashiCorpVaultConfigurationProvider(HashiCorpVaultOptions options) + { + _options = options; + } + + public override void Load() + { + IAuthMethodInfo authMethod = null; + + if (_options.AuthMethod == "Token") + { + authMethod = new TokenAuthMethodInfo(_options.Auth.Token.Token); + } + else if (_options.AuthMethod == "UserPass") + { + authMethod = new UserPassAuthMethodInfo(_options.Auth.UserPass.UserName, _options.Auth.UserPass.Password); + } + + var vaultClientSettings = new VaultClientSettings(_options.Address, authMethod); + var client = new VaultClient(vaultClientSettings); + var secrets = client.V1.Secrets.KeyValue.V2.ReadSecretAsync(path: _options.SecretPath, mountPoint: _options.SecretEnginePath) + .GetAwaiter().GetResult(); + Data = secrets.Data.Data.ToDictionary(x => x.Key, x => x.Value.ToString()); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationSource.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationSource.cs new file mode 100644 index 000000000..ecf619788 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/HashiCorpVaultConfigurationSource.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public class HashiCorpVaultConfigurationSource : IConfigurationSource + { + private readonly HashiCorpVaultOptions _options; + + public HashiCorpVaultConfigurationSource(HashiCorpVaultOptions options) + { + _options = options; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new HashiCorpVaultConfigurationProvider(_options); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationExtensions.cs new file mode 100644 index 000000000..39683346b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Configuration; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public static class SqlConfigurationExtensions + { + public static IConfigurationBuilder AddSqlServer(this IConfigurationBuilder builder, SqlServerOptions options) + { + return builder.Add(new SqlConfigurationSource(options)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationProvider.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationProvider.cs new file mode 100644 index 000000000..0721880ee --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationProvider.cs @@ -0,0 +1,52 @@ +using CryptographyHelper; +using CryptographyHelper.AsymmetricAlgorithms; +using Dapper; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Configuration; +using System.Linq; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public class SqlConfigurationProvider : ConfigurationProvider + { + private readonly SqlServerOptions _options; + + public SqlConfigurationProvider(SqlServerOptions options) + { + _options = options; + } + + public override void Load() + { + using (var conn = new SqlConnection(_options.ConnectionString)) + { + conn.Open(); + var data = conn.Query(_options.SqlQuery).ToList(); + + var cert = data.Any(x => x.IsSensitive) + ? _options.Certificate.FindCertificate() + : null; + + foreach (var entry in data) + { + if (entry.IsSensitive) + { + var decrypted = entry.Value.FromBase64String().UseRSA(cert).Decrypt(); + entry.Value = decrypted.GetString(); + } + } + + Data = data.ToDictionary(c => c.Key, c => c.Value); + } + } + } + + public class ConfigurationEntry + { + public string Key { get; set; } + + public string Value { get; set; } + + public bool IsSensitive { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationSource.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationSource.cs new file mode 100644 index 000000000..82bae88b0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Configuration/SqlConfigurationSource.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.Configuration; + +namespace ClassifiedAds.Infrastructure.Configuration +{ + public class SqlConfigurationSource : IConfigurationSource + { + private readonly SqlServerOptions _options; + + public SqlConfigurationSource(SqlServerOptions options) + { + _options = options; + } + + public IConfigurationProvider Build(IConfigurationBuilder builder) + { + return new SqlConfigurationProvider(_options); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Csv/CsvReader.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Csv/CsvReader.cs new file mode 100644 index 000000000..b537c5d8e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Csv/CsvReader.cs @@ -0,0 +1,18 @@ +using ClassifiedAds.CrossCuttingConcerns.Csv; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace ClassifiedAds.Infrastructure.Csv +{ + public class CsvReader : ICsvReader + { + public IEnumerable Read(Stream stream) + { + using var reader = new StreamReader(stream); + using var csv = new CsvHelper.CsvReader(reader, CultureInfo.InvariantCulture); + return csv.GetRecords().ToList(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Csv/CsvWriter.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Csv/CsvWriter.cs new file mode 100644 index 000000000..2c3622866 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Csv/CsvWriter.cs @@ -0,0 +1,17 @@ +using ClassifiedAds.CrossCuttingConcerns.Csv; +using System.Collections.Generic; +using System.Globalization; +using System.IO; + +namespace ClassifiedAds.Infrastructure.Csv +{ + public class CsvWriter : ICsvWriter + { + public void Write(IEnumerable collection, Stream stream) + { + using var writer = new StreamWriter(stream); + using var csv = new CsvHelper.CsvWriter(writer, CultureInfo.InvariantCulture); + csv.WriteRecords(collection); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/DistributedTracing/DistributedTracingExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/DistributedTracing/DistributedTracingExtensions.cs new file mode 100644 index 000000000..9ba435d7e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/DistributedTracing/DistributedTracingExtensions.cs @@ -0,0 +1,60 @@ +using Microsoft.Extensions.DependencyInjection; +using OpenTelemetry.Resources; +using OpenTelemetry.Trace; +using System; + +namespace ClassifiedAds.Infrastructure.DistributedTracing +{ + public static class DistributedTracingExtensions + { + public static IServiceCollection AddDistributedTracing(this IServiceCollection services, DistributedTracingOptions options = null) + { + if (options == null || !options.IsEnabled) + { + return services; + } + + if (options?.Exporter == ExporterOptions.Zipkin) + { + services.AddOpenTelemetryTracing((builder) => builder + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(options.Zipkin.ServiceName)) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .SetSampler(new AlwaysOnSampler()) + .AddZipkinExporter(zipkinOptions => + { + zipkinOptions.Endpoint = new Uri(options.Zipkin.Endpoint); + })); + } + + if (options?.Exporter == ExporterOptions.Jaeger) + { + services.AddOpenTelemetryTracing((builder) => builder + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(options.Jaeger.ServiceName)) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .SetSampler(new AlwaysOnSampler()) + .AddJaegerExporter(jaegerOptions => + { + jaegerOptions.AgentHost = options.Jaeger.Host; + jaegerOptions.AgentPort = options.Jaeger.Port; + })); + } + + if (options?.Exporter == ExporterOptions.Otlp) + { + services.AddOpenTelemetryTracing((builder) => builder + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(options.Otlp.ServiceName)) + .AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .SetSampler(new AlwaysOnSampler()) + .AddOtlpExporter(otlpOptions => + { + otlpOptions.Endpoint = new Uri(options.Otlp.Endpoint); + })); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/DistributedTracing/DistributedTracingOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/DistributedTracing/DistributedTracingOptions.cs new file mode 100644 index 000000000..57c07cf4a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/DistributedTracing/DistributedTracingOptions.cs @@ -0,0 +1,45 @@ +namespace ClassifiedAds.Infrastructure.DistributedTracing +{ + public class DistributedTracingOptions + { + public bool IsEnabled { get; set; } + + public ExporterOptions Exporter { get; set; } + + public ZipkinOptions Zipkin { get; set; } + + public JaegerOptions Jaeger { get; set; } + + public OtlpOptions Otlp { get; set; } + } + + public enum ExporterOptions + { + Zipkin, + Jaeger, + Otlp, + } + + public class ZipkinOptions + { + public string ServiceName { get; set; } + + public string Endpoint { get; set; } + } + + public class JaegerOptions + { + public string ServiceName { get; set; } + + public string Host { get; set; } + + public int Port { get; set; } + } + + public class OtlpOptions + { + public string ServiceName { get; set; } + + public string Endpoint { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/ConfigurationEntryExcelReader.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/ConfigurationEntryExcelReader.cs new file mode 100644 index 000000000..67e4b1b3e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/ConfigurationEntryExcelReader.cs @@ -0,0 +1,49 @@ +using ClassifiedAds.CrossCuttingConcerns.Excel; +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using ClassifiedAds.Domain.Entities; +using ClosedXML.Excel; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ClassifiedAds.Infrastructure.Excel.ClosedXML +{ + public class ConfigurationEntryExcelReader : IExcelReader> + { + public List Read(Stream stream) + { + using var workbook = new XLWorkbook(stream); + var worksheet = workbook.Worksheets.First(); + + string result = worksheet.VerifyHeader(1, GetCorrectHeaders()); + if (!string.IsNullOrEmpty(result)) + { + throw new ValidationException(result); + } + + var rows = new List(); + + for (var i = 2; i <= worksheet.LastRowUsed().RowNumber(); i++) + { + var row = new ConfigurationEntry + { + Key = worksheet.Cell("A" + i).GetString(), + Value = worksheet.Cell("B" + i).GetString(), + }; + + rows.Add(row); + } + + return rows; + } + + private static Dictionary GetCorrectHeaders() + { + return new Dictionary + { + { "A", "Key" }, + { "B", "Value" }, + }; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/ConfigurationEntryExcelWriter.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/ConfigurationEntryExcelWriter.cs new file mode 100644 index 000000000..b4c964dd0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/ConfigurationEntryExcelWriter.cs @@ -0,0 +1,31 @@ +using ClassifiedAds.CrossCuttingConcerns.Excel; +using ClassifiedAds.Domain.Entities; +using ClosedXML.Excel; +using System.Collections.Generic; +using System.IO; + +namespace ClassifiedAds.Infrastructure.Excel.ClosedXML +{ + public class ConfigurationEntryExcelWriter : IExcelWriter> + { + public void Write(List data, Stream stream) + { + using var workbook = new XLWorkbook(); + var worksheet = workbook.Worksheets.Add("Sheet1"); + + worksheet.Cell("A1").Value = "Key"; + worksheet.Cell("B1").Value = "Value"; + worksheet.Range("A1:B1").Style.Font.Bold = true; + + int i = 2; + foreach (var row in data) + { + worksheet.Cell("A" + i).Value = row.Key; + worksheet.Cell("B" + i).Value = row.Value; + i++; + } + + workbook.SaveAs(stream); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/IXLWorksheetExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/IXLWorksheetExtensions.cs new file mode 100644 index 000000000..5b7d45081 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ClosedXML/IXLWorksheetExtensions.cs @@ -0,0 +1,24 @@ +using ClosedXML.Excel; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.Excel.ClosedXML +{ + public static class IXLWorksheetExtensions + { + public static string VerifyHeader(this IXLWorksheet worksheet, int headerIndex, Dictionary expectedValues) + { + foreach (var correctHeader in expectedValues) + { + var currentHeader = worksheet.Cell(correctHeader.Key + headerIndex).GetString(); + + if (!correctHeader.Value.Equals(currentHeader, StringComparison.OrdinalIgnoreCase)) + { + return $"Wrong Template! The expected value of cell [{correctHeader.Key}{headerIndex}] is: {correctHeader.Value} but the actual value is: {currentHeader}"; + } + } + + return string.Empty; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ConfigurationEntryExcelReader.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ConfigurationEntryExcelReader.cs new file mode 100644 index 000000000..8a33647f2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ConfigurationEntryExcelReader.cs @@ -0,0 +1,49 @@ +using ClassifiedAds.CrossCuttingConcerns.Excel; +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using ClassifiedAds.Domain.Entities; +using OfficeOpenXml; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ClassifiedAds.Infrastructure.Excel.EPPlus +{ + public class ConfigurationEntryExcelReader : IExcelReader> + { + public List Read(Stream stream) + { + using var pck = new ExcelPackage(stream); + var worksheet = pck.Workbook.Worksheets.First(); + + string result = worksheet.VerifyHeader(1, GetCorrectHeaders()); + if (!string.IsNullOrEmpty(result)) + { + throw new ValidationException(result); + } + + var rows = new List(); + + for (var i = 2; i <= worksheet.Dimension.End.Row; i++) + { + var row = new ConfigurationEntry + { + Key = worksheet.GetCellValue("A", i), + Value = worksheet.GetCellValue("B", i), + }; + + rows.Add(row); + } + + return rows; + } + + private static Dictionary GetCorrectHeaders() + { + return new Dictionary + { + { "A", "Key" }, + { "B", "Value" }, + }; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ConfigurationEntryExcelWriter.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ConfigurationEntryExcelWriter.cs new file mode 100644 index 000000000..f7d7e7018 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ConfigurationEntryExcelWriter.cs @@ -0,0 +1,31 @@ +using ClassifiedAds.CrossCuttingConcerns.Excel; +using ClassifiedAds.Domain.Entities; +using OfficeOpenXml; +using System.Collections.Generic; +using System.IO; + +namespace ClassifiedAds.Infrastructure.Excel.EPPlus +{ + public class ConfigurationEntryExcelWriter : IExcelWriter> + { + public void Write(List data, Stream stream) + { + using var pck = new ExcelPackage(); + var worksheet = pck.Workbook.Worksheets.Add("Sheet1"); + + worksheet.Cells["A1"].Value = "Key"; + worksheet.Cells["B1"].Value = "Value"; + worksheet.Cells["A1:B1"].Style.Font.Bold = true; + + int i = 2; + foreach (var row in data) + { + worksheet.Cells["A" + i].Value = row.Key; + worksheet.Cells["B" + i].Value = row.Value; + i++; + } + + pck.SaveAs(stream); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ExcelWorksheetExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ExcelWorksheetExtensions.cs new file mode 100644 index 000000000..d86bdf2c4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/EPPlus/ExcelWorksheetExtensions.cs @@ -0,0 +1,59 @@ +using OfficeOpenXml; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.Excel.EPPlus +{ + public static class ExcelWorksheetExtensions + { + public static string GetCellValue(this ExcelWorksheet worksheet, string col, int row) + { + return worksheet.Cells[col + row].Value?.ToString()?.Trim(); + } + + public static string GetCellText(this ExcelWorksheet worksheet, string col, int row) + { + return worksheet.Cells[col + row].Text?.Trim(); + } + + public static string GetCellComment(this ExcelWorksheet worksheet, string col, int row) + { + return worksheet.Cells[col + row].Comment?.Text; + } + + public static DateTime? GetCellDateTimeValue(this ExcelWorksheet worksheet, string col, int row) + { + double resFrom; + DateTime dateTime; + if (!string.IsNullOrWhiteSpace(worksheet.GetCellValue(col, row))) + { + if (double.TryParse(worksheet.GetCellValue(col, row), out resFrom)) + { + return DateTime.FromOADate(resFrom); + } + + if (DateTime.TryParse(worksheet.GetCellValue(col, row), out dateTime)) + { + return dateTime; + } + } + + return null; + } + + public static string VerifyHeader(this ExcelWorksheet worksheet, int headerIndex, Dictionary expectedValues) + { + foreach (var correctHeader in expectedValues) + { + var currentHeader = worksheet.GetCellValue(correctHeader.Key, headerIndex); + + if (!correctHeader.Value.Equals(currentHeader, StringComparison.OrdinalIgnoreCase)) + { + return $"Wrong Template! The expected value of cell [{correctHeader.Key}{headerIndex}] is: {correctHeader.Value} but the actual value is: {currentHeader}"; + } + } + + return string.Empty; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ExcelDataReader/ConfigurationEntryExcelReader.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ExcelDataReader/ConfigurationEntryExcelReader.cs new file mode 100644 index 000000000..eb198b4b6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Excel/ExcelDataReader/ConfigurationEntryExcelReader.cs @@ -0,0 +1,78 @@ +using ClassifiedAds.CrossCuttingConcerns.Excel; +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using ClassifiedAds.Domain.Entities; +using ExcelDataReader; +using System; +using System.Collections.Generic; +using System.IO; + +namespace ClassifiedAds.Infrastructure.Excel.ExcelDataReader +{ + public class ConfigurationEntryExcelReader : IExcelReader> + { + public List Read(Stream stream) + { + var rows = new List(); + int headerIndex = 0; + + using (var reader = ExcelReaderFactory.CreateReader(stream)) + { + int sheetIndex = 0; + do + { + string sheetName = reader.Name; + if (sheetName != "Sheet1") + { + continue; + } + + int rowIndex = 0; + while (reader.Read()) + { + if (rowIndex < headerIndex) + { + rowIndex++; + continue; + } + else if (rowIndex == headerIndex) + { + foreach (var header in GetCorrectHeaders()) + { + if (!string.Equals(reader.GetValue(header.Key)?.ToString(), header.Value, StringComparison.OrdinalIgnoreCase)) + { + throw new ValidationException("Wrong Template!"); + } + } + + rowIndex++; + continue; + } + + var row = new ConfigurationEntry + { + Key = reader.GetValue(0)?.ToString(), + Value = reader.GetValue(1)?.ToString(), + }; + + rows.Add(row); + rowIndex++; + } + + sheetIndex++; + } + while (reader.NextResult()); + } + + return rows; + } + + private static Dictionary GetCorrectHeaders() + { + return new Dictionary + { + { 0, "Key" }, + { 1, "Value" }, + }; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/HealthCheckBuilderExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/HealthCheckBuilderExtensions.cs new file mode 100644 index 000000000..ba81b21eb --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/HealthCheckBuilderExtensions.cs @@ -0,0 +1,169 @@ +using ClassifiedAds.Infrastructure.HealthChecks; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus; +using ClassifiedAds.Infrastructure.MessageBrokers.Kafka; +using ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ; +using ClassifiedAds.Infrastructure.Storages.Amazon; +using ClassifiedAds.Infrastructure.Storages.Azure; +using ClassifiedAds.Infrastructure.Storages.Local; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class HealthCheckBuilderExtensions + { + public static IHealthChecksBuilder AddHttp( + this IHealthChecksBuilder builder, + string uri, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new HttpHealthCheck(uri), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddSqlServer( + this IHealthChecksBuilder builder, + string connectionString, + string healthQuery = default, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new SqlServerHealthCheck(connectionString, healthQuery), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddAmazonS3( + this IHealthChecksBuilder builder, + AmazonOptions amazonOptions, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new AmazonS3HealthCheck(amazonOptions), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddAzureBlobStorage( + this IHealthChecksBuilder builder, + AzureBlobOption azureBlobOptions, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new AzureBlobStorageHealthCheck(azureBlobOptions), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddAzureQueueStorage( + this IHealthChecksBuilder builder, + string connectionString, + string queueName, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new AzureQueueStorageHealthCheck( + connectionString: connectionString, + queueName: queueName), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddAzureServiceBusQueue( + this IHealthChecksBuilder builder, + string connectionString, + string queueName, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new AzureServiceBusQueueHealthCheck( + connectionString: connectionString, + queueName: queueName), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddKafka( + this IHealthChecksBuilder builder, + string bootstrapServers, + string topic, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new KafkaHealthCheck(bootstrapServers: bootstrapServers, topic: topic), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddRabbitMQ( + this IHealthChecksBuilder builder, + RabbitMQHealthCheckOptions options, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new RabbitMQHealthCheck(options), + failureStatus, + tags, + timeout)); + } + + public static IHealthChecksBuilder AddLocalFile( + this IHealthChecksBuilder builder, + LocalFileHealthCheckOptions options, + string name = default, + HealthStatus? failureStatus = default, + IEnumerable tags = default, + TimeSpan? timeout = default) + { + return builder.Add(new HealthCheckRegistration( + name, + new LocalFileHealthCheck(options), + failureStatus, + tags, + timeout)); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/HttpHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/HttpHealthCheck.cs new file mode 100644 index 000000000..0245bb2fa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/HttpHealthCheck.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.HealthChecks +{ + public class HttpHealthCheck : IHealthCheck + { + private static HttpClient _httpClient = new HttpClient(); + private readonly string _uri; + + public HttpHealthCheck(string uri) + { + _uri = uri; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var response = await _httpClient.GetAsync(_uri); + if (response.IsSuccessStatusCode) + { + return HealthCheckResult.Healthy($"Uri: '{_uri}', StatusCode: '{response.StatusCode}'"); + } + else + { + return new HealthCheckResult(context.Registration.FailureStatus, $"Uri: '{_uri}', StatusCode: '{response.StatusCode}'"); + } + } + catch (Exception exception) + { + return new HealthCheckResult(context.Registration.FailureStatus, $"Uri: '{_uri}', Exception: '{exception.Message}'", exception); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/NetworkPortCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/NetworkPortCheck.cs new file mode 100644 index 000000000..08e6eefb6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/NetworkPortCheck.cs @@ -0,0 +1,44 @@ +using System; +using System.Net.Sockets; +using System.Threading; + +namespace ClassifiedAds.Infrastructure.HealthChecks +{ + public class NetworkPortCheck + { + public static bool IsReady(string address) + { + var parts = address.Split(':'); + string host = parts[0]; + int port = int.Parse(parts[1]); + + try + { + TcpClient client = new TcpClient(host, port); + return client.Connected; + } + catch (Exception ex) + { + Console.WriteLine($"Failed to connect to {address} :" + ex.Message); + return false; + } + } + + public static void Wait(string address, int retries = 0, int timeOut = 30000) + { + int count = 0; + do + { + var isReady = IsReady(address); + if (isReady) + { + return; + } + + count++; + Thread.Sleep(timeOut); + } + while (count <= retries); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/SqlServerHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/SqlServerHealthCheck.cs new file mode 100644 index 000000000..507e45aaa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HealthChecks/SqlServerHealthCheck.cs @@ -0,0 +1,41 @@ +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.HealthChecks +{ + public class SqlServerHealthCheck : IHealthCheck + { + private readonly string _connectionString; + + private readonly string _sql; + + public SqlServerHealthCheck(string connectionString, string sql) + { + _connectionString = connectionString ?? throw new ArgumentNullException("connectionString"); + _sql = sql ?? throw new ArgumentNullException("sql"); + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + using SqlConnection connection = new SqlConnection(_connectionString); + await connection.OpenAsync(cancellationToken); + using (SqlCommand command = connection.CreateCommand()) + { + command.CommandText = _sql; + await command.ExecuteScalarAsync(cancellationToken); + } + + return HealthCheckResult.Healthy(); + } + catch (Exception exception) + { + return new HealthCheckResult(context.Registration.FailureStatus, null, exception); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HostedServices/CronJobBackgroundService.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HostedServices/CronJobBackgroundService.cs new file mode 100644 index 000000000..9277100a6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HostedServices/CronJobBackgroundService.cs @@ -0,0 +1,33 @@ +using Microsoft.Extensions.Hosting; +using Quartz; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.HostedServices +{ + public abstract class CronJobBackgroundService : BackgroundService + { + protected string Cron { get; set; } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + var cron = new CronExpression(Cron); + var next = cron.GetNextValidTimeAfter(DateTimeOffset.Now); + + while (!stoppingToken.IsCancellationRequested) + { + if (DateTimeOffset.Now > next) + { + await DoWork(stoppingToken); + + next = cron.GetNextValidTimeAfter(DateTimeOffset.Now); + } + + await Task.Delay(1000, stoppingToken); + } + } + + protected abstract Task DoWork(CancellationToken stoppingToken); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HtmlGenerators/HtmlGenerator.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HtmlGenerators/HtmlGenerator.cs new file mode 100644 index 000000000..0d358f8de --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HtmlGenerators/HtmlGenerator.cs @@ -0,0 +1,21 @@ +using ClassifiedAds.CrossCuttingConcerns.HtmlGenerator; +using RazorLight; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.HtmlGenerators +{ + public class HtmlGenerator : IHtmlGenerator + { + private readonly IRazorLightEngine _razorLightEngine; + + public HtmlGenerator(IRazorLightEngine razorLightEngine) + { + _razorLightEngine = razorLightEngine; + } + + public Task GenerateAsync(string template, object model) + { + return _razorLightEngine.CompileRenderAsync(template, model); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HtmlGenerators/HtmlGeneratorCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HtmlGenerators/HtmlGeneratorCollectionExtensions.cs new file mode 100644 index 000000000..767908860 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HtmlGenerators/HtmlGeneratorCollectionExtensions.cs @@ -0,0 +1,23 @@ +using ClassifiedAds.CrossCuttingConcerns.HtmlGenerator; +using ClassifiedAds.Infrastructure.HtmlGenerators; +using RazorLight; +using System; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class HtmlGeneratorCollectionExtensions + { + public static IServiceCollection AddHtmlGenerator(this IServiceCollection services) + { + var engine = new RazorLightEngineBuilder() + .UseFileSystemProject(Environment.CurrentDirectory) + .UseMemoryCachingProvider() + .Build(); + + services.AddSingleton(engine); + services.AddSingleton(); + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/HttpMessageHandlers/DebuggingHandler.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/HttpMessageHandlers/DebuggingHandler.cs new file mode 100644 index 000000000..43c2956b0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/HttpMessageHandlers/DebuggingHandler.cs @@ -0,0 +1,19 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.HttpMessageHandlers +{ + public class DebuggingHandler : DelegatingHandler + { + public DebuggingHandler() + { + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await base.SendAsync(request, cancellationToken); + return response; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/AnonymousUser.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/AnonymousUser.cs new file mode 100644 index 000000000..bd7d47240 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/AnonymousUser.cs @@ -0,0 +1,12 @@ +using ClassifiedAds.Domain.Identity; +using System; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class AnonymousUser : ICurrentUser + { + public bool IsAuthenticated => false; + + public Guid UserId => Guid.Empty; + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/CurrentWebUser.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/CurrentWebUser.cs new file mode 100644 index 000000000..15a8ccde5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/CurrentWebUser.cs @@ -0,0 +1,36 @@ +using ClassifiedAds.Domain.Identity; +using Microsoft.AspNetCore.Http; +using System; +using System.Security.Claims; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class CurrentWebUser : ICurrentUser + { + private readonly IHttpContextAccessor _context; + + public CurrentWebUser(IHttpContextAccessor context) + { + _context = context; + } + + public bool IsAuthenticated + { + get + { + return _context.HttpContext.User.Identity.IsAuthenticated; + } + } + + public Guid UserId + { + get + { + var userId = _context.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value + ?? _context.HttpContext.User.FindFirst("sub")?.Value; + + return Guid.Parse(userId); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/EmailConfirmationTokenProvider.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/EmailConfirmationTokenProvider.cs new file mode 100644 index 000000000..46d402a98 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/EmailConfirmationTokenProvider.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class EmailConfirmationTokenProvider : DataProtectorTokenProvider + where TUser : class + { + public EmailConfirmationTokenProvider(IDataProtectionProvider dataProtectionProvider, + IOptions options, ILogger> logger) + : base(dataProtectionProvider, options, logger) + { + } + } + + public class EmailConfirmationTokenProviderOptions : DataProtectionTokenProviderOptions + { + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/HistoricalPasswordValidator.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/HistoricalPasswordValidator.cs new file mode 100644 index 000000000..1ea6dfe25 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/HistoricalPasswordValidator.cs @@ -0,0 +1,24 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.AspNetCore.Identity; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class HistoricalPasswordValidator : IPasswordValidator + { + public Task ValidateAsync(UserManager manager, User user, string password) + { + if (password.Contains("testhistoricalpassword")) + { + return Task.FromResult(IdentityResult.Failed(new IdentityError + { + Code = "HistoricalPassword", + Description = "HistoricalPasswordValidator testing.", + })); + } + + // TODO: check password histories, etc. + return Task.FromResult(IdentityResult.Success); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/IdentityServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/IdentityServiceCollectionExtensions.cs new file mode 100644 index 000000000..825118c2f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/IdentityServiceCollectionExtensions.cs @@ -0,0 +1,93 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Identity; +using ClassifiedAds.Infrastructure.Identity; +using Microsoft.AspNetCore.Identity; +using System; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class IdentityServiceCollectionExtensions + { + public static IServiceCollection AddIdentity(this IServiceCollection services) + { + services.AddIdentity() + .AddTokenProviders() + .AddPasswordValidators(); + + services.AddTransient, UserStore>(); + services.AddTransient, RoleStore>(); + services.AddScoped(); + + ConfigureOptions(services); + + services.ConfigureApplicationCookie(options => + { + options.LoginPath = "/Account/Login"; + }); + + return services; + } + + public static IServiceCollection AddIdentityCore(this IServiceCollection services) + { + services.AddIdentityCore() + .AddTokenProviders() + .AddPasswordValidators(); + + services.AddTransient, UserStore>(); + services.AddTransient, RoleStore>(); + services.AddScoped(); + + ConfigureOptions(services); + + return services; + } + + private static IdentityBuilder AddTokenProviders(this IdentityBuilder identityBuilder) + { + identityBuilder + .AddDefaultTokenProviders() + .AddTokenProvider>("EmailConfirmation"); + + return identityBuilder; + } + + private static IdentityBuilder AddPasswordValidators(this IdentityBuilder identityBuilder) + { + identityBuilder + .AddPasswordValidator() + .AddPasswordValidator(); + + return identityBuilder; + } + + private static void ConfigureOptions(IServiceCollection services) + { + services.Configure(options => + { + options.TokenLifespan = TimeSpan.FromHours(3); + }); + + services.Configure(options => + { + options.TokenLifespan = TimeSpan.FromDays(2); + }); + + services.Configure(options => + { + options.Tokens.EmailConfirmationTokenProvider = "EmailConfirmation"; + + // Default Lockout settings. + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); + options.Lockout.MaxFailedAccessAttempts = 5; + options.Lockout.AllowedForNewUsers = true; + }); + + services.Configure(option => + { + // option.IterationCount = 10000; + // option.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2; + }); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/PasswordHasher.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/PasswordHasher.cs new file mode 100644 index 000000000..8eef788d1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/PasswordHasher.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Identity; +using Microsoft.AspNetCore.Identity; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class PasswordHasher : IPasswordHasher + { + public bool VerifyHashedPassword(User user, string hashedPassword, string providedPassword) + { + return new PasswordHasher().VerifyHashedPassword(user, hashedPassword, providedPassword) != PasswordVerificationResult.Failed; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/RoleStore.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/RoleStore.cs new file mode 100644 index 000000000..5695ec569 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/RoleStore.cs @@ -0,0 +1,73 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using Microsoft.AspNetCore.Identity; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class RoleStore : IRoleStore + { + private readonly IRepository _roleRepository; + + public RoleStore(IRepository roleRepository) + { + _roleRepository = roleRepository; + } + + public void Dispose() + { + } + + public Task CreateAsync(Role role, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task DeleteAsync(Role role, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task FindByIdAsync(string roleId, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task GetRoleIdAsync(Role role, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task GetRoleNameAsync(Role role, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task UpdateAsync(Role role, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/UserStore.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/UserStore.cs new file mode 100644 index 000000000..035762ebf --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/UserStore.cs @@ -0,0 +1,324 @@ +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class UserStore : IUserStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserPhoneNumberStore, + IUserTwoFactorStore, + IUserLockoutStore, + IUserAuthenticationTokenStore, + IUserAuthenticatorKeyStore, + IUserTwoFactorRecoveryCodeStore + { + private readonly IUnitOfWork _unitOfWork; + private readonly IUserRepository _userRepository; + + public UserStore(IUserRepository userRepository) + { + _unitOfWork = userRepository.UnitOfWork; + _userRepository = userRepository; + } + + public void Dispose() + { + } + + public async Task CreateAsync(User user, CancellationToken cancellationToken) + { + user.PasswordHistories = new List() + { + new PasswordHistory + { + PasswordHash = user.PasswordHash, + CreatedDateTime = DateTimeOffset.Now, + }, + }; + await _userRepository.AddOrUpdateAsync(user); + await _unitOfWork.SaveChangesAsync(); + return IdentityResult.Success; + } + + public Task DeleteAsync(User user, CancellationToken cancellationToken) + { + _userRepository.Delete(user); + return Task.FromResult(IdentityResult.Success); + } + + public Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken) + { + return _userRepository.Get(new UserQueryOptions { IncludeTokens = true }).FirstOrDefaultAsync(x => x.NormalizedEmail == normalizedEmail); + } + + public Task FindByIdAsync(string userId, CancellationToken cancellationToken) + { + return _userRepository.Get(new UserQueryOptions { IncludeTokens = true }).FirstOrDefaultAsync(x => x.Id == Guid.Parse(userId)); + } + + public Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken) + { + return _userRepository.Get(new UserQueryOptions { IncludeTokens = true }).FirstOrDefaultAsync(x => x.NormalizedUserName == normalizedUserName); + } + + public Task GetAccessFailedCountAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.AccessFailedCount); + } + + public Task GetEmailAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.Email); + } + + public Task GetEmailConfirmedAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.EmailConfirmed); + } + + public Task GetLockoutEnabledAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.LockoutEnabled); + } + + public Task GetLockoutEndDateAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.LockoutEnd); + } + + public Task GetNormalizedEmailAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.NormalizedEmail); + } + + public Task GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.NormalizedUserName); + } + + public Task GetPasswordHashAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.PasswordHash); + } + + public Task GetPhoneNumberAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.PhoneNumber); + } + + public Task GetPhoneNumberConfirmedAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.PhoneNumberConfirmed); + } + + public Task GetSecurityStampAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.SecurityStamp ?? string.Empty); + } + + public Task GetTwoFactorEnabledAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.TwoFactorEnabled); + } + + public Task GetUserIdAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.Id.ToString()); + } + + public Task GetUserNameAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.UserName); + } + + public Task HasPasswordAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.PasswordHash != null); + } + + public Task IncrementAccessFailedCountAsync(User user, CancellationToken cancellationToken) + { + user.AccessFailedCount++; + return Task.FromResult(user.AccessFailedCount); + } + + public Task ResetAccessFailedCountAsync(User user, CancellationToken cancellationToken) + { + user.AccessFailedCount = 0; + return Task.CompletedTask; + } + + public Task SetEmailAsync(User user, string email, CancellationToken cancellationToken) + { + user.Email = email; + return Task.CompletedTask; + } + + public Task SetEmailConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken) + { + user.EmailConfirmed = confirmed; + return Task.CompletedTask; + } + + public Task SetLockoutEnabledAsync(User user, bool enabled, CancellationToken cancellationToken) + { + user.LockoutEnabled = enabled; + return Task.CompletedTask; + } + + public Task SetLockoutEndDateAsync(User user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken) + { + user.LockoutEnd = lockoutEnd; + return Task.CompletedTask; + } + + public Task SetNormalizedEmailAsync(User user, string normalizedEmail, CancellationToken cancellationToken) + { + user.NormalizedEmail = normalizedEmail; + return Task.CompletedTask; + } + + public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken) + { + user.NormalizedUserName = normalizedName; + return Task.CompletedTask; + } + + public Task SetPasswordHashAsync(User user, string passwordHash, CancellationToken cancellationToken) + { + user.PasswordHash = passwordHash; + return Task.CompletedTask; + } + + public Task SetPhoneNumberAsync(User user, string phoneNumber, CancellationToken cancellationToken) + { + user.PhoneNumber = phoneNumber; + return Task.CompletedTask; + } + + public Task SetPhoneNumberConfirmedAsync(User user, bool confirmed, CancellationToken cancellationToken) + { + user.PhoneNumberConfirmed = confirmed; + return Task.CompletedTask; + } + + public Task SetSecurityStampAsync(User user, string stamp, CancellationToken cancellationToken) + { + user.SecurityStamp = stamp; + return Task.CompletedTask; + } + + public Task SetTwoFactorEnabledAsync(User user, bool enabled, CancellationToken cancellationToken) + { + user.TwoFactorEnabled = enabled; + return Task.CompletedTask; + } + + public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken) + { + user.UserName = userName; + return Task.CompletedTask; + } + + public async Task UpdateAsync(User user, CancellationToken cancellationToken) + { + await _userRepository.AddOrUpdateAsync(user); + await _unitOfWork.SaveChangesAsync(); + return IdentityResult.Success; + } + + private const string AuthenticatorStoreLoginProvider = "[AuthenticatorStore]"; + private const string AuthenticatorKeyTokenName = "AuthenticatorKey"; + private const string RecoveryCodeTokenName = "RecoveryCodes"; + + public Task GetTokenAsync(User user, string loginProvider, string name, CancellationToken cancellationToken) + { + var tokenEntity = user.Tokens.SingleOrDefault( + l => l.TokenName == name && l.LoginProvider == loginProvider); + return Task.FromResult(tokenEntity?.TokenValue); + } + + public async Task SetTokenAsync(User user, string loginProvider, string name, string value, CancellationToken cancellationToken) + { + var tokenEntity = user.Tokens.SingleOrDefault( + l => l.TokenName == name && l.LoginProvider == loginProvider); + if (tokenEntity != null) + { + tokenEntity.TokenValue = value; + } + else + { + user.Tokens.Add(new UserToken + { + UserId = user.Id, + LoginProvider = loginProvider, + TokenName = name, + TokenValue = value, + }); + } + + await _unitOfWork.SaveChangesAsync(); + } + + public async Task RemoveTokenAsync(User user, string loginProvider, string name, CancellationToken cancellationToken) + { + var tokenEntity = user.Tokens.SingleOrDefault( + l => l.TokenName == name && l.LoginProvider == loginProvider); + if (tokenEntity != null) + { + user.Tokens.Remove(tokenEntity); + await _unitOfWork.SaveChangesAsync(); + } + } + + public Task SetAuthenticatorKeyAsync(User user, string key, CancellationToken cancellationToken) + { + return SetTokenAsync(user, AuthenticatorStoreLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken); + } + + public Task GetAuthenticatorKeyAsync(User user, CancellationToken cancellationToken) + { + return GetTokenAsync(user, AuthenticatorStoreLoginProvider, AuthenticatorKeyTokenName, cancellationToken); + } + + public Task ReplaceCodesAsync(User user, IEnumerable recoveryCodes, CancellationToken cancellationToken) + { + var mergedCodes = string.Join(";", recoveryCodes); + return SetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken); + } + + public async Task RedeemCodeAsync(User user, string code, CancellationToken cancellationToken) + { + var mergedCodes = await GetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? string.Empty; + var splitCodes = mergedCodes.Split(';'); + if (splitCodes.Contains(code)) + { + var updatedCodes = new List(splitCodes.Where(s => s != code)); + await ReplaceCodesAsync(user, updatedCodes, cancellationToken); + return true; + } + + return false; + } + + public async Task CountCodesAsync(User user, CancellationToken cancellationToken) + { + var mergedCodes = await GetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? string.Empty; + if (mergedCodes.Length > 0) + { + return mergedCodes.Split(';').Length; + } + + return 0; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/WeakPasswordValidator.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/WeakPasswordValidator.cs new file mode 100644 index 000000000..586fc5718 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Identity/WeakPasswordValidator.cs @@ -0,0 +1,25 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.AspNetCore.Identity; +using System; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Identity +{ + public class WeakPasswordValidator : IPasswordValidator + { + public Task ValidateAsync(UserManager manager, User user, string password) + { + if (password.Contains("testweakpassword")) + { + return Task.FromResult(IdentityResult.Failed(new IdentityError + { + Code = "WeakPassword", + Description = "WeakPasswordValidator testing.", + })); + } + + // TODO: check weak password, leaked password, password histories, etc. + return Task.FromResult(IdentityResult.Success); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/ErrorCatchingInterceptor.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/ErrorCatchingInterceptor.cs new file mode 100644 index 000000000..405f04fb7 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/ErrorCatchingInterceptor.cs @@ -0,0 +1,92 @@ +using Castle.DynamicProxy; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Interceptors +{ + public class ErrorCatchingInterceptor : IInterceptor + { + private readonly ILogger _logger; + + public ErrorCatchingInterceptor(ILogger logger) + { + _logger = logger; + } + + public void Intercept(IInvocation invocation) + { + try + { + var returnType = invocation.Method.ReturnType; + if (returnType == typeof(Task)) + { + invocation.Proceed(); + invocation.ReturnValue = InterceptResultAsync((dynamic)invocation.ReturnValue, invocation); + } + else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + invocation.Proceed(); + invocation.ReturnValue = InterceptResultAsync((dynamic)invocation.ReturnValue, invocation); + } + else + { + invocation.Proceed(); + } + } + catch (Exception ex) + { + LogException(invocation, ex); + throw; + } + } + + private async Task InterceptResultAsync(Task task, IInvocation invocation) + { + try + { + await task.ConfigureAwait(false); + } + catch (Exception ex) + { + LogException(invocation, ex); + throw; + } + } + + private async Task InterceptResultAsync(Task task, IInvocation invocation) + { + try + { + T result = await task.ConfigureAwait(false); + return result; + } + catch (Exception ex) + { + LogException(invocation, ex); + throw; + } + } + + private void LogException(IInvocation invocation, Exception ex) + { + var method = invocation.Method; + var className = method.DeclaringType.Name; + var methodName = method.Name; + + // TODO: Ignore serialize large argument object. + var arguments = JsonSerializer.Serialize(invocation.Arguments.Where(x => x.GetType() != typeof(CancellationToken)), new JsonSerializerOptions() + { + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + ReferenceHandler = ReferenceHandler.IgnoreCycles, + }); + + _logger.LogError($"An unhandled exception has occurred while executing the method: {className}.{methodName} with ({arguments}). {Environment.NewLine}{ex}"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/InterceptorsOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/InterceptorsOptions.cs new file mode 100644 index 000000000..1e01bab51 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/InterceptorsOptions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.Interceptors +{ + public class InterceptorsOptions + { + public bool LoggingInterceptor { get; set; } + + public bool ErrorCatchingInterceptor { get; set; } + + public Type[] GetInterceptors() + { + var interceptors = new List(); + if (LoggingInterceptor) + { + interceptors.Add(typeof(LoggingInterceptor)); + } + + if (ErrorCatchingInterceptor) + { + interceptors.Add(typeof(ErrorCatchingInterceptor)); + } + + return interceptors.ToArray(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/LoggingInterceptor.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/LoggingInterceptor.cs new file mode 100644 index 000000000..22ef312c7 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/LoggingInterceptor.cs @@ -0,0 +1,84 @@ +using Castle.DynamicProxy; +using Microsoft.Extensions.Logging; +using System.Diagnostics; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Interceptors +{ + public class LoggingInterceptor : IInterceptor + { + private readonly ILogger _logger; + + public LoggingInterceptor(ILogger logger) + { + _logger = logger; + } + + public void Intercept(IInvocation invocation) + { + var method = invocation.Method; + var className = method.DeclaringType.Name; + var methodName = method.Name; + + var arguments = JsonSerializer.Serialize(invocation.Arguments.Where(x => x.GetType() != typeof(CancellationToken)), new JsonSerializerOptions() + { + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + ReferenceHandler = ReferenceHandler.IgnoreCycles, + }); + + _logger.LogDebug($"Start calling method: {className}.{methodName} with ({arguments})."); + + var watch = new Stopwatch(); + watch.Start(); + + var returnType = invocation.Method.ReturnType; + if (returnType == typeof(Task)) + { + invocation.Proceed(); + invocation.ReturnValue = InterceptResultAsync((dynamic)invocation.ReturnValue, watch, className, methodName); + } + else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) + { + invocation.Proceed(); + invocation.ReturnValue = InterceptResultAsync((dynamic)invocation.ReturnValue, watch, className, methodName); + } + else + { + invocation.Proceed(); + InterceptResult(invocation.ReturnValue, watch, className, methodName); + } + } + + private void InterceptResult(object returnValue, Stopwatch watch, string className, string methodName) + { + watch.Stop(); + + _logger.LogDebug($"Finished calling method: {className}.{methodName}. Took: {watch.ElapsedMilliseconds} milliseconds"); + } + + private async Task InterceptResultAsync(Task task, Stopwatch watch, string className, string methodName) + { + await task.ConfigureAwait(false); + + watch.Stop(); + + _logger.LogDebug($"Finished calling method: {className}.{methodName}. Took: {watch.ElapsedMilliseconds} milliseconds"); + } + + private async Task InterceptResultAsync(Task task, Stopwatch watch, string className, string methodName) + { + T result = await task.ConfigureAwait(false); + + watch.Stop(); + + _logger.LogDebug($"Finished calling method: {className}.{methodName}. Took: {watch.ElapsedMilliseconds} milliseconds"); + + return result; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/Reference.md b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/Reference.md new file mode 100644 index 000000000..7496a4f09 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/Reference.md @@ -0,0 +1,2 @@ +- [c# - Intercept the call to an async method using DynamicProxy - Stack Overflow](https://stackoverflow.com/questions/14288075/intercept-the-call-to-an-async-method-using-dynamicproxy/14288799) +- [c# - Intercept async method that returns generic Task<> via DynamicProxy - Stack Overflow](https://stackoverflow.com/questions/28099669/intercept-async-method-that-returns-generic-task-via-dynamicproxy) diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/ServiceCollectionServiceExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/ServiceCollectionServiceExtensions.cs new file mode 100644 index 000000000..980128819 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Interceptors/ServiceCollectionServiceExtensions.cs @@ -0,0 +1,47 @@ +using Castle.DynamicProxy; +using ClassifiedAds.Infrastructure.Interceptors; +using System; +using System.Linq; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionServiceExtensions + { + public static IServiceCollection ConfigureInterceptors(this IServiceCollection services) + { + services.AddSingleton(new ProxyGenerator()); + services.AddTransient(); + services.AddTransient(); + return services; + } + + public static IServiceCollection AddInterceptors(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime serviceLifetime, Type[] interceptorTypes) + { + if (interceptorTypes == null || !interceptorTypes.Any()) + { + return services; + } + + services.Add(new ServiceDescriptor(implementationType, implementationType, serviceLifetime)); + + var serviceDescriptor = new ServiceDescriptor(serviceType, provider => + { + var target = provider.GetService(implementationType); + + var interceptors = interceptorTypes.Select(x => (IInterceptor)provider.GetService(x)).ToArray(); + + var proxy = provider.GetService().CreateInterfaceProxyWithTarget(serviceType, target, interceptors); + return proxy; + }, serviceLifetime); + + services.Add(serviceDescriptor); + + return services; + } + + public static IServiceCollection AddInterceptors(this IServiceCollection services, Type serviceType, Type implementationType, ServiceLifetime serviceLifetime, InterceptorsOptions interceptorsOptions) + { + return services.AddInterceptors(serviceType, implementationType, serviceLifetime, interceptorsOptions?.GetInterceptors()); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/DefaultStringLocalizer.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/DefaultStringLocalizer.cs new file mode 100644 index 000000000..25b0fa0d1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/DefaultStringLocalizer.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.Localization; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.Localization +{ + public class DefaultStringLocalizer : IStringLocalizer + { + public LocalizedString this[string name] + { + get + { + return new LocalizedString(name, name); + } + } + + public LocalizedString this[string name, params object[] arguments] + { + get + { + var value = string.Format(name, arguments); + return new LocalizedString(name, value); + } + } + + public IEnumerable GetAllStrings(bool includeParentCultures) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/LocalizationProviders.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/LocalizationProviders.cs new file mode 100644 index 000000000..dde7fead5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/LocalizationProviders.cs @@ -0,0 +1,7 @@ +namespace ClassifiedAds.Infrastructure.Localization +{ + public class LocalizationProviders + { + public SqlServerOptions SqlServer { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/LocalizationServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/LocalizationServiceCollectionExtensions.cs new file mode 100644 index 000000000..4c20aef14 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/LocalizationServiceCollectionExtensions.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Localization; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using System.Globalization; + +namespace ClassifiedAds.Infrastructure.Localization +{ + public static class LocalizationServiceCollectionExtensions + { + public static IServiceCollection AddClassifiedAdsLocalization(this IServiceCollection services, LocalizationProviders providers = null) + { + if (providers?.SqlServer?.IsEnabled ?? false) + { + services.Configure(op => + { + op.ConnectionString = providers.SqlServer.ConnectionString; + op.SqlQuery = providers.SqlServer.SqlQuery; + op.CacheMinutes = providers.SqlServer.CacheMinutes; + }); + + services.AddSingleton(); + services.AddScoped(provider => provider.GetRequiredService().Create(null)); + } + else + { + services.AddScoped(); + } + + services.AddLocalization(); + + services.Configure(options => + { + var supportedCultures = new[] + { + new CultureInfo("en-US"), + new CultureInfo("vi-VN"), + }; + + options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US"); + options.SupportedCultures = supportedCultures; + options.SupportedUICultures = supportedCultures; + + options.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context => + { + // My custom request culture logic + // return new ProviderCultureResult("vi-VN"); + return new ProviderCultureResult("en-US"); + })); + }); + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerOptions.cs new file mode 100644 index 000000000..874c653a1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerOptions.cs @@ -0,0 +1,13 @@ +namespace ClassifiedAds.Infrastructure.Localization +{ + public class SqlServerOptions + { + public bool IsEnabled { get; set; } + + public string ConnectionString { get; set; } + + public string SqlQuery { get; set; } + + public int CacheMinutes { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerStringLocalizer.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerStringLocalizer.cs new file mode 100644 index 000000000..c6f895026 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerStringLocalizer.cs @@ -0,0 +1,53 @@ +using Microsoft.Extensions.Localization; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace ClassifiedAds.Infrastructure.Localization +{ + public class SqlServerStringLocalizer : IStringLocalizer + { + private readonly Dictionary> _data; + + public SqlServerStringLocalizer(Dictionary> data) + { + _data = data; + } + + public LocalizedString this[string name] + { + get + { + var value = GetString(name); + return new LocalizedString(name, value); + } + } + + public LocalizedString this[string name, params object[] arguments] + { + get + { + var format = GetString(name); + var value = string.Format(format ?? name, arguments); + return new LocalizedString(name, value); + } + } + + public IEnumerable GetAllStrings(bool includeParentCultures) + { + throw new NotImplementedException(); + } + + private string GetString(string name) + { + var culture = CultureInfo.CurrentCulture.ToString(); + + if (_data.ContainsKey(name) && _data[name].ContainsKey(culture)) + { + return _data[name][culture]; + } + + return name; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerStringLocalizerFactory.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerStringLocalizerFactory.cs new file mode 100644 index 000000000..e615b7c0c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Localization/SqlServerStringLocalizerFactory.cs @@ -0,0 +1,62 @@ +using Dapper; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Options; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ClassifiedAds.Infrastructure.Localization +{ + public class SqlServerStringLocalizerFactory : IStringLocalizerFactory + { + private readonly SqlServerOptions _options; + private readonly IMemoryCache _memoryCache; + + public SqlServerStringLocalizerFactory(IOptions options, IMemoryCache memoryCache) + { + _options = options.Value; + _memoryCache = memoryCache; + } + + public IStringLocalizer Create(Type resourceSource) + { + return new SqlServerStringLocalizer(LoadData()); + } + + public IStringLocalizer Create(string baseName, string location) + { + return new SqlServerStringLocalizer(LoadData()); + } + + private Dictionary> LoadData() + { + var data = _memoryCache.Get>>(typeof(SqlServerStringLocalizerFactory).FullName); + + if (data == null) + { + using var conn = new SqlConnection(_options.ConnectionString); + { + conn.Open(); + data = conn.Query(_options.SqlQuery) + .GroupBy(x => x.Name) + .ToDictionary(x => x.Key, x => x.ToDictionary(y => y.Culture, y => y.Value)); + } + + _memoryCache.Set(typeof(SqlServerStringLocalizerFactory).FullName, data, DateTimeOffset.Now.AddMinutes(_options.CacheMinutes)); + } + + return data; + } + } + + public class LocalizationEntry + { + public string Name { get; set; } + + public string Value { get; set; } + + public string Culture { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ActivityEnricher.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ActivityEnricher.cs new file mode 100644 index 000000000..3fb3b9a47 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ActivityEnricher.cs @@ -0,0 +1,61 @@ +/* + * https://github.com/serilog/serilog-aspnetcore/issues/207 + * + */ + +using Serilog.Core; +using Serilog.Events; +using System.Diagnostics; + +namespace ClassifiedAds.Infrastructure.Logging +{ + public class ActivityEnricher : ILogEventEnricher + { + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + var activity = Activity.Current; + + if (activity == null) + { + return; + } + + logEvent.AddPropertyIfAbsent(new LogEventProperty("SpanId", new ScalarValue(activity.GetSpanId()))); + logEvent.AddPropertyIfAbsent(new LogEventProperty("TraceId", new ScalarValue(activity.GetTraceId()))); + logEvent.AddPropertyIfAbsent(new LogEventProperty("ParentId", new ScalarValue(activity.GetParentId()))); + } + } + + internal static class ActivityExtensions + { + public static string GetSpanId(this Activity activity) + { + return activity.IdFormat switch + { + ActivityIdFormat.Hierarchical => activity.Id, + ActivityIdFormat.W3C => activity.SpanId.ToHexString(), + _ => null, + } ?? string.Empty; + } + + public static string GetTraceId(this Activity activity) + { + return activity.IdFormat switch + { + ActivityIdFormat.Hierarchical => activity.RootId, + ActivityIdFormat.W3C => activity.TraceId.ToHexString(), + _ => null, + } ?? string.Empty; + } + + public static string GetParentId(this Activity activity) + { + return activity.IdFormat switch + { + ActivityIdFormat.Hierarchical => activity.ParentId, + ActivityIdFormat.W3C => activity.ParentSpanId.ToHexString(), + _ => null, + } ?? string.Empty; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ApplicationInsightsOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ApplicationInsightsOptions.cs new file mode 100644 index 000000000..47f9524ea --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ApplicationInsightsOptions.cs @@ -0,0 +1,9 @@ +namespace ClassifiedAds.Infrastructure.Logging +{ + public class ApplicationInsightsOptions + { + public bool IsEnabled { get; set; } + + public string InstrumentationKey { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ElasticsearchOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ElasticsearchOptions.cs new file mode 100644 index 000000000..3465757f4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/ElasticsearchOptions.cs @@ -0,0 +1,15 @@ +using Serilog.Events; + +namespace ClassifiedAds.Infrastructure.Logging +{ + public class ElasticsearchOptions + { + public bool IsEnabled { get; set; } + + public string Host { get; set; } + + public string IndexFormat { get; set; } + + public LogEventLevel MinimumLogEventLevel { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/EventLogOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/EventLogOptions.cs new file mode 100644 index 000000000..b36f1d9ba --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/EventLogOptions.cs @@ -0,0 +1,11 @@ +namespace ClassifiedAds.Infrastructure.Logging +{ + public class EventLogOptions + { + public bool IsEnabled { get; set; } + + public string LogName { get; set; } + + public string SourceName { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/FileOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/FileOptions.cs new file mode 100644 index 000000000..4fa1eb91a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/FileOptions.cs @@ -0,0 +1,9 @@ +using Serilog.Events; + +namespace ClassifiedAds.Infrastructure.Logging +{ + public class FileOptions + { + public LogEventLevel MinimumLogEventLevel { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/LoggingExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/LoggingExtensions.cs new file mode 100644 index 000000000..fe02c0c7c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/LoggingExtensions.cs @@ -0,0 +1,261 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.EventLog; +using Serilog; +using Serilog.Exceptions; +using Serilog.Formatting.Json; +using Serilog.Sinks.Elasticsearch; +using Serilog.Sinks.File; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; + +namespace ClassifiedAds.Infrastructure.Logging +{ + public static class LoggingExtensions + { + private static void UseClassifiedAdsLogger(this IWebHostEnvironment env, LoggingOptions options) + { + var assemblyName = Assembly.GetEntryAssembly()?.GetName().Name; + + var logsPath = Path.Combine(env.ContentRootPath, "logs"); + Directory.CreateDirectory(logsPath); + var loggerConfiguration = new LoggerConfiguration(); + + loggerConfiguration = loggerConfiguration + .MinimumLevel.Debug() + .Enrich.FromLogContext() + .Enrich.With() + .Enrich.WithMachineName() + .Enrich.WithEnvironmentUserName() + .Enrich.WithProperty("Assembly", assemblyName) + .Enrich.WithProperty("Application", env.ApplicationName) + .Enrich.WithProperty("EnvironmentName", env.EnvironmentName) + .Enrich.WithProperty("ContentRootPath", env.ContentRootPath) + .Enrich.WithProperty("WebRootPath", env.WebRootPath) + .Enrich.WithExceptionDetails() + .Filter.ByIncludingOnly((logEvent) => + { + if (logEvent.Level >= options.File.MinimumLogEventLevel + || logEvent.Level >= options.Elasticsearch.MinimumLogEventLevel) + { + var sourceContext = logEvent.Properties.ContainsKey("SourceContext") + ? logEvent.Properties["SourceContext"].ToString() + : null; + + var logLevel = GetLogLevel(sourceContext, options); + + return logEvent.Level >= logLevel; + } + + return false; + }) + .WriteTo.File(Path.Combine(logsPath, "log.txt"), + fileSizeLimitBytes: 10 * 1024 * 1024, + rollOnFileSizeLimit: true, + shared: true, + flushToDiskInterval: TimeSpan.FromSeconds(1), + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] [TraceId: {TraceId}] {Message:lj}{NewLine}{Exception}", + restrictedToMinimumLevel: options.File.MinimumLogEventLevel); + + if (options.Elasticsearch != null && options.Elasticsearch.IsEnabled) + { + loggerConfiguration = loggerConfiguration + .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(options.Elasticsearch.Host)) + { + MinimumLogEventLevel = options.Elasticsearch.MinimumLogEventLevel, + AutoRegisterTemplate = true, + AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6, + IndexFormat = options.Elasticsearch.IndexFormat + "-{0:yyyy.MM.dd}", + // BufferBaseFilename = Path.Combine(env.ContentRootPath, "logs", "buffer"), + InlineFields = true, + EmitEventFailure = EmitEventFailureHandling.WriteToFailureSink, + FailureSink = new FileSink(Path.Combine(logsPath, "elasticsearch-failures.txt"), new JsonFormatter(), null), + }); + } + + Log.Logger = loggerConfiguration.CreateLogger(); + } + + private static LoggingOptions SetDefault(LoggingOptions options) + { + options ??= new LoggingOptions + { + }; + + options.LogLevel ??= new Dictionary(); + + if (!options.LogLevel.ContainsKey("Default")) + { + options.LogLevel["Default"] = "Warning"; + } + + options.File ??= new FileOptions + { + MinimumLogEventLevel = Serilog.Events.LogEventLevel.Warning, + }; + + options.Elasticsearch ??= new ElasticsearchOptions + { + IsEnabled = false, + MinimumLogEventLevel = Serilog.Events.LogEventLevel.Warning, + }; + + options.EventLog ??= new EventLogOptions + { + IsEnabled = false, + }; + return options; + } + + private static Serilog.Events.LogEventLevel GetLogLevel(string context, LoggingOptions options) + { + context = context.Replace("\"", string.Empty); + string level = "Default"; + var matches = options.LogLevel.Keys.Where(k => context.StartsWith(k, StringComparison.OrdinalIgnoreCase)); + + if (matches.Any()) + { + level = matches.Max(); + } + + return (Serilog.Events.LogEventLevel)Enum.Parse(typeof(Serilog.Events.LogEventLevel), options.LogLevel[level], true); + } + + public static IWebHostBuilder UseClassifiedAdsLogger(this IWebHostBuilder builder, Func logOptions) + { + builder.ConfigureLogging((context, logging) => + { + logging.Configure(options => + { + // options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId | ActivityTrackingOptions.TraceId | ActivityTrackingOptions.ParentId; + }); + + logging.AddAzureWebAppDiagnostics(); + + logging.AddSerilog(); + + LoggingOptions options = SetDefault(logOptions(context.Configuration)); + + if (options.EventLog != null && options.EventLog.IsEnabled) + { + logging.AddEventLog(new EventLogSettings + { + LogName = options.EventLog.LogName, + SourceName = options.EventLog.SourceName, + }); + } + + if (options?.ApplicationInsights?.IsEnabled ?? false) + { + logging.AddApplicationInsights(options.ApplicationInsights.InstrumentationKey); + } + + context.HostingEnvironment.UseClassifiedAdsLogger(options); + }); + + return builder; + } + + public static IHostBuilder UseClassifiedAdsLogger(this IHostBuilder builder, Func logOptions) + { + builder.ConfigureLogging((context, logging) => + { + logging.Configure(options => + { + // options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId | ActivityTrackingOptions.TraceId | ActivityTrackingOptions.ParentId; + }); + + logging.AddAzureWebAppDiagnostics(); + + logging.AddSerilog(); + + LoggingOptions options = SetDefault(logOptions(context.Configuration)); + + if (options.EventLog != null && options.EventLog.IsEnabled) + { + logging.AddEventLog(new EventLogSettings + { + LogName = options.EventLog.LogName, + SourceName = options.EventLog.SourceName, + }); + } + + if (options?.ApplicationInsights?.IsEnabled ?? false) + { + logging.AddApplicationInsights(options.ApplicationInsights.InstrumentationKey); + } + + context.HostingEnvironment.UseClassifiedAdsLogger(options); + }); + + return builder; + } + + private static void UseClassifiedAdsLogger(this IHostEnvironment env, LoggingOptions options) + { + var assemblyName = Assembly.GetEntryAssembly()?.GetName().Name; + + var logsPath = Path.Combine(env.ContentRootPath, "logs"); + Directory.CreateDirectory(logsPath); + var loggerConfiguration = new LoggerConfiguration(); + + loggerConfiguration = loggerConfiguration + .MinimumLevel.Debug() + .Enrich.FromLogContext() + .Enrich.With() + .Enrich.WithMachineName() + .Enrich.WithEnvironmentUserName() + .Enrich.WithProperty("Assembly", assemblyName) + .Enrich.WithProperty("Application", env.ApplicationName) + .Enrich.WithProperty("EnvironmentName", env.EnvironmentName) + .Enrich.WithProperty("ContentRootPath", env.ContentRootPath) + .Enrich.WithExceptionDetails() + .Filter.ByIncludingOnly((logEvent) => + { + if (logEvent.Level >= options.File.MinimumLogEventLevel + || logEvent.Level >= options.Elasticsearch.MinimumLogEventLevel) + { + var sourceContext = logEvent.Properties.ContainsKey("SourceContext") + ? logEvent.Properties["SourceContext"].ToString() + : null; + + var logLevel = GetLogLevel(sourceContext, options); + + return logEvent.Level >= logLevel; + } + + return false; + }) + .WriteTo.File(Path.Combine(logsPath, "log.txt"), + fileSizeLimitBytes: 10 * 1024 * 1024, + rollOnFileSizeLimit: true, + shared: true, + flushToDiskInterval: TimeSpan.FromSeconds(1), + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] [TraceId: {TraceId}] {Message:lj}{NewLine}{Exception}", + restrictedToMinimumLevel: options.File.MinimumLogEventLevel); + + if (options.Elasticsearch != null && options.Elasticsearch.IsEnabled) + { + loggerConfiguration = loggerConfiguration + .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri(options.Elasticsearch.Host)) + { + MinimumLogEventLevel = options.Elasticsearch.MinimumLogEventLevel, + AutoRegisterTemplate = true, + AutoRegisterTemplateVersion = AutoRegisterTemplateVersion.ESv6, + IndexFormat = options.Elasticsearch.IndexFormat + "-{0:yyyy.MM.dd}", + // BufferBaseFilename = Path.Combine(env.ContentRootPath, "logs", "buffer"), + InlineFields = true, + EmitEventFailure = EmitEventFailureHandling.WriteToFailureSink, + FailureSink = new FileSink(Path.Combine(logsPath, "elasticsearch-failures.txt"), new JsonFormatter(), null), + }); + } + + Log.Logger = loggerConfiguration.CreateLogger(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/LoggingOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/LoggingOptions.cs new file mode 100644 index 000000000..ef51eac0f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Logging/LoggingOptions.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.Logging +{ + public class LoggingOptions + { + public Dictionary LogLevel { get; set; } + + public FileOptions File { get; set; } + + public ElasticsearchOptions Elasticsearch { get; set; } + + public EventLogOptions EventLog { get; set; } + + public ApplicationInsightsOptions ApplicationInsights { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventGrid/AzureEventGridOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventGrid/AzureEventGridOptions.cs new file mode 100644 index 000000000..7ac06c599 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventGrid/AzureEventGridOptions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureEventGrid +{ + public class AzureEventGridOptions + { + public string DomainEndpoint { get; set; } + + public string DomainKey { get; set; } + + public Dictionary Topics { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventGrid/AzureEventGridSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventGrid/AzureEventGridSender.cs new file mode 100644 index 000000000..cf26f455c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventGrid/AzureEventGridSender.cs @@ -0,0 +1,51 @@ +using Azure; +using Azure.Messaging.EventGrid; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureEventGrid +{ + public class AzureEventGridSender : IMessageSender + { + private readonly string _domainEndpoint; + private readonly string _domainKey; + private readonly string _topic; + + public AzureEventGridSender(string domainEndpoint, string domainKey, string topic) + { + _domainEndpoint = domainEndpoint; + _domainKey = domainKey; + _topic = topic; + } + + public async Task SendAsync(T message, MetaData metaData, CancellationToken cancellationToken = default) + { + EventGridPublisherClient client = new EventGridPublisherClient(new Uri(_domainEndpoint), new AzureKeyCredential(_domainKey)); + + var data = new BinaryData(JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + })); + + var events = new List() + { + new EventGridEvent("TEST", typeof(T).FullName, "1.0", data) + { + Id = Guid.NewGuid().ToString(), + EventType = typeof(T).FullName, + Topic = _topic, + EventTime = DateTime.UtcNow, + Subject = "TEST", + DataVersion = "1.0", + }, + }; + + await client.SendEventsAsync(events, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubOptions.cs new file mode 100644 index 000000000..16d11638a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubOptions.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureEventHub +{ + public class AzureEventHubOptions + { + public string ConnectionString { get; set; } + + public string StorageConnectionString { get; set; } + + public Dictionary Hubs { get; set; } + + public Dictionary StorageContainerNames { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubReceiver.cs new file mode 100644 index 000000000..b36db8c4d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubReceiver.cs @@ -0,0 +1,82 @@ +using Azure.Messaging.EventHubs; +using Azure.Messaging.EventHubs.Consumer; +using Azure.Messaging.EventHubs.Processor; +using Azure.Storage.Blobs; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureEventHub +{ + public class AzureEventHubReceiver : IMessageReceiver, IDisposable + { + private readonly string _eventHubConnectionString; + private readonly string _eventHubName; + private readonly string _storageConnectionString; + private readonly string _storageContainerName; + + public AzureEventHubReceiver(string eventHubConnectionString, string eventHubName, string storageConnectionString, string storageContainerName) + { + _eventHubConnectionString = eventHubConnectionString; + _eventHubName = eventHubName; + _storageConnectionString = storageConnectionString; + _storageContainerName = storageContainerName; + } + + public void Dispose() + { + } + + public void Receive(Action action) + { + ReceiveAsync(action).GetAwaiter().GetResult(); + } + + public async Task ReceiveAsync(Action action) + { + var storageClient = new BlobContainerClient(_storageConnectionString, _storageContainerName); + + Task ProcessEventHandler(ProcessEventArgs eventArgs) + { + try + { + var messageAsString = Encoding.UTF8.GetString(eventArgs.Data.EventBody); + var message = JsonSerializer.Deserialize>(messageAsString); + action(message.Data, message.MetaData); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return Task.CompletedTask; + } + + Task ProcessErrorHandler(ProcessErrorEventArgs eventArgs) + { + try + { + Console.WriteLine(eventArgs.Exception); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + + return Task.CompletedTask; + } + + var processor = new EventProcessorClient( + storageClient, + EventHubConsumerClient.DefaultConsumerGroupName, + _eventHubConnectionString, + _eventHubName); + + processor.ProcessEventAsync += ProcessEventHandler; + processor.ProcessErrorAsync += ProcessErrorHandler; + await processor.StartProcessingAsync(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubSender.cs new file mode 100644 index 000000000..1d9961f56 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureEventHub/AzureEventHubSender.cs @@ -0,0 +1,40 @@ +using Azure.Messaging.EventHubs; +using Azure.Messaging.EventHubs.Producer; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureEventHub +{ + public class AzureEventHubSender : IMessageSender + { + private readonly string _connectionString; + private readonly string _hubName; + + public AzureEventHubSender(string connectionString, string hubName) + { + _connectionString = connectionString; + _hubName = hubName; + } + + public async Task SendAsync(T message, MetaData metaData, CancellationToken cancellationToken = default) + { + var producer = new EventHubProducerClient(_connectionString, _hubName); + + var events = new List + { + new EventData(JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + })), + }; + + await producer.SendAsync(events, cancellationToken); + + await producer.CloseAsync(cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueOptions.cs new file mode 100644 index 000000000..8a06524ae --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueOptions.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue +{ + public class AzureQueueOptions + { + public string ConnectionString { get; set; } + + public Dictionary QueueNames { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueReceiver.cs new file mode 100644 index 000000000..69bdf85b7 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueReceiver.cs @@ -0,0 +1,77 @@ +using Azure.Storage.Queues; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue +{ + public class AzureQueueReceiver : IMessageReceiver + { + private readonly string _connectionString; + private readonly string _queueName; + private readonly QueueMessageEncoding _messageEncoding; + + public AzureQueueReceiver(string connectionString, string queueName, QueueMessageEncoding messageEncoding = QueueMessageEncoding.None) + { + _connectionString = connectionString; + _queueName = queueName; + _messageEncoding = messageEncoding; + } + + public void Receive(Action action) + { + Task.Factory.StartNew(() => ReceiveAsync(action)); + } + + private Task ReceiveAsync(Action action) + { + return ReceiveStringAsync(retrievedMessage => + { + var message = JsonSerializer.Deserialize>(retrievedMessage); + action(message.Data, message.MetaData); + }); + } + + public void ReceiveString(Action action) + { + Task.Factory.StartNew(() => ReceiveStringAsync(action)); + } + + private async Task ReceiveStringAsync(Action action) + { + var queueClient = new QueueClient(_connectionString, _queueName, new QueueClientOptions + { + MessageEncoding = _messageEncoding, + }); + + await queueClient.CreateIfNotExistsAsync(); + + while (true) + { + try + { + var retrievedMessages = (await queueClient.ReceiveMessagesAsync()).Value; + + if (retrievedMessages.Length > 0) + { + foreach (var retrievedMessage in retrievedMessages) + { + action(retrievedMessage.Body.ToString()); + await queueClient.DeleteMessageAsync(retrievedMessage.MessageId, retrievedMessage.PopReceipt); + } + } + else + { + await Task.Delay(1000); + } + } + catch (Exception ex) + { + Console.WriteLine(ex); + await Task.Delay(1000); + } + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueSender.cs new file mode 100644 index 000000000..1115ef6db --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueSender.cs @@ -0,0 +1,34 @@ +using Azure.Storage.Queues; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue +{ + public class AzureQueueSender : IMessageSender + { + private readonly string _connectionString; + private readonly string _queueName; + + public AzureQueueSender(string connectionString, string queueName) + { + _connectionString = connectionString; + _queueName = queueName; + } + + public async Task SendAsync(T message, MetaData metaData, CancellationToken cancellationToken = default) + { + var queueClient = new QueueClient(_connectionString, _queueName); + await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken); + + var jsonMessage = JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + }); + + await queueClient.SendMessageAsync(jsonMessage, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueStorageHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueStorageHealthCheck.cs new file mode 100644 index 000000000..d96f1b3a5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureQueue/AzureQueueStorageHealthCheck.cs @@ -0,0 +1,41 @@ +using Azure.Storage.Queues; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue +{ + public class AzureQueueStorageHealthCheck : IHealthCheck + { + private readonly string _connectionString; + private readonly string _queueName; + + public AzureQueueStorageHealthCheck(string connectionString, string queueName) + { + _connectionString = connectionString; + _queueName = queueName; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var queueClient = new QueueClient(_connectionString, _queueName); + + if (!await queueClient.ExistsAsync(cancellationToken)) + { + return new HealthCheckResult(context.Registration.FailureStatus, description: $"Queue '{_queueName}' not exists"); + } + + await queueClient.GetPropertiesAsync(cancellationToken); + + return HealthCheckResult.Healthy(); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusOptions.cs new file mode 100644 index 000000000..e1fab4d97 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusOptions.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus +{ + public class AzureServiceBusOptions + { + public string ConnectionString { get; set; } + + public Dictionary QueueNames { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusQueueHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusQueueHealthCheck.cs new file mode 100644 index 000000000..684681c83 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusQueueHealthCheck.cs @@ -0,0 +1,40 @@ +using Azure.Messaging.ServiceBus.Administration; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus +{ + public class AzureServiceBusQueueHealthCheck : IHealthCheck + { + private readonly string _connectionString; + private readonly string _queueName; + + public AzureServiceBusQueueHealthCheck(string connectionString, string queueName) + { + _connectionString = connectionString; + _queueName = queueName; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var client = new ServiceBusAdministrationClient(_connectionString); + var queue = await client.GetQueueAsync(_queueName); + + if (string.Equals(queue?.Value?.Name, _queueName, StringComparison.OrdinalIgnoreCase)) + { + return HealthCheckResult.Healthy(); + } + + return new HealthCheckResult(context.Registration.FailureStatus, description: $"Queue: '{_queueName}' doesn't exist"); + } + catch (Exception ex) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: ex); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusReceiver.cs new file mode 100644 index 000000000..da4aaeebb --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusReceiver.cs @@ -0,0 +1,61 @@ +using Azure.Messaging.ServiceBus; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus +{ + public class AzureServiceBusReceiver : IMessageReceiver + { + private readonly string _connectionString; + private readonly string _queueName; + + public AzureServiceBusReceiver(string connectionString, string queueName) + { + _connectionString = connectionString; + _queueName = queueName; + } + + public void Receive(Action action) + { + Task.Factory.StartNew(() => ReceiveAsync(action)); + } + + private Task ReceiveAsync(Action action) + { + return ReceiveStringAsync(retrievedMessage => + { + var message = JsonSerializer.Deserialize>(retrievedMessage); + action(message.Data, message.MetaData); + }); + } + + public void ReceiveString(Action action) + { + Task.Factory.StartNew(() => ReceiveStringAsync(action)); + } + + private async Task ReceiveStringAsync(Action action) + { + await using var client = new ServiceBusClient(_connectionString); + ServiceBusReceiver receiver = client.CreateReceiver(_queueName); + + while (true) + { + var retrievedMessage = await receiver.ReceiveMessageAsync(); + + if (retrievedMessage != null) + { + action(Encoding.UTF8.GetString(retrievedMessage.Body)); + await receiver.CompleteMessageAsync(retrievedMessage); + } + else + { + await Task.Delay(1000); + } + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusSender.cs new file mode 100644 index 000000000..1fc6881df --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusSender.cs @@ -0,0 +1,33 @@ +using Azure.Messaging.ServiceBus; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus +{ + public class AzureServiceBusSender : IMessageSender + { + private readonly string _connectionString; + private readonly string _queueName; + + public AzureServiceBusSender(string connectionString, string queueName) + { + _connectionString = connectionString; + _queueName = queueName; + } + + public async Task SendAsync(T message, MetaData metaData, CancellationToken cancellationToken = default) + { + await using var client = new ServiceBusClient(_connectionString); + ServiceBusSender sender = client.CreateSender(_queueName); + var serviceBusMessage = new ServiceBusMessage(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + }))); + await sender.SendMessageAsync(serviceBusMessage, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusSubscriptionReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusSubscriptionReceiver.cs new file mode 100644 index 000000000..15d9babbf --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusSubscriptionReceiver.cs @@ -0,0 +1,63 @@ +using Azure.Messaging.ServiceBus; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus +{ + public class AzureServiceBusSubscriptionReceiver : IMessageReceiver + { + private readonly string _connectionString; + private readonly string _topicName; + private readonly string _subscriptionName; + + public AzureServiceBusSubscriptionReceiver(string connectionString, string topicName, string subscriptionName) + { + _connectionString = connectionString; + _topicName = topicName; + _subscriptionName = subscriptionName; + } + + public void Receive(Action action) + { + Task.Factory.StartNew(() => ReceiveAsync(action)); + } + + private Task ReceiveAsync(Action action) + { + return ReceiveStringAsync(retrievedMessage => + { + var message = JsonSerializer.Deserialize>(retrievedMessage); + action(message.Data, message.MetaData); + }); + } + + public void ReceiveString(Action action) + { + Task.Factory.StartNew(() => ReceiveStringAsync(action)); + } + + private async Task ReceiveStringAsync(Action action) + { + await using var client = new ServiceBusClient(_connectionString); + ServiceBusReceiver receiver = client.CreateReceiver(_topicName, _subscriptionName); + + while (true) + { + var retrievedMessage = await receiver.ReceiveMessageAsync(); + + if (retrievedMessage != null) + { + action(Encoding.UTF8.GetString(retrievedMessage.Body)); + await receiver.CompleteMessageAsync(retrievedMessage); + } + else + { + await Task.Delay(1000); + } + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusTopicSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusTopicSender.cs new file mode 100644 index 000000000..ed5aac057 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/AzureServiceBus/AzureServiceBusTopicSender.cs @@ -0,0 +1,33 @@ +using Azure.Messaging.ServiceBus; +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus +{ + public class AzureServiceBusTopicSender : IMessageSender + { + private readonly string _connectionString; + private readonly string _topicName; + + public AzureServiceBusTopicSender(string connectionString, string topicName) + { + _connectionString = connectionString; + _topicName = topicName; + } + + public async Task SendAsync(T message, MetaData metaData, CancellationToken cancellationToken = default) + { + await using var client = new ServiceBusClient(_connectionString); + ServiceBusSender sender = client.CreateSender(_topicName); + var serviceBusMessage = new ServiceBusMessage(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + }))); + await sender.SendMessageAsync(serviceBusMessage, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Fake/FakeReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Fake/FakeReceiver.cs new file mode 100644 index 000000000..d161a15d6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Fake/FakeReceiver.cs @@ -0,0 +1,13 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.Fake +{ + public class FakeReceiver : IMessageReceiver + { + public void Receive(Action action) + { + // do nothing + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Fake/FakeSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Fake/FakeSender.cs new file mode 100644 index 000000000..49bfd8b7a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Fake/FakeSender.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.Fake +{ + public class FakeSender : IMessageSender + { + public Task SendAsync(T message, MetaData metaData = null, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaHealthCheck.cs new file mode 100644 index 000000000..067cae650 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaHealthCheck.cs @@ -0,0 +1,45 @@ +using Confluent.Kafka; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.Kafka +{ + public class KafkaHealthCheck : IHealthCheck + { + private readonly string _bootstrapServers; + private readonly string _topic; + + public KafkaHealthCheck(string bootstrapServers, string topic) + { + _bootstrapServers = bootstrapServers; + _topic = topic; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + var config = new ProducerConfig { BootstrapServers = _bootstrapServers, MessageTimeoutMs = 10000 }; + + using var producer = new ProducerBuilder(config).Build(); + var result = await producer.ProduceAsync(_topic, new Message + { + Value = $"Health Check {DateTimeOffset.Now}", + }, cancellationToken); + + if (result.Status == PersistenceStatus.NotPersisted) + { + return new HealthCheckResult(context.Registration.FailureStatus, "Message was never transmitted to the broker, or failed with an error indicating it was not written to the log. Application retry risks ordering, but not duplication"); + } + + return HealthCheckResult.Healthy(); + } + catch (Exception exception) + { + return new HealthCheckResult(context.Registration.FailureStatus, exception: exception); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaOptions.cs new file mode 100644 index 000000000..b253cce4d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaOptions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.Kafka +{ + public class KafkaOptions + { + public string BootstrapServers { get; set; } + + public string GroupId { get; set; } + + public Dictionary Topics { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaReceiver.cs new file mode 100644 index 000000000..15a9423cd --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaReceiver.cs @@ -0,0 +1,74 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using Confluent.Kafka; +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.Kafka +{ + public class KafkaReceiver : IMessageReceiver, IDisposable + { + private readonly IConsumer _consumer; + + public KafkaReceiver(string bootstrapServers, string topic, string groupId) + { + var config = new ConsumerConfig + { + GroupId = groupId, + BootstrapServers = bootstrapServers, + AutoOffsetReset = AutoOffsetReset.Earliest, + }; + + _consumer = new ConsumerBuilder(config).Build(); + _consumer.Subscribe(topic); + } + + public void Dispose() + { + _consumer.Dispose(); + } + + public void Receive(Action action) + { + CancellationTokenSource cts = new CancellationTokenSource(); + var cancellationToken = cts.Token; + + Task.Factory.StartNew(() => + { + try + { + StartReceiving(action, cancellationToken); + } + catch (OperationCanceledException) + { + Console.WriteLine("Closing consumer."); + _consumer.Close(); + } + }); + } + + private void StartReceiving(Action action, CancellationToken cancellationToken) + { + while (true) + { + try + { + var consumeResult = _consumer.Consume(cancellationToken); + + if (consumeResult.IsPartitionEOF) + { + continue; + } + + var message = JsonSerializer.Deserialize>(consumeResult.Message.Value); + action(message.Data, message.MetaData); + } + catch (ConsumeException e) + { + Console.WriteLine($"Consume error: {e.Error.Reason}"); + } + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaSender.cs new file mode 100644 index 000000000..710116178 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/Kafka/KafkaSender.cs @@ -0,0 +1,41 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using Confluent.Kafka; +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.Kafka +{ + public class KafkaSender : IMessageSender, IDisposable + { + private readonly string _topic; + private readonly IProducer _producer; + + public KafkaSender(string bootstrapServers, string topic) + { + _topic = topic; + + var config = new ProducerConfig { BootstrapServers = bootstrapServers }; + _producer = new ProducerBuilder(config).Build(); + } + + public void Dispose() + { + _producer.Flush(TimeSpan.FromSeconds(10)); + _producer.Dispose(); + } + + public async Task SendAsync(T message, MetaData metaData, CancellationToken cancellationToken = default) + { + _ = await _producer.ProduceAsync(_topic, new Message + { + Value = JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + }), + }, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/MessageBrokerOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/MessageBrokerOptions.cs new file mode 100644 index 000000000..8a7154c79 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/MessageBrokerOptions.cs @@ -0,0 +1,61 @@ +using ClassifiedAds.Infrastructure.MessageBrokers.AzureEventGrid; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureEventHub; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus; +using ClassifiedAds.Infrastructure.MessageBrokers.Kafka; +using ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ; + +namespace ClassifiedAds.Infrastructure.MessageBrokers +{ + public class MessageBrokerOptions + { + public string Provider { get; set; } + + public RabbitMQOptions RabbitMQ { get; set; } + + public KafkaOptions Kafka { get; set; } + + public AzureQueueOptions AzureQueue { get; set; } + + public AzureServiceBusOptions AzureServiceBus { get; set; } + + public AzureEventGridOptions AzureEventGrid { get; set; } + + public AzureEventHubOptions AzureEventHub { get; set; } + + public bool UsedRabbitMQ() + { + return Provider == "RabbitMQ"; + } + + public bool UsedKafka() + { + return Provider == "Kafka"; + } + + public bool UsedAzureQueue() + { + return Provider == "AzureQueue"; + } + + public bool UsedAzureServiceBus() + { + return Provider == "AzureServiceBus"; + } + + public bool UsedAzureEventGrid() + { + return Provider == "AzureEventGrid"; + } + + public bool UsedAzureEventHub() + { + return Provider == "AzureEventHub"; + } + + public bool UsedFake() + { + return Provider == "Fake"; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/MessageBrokersCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/MessageBrokersCollectionExtensions.cs new file mode 100644 index 000000000..9bb5f4306 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/MessageBrokersCollectionExtensions.cs @@ -0,0 +1,247 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using ClassifiedAds.Infrastructure.MessageBrokers; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureEventGrid; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureEventHub; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureQueue; +using ClassifiedAds.Infrastructure.MessageBrokers.AzureServiceBus; +using ClassifiedAds.Infrastructure.MessageBrokers.Fake; +using ClassifiedAds.Infrastructure.MessageBrokers.Kafka; +using ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System.Collections.Generic; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class MessageBrokersCollectionExtensions + { + public static IServiceCollection AddAzureEventGridSender(this IServiceCollection services, AzureEventGridOptions options) + { + services.AddSingleton>(new AzureEventGridSender( + options.DomainEndpoint, + options.DomainKey, + options.Topics[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddAzureEventHubSender(this IServiceCollection services, AzureEventHubOptions options) + { + services.AddSingleton>(new AzureEventHubSender( + options.ConnectionString, + options.Hubs[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddAzureEventHubReceiver(this IServiceCollection services, AzureEventHubOptions options) + { + services.AddTransient>(x => new AzureEventHubReceiver( + options.ConnectionString, + options.Hubs[typeof(T).Name], + options.StorageConnectionString, + options.StorageContainerNames[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddAzureQueueSender(this IServiceCollection services, AzureQueueOptions options) + { + services.AddSingleton>(new AzureQueueSender( + options.ConnectionString, + options.QueueNames[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddAzureQueueReceiver(this IServiceCollection services, AzureQueueOptions options) + { + services.AddTransient>(x => new AzureQueueReceiver( + options.ConnectionString, + options.QueueNames[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddAzureServiceBusSender(this IServiceCollection services, AzureServiceBusOptions options) + { + services.AddSingleton>(new AzureServiceBusSender( + options.ConnectionString, + options.QueueNames[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddAzureServiceBusReceiver(this IServiceCollection services, AzureServiceBusOptions options) + { + services.AddTransient>(x => new AzureServiceBusReceiver( + options.ConnectionString, + options.QueueNames[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddFakeSender(this IServiceCollection services) + { + services.AddSingleton>(new FakeSender()); + return services; + } + + public static IServiceCollection AddFakeReceiver(this IServiceCollection services) + { + services.AddTransient>(x => new FakeReceiver()); + return services; + } + + public static IServiceCollection AddKafkaSender(this IServiceCollection services, KafkaOptions options) + { + services.AddSingleton>(new KafkaSender(options.BootstrapServers, options.Topics[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddKafkaReceiver(this IServiceCollection services, KafkaOptions options) + { + services.AddTransient>(x => new KafkaReceiver(options.BootstrapServers, + options.Topics[typeof(T).Name], + options.GroupId)); + return services; + } + + public static IServiceCollection AddRabbitMQSender(this IServiceCollection services, RabbitMQOptions options) + { + services.AddSingleton>(new RabbitMQSender(new RabbitMQSenderOptions + { + HostName = options.HostName, + UserName = options.UserName, + Password = options.Password, + ExchangeName = options.ExchangeName, + RoutingKey = options.RoutingKeys[typeof(T).Name], + })); + return services; + } + + public static IServiceCollection AddRabbitMQReceiver(this IServiceCollection services, RabbitMQOptions options) + { + services.AddTransient>(x => new RabbitMQReceiver(new RabbitMQReceiverOptions + { + HostName = options.HostName, + UserName = options.UserName, + Password = options.Password, + ExchangeName = options.ExchangeName, + RoutingKey = options.RoutingKeys[typeof(T).Name], + QueueName = options.QueueNames[typeof(T).Name], + AutomaticCreateEnabled = true, + })); + return services; + } + + public static IServiceCollection AddMessageBusSender(this IServiceCollection services, MessageBrokerOptions options, IHealthChecksBuilder healthChecksBuilder = null, HashSet checkDulicated = null) + { + if (options.UsedRabbitMQ()) + { + services.AddRabbitMQSender(options.RabbitMQ); + + if (healthChecksBuilder != null) + { + var name = "Message Broker (RabbitMQ)"; + + healthChecksBuilder.AddRabbitMQ(new RabbitMQHealthCheckOptions + { + HostName = options.RabbitMQ.HostName, + UserName = options.RabbitMQ.UserName, + Password = options.RabbitMQ.Password, + }, + name: name, + failureStatus: HealthStatus.Degraded); + + checkDulicated?.Add(name); + } + } + else if (options.UsedKafka()) + { + services.AddKafkaSender(options.Kafka); + + if (healthChecksBuilder != null) + { + var name = "Message Broker (Kafka)"; + + if (checkDulicated == null || !checkDulicated.Contains(name)) + { + healthChecksBuilder.AddKafka( + bootstrapServers: options.Kafka.BootstrapServers, + topic: "healthcheck", + name: name, + failureStatus: HealthStatus.Degraded); + } + + checkDulicated?.Add(name); + } + } + else if (options.UsedAzureQueue()) + { + services.AddAzureQueueSender(options.AzureQueue); + + if (healthChecksBuilder != null) + { + healthChecksBuilder.AddAzureQueueStorage(connectionString: options.AzureQueue.ConnectionString, + queueName: options.AzureQueue.QueueNames[typeof(T).Name], + name: $"Message Broker (Azure Queue) {typeof(T).Name}", + failureStatus: HealthStatus.Degraded); + } + } + else if (options.UsedAzureServiceBus()) + { + services.AddAzureServiceBusSender(options.AzureServiceBus); + + if (healthChecksBuilder != null) + { + healthChecksBuilder.AddAzureServiceBusQueue( + connectionString: options.AzureServiceBus.ConnectionString, + queueName: options.AzureServiceBus.QueueNames[typeof(T).Name], + name: $"Message Broker (Azure Service Bus) {typeof(T).Name}", + failureStatus: HealthStatus.Degraded); + } + } + else if (options.UsedAzureEventGrid()) + { + services.AddAzureEventGridSender(options.AzureEventGrid); + + // TODO: Add Health Check + } + else if (options.UsedAzureEventHub()) + { + services.AddAzureEventHubSender(options.AzureEventHub); + + // TODO: Add Health Check + } + else if (options.UsedFake()) + { + services.AddFakeSender(); + } + + return services; + } + + public static IServiceCollection AddMessageBusReceiver(this IServiceCollection services, MessageBrokerOptions options) + { + if (options.UsedRabbitMQ()) + { + services.AddRabbitMQReceiver(options.RabbitMQ); + } + else if (options.UsedKafka()) + { + services.AddKafkaReceiver(options.Kafka); + } + else if (options.UsedAzureQueue()) + { + services.AddAzureQueueReceiver(options.AzureQueue); + } + else if (options.UsedAzureServiceBus()) + { + services.AddAzureServiceBusReceiver(options.AzureServiceBus); + } + else if (options.UsedAzureEventHub()) + { + services.AddAzureEventHubReceiver(options.AzureEventHub); + } + else if (options.UsedFake()) + { + services.AddFakeReceiver(); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQHealthCheck.cs new file mode 100644 index 000000000..c96a90b2a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQHealthCheck.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using RabbitMQ.Client; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ +{ + public class RabbitMQHealthCheck : IHealthCheck + { + private readonly RabbitMQHealthCheckOptions _options; + + public RabbitMQHealthCheck(RabbitMQHealthCheckOptions options) + { + _options = options; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var connectionFactory = new ConnectionFactory + { + HostName = _options.HostName, + UserName = _options.UserName, + Password = _options.Password, + }; + + using var connection = connectionFactory.CreateConnection(); + using var model = connection.CreateModel(); + + return Task.FromResult(HealthCheckResult.Healthy($"HostName: {_options.HostName}")); + } + catch (Exception exception) + { + return Task.FromResult(new HealthCheckResult(context.Registration.FailureStatus, null, exception)); + } + } + } + + public class RabbitMQHealthCheckOptions + { + public string HostName { get; set; } + + public string UserName { get; set; } + + public string Password { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQOptions.cs new file mode 100644 index 000000000..b58b0f806 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQOptions.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ +{ + public class RabbitMQOptions + { + public string HostName { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public string ExchangeName { get; set; } + + public Dictionary RoutingKeys { get; set; } + + public Dictionary QueueNames { get; set; } + + public string ConnectionString + { + get + { + return $"amqp://{UserName}:{Password}@{HostName}/%2f"; + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQReceiver.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQReceiver.cs new file mode 100644 index 000000000..29625140b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQReceiver.cs @@ -0,0 +1,74 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using System; +using System.Text; +using System.Text.Json; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ +{ + public class RabbitMQReceiver : IMessageReceiver, IDisposable + { + private IConnection _connection; + private IModel _channel; + private string _queueName; + private readonly RabbitMQReceiverOptions _options; + + public RabbitMQReceiver(RabbitMQReceiverOptions options) + { + _options = options; + + _connection = new ConnectionFactory + { + HostName = options.HostName, + UserName = options.UserName, + Password = options.Password, + AutomaticRecoveryEnabled = true, + }.CreateConnection(); + + _queueName = options.QueueName; + + _connection.ConnectionShutdown += Connection_ConnectionShutdown; + } + + private void Connection_ConnectionShutdown(object sender, ShutdownEventArgs e) + { + // TODO: add log here + } + + public void Receive(Action action) + { + _channel = _connection.CreateModel(); + + if (_options.AutomaticCreateEnabled) + { + _channel.QueueDeclare(_options.QueueName, true, false, false, null); + _channel.QueueBind(_options.QueueName, _options.ExchangeName, _options.RoutingKey, null); + } + + /*In order to defeat that we can use the basicQos method with the prefetchCount = 1 setting. + This tells RabbitMQ not to give more than one message to a worker at a time. + Or, in other words, don't dispatch a new message to a worker until it has processed and acknowledged the previous one. + Instead, it will dispatch it to the next worker that is not still busy.*/ + _channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false); + + var consumer = new EventingBasicConsumer(_channel); + consumer.Received += (model, ea) => + { + var body = Encoding.UTF8.GetString(ea.Body.Span); + var message = JsonSerializer.Deserialize>(body); + action(message.Data, message.MetaData); + _channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false); + }; + _channel.BasicConsume(queue: _queueName, + autoAck: false, + consumer: consumer); + } + + public void Dispose() + { + _channel.Dispose(); + _connection.Dispose(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQReceiverOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQReceiverOptions.cs new file mode 100644 index 000000000..10fbfe266 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQReceiverOptions.cs @@ -0,0 +1,13 @@ +namespace ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ +{ + public class RabbitMQReceiverOptions + { + public string HostName { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public string QueueName { get; set; } + public bool AutomaticCreateEnabled { get; set; } + public string ExchangeName { get; set; } + public string RoutingKey { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQSender.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQSender.cs new file mode 100644 index 000000000..bb317941c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQSender.cs @@ -0,0 +1,50 @@ +using ClassifiedAds.Domain.Infrastructure.MessageBrokers; +using RabbitMQ.Client; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ +{ + public class RabbitMQSender : IMessageSender + { + private readonly IConnectionFactory _connectionFactory; + private readonly string _exchangeName; + private readonly string _routingKey; + + public RabbitMQSender(RabbitMQSenderOptions options) + { + _connectionFactory = new ConnectionFactory + { + HostName = options.HostName, + UserName = options.UserName, + Password = options.Password, + }; + + _exchangeName = options.ExchangeName; + _routingKey = options.RoutingKey; + } + + public async Task SendAsync(T message, MetaData metaData = null, CancellationToken cancellationToken = default) + { + await Task.Run(() => + { + using var connection = _connectionFactory.CreateConnection(); + using var channel = connection.CreateModel(); + var body = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new Message + { + Data = message, + MetaData = metaData, + })); + var properties = channel.CreateBasicProperties(); + properties.Persistent = true; + + channel.BasicPublish(exchange: _exchangeName, + routingKey: _routingKey, + basicProperties: properties, + body: body); + }, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQSenderOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQSenderOptions.cs new file mode 100644 index 000000000..523a7fb9a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/MessageBrokers/RabbitMQ/RabbitMQSenderOptions.cs @@ -0,0 +1,11 @@ +namespace ClassifiedAds.Infrastructure.MessageBrokers.RabbitMQ +{ + public class RabbitMQSenderOptions + { + public string HostName { get; set; } + public string UserName { get; set; } + public string Password { get; set; } + public string ExchangeName { get; set; } + public string RoutingKey { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/AzureApplicationInsightsOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/AzureApplicationInsightsOptions.cs new file mode 100644 index 000000000..8a1ae1702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/AzureApplicationInsightsOptions.cs @@ -0,0 +1,11 @@ +namespace ClassifiedAds.Infrastructure.Monitoring.AzureApplicationInsights +{ + public class AzureApplicationInsightsOptions + { + public bool IsEnabled { get; set; } + + public string InstrumentationKey { get; set; } + + public bool EnableSqlCommandTextInstrumentation { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/AzureApplicationInsightsServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/AzureApplicationInsightsServiceCollectionExtensions.cs new file mode 100644 index 000000000..3c9696dcd --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/AzureApplicationInsightsServiceCollectionExtensions.cs @@ -0,0 +1,30 @@ +using Microsoft.ApplicationInsights.DependencyCollector; +using Microsoft.ApplicationInsights.Extensibility; +using Microsoft.Extensions.DependencyInjection; + +namespace ClassifiedAds.Infrastructure.Monitoring.AzureApplicationInsights +{ + public static class AzureApplicationInsightsServiceCollectionExtensions + { + public static IServiceCollection AddAzureApplicationInsights(this IServiceCollection services, AzureApplicationInsightsOptions azureApplicationInsightsOptions = null) + { + if (azureApplicationInsightsOptions?.IsEnabled ?? false) + { + services.AddApplicationInsightsTelemetry(opt => + { + opt.InstrumentationKey = azureApplicationInsightsOptions.InstrumentationKey; + }); + + services.ConfigureTelemetryModule((module, o) => + { + module.EnableSqlCommandTextInstrumentation = azureApplicationInsightsOptions.EnableSqlCommandTextInstrumentation; + }); + + services.AddApplicationInsightsTelemetryProcessor(); + services.AddSingleton(); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/CustomTelemetryInitializer.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/CustomTelemetryInitializer.cs new file mode 100644 index 000000000..90ffc02f3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/CustomTelemetryInitializer.cs @@ -0,0 +1,29 @@ +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.DataContracts; +using Microsoft.ApplicationInsights.Extensibility; + +namespace ClassifiedAds.Infrastructure.Monitoring.AzureApplicationInsights +{ + public class CustomTelemetryInitializer : ITelemetryInitializer + { + public void Initialize(ITelemetry telemetry) + { + if (!(telemetry is RequestTelemetry requestTelemetry)) + { + return; + } + + if (int.TryParse(requestTelemetry.ResponseCode, out int code)) + { + if (code >= 400 && code < 500) + { + // If we set the Success property, the SDK won't change it: + requestTelemetry.Success = true; + + // Allow us to filter these requests in the portal: + requestTelemetry.Properties["Overridden400s"] = "true"; + } + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/CustomTelemetryProcessor.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/CustomTelemetryProcessor.cs new file mode 100644 index 000000000..6babba2e8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/AzureApplicationInsights/CustomTelemetryProcessor.cs @@ -0,0 +1,39 @@ +using Microsoft.ApplicationInsights.Channel; +using Microsoft.ApplicationInsights.DataContracts; +using Microsoft.ApplicationInsights.Extensibility; + +namespace ClassifiedAds.Infrastructure.Monitoring.AzureApplicationInsights +{ + public class CustomTelemetryProcessor : ITelemetryProcessor + { + private ITelemetryProcessor Next { get; set; } + + public CustomTelemetryProcessor(ITelemetryProcessor next) + { + Next = next; + } + + public void Process(ITelemetry item) + { + if (!OKtoSend(item)) + { + return; + } + + Next.Process(item); + } + + private bool OKtoSend(ITelemetry item) + { + if (item is RequestTelemetry request) + { + } + + if (item is DependencyTelemetry dependency) + { + } + + return true; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MiniProfiler/MiniProfilerOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MiniProfiler/MiniProfilerOptions.cs new file mode 100644 index 000000000..cf6f4995e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MiniProfiler/MiniProfilerOptions.cs @@ -0,0 +1,20 @@ +namespace ClassifiedAds.Infrastructure.Monitoring.MiniProfiler +{ + public class MiniProfilerOptions + { + public bool IsEnabled { get; set; } + + public SqlServerStorageOptions SqlServerStorage { get; set; } + + public class SqlServerStorageOptions + { + public string ConectionString { get; set; } + + public string ProfilersTable { get; set; } + + public string TimingsTable { get; set; } + + public string ClientTimingsTable { get; set; } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MiniProfiler/MiniProfilerServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MiniProfiler/MiniProfilerServiceCollectionExtensions.cs new file mode 100644 index 000000000..7c05b9b0a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MiniProfiler/MiniProfilerServiceCollectionExtensions.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.DependencyInjection; +using StackExchange.Profiling.Storage; +using System.Security.Claims; + +namespace ClassifiedAds.Infrastructure.Monitoring.MiniProfiler +{ + public static class MiniProfilerServiceCollectionExtensions + { + public static IServiceCollection AddMiniProfiler(this IServiceCollection services, MiniProfilerOptions miniProfilerOptions = null) + { + if (miniProfilerOptions?.IsEnabled ?? false) + { + services.AddMiniProfiler(options => + { + options.UserIdProvider = (request) => + { + var id = request.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value + ?? request.HttpContext.User.FindFirst("sub")?.Value; + return id; + }; + + options.RouteBasePath = "/profiler"; // access /profiler/results to see last profile check + options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.BottomLeft; + options.PopupShowTimeWithChildren = true; + if (!string.IsNullOrEmpty(miniProfilerOptions?.SqlServerStorage?.ConectionString)) + { + var storageOpt = miniProfilerOptions.SqlServerStorage; + var storage = new SqlServerStorage(storageOpt.ConectionString, storageOpt.ProfilersTable, storageOpt.TimingsTable, storageOpt.ClientTimingsTable); + _ = storage.TableCreationScripts; + + options.Storage = storage; + } + + options.ShouldProfile = (request) => + { + if (request.Path.StartsWithSegments("/healthcheck")) + { + return false; + } + + return true; + }; + }) + .AddEntityFramework(); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MonitoringExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MonitoringExtensions.cs new file mode 100644 index 000000000..a1924df05 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MonitoringExtensions.cs @@ -0,0 +1,35 @@ +using ClassifiedAds.Infrastructure.Monitoring.AzureApplicationInsights; +using ClassifiedAds.Infrastructure.Monitoring.MiniProfiler; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace ClassifiedAds.Infrastructure.Monitoring +{ + public static class MonitoringExtensions + { + public static IServiceCollection AddMonitoringServices(this IServiceCollection services, MonitoringOptions monitoringOptions = null) + { + if (monitoringOptions?.MiniProfiler?.IsEnabled ?? false) + { + services.AddMiniProfiler(monitoringOptions.MiniProfiler); + } + + if (monitoringOptions?.AzureApplicationInsights?.IsEnabled ?? false) + { + services.AddAzureApplicationInsights(monitoringOptions.AzureApplicationInsights); + } + + return services; + } + + public static IApplicationBuilder UseMonitoringServices(this IApplicationBuilder builder, MonitoringOptions monitoringOptions) + { + if (monitoringOptions?.MiniProfiler?.IsEnabled ?? false) + { + builder.UseMiniProfiler(); + } + + return builder; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MonitoringOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MonitoringOptions.cs new file mode 100644 index 000000000..f5f9f47b2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Monitoring/MonitoringOptions.cs @@ -0,0 +1,12 @@ +using ClassifiedAds.Infrastructure.Monitoring.AzureApplicationInsights; +using ClassifiedAds.Infrastructure.Monitoring.MiniProfiler; + +namespace ClassifiedAds.Infrastructure.Monitoring +{ + public class MonitoringOptions + { + public MiniProfilerOptions MiniProfiler { get; set; } + + public AzureApplicationInsightsOptions AzureApplicationInsights { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Networking/FileDownloader.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Networking/FileDownloader.cs new file mode 100644 index 000000000..162dbb123 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Networking/FileDownloader.cs @@ -0,0 +1,22 @@ +using ClassifiedAds.Domain.Infrastructure.Networking; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Networking +{ + public class FileDownloader : IFileDownloader + { + public void DownloadFile(string url, string path) + { + var client = new WebClient(); + client.DownloadFile(url, path); + } + + public async Task DownloadFileAsync(string url, string path, CancellationToken cancellationToken = default) + { + var client = new WebClient(); + await client.DownloadFileTaskAsync(url, path); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/EmailNotificationServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/EmailNotificationServiceCollectionExtensions.cs new file mode 100644 index 000000000..594313341 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/EmailNotificationServiceCollectionExtensions.cs @@ -0,0 +1,46 @@ +using ClassifiedAds.Domain.Notification; +using ClassifiedAds.Infrastructure.Notification.Email; +using ClassifiedAds.Infrastructure.Notification.Email.SendGrid; +using ClassifiedAds.Infrastructure.Notification.Email.SmtpClient; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class EmailNotificationServiceCollectionExtensions + { + public static IServiceCollection AddSmtpClientEmailNotification(this IServiceCollection services, SmtpClientOptions options) + { + services.AddSingleton(new SmtpClientEmailNotification(options)); + return services; + } + + public static IServiceCollection AddFakeEmailNotification(this IServiceCollection services) + { + services.AddSingleton(new FakeEmailNotification()); + return services; + } + + public static IServiceCollection AddSendGridEmailNotification(this IServiceCollection services, SendGridOptions options) + { + services.AddSingleton(new SendGridEmailNotification(options)); + return services; + } + + public static IServiceCollection AddEmailNotification(this IServiceCollection services, EmailOptions options) + { + if (options.UsedFake()) + { + services.AddFakeEmailNotification(); + } + else if (options.UsedSmtpClient()) + { + services.AddSmtpClientEmailNotification(options.SmtpClient); + } + else if (options.UsedSendGrid()) + { + services.AddSendGridEmailNotification(options.SendGrid); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/EmailOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/EmailOptions.cs new file mode 100644 index 000000000..c24fda9c3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/EmailOptions.cs @@ -0,0 +1,29 @@ +using ClassifiedAds.Infrastructure.Notification.Email.SendGrid; +using ClassifiedAds.Infrastructure.Notification.Email.SmtpClient; + +namespace ClassifiedAds.Infrastructure.Notification.Email +{ + public class EmailOptions + { + public string Provider { get; set; } + + public SmtpClientOptions SmtpClient { get; set; } + + public SendGridOptions SendGrid { get; set; } + + public bool UsedFake() + { + return Provider == "Fake"; + } + + public bool UsedSmtpClient() + { + return Provider == "SmtpClient"; + } + + public bool UsedSendGrid() + { + return Provider == "SendGrid"; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/Fake/FakeEmailNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/Fake/FakeEmailNotification.cs new file mode 100644 index 000000000..ad2c0486d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/Fake/FakeEmailNotification.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.Domain.Notification; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Email.SmtpClient +{ + public class FakeEmailNotification : IEmailNotification + { + public Task SendAsync(IEmailMessage emailMessage, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SendGrid/SendGridEmailNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SendGrid/SendGridEmailNotification.cs new file mode 100644 index 000000000..0d1aaff0d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SendGrid/SendGridEmailNotification.cs @@ -0,0 +1,33 @@ +using ClassifiedAds.Domain.Notification; +using SendGrid; +using SendGrid.Helpers.Mail; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Email.SendGrid +{ + public class SendGridEmailNotification : IEmailNotification + { + private readonly SendGridOptions _options; + + public SendGridEmailNotification(SendGridOptions options) + { + _options = options; + } + + public async Task SendAsync(IEmailMessage emailMessage, CancellationToken cancellationToken = default) + { + var client = new SendGridClient(_options.ApiKey); + var from = new EmailAddress(!string.IsNullOrWhiteSpace(_options.OverrideFrom) ? _options.OverrideFrom : emailMessage.From); + + var tos = (!string.IsNullOrWhiteSpace(_options.OverrideTos) ? _options.OverrideTos : emailMessage.Tos)?.Split(';') + .Where(x => !string.IsNullOrWhiteSpace(x)) + .Select(x => new EmailAddress(x)) + .ToList(); + + var msg = MailHelper.CreateSingleEmailToMultipleRecipients(from, tos, emailMessage.Subject, string.Empty, emailMessage.Body, showAllRecipients: true); + var response = await client.SendEmailAsync(msg, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SendGrid/SendGridOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SendGrid/SendGridOptions.cs new file mode 100644 index 000000000..ded9c2e35 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SendGrid/SendGridOptions.cs @@ -0,0 +1,11 @@ +namespace ClassifiedAds.Infrastructure.Notification.Email.SendGrid +{ + public class SendGridOptions + { + public string ApiKey { get; set; } + + public string OverrideFrom { get; set; } + + public string OverrideTos { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SmtpClient/SmtpClientEmailNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SmtpClient/SmtpClientEmailNotification.cs new file mode 100644 index 000000000..607697be1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SmtpClient/SmtpClientEmailNotification.cs @@ -0,0 +1,65 @@ +using ClassifiedAds.Domain.Notification; +using System.Linq; +using System.Net.Mail; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Email.SmtpClient +{ + public class SmtpClientEmailNotification : IEmailNotification + { + private readonly SmtpClientOptions _options; + + public SmtpClientEmailNotification(SmtpClientOptions options) + { + _options = options; + } + + public async Task SendAsync(IEmailMessage emailMessage, CancellationToken cancellationToken = default) + { + var mail = new MailMessage(); + + mail.From = new MailAddress(emailMessage.From); + + emailMessage.Tos?.Split(';') + .Where(x => !string.IsNullOrWhiteSpace(x)) + .ToList() + .ForEach(x => mail.To.Add(x)); + + emailMessage.CCs?.Split(';') + .Where(x => !string.IsNullOrWhiteSpace(x)) + .ToList() + .ForEach(x => mail.CC.Add(x)); + + emailMessage.BCCs?.Split(';') + .Where(x => !string.IsNullOrWhiteSpace(x)) + .ToList() + .ForEach(x => mail.Bcc.Add(x)); + + mail.Subject = emailMessage.Subject; + + mail.Body = emailMessage.Body; + + mail.IsBodyHtml = true; + + var smtpClient = new System.Net.Mail.SmtpClient(_options.Host); + + if (_options.Port.HasValue) + { + smtpClient.Port = _options.Port.Value; + } + + if (!string.IsNullOrWhiteSpace(_options.UserName) && !string.IsNullOrWhiteSpace(_options.Password)) + { + smtpClient.Credentials = new System.Net.NetworkCredential(_options.UserName, _options.Password); + } + + if (_options.EnableSsl.HasValue) + { + smtpClient.EnableSsl = _options.EnableSsl.Value; + } + + await smtpClient.SendMailAsync(mail, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SmtpClient/SmtpClientOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SmtpClient/SmtpClientOptions.cs new file mode 100644 index 000000000..e7484d8f3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Email/SmtpClient/SmtpClientOptions.cs @@ -0,0 +1,15 @@ +namespace ClassifiedAds.Infrastructure.Notification.Email.SmtpClient +{ + public class SmtpClientOptions + { + public string Host { get; set; } + + public int? Port { get; set; } + + public string UserName { get; set; } + + public string Password { get; set; } + + public bool? EnableSsl { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/NotificationOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/NotificationOptions.cs new file mode 100644 index 000000000..dc66fcca6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/NotificationOptions.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Infrastructure.Notification.Email; +using ClassifiedAds.Infrastructure.Notification.Sms; +using ClassifiedAds.Infrastructure.Notification.Web; + +namespace ClassifiedAds.Infrastructure.Notification +{ + public class NotificationOptions + { + public EmailOptions Email { get; set; } + + public SmsOptions Sms { get; set; } + + public WebOptions Web { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/NotificationServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/NotificationServiceCollectionExtensions.cs new file mode 100644 index 000000000..63c4fdf2e --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/NotificationServiceCollectionExtensions.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.Infrastructure.Notification; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class NotificationServiceCollectionExtensions + { + public static IServiceCollection AddNotificationServices(this IServiceCollection services, NotificationOptions options) + { + services.AddEmailNotification(options.Email); + + services.AddSmsNotification(options.Sms); + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Azure/AzureOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Azure/AzureOptions.cs new file mode 100644 index 000000000..407f4bdb5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Azure/AzureOptions.cs @@ -0,0 +1,8 @@ +namespace ClassifiedAds.Infrastructure.Notification.Sms.Azure +{ + public class AzureOptions + { + public string ConnectionString { get; set; } + public string FromNumber { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Azure/AzureSmsNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Azure/AzureSmsNotification.cs new file mode 100644 index 000000000..79c7ec2f8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Azure/AzureSmsNotification.cs @@ -0,0 +1,31 @@ +using Azure.Communication.Sms; +using ClassifiedAds.Domain.Notification; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Sms.Azure +{ + public class AzureSmsNotification : ISmsNotification + { + private readonly AzureOptions _options; + + public AzureSmsNotification(AzureOptions options) + { + _options = options; + } + + public async Task SendAsync(ISmsMessage smsMessage, CancellationToken cancellationToken = default) + { + var smsClient = new SmsClient(_options.ConnectionString); + var response = await smsClient.SendAsync( + from: _options.FromNumber, + to: smsMessage.PhoneNumber, + message: smsMessage.Message, + cancellationToken: cancellationToken); + + if (!string.IsNullOrWhiteSpace(response?.Value?.MessageId)) + { + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Fake/FakeSmsNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Fake/FakeSmsNotification.cs new file mode 100644 index 000000000..fa982b793 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Fake/FakeSmsNotification.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.Domain.Notification; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Sms.Fake +{ + public class FakeSmsNotification : ISmsNotification + { + public Task SendAsync(ISmsMessage smsMessage, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/SmsNotificationServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/SmsNotificationServiceCollectionExtensions.cs new file mode 100644 index 000000000..9b86df612 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/SmsNotificationServiceCollectionExtensions.cs @@ -0,0 +1,47 @@ +using ClassifiedAds.Domain.Notification; +using ClassifiedAds.Infrastructure.Notification.Sms; +using ClassifiedAds.Infrastructure.Notification.Sms.Azure; +using ClassifiedAds.Infrastructure.Notification.Sms.Fake; +using ClassifiedAds.Infrastructure.Notification.Sms.Twilio; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class SmsNotificationServiceCollectionExtensions + { + public static IServiceCollection AddTwilioSmsNotification(this IServiceCollection services, TwilioOptions options) + { + services.AddSingleton(new TwilioSmsNotification(options)); + return services; + } + + public static IServiceCollection AddAzureSmsNotification(this IServiceCollection services, AzureOptions options) + { + services.AddSingleton(new AzureSmsNotification(options)); + return services; + } + + public static IServiceCollection AddFakeSmsNotification(this IServiceCollection services) + { + services.AddSingleton(new FakeSmsNotification()); + return services; + } + + public static IServiceCollection AddSmsNotification(this IServiceCollection services, SmsOptions options) + { + if (options.UsedFake()) + { + services.AddFakeSmsNotification(); + } + else if (options.UsedTwilio()) + { + services.AddTwilioSmsNotification(options.Twilio); + } + else if (options.UsedAzure()) + { + services.AddAzureSmsNotification(options.Azure); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/SmsOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/SmsOptions.cs new file mode 100644 index 000000000..db7cab1d2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/SmsOptions.cs @@ -0,0 +1,29 @@ +using ClassifiedAds.Infrastructure.Notification.Sms.Azure; +using ClassifiedAds.Infrastructure.Notification.Sms.Twilio; + +namespace ClassifiedAds.Infrastructure.Notification.Sms +{ + public class SmsOptions + { + public string Provider { get; set; } + + public TwilioOptions Twilio { get; set; } + + public AzureOptions Azure { get; set; } + + public bool UsedFake() + { + return Provider == "Fake"; + } + + public bool UsedTwilio() + { + return Provider == "Twilio"; + } + + public bool UsedAzure() + { + return Provider == "Azure"; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Twilio/TwilioOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Twilio/TwilioOptions.cs new file mode 100644 index 000000000..74abf0de2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Twilio/TwilioOptions.cs @@ -0,0 +1,11 @@ +namespace ClassifiedAds.Infrastructure.Notification.Sms.Twilio +{ + public class TwilioOptions + { + public string AccountSId { get; set; } + + public string AuthToken { get; set; } + + public string FromNumber { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Twilio/TwilioSmsNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Twilio/TwilioSmsNotification.cs new file mode 100644 index 000000000..9893beca8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Sms/Twilio/TwilioSmsNotification.cs @@ -0,0 +1,33 @@ +using ClassifiedAds.Domain.Notification; +using System.Threading; +using System.Threading.Tasks; +using Twilio; +using Twilio.Rest.Api.V2010.Account; +using Twilio.Types; + +namespace ClassifiedAds.Infrastructure.Notification.Sms.Twilio +{ + public class TwilioSmsNotification : ISmsNotification + { + private readonly TwilioOptions _options; + + public TwilioSmsNotification(TwilioOptions options) + { + _options = options; + } + + public async Task SendAsync(ISmsMessage smsMessage, CancellationToken cancellationToken = default) + { + TwilioClient.Init(_options.AccountSId, _options.AuthToken); + + var message = await MessageResource.CreateAsync( + body: smsMessage.Message, + from: new PhoneNumber(_options.FromNumber), + to: new PhoneNumber(smsMessage.PhoneNumber)); + + if (!string.IsNullOrWhiteSpace(message.Sid)) + { + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/Fake/FakeWebNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/Fake/FakeWebNotification.cs new file mode 100644 index 000000000..629065ba6 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/Fake/FakeWebNotification.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.Domain.Notification; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Web.Fake +{ + public class FakeWebNotification : IWebNotification + { + public Task SendAsync(T message, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/SignalR/SignalRNotification.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/SignalR/SignalRNotification.cs new file mode 100644 index 000000000..30e3da630 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/SignalR/SignalRNotification.cs @@ -0,0 +1,36 @@ +using ClassifiedAds.Domain.Notification; +using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.Extensions.DependencyInjection; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Notification.Web.SignalR +{ + public class SignalRNotification : IWebNotification + { + private readonly HubConnection _connection; + private readonly string _endpoint; + private readonly string _eventName; + + public SignalRNotification(string endpoint, string hubName, string eventName) + { + _endpoint = endpoint + "/" + hubName; + _eventName = eventName; + + _connection = new HubConnectionBuilder() + .WithUrl(_endpoint) + .AddMessagePackProtocol() + .Build(); + } + + public async Task SendAsync(T message, CancellationToken cancellationToken = default) + { + if (_connection.State != HubConnectionState.Connected) + { + _connection.StartAsync(cancellationToken).GetAwaiter().GetResult(); + } + + await _connection.InvokeAsync(_eventName, message, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/SignalR/SignalROptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/SignalR/SignalROptions.cs new file mode 100644 index 000000000..c062ee75c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/SignalR/SignalROptions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ClassifiedAds.Infrastructure.Notification.Web.SignalR +{ + public class SignalROptions + { + public string Endpoint { get; set; } + + public Dictionary Hubs { get; set; } + + public Dictionary MethodNames { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/WebNotificationServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/WebNotificationServiceCollectionExtensions.cs new file mode 100644 index 000000000..b772556f1 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/WebNotificationServiceCollectionExtensions.cs @@ -0,0 +1,36 @@ +using ClassifiedAds.Domain.Notification; +using ClassifiedAds.Infrastructure.Notification.Web; +using ClassifiedAds.Infrastructure.Notification.Web.Fake; +using ClassifiedAds.Infrastructure.Notification.Web.SignalR; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class WebNotificationServiceCollectionExtensions + { + public static IServiceCollection AddSignalRWebNotification(this IServiceCollection services, SignalROptions options) + { + services.AddSingleton>(new SignalRNotification(options.Endpoint, options.Hubs[typeof(T).Name], options.MethodNames[typeof(T).Name])); + return services; + } + + public static IServiceCollection AddFakeWebNotification(this IServiceCollection services) + { + services.AddSingleton>(new FakeWebNotification()); + return services; + } + + public static IServiceCollection AddWebNotification(this IServiceCollection services, WebOptions options) + { + if (options.UsedFake()) + { + services.AddFakeWebNotification(); + } + else if (options.UsedSignalR()) + { + services.AddSignalRWebNotification(options.SignalR); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/WebOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/WebOptions.cs new file mode 100644 index 000000000..6798cebf0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Notification/Web/WebOptions.cs @@ -0,0 +1,21 @@ +using ClassifiedAds.Infrastructure.Notification.Web.SignalR; + +namespace ClassifiedAds.Infrastructure.Notification.Web +{ + public class WebOptions + { + public string Provider { get; set; } + + public SignalROptions SignalR { get; set; } + + public bool UsedFake() + { + return Provider == "Fake"; + } + + public bool UsedSignalR() + { + return Provider == "SignalR"; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/OS/DateTimeProvider.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/OS/DateTimeProvider.cs new file mode 100644 index 000000000..6bcdab696 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/OS/DateTimeProvider.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using System; + +namespace ClassifiedAds.Infrastructure.OS +{ + public class DateTimeProvider : IDateTimeProvider + { + public DateTime Now => DateTime.Now; + + public DateTime UtcNow => DateTime.UtcNow; + + public DateTimeOffset OffsetNow => DateTimeOffset.Now; + + public DateTimeOffset OffsetUtcNow => DateTimeOffset.UtcNow; + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/OS/DateTimeProviderExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/OS/DateTimeProviderExtensions.cs new file mode 100644 index 000000000..38d28f9a4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/OS/DateTimeProviderExtensions.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Infrastructure.OS; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class DateTimeProviderExtensions + { + public static IServiceCollection AddDateTimeProvider(this IServiceCollection services) + { + _ = services.AddSingleton(); + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/DinkToPdf/DinkToPdfConverter.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/DinkToPdf/DinkToPdfConverter.cs new file mode 100644 index 000000000..ffd0ad58f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/DinkToPdf/DinkToPdfConverter.cs @@ -0,0 +1,51 @@ +using ClassifiedAds.CrossCuttingConcerns.PdfConverter; +using DinkToPdf; +using DinkToPdf.Contracts; +using System.IO; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.PdfConverters.DinkToPdf +{ + public class DinkToPdfConverter : IPdfConverter + { + private readonly IConverter _converter; + + public DinkToPdfConverter(IConverter converter) + { + _converter = converter; + } + + public Stream Convert(string html, PdfOptions pdfOptions = null) + { + var doc = new HtmlToPdfDocument() + { + GlobalSettings = + { + ColorMode = ColorMode.Color, + Orientation = Orientation.Portrait, + PaperSize = PaperKind.A4, + Margins = new MarginSettings() { Top = 10, Bottom = 15, Left = 10, Right = 10 }, + }, + Objects = + { + new ObjectSettings() + { + PagesCount = true, + HtmlContent = html, + WebSettings = { DefaultEncoding = "utf-8", Background = true }, + HeaderSettings = { FontSize = 9, Right = "Page [page] of [toPage]", Line = true, Spacing = 2.812 }, + }, + }, + }; + + byte[] pdf = _converter.Convert(doc); + + return new MemoryStream(pdf); + } + + public Task ConvertAsync(string html, PdfOptions pdfOptions = null) + { + return Task.FromResult(Convert(html, pdfOptions)); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/DinkToPdf/DinkToPdfConverterCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/DinkToPdf/DinkToPdfConverterCollectionExtensions.cs new file mode 100644 index 000000000..ff031cf6c --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/DinkToPdf/DinkToPdfConverterCollectionExtensions.cs @@ -0,0 +1,18 @@ +using ClassifiedAds.CrossCuttingConcerns.PdfConverter; +using ClassifiedAds.Infrastructure.PdfConverters.DinkToPdf; +using DinkToPdf; +using DinkToPdf.Contracts; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class DinkToPdfConverterCollectionExtensions + { + public static IServiceCollection AddDinkToPdfConverter(this IServiceCollection services) + { + services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools())); + services.AddSingleton(); + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/PuppeteerSharp/PuppeteerSharpConverter.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/PuppeteerSharp/PuppeteerSharpConverter.cs new file mode 100644 index 000000000..b7aa7f367 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/PuppeteerSharp/PuppeteerSharpConverter.cs @@ -0,0 +1,26 @@ +using ClassifiedAds.CrossCuttingConcerns.PdfConverter; +using PuppeteerSharp; +using System.IO; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.PdfConverters.PuppeteerSharp +{ + public class PuppeteerSharpConverter : IPdfConverter + { + public Stream Convert(string html, CrossCuttingConcerns.PdfConverter.PdfOptions pdfOptions = null) + { + return ConvertAsync(html, pdfOptions).GetAwaiter().GetResult(); + } + + public async Task ConvertAsync(string html, CrossCuttingConcerns.PdfConverter.PdfOptions pdfOptions = null) + { + await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true }); + await using var page = await browser.NewPageAsync(); + await page.SetContentAsync(html); + return new MemoryStream(await page.PdfDataAsync(new global::PuppeteerSharp.PdfOptions + { + PrintBackground = true, + })); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/PuppeteerSharp/PuppeteerSharpConverterCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/PuppeteerSharp/PuppeteerSharpConverterCollectionExtensions.cs new file mode 100644 index 000000000..84f141e3f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/PdfConverters/PuppeteerSharp/PuppeteerSharpConverterCollectionExtensions.cs @@ -0,0 +1,19 @@ +using ClassifiedAds.CrossCuttingConcerns.PdfConverter; +using ClassifiedAds.Infrastructure.PdfConverters.PuppeteerSharp; +using PuppeteerSharp; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class PuppeteerSharpConverterCollectionExtensions + { + public static IServiceCollection AddPuppeteerSharpPdfConverter(this IServiceCollection services) + { + var browserFetcher = new BrowserFetcher(); + browserFetcher.DownloadAsync().GetAwaiter().GetResult(); + + services.AddSingleton(); + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonOptions.cs new file mode 100644 index 000000000..e204b1881 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonOptions.cs @@ -0,0 +1,15 @@ +namespace ClassifiedAds.Infrastructure.Storages.Amazon +{ + public class AmazonOptions + { + public string AccessKeyID { get; set; } + + public string SecretAccessKey { get; set; } + + public string BucketName { get; set; } + + public string Path { get; set; } + + public string RegionEndpoint { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonS3HealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonS3HealthCheck.cs new file mode 100644 index 000000000..5a18b9503 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonS3HealthCheck.cs @@ -0,0 +1,51 @@ +using Amazon; +using Amazon.S3; +using Amazon.S3.Transfer; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Amazon +{ + public class AmazonS3HealthCheck : IHealthCheck + { + private readonly IAmazonS3 _client; + private readonly AmazonOptions _options; + + public AmazonS3HealthCheck(AmazonOptions options) + { + _client = new AmazonS3Client(options.AccessKeyID, options.SecretAccessKey, RegionEndpoint.GetBySystemName(options.RegionEndpoint)); + _options = options; + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + var fileName = _options.Path + $"HealthCheck/{DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")}-{Guid.NewGuid()}.txt"; + var fileTransferUtility = new TransferUtility(_client); + + using var stream = new MemoryStream(Encoding.UTF8.GetBytes($"HealthCheck {DateTime.Now}")); + var uploadRequest = new TransferUtilityUploadRequest + { + InputStream = stream, + Key = fileName, + BucketName = _options.BucketName, + CannedACL = S3CannedACL.NoACL, + }; + + await fileTransferUtility.UploadAsync(uploadRequest, cancellationToken); + await _client.DeleteObjectAsync(_options.BucketName, fileName, cancellationToken); + + return HealthCheckResult.Healthy($"BucketName: {_options.BucketName}"); + } + catch (Exception exception) + { + return new HealthCheckResult(context.Registration.FailureStatus, null, exception); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonS3StorageManager.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonS3StorageManager.cs new file mode 100644 index 000000000..7ec35d9a2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Amazon/AmazonS3StorageManager.cs @@ -0,0 +1,91 @@ +using Amazon; +using Amazon.S3; +using Amazon.S3.Model; +using Amazon.S3.Transfer; +using ClassifiedAds.Domain.Infrastructure.Storages; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Amazon +{ + public class AmazonS3StorageManager : IFileStorageManager + { + private readonly IAmazonS3 _client; + private readonly AmazonOptions _options; + + public AmazonS3StorageManager(AmazonOptions options) + { + _client = new AmazonS3Client(options.AccessKeyID, options.SecretAccessKey, RegionEndpoint.GetBySystemName(options.RegionEndpoint)); + _options = options; + } + + private string GetKey(IFileEntry fileEntry) + { + return _options.Path + fileEntry.FileLocation; + } + + public async Task CreateAsync(IFileEntry fileEntry, Stream stream, CancellationToken cancellationToken = default) + { + var fileTransferUtility = new TransferUtility(_client); + + var uploadRequest = new TransferUtilityUploadRequest + { + InputStream = stream, + Key = GetKey(fileEntry), + BucketName = _options.BucketName, + CannedACL = S3CannedACL.NoACL, + }; + + await fileTransferUtility.UploadAsync(uploadRequest, cancellationToken); + } + + public async Task DeleteAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + await _client.DeleteObjectAsync(_options.BucketName, GetKey(fileEntry), cancellationToken); + } + + public async Task ReadAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + var request = new GetObjectRequest + { + BucketName = _options.BucketName, + Key = GetKey(fileEntry), + }; + + using var response = await _client.GetObjectAsync(request, cancellationToken); + using var responseStream = response.ResponseStream; + using var reader = new MemoryStream(); + await responseStream.CopyToAsync(reader, cancellationToken); + return reader.ToArray(); + } + + public async Task ArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + var copy = new CopyObjectRequest + { + SourceBucket = _options.BucketName, + SourceKey = GetKey(fileEntry), + DestinationBucket = _options.BucketName, + DestinationKey = GetKey(fileEntry), + StorageClass = S3StorageClass.StandardInfrequentAccess, + }; + + await _client.CopyObjectAsync(copy, cancellationToken); + } + + public async Task UnArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + var copy = new CopyObjectRequest + { + SourceBucket = _options.BucketName, + SourceKey = GetKey(fileEntry), + DestinationBucket = _options.BucketName, + DestinationKey = GetKey(fileEntry), + StorageClass = S3StorageClass.Standard, + }; + + await _client.CopyObjectAsync(copy, cancellationToken); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobOption.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobOption.cs new file mode 100644 index 000000000..cc993da48 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobOption.cs @@ -0,0 +1,11 @@ +namespace ClassifiedAds.Infrastructure.Storages.Azure +{ + public class AzureBlobOption + { + public string ConnectionString { get; set; } + + public string Container { get; set; } + + public string Path { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobStorageHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobStorageHealthCheck.cs new file mode 100644 index 000000000..34e3aec6d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobStorageHealthCheck.cs @@ -0,0 +1,41 @@ +using Azure.Storage.Blobs; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Azure +{ + public class AzureBlobStorageHealthCheck : IHealthCheck + { + private readonly AzureBlobOption _option; + private readonly BlobContainerClient _container; + + public AzureBlobStorageHealthCheck(AzureBlobOption option) + { + _option = option; + _container = new BlobContainerClient(_option.ConnectionString, _option.Container); + } + + public async Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default(CancellationToken)) + { + try + { + var fileName = _option.Path + $"HealthCheck/{DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss")}-{Guid.NewGuid()}.txt"; + using var stream = new MemoryStream(Encoding.UTF8.GetBytes($"HealthCheck {DateTime.Now}")); + await _container.CreateIfNotExistsAsync(cancellationToken: cancellationToken); + BlobClient blob = _container.GetBlobClient(fileName); + await blob.UploadAsync(stream, overwrite: true, cancellationToken); + await blob.DeleteAsync(cancellationToken: cancellationToken); + + return HealthCheckResult.Healthy($"ContainerName: {_option.Container}"); + } + catch (Exception exception) + { + return new HealthCheckResult(context.Registration.FailureStatus, null, exception); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobStorageManager.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobStorageManager.cs new file mode 100644 index 000000000..9cd21a80b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Azure/AzureBlobStorageManager.cs @@ -0,0 +1,64 @@ +using Azure.Storage.Blobs; +using Azure.Storage.Blobs.Models; +using ClassifiedAds.Domain.Infrastructure.Storages; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Azure +{ + public class AzureBlobStorageManager : IFileStorageManager + { + private readonly AzureBlobOption _option; + private readonly BlobContainerClient _container; + + public AzureBlobStorageManager(AzureBlobOption option) + { + _option = option; + _container = new BlobContainerClient(_option.ConnectionString, _option.Container); + } + + private string GetBlobName(IFileEntry fileEntry) + { + return _option.Path + fileEntry.FileLocation; + } + + public async Task CreateAsync(IFileEntry fileEntry, Stream stream, CancellationToken cancellationToken = default) + { + await _container.CreateIfNotExistsAsync(cancellationToken: cancellationToken); + + BlobClient blob = _container.GetBlobClient(GetBlobName(fileEntry)); + await blob.UploadAsync(stream, overwrite: true, cancellationToken); + } + + public async Task DeleteAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + BlobClient blob = _container.GetBlobClient(GetBlobName(fileEntry)); + await blob.DeleteAsync(cancellationToken: cancellationToken); + } + + public async Task ReadAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + BlobClient blob = _container.GetBlobClient(GetBlobName(fileEntry)); + using var stream = new MemoryStream(); + await blob.DownloadToAsync(stream, cancellationToken); + return stream.ToArray(); + } + + public async Task ArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + BlobClient blob = _container.GetBlobClient(GetBlobName(fileEntry)); + await blob.SetAccessTierAsync(AccessTier.Cool, cancellationToken: cancellationToken); + } + + public async Task UnArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + BlobClient blob = _container.GetBlobClient(GetBlobName(fileEntry)); + await blob.SetAccessTierAsync(AccessTier.Hot, cancellationToken: cancellationToken); + } + + public void Dispose() + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Fake/FakeStorageManager.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Fake/FakeStorageManager.cs new file mode 100644 index 000000000..4e05233d4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Fake/FakeStorageManager.cs @@ -0,0 +1,37 @@ +using ClassifiedAds.Domain.Infrastructure.Storages; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Fake +{ + public class FakeStorageManager : IFileStorageManager + { + public Task CreateAsync(IFileEntry fileEntry, Stream stream, CancellationToken cancellationToken = default) + { + fileEntry.FileLocation = "Fake.txt"; + return Task.CompletedTask; + } + + public Task DeleteAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + public Task ReadAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + return Task.FromResult(Encoding.UTF8.GetBytes("The content is generated by Fake Storage Manager.")); + } + + public Task ArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + public Task UnArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalFileHealthCheck.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalFileHealthCheck.cs new file mode 100644 index 000000000..0473c5a3f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalFileHealthCheck.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Diagnostics.HealthChecks; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Local +{ + public class LocalFileHealthCheck : IHealthCheck + { + private readonly LocalFileHealthCheckOptions _options; + + public LocalFileHealthCheck(LocalFileHealthCheckOptions options) + { + _options = options; + } + + public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) + { + try + { + var testFile = $"{_options.Path}\\HealthCheck_{Guid.NewGuid()}.txt"; + using (var fs = File.Create(testFile)) + { + } + + File.Delete(testFile); + + return Task.FromResult(HealthCheckResult.Healthy($"Path: {_options.Path}")); + } + catch (Exception ex) + { + if (context.Registration.FailureStatus == HealthStatus.Unhealthy) + { + return Task.FromResult(HealthCheckResult.Unhealthy(ex.Message)); + } + else + { + return Task.FromResult(HealthCheckResult.Degraded(ex.Message, ex)); + } + } + } + } + + public class LocalFileHealthCheckOptions : LocalOptions + { + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalFileStorageManager.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalFileStorageManager.cs new file mode 100644 index 000000000..509a62398 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalFileStorageManager.cs @@ -0,0 +1,63 @@ +using ClassifiedAds.Domain.Infrastructure.Storages; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Storages.Local +{ + public class LocalFileStorageManager : IFileStorageManager + { + private readonly LocalOptions _option; + + public LocalFileStorageManager(LocalOptions option) + { + _option = option; + } + + public async Task CreateAsync(IFileEntry fileEntry, Stream stream, CancellationToken cancellationToken = default) + { + var filePath = Path.Combine(_option.Path, fileEntry.FileLocation); + + var folder = Path.GetDirectoryName(filePath); + + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + + using (var fileStream = File.Create(filePath)) + { + await stream.CopyToAsync(fileStream, cancellationToken); + } + } + + public async Task DeleteAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + await Task.Run(() => + { + var path = Path.Combine(_option.Path, fileEntry.FileLocation); + if (File.Exists(path)) + { + File.Delete(path); + } + }, cancellationToken); + } + + public Task ReadAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + return File.ReadAllBytesAsync(Path.Combine(_option.Path, fileEntry.FileLocation), cancellationToken); + } + + public Task ArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + // TODO: move to archive storage + return Task.CompletedTask; + } + + public Task UnArchiveAsync(IFileEntry fileEntry, CancellationToken cancellationToken = default) + { + // TODO: move to active storage + return Task.CompletedTask; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalOptions.cs new file mode 100644 index 000000000..b4424d155 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/Local/LocalOptions.cs @@ -0,0 +1,7 @@ +namespace ClassifiedAds.Infrastructure.Storages.Local +{ + public class LocalOptions + { + public string Path { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/StorageOptions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/StorageOptions.cs new file mode 100644 index 000000000..36f774bea --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/StorageOptions.cs @@ -0,0 +1,37 @@ +using ClassifiedAds.Infrastructure.Storages.Amazon; +using ClassifiedAds.Infrastructure.Storages.Azure; +using ClassifiedAds.Infrastructure.Storages.Local; + +namespace ClassifiedAds.Infrastructure.Storages +{ + public class StorageOptions + { + public string Provider { get; set; } + + public LocalOptions Local { get; set; } + + public AzureBlobOption Azure { get; set; } + + public AmazonOptions Amazon { get; set; } + + public bool UsedLocal() + { + return Provider == "Local"; + } + + public bool UsedAzure() + { + return Provider == "Azure"; + } + + public bool UsedAmazon() + { + return Provider == "Amazon"; + } + + public bool UsedFake() + { + return Provider == "Fake"; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/StoragesCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/StoragesCollectionExtensions.cs new file mode 100644 index 000000000..bf2a796aa --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Storages/StoragesCollectionExtensions.cs @@ -0,0 +1,89 @@ +using ClassifiedAds.Domain.Infrastructure.Storages; +using ClassifiedAds.Infrastructure.Storages; +using ClassifiedAds.Infrastructure.Storages.Amazon; +using ClassifiedAds.Infrastructure.Storages.Azure; +using ClassifiedAds.Infrastructure.Storages.Fake; +using ClassifiedAds.Infrastructure.Storages.Local; +using Microsoft.Extensions.Diagnostics.HealthChecks; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class StoragesCollectionExtensions + { + public static IServiceCollection AddLocalStorageManager(this IServiceCollection services, LocalOptions options) + { + services.AddSingleton(new LocalFileStorageManager(options)); + + return services; + } + + public static IServiceCollection AddAzureBlobStorageManager(this IServiceCollection services, AzureBlobOption options) + { + services.AddSingleton(new AzureBlobStorageManager(options)); + + return services; + } + + public static IServiceCollection AddAmazonS3StorageManager(this IServiceCollection services, AmazonOptions options) + { + services.AddSingleton(new AmazonS3StorageManager(options)); + + return services; + } + + public static IServiceCollection AddFakeStorageManager(this IServiceCollection services) + { + services.AddSingleton(new FakeStorageManager()); + + return services; + } + + public static IServiceCollection AddStorageManager(this IServiceCollection services, StorageOptions options, IHealthChecksBuilder healthChecksBuilder = null) + { + if (options.UsedAzure()) + { + services.AddAzureBlobStorageManager(options.Azure); + + if (healthChecksBuilder != null) + { + healthChecksBuilder.AddAzureBlobStorage( + options.Azure, + name: "Storage (Azure Blob)", + failureStatus: HealthStatus.Degraded); + } + } + else if (options.UsedAmazon()) + { + services.AddAmazonS3StorageManager(options.Amazon); + + if (healthChecksBuilder != null) + { + healthChecksBuilder.AddAmazonS3( + options.Amazon, + name: "Storage (Amazon S3)", + failureStatus: HealthStatus.Degraded); + } + } + else if (options.UsedLocal()) + { + services.AddLocalStorageManager(options.Local); + + if (healthChecksBuilder != null) + { + healthChecksBuilder.AddLocalFile(new LocalFileHealthCheckOptions + { + Path = options.Local.Path, + }, + name: "Storage (Local Directory)", + failureStatus: HealthStatus.Degraded); + } + } + else + { + services.AddFakeStorageManager(); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authentication/TokenManager.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authentication/TokenManager.cs new file mode 100644 index 000000000..a186e6af9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authentication/TokenManager.cs @@ -0,0 +1,55 @@ +using IdentityModel.Client; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Authentication +{ + public class TokenManager + { + private readonly IHttpClientFactory _httpClientFactory; + private readonly OpenIdConnectOptions _options; + + public TokenManager(IHttpClientFactory httpClientFactory, OpenIdConnectOptions options) + { + _httpClientFactory = httpClientFactory; + _options = options; + } + + public async Task RefreshToken(string refreshToken) + { + var httpClient = _httpClientFactory.CreateClient(); + var metaDataResponse = await httpClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest + { + Address = _options.Authority, + Policy = { RequireHttps = _options.RequireHttpsMetadata }, + }); + + var response = await httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest + { + Address = metaDataResponse.TokenEndpoint, + ClientId = _options.ClientId, + ClientSecret = _options.ClientSecret, + RefreshToken = refreshToken, + }); + + if (response.IsError) + { + if (response.HttpStatusCode == System.Net.HttpStatusCode.BadRequest) + { + return null; + } + + throw new Exception(response.Raw); + } + + return new TokenModel + { + AccessToken = response.AccessToken, + RefreshToken = response.RefreshToken, + ExpiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(response.ExpiresIn), + }; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authentication/TokenModel.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authentication/TokenModel.cs new file mode 100644 index 000000000..c7982bfe0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authentication/TokenModel.cs @@ -0,0 +1,21 @@ +using System; + +namespace ClassifiedAds.Infrastructure.Web.Authentication +{ + public class TokenModel + { + public string AccessToken { get; set; } + + public string RefreshToken { get; set; } + + public DateTimeOffset ExpiresAt { get; set; } + + public bool TokenExpired + { + get + { + return ExpiresAt.AddSeconds(-60).ToUniversalTime() <= DateTime.UtcNow; + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/AuthorizePolicyAttribute.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/AuthorizePolicyAttribute.cs new file mode 100644 index 000000000..39b3876e2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/AuthorizePolicyAttribute.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; +using System; + +namespace ClassifiedAds.Infrastructure.Web.Authorization.Policies +{ + public class AuthorizePolicyAttribute : AuthorizeAttribute + { + public AuthorizePolicyAttribute(Type policy) + : base(policy.FullName) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/IPolicy.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/IPolicy.cs new file mode 100644 index 000000000..9d08a6c06 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/IPolicy.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Authorization; + +namespace ClassifiedAds.Infrastructure.Web.Authorization.Policies +{ + public interface IPolicy + { + void Configure(AuthorizationPolicyBuilder policy); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/PolicyServiceCollectionExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/PolicyServiceCollectionExtensions.cs new file mode 100644 index 000000000..ce83efcee --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Authorization/Policies/PolicyServiceCollectionExtensions.cs @@ -0,0 +1,42 @@ +using ClassifiedAds.Infrastructure.Web.Authorization.Policies; +using Microsoft.AspNetCore.Authorization; +using System; +using System.Linq; +using System.Reflection; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class PolicyServiceCollectionExtensions + { + public static IServiceCollection AddAuthorizationPolicies(this IServiceCollection services, Assembly assembly) + { + services.Configure(options => + { + var policyTypes = assembly.GetTypes().Where(t => t.GetInterfaces().Any(i => i == typeof(IPolicy))).ToList(); + + foreach (var type in policyTypes) + { + var obj = (IPolicy)Activator.CreateInstance(type); + + var policyName = type.FullName; + + options.AddPolicy(policyName, policy => + { + obj.Configure(policy); + }); + } + }); + + var requirementHandlerTypes = assembly.GetTypes() + .Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(AuthorizationHandler<>)) + .ToList(); + + foreach (var type in requirementHandlerTypes) + { + services.AddSingleton(typeof(IAuthorizationHandler), type); + } + + return services; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Filters/GlobalExceptionFilter.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Filters/GlobalExceptionFilter.cs new file mode 100644 index 000000000..d373d1aa3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Filters/GlobalExceptionFilter.cs @@ -0,0 +1,75 @@ +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using System.Net; + +namespace ClassifiedAds.Infrastructure.Web.Filters +{ + public class GlobalExceptionFilter : IExceptionFilter + { + private readonly ILogger _logger; + private readonly GlobalExceptionFilterOptions _options; + + public GlobalExceptionFilter(ILogger logger, + IOptionsSnapshot options) + { + _logger = logger; + _options = options.Value; + } + + public void OnException(ExceptionContext context) + { + if (context.Exception is NotFoundException) + { + context.Result = new NotFoundResult(); + } + else if (context.Exception is ValidationException) + { + context.Result = new BadRequestObjectResult(context.Exception.Message); + } + else + { + _logger.LogError(context.Exception, $"[{DateTime.UtcNow.Ticks}-{Environment.CurrentManagedThreadId}]"); + + if (_options.DetailLevel == GlobalExceptionDetailLevel.Throw) + { + return; + } + + context.Result = new ObjectResult(new { Message = GetErrorMessage(context.Exception) }) + { + StatusCode = (int)HttpStatusCode.InternalServerError, + }; + } + } + + private string GetErrorMessage(Exception ex) + { + return _options.DetailLevel switch + { + GlobalExceptionDetailLevel.None => "An internal exception has occurred.", + GlobalExceptionDetailLevel.Message => ex.Message, + GlobalExceptionDetailLevel.StackTrace => ex.StackTrace, + GlobalExceptionDetailLevel.ToString => ex.ToString(), + _ => "An internal exception has occurred.", + }; + } + } + + public class GlobalExceptionFilterOptions + { + public GlobalExceptionDetailLevel DetailLevel { get; set; } + } + + public enum GlobalExceptionDetailLevel + { + None, + Message, + StackTrace, + ToString, + Throw, + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/AccessTokenFromFormMiddleware.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/AccessTokenFromFormMiddleware.cs new file mode 100644 index 000000000..2f64f881d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/AccessTokenFromFormMiddleware.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Middleware +{ + internal class AccessTokenFromFormMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public AccessTokenFromFormMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + if (context.Request.Method == "POST" + && context.Request.ContentType == "application/x-www-form-urlencoded" + && string.IsNullOrEmpty(context.Request.Headers["Authorization"])) + { + var token = context.Request.Form["access_token"]; + if (!string.IsNullOrEmpty(token)) + { + context.Request.Headers.Add("Authorization", "Bearer " + token); + } + } + + await _next(context); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/DebuggingMiddleware.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/DebuggingMiddleware.cs new file mode 100644 index 000000000..5819a1b6a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/DebuggingMiddleware.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Middleware +{ + public class DebuggingMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public DebuggingMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + var stopwatch = Stopwatch.StartNew(); + await _next(context); + var elapsedTime = stopwatch.Elapsed; + + // inspect the context here + // if (context.Request.Path.HasValue && context.Request.Path.Value.Contains("oidc")) + // { + // } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/GlobalExceptionHandlerMiddleware.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/GlobalExceptionHandlerMiddleware.cs new file mode 100644 index 000000000..37f1a25de --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/GlobalExceptionHandlerMiddleware.cs @@ -0,0 +1,87 @@ +using ClassifiedAds.CrossCuttingConcerns.Exceptions; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System; +using System.Net; +using System.Text.Json; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Middleware +{ + public class GlobalExceptionHandlerMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private readonly GlobalExceptionHandlerMiddlewareOptions _options; + + public GlobalExceptionHandlerMiddleware(RequestDelegate next, + ILogger logger, + GlobalExceptionHandlerMiddlewareOptions options) + { + _next = next; + _logger = logger; + _options = options; + } + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + var response = context.Response; + response.ContentType = "application/json"; + + switch (ex) + { + case ValidationException: + response.StatusCode = (int)HttpStatusCode.BadRequest; + break; + case NotFoundException: + response.StatusCode = (int)HttpStatusCode.NotFound; + break; + default: + _logger.LogError(ex, $"[{DateTime.UtcNow.Ticks}-{Environment.CurrentManagedThreadId}]"); + response.StatusCode = (int)HttpStatusCode.InternalServerError; + break; + } + + var result = JsonSerializer.Serialize(new { message = GetErrorMessage(ex) }); + await response.WriteAsync(result); + } + } + + private string GetErrorMessage(Exception ex) + { + if (ex is ValidationException) + { + return ex.Message; + } + + return _options.DetailLevel switch + { + GlobalExceptionDetailLevel.None => "An internal exception has occurred.", + GlobalExceptionDetailLevel.Message => ex.Message, + GlobalExceptionDetailLevel.StackTrace => ex.StackTrace, + GlobalExceptionDetailLevel.ToString => ex.ToString(), + _ => "An internal exception has occurred.", + }; + } + } + + public class GlobalExceptionHandlerMiddlewareOptions + { + public GlobalExceptionDetailLevel DetailLevel { get; set; } + } + + public enum GlobalExceptionDetailLevel + { + None, + Message, + StackTrace, + ToString, + Throw, + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/IApplicationBuilderExtensions.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/IApplicationBuilderExtensions.cs new file mode 100644 index 000000000..9e775d102 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/IApplicationBuilderExtensions.cs @@ -0,0 +1,54 @@ +using ClassifiedAds.Infrastructure.Web.Middleware; +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Builder +{ + public static class IApplicationBuilderExtensions + { + public static IApplicationBuilder UseIPFiltering(this IApplicationBuilder app) + { + app.UseMiddleware(); + return app; + } + + public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder app, Dictionary headers) + { + app.UseMiddleware(headers); + return app; + } + + public static IApplicationBuilder UseDebuggingMiddleware(this IApplicationBuilder app) + { + app.UseMiddleware(); + return app; + } + + public static IApplicationBuilder UseGlobalExceptionHandlerMiddleware(this IApplicationBuilder app, GlobalExceptionHandlerMiddlewareOptions options = default) + { + options ??= new GlobalExceptionHandlerMiddlewareOptions(); + app.UseMiddleware(options); + return app; + } + + public static IApplicationBuilder UseGlobalExceptionHandlerMiddleware(this IApplicationBuilder app, Action configureOptions) + { + var options = new GlobalExceptionHandlerMiddlewareOptions(); + configureOptions(options); + app.UseMiddleware(options); + return app; + } + + public static IApplicationBuilder UseLoggingStatusCodeMiddleware(this IApplicationBuilder app) + { + app.UseMiddleware(); + return app; + } + + public static IApplicationBuilder UseAccessTokenFromFormMiddleware(this IApplicationBuilder app) + { + app.UseMiddleware(); + return app; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/IPFilteringMiddleware.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/IPFilteringMiddleware.cs new file mode 100644 index 000000000..1ee3fef97 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/IPFilteringMiddleware.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Middleware +{ + public class IPFilteringMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public IPFilteringMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + var remoteIp = context.Connection.RemoteIpAddress; + _logger.LogInformation($"Request from Remote IP address: {remoteIp}"); + + await _next(context); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/LoggingStatusCodeMiddleware.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/LoggingStatusCodeMiddleware.cs new file mode 100644 index 000000000..820625860 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/LoggingStatusCodeMiddleware.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Middleware +{ + public class LoggingStatusCodeMiddleware + { + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public LoggingStatusCodeMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + await _next(context); + + var statusCode = context.Response.StatusCode; + var path = context.Request.Path; + var method = context.Request.Method; + var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? context.User.FindFirst("sub")?.Value; + var remoteIp = context.Connection.RemoteIpAddress; + + var statusCodes = new[] { StatusCodes.Status401Unauthorized, StatusCodes.Status403Forbidden }; + + if (statusCodes.Contains(statusCode)) + { + _logger.LogWarning($"StatusCode: {statusCode}, UserId: {userId}, Path: {path}, Method: {method}, IP: {remoteIp}"); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/SecurityHeadersMiddleware.cs b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/SecurityHeadersMiddleware.cs new file mode 100644 index 000000000..50cefa153 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Infrastructure/Web/Middleware/SecurityHeadersMiddleware.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Http; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace ClassifiedAds.Infrastructure.Web.Middleware +{ + public class SecurityHeadersMiddleware + { + private readonly RequestDelegate _next; + private readonly Dictionary _headers; + + public SecurityHeadersMiddleware(RequestDelegate next, Dictionary headers) + { + _next = next; + _headers = headers; + } + + public async Task Invoke(HttpContext context) + { + foreach (var header in _headers) + { + context.Response.Headers[header.Key] = header.Value; + } + + await _next(context); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Migrator/.editorconfig b/src/IdentityServer/ClassifiedAds.Migrator/.editorconfig new file mode 100644 index 000000000..43d5b5702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/.editorconfig @@ -0,0 +1,224 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +# insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = false +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + +############################### +# StyleCop # +############################### +[*.cs] + +# SA1027: Use tabs correctly +dotnet_diagnostic.SA1027.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +dotnet_diagnostic.SA1111.severity = none + +# SA1113: Comma should be on the same line as previous parameter +dotnet_diagnostic.SA1113.severity = none + +# SA1115: Parameter should follow comma +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1208: System using directives should be placed before other using directives +dotnet_diagnostic.SA1208.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +dotnet_diagnostic.SA1310.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# SA1610: Property documentation should have value text +dotnet_diagnostic.SA1610.severity = none + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = suggestion diff --git a/src/IdentityServer/ClassifiedAds.Migrator/ClassifiedAds.Migrator.csproj b/src/IdentityServer/ClassifiedAds.Migrator/ClassifiedAds.Migrator.csproj new file mode 100644 index 000000000..16af7acdc --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/ClassifiedAds.Migrator.csproj @@ -0,0 +1,41 @@ + + + + net6.0 + Recommended + All + b8df572d-5be2-4f41-b55a-8f4e2ef2b569 + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/src/IdentityServer/ClassifiedAds.Migrator/DbUp/MiniProfiler/0001-CreateTables.sql b/src/IdentityServer/ClassifiedAds.Migrator/DbUp/MiniProfiler/0001-CreateTables.sql new file mode 100644 index 000000000..929572742 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/DbUp/MiniProfiler/0001-CreateTables.sql @@ -0,0 +1,69 @@ +CREATE TABLE [MiniProfilerClientTimings] ( + [RowId] int NOT NULL IDENTITY, + [Id] uniqueidentifier NOT NULL, + [MiniProfilerId] uniqueidentifier NOT NULL, + [Name] nvarchar(200) NULL, + [Start] decimal(9,3) NOT NULL, + [Duration] decimal(9,3) NOT NULL, + CONSTRAINT [PK_MiniProfilerClientTimings] PRIMARY KEY ([RowId]) +); +GO + + +CREATE TABLE [MiniProfilers] ( + [RowId] int NOT NULL IDENTITY, + [Id] uniqueidentifier NOT NULL, + [RootTimingId] uniqueidentifier NULL, + [Name] nvarchar(200) NULL, + [Started] datetime2 NOT NULL, + [DurationMilliseconds] decimal(15,1) NOT NULL, + [User] nvarchar(100) NULL, + [HasUserViewed] bit NOT NULL, + [MachineName] nvarchar(100) NULL, + [CustomLinksJson] nvarchar(max) NULL, + [ClientTimingsRedirectCount] int NULL, + CONSTRAINT [PK_MiniProfilers] PRIMARY KEY ([RowId]) +); +GO + + +CREATE TABLE [MiniProfilerTimings] ( + [RowId] int NOT NULL IDENTITY, + [Id] uniqueidentifier NOT NULL, + [MiniProfilerId] uniqueidentifier NOT NULL, + [ParentTimingId] uniqueidentifier NULL, + [Name] nvarchar(200) NULL, + [DurationMilliseconds] decimal(15,3) NOT NULL, + [StartMilliseconds] decimal(15,3) NOT NULL, + [IsRoot] bit NOT NULL, + [Depth] smallint NOT NULL, + [CustomTimingsJson] nvarchar(max) NULL, + CONSTRAINT [PK_MiniProfilerTimings] PRIMARY KEY ([RowId]) +); +GO + + +CREATE UNIQUE INDEX [IX_MiniProfilerClientTimings_Id] ON [MiniProfilerClientTimings] ([Id]); +GO + + +CREATE INDEX [IX_MiniProfilerClientTimings_MiniProfilerId] ON [MiniProfilerClientTimings] ([MiniProfilerId]); +GO + + +CREATE UNIQUE INDEX [IX_MiniProfilers_Id] ON [MiniProfilers] ([Id]); +GO + + +CREATE INDEX [IX_MiniProfilers_User_HasUserViewed] ON [MiniProfilers] ([User], [HasUserViewed]) INCLUDE ([Id], [Started]); +GO + + +CREATE UNIQUE INDEX [IX_MiniProfilerTimings_Id] ON [MiniProfilerTimings] ([Id]); +GO + + +CREATE INDEX [IX_MiniProfilerTimings_MiniProfilerId] ON [MiniProfilerTimings] ([MiniProfilerId]); +GO + + diff --git a/src/IdentityServer/ClassifiedAds.Migrator/DbUp/SqlServerDistributedCache/0001-CreateTables.sql b/src/IdentityServer/ClassifiedAds.Migrator/DbUp/SqlServerDistributedCache/0001-CreateTables.sql new file mode 100644 index 000000000..a1ed5dbe5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/DbUp/SqlServerDistributedCache/0001-CreateTables.sql @@ -0,0 +1,18 @@ +CREATE TABLE [dbo].[CacheEntries]( + [Id] [nvarchar](449) NOT NULL, + [Value] [varbinary](max) NOT NULL, + [ExpiresAtTime] [datetimeoffset](7) NOT NULL, + [SlidingExpirationInSeconds] [bigint] NULL, + [AbsoluteExpiration] [datetimeoffset](7) NULL, +PRIMARY KEY CLUSTERED +( + [Id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX [Index_ExpiresAtTime] ON [dbo].[CacheEntries] +( + [ExpiresAtTime] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +GO diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Dockerfile b/src/IdentityServer/ClassifiedAds.Migrator/Dockerfile new file mode 100644 index 000000000..5b048b6b3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/Dockerfile @@ -0,0 +1,36 @@ +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env +WORKDIR /ClassifiedAds.Monolith + +# Copy csproj and restore as distinct layers +COPY ./ClassifiedAds.Application/*.csproj ./ClassifiedAds.Application/ +COPY ./ClassifiedAds.ArchTests/*.csproj ./ClassifiedAds.ArchTests/ +COPY ./ClassifiedAds.BackgroundServer/*.csproj ./ClassifiedAds.BackgroundServer/ +COPY ./ClassifiedAds.Blazor.Modules/*.csproj ./ClassifiedAds.Blazor.Modules/ +COPY ./ClassifiedAds.BlazorServerSide/*.csproj ./ClassifiedAds.BlazorServerSide/ +COPY ./ClassifiedAds.BlazorWebAssembly/*.csproj ./ClassifiedAds.BlazorWebAssembly/ +COPY ./ClassifiedAds.ContractTests/*.csproj ./ClassifiedAds.ContractTests/ +COPY ./ClassifiedAds.CrossCuttingConcerns/*.csproj ./ClassifiedAds.CrossCuttingConcerns/ +COPY ./ClassifiedAds.Domain/*.csproj ./ClassifiedAds.Domain/ +COPY ./ClassifiedAds.EndToEndTests/*.csproj ./ClassifiedAds.EndToEndTests/ +COPY ./ClassifiedAds.GraphQL/*.csproj ./ClassifiedAds.GraphQL/ +COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ +COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ +COPY ./ClassifiedAds.IntegrationTests/*.csproj ./ClassifiedAds.IntegrationTests/ +COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ +COPY ./ClassifiedAds.Persistence/*.csproj ./ClassifiedAds.Persistence/ +COPY ./ClassifiedAds.UnitTests/*.csproj ./ClassifiedAds.UnitTests/ +COPY ./ClassifiedAds.WebAPI/*.csproj ./ClassifiedAds.WebAPI/ +COPY ./ClassifiedAds.WebMVC/*.csproj ./ClassifiedAds.WebMVC/ +COPY ./ClassifiedAds.Monolith.sln . +RUN dotnet restore + +# Copy everything else and build +COPY . ./ +RUN dotnet publish ./ClassifiedAds.Migrator/ClassifiedAds.Migrator.csproj -c Release -o out + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:6.0 +WORKDIR /ClassifiedAds.Monolith +COPY --from=build-env /ClassifiedAds.Monolith/out . + +ENTRYPOINT ["dotnet", "ClassifiedAds.Migrator.dll"] \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Migrator/PowerShell.txt b/src/IdentityServer/ClassifiedAds.Migrator/PowerShell.txt new file mode 100644 index 000000000..6b84c45db --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/PowerShell.txt @@ -0,0 +1,13 @@ +https://docs.microsoft.com/en-us/ef/core/cli/powershell + +Add-Migration -Context ConfigurationDbContext Init -OutputDir Migrations/ConfigurationDb +Add-Migration -Context PersistedGrantDbContext Init -OutputDir Migrations/PersistedGrantDb + +Update-Database -Context ConfigurationDbContext +Update-Database -Context PersistedGrantDbContext + +Script-DbContext -Context ConfigurationDbContext +Script-DbContext -Context PersistedGrantDbContext + +Script-Migration -Context ConfigurationDbContext +Script-Migration -Context PersistedGrantDbContext diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Program.cs b/src/IdentityServer/ClassifiedAds.Migrator/Program.cs new file mode 100644 index 000000000..91fd4d6f0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace ClassifiedAds.Migrator +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Properties/launchSettings.json b/src/IdentityServer/ClassifiedAds.Migrator/Properties/launchSettings.json new file mode 100644 index 000000000..c848ddb42 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:54794/", + "sslPort": 44380 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "ClassifiedAds.Migrator": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:5001;http://localhost:5000" + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Functions/scripts.sql b/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Functions/scripts.sql new file mode 100644 index 000000000..e69de29bb diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Indexes/scripts.sql b/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Indexes/scripts.sql new file mode 100644 index 000000000..e69de29bb diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Stored Procedures/scripts.sql b/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Stored Procedures/scripts.sql new file mode 100644 index 000000000..e69de29bb diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Types/scripts.sql b/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Types/scripts.sql new file mode 100644 index 000000000..e69de29bb diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Views/scripts.sql b/src/IdentityServer/ClassifiedAds.Migrator/Scripts/Views/scripts.sql new file mode 100644 index 000000000..e69de29bb diff --git a/src/IdentityServer/ClassifiedAds.Migrator/Startup.cs b/src/IdentityServer/ClassifiedAds.Migrator/Startup.cs new file mode 100644 index 000000000..fa9956ca3 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/Startup.cs @@ -0,0 +1,70 @@ +using ClassifiedAds.Infrastructure.HealthChecks; +using DbUp; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Polly; +using System; +using System.Reflection; + +namespace ClassifiedAds.Migrator +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + if (string.Equals(Configuration["CheckDependency:Enabled"], "true", System.StringComparison.OrdinalIgnoreCase)) + { + NetworkPortCheck.Wait(Configuration["CheckDependency:Host"], 5); + } + + services.AddDateTimeProvider(); + + services.AddPersistence(Configuration["ConnectionStrings:ClassifiedAds"], + typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + + services.AddIdentityServer() + .AddIdServerPersistence(Configuration.GetConnectionString("ClassifiedAds"), + typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + Policy.Handle().WaitAndRetry(new[] + { + TimeSpan.FromSeconds(10), + TimeSpan.FromSeconds(20), + TimeSpan.FromSeconds(30), + }) + .Execute(() => + { + app.MigrateIdServerDb(); + + var upgrader = DeployChanges.To + .SqlDatabase(Configuration.GetConnectionString("ClassifiedAds")) + .WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly()) + .LogToConsole() + .Build(); + + var result = upgrader.PerformUpgrade(); + + if (!result.Successful) + { + throw result.Error; + } + }); + } + } +} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/appsettings.Development.json b/src/IdentityServer/ClassifiedAds.Migrator/appsettings.Development.json similarity index 100% rename from src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/appsettings.Development.json rename to src/IdentityServer/ClassifiedAds.Migrator/appsettings.Development.json diff --git a/src/IdentityServer/ClassifiedAds.Migrator/appsettings.json b/src/IdentityServer/ClassifiedAds.Migrator/appsettings.json new file mode 100644 index 000000000..72517c996 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/appsettings.json @@ -0,0 +1,15 @@ +{ + "ConnectionStrings": { + "ClassifiedAds": "Server=127.0.0.1;Database=ClassifiedAds;User Id=sa;Password=sqladmin123!@#;MultipleActiveResultSets=true" + }, + "CheckDependency": { + "Enabled": false, + "Host": "localhost:1433" + }, + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/IdentityServer/ClassifiedAds.Migrator/dotnet-cli.txt b/src/IdentityServer/ClassifiedAds.Migrator/dotnet-cli.txt new file mode 100644 index 000000000..f91ae9b44 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Migrator/dotnet-cli.txt @@ -0,0 +1,15 @@ +https://docs.microsoft.com/en-us/ef/core/cli/dotnet + +dotnet tool install --global dotnet-ef --version="5.0" + +dotnet ef migrations add Init --context ConfigurationDbContext -o Migrations/ConfigurationDb +dotnet ef migrations add Init --context PersistedGrantDbContext -o Migrations/PersistedGrantDb + +dotnet ef migrations script --context ConfigurationDbContext +dotnet ef migrations script --context PersistedGrantDbContext + +dotnet ef dbcontext script --context ConfigurationDbContext +dotnet ef dbcontext script --context PersistedGrantDbContext + +dotnet ef database update --context ConfigurationDbContext +dotnet ef database update --context PersistedGrantDbContext diff --git a/src/IdentityServer/ClassifiedAds.Persistence/.editorconfig b/src/IdentityServer/ClassifiedAds.Persistence/.editorconfig new file mode 100644 index 000000000..43d5b5702 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/.editorconfig @@ -0,0 +1,224 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +# insert_final_newline = true +charset = utf-8-bom +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = false +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion + +############################### +# StyleCop # +############################### +[*.cs] + +# SA1027: Use tabs correctly +dotnet_diagnostic.SA1027.severity = none + +# SA1101: Prefix local calls with this +dotnet_diagnostic.SA1101.severity = none + +# SA1108: Block statements should not contain embedded comments +dotnet_diagnostic.SA1108.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +dotnet_diagnostic.SA1111.severity = none + +# SA1113: Comma should be on the same line as previous parameter +dotnet_diagnostic.SA1113.severity = none + +# SA1115: Parameter should follow comma +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +dotnet_diagnostic.SA1117.severity = none + +# SA1123: Do not place regions within elements +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +dotnet_diagnostic.SA1124.severity = none + +# SA1200: Using directives should be placed correctly +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +dotnet_diagnostic.SA1204.severity = none + +# SA1208: System using directives should be placed before other using directives +dotnet_diagnostic.SA1208.severity = none + +# SA1309: Field names should not begin with underscore +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +dotnet_diagnostic.SA1310.severity = none + +# SA1402: File may only contain a single type +dotnet_diagnostic.SA1402.severity = none + +# SA1413: Use trailing comma in multi-line initializers +dotnet_diagnostic.SA1413.severity = none + +# SA1516: Elements should be separated by blank line +dotnet_diagnostic.SA1516.severity = none + +# SA1600: Elements should be documented +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +dotnet_diagnostic.SA1602.severity = none + +# SA1610: Property documentation should have value text +dotnet_diagnostic.SA1610.severity = none + +# SA1623: Property summary documentation should match accessors +dotnet_diagnostic.SA1623.severity = none + +# SA1629: Documentation text should end with a period +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +dotnet_diagnostic.SA1633.severity = none + +# SA1649: File name should match first type name +dotnet_diagnostic.SA1649.severity = suggestion diff --git a/src/IdentityServer/ClassifiedAds.Persistence/AdsDbContext.cs b/src/IdentityServer/ClassifiedAds.Persistence/AdsDbContext.cs new file mode 100644 index 000000000..174313368 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/AdsDbContext.cs @@ -0,0 +1,57 @@ +using ClassifiedAds.Domain.Repositories; +using ClassifiedAds.Persistence.Locks; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Data; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Persistence +{ + public class AdsDbContext : DbContext, IUnitOfWork, IDataProtectionKeyContext + { + private IDbContextTransaction _dbContextTransaction; + + public AdsDbContext(DbContextOptions options) + : base(options) + { + } + + public DbSet DataProtectionKeys { get; set; } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, CancellationToken cancellationToken = default) + { + _dbContextTransaction = await Database.BeginTransactionAsync(isolationLevel, cancellationToken); + return _dbContextTransaction; + } + + public async Task BeginTransactionAsync(IsolationLevel isolationLevel = IsolationLevel.ReadCommitted, string lockName = null, CancellationToken cancellationToken = default) + { + _dbContextTransaction = await Database.BeginTransactionAsync(isolationLevel, cancellationToken); + + var sqlLock = new SqlDistributedLock(_dbContextTransaction.GetDbTransaction() as SqlTransaction); + var lockScope = sqlLock.Acquire(lockName); + if (lockScope == null) + { + throw new Exception($"Could not acquire lock: {lockName}"); + } + + return _dbContextTransaction; + } + + public async Task CommitTransactionAsync(CancellationToken cancellationToken = default) + { + await _dbContextTransaction.CommitAsync(cancellationToken); + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/AdsDbContextMultiTenant.cs b/src/IdentityServer/ClassifiedAds.Persistence/AdsDbContextMultiTenant.cs new file mode 100644 index 000000000..365adc47d --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/AdsDbContextMultiTenant.cs @@ -0,0 +1,22 @@ +using ClassifiedAds.CrossCuttingConcerns.Tenants; +using Microsoft.EntityFrameworkCore; + +namespace ClassifiedAds.Persistence +{ + public class AdsDbContextMultiTenant : AdsDbContext + { + private readonly IConnectionStringResolver _connectionStringResolver; + + public AdsDbContextMultiTenant( + IConnectionStringResolver connectionStringResolver) + : base(new DbContextOptions()) + { + _connectionStringResolver = connectionStringResolver; + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlServer(_connectionStringResolver.ConnectionString); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreaker.cs b/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreaker.cs new file mode 100644 index 000000000..ffadd81d0 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreaker.cs @@ -0,0 +1,29 @@ +using ClassifiedAds.CrossCuttingConcerns.CircuitBreakers; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Persistence.CircuitBreakers +{ + public class CircuitBreaker : ICircuitBreaker + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public CircuitStatus Status { get; set; } + + public DateTimeOffset CreatedDateTime { get; set; } + + public DateTimeOffset LastStatusUpdated { get; set; } + + public ICollection CircuitBreakerLogs { get; set; } + + public void EnsureOkStatus() + { + if (Status == CircuitStatus.Open) + { + throw new CircuitBreakerOpenException(); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreakerLog.cs b/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreakerLog.cs new file mode 100644 index 000000000..3b3ecdeba --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreakerLog.cs @@ -0,0 +1,18 @@ +using ClassifiedAds.CrossCuttingConcerns.CircuitBreakers; +using System; + +namespace ClassifiedAds.Persistence.CircuitBreakers +{ + public class CircuitBreakerLog + { + public Guid Id { get; set; } + + public Guid CircuitBreakerId { get; set; } + + public CircuitStatus Status { get; set; } + + public bool Succeeded { get; set; } + + public DateTimeOffset CreatedDateTime { get; set; } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreakerManager.cs b/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreakerManager.cs new file mode 100644 index 000000000..dd6579497 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/CircuitBreakers/CircuitBreakerManager.cs @@ -0,0 +1,120 @@ +using ClassifiedAds.CrossCuttingConcerns.CircuitBreakers; +using ClassifiedAds.CrossCuttingConcerns.OS; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace ClassifiedAds.Persistence.CircuitBreakers +{ + public class CircuitBreakerManager : ICircuitBreakerManager, IDisposable + { + private readonly AdsDbContext _dbContext; + private readonly IDateTimeProvider _dateTimeProvider; + + public CircuitBreakerManager(IDbContextFactory dbContextFactory, IDateTimeProvider dateTimeProvider) + { + _dbContext = dbContextFactory.CreateDbContext(); + _dateTimeProvider = dateTimeProvider; + } + + public ICircuitBreaker GetCircuitBreaker(string name, TimeSpan openTime) + { + CircuitBreaker GetCircuitBreakerByName() + { + return _dbContext.Set().Where(x => x.Name.Equals(name)).FirstOrDefault(); + } + + var circuitBreaker = GetCircuitBreakerByName(); + if (circuitBreaker == null) + { + try + { + circuitBreaker = new CircuitBreaker + { + Name = name, + Status = CircuitStatus.Closed, + CreatedDateTime = _dateTimeProvider.Now, + LastStatusUpdated = _dateTimeProvider.Now, + }; + + _dbContext.Set().Add(circuitBreaker); + _dbContext.SaveChanges(); + } + catch (Exception) + { + circuitBreaker = GetCircuitBreakerByName(); + if (circuitBreaker == null) + { + throw; + } + } + } + + if (circuitBreaker.Status == CircuitStatus.Open && circuitBreaker.LastStatusUpdated + openTime <= _dateTimeProvider.Now) + { + circuitBreaker.Status = CircuitStatus.HalfOpen; + circuitBreaker.LastStatusUpdated = _dateTimeProvider.Now; + _dbContext.SaveChanges(); + } + + return circuitBreaker; + } + + public void LogFailure(ICircuitBreaker circuitBreaker, int maximumNumberOfFailures, TimeSpan period) + { + _dbContext.Set().Add(new CircuitBreakerLog + { + CircuitBreakerId = ((CircuitBreaker)circuitBreaker).Id, + Status = ((CircuitBreaker)circuitBreaker).Status, + Succeeded = false, + CreatedDateTime = _dateTimeProvider.OffsetNow, + }); + + UpdateCircuitBreakerStatus(circuitBreaker, circuitBreaker.Status == CircuitStatus.HalfOpen, CircuitStatus.Open); + + _dbContext.SaveChanges(); + + if (circuitBreaker.Status == CircuitStatus.Closed) + { + var sinceLastTime = _dateTimeProvider.OffsetNow - period; + var numberOfFailures = _dbContext.Set().Where(x => x.CircuitBreakerId == ((CircuitBreaker)circuitBreaker).Id + && x.Succeeded == false && x.CreatedDateTime >= sinceLastTime).Count(); + + UpdateCircuitBreakerStatus(circuitBreaker, numberOfFailures >= maximumNumberOfFailures, CircuitStatus.Open); + + _dbContext.SaveChanges(); + } + } + + public void LogSuccess(ICircuitBreaker circuitBreaker) + { + _dbContext.Set().Add(new CircuitBreakerLog + { + CircuitBreakerId = ((CircuitBreaker)circuitBreaker).Id, + Status = ((CircuitBreaker)circuitBreaker).Status, + Succeeded = true, + CreatedDateTime = _dateTimeProvider.OffsetNow, + }); + + UpdateCircuitBreakerStatus(circuitBreaker, circuitBreaker.Status == CircuitStatus.HalfOpen, CircuitStatus.Closed); + + _dbContext.SaveChanges(); + } + + private void UpdateCircuitBreakerStatus(ICircuitBreaker circuitBreaker, bool shouldUpdate, CircuitStatus circuitStatus) + { + if (!shouldUpdate) + { + return; + } + + circuitBreaker.Status = circuitStatus; + circuitBreaker.LastStatusUpdated = _dateTimeProvider.OffsetNow; + } + + public void Dispose() + { + _dbContext.Dispose(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/ClassifiedAds.Persistence.csproj b/src/IdentityServer/ClassifiedAds.Persistence/ClassifiedAds.Persistence.csproj new file mode 100644 index 000000000..54575cea2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/ClassifiedAds.Persistence.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + Recommended + All + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/Monolith/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs b/src/IdentityServer/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs similarity index 100% rename from src/Monolith/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs rename to src/IdentityServer/ClassifiedAds.Persistence/IdServerPersistenceExtensions.cs diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Locks/LockManager.cs b/src/IdentityServer/ClassifiedAds.Persistence/Locks/LockManager.cs new file mode 100644 index 000000000..cd8c9acd2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Locks/LockManager.cs @@ -0,0 +1,145 @@ +using ClassifiedAds.CrossCuttingConcerns.Locks; +using ClassifiedAds.CrossCuttingConcerns.OS; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; +using System; +using System.Data; + +namespace ClassifiedAds.Persistence.Locks +{ + public class LockManager : ILockManager + { + private readonly AdsDbContext _dbContext; + private readonly IDateTimeProvider _dateTimeProvider; + + public LockManager(AdsDbContext dbContext, IDateTimeProvider dateTimeProvider) + { + _dbContext = dbContext; + _dateTimeProvider = dateTimeProvider; + } + + private void CreateLock(string entityName, string entityId) + { + string sql = @" + merge into + [dbo].[Locks] with (holdlock) t + using + (values (@entityName, @entityId)) s([EntityName], [EntityId]) + on + t.[EntityName] = s.[EntityName] and t.[EntityId] = s.[EntityId] + when not matched then + insert ([EntityName], [EntityId]) values (s.[EntityName], s.[EntityId]); + "; + + _dbContext.Database.ExecuteSqlRaw(sql, + new SqlParameter("entityName", entityName), + new SqlParameter("entityId", entityId)); + } + + public bool AcquireLock(string entityName, string entityId, string ownerId, TimeSpan expirationIn) + { + CreateLock(entityName, entityId); + + if (ExtendLock(entityName, entityId, ownerId, expirationIn)) + { + return true; + } + + var now = _dateTimeProvider.OffsetNow; + var expired = now + expirationIn; + + string sql = @" + Update Locks set OwnerId = @OwnerId, + AcquiredDateTime = @AcquiredDateTime, + ExpiredDateTime = @ExpiredDateTime + where EntityId = @EntityId + and EntityName = @EntityName + and (OwnerId is NULL or ExpiredDateTime < @AcquiredDateTime)"; + + var rs = _dbContext.Database.ExecuteSqlRaw(sql, + new SqlParameter("EntityName", entityName), + new SqlParameter("EntityId", entityId), + new SqlParameter("OwnerId", ownerId), + new SqlParameter("AcquiredDateTime", SqlDbType.DateTimeOffset) { Value = now }, + new SqlParameter("ExpiredDateTime", SqlDbType.DateTimeOffset) { Value = expired }); + + return rs > 0; + } + + public bool ExtendLock(string entityName, string entityId, string ownerId, TimeSpan expirationIn) + { + var now = _dateTimeProvider.OffsetNow; + var expired = now + expirationIn; + + string sql = @" + Update Locks set ExpiredDateTime = @ExpiredDateTime + where EntityId = @EntityId + and EntityName = @EntityName + and OwnerId = @OwnerId"; + + var rs = _dbContext.Database.ExecuteSqlRaw(sql, + new SqlParameter("EntityName", entityName), + new SqlParameter("EntityId", entityId), + new SqlParameter("OwnerId", ownerId), + new SqlParameter("ExpiredDateTime", SqlDbType.DateTimeOffset) { Value = expired }); + + return rs > 0; + } + + public bool ReleaseLock(string entityName, string entityId, string ownerId) + { + string sql = @" + Update Locks set OwnerId = NULL, + AcquiredDateTime = NULL, + ExpiredDateTime = NULL + where EntityId = @EntityId + and EntityName = @EntityName + and OwnerId = @OwnerId"; + + _ = _dbContext.Database.ExecuteSqlRaw(sql, + new SqlParameter("EntityName", entityName), + new SqlParameter("EntityId", entityId), + new SqlParameter("OwnerId", ownerId)); + + return true; + } + + public bool ReleaseLocks(string ownerId) + { + string sql = @" + Update Locks set OwnerId = NULL, + AcquiredDateTime = NULL, + ExpiredDateTime = NULL + where OwnerId = @OwnerId"; + + _ = _dbContext.Database.ExecuteSqlRaw(sql, + new SqlParameter("OwnerId", ownerId)); + + return true; + } + + public bool ReleaseExpiredLocks() + { + var now = _dateTimeProvider.OffsetNow; + + string sql = @" + Update Locks set OwnerId = NULL, + AcquiredDateTime = NULL, + ExpiredDateTime = NULL + where ExpiredDateTime < @now"; + + _ = _dbContext.Database.ExecuteSqlRaw(sql, + new SqlParameter("now", SqlDbType.DateTimeOffset) { Value = now }); + + return true; + } + + public void EnsureAcquiringLock(string entityName, string entityId, string ownerId, TimeSpan expirationIn) + { + if (!AcquireLock(entityName, entityId, ownerId, expirationIn)) + { + throw new CouldNotAcquireLockException(); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Locks/SqlDistributedLock.cs b/src/IdentityServer/ClassifiedAds.Persistence/Locks/SqlDistributedLock.cs new file mode 100644 index 000000000..a9f6e5141 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Locks/SqlDistributedLock.cs @@ -0,0 +1,142 @@ +using ClassifiedAds.CrossCuttingConcerns.Locks; +using EntityFrameworkCore.SqlServer.SimpleBulks.Extensions; +using Microsoft.Data.SqlClient; +using System; +using System.Data; + +namespace ClassifiedAds.Persistence.Locks +{ + public class SqlDistributedLock : IDistributedLock + { + public const int AlreadyHeldReturnCode = 103; + + private readonly SqlConnection _connection; + private readonly SqlTransaction _transaction; + private readonly string _connectionString; + + public bool HasTransaction + { + get + { + return _transaction != null; + } + } + + public SqlDistributedLock(SqlConnection connection) + { + _connection = connection; + } + + public SqlDistributedLock(SqlTransaction transaction) + { + _transaction = transaction; + _connection = _transaction.Connection; + } + + public SqlDistributedLock(string connectionString) + { + _connectionString = connectionString; + _connection = new SqlConnection(connectionString); + } + + public IDistributedLockScope Acquire(string lockName) + { + SqlParameter returnValue; + var acquireCommand = CreateAcquireCommand(0, lockName, -1, out returnValue); + + acquireCommand.ExecuteNonQuery(); + + if (ParseReturnCode((int)returnValue.Value)) + { + return new SqlDistributedLockScope(_connection, _transaction, lockName); + } + else + { + return null; + } + } + + public IDistributedLockScope TryAcquire(string lockName) + { + SqlParameter returnValue; + var acquireCommand = CreateAcquireCommand(30, lockName, 0, out returnValue); + + acquireCommand.ExecuteNonQuery(); + + if (ParseReturnCode((int)returnValue.Value)) + { + return new SqlDistributedLockScope(_connection, _transaction, lockName); + } + else + { + return null; + } + } + + private SqlCommand CreateAcquireCommand(int commandTimeout, string lockName, int lockTimeout, out SqlParameter returnValue) + { + _connection.EnsureOpen(); + + SqlCommand command = _connection.CreateCommand(); + command.Transaction = _transaction; + + returnValue = command.Parameters.Add(new SqlParameter { ParameterName = "Result", DbType = DbType.Int32, Direction = ParameterDirection.Output }); + command.CommandText = + $@"IF APPLOCK_MODE('public', @Resource, @LockOwner) != 'NoLock' {(HasTransaction ? " OR APPLOCK_MODE('public', @Resource, 'Session') != 'NoLock'" : string.Empty)} + SET @Result = {AlreadyHeldReturnCode} + ELSE + EXEC @Result = dbo.sp_getapplock @Resource = @Resource, @LockMode = @LockMode, @LockOwner = @LockOwner, @LockTimeout = @LockTimeout, @DbPrincipal = 'public'" + ; + + command.CommandTimeout = commandTimeout; + + command.Parameters.Add(new SqlParameter("Resource", lockName)); + command.Parameters.Add(new SqlParameter("LockMode", "Exclusive")); + command.Parameters.Add(new SqlParameter("LockOwner", HasTransaction ? "Transaction" : "Session")); + command.Parameters.Add(new SqlParameter("LockTimeout", lockTimeout)); + + return command; + } + + /// + /// sp_getapplock exit codes documented at + /// https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-getapplock-transact-sql#return-code-values + /// + /// code returned after calling sp_getapplock + /// true/false + public static bool ParseReturnCode(int returnCode) + { + switch (returnCode) + { + case 0: + case 1: + return true; + case -1: + return false; + case -2: + throw new OperationCanceledException("The lock request was canceled."); + case -3: + throw new Exception("The lock request was chosen as a deadlock victim."); + case -999: + throw new ArgumentException("parameter validation or other error"); + case AlreadyHeldReturnCode: + return false; + } + + if (returnCode <= 0) + { + throw new InvalidOperationException($"Could not acquire lock with return code: {returnCode}"); + } + + return false; + } + + public void Dispose() + { + if (!string.IsNullOrEmpty(_connectionString)) + { + _connection.Dispose(); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Locks/SqlDistributedLockScope.cs b/src/IdentityServer/ClassifiedAds.Persistence/Locks/SqlDistributedLockScope.cs new file mode 100644 index 000000000..632de23ee --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Locks/SqlDistributedLockScope.cs @@ -0,0 +1,109 @@ +using ClassifiedAds.CrossCuttingConcerns.Locks; +using Microsoft.Data.SqlClient; +using System; +using System.Data; + +namespace ClassifiedAds.Persistence.Locks +{ + public class SqlDistributedLockScope : IDistributedLockScope + { + private readonly SqlConnection _connection; + private readonly SqlTransaction _transaction; + private readonly string _lockName; + + public bool HasTransaction + { + get + { + return _transaction != null; + } + } + + public SqlDistributedLockScope(SqlConnection connection, SqlTransaction transaction, string lockName) + { + _connection = connection; + _transaction = transaction; + _lockName = lockName; + } + + public void Dispose() + { + if (HasTransaction) + { + return; + } + + SqlParameter returnValue; + var releaseCommand = CreateReleaseCommand(_lockName, false, out returnValue); + releaseCommand.ExecuteNonQuery(); + + if (ParseReturnCode((int)returnValue.Value)) + { + } + } + + public bool StillHoldingLock() + { + var command = _connection.CreateCommand(); + command.Transaction = _transaction; + + command.CommandText = @"SELECT APPLOCK_MODE('public', @Resource, @LockOwner)"; + command.Parameters.Add(new SqlParameter("Resource", _lockName)); + command.Parameters.Add(new SqlParameter("LockOwner", HasTransaction ? "Transaction" : "Session")); + var lockMode = (string)command.ExecuteScalar(); + + return lockMode != "NoLock"; + } + + private SqlCommand CreateReleaseCommand(string lockName, bool isTry, out SqlParameter returnValue) + { + var command = _connection.CreateCommand(); + command.Transaction = _transaction; + + if (isTry) + { + command.CommandText = + @"IF APPLOCK_MODE('public', @Resource, @LockOwner) != 'NoLock' + EXEC @Result = dbo.sp_releaseapplock @Resource, @LockOwner + ELSE + SET @Result = 0" + ; + } + else + { + command.CommandText = "dbo.sp_releaseapplock"; + command.CommandType = CommandType.StoredProcedure; + } + + command.Parameters.Add(new SqlParameter("Resource", lockName)); + command.Parameters.Add(new SqlParameter("LockOwner", HasTransaction ? "Transaction" : "Session")); + + if (isTry) + { + returnValue = command.Parameters.Add(new SqlParameter { ParameterName = "Result", DbType = DbType.Int32, Direction = ParameterDirection.Output }); + } + else + { + returnValue = command.Parameters.Add(new SqlParameter { DbType = DbType.Int32, Direction = ParameterDirection.ReturnValue }); + } + + return command; + } + + /// + /// sp_releaseapplock exit codes documented at + /// https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-releaseapplock-transact-sql#return-code-values + /// + /// code returned after calling sp_releaseapplock + /// true/false + public static bool ParseReturnCode(int returnCode) + { + if (returnCode >= 0) + { + return true; + } + + throw new InvalidOperationException($"Could not release lock with return code: {returnCode}"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/AuditLogEntryConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/AuditLogEntryConfiguration.cs new file mode 100644 index 000000000..d33fc562f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/AuditLogEntryConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class AuditLogEntryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("AuditLogEntries"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CircuitBreakerConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CircuitBreakerConfiguration.cs new file mode 100644 index 000000000..5fe4e1aa5 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CircuitBreakerConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Persistence.CircuitBreakers; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class CircuitBreakerConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("CircuitBreakers"); + builder.HasIndex(x => new { x.Name }).IsUnique(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CircuitBreakerLogConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CircuitBreakerLogConfiguration.cs new file mode 100644 index 000000000..ed51af968 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CircuitBreakerLogConfiguration.cs @@ -0,0 +1,14 @@ +using ClassifiedAds.Persistence.CircuitBreakers; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class CircuitBreakerLogConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("CircuitBreakerLogs"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/ConfigurationEntryConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/ConfigurationEntryConfiguration.cs new file mode 100644 index 000000000..6466acc3f --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/ConfigurationEntryConfiguration.cs @@ -0,0 +1,28 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class ConfigurationEntryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("ConfigurationEntries"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + + // Seed + builder.HasData(new List + { + new ConfigurationEntry + { + Id = Guid.Parse("8A051AA5-BCD1-EA11-B098-AC728981BD15"), + Key = "SecurityHeaders:Test-Read-From-SqlServer", + Value = "this-is-read-from-sqlserver", + }, + }); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CustomMigrationHistoryConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CustomMigrationHistoryConfiguration.cs new file mode 100644 index 000000000..6cc893f33 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/CustomMigrationHistoryConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class CustomMigrationHistoryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("_CustomMigrationHistories"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/EmailMessageAttachmentConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/EmailMessageAttachmentConfiguration.cs new file mode 100644 index 000000000..8956f1ad4 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/EmailMessageAttachmentConfiguration.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class EmailMessageAttachmentConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("EmailMessageAttachments"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/EmailMessageConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/EmailMessageConfiguration.cs new file mode 100644 index 000000000..fb905bba9 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/EmailMessageConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class EmailMessageConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("EmailMessages"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/FileEntryConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/FileEntryConfiguration.cs new file mode 100644 index 000000000..856ccb51b --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/FileEntryConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class FileEntryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("FileEntries"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/LocalizationEntryConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/LocalizationEntryConfiguration.cs new file mode 100644 index 000000000..c2b759a19 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/LocalizationEntryConfiguration.cs @@ -0,0 +1,36 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class LocalizationEntryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("LocalizationEntries"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + + // Seed + builder.HasData(new List + { + new LocalizationEntry + { + Id = Guid.Parse("29A4AACB-4DDF-4F85-ACED-C5283A8BDD7F"), + Name = "Test", + Value = "Test", + Culture = "en-US", + }, + new LocalizationEntry + { + Id = Guid.Parse("5A262D8A-B0D9-45D3-8C0E-18B2C882B9FE"), + Name = "Test", + Value = "Kiem Tra", + Culture = "vi-VN", + }, + }); + } + } +} \ No newline at end of file diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/LockConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/LockConfiguration.cs new file mode 100644 index 000000000..bb3dc8894 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/LockConfiguration.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class LockConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Locks"); + builder.HasKey(x => new { x.EntityId, x.EntityName }); + builder.HasIndex(x => new { x.OwnerId }); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/OutboxEventConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/OutboxEventConfiguration.cs new file mode 100644 index 000000000..352157e1a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/OutboxEventConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class OutboxEventConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("OutboxEvents"); + builder.Property(x => x.Id).UseIdentityColumn(); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/PasswordHistoryConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/PasswordHistoryConfiguration.cs new file mode 100644 index 000000000..712067f75 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/PasswordHistoryConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class PasswordHistoryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("PasswordHistories"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/ProductConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/ProductConfiguration.cs new file mode 100644 index 000000000..316766fbf --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/ProductConfiguration.cs @@ -0,0 +1,36 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class ProductConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Products"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + + // Seed + builder.HasData(new List + { + new Product + { + Id = Guid.Parse("6672E891-0D94-4620-B38A-DBC5B02DA9F7"), + Code = "TEST", + Name = "Test", + Description = "Description" + }, + new Product + { + Id = Guid.Parse("CC9D7ECA-6428-4E6D-B40B-2C8D93AB7347"), + Code = "PD001", + Name = "Iphone X", + Description = "Iphone X" + } + }); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs new file mode 100644 index 000000000..b59e5456a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/RoleClaimConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class RoleClaimConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("RoleClaims"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs new file mode 100644 index 000000000..57d5e6351 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/RoleConfiguration.cs @@ -0,0 +1,23 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class RoleConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Roles"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + + builder.HasMany(x => x.Claims) + .WithOne(x => x.Role) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(x => x.UserRoles) + .WithOne(x => x.Role) + .OnDelete(DeleteBehavior.Cascade); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/SmsMessageConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/SmsMessageConfiguration.cs new file mode 100644 index 000000000..dd878d3b8 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/SmsMessageConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class SmsMessageConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("SmsMessages"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs new file mode 100644 index 000000000..e54c53c82 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserClaimConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class UserClaimConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("UserClaims"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs new file mode 100644 index 000000000..bc37e3700 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserConfiguration.cs @@ -0,0 +1,41 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class UserConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Users"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + + builder.HasMany(x => x.Claims) + .WithOne(x => x.User) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasMany(x => x.UserRoles) + .WithOne(x => x.User) + .OnDelete(DeleteBehavior.Cascade); + + // Seed + builder.HasData(new List + { + new User + { + Id = Guid.Parse("12837D3D-793F-EA11-BECB-5CEA1D05F660"), + UserName = "phong@gmail.com", + NormalizedUserName = "PHONG@GMAIL.COM", + Email = "phong@gmail.com", + NormalizedEmail = "PHONG@GMAIL.COM", + PasswordHash = "AQAAAAEAACcQAAAAELBcKuXWkiRQEYAkD/qKs9neac5hxWs3bkegIHpGLtf+zFHuKnuI3lBqkWO9TMmFAQ==", // v*7Un8b4rcN@<-RN + SecurityStamp = "5M2QLL65J6H6VFIS7VZETKXY27KNVVYJ", + LockoutEnabled = true, + }, + }); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs new file mode 100644 index 000000000..d34427b65 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserRoleConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class UserRoleConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("UserRoles"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserTokenConfiguration.cs b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserTokenConfiguration.cs new file mode 100644 index 000000000..aa2284351 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/MappingConfigurations/UserTokenConfiguration.cs @@ -0,0 +1,15 @@ +using ClassifiedAds.Domain.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ClassifiedAds.Persistence.MappingConfigurations +{ + public class UserTokenConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("UserTokens"); + builder.Property(x => x.Id).HasDefaultValueSql("newsequentialid()"); + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/PersistenceExtensions.cs b/src/IdentityServer/ClassifiedAds.Persistence/PersistenceExtensions.cs new file mode 100644 index 000000000..439eb8429 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/PersistenceExtensions.cs @@ -0,0 +1,82 @@ +using ClassifiedAds.CrossCuttingConcerns.CircuitBreakers; +using ClassifiedAds.CrossCuttingConcerns.Locks; +using ClassifiedAds.CrossCuttingConcerns.Tenants; +using ClassifiedAds.Domain.Repositories; +using ClassifiedAds.Persistence; +using ClassifiedAds.Persistence.CircuitBreakers; +using ClassifiedAds.Persistence.Locks; +using ClassifiedAds.Persistence.Repositories; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using System; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class PersistenceExtensions + { + public static IServiceCollection AddPersistence(this IServiceCollection services, string connectionString, string migrationsAssembly = "") + { + services.AddDbContext(options => options.UseSqlServer(connectionString, sql => + { + if (!string.IsNullOrEmpty(migrationsAssembly)) + { + sql.MigrationsAssembly(migrationsAssembly); + } + })) + .AddDbContextFactory((Action)null, ServiceLifetime.Scoped) + .AddRepositories(); + + services.AddScoped(typeof(IDistributedLock), _ => new SqlDistributedLock(connectionString)); + + return services; + } + + public static IServiceCollection AddMultiTenantPersistence(this IServiceCollection services, Type connectionStringResolverType, Type tenantResolverType) + { + services.AddScoped(typeof(IConnectionStringResolver), connectionStringResolverType); + services.AddScoped(typeof(ITenantResolver), tenantResolverType); + + services.AddDbContext(options => { }) + .AddScoped(typeof(AdsDbContext), services => + { + return services.GetRequiredService(); + }) + .AddRepositories(); + + services.AddScoped(typeof(IDistributedLock), services => + { + return new SqlDistributedLock(services.GetRequiredService>().ConnectionString); + }); + + return services; + } + + private static IServiceCollection AddRepositories(this IServiceCollection services) + { + services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>)) + .AddScoped(typeof(IAuditLogEntryRepository), typeof(AuditLogEntryRepository)) + .AddScoped(typeof(IEmailMessageRepository), typeof(EmailMessageRepository)) + .AddScoped(typeof(ISmsMessageRepository), typeof(SmsMessageRepository)) + .AddScoped(typeof(IUserRepository), typeof(UserRepository)) + .AddScoped(typeof(IRoleRepository), typeof(RoleRepository)); + + services.AddScoped(typeof(IUnitOfWork), services => + { + return services.GetRequiredService(); + }); + + services.AddScoped(); + services.AddScoped(); + + return services; + } + + public static void MigrateAdsDb(this IApplicationBuilder app) + { + using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) + { + serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); + } + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Repositories/AuditLogEntryRepository.cs b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/AuditLogEntryRepository.cs new file mode 100644 index 000000000..fd121c843 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/AuditLogEntryRepository.cs @@ -0,0 +1,39 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class AuditLogEntryRepository : Repository, IAuditLogEntryRepository + { + public AuditLogEntryRepository(AdsDbContext dbContext, IDateTimeProvider dateTimeProvider) + : base(dbContext, dateTimeProvider) + { + } + + public IQueryable Get(AuditLogEntryQueryOptions queryOptions) + { + var query = GetAll(); + + if (queryOptions.UserId != Guid.Empty) + { + query = query.Where(x => x.UserId == queryOptions.UserId); + } + + if (!string.IsNullOrEmpty(queryOptions.ObjectId)) + { + query = query.Where(x => x.ObjectId == queryOptions.ObjectId); + } + + if (queryOptions.AsNoTracking) + { + query = query.AsNoTracking(); + } + + return query; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Repositories/EmailMessageRepository.cs b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/EmailMessageRepository.cs new file mode 100644 index 000000000..5c0b9bbe2 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/EmailMessageRepository.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class EmailMessageRepository : Repository, IEmailMessageRepository + { + public EmailMessageRepository(AdsDbContext dbContext, + IDateTimeProvider dateTimeProvider) + : base(dbContext, dateTimeProvider) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Repositories/Repository.cs b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/Repository.cs new file mode 100644 index 000000000..79406938a --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/Repository.cs @@ -0,0 +1,124 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using EntityFrameworkCore.SqlServer.SimpleBulks.BulkDelete; +using EntityFrameworkCore.SqlServer.SimpleBulks.BulkInsert; +using EntityFrameworkCore.SqlServer.SimpleBulks.BulkMerge; +using EntityFrameworkCore.SqlServer.SimpleBulks.BulkUpdate; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class Repository : IRepository + where T : Entity, IAggregateRoot + { + private readonly AdsDbContext _dbContext; + private readonly IDateTimeProvider _dateTimeProvider; + + protected DbSet DbSet => _dbContext.Set(); + + public IUnitOfWork UnitOfWork + { + get + { + return _dbContext; + } + } + + public Repository(AdsDbContext dbContext, IDateTimeProvider dateTimeProvider) + { + _dbContext = dbContext; + _dateTimeProvider = dateTimeProvider; + } + + public async Task AddOrUpdateAsync(T entity, CancellationToken cancellationToken = default) + { + if (entity.Id.Equals(default(TKey))) + { + await AddAsync(entity, cancellationToken); + } + else + { + await UpdateAsync(entity, cancellationToken); + } + } + + public async Task AddAsync(T entity, CancellationToken cancellationToken = default) + { + entity.CreatedDateTime = _dateTimeProvider.OffsetNow; + await DbSet.AddAsync(entity, cancellationToken); + } + + public Task UpdateAsync(T entity, CancellationToken cancellationToken = default) + { + entity.UpdatedDateTime = _dateTimeProvider.OffsetNow; + return Task.CompletedTask; + } + + public void Delete(T entity) + { + DbSet.Remove(entity); + } + + public IQueryable GetAll() + { + return _dbContext.Set(); + } + + public Task FirstOrDefaultAsync(IQueryable query) + { + return query.FirstOrDefaultAsync(); + } + + public Task SingleOrDefaultAsync(IQueryable query) + { + return query.SingleOrDefaultAsync(); + } + + public Task> ToListAsync(IQueryable query) + { + return query.ToListAsync(); + } + + public void BulkInsert(IEnumerable entities) + { + _dbContext.BulkInsert(entities); + } + + public void BulkInsert(IEnumerable entities, Expression> columnNamesSelector) + { + _dbContext.BulkInsert(entities, columnNamesSelector); + } + + public void BulkUpdate(IEnumerable entities, Expression> columnNamesSelector) + { + _dbContext.BulkUpdate(entities, columnNamesSelector); + } + + public void BulkDelete(IEnumerable entities) + { + _dbContext.BulkDelete(entities); + } + + public void BulkMerge(IEnumerable entities, Expression> idSelector, Expression> updateColumnNamesSelector, Expression> insertColumnNamesSelector) + { + _dbContext.BulkMerge(entities, idSelector, updateColumnNamesSelector, insertColumnNamesSelector); + } + + public void SetRowVersion(T entity, byte[] version) + { + _dbContext.Entry(entity).OriginalValues[nameof(entity.RowVersion)] = version; + } + + public bool IsDbUpdateConcurrencyException(Exception ex) + { + return ex is DbUpdateConcurrencyException; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Repositories/RoleRepository.cs b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/RoleRepository.cs new file mode 100644 index 000000000..fd1c40136 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/RoleRepository.cs @@ -0,0 +1,44 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class RoleRepository : Repository, IRoleRepository + { + public RoleRepository(AdsDbContext dbContext, IDateTimeProvider dateTimeProvider) + : base(dbContext, dateTimeProvider) + { + } + + public IQueryable Get(RoleQueryOptions queryOptions) + { + var query = GetAll(); + + if (queryOptions.IncludeClaims) + { + query = query.Include(x => x.Claims); + } + + if (queryOptions.IncludeUserRoles) + { + query = query.Include(x => x.UserRoles); + } + + if (queryOptions.IncludeUsers) + { + query = query.Include("UserRoles.User"); + } + + if (queryOptions.AsNoTracking) + { + query = query = query.AsNoTracking(); + } + + return query; + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Repositories/SmsMessageRepository.cs b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/SmsMessageRepository.cs new file mode 100644 index 000000000..a49a53943 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/SmsMessageRepository.cs @@ -0,0 +1,16 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using System; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class SmsMessageRepository : Repository, ISmsMessageRepository + { + public SmsMessageRepository(AdsDbContext dbContext, + IDateTimeProvider dateTimeProvider) + : base(dbContext, dateTimeProvider) + { + } + } +} diff --git a/src/IdentityServer/ClassifiedAds.Persistence/Repositories/UserRepository.cs b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/UserRepository.cs new file mode 100644 index 000000000..95d522c33 --- /dev/null +++ b/src/IdentityServer/ClassifiedAds.Persistence/Repositories/UserRepository.cs @@ -0,0 +1,54 @@ +using ClassifiedAds.CrossCuttingConcerns.OS; +using ClassifiedAds.Domain.Entities; +using ClassifiedAds.Domain.Repositories; +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; + +namespace ClassifiedAds.Persistence.Repositories +{ + public class UserRepository : Repository, IUserRepository + { + public UserRepository(AdsDbContext dbContext, IDateTimeProvider dateTimeProvider) + : base(dbContext, dateTimeProvider) + { + } + + public IQueryable Get(UserQueryOptions queryOptions) + { + var query = GetAll(); + + if (queryOptions.IncludePasswordHistories) + { + query = query.Include(x => x.PasswordHistories); + } + + if (queryOptions.IncludeTokens) + { + query = query.Include(x => x.Tokens); + } + + if (queryOptions.IncludeClaims) + { + query = query.Include(x => x.Claims); + } + + if (queryOptions.IncludeUserRoles) + { + query = query.Include(x => x.UserRoles); + } + + if (queryOptions.IncludeRoles) + { + query = query.Include("UserRoles.Role"); + } + + if (queryOptions.AsNoTracking) + { + query = query = query.AsNoTracking(); + } + + return query; + } + } +} diff --git a/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x64.dll b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x64.dll new file mode 100644 index 000000000..98a007bbc Binary files /dev/null and b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x64.dll differ diff --git a/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x64.so b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x64.so new file mode 100644 index 000000000..eecc8834a Binary files /dev/null and b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x64.so differ diff --git a/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x86.dll b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x86.dll new file mode 100644 index 000000000..c5e529e21 Binary files /dev/null and b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x86.dll differ diff --git a/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x86.so b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x86.so new file mode 100644 index 000000000..802f1dfee Binary files /dev/null and b/src/IdentityServer/libs/libwkhtmltox/libwkhtmltox-0.12.4-x86.so differ diff --git a/src/Microservices/ClassifiedAds.Microservices.sln b/src/Microservices/ClassifiedAds.Microservices.sln index aafdbfea9..d00d13c9a 100644 --- a/src/Microservices/ClassifiedAds.Microservices.sln +++ b/src/Microservices/ClassifiedAds.Microservices.sln @@ -49,19 +49,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.Noti EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.Identity.IntegrationTests", "Services.Identity\ClassifiedAds.Services.Identity.IntegrationTests\ClassifiedAds.Services.Identity.IntegrationTests.csproj", "{50B4F734-D7E2-4846-9E68-31CCBE678AF3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.Identity.AuthServer", "Services.Identity\ClassifiedAds.Services.Identity.AuthServer\ClassifiedAds.Services.Identity.AuthServer.csproj", "{13C9FFD9-FF67-4B0C-84CA-E61118E39C16}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Gateways.WebAPI", "Gateways.WebAPI\ClassifiedAds.Gateways.WebAPI\ClassifiedAds.Gateways.WebAPI.csproj", "{1760E9D6-C483-433F-A829-6C1CAFFAC0F0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration", "Configuration", "{12076161-F47C-47E6-849A-92377D2F2923}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.Configuration.Api", "Services.Configuration\ClassifiedAds.Services.Configuration.Api\ClassifiedAds.Services.Configuration.Api.csproj", "{06E1983D-E90A-4853-82BE-C9557506A7ED}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassifiedAds.Services.AuditLog", "Services.AuditLog\ClassifiedAds.Services.AuditLog\ClassifiedAds.Services.AuditLog.csproj", "{47340F4E-FE50-48F9-ACF8-140EDF71DBAB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.AuditLog", "Services.AuditLog\ClassifiedAds.Services.AuditLog\ClassifiedAds.Services.AuditLog.csproj", "{47340F4E-FE50-48F9-ACF8-140EDF71DBAB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassifiedAds.Services.Identity", "Services.Identity\ClassifiedAds.Services.Identity\ClassifiedAds.Services.Identity.csproj", "{51029D56-04FE-46C8-B176-0C65B6980518}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.Identity", "Services.Identity\ClassifiedAds.Services.Identity\ClassifiedAds.Services.Identity.csproj", "{51029D56-04FE-46C8-B176-0C65B6980518}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassifiedAds.Services.Notification", "Services.Notification\ClassifiedAds.Services.Notification\ClassifiedAds.Services.Notification.csproj", "{F09154ED-2C3B-4316-834A-E8207AFDDD6D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassifiedAds.Services.Notification", "Services.Notification\ClassifiedAds.Services.Notification\ClassifiedAds.Services.Notification.csproj", "{F09154ED-2C3B-4316-834A-E8207AFDDD6D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -129,10 +127,6 @@ Global {50B4F734-D7E2-4846-9E68-31CCBE678AF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {50B4F734-D7E2-4846-9E68-31CCBE678AF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {50B4F734-D7E2-4846-9E68-31CCBE678AF3}.Release|Any CPU.Build.0 = Release|Any CPU - {13C9FFD9-FF67-4B0C-84CA-E61118E39C16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13C9FFD9-FF67-4B0C-84CA-E61118E39C16}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13C9FFD9-FF67-4B0C-84CA-E61118E39C16}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13C9FFD9-FF67-4B0C-84CA-E61118E39C16}.Release|Any CPU.Build.0 = Release|Any CPU {1760E9D6-C483-433F-A829-6C1CAFFAC0F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1760E9D6-C483-433F-A829-6C1CAFFAC0F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {1760E9D6-C483-433F-A829-6C1CAFFAC0F0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -178,7 +172,6 @@ Global {89BB2B6E-7A83-4DBA-87A7-FD47E09C0779} = {5EF4FB70-0925-4193-AE9D-FDF8D1DCA82E} {62C1D830-7E6C-4A00-993E-59DA08420122} = {5EF4FB70-0925-4193-AE9D-FDF8D1DCA82E} {50B4F734-D7E2-4846-9E68-31CCBE678AF3} = {511C62E1-D820-4032-8CEB-5558B94D5833} - {13C9FFD9-FF67-4B0C-84CA-E61118E39C16} = {511C62E1-D820-4032-8CEB-5558B94D5833} {1760E9D6-C483-433F-A829-6C1CAFFAC0F0} = {7C1260FA-4009-489B-82CE-D8CA5FE7D7D9} {12076161-F47C-47E6-849A-92377D2F2923} = {1E828A01-F4C9-4D88-8B21-559A889B6F42} {06E1983D-E90A-4853-82BE-C9557506A7ED} = {12076161-F47C-47E6-849A-92377D2F2923} diff --git a/src/Microservices/Jenkinsfile b/src/Microservices/Jenkinsfile index 87983056e..ac98c7742 100644 --- a/src/Microservices/Jenkinsfile +++ b/src/Microservices/Jenkinsfile @@ -28,7 +28,6 @@ pipeline { sh "dotnet publish -p:Version=${VERSION} Services.AuditLog/ClassifiedAds.Services.AuditLog.Grpc/*.csproj --configuration Release" sh "dotnet publish -p:Version=${VERSION} Services.Configuration/ClassifiedAds.Services.Configuration.Api/*.csproj --configuration Release" sh "dotnet publish -p:Version=${VERSION} Services.Identity/ClassifiedAds.Services.Identity.Api/*.csproj --configuration Release" - sh "dotnet publish -p:Version=${VERSION} Services.Identity/ClassifiedAds.Services.Identity.AuthServer/*.csproj --configuration Release" sh "dotnet publish -p:Version=${VERSION} Services.Identity/ClassifiedAds.Services.Identity.Grpc/*.csproj --configuration Release" sh "dotnet publish -p:Version=${VERSION} Services.Notification/ClassifiedAds.Services.Notification.Api/*.csproj --configuration Release" sh "dotnet publish -p:Version=${VERSION} Services.Notification/ClassifiedAds.Services.Notification.Background/*.csproj --configuration Release" diff --git a/src/Microservices/README.md b/src/Microservices/README.md index b7805234f..3f3e45fb1 100644 --- a/src/Microservices/README.md +++ b/src/Microservices/README.md @@ -11,8 +11,8 @@ | Services.AuditLog.Grpc | [appsettings.json](Services.AuditLog/ClassifiedAds.Services.AuditLog.Grpc/appsettings.json) | ConnectionStrings:ClassifiedAds | AuditLog | Services.Configuration.Api | [appsettings.json](Services.Configuration/ClassifiedAds.Services.Configuration.Api/appsettings.json) | ConnectionStrings:ClassifiedAds | Configuration | Services.Identity.Api | [appsettings.json](Services.Identity/ClassifiedAds.Services.Identity.Api/appsettings.json) | ConnectionStrings:ClassifiedAds | Identity - | Services.Identity.AuthServer | [appsettings.json](Services.Identity/ClassifiedAds.Services.Identity.AuthServer/appsettings.json) | ConnectionStrings:ClassifiedAds | Identity | Services.Identity.Grpc | [appsettings.json](Services.Identity/ClassifiedAds.Services.Identity.Grpc/appsettings.json) | ConnectionStrings:ClassifiedAds | Identity + | ClassifiedAds.IdentityServer | [appsettings.json](../IdentityServer/ClassifiedAds.IdentityServer/appsettings.json) | ConnectionStrings:ClassifiedAds | Identity | Services.Notification.Api | [appsettings.json](Services.Notification/ClassifiedAds.Services.Notification.Api/appsettings.json) | ConnectionStrings:ClassifiedAds | Notification | Services.Notification.Background | [appsettings.json](Services.Notification/ClassifiedAds.Services.Notification.Background/appsettings.json) | ConnectionStrings:ClassifiedAds | Notification | Services.Notification.Grpc | [appsettings.json](Services.Notification/ClassifiedAds.Services.Notification.Grpc/appsettings.json) | ConnectionStrings:ClassifiedAds | Notification @@ -41,7 +41,7 @@ dotnet ef migrations add Init --context IdentityDbContext -o Migrations/IdentityDb dotnet ef database update --context IdentityDbContext ``` - + Navigate to [Identity.AuthServer](Services.Identity/ClassifiedAds.Services.Identity.AuthServer/) and run these commands: + + Navigate to [ClassifiedAds.IdentityServer](../IdentityServer/ClassifiedAds.Migrator/) and run these commands: ``` dotnet ef migrations add Init --context ConfigurationDbContext -o Migrations/ConfigurationDb dotnet ef migrations add Init --context PersistedGrantDbContext -o Migrations/PersistedGrantDb @@ -80,7 +80,7 @@ Add-Migration -Context IdentityDbContext Init -OutputDir Migrations/IdentityDb Update-Database -Context IdentityDbContext ``` - + Select **Identity.AuthServer** as Default Project and run these commands: + + Select **../IdentityServer/ClassifiedAds.Migrator** as Default Project and run these commands: ``` Add-Migration -Context ConfigurationDbContext Init -OutputDir Migrations/ConfigurationDb Add-Migration -Context PersistedGrantDbContext Init -OutputDir Migrations/PersistedGrantDb @@ -219,12 +219,12 @@ dotnet build -p:Version=1.0.0.1 -c Release + dotnet publish -p:Version=1.0.0.1 -c Release ../IdentityServer/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj -o ./publish/ClassifiedAds.IdentityServer dotnet publish -p:Version=1.0.0.1 -c Release ./Gateways.WebAPI/ClassifiedAds.Gateways.WebAPI/ClassifiedAds.Gateways.WebAPI.csproj -o ./publish/ClassifiedAds.Gateways.WebAPI dotnet publish -p:Version=1.0.0.1 -c Release ./Services.AuditLog/ClassifiedAds.Services.AuditLog.Api/ClassifiedAds.Services.AuditLog.Api.csproj -o ./publish/ClassifiedAds.Services.AuditLog.Api dotnet publish -p:Version=1.0.0.1 -c Release ./Services.AuditLog/ClassifiedAds.Services.AuditLog.Grpc/ClassifiedAds.Services.AuditLog.Grpc.csproj -o ./publish/ClassifiedAds.Services.AuditLog.Grpc dotnet publish -p:Version=1.0.0.1 -c Release ./Services.Configuration/ClassifiedAds.Services.Configuration.Api/ClassifiedAds.Services.Configuration.Api.csproj -o ./publish/ClassifiedAds.Services.Configuration.Api dotnet publish -p:Version=1.0.0.1 -c Release ./Services.Identity/ClassifiedAds.Services.Identity.Api/ClassifiedAds.Services.Identity.Api.csproj -o ./publish/ClassifiedAds.Services.Identity.Api - dotnet publish -p:Version=1.0.0.1 -c Release ./Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ClassifiedAds.Services.Identity.AuthServer.csproj -o ./publish/ClassifiedAds.Services.Identity.AuthServer dotnet publish -p:Version=1.0.0.1 -c Release ./Services.Identity/ClassifiedAds.Services.Identity.Grpc/ClassifiedAds.Services.Identity.Grpc.csproj -o ./publish/ClassifiedAds.Services.Identity.Grpc dotnet publish -p:Version=1.0.0.1 -c Release ./Services.Notification/ClassifiedAds.Services.Notification.Api/ClassifiedAds.Services.Notification.Api.csproj -o ./publish/ClassifiedAds.Services.Notification.Api dotnet publish -p:Version=1.0.0.1 -c Release ./Services.Notification/ClassifiedAds.Services.Notification.Background/ClassifiedAds.Services.Notification.Background.csproj -o ./publish/ClassifiedAds.Services.Notification.Background @@ -235,12 +235,12 @@ - Pack ``` + dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.IdentityServer --basePath=./publish/ClassifiedAds.IdentityServer dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Gateways.WebAPI --basePath=./publish/ClassifiedAds.Gateways.WebAPI dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.AuditLog.Api --basePath=./publish/ClassifiedAds.Services.AuditLog.Api dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.AuditLog.Grpc --basePath=./publish/ClassifiedAds.Services.AuditLog.Grpc dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.Configuration.Api --basePath=./publish/ClassifiedAds.Services.Configuration.Api dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.Identity.Api --basePath=./publish/ClassifiedAds.Services.Identity.Api - dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.Identity.AuthServer --basePath=./publish/ClassifiedAds.Services.Identity.AuthServer dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.Identity.Grpc --basePath=./publish/ClassifiedAds.Services.Identity.Grpc dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.Notification.Api --basePath=./publish/ClassifiedAds.Services.Notification.Api dotnet octo pack --version=1.0.0.1 --outFolder=./publish --overwrite --id=ClassifiedAds.Services.Notification.Background --basePath=./publish/ClassifiedAds.Services.Notification.Background diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/.editorconfig b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/.editorconfig deleted file mode 100644 index 46bed8199..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/.editorconfig +++ /dev/null @@ -1,218 +0,0 @@ -# To learn more about .editorconfig see https://aka.ms/editorconfigdocs -############################### -# Core EditorConfig Options # -############################### -# All files -[*] -indent_style = space - -# XML project files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] -indent_size = 2 - -# XML config files -[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] -indent_size = 2 - -# Code files -[*.{cs,csx,vb,vbx}] -indent_size = 4 -insert_final_newline = true -charset = utf-8-bom -############################### -# .NET Coding Conventions # -############################### -[*.{cs,vb}] -# Organize usings -dotnet_sort_system_directives_first = true -# this. preferences -dotnet_style_qualification_for_field = false:silent -dotnet_style_qualification_for_property = false:silent -dotnet_style_qualification_for_method = false:silent -dotnet_style_qualification_for_event = false:silent -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:silent -dotnet_style_predefined_type_for_member_access = true:silent -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -# Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_style_readonly_field = true:suggestion -# Expression-level preferences -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent -############################### -# Naming Conventions # -############################### -# Style Definitions -dotnet_naming_style.pascal_case_style.capitalization = pascal_case -# Use PascalCase for constant fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style -dotnet_naming_symbols.constant_fields.applicable_kinds = field -dotnet_naming_symbols.constant_fields.applicable_accessibilities = * -dotnet_naming_symbols.constant_fields.required_modifiers = const -############################### -# C# Coding Conventions # -############################### -[*.cs] -# var preferences -csharp_style_var_for_built_in_types = true:silent -csharp_style_var_when_type_is_apparent = true:silent -csharp_style_var_elsewhere = true:silent -# Expression-bodied members -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:silent -# Pattern matching preferences -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -# Null-checking preferences -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion -# Modifier preferences -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion -# Expression-level preferences -csharp_prefer_braces = true:silent -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -############################### -# C# Formatting Rules # -############################### -# New line preferences -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_between_query_expression_clauses = true -# Indentation preferences -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -csharp_indent_labels = flush_left -# Space preferences -csharp_space_after_cast = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -# Wrapping preferences -csharp_preserve_single_line_statements = true -csharp_preserve_single_line_blocks = true -############################### -# VB Coding Conventions # -############################### -[*.vb] -# Modifier preferences -visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion - -############################### -# StyleCop # -############################### -[*.cs] - -# SA1027: Use tabs correctly -dotnet_diagnostic.SA1027.severity = none - -# SA1101: Prefix local calls with this -dotnet_diagnostic.SA1101.severity = none - -# SA1108: Block statements should not contain embedded comments -dotnet_diagnostic.SA1108.severity = none - -# SA1111: Closing parenthesis should be on line of last parameter -dotnet_diagnostic.SA1111.severity = none - -# SA1113: Comma should be on the same line as previous parameter -dotnet_diagnostic.SA1113.severity = none - -# SA1115: Parameter should follow comma -dotnet_diagnostic.SA1115.severity = none - -# SA1116: Split parameters should start on line after declaration -dotnet_diagnostic.SA1116.severity = none - -# SA1117: Parameters should be on same line or separate lines -dotnet_diagnostic.SA1117.severity = none - -# SA1123: Do not place regions within elements -dotnet_diagnostic.SA1123.severity = none - -# SA1124: Do not use regions -dotnet_diagnostic.SA1124.severity = none - -# SA1200: Using directives should be placed correctly -dotnet_diagnostic.SA1200.severity = none - -# SA1201: Elements should appear in the correct order -dotnet_diagnostic.SA1201.severity = none - -# SA1202: Elements should be ordered by access -dotnet_diagnostic.SA1202.severity = none - -# SA1203: Constants should appear before fields -dotnet_diagnostic.SA1203.severity = none - -# SA1204: Static elements should appear before instance elements -dotnet_diagnostic.SA1204.severity = none - -# SA1208: System using directives should be placed before other using directives -dotnet_diagnostic.SA1208.severity = none - -# SA1309: Field names should not begin with underscore -dotnet_diagnostic.SA1309.severity = none - -# SA1310: Field names should not contain underscore -dotnet_diagnostic.SA1310.severity = none - -# SA1402: File may only contain a single type -dotnet_diagnostic.SA1402.severity = none - -# SA1516: Elements should be separated by blank line -dotnet_diagnostic.SA1516.severity = none - -# SA1600: Elements should be documented -dotnet_diagnostic.SA1600.severity = none - -# SA1601: Partial elements should be documented -dotnet_diagnostic.SA1601.severity = none - -# SA1602: Enumeration items should be documented -dotnet_diagnostic.SA1602.severity = none - -# SA1610: Property documentation should have value text -dotnet_diagnostic.SA1610.severity = none - -# SA1623: Property summary documentation should match accessors -dotnet_diagnostic.SA1623.severity = none - -# SA1629: Documentation text should end with a period -dotnet_diagnostic.SA1629.severity = none - -# SA1633: File should have header -dotnet_diagnostic.SA1633.severity = none \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs deleted file mode 100644 index 00a70e8ef..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ConfirmEmailModel : PageModel - { - private readonly UserManager _userManager; - - public ConfirmEmailModel(UserManager userManager) - { - _userManager = userManager; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync(string userId, string code) - { - if (userId == null || code == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByIdAsync(userId); - if (user == null) - { - return NotFound($"Unable to load user with ID '{userId}'."); - } - - code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); - var result = await _userManager.ConfirmEmailAsync(user, code); - StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email."; - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs deleted file mode 100644 index 141733a00..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ConfirmEmailChangeModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public ConfirmEmailChangeModel(UserManager userManager, SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync(string userId, string email, string code) - { - if (userId == null || email == null || code == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByIdAsync(userId); - if (user == null) - { - return NotFound($"Unable to load user with ID '{userId}'."); - } - - code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); - var result = await _userManager.ChangeEmailAsync(user, email, code); - if (!result.Succeeded) - { - StatusMessage = "Error changing email."; - return Page(); - } - - // In our UI email and user name are one and the same, so when we update the email - // we need to update the user name. - var setUserNameResult = await _userManager.SetUserNameAsync(user, email); - if (!setUserNameResult.Succeeded) - { - StatusMessage = "Error changing user name."; - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Thank you for confirming your email change."; - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs deleted file mode 100644 index 5c96152b4..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Security.Claims; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ExternalLoginModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - private readonly ILogger _logger; - - public ExternalLoginModel( - SignInManager signInManager, - UserManager userManager, - ILogger logger, - IEmailSender emailSender) - { - _signInManager = signInManager; - _userManager = userManager; - _logger = logger; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ProviderDisplayName { get; set; } - - public string ReturnUrl { get; set; } - - [TempData] - public string ErrorMessage { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public IActionResult OnGetAsync() - { - return RedirectToPage("./Login"); - } - - public IActionResult OnPost(string provider, string returnUrl = null) - { - // Request a redirect to the external login provider. - var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); - return new ChallengeResult(provider, properties); - } - - public async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) - { - returnUrl = returnUrl ?? Url.Content("~/"); - if (remoteError != null) - { - ErrorMessage = $"Error from external provider: {remoteError}"; - return RedirectToPage("./Login", new {ReturnUrl = returnUrl }); - } - var info = await _signInManager.GetExternalLoginInfoAsync(); - if (info == null) - { - ErrorMessage = "Error loading external login information."; - return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); - } - - // Sign in the user with this external login provider if the user already has a login. - var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); - if (result.Succeeded) - { - _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); - return LocalRedirect(returnUrl); - } - if (result.IsLockedOut) - { - return RedirectToPage("./Lockout"); - } - else - { - // If the user does not have an account, then ask the user to create an account. - ReturnUrl = returnUrl; - ProviderDisplayName = info.ProviderDisplayName; - if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) - { - Input = new InputModel - { - Email = info.Principal.FindFirstValue(ClaimTypes.Email) - }; - } - return Page(); - } - } - - public async Task OnPostConfirmationAsync(string returnUrl = null) - { - returnUrl = returnUrl ?? Url.Content("~/"); - // Get the information about the user from the external login provider - var info = await _signInManager.GetExternalLoginInfoAsync(); - if (info == null) - { - ErrorMessage = "Error loading external login information during confirmation."; - return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); - } - - if (ModelState.IsValid) - { - var user = new User { UserName = Input.Email, Email = Input.Email }; - - var result = await _userManager.CreateAsync(user); - if (result.Succeeded) - { - result = await _userManager.AddLoginAsync(user, info); - if (result.Succeeded) - { - _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); - - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = userId, code = code }, - protocol: Request.Scheme); - - await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", - $"Please confirm your account by clicking here."); - - // If account confirmation is required, we need to show the link if we don't have a real email sender - if (_userManager.Options.SignIn.RequireConfirmedAccount) - { - return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email }); - } - - await _signInManager.SignInAsync(user, isPersistent: false, info.LoginProvider); - - return LocalRedirect(returnUrl); - } - } - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - ProviderDisplayName = info.ProviderDisplayName; - ReturnUrl = returnUrl; - return Page(); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs deleted file mode 100644 index 52d2a19ef..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ForgotPasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - - public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) - { - _userManager = userManager; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public async Task OnPostAsync() - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) - { - // Don't reveal that the user does not exist or is not confirmed - return RedirectToPage("./ForgotPasswordConfirmation"); - } - - // For more information on how to enable account confirmation and password reset please - // visit https://go.microsoft.com/fwlink/?LinkID=532713 - var code = await _userManager.GeneratePasswordResetTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ResetPassword", - pageHandler: null, - values: new { area = "Identity", code }, - protocol: Request.Scheme); - - await _emailSender.SendEmailAsync( - Input.Email, - "Reset Password", - $"Please reset your password by clicking here."); - - return RedirectToPage("./ForgotPasswordConfirmation"); - } - - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Login.cshtml.cs deleted file mode 100644 index 9517c0a6a..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Login.cshtml.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginModel(SignInManager signInManager, - ILogger logger, - UserManager userManager) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public IList ExternalLogins { get; set; } - - public string ReturnUrl { get; set; } - - [TempData] - public string ErrorMessage { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - if (!string.IsNullOrEmpty(ErrorMessage)) - { - ModelState.AddModelError(string.Empty, ErrorMessage); - } - - returnUrl ??= Url.Content("~/"); - - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - - ReturnUrl = returnUrl; - } - - public async Task OnPostAsync(string returnUrl = null) - { - returnUrl ??= Url.Content("~/"); - - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - - if (ModelState.IsValid) - { - // This doesn't count login failures towards account lockout - // To enable password failures to trigger account lockout, set lockoutOnFailure: true - var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); - if (result.Succeeded) - { - _logger.LogInformation("User logged in."); - return LocalRedirect(returnUrl); - } - if (result.RequiresTwoFactor) - { - return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); - } - if (result.IsLockedOut) - { - _logger.LogWarning("User account locked out."); - return RedirectToPage("./Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return Page(); - } - } - - // If we got this far, something failed, redisplay form - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs deleted file mode 100644 index 83c4eeede..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginWith2faModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginWith2faModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public bool RememberMe { get; set; } - - public string ReturnUrl { get; set; } - - public class InputModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Authenticator code")] - public string TwoFactorCode { get; set; } - - [Display(Name = "Remember this machine")] - public bool RememberMachine { get; set; } - } - - public async Task OnGetAsync(bool rememberMe, string returnUrl = null) - { - // Ensure the user has gone through the username & password screen first - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - ReturnUrl = returnUrl; - RememberMe = rememberMe; - - return Page(); - } - - public async Task OnPostAsync(bool rememberMe, string returnUrl = null) - { - if (!ModelState.IsValid) - { - return Page(); - } - - returnUrl = returnUrl ?? Url.Content("~/"); - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); - - var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine); - - if (result.Succeeded) - { - _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id); - return LocalRedirect(returnUrl); - } - else if (result.IsLockedOut) - { - _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); - return RedirectToPage("./Lockout"); - } - else - { - _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id); - ModelState.AddModelError(string.Empty, "Invalid authenticator code."); - return Page(); - } - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs deleted file mode 100644 index 4c7cb94a2..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginWithRecoveryCodeModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginWithRecoveryCodeModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ReturnUrl { get; set; } - - public class InputModel - { - [BindProperty] - [Required] - [DataType(DataType.Text)] - [Display(Name = "Recovery Code")] - public string RecoveryCode { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - // Ensure the user has gone through the username & password screen first - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - ReturnUrl = returnUrl; - - return Page(); - } - - public async Task OnPostAsync(string returnUrl = null) - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); - - var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); - - if (result.Succeeded) - { - _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id); - return LocalRedirect(returnUrl ?? Url.Content("~/")); - } - if (result.IsLockedOut) - { - _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); - return RedirectToPage("./Lockout"); - } - else - { - _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id); - ModelState.AddModelError(string.Empty, "Invalid recovery code entered."); - return Page(); - } - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Logout.cshtml.cs deleted file mode 100644 index 1d68a81cb..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Logout.cshtml.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LogoutModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LogoutModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - public void OnGet() - { - } - - public async Task OnPost(string returnUrl = null) - { - await _signInManager.SignOutAsync(); - _logger.LogInformation("User logged out."); - if (returnUrl != null) - { - return LocalRedirect(returnUrl); - } - else - { - return RedirectToPage(); - } - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs deleted file mode 100644 index 416a46fd3..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ChangePasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public ChangePasswordModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public class InputModel - { - [Required] - [DataType(DataType.Password)] - [Display(Name = "Current password")] - public string OldPassword { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var hasPassword = await _userManager.HasPasswordAsync(user); - if (!hasPassword) - { - return RedirectToPage("./SetPassword"); - } - - return Page(); - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); - if (!changePasswordResult.Succeeded) - { - foreach (var error in changePasswordResult.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - _logger.LogInformation("User changed their password successfully."); - StatusMessage = "Your password has been changed."; - - return RedirectToPage(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs deleted file mode 100644 index 68f264db9..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class DeletePersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public DeletePersonalDataModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - } - - public bool RequirePassword { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - RequirePassword = await _userManager.HasPasswordAsync(user); - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - RequirePassword = await _userManager.HasPasswordAsync(user); - if (RequirePassword) - { - if (!await _userManager.CheckPasswordAsync(user, Input.Password)) - { - ModelState.AddModelError(string.Empty, "Incorrect password."); - return Page(); - } - } - - var result = await _userManager.DeleteAsync(user); - var userId = await _userManager.GetUserIdAsync(user); - if (!result.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); - } - - await _signInManager.SignOutAsync(); - - _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); - - return Redirect("~/"); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs deleted file mode 100644 index f82d738dc..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class Disable2faModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public Disable2faModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!await _userManager.GetTwoFactorEnabledAsync(user)) - { - throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); - if (!disable2faResult.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); - } - - _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); - StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; - return RedirectToPage("./TwoFactorAuthentication"); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs deleted file mode 100644 index fdf54e570..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class DownloadPersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public DownloadPersonalDataModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); - - // Only include personal data for download - var personalData = new Dictionary(); - var personalDataProps = typeof(User).GetProperties().Where( - prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); - foreach (var p in personalDataProps) - { - personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); - } - - var logins = await _userManager.GetLoginsAsync(user); - foreach (var l in logins) - { - personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); - } - - Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); - return new FileContentResult(JsonSerializer.SerializeToUtf8Bytes(personalData), "application/json"); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs deleted file mode 100644 index bda492353..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public partial class EmailModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IEmailSender _emailSender; - - public EmailModel( - UserManager userManager, - SignInManager signInManager, - IEmailSender emailSender) - { - _userManager = userManager; - _signInManager = signInManager; - _emailSender = emailSender; - } - - public string Username { get; set; } - - public string Email { get; set; } - - public bool IsEmailConfirmed { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - [Display(Name = "New email")] - public string NewEmail { get; set; } - } - - private async Task LoadAsync(User user) - { - var email = await _userManager.GetEmailAsync(user); - Email = email; - - Input = new InputModel - { - NewEmail = email, - }; - - IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await LoadAsync(user); - return Page(); - } - - public async Task OnPostChangeEmailAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadAsync(user); - return Page(); - } - - var email = await _userManager.GetEmailAsync(user); - if (Input.NewEmail != email) - { - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); - var callbackUrl = Url.Page( - "/Account/ConfirmEmailChange", - pageHandler: null, - values: new { userId = userId, email = Input.NewEmail, code = code }, - protocol: Request.Scheme); - await _emailSender.SendEmailAsync( - Input.NewEmail, - "Confirm your email", - $"Please confirm your account by clicking here."); - - StatusMessage = "Confirmation link to change email sent. Please check your email."; - return RedirectToPage(); - } - - StatusMessage = "Your email is unchanged."; - return RedirectToPage(); - } - - public async Task OnPostSendVerificationEmailAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadAsync(user); - return Page(); - } - - var userId = await _userManager.GetUserIdAsync(user); - var email = await _userManager.GetEmailAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = userId, code = code }, - protocol: Request.Scheme); - await _emailSender.SendEmailAsync( - email, - "Confirm your email", - $"Please confirm your account by clicking here."); - - StatusMessage = "Verification email sent. Please check your email."; - return RedirectToPage(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs deleted file mode 100644 index 6f635be35..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Collections.Generic; -using System.Text; -using System.Text.Encodings.Web; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class EnableAuthenticatorModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - private readonly UrlEncoder _urlEncoder; - - private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; - - public EnableAuthenticatorModel( - UserManager userManager, - ILogger logger, - UrlEncoder urlEncoder) - { - _userManager = userManager; - _logger = logger; - _urlEncoder = urlEncoder; - } - - public string SharedKey { get; set; } - - public string AuthenticatorUri { get; set; } - - [TempData] - public string[] RecoveryCodes { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Verification Code")] - public string Code { get; set; } - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await LoadSharedKeyAndQrCodeUriAsync(user); - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadSharedKeyAndQrCodeUriAsync(user); - return Page(); - } - - // Strip spaces and hypens - var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); - - var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( - user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); - - if (!is2faTokenValid) - { - ModelState.AddModelError("Input.Code", "Verification code is invalid."); - await LoadSharedKeyAndQrCodeUriAsync(user); - return Page(); - } - - await _userManager.SetTwoFactorEnabledAsync(user, true); - var userId = await _userManager.GetUserIdAsync(user); - _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); - - StatusMessage = "Your authenticator app has been verified."; - - if (await _userManager.CountRecoveryCodesAsync(user) == 0) - { - var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); - RecoveryCodes = recoveryCodes.ToArray(); - return RedirectToPage("./ShowRecoveryCodes"); - } - else - { - return RedirectToPage("./TwoFactorAuthentication"); - } - } - - private async Task LoadSharedKeyAndQrCodeUriAsync(User user) - { - // Load the authenticator key & QR code URI to display on the form - var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); - if (string.IsNullOrEmpty(unformattedKey)) - { - await _userManager.ResetAuthenticatorKeyAsync(user); - unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); - } - - SharedKey = FormatKey(unformattedKey); - - var email = await _userManager.GetEmailAsync(user); - AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey); - } - - private string FormatKey(string unformattedKey) - { - var result = new StringBuilder(); - int currentPosition = 0; - while (currentPosition + 4 < unformattedKey.Length) - { - result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); - currentPosition += 4; - } - if (currentPosition < unformattedKey.Length) - { - result.Append(unformattedKey.Substring(currentPosition)); - } - - return result.ToString().ToLowerInvariant(); - } - - private string GenerateQrCodeUri(string email, string unformattedKey) - { - return string.Format( - AuthenticatorUriFormat, - _urlEncoder.Encode("ClassifiedAds.IdentityServer"), - _urlEncoder.Encode(email), - unformattedKey); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs deleted file mode 100644 index ba53f9f43..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ExternalLoginsModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public ExternalLoginsModel( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - public IList CurrentLogins { get; set; } - - public IList OtherLogins { get; set; } - - public bool ShowRemoveButton { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - CurrentLogins = await _userManager.GetLoginsAsync(user); - OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) - .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) - .ToList(); - ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1; - return Page(); - } - - public async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey); - if (!result.Succeeded) - { - StatusMessage = "The external login was not removed."; - return RedirectToPage(); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "The external login was removed."; - return RedirectToPage(); - } - - public async Task OnPostLinkLoginAsync(string provider) - { - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - // Request a redirect to the external login provider to link a login for the current user - var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback"); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); - return new ChallengeResult(provider, properties); - } - - public async Task OnGetLinkLoginCallbackAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - var info = await _signInManager.GetExternalLoginInfoAsync(user.Id.ToString()); - if (info == null) - { - throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); - } - - var result = await _userManager.AddLoginAsync(user, info); - if (!result.Succeeded) - { - StatusMessage = "The external login was not added. External logins can only be associated with one account."; - return RedirectToPage(); - } - - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - StatusMessage = "The external login was added."; - return RedirectToPage(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs deleted file mode 100644 index 5d172489a..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class GenerateRecoveryCodesModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public GenerateRecoveryCodesModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - [TempData] - public string[] RecoveryCodes { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); - if (!isTwoFactorEnabled) - { - var userId = await _userManager.GetUserIdAsync(user); - throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); - var userId = await _userManager.GetUserIdAsync(user); - if (!isTwoFactorEnabled) - { - throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled."); - } - - var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); - RecoveryCodes = recoveryCodes.ToArray(); - - _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); - StatusMessage = "You have generated new recovery codes."; - return RedirectToPage("./ShowRecoveryCodes"); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs deleted file mode 100644 index 4c4772d1a..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public partial class IndexModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public IndexModel( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - public string Username { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } - } - - private async Task LoadAsync(User user) - { - var userName = await _userManager.GetUserNameAsync(user); - var phoneNumber = await _userManager.GetPhoneNumberAsync(user); - - Username = userName; - - Input = new InputModel - { - PhoneNumber = phoneNumber - }; - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await LoadAsync(user); - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadAsync(user); - return Page(); - } - - var phoneNumber = await _userManager.GetPhoneNumberAsync(user); - if (Input.PhoneNumber != phoneNumber) - { - var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); - if (!setPhoneResult.Succeeded) - { - StatusMessage = "Unexpected error when trying to set phone number."; - return RedirectToPage(); - } - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your profile has been updated"; - return RedirectToPage(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs deleted file mode 100644 index 1a5bc57ce..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class PersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public PersonalDataModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - return Page(); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs deleted file mode 100644 index d7017d275..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ResetAuthenticatorModel : PageModel - { - UserManager _userManager; - private readonly SignInManager _signInManager; - ILogger _logger; - - public ResetAuthenticatorModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID 'user.Id'."); - } - - await _userManager.SetTwoFactorEnabledAsync(user, false); - await _userManager.ResetAuthenticatorKeyAsync(user); - _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id); - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key."; - - return RedirectToPage("./EnableAuthenticator"); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs deleted file mode 100644 index daaf6cbb6..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class SetPasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public SetPasswordModel( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - [BindProperty] - public InputModel Input { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public class InputModel - { - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var hasPassword = await _userManager.HasPasswordAsync(user); - - if (hasPassword) - { - return RedirectToPage("./ChangePassword"); - } - - return Page(); - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); - if (!addPasswordResult.Succeeded) - { - foreach (var error in addPasswordResult.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your password has been set."; - - return RedirectToPage(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs deleted file mode 100644 index a3da9f3fa..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ShowRecoveryCodesModel : PageModel - { - [TempData] - public string[] RecoveryCodes { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public IActionResult OnGet() - { - if (RecoveryCodes == null || RecoveryCodes.Length == 0) - { - return RedirectToPage("./TwoFactorAuthentication"); - } - - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs deleted file mode 100644 index e78b301ee..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class TwoFactorAuthenticationModel : PageModel - { - private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}"; - - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public TwoFactorAuthenticationModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - public bool HasAuthenticator { get; set; } - - public int RecoveryCodesLeft { get; set; } - - [BindProperty] - public bool Is2faEnabled { get; set; } - - public bool IsMachineRemembered { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; - Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); - IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user); - RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); - - return Page(); - } - - public async Task OnPost() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await _signInManager.ForgetTwoFactorClientAsync(); - StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code."; - return RedirectToPage(); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Register.cshtml.cs deleted file mode 100644 index c45eef973..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/Register.cshtml.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class RegisterModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - private readonly ILogger _logger; - private readonly IEmailSender _emailSender; - - public RegisterModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger, - IEmailSender emailSender) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ReturnUrl { get; set; } - - public IList ExternalLogins { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - ReturnUrl = returnUrl; - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - } - - public async Task OnPostAsync(string returnUrl = null) - { - returnUrl ??= Url.Content("~/"); - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - if (ModelState.IsValid) - { - var user = new User { UserName = Input.Email, Email = Input.Email }; - var result = await _userManager.CreateAsync(user, Input.Password); - if (result.Succeeded) - { - _logger.LogInformation("User created a new account with password."); - - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl }, - protocol: Request.Scheme); - - await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", - $"Please confirm your account by clicking here."); - - if (_userManager.Options.SignIn.RequireConfirmedAccount) - { - return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); - } - else - { - await _signInManager.SignInAsync(user, isPersistent: false); - return LocalRedirect(returnUrl); - } - } - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - // If we got this far, something failed, redisplay form - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs deleted file mode 100644 index 437932f16..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using System.Text; -using System.Threading.Tasks; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class RegisterConfirmationModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _sender; - - public RegisterConfirmationModel(UserManager userManager, IEmailSender sender) - { - _userManager = userManager; - _sender = sender; - } - - public string Email { get; set; } - - public bool DisplayConfirmAccountLink { get; set; } - - public string EmailConfirmationUrl { get; set; } - - public async Task OnGetAsync(string email, string returnUrl = null) - { - if (email == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByEmailAsync(email); - if (user == null) - { - return NotFound($"Unable to load user with email '{email}'."); - } - - Email = email; - // Once you add a real email sender, you should remove this code that lets you confirm the account - DisplayConfirmAccountLink = true; - if (DisplayConfirmAccountLink) - { - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - EmailConfirmationUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl }, - protocol: Request.Scheme); - } - - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs deleted file mode 100644 index b67d78b24..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; - -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ResendEmailConfirmationModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - - public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender) - { - _userManager = userManager; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public void OnGet() - { - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null) - { - ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email."); - return Page(); - } - - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { userId = userId, code = code }, - protocol: Request.Scheme); - await _emailSender.SendEmailAsync( - Input.Email, - "Confirm your email", - $"Please confirm your account by clicking here."); - - ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email."); - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs deleted file mode 100644 index c76f72819..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ResetPasswordModel : PageModel - { - private readonly UserManager _userManager; - - public ResetPasswordModel(UserManager userManager) - { - _userManager = userManager; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - - public string Code { get; set; } - } - - public IActionResult OnGet(string code = null) - { - if (code == null) - { - return BadRequest("A code must be supplied for password reset."); - } - else - { - Input = new InputModel - { - Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) - }; - return Page(); - } - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null) - { - // Don't reveal that the user does not exist - return RedirectToPage("./ResetPasswordConfirmation"); - } - - var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); - if (result.Succeeded) - { - return RedirectToPage("./ResetPasswordConfirmation"); - } - - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ViewImports.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ViewImports.cshtml deleted file mode 100644 index 89f1f3971..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Areas/Identity/Pages/_ViewImports.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@using Microsoft.AspNetCore.Identity -@using ClassifiedAds.IdentityServer.Areas.Identity -@using ClassifiedAds.IdentityServer.Areas.Identity.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@using ClassifiedAds.Services.Identity.Entities diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ClassifiedAds.Services.Identity.AuthServer.csproj b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ClassifiedAds.Services.Identity.AuthServer.csproj deleted file mode 100644 index 7cee44c8b..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ClassifiedAds.Services.Identity.AuthServer.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - Always - - - - diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/AppSettings.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/AppSettings.cs deleted file mode 100644 index 0ed36c6fc..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ConfigurationOptions/AppSettings.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin; -using System.Collections.Generic; - -namespace ClassifiedAds.IdentityServer.ConfigurationOptions -{ - public class AppSettings : Services.Identity.ConfigurationOptions.AppSettings - { - public IdentityServerOptions IdentityServer { get; set; } - - public Dictionary SecurityHeaders { get; set; } - - public ExternalLoginOptions ExternalLogin { get; set; } - - public CookiePolicyOptions CookiePolicyOptions { get; set; } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/RoleController.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/RoleController.cs deleted file mode 100644 index 4e9c71fde..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/RoleController.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Application; -using ClassifiedAds.IdentityServer.Models.RoleModels; -using ClassifiedAds.Services.Identity.Commands.Roles; -using ClassifiedAds.Services.Identity.Entities; -using ClassifiedAds.Services.Identity.Queries; -using Microsoft.AspNetCore.Mvc; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class RoleController : Controller - { - private readonly Dispatcher _dispatcher; - - public RoleController(Dispatcher dispatcher) - { - _dispatcher = dispatcher; - } - - public async Task Index() - { - var roles = await _dispatcher.DispatchAsync(new GetRolesQuery { AsNoTracking = true }); - return View(roles); - } - - public async Task Edit(Guid id) - { - Role role; - if (id == Guid.Empty) - { - role = new Role(); - } - else - { - role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, AsNoTracking = true }); - } - - var model = role; - - return View(model); - } - - [HttpPost] - public async Task Edit(Role model) - { - Role role; - - if (model.Id == Guid.Empty) - { - role = new Role - { - Name = model.Name, - NormalizedName = model.Name.ToUpper(), - }; - - await _dispatcher.DispatchAsync(new AddUpdateRoleCommand { Role = role }); - } - else - { - role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Id }); - role.Name = model.Name; - role.NormalizedName = model.Name.ToUpper(); - await _dispatcher.DispatchAsync(new AddUpdateRoleCommand { Role = role }); - } - - return RedirectToAction(nameof(Edit), new { role.Id }); - } - - public async Task Delete(Guid id) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, AsNoTracking = true }); - return View(role); - } - - [HttpPost] - public async Task Delete(Role model) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Id }); - await _dispatcher.DispatchAsync(new DeleteRoleCommand { Role = role }); - - return RedirectToAction(nameof(Index)); - } - - public async Task Claims(Guid id) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, IncludeClaims = true, AsNoTracking = true }); - - return View(ClaimsModel.FromEntity(role)); - } - - [HttpPost] - public async Task Claims(ClaimModel model) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Role.Id, IncludeClaims = true }); - - var claim = new RoleClaim - { - Type = model.Type, - Value = model.Value, - }; - - await _dispatcher.DispatchAsync(new AddClaimCommand { Role = role, Claim = claim }); - - return RedirectToAction(nameof(Claims), new { id = role.Id }); - } - - public async Task DeleteClaim(Guid roleId, Guid claimId) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = roleId, IncludeClaims = true, AsNoTracking = true }); - var claim = role.Claims.FirstOrDefault(x => x.Id == claimId); - - return View(ClaimModel.FromEntity(claim)); - } - - [HttpPost] - public async Task DeleteClaim(ClaimModel model) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Role.Id, IncludeClaims = true }); - - var claim = role.Claims.FirstOrDefault(x => x.Id == model.Id); - - await _dispatcher.DispatchAsync(new DeleteClaimCommand { Role = role, Claim = claim }); - - return RedirectToAction(nameof(Claims), new { id = role.Id }); - } - - public async Task Users(Guid id) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, IncludeUsers = true, AsNoTracking = true }); - - var users = role.UserRoles.Select(x => x.User).ToList(); - var model = new UsersModel - { - Role = role, - Users = users, - }; - - return View(model); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/UserController.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/UserController.cs deleted file mode 100644 index 9aab6d606..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Controllers/UserController.cs +++ /dev/null @@ -1,229 +0,0 @@ -using ClassifiedAds.Application; -using ClassifiedAds.CrossCuttingConcerns.OS; -using ClassifiedAds.IdentityServer.Models.UserModels; -using ClassifiedAds.Services.Identity.Commands.Users; -using ClassifiedAds.Services.Identity.Entities; -using ClassifiedAds.Services.Identity.Queries; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class UserController : Controller - { - private readonly Dispatcher _dispatcher; - private readonly UserManager _userManager; - private readonly IDateTimeProvider _dateTimeProvider; - - public UserController(Dispatcher dispatcher, - UserManager userManager, - ILogger logger, - IDateTimeProvider dateTimeProvider) - { - _dispatcher = dispatcher; - _userManager = userManager; - _dateTimeProvider = dateTimeProvider; - logger.LogInformation("UserController"); - } - - public async Task Index() - { - var users = await _dispatcher.DispatchAsync(new GetUsersQuery { AsNoTracking = true }); - return View(users); - } - - public async Task Profile(Guid id) - { - var user = id != Guid.Empty - ? await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, AsNoTracking = true }) - : new User(); - return View(user); - } - - [HttpPost] - public async Task Profile(User model) - { - User user; - if (model.Id != Guid.Empty) - { - user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.Id }); - } - else - { - user = new User(); - } - - user.UserName = model.UserName; - user.NormalizedUserName = model.UserName.ToUpper(); - user.Email = model.Email; - user.NormalizedEmail = model.Email.ToUpper(); - user.EmailConfirmed = model.EmailConfirmed; - user.PhoneNumber = model.PhoneNumber; - user.PhoneNumberConfirmed = model.PhoneNumberConfirmed; - user.TwoFactorEnabled = model.TwoFactorEnabled; - user.LockoutEnabled = model.LockoutEnabled; - user.LockoutEnd = model.LockoutEnd; - user.AccessFailedCount = model.AccessFailedCount; - - _ = model.Id != Guid.Empty - ? await _userManager.UpdateAsync(user) - : await _userManager.CreateAsync(user); - - return RedirectToAction(nameof(Profile), new { user.Id }); - } - - public async Task ChangePassword(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, AsNoTracking = true }); - return View(ChangePasswordModel.FromEntity(user)); - } - - public async Task Delete(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, AsNoTracking = true }); - return View(user); - } - - [HttpPost] - public async Task Delete(User model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.Id }); - await _dispatcher.DispatchAsync(new DeleteUserCommand { User = user }); - return RedirectToAction(nameof(Index)); - } - - [HttpPost] - public async Task ChangePassword(ChangePasswordModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.Id }); - var token = await _userManager.GeneratePasswordResetTokenAsync(user); - var rs = await _userManager.ResetPasswordAsync(user, token, model.ConfirmPassword); - - if (rs.Succeeded) - { - return RedirectToAction(nameof(Profile), new { model.Id }); - } - - foreach (var error in rs.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - - return View(ChangePasswordModel.FromEntity(user)); - } - - public async Task Claims(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, IncludeClaims = true, AsNoTracking = true }); - return View(ClaimsModel.FromEntity(user)); - } - - [HttpPost] - public async Task Claims(ClaimModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeClaims = true }); - await _dispatcher.DispatchAsync(new AddClaimCommand - { - User = user, - Claim = new UserClaim - { - Type = model.Type, - Value = model.Value, - }, - }); - - return RedirectToAction(nameof(Claims), new { id = user.Id }); - } - - public async Task DeleteClaim(Guid userId, Guid claimId) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = userId, IncludeClaims = true, AsNoTracking = true }); - var claim = user.Claims.FirstOrDefault(x => x.Id == claimId); - - return View(ClaimModel.FromEntity(claim)); - } - - [HttpPost] - public async Task DeleteClaim(ClaimModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeClaims = true }); - - var claim = user.Claims.FirstOrDefault(x => x.Id == model.Id); - - await _dispatcher.DispatchAsync(new DeleteClaimCommand - { - User = user, - Claim = claim, - }); - - return RedirectToAction(nameof(Claims), new { id = user.Id }); - } - - public async Task Roles(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, IncludeRoles = true, AsNoTracking = true }); - - var roles = await _dispatcher.DispatchAsync(new GetRolesQuery { AsNoTracking = true }); - - var model = new RolesModel - { - User = user, - UserRoles = user.UserRoles.Select(x => new RoleModel { Role = x.Role, RoleId = x.RoleId }).ToList(), - Roles = roles.Where(x => !user.UserRoles.Any(y => y.RoleId == x.Id)).ToList(), - }; - - return View(model); - } - - [HttpPost] - public async Task Roles(RolesModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeUserRoles = true }); - - await _dispatcher.DispatchAsync(new AddRoleCommand - { - User = user, - Role = new UserRole - { - RoleId = model.Role.RoleId, - }, - }); - - return RedirectToAction(nameof(Roles), new { model.User.Id }); - } - - public async Task DeleteRole(Guid id, Guid roleId) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, IncludeRoles = true, AsNoTracking = true }); - var role = user.UserRoles.FirstOrDefault(x => x.RoleId == roleId); - var model = new RoleModel { User = user, Role = role.Role }; - - return View(model); - } - - [HttpPost] - public async Task DeleteRole(RoleModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeUserRoles = true }); - - var role = user.UserRoles.FirstOrDefault(x => x.RoleId == model.Role.Id); - - await _dispatcher.DispatchAsync(new DeleteRoleCommand - { - User = user, - Role = role, - }); - - return RedirectToAction(nameof(Roles), new { model.User.Id }); - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Dockerfile b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Dockerfile deleted file mode 100644 index cb08d615f..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env -WORKDIR /ClassifiedAds.Microservices - -# Copy csproj and restore as distinct layers -COPY ./Common/ClassifiedAds.Application/*.csproj ./Common/ClassifiedAds.Application/ -COPY ./Common/ClassifiedAds.CrossCuttingConcerns/*.csproj ./Common/ClassifiedAds.CrossCuttingConcerns/ -COPY ./Common/ClassifiedAds.Domain/*.csproj ./Common/ClassifiedAds.Domain/ -COPY ./Common/ClassifiedAds.Infrastructure/*.csproj ./Common/ClassifiedAds.Infrastructure/ -RUN dotnet restore ./Common/ClassifiedAds.Application/ClassifiedAds.Application.csproj -RUN dotnet restore ./Common/ClassifiedAds.Infrastructure/ClassifiedAds.Infrastructure.csproj - -COPY ./Services.Identity/ClassifiedAds.Services.Identity/*.csproj ./Services.Identity/ClassifiedAds.Services.Identity/ -RUN dotnet restore ./Services.Identity/ClassifiedAds.Services.Identity/ClassifiedAds.Services.Identity.csproj - -COPY ./Services.Identity/ClassifiedAds.Services.Identity.AuthServer/*.csproj ./Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ -RUN dotnet restore ./Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ClassifiedAds.Services.Identity.AuthServer.csproj - -# Copy everything else and build ClassifiedAds -COPY . ./ -RUN dotnet publish ./Services.Identity/ClassifiedAds.Services.Identity.AuthServer/ClassifiedAds.Services.Identity.AuthServer.csproj -c Release -o out - -# Build runtime image -FROM mcr.microsoft.com/dotnet/aspnet:6.0 -WORKDIR /ClassifiedAds.Microservices -COPY --from=build-env /ClassifiedAds.Microservices/out . - -ENTRYPOINT ["dotnet", "ClassifiedAds.Services.Identity.AuthServer.dll"] \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/IdServerPersistenceExtensions.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/IdServerPersistenceExtensions.cs deleted file mode 100644 index 520657369..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/IdServerPersistenceExtensions.cs +++ /dev/null @@ -1,316 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using ClassifiedAds.CrossCuttingConcerns.ExtensionMethods; -using IdentityServer4; -using IdentityServer4.EntityFramework.DbContexts; -using IdentityServer4.EntityFramework.Mappers; -using IdentityServer4.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.EntityFrameworkCore; - -namespace Microsoft.Extensions.DependencyInjection -{ - public static class IdServerPersistenceExtensions - { - public static IIdentityServerBuilder AddTokenProviderModule(this IIdentityServerBuilder services, string connectionString, string migrationsAssembly = "") - { - services.AddConfigurationStore(options => - { - options.ConfigureDbContext = builder => - builder.UseSqlServer(connectionString, - sql => - { - if (!string.IsNullOrEmpty(migrationsAssembly)) - { - sql.MigrationsAssembly(migrationsAssembly); - } - }); - options.DefaultSchema = "idsv"; - }) - .AddOperationalStore(options => - { - options.ConfigureDbContext = builder => - builder.UseSqlServer(connectionString, - sql => - { - if (!string.IsNullOrEmpty(migrationsAssembly)) - { - sql.MigrationsAssembly(migrationsAssembly); - } - }); - options.DefaultSchema = "idsv"; - - // this enables automatic token cleanup. this is optional. - options.EnableTokenCleanup = true; - options.TokenCleanupInterval = 30; // interval in seconds - }); - return services; - } - - public static void MigrateIdServerDb(this IApplicationBuilder app) - { - using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) - { - serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); - - var context = serviceScope.ServiceProvider.GetRequiredService(); - context.Database.Migrate(); - - var clients = new List(); - if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.WebMVC")) - { - clients.Add(new Client - { - ClientId = "ClassifiedAds.WebMVC", - ClientName = "ClassifiedAds Web MVC", - AllowedGrantTypes = GrantTypes.Code.Combines(GrantTypes.ResourceOwnerPassword), - RequirePkce = true, - RedirectUris = - { - "https://localhost:44364/signin-oidc", - "http://host.docker.internal:9003/signin-oidc", - }, - PostLogoutRedirectUris = - { - "https://localhost:44364/signout-callback-oidc", - "http://host.docker.internal:9003/signout-callback-oidc", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - RequireConsent = true, - }); - } - - if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.BlazorServerSide")) - { - clients.Add(new Client - { - ClientId = "ClassifiedAds.BlazorServerSide", - ClientName = "ClassifiedAds BlazorServerSide", - AllowedGrantTypes = GrantTypes.Code.Combines(GrantTypes.ResourceOwnerPassword), - RequirePkce = true, - RedirectUris = - { - "https://localhost:44331/signin-oidc", - "http://host.docker.internal:9008/signin-oidc", - }, - PostLogoutRedirectUris = - { - "https://localhost:44331/signout-callback-oidc", - "http://host.docker.internal:9008/signout-callback-oidc", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - RequireConsent = true, - }); - } - - if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.BlazorWebAssembly")) - { - clients.Add(new Client - { - ClientId = "ClassifiedAds.BlazorWebAssembly", - ClientName = "ClassifiedAds BlazorWebAssembly", - AllowedGrantTypes = GrantTypes.Code, - RequireClientSecret = false, - RequirePkce = true, - RedirectUris = - { - "https://localhost:44348/authentication/login-callback", - }, - PostLogoutRedirectUris = - { - "https://localhost:44348/authentication/logout-callback", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - RequireConsent = true, - }); - } - - if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.Angular")) - { - clients.Add(new Client - { - ClientId = "ClassifiedAds.Angular", - ClientName = "ClassifiedAds Angular", - AllowedGrantTypes = GrantTypes.Code, - RequireClientSecret = false, - RequirePkce = true, - AllowAccessTokensViaBrowser = true, - RedirectUris = - { - "http://localhost:4200/oidc-login-redirect", - }, - PostLogoutRedirectUris = - { - "http://localhost:4200/?postLogout=true", - }, - AllowedCorsOrigins = - { - "http://localhost:4200", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - RequireConsent = true, - }); - } - - if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.React")) - { - clients.Add(new Client - { - ClientId = "ClassifiedAds.React", - ClientName = "ClassifiedAds React", - AllowedGrantTypes = GrantTypes.Code, - RequireClientSecret = false, - RequirePkce = true, - AllowAccessTokensViaBrowser = true, - RedirectUris = - { - "http://localhost:3000/oidc-login-redirect", - }, - PostLogoutRedirectUris = - { - "http://localhost:3000/?postLogout=true", - }, - AllowedCorsOrigins = - { - "http://localhost:3000", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - RequireConsent = true, - }); - } - - if (!context.Clients.Any(x => x.ClientId == "ClassifiedAds.Vue")) - { - clients.Add(new Client - { - ClientId = "ClassifiedAds.Vue", - ClientName = "ClassifiedAds Vue", - AllowedGrantTypes = GrantTypes.Code, - RequireClientSecret = false, - RequirePkce = true, - AllowAccessTokensViaBrowser = true, - RedirectUris = - { - "http://localhost:8080/oidc-login-redirect", - }, - PostLogoutRedirectUris = - { - "http://localhost:8080/?postLogout=true", - }, - AllowedCorsOrigins = - { - "http://localhost:8080", - }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "ClassifiedAds.WebAPI", - }, - ClientSecrets = - { - new Secret("secret".Sha256()), - }, - AllowOfflineAccess = true, - RequireConsent = true, - }); - } - - if (clients.Any()) - { - context.Clients.AddRange(clients.Select(x => x.ToEntity())); - context.SaveChanges(); - } - - if (!context.IdentityResources.Any()) - { - var identityResources = new List() - { - new IdentityResources.OpenId(), - new IdentityResources.Profile(), - }; - - context.IdentityResources.AddRange(identityResources.Select(x => x.ToEntity())); - - context.SaveChanges(); - } - - if (!context.ApiScopes.Any()) - { - var apiScopes = new List() - { - new ApiScope("ClassifiedAds.WebAPI", "ClassifiedAds Web API"), - }; - - context.ApiScopes.AddRange(apiScopes.Select(x => x.ToEntity())); - context.SaveChanges(); - } - - if (!context.ApiResources.Any()) - { - var apiResources = new List - { - new ApiResource("ClassifiedAds.WebAPI", "ClassifiedAds Web API", new[] { "role" }) - { - Scopes = { "ClassifiedAds.WebAPI" }, - }, - }; - - context.ApiResources.AddRange(apiResources.Select(x => x.ToEntity())); - - context.SaveChanges(); - } - } - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/ClaimModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/ClaimModel.cs deleted file mode 100644 index 6076ef8c1..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/ClaimModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class ClaimModel - { - public Guid Id { get; set; } - public string Type { get; set; } - public string Value { get; set; } - public Role Role { get; set; } - - public static ClaimModel FromEntity(RoleClaim claim) - { - return new ClaimModel - { - Id = claim.Id, - Type = claim.Type, - Value = claim.Value, - Role = claim.Role, - }; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/ClaimsModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/ClaimsModel.cs deleted file mode 100644 index b32d113dc..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/ClaimsModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class ClaimsModel : ClaimModel - { - public List Claims { get; set; } - - public static ClaimsModel FromEntity(Role role) - { - return new ClaimsModel - { - Role = role, - Claims = role.Claims?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/RoleModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/RoleModel.cs deleted file mode 100644 index c5386c24c..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/RoleModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class RoleModel - { - public Guid Id { get; set; } - - public string Name { get; set; } - - public static RoleModel FromEntity(Role role) - { - return new RoleModel { Id = role.Id, Name = role.Name }; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/UsersModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/UsersModel.cs deleted file mode 100644 index 3cba6040f..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/RoleModels/UsersModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System.Collections.Generic; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class UsersModel - { - public Role Role { get; set; } - - public List Users { get; set; } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ChangePasswordModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ChangePasswordModel.cs deleted file mode 100644 index e0d9689f3..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ChangePasswordModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System; -using System.ComponentModel.DataAnnotations; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class ChangePasswordModel - { - public Guid Id { get; set; } - public string UserName { get; set; } - public string Password { get; set; } - - [Compare(nameof(Password))] - public string ConfirmPassword { get; set; } - - public static ChangePasswordModel FromEntity(User user) - { - return new ChangePasswordModel - { - Id = user.Id, - UserName = user.UserName, - }; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ClaimModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ClaimModel.cs deleted file mode 100644 index 8bd5acce2..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ClaimModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class ClaimModel - { - public Guid Id { get; set; } - public string Type { get; set; } - public string Value { get; set; } - public User User { get; set; } - - public static ClaimModel FromEntity(UserClaim claim) - { - return new ClaimModel - { - Id = claim.Id, - Type = claim.Type, - Value = claim.Value, - User = claim.User, - }; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ClaimsModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ClaimsModel.cs deleted file mode 100644 index 422c4d8c6..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/ClaimsModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class ClaimsModel : ClaimModel - { - public List Claims { get; set; } - - public static ClaimsModel FromEntity(User user) - { - return new ClaimsModel - { - User = user, - Claims = user.Claims?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/RoleModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/RoleModel.cs deleted file mode 100644 index 7d3b13265..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/RoleModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class RoleModel - { - public Guid RoleId { get; set; } - - public Guid UserId { get; set; } - - public User User { get; set; } - - public Role Role { get; set; } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/RolesModel.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/RolesModel.cs deleted file mode 100644 index a7ffa3d1d..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Models/UserModels/RolesModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using System.Collections.Generic; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class RolesModel - { - public User User { get; set; } - - public RoleModel Role { get; set; } - - public List Roles { get; set; } - - public List UserRoles { get; set; } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/PowerShell.txt b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/PowerShell.txt deleted file mode 100644 index 616ee73ca..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/PowerShell.txt +++ /dev/null @@ -1,5 +0,0 @@ -Add-Migration -Context ConfigurationDbContext Init -OutputDir Migrations/ConfigurationDb -Add-Migration -Context PersistedGrantDbContext Init -OutputDir Migrations/PersistedGrantDb - -Update-Database -Context ConfigurationDbContext -Update-Database -Context PersistedGrantDbContext \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/AccountController.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/AccountController.cs deleted file mode 100644 index f2457bd3c..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/AccountController.cs +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - -using System; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Application; -using ClassifiedAds.IdentityServer.Models; -using ClassifiedAds.IdentityServer.Quickstart.Account; -using ClassifiedAds.Services.Identity.Commands.EmailMessages; -using ClassifiedAds.Services.Identity.Commands.SmsMessages; -using ClassifiedAds.Services.Identity.DTOs; -using ClassifiedAds.Services.Identity.Entities; -using IdentityModel; -using IdentityServer4; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Quickstart.UI; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.Extensions.Logging; - -namespace IdentityServerHost.Quickstart.UI -{ - /// - /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. - /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! - /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval - /// - [SecurityHeaders] - [AllowAnonymous] - public class AccountController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IAuthenticationSchemeProvider _schemeProvider; - private readonly IEventService _events; - - private readonly ILogger _logger; - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly Dispatcher _dispatcher; - - public AccountController( - ILogger logger, - UserManager userManager, - SignInManager signInManager, - Dispatcher dispatcher, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IAuthenticationSchemeProvider schemeProvider, - IEventService events) - { - _logger = logger; - _userManager = userManager; - _signInManager = signInManager; - _dispatcher = dispatcher; - _interaction = interaction; - _clientStore = clientStore; - _schemeProvider = schemeProvider; - _events = events; - } - - /// - /// Entry point into the login workflow - /// - [HttpGet] - public async Task Login(string returnUrl) - { - // build a model so we know what to show on the login page - var vm = await BuildLoginViewModelAsync(returnUrl); - - if (vm.IsExternalLoginOnly) - { - // we only have one option for logging in and it's an external provider - return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl }); - } - - return View(vm); - } - - /// - /// Handle postback from username/password login - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Login(LoginInputModel model, string button) - { - // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - - // the user clicked the "cancel" button - if (button != "login") - { - if (context != null) - { - // if the user cancels, send a result back into IdentityServer as if they - // denied the consent (even if this client does not require consent). - // this will send back an access denied OIDC error response to the client. - await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - return Redirect(model.ReturnUrl); - } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } - } - - if (ModelState.IsValid) - { - var user = await _userManager.FindByNameAsync(model.Username); - - if (user != null && user.TwoFactorEnabled) - { - var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: false); - if (result.Succeeded) - { - _logger.LogInformation(1, "User logged in."); - return RedirectToLocal(model.ReturnUrl); - } - - if (result.RequiresTwoFactor) - { - return RedirectToAction(nameof(SendCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberLogin }); - } - - if (result.IsLockedOut) - { - _logger.LogWarning(2, "User account locked out."); - return View("Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return View(await BuildLoginViewModelAsync(model)); - } - } - - if (user != null && await _userManager.CheckPasswordAsync(user, model.Password)) - { - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, clientId: context?.Client.ClientId)); - - // only set explicit expiration here if user chooses "remember me". - // otherwise we rely upon expiration configured in cookie middleware. - AuthenticationProperties props = null; - if (AccountOptions.AllowRememberLogin && model.RememberLogin) - { - props = new AuthenticationProperties - { - IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) - }; - }; - - // issue authentication cookie with subject ID and username - var isuser = new IdentityServerUser(user.Id.ToString()) - { - DisplayName = user.UserName, - }; - - await HttpContext.SignInAsync(isuser, props); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - return Redirect(model.ReturnUrl); - } - - // request for a local page - if (Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - } - - await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); - ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); - } - - // something went wrong, show form with error - var vm = await BuildLoginViewModelAsync(model); - return View(vm); - } - - // - // GET: /Account/SendCode - [HttpGet] - [AllowAnonymous] - public async Task SendCode(string returnUrl = null, bool rememberMe = false) - { - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - return View("Error"); - } - - var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); - var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); - return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); - } - - // - // POST: /Account/SendCode - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task SendCode(SendCodeViewModel model) - { - if (!ModelState.IsValid) - { - return View(); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - return View("Error"); - } - - if (model.SelectedProvider == "Authenticator") - { - return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); - } - - // Generate the token and send it - var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); - if (string.IsNullOrWhiteSpace(code)) - { - return View("Error"); - } - - var message = "Your security code is: " + code; - if (model.SelectedProvider == "Email") - { - await _dispatcher.DispatchAsync(new AddEmailMessageCommand - { - EmailMessage = new EmailMessageDTO - { - From = "phong@gmail.com", - Tos = user.Email, - Subject = "Security Code", - Body = message, - }, - }); - } - else if (model.SelectedProvider == "Phone") - { - await _dispatcher.DispatchAsync(new AddSmsMessageCommand - { - SmsMessage = new SmsMessageDTO - { - PhoneNumber = user.PhoneNumber, - Message = message, - }, - }); - } - - return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); - } - - // - // GET: /Account/VerifyCode - [HttpGet] - [AllowAnonymous] - public async Task VerifyCode(string provider, bool rememberMe, string returnUrl = null) - { - // Require that the user has already logged in via username/password or external login - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - return View("Error"); - } - - return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); - } - - // - // POST: /Account/VerifyCode - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task VerifyCode(VerifyCodeViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - - // The following code protects for brute force attacks against the two factor codes. - // If a user enters incorrect codes for a specified amount of time then the user account - // will be locked out for a specified amount of time. - var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); - if (result.Succeeded) - { - // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, clientId: context?.Client.ClientId)); - - // only set explicit expiration here if user chooses "remember me". - // otherwise we rely upon expiration configured in cookie middleware. - AuthenticationProperties props = null; - if (AccountOptions.AllowRememberLogin && model.RememberMe) - { - props = new AuthenticationProperties - { - IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) - }; - } - - // issue authentication cookie with subject ID and username - var isuser = new IdentityServerUser(user.Id.ToString()) - { - DisplayName = user.UserName, - }; - - await HttpContext.SignInAsync(isuser, props); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - return Redirect(model.ReturnUrl); - } - - // request for a local page - if (Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - return RedirectToLocal(model.ReturnUrl); - } - - if (result.IsLockedOut) - { - _logger.LogWarning(7, "User account locked out."); - return View("Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid code."); - return View(model); - } - } - - - /// - /// Show logout page - /// - [HttpGet] - public async Task Logout(string logoutId) - { - // build a model so the logout page knows what to display - var vm = await BuildLogoutViewModelAsync(logoutId); - - if (vm.ShowLogoutPrompt == false) - { - // if the request for logout was properly authenticated from IdentityServer, then - // we don't need to show the prompt and can just log the user out directly. - return await Logout(vm); - } - - return View(vm); - } - - /// - /// Handle logout page postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Logout(LogoutInputModel model) - { - // build a model so the logged out page knows what to display - var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); - - if (User?.Identity.IsAuthenticated == true) - { - // delete local authentication cookie - await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme); - - // raise the logout event - await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); - } - - // check if we need to trigger sign-out at an upstream identity provider - if (vm.TriggerExternalSignout) - { - // build a return URL so the upstream provider will redirect back - // to us after the user has logged out. this allows us to then - // complete our single sign-out processing. - string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); - - // this triggers a redirect to the external provider for sign-out - return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); - } - - return View("LoggedOut", vm); - } - - [HttpGet] - public IActionResult AccessDenied() - { - return View(); - } - - - /*****************************************/ - /* helper APIs for the AccountController */ - /*****************************************/ - private async Task BuildLoginViewModelAsync(string returnUrl) - { - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) - { - var local = context.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider; - - // this is meant to short circuit the UI and only trigger the one external IdP - var vm = new LoginViewModel - { - EnableLocalLogin = local, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - }; - - if (!local) - { - vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; - } - - return vm; - } - - var schemes = await _schemeProvider.GetAllSchemesAsync(); - - var providers = schemes - .Where(x => x.DisplayName != null) - .Select(x => new ExternalProvider - { - DisplayName = x.DisplayName ?? x.Name, - AuthenticationScheme = x.Name, - }).ToList(); - - var allowLocal = true; - if (context?.Client.ClientId != null) - { - var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); - if (client != null) - { - allowLocal = client.EnableLocalLogin; - - if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) - { - providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); - } - } - } - - return new LoginViewModel - { - AllowRememberLogin = AccountOptions.AllowRememberLogin, - EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - ExternalProviders = providers.ToArray(), - }; - } - - private async Task BuildLoginViewModelAsync(LoginInputModel model) - { - var vm = await BuildLoginViewModelAsync(model.ReturnUrl); - vm.Username = model.Username; - vm.RememberLogin = model.RememberLogin; - return vm; - } - - private async Task BuildLogoutViewModelAsync(string logoutId) - { - var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; - - if (User?.Identity.IsAuthenticated != true) - { - // if the user is not authenticated, then just show logged out page - vm.ShowLogoutPrompt = false; - return vm; - } - - var context = await _interaction.GetLogoutContextAsync(logoutId); - if (context?.ShowSignoutPrompt == false) - { - // it's safe to automatically sign-out - vm.ShowLogoutPrompt = false; - return vm; - } - - // show the logout prompt. this prevents attacks where the user - // is automatically signed out by another malicious web page. - return vm; - } - - private async Task BuildLoggedOutViewModelAsync(string logoutId) - { - // get context information (client name, post logout redirect URI and iframe for federated signout) - var logout = await _interaction.GetLogoutContextAsync(logoutId); - - var vm = new LoggedOutViewModel - { - AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, - PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, - ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, - SignOutIframeUrl = logout?.SignOutIFrameUrl, - LogoutId = logoutId, - }; - - if (User?.Identity.IsAuthenticated == true) - { - var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; - if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider) - { - var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); - if (providerSupportsSignout) - { - if (vm.LogoutId == null) - { - // if there's no current logout context, we need to create one - // this captures necessary info from the current logged in user - // before we signout and redirect away to the external IdP for signout - vm.LogoutId = await _interaction.CreateLogoutContextAsync(); - } - - vm.ExternalAuthenticationScheme = idp; - } - } - } - - return vm; - } - - [HttpGet] - public IActionResult Register() - { - return View(); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Register(RegisterModel model) - { - if (!ModelState.IsValid) - { - return View(); - } - - var user = await _userManager.FindByNameAsync(model.UserName); - - if (user != null) - { - return View("Success"); - } - - user = new User - { - UserName = model.UserName, - Email = model.UserName, - }; - - var result = await _userManager.CreateAsync(user, model.Password); - - if (!result.Succeeded) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - - return View(); - } - - var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var confirmationEmail = Url.Action("ConfirmEmailAddress", "Account", - new { token = token, email = user.Email }, Request.Scheme); - - await _dispatcher.DispatchAsync(new AddEmailMessageCommand - { - EmailMessage = new EmailMessageDTO - { - From = "phong@gmail.com", - Tos = user.Email, - Subject = "Confirmation Email", - Body = string.Format("Confirmation Email: {0}", confirmationEmail), - }, - }); - - return View("Success"); - } - - [HttpGet] - public async Task ConfirmEmailAddress(string token, string email) - { - var user = await _userManager.FindByEmailAsync(email); - - if (user == null) - { - return View("Error"); - } - - var result = await _userManager.ConfirmEmailAsync(user, token); - - if (result.Succeeded) - { - return View("Success"); - } - - return View("Error"); - } - - [HttpGet] - public IActionResult ForgotPassword() - { - return View(); - } - - [HttpPost] - public async Task ForgotPassword(ForgotPasswordModel model) - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(model.Email); - - if (user != null) - { - var token = await _userManager.GeneratePasswordResetTokenAsync(user); - var resetUrl = Url.Action("ResetPassword", "Account", - new { token = token, email = user.Email }, Request.Scheme); - - await _dispatcher.DispatchAsync(new AddEmailMessageCommand - { - EmailMessage = new EmailMessageDTO - { - From = "phong@gmail.com", - Tos = user.Email, - Subject = "Forgot Password", - Body = string.Format("Reset Url: {0}", resetUrl), - }, - }); - } - else - { - // email user and inform them that they do not have an account - } - - return View("Success"); - } - - return View(); - } - - [HttpGet] - public IActionResult ResetPassword(string token, string email) - { - return View(new ResetPasswordModel { Token = token, Email = email }); - } - - [HttpPost] - public async Task ResetPassword(ResetPasswordModel model) - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(model.Email); - - if (user != null) - { - var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password); - - if (!result.Succeeded) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - - return View(); - } - - return View("Success"); - } - - ModelState.AddModelError(string.Empty, "Invalid Request"); - } - - return View(); - } - - private IActionResult RedirectToLocal(string returnUrl) - { - if (Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - else - { - return RedirectToAction(nameof(HomeController.Index), "Home"); - } - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/ExternalController.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/ExternalController.cs deleted file mode 100644 index aecc8e097..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Account/ExternalController.cs +++ /dev/null @@ -1,218 +0,0 @@ -using ClassifiedAds.Services.Identity.Entities; -using IdentityModel; -using IdentityServer4; -using IdentityServer4.Events; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [AllowAnonymous] - public class ExternalController : Controller - { - private readonly UserManager _userManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly ILogger _logger; - private readonly IEventService _events; - - public ExternalController( - UserManager userManager, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IEventService events, - ILogger logger) - { - _userManager = userManager; - _interaction = interaction; - _clientStore = clientStore; - _logger = logger; - _events = events; - } - - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public IActionResult Challenge(string scheme, string returnUrl) - { - if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; - - // validate returnUrl - either it is a valid OIDC URL or back to a local page - if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - // start challenge and roundtrip the return URL and scheme - var props = new AuthenticationProperties - { - RedirectUri = Url.Action(nameof(Callback)), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", scheme }, - } - }; - - return Challenge(props, scheme); - - } - - /// - /// Post processing of external authentication - /// - [HttpGet] - public async Task Callback() - { - // read external identity from the temporary cookie - var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); - if (result?.Succeeded != true) - { - throw new Exception("External authentication error"); - } - - if (_logger.IsEnabled(LogLevel.Debug)) - { - var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); - _logger.LogDebug("External claims: {@claims}", externalClaims); - } - - // lookup our user and external provider info - var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); - if (user == null) - { - // this might be where you might initiate a custom workflow for user registration - // in this sample we don't show how that would be done, as our sample implementation - // simply auto-provisions new external user - user = AutoProvisionUser(provider, providerUserId, claims); - } - - // this allows us to collect any additional claims or properties - // for the specific protocols used and store them in the local auth cookie. - // this is typically used to store data needed for signout from those protocols. - var additionalLocalClaims = new List(); - var localSignInProps = new AuthenticationProperties(); - ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); - - // issue authentication cookie for user - var isuser = new IdentityServerUser(user.Id.ToString()) - { - DisplayName = user.UserName, - IdentityProvider = provider, - AdditionalClaims = additionalLocalClaims, - }; - - await HttpContext.SignInAsync(isuser, localSignInProps); - - // delete temporary cookie used during external authentication - await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); - - // retrieve return URL - var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; - - // check if external login is in the context of an OIDC request - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id.ToString(), user.UserName, true, context?.Client.ClientId)); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", returnUrl); - } - } - - return Redirect(returnUrl); - } - - private (User user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) - { - var externalUser = result.Principal; - var provider = result.Properties.Items["scheme"]; - - // try to determine the unique id of the external user (issued by the provider) - // the most common claim type for that are the sub claim and the NameIdentifier - // depending on the external provider, some other claim type might be used - var emailClaim = externalUser.FindFirst(ClaimTypes.Email) ?? - externalUser.FindFirst(JwtClaimTypes.Subject) ?? - externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? - throw new Exception("Unknown userid"); - - if (provider == "AAD") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Upn); - } - else if (provider == "Microsoft") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Email); - } - else if (provider == "Google") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Email); - } - else if (provider == "Facebook") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Email); - } - - // remove the user id claim so we don't include it as an extra claim if/when we provision the user - var claims = externalUser.Claims.ToList(); - claims.Remove(emailClaim); - - var providerUserEmail = emailClaim.Value; - - // find external user - var user = _userManager.FindByEmailAsync(providerUserEmail).GetAwaiter().GetResult(); - - return (user, provider, providerUserEmail, claims); - } - - private User AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) - { - var user = new User - { - UserName = providerUserId, - Email = providerUserId, - }; - var rs = _userManager.CreateAsync(user, "4SBbS]#Nc3*Dca").GetAwaiter().GetResult(); - return user; - } - - // if the external login is OIDC-based, there are certain things we need to preserve to make logout work - // this will be different for WS-Fed, SAML2p or other protocols - private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - // if the external system sent a session id claim, copy it over - // so we can use it for single sign-out - var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); - if (sid != null) - { - localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); - } - - // if the external provider issued an id_token, we'll keep it for signout - var idToken = externalResult.Properties.GetTokenValue("id_token"); - if (idToken != null) - { - localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); - } - } - } -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ManageController.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ManageController.cs deleted file mode 100644 index 6e0904bae..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Quickstart/Manage/ManageController.cs +++ /dev/null @@ -1,386 +0,0 @@ -using ClassifiedAds.Application; -using ClassifiedAds.IdentityServer.Manage.Models; -using ClassifiedAds.Services.Identity.Commands.SmsMessages; -using ClassifiedAds.Services.Identity.DTOs; -using ClassifiedAds.Services.Identity.Entities; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Quickstart.Manage -{ - [Authorize] - public class ManageController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly Dispatcher _dispatcher; - private readonly ILogger _logger; - - public ManageController( - UserManager userManager, - SignInManager signInManager, - ILoggerFactory loggerFactory, - Dispatcher dispatcher) - { - _userManager = userManager; - _signInManager = signInManager; - _dispatcher = dispatcher; - _logger = loggerFactory.CreateLogger(); - } - - // - // GET: /Manage/Index - [HttpGet] - public async Task Index(ManageMessageId? message = null) - { - ViewData["StatusMessage"] = - message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." - : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." - : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." - : message == ManageMessageId.Error ? "An error has occurred." - : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." - : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." - : ""; - - var user = await GetCurrentUserAsync(); - var model = new IndexViewModel - { - HasPassword = await _userManager.HasPasswordAsync(user), - PhoneNumber = await _userManager.GetPhoneNumberAsync(user), - TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user), - //Logins = await _userManager.GetLoginsAsync(user), - Logins = new List(), - BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user), - AuthenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user) - }; - return View(model); - } - - // - // POST: /Manage/RemoveLogin - [HttpPost] - [ValidateAntiForgeryToken] - public async Task RemoveLogin(RemoveLoginViewModel account) - { - ManageMessageId? message = ManageMessageId.Error; - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - message = ManageMessageId.RemoveLoginSuccess; - } - } - return RedirectToAction(nameof(ManageLogins), new { Message = message }); - } - - // - // GET: /Manage/AddPhoneNumber - public IActionResult AddPhoneNumber() - { - return View(); - } - - // - // POST: /Manage/AddPhoneNumber - [HttpPost] - [ValidateAntiForgeryToken] - public async Task AddPhoneNumber(AddPhoneNumberViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - // Generate the token and send it - var user = await GetCurrentUserAsync(); - var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber); - await _dispatcher.DispatchAsync(new AddSmsMessageCommand - { - SmsMessage = new SmsMessageDTO - { - PhoneNumber = model.PhoneNumber, - Message = "Your security code is: " + code, - }, - }); - return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber }); - } - - // - // POST: /Manage/ResetAuthenticatorKey - [HttpPost] - [ValidateAntiForgeryToken] - public async Task ResetAuthenticatorKey() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - await _userManager.ResetAuthenticatorKeyAsync(user); - _logger.LogInformation(1, "User reset authenticator key."); - } - return RedirectToAction(nameof(Index), "Manage"); - } - - // - // POST: /Manage/GenerateRecoveryCode - [HttpPost] - [ValidateAntiForgeryToken] - public async Task GenerateRecoveryCode() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - var codes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 5); - _logger.LogInformation(1, "User generated new recovery code."); - return View("DisplayRecoveryCodes", new DisplayRecoveryCodesViewModel { Codes = codes }); - } - return View("Error"); - } - - // - // POST: /Manage/EnableTwoFactorAuthentication - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EnableTwoFactorAuthentication() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - await _userManager.SetTwoFactorEnabledAsync(user, true); - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation(1, "User enabled two-factor authentication."); - } - return RedirectToAction(nameof(Index), "Manage"); - } - - // - // POST: /Manage/DisableTwoFactorAuthentication - [HttpPost] - [ValidateAntiForgeryToken] - public async Task DisableTwoFactorAuthentication() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - await _userManager.SetTwoFactorEnabledAsync(user, false); - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation(2, "User disabled two-factor authentication."); - } - return RedirectToAction(nameof(Index), "Manage"); - } - - // - // GET: /Manage/VerifyPhoneNumber - [HttpGet] - public async Task VerifyPhoneNumber(string phoneNumber) - { - var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); - // Send an SMS to verify the phone number - return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); - } - - // - // POST: /Manage/VerifyPhoneNumber - [HttpPost] - [ValidateAntiForgeryToken] - public async Task VerifyPhoneNumber(VerifyPhoneNumberViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess }); - } - } - // If we got this far, something failed, redisplay the form - ModelState.AddModelError(string.Empty, "Failed to verify phone number"); - return View(model); - } - - // - // GET: /Manage/RemovePhoneNumber - [HttpPost] - [ValidateAntiForgeryToken] - public async Task RemovePhoneNumber() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.SetPhoneNumberAsync(user, null); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess }); - } - } - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); - } - - // - // GET: /Manage/ChangePassword - [HttpGet] - public IActionResult ChangePassword() - { - return View(); - } - - // - // POST: /Manage/ChangePassword - [HttpPost] - [ValidateAntiForgeryToken] - public async Task ChangePassword(ChangePasswordViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation(3, "User changed their password successfully."); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess }); - } - AddErrors(result); - return View(model); - } - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); - } - - // - // GET: /Manage/SetPassword - [HttpGet] - public IActionResult SetPassword() - { - return View(); - } - - // - // POST: /Manage/SetPassword - [HttpPost] - [ValidateAntiForgeryToken] - public async Task SetPassword(SetPasswordViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.AddPasswordAsync(user, model.NewPassword); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess }); - } - AddErrors(result); - return View(model); - } - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); - } - - //GET: /Manage/ManageLogins - [HttpGet] - public async Task ManageLogins(ManageMessageId? message = null) - { - ViewData["StatusMessage"] = - message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." - : message == ManageMessageId.AddLoginSuccess ? "The external login was added." - : message == ManageMessageId.Error ? "An error has occurred." - : ""; - var user = await GetCurrentUserAsync(); - if (user == null) - { - return View("Error"); - } - var userLogins = await _userManager.GetLoginsAsync(user); - var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync(); - var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList(); - ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1; - return View(new ManageLoginsViewModel - { - CurrentLogins = userLogins, - OtherLogins = otherLogins - }); - } - - // - // POST: /Manage/LinkLogin - [HttpPost] - [ValidateAntiForgeryToken] - public IActionResult LinkLogin(string provider) - { - // Request a redirect to the external login provider to link a login for the current user - var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); - return Challenge(properties, provider); - } - - // - // GET: /Manage/LinkLoginCallback - [HttpGet] - public async Task LinkLoginCallback() - { - var user = await GetCurrentUserAsync(); - if (user == null) - { - return View("Error"); - } - var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); - if (info == null) - { - return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error }); - } - var result = await _userManager.AddLoginAsync(user, info); - var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; - return RedirectToAction(nameof(ManageLogins), new { Message = message }); - } - - #region Helpers - - private void AddErrors(IdentityResult result) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - public enum ManageMessageId - { - AddPhoneSuccess, - AddLoginSuccess, - ChangePasswordSuccess, - SetTwoFactorSuccess, - SetPasswordSuccess, - RemoveLoginSuccess, - RemovePhoneSuccess, - Error - } - - private Task GetCurrentUserAsync() - { - return _userManager.GetUserAsync(HttpContext.User); - } - - #endregion - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Startup.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Startup.cs deleted file mode 100644 index 31ce1cc73..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Startup.cs +++ /dev/null @@ -1,126 +0,0 @@ -using ClassifiedAds.IdentityServer.ConfigurationOptions; -using ClassifiedAds.Infrastructure.Monitoring; -using ClassifiedAds.Services.Identity.Entities; -using ClassifiedAds.Services.Identity.Repositories; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.DataProtection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using System.Reflection; - -namespace ClassifiedAds.IdentityServer -{ - public class Startup - { - public Startup(IConfiguration configuration, IWebHostEnvironment env) - { - Configuration = configuration; - - AppSettings = new AppSettings(); - Configuration.Bind(AppSettings); - } - - public IConfiguration Configuration { get; } - - private AppSettings AppSettings { get; set; } - - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - services.Configure(options => - { - options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; - options.KnownNetworks.Clear(); - options.KnownProxies.Clear(); - }); - - if (AppSettings.CookiePolicyOptions?.IsEnabled ?? false) - { - services.Configure(options => - { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.CheckConsentNeeded = context => true; - options.MinimumSameSitePolicy = AppSettings.CookiePolicyOptions.MinimumSameSitePolicy; - options.Secure = AppSettings.CookiePolicyOptions.Secure; - }); - } - - services.AddControllersWithViews(); - services.AddRazorPages(); - - services.AddCors(); - - services.AddDateTimeProvider(); - - services.AddIdentityModule(AppSettings) - .AddApplicationServices(); - - services.AddIdentityServer(options => - { - if (!string.IsNullOrWhiteSpace(AppSettings.IdentityServer.IssuerUri)) - { - options.IssuerUri = AppSettings.IdentityServer.IssuerUri; - } - - options.InputLengthRestrictions.Password = int.MaxValue; - options.InputLengthRestrictions.UserName = int.MaxValue; - }) - .AddSigningCredential(AppSettings.IdentityServer.Certificate.FindCertificate()) - .AddAspNetIdentity() - .AddTokenProviderModule(AppSettings.ConnectionStrings.ClassifiedAds, typeof(Startup).GetTypeInfo().Assembly.GetName().Name); - - services.AddDataProtection() - .PersistKeysToDbContext() - .SetApplicationName("ClassifiedAds"); - - services.AddCaches(AppSettings.Caching) - .AddMonitoringServices(AppSettings.Monitoring); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.MigrateIdServerDb(); - - app.UseForwardedHeaders(); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseStaticFiles(); - - if (AppSettings.CookiePolicyOptions?.IsEnabled ?? false) - { - app.UseCookiePolicy(); - } - - app.UseRouting(); - - app.UseCors( - builder => builder - .AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod() - ); - - app.UseSecurityHeaders(AppSettings.SecurityHeaders); - - app.UseIdentityServer(); - app.UseAuthorization(); - - app.UseMonitoringServices(AppSettings.Monitoring); - - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - endpoints.MapRazorPages(); - }); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/TagHelpers/PickerTagHelper.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/TagHelpers/PickerTagHelper.cs deleted file mode 100644 index 086ee8e36..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/TagHelpers/PickerTagHelper.cs +++ /dev/null @@ -1,151 +0,0 @@ -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Razor.TagHelpers; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text.Json; - -namespace ClassifiedAds.Services.Identity.AuthServer.TagHelpers -{ - [HtmlTargetElement("picker")] - public class PickerTagHelper : TagHelper - { - public string Url { get; set; } - - [Required] - public string Id { get; set; } - - public string SelectedItemsTitle { get; set; } - - public string SearchInputPlaceholder { get; set; } - - public string SearchResultTitle { get; set; } - - public string SuggestedItemsTitle { get; set; } - - public string NoItemSelectedTitle { get; set; } - - public List SelectedItems { get; set; } - - public string SelectedItem { get; set; } - - public string ShowAllItemsTitle { get; set; } - - public int MinSearchText { get; set; } - - public bool MultipleSelect { get; set; } - - public bool AllowItemAlreadySelectedNotification { get; set; } = true; - - public string ItemAlreadySelectedTitle { get; set; } - - public bool AllowSuggestedItems { get; set; } = true; - - public int TopSuggestedItems { get; set; } = 5; - - public bool Required { get; set; } - - public string RequiredMessage { get; set; } - - public override void Process(TagHelperContext context, TagHelperOutput output) - { - AddWrapper(output); - AddComponent(output); - AddHiddenField(output); - } - - private void AddWrapper(TagHelperOutput output) - { - output.TagMode = TagMode.StartTagAndEndTag; - output.TagName = "div"; - output.Attributes.Add("class", "hidden picker"); - output.Attributes.Add(new TagHelperAttribute("data-bind", new HtmlString("css: { hidden: false }"))); - } - - private void AddComponent(TagHelperOutput output) - { - var selectedItems = GetSelectedItems(); - - var component = new - { - name = "picker", - @params = new - { - search = "", - hiddenId = Id, - url = Url, - selectedItemsTitle = SelectedItemsTitle, - allowSuggestedItems = AllowSuggestedItems, - searchResultTitle = SearchResultTitle, - suggestedItemsTitle = SuggestedItemsTitle, - noItemSelectedTitle = NoItemSelectedTitle, - searchInputPlaceholder = SearchInputPlaceholder, - showAllItemsTitle = ShowAllItemsTitle, - selectedItems, - minSearchText = MinSearchText, - topSuggestedItems = TopSuggestedItems, - multipleSelect = MultipleSelect, - allowItemAlreadySelectedNotification = AllowItemAlreadySelectedNotification, - itemAlreadySelectedTitle = ItemAlreadySelectedTitle - } - }; - - var rawPickerHtml = new HtmlString($"
"); - - output.Content.AppendHtml(rawPickerHtml); - } - - /// - /// Get Selected Items - /// - /// - private List GetSelectedItems() - { - return MultipleSelect ? GetSelectedItemsWithRemovedQuotes() : GetSelectedItemWithRemovedQuotes(); - } - - /// - /// Get Selected Items - with removed quotes - /// - /// - private List GetSelectedItemsWithRemovedQuotes() - { - for (var i = 0; i < SelectedItems?.Count; i++) - { - SelectedItems[i] = SelectedItems[i].Replace("'", "").Replace("\"", ""); - } - - return SelectedItems; - } - - /// - /// Get Selected Item - with removed quotes, the picker component expect the collection of items, therefore it is used the list for single value as well - /// - /// - private List GetSelectedItemWithRemovedQuotes() - { - SelectedItem = SelectedItem.Replace("'", "").Replace("\"", ""); - - return string.IsNullOrWhiteSpace(SelectedItem) ? new List() : new List { SelectedItem }; - } - - private void AddHiddenField(TagHelperOutput output) - { - var hiddenField = new TagBuilder("input"); - hiddenField.Attributes.Add("type", "hidden"); - hiddenField.Attributes.Add("id", Id); - hiddenField.Attributes.Add("name", Id); - hiddenField.Attributes.Add("value", string.Empty); - - if (Required) - { - hiddenField.Attributes.Add("required", string.Empty); - hiddenField.Attributes.Add("data-val", "true"); - hiddenField.Attributes.Add("data-val-required", RequiredMessage ?? $"The {Id} field is required."); - hiddenField.Attributes.Add("aria-required", "true"); - } - - output.Content.AppendHtml(hiddenField); - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/TagHelpers/SwitchTagHelper.cs b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/TagHelpers/SwitchTagHelper.cs deleted file mode 100644 index d9aedc1e9..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/TagHelpers/SwitchTagHelper.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace ClassifiedAds.Services.Identity.AuthServer.TagHelpers -{ - [HtmlTargetElement("toggle-button")] - public class SwitchTagHelper : TagHelper - { - public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) - { - var childContent = await output.GetChildContentAsync(); - - var divSlider = new TagBuilder("div"); - divSlider.AddCssClass("slider round"); - - output.TagName = "label"; - output.Attributes.Add("class", "switch"); - output.Content.AppendHtml(childContent); - output.Content.AppendHtml(divSlider); - output.TagMode = TagMode.StartTagAndEndTag; - } - } -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Delete.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Delete.cshtml deleted file mode 100644 index 261d8cd71..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Delete.cshtml +++ /dev/null @@ -1,46 +0,0 @@ -@using ClassifiedAds.Services.Identity.Entities; -@model Role - -
-
- -
-
- -
- -

Delete

- - - -
-
Delete
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Edit.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Edit.cshtml deleted file mode 100644 index 5c3336190..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Edit.cshtml +++ /dev/null @@ -1,63 +0,0 @@ -@using ClassifiedAds.Services.Identity.Entities; -@model Role - -
-
- -
- -
-

Role

-
-
- - -
- -
- - @if (Model.Id != Guid.Empty) - { - -
- -
- } - - - -
-
Role
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Index.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Index.cshtml deleted file mode 100644 index 3a7ca2fde..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Role/Index.cshtml +++ /dev/null @@ -1,49 +0,0 @@ -@using ClassifiedAds.Services.Identity.Entities; -@model IEnumerable - -

Roles

- - Add Role - -
-
- @*@await Html.PartialAsync("Common/Search", new Search { Action = "Search", Controller = "Role" })*@ -
-
- -
-
-
- - - - - - - - - - @foreach (var role in Model) - { - - - - - - } - -
Name
- Edit - Users - @role.Name - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Roles", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
\ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_LoginPartial.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_LoginPartial.cshtml deleted file mode 100644 index 43a954090..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/Shared/_LoginPartial.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@using Microsoft.AspNetCore.Identity -@using ClassifiedAds.Services.Identity.Entities; - -@inject SignInManager SignInManager -@inject UserManager UserManager - -@if (SignInManager.IsSignedIn(User)) -{ - -} -else -{ - -} diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Delete.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Delete.cshtml deleted file mode 100644 index c91ceb41f..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Delete.cshtml +++ /dev/null @@ -1,46 +0,0 @@ -@using ClassifiedAds.Services.Identity.Entities; -@model User - -
-
- -
-
- -
- -

Delete

- - - -
-
Delete
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Index.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Index.cshtml deleted file mode 100644 index cef5e9f53..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Index.cshtml +++ /dev/null @@ -1,55 +0,0 @@ -@using ClassifiedAds.Services.Identity.Entities; -@model IEnumerable - - Add User - -
-
- @*@await Html.PartialAsync("Common/Search", new Search() { Action = "Users", Controller = "Identity" })*@ -
-
- -
-
-
- - - - - - - - - - - - - - @foreach (var user in Model) - { - - - - - - - - - } - -
User IdUser NameEmail
- Edit - - - @user.Id@user.UserName@user.Email - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Users", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ -
-
\ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Profile.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Profile.cshtml deleted file mode 100644 index 6cffef591..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/User/Profile.cshtml +++ /dev/null @@ -1,175 +0,0 @@ -@using ClassifiedAds.Services.Identity.Entities; -@model User - -
-
- -
- -
-

User Profile

-
-
- -
- -
- - @if (Model.Id != Guid.Empty) - { - - - } - - - - -
-
Profile
-
- -
-
- -
-
- -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- - - -
-
- - -
- -
- -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- -
-
- - -
- -
-
- -
-
-
-
-
-
- - -
- -
- -
-
-
-
-
-
-
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/_ViewImports.cshtml b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/_ViewImports.cshtml deleted file mode 100644 index 6c17a67ce..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/Views/_ViewImports.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@using IdentityServer4.Quickstart.UI -@using IdentityServerHost.Quickstart.UI -@using ClassifiedAds.IdentityServer.Models -@using ClassifiedAds.IdentityServer.Manage.Models -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@using StackExchange.Profiling -@addTagHelper *, MiniProfiler.AspNetCore.Mvc -@addTagHelper *, ClassifiedAds.Services.Identity.AuthServer diff --git a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/appsettings.json b/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/appsettings.json deleted file mode 100644 index 6f1549937..000000000 --- a/src/Microservices/Services.Identity/ClassifiedAds.Services.Identity.AuthServer/appsettings.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "ConnectionStrings": { - "ClassifiedAds": "Server=127.0.0.1;Database=ClassifiedAds.Microservices.Identity;User Id=sa;Password=sqladmin123!@#;MultipleActiveResultSets=true" - }, - "IdentityServer": { - "IssuerUri": "", - "Certificate": { - "Thumbprint": null, - "Path": "Certs/classifiedads.identityserver.pfx", - "Password": "password1234" - } - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "System": "Warning", - "Microsoft": "Warning" - }, - "File": { - "MinimumLogEventLevel": "Debug" - }, - "Elasticsearch": { - "IsEnabled": false, - "Host": "http://localhost:9200", - "IndexFormat": "classifiedads", - "MinimumLogEventLevel": "Debug" - } - }, - "Caching": { - "InMemory": { - "SizeLimit": null - }, - "Distributed": { - "Provider": "InMemory", - "InMemory": { - "SizeLimit": null - }, - "Redis": { - "Configuration": "", - "InstanceName": "" - }, - "SqlServer": { - "ConnectionString": "", - "SchemaName": "", - "TableName": "" - } - } - }, - "Monitoring": { - "MiniProfiler": { - "IsEnabled": true - }, - "AzureApplicationInsights": { - "IsEnabled": false - } - }, - "CookiePolicyOptions": { - "IsEnabled": false - }, - "SecurityHeaders": { - "Content-Security-Policy": "frame-ancestors 'none'", - "Feature-Policy": "camera 'none'", - "Referrer-Policy": "strict-origin-when-cross-origin", - "X-Content-Type-Options": "nosniff", - "X-Frame-Options": "DENY", - "X-XSS-Protection": "1; mode=block", - "Cache-Control": "no-cache, no-store, must-revalidate", - "Pragma": "no-cache", - "Expires": "0" - }, - "Interceptors": { - "LoggingInterceptor": true, - "ErrorCatchingInterceptor": false - }, - "ExternalLogin": { - "AzureActiveDirectory": { - "IsEnabled": true, - "Authority": "https://login.microsoftonline.com/", - "ClientId": " -

@ViewData["Title"]

-

You do not have access to this resource.

- diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs deleted file mode 100644 index 2a50edd8e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - public class AccessDeniedModel : PageModel - { - public void OnGet() - { - - } - } -} - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml deleted file mode 100644 index 7984358ac..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@page -@model ConfirmEmailModel -@{ - ViewData["Title"] = "Confirm email"; -} - -

@ViewData["Title"]

- \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs deleted file mode 100644 index cd0485085..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ConfirmEmailModel : PageModel - { - private readonly UserManager _userManager; - - public ConfirmEmailModel(UserManager userManager) - { - _userManager = userManager; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync(string userId, string code) - { - if (userId == null || code == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByIdAsync(userId); - if (user == null) - { - return NotFound($"Unable to load user with ID '{userId}'."); - } - - code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); - var result = await _userManager.ConfirmEmailAsync(user, code); - StatusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email."; - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml deleted file mode 100644 index d7cb204dd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@page -@model ConfirmEmailChangeModel -@{ - ViewData["Title"] = "Confirm email change"; -} - -

@ViewData["Title"]

- \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs deleted file mode 100644 index a40254b85..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ConfirmEmailChange.cshtml.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ConfirmEmailChangeModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public ConfirmEmailChangeModel(UserManager userManager, SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync(string userId, string email, string code) - { - if (userId == null || email == null || code == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByIdAsync(userId); - if (user == null) - { - return NotFound($"Unable to load user with ID '{userId}'."); - } - - code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)); - var result = await _userManager.ChangeEmailAsync(user, email, code); - if (!result.Succeeded) - { - StatusMessage = "Error changing email."; - return Page(); - } - - // In our UI email and user name are one and the same, so when we update the email - // we need to update the user name. - var setUserNameResult = await _userManager.SetUserNameAsync(user, email); - if (!setUserNameResult.Succeeded) - { - StatusMessage = "Error changing user name."; - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Thank you for confirming your email change."; - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml deleted file mode 100644 index b22d25639..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml +++ /dev/null @@ -1,33 +0,0 @@ -@page -@model ExternalLoginModel -@{ - ViewData["Title"] = "Register"; -} - -

@ViewData["Title"]

-

Associate your @Model.ProviderDisplayName account.

-
- -

- You've successfully authenticated with @Model.ProviderDisplayName. - Please enter an email address for this site below and click the Register button to finish - logging in. -

- -
-
-
-
-
- - - -
- -
-
-
- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs deleted file mode 100644 index 86f5da08c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Security.Claims; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ExternalLoginModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - private readonly ILogger _logger; - - public ExternalLoginModel( - SignInManager signInManager, - UserManager userManager, - ILogger logger, - IEmailSender emailSender) - { - _signInManager = signInManager; - _userManager = userManager; - _logger = logger; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ProviderDisplayName { get; set; } - - public string ReturnUrl { get; set; } - - [TempData] - public string ErrorMessage { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public IActionResult OnGetAsync() - { - return RedirectToPage("./Login"); - } - - public IActionResult OnPost(string provider, string returnUrl = null) - { - // Request a redirect to the external login provider. - var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); - return new ChallengeResult(provider, properties); - } - - public async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null) - { - returnUrl = returnUrl ?? Url.Content("~/"); - if (remoteError != null) - { - ErrorMessage = $"Error from external provider: {remoteError}"; - return RedirectToPage("./Login", new {ReturnUrl = returnUrl }); - } - var info = await _signInManager.GetExternalLoginInfoAsync(); - if (info == null) - { - ErrorMessage = "Error loading external login information."; - return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); - } - - // Sign in the user with this external login provider if the user already has a login. - var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor : true); - if (result.Succeeded) - { - _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider); - return LocalRedirect(returnUrl); - } - if (result.IsLockedOut) - { - return RedirectToPage("./Lockout"); - } - else - { - // If the user does not have an account, then ask the user to create an account. - ReturnUrl = returnUrl; - ProviderDisplayName = info.ProviderDisplayName; - if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) - { - Input = new InputModel - { - Email = info.Principal.FindFirstValue(ClaimTypes.Email) - }; - } - return Page(); - } - } - - public async Task OnPostConfirmationAsync(string returnUrl = null) - { - returnUrl = returnUrl ?? Url.Content("~/"); - // Get the information about the user from the external login provider - var info = await _signInManager.GetExternalLoginInfoAsync(); - if (info == null) - { - ErrorMessage = "Error loading external login information during confirmation."; - return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); - } - - if (ModelState.IsValid) - { - var user = new User { UserName = Input.Email, Email = Input.Email }; - - var result = await _userManager.CreateAsync(user); - if (result.Succeeded) - { - result = await _userManager.AddLoginAsync(user, info); - if (result.Succeeded) - { - _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); - - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = userId, code = code }, - protocol: Request.Scheme); - - await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", - $"Please confirm your account by clicking here."); - - // If account confirmation is required, we need to show the link if we don't have a real email sender - if (_userManager.Options.SignIn.RequireConfirmedAccount) - { - return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email }); - } - - await _signInManager.SignInAsync(user, isPersistent: false, info.LoginProvider); - - return LocalRedirect(returnUrl); - } - } - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - ProviderDisplayName = info.ProviderDisplayName; - ReturnUrl = returnUrl; - return Page(); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml deleted file mode 100644 index 1342aa7dd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model ForgotPasswordModel -@{ - ViewData["Title"] = "Forgot your password?"; -} - -

@ViewData["Title"]

-

Enter your email.

-
-
-
-
-
-
- - - -
- -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs deleted file mode 100644 index 1f53575e9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ForgotPasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - - public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) - { - _userManager = userManager; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public async Task OnPostAsync() - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) - { - // Don't reveal that the user does not exist or is not confirmed - return RedirectToPage("./ForgotPasswordConfirmation"); - } - - // For more information on how to enable account confirmation and password reset please - // visit https://go.microsoft.com/fwlink/?LinkID=532713 - var code = await _userManager.GeneratePasswordResetTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ResetPassword", - pageHandler: null, - values: new { area = "Identity", code }, - protocol: Request.Scheme); - - await _emailSender.SendEmailAsync( - Input.Email, - "Reset Password", - $"Please reset your password by clicking here."); - - return RedirectToPage("./ForgotPasswordConfirmation"); - } - - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml deleted file mode 100644 index 9468da0dd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml +++ /dev/null @@ -1,11 +0,0 @@ -@page -@model ForgotPasswordConfirmation -@{ - ViewData["Title"] = "Forgot password confirmation"; -} - -

@ViewData["Title"]

-

- Please check your email to reset your password. -

- diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs deleted file mode 100644 index a80521e0a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ForgotPasswordConfirmation : PageModel - { - public void OnGet() - { - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml deleted file mode 100644 index 4eded8820..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@page -@model LockoutModel -@{ - ViewData["Title"] = "Locked out"; -} - -
-

@ViewData["Title"]

-

This account has been locked out, please try again later.

-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs deleted file mode 100644 index ffbcd0fc4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Lockout.cshtml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LockoutModel : PageModel - { - public void OnGet() - { - - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml deleted file mode 100644 index c11740e97..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml +++ /dev/null @@ -1,85 +0,0 @@ -@page -@model LoginModel - -@{ - ViewData["Title"] = "Log in"; -} - -

@ViewData["Title"]

-
-
-
-
-

Use a local account to log in.

-
-
-
- - - -
-
- - - -
-
-
- -
-
-
- -
- -
-
-
-
-
-

Use another service to log in.

-
- @{ - if ((Model.ExternalLogins?.Count ?? 0) == 0) - { -
-

- There are no external authentication services configured. See this article - for details on setting up this ASP.NET application to support logging in via external services. -

-
- } - else - { -
-
-

- @foreach (var provider in Model.ExternalLogins) - { - - } -

-
-
- } - } -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs deleted file mode 100644 index cf882e7ea..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Login.cshtml.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginModel(SignInManager signInManager, - ILogger logger, - UserManager userManager) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public IList ExternalLogins { get; set; } - - public string ReturnUrl { get; set; } - - [TempData] - public string ErrorMessage { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - if (!string.IsNullOrEmpty(ErrorMessage)) - { - ModelState.AddModelError(string.Empty, ErrorMessage); - } - - returnUrl ??= Url.Content("~/"); - - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - - ReturnUrl = returnUrl; - } - - public async Task OnPostAsync(string returnUrl = null) - { - returnUrl ??= Url.Content("~/"); - - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - - if (ModelState.IsValid) - { - // This doesn't count login failures towards account lockout - // To enable password failures to trigger account lockout, set lockoutOnFailure: true - var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); - if (result.Succeeded) - { - _logger.LogInformation("User logged in."); - return LocalRedirect(returnUrl); - } - if (result.RequiresTwoFactor) - { - return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); - } - if (result.IsLockedOut) - { - _logger.LogWarning("User account locked out."); - return RedirectToPage("./Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return Page(); - } - } - - // If we got this far, something failed, redisplay form - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml deleted file mode 100644 index a9d25fd86..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml +++ /dev/null @@ -1,41 +0,0 @@ -@page -@model LoginWith2faModel -@{ - ViewData["Title"] = "Two-factor authentication"; -} - -

@ViewData["Title"]

-
-

Your login is protected with an authenticator app. Enter your authenticator code below.

-
-
-
- -
-
- - - -
-
-
- -
-
-
- -
-
-
-
-

- Don't have access to your authenticator device? You can - log in with a recovery code. -

- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs deleted file mode 100644 index 6bbc4e856..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginWith2faModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginWith2faModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public bool RememberMe { get; set; } - - public string ReturnUrl { get; set; } - - public class InputModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Authenticator code")] - public string TwoFactorCode { get; set; } - - [Display(Name = "Remember this machine")] - public bool RememberMachine { get; set; } - } - - public async Task OnGetAsync(bool rememberMe, string returnUrl = null) - { - // Ensure the user has gone through the username & password screen first - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - ReturnUrl = returnUrl; - RememberMe = rememberMe; - - return Page(); - } - - public async Task OnPostAsync(bool rememberMe, string returnUrl = null) - { - if (!ModelState.IsValid) - { - return Page(); - } - - returnUrl = returnUrl ?? Url.Content("~/"); - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); - - var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine); - - if (result.Succeeded) - { - _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id); - return LocalRedirect(returnUrl); - } - else if (result.IsLockedOut) - { - _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); - return RedirectToPage("./Lockout"); - } - else - { - _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id); - ModelState.AddModelError(string.Empty, "Invalid authenticator code."); - return Page(); - } - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml deleted file mode 100644 index abd45aaf8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@page -@model LoginWithRecoveryCodeModel -@{ - ViewData["Title"] = "Recovery code verification"; -} - -

@ViewData["Title"]

-
-

- You have requested to log in with a recovery code. This login will not be remembered until you provide - an authenticator app code at log in or disable 2FA and log in again. -

-
-
-
-
-
- - - -
- -
-
-
- - @section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs deleted file mode 100644 index c01c592f7..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginWithRecoveryCodeModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginWithRecoveryCodeModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ReturnUrl { get; set; } - - public class InputModel - { - [BindProperty] - [Required] - [DataType(DataType.Text)] - [Display(Name = "Recovery Code")] - public string RecoveryCode { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - // Ensure the user has gone through the username & password screen first - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - ReturnUrl = returnUrl; - - return Page(); - } - - public async Task OnPostAsync(string returnUrl = null) - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - throw new InvalidOperationException($"Unable to load two-factor authentication user."); - } - - var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); - - var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); - - if (result.Succeeded) - { - _logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id); - return LocalRedirect(returnUrl ?? Url.Content("~/")); - } - if (result.IsLockedOut) - { - _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); - return RedirectToPage("./Lockout"); - } - else - { - _logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id); - ModelState.AddModelError(string.Empty, "Invalid recovery code entered."); - return Page(); - } - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml deleted file mode 100644 index eca33c640..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml +++ /dev/null @@ -1,21 +0,0 @@ -@page -@model LogoutModel -@{ - ViewData["Title"] = "Log out"; -} - -
-

@ViewData["Title"]

- @{ - if (User.Identity.IsAuthenticated) - { -
- -
- } - else - { -

You have successfully logged out of the application.

- } - } -
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs deleted file mode 100644 index 98f7e76e9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Logout.cshtml.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LogoutModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LogoutModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - public void OnGet() - { - } - - public async Task OnPost(string returnUrl = null) - { - await _signInManager.SignOutAsync(); - _logger.LogInformation("User logged out."); - if (returnUrl != null) - { - return LocalRedirect(returnUrl); - } - else - { - return RedirectToPage(); - } - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml deleted file mode 100644 index 69056f90a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml +++ /dev/null @@ -1,36 +0,0 @@ -@page -@model ChangePasswordModel -@{ - ViewData["Title"] = "Change password"; - ViewData["ActivePage"] = ManageNavPages.ChangePassword; -} - -

@ViewData["Title"]

- -
-
-
-
-
- - - -
-
- - - -
-
- - - -
- -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs deleted file mode 100644 index 631930e8a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ChangePasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public ChangePasswordModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public class InputModel - { - [Required] - [DataType(DataType.Password)] - [Display(Name = "Current password")] - public string OldPassword { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var hasPassword = await _userManager.HasPasswordAsync(user); - if (!hasPassword) - { - return RedirectToPage("./SetPassword"); - } - - return Page(); - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); - if (!changePasswordResult.Succeeded) - { - foreach (var error in changePasswordResult.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - _logger.LogInformation("User changed their password successfully."); - StatusMessage = "Your password has been changed."; - - return RedirectToPage(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml deleted file mode 100644 index 3200cd689..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@page -@model DeletePersonalDataModel -@{ - ViewData["Title"] = "Delete Personal Data"; - ViewData["ActivePage"] = ManageNavPages.PersonalData; -} - -

@ViewData["Title"]

- - - -
-
-
- @if (Model.RequirePassword) - { -
- - - -
- } - -
-
- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs deleted file mode 100644 index d77d4ea5f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class DeletePersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public DeletePersonalDataModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - } - - public bool RequirePassword { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - RequirePassword = await _userManager.HasPasswordAsync(user); - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - RequirePassword = await _userManager.HasPasswordAsync(user); - if (RequirePassword) - { - if (!await _userManager.CheckPasswordAsync(user, Input.Password)) - { - ModelState.AddModelError(string.Empty, "Incorrect password."); - return Page(); - } - } - - var result = await _userManager.DeleteAsync(user); - var userId = await _userManager.GetUserIdAsync(user); - if (!result.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'."); - } - - await _signInManager.SignOutAsync(); - - _logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); - - return Redirect("~/"); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml deleted file mode 100644 index 769f09a9c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model Disable2faModel -@{ - ViewData["Title"] = "Disable two-factor authentication (2FA)"; - ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; -} - - -

@ViewData["Title"]

- - - -
-
- -
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs deleted file mode 100644 index 65ac97c91..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class Disable2faModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public Disable2faModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!await _userManager.GetTwoFactorEnabledAsync(user)) - { - throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); - if (!disable2faResult.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); - } - - _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); - StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; - return RedirectToPage("./TwoFactorAuthentication"); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml deleted file mode 100644 index 87470c2f0..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml +++ /dev/null @@ -1,12 +0,0 @@ -@page -@model DownloadPersonalDataModel -@{ - ViewData["Title"] = "Download Your Data"; - ViewData["ActivePage"] = ManageNavPages.PersonalData; -} - -

@ViewData["Title"]

- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs deleted file mode 100644 index 6d94b519e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class DownloadPersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public DownloadPersonalDataModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - _logger.LogInformation("User with ID '{UserId}' asked for their personal data.", _userManager.GetUserId(User)); - - // Only include personal data for download - var personalData = new Dictionary(); - var personalDataProps = typeof(User).GetProperties().Where( - prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); - foreach (var p in personalDataProps) - { - personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); - } - - var logins = await _userManager.GetLoginsAsync(user); - foreach (var l in logins) - { - personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); - } - - Response.Headers.Add("Content-Disposition", "attachment; filename=PersonalData.json"); - return new FileContentResult(JsonSerializer.SerializeToUtf8Bytes(personalData), "application/json"); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml deleted file mode 100644 index db5b5c20a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml +++ /dev/null @@ -1,41 +0,0 @@ -@page -@model EmailModel -@{ - ViewData["Title"] = "Manage Email"; - ViewData["ActivePage"] = ManageNavPages.Email; -} - -

@ViewData["Title"]

- -
-
-
-
-
- - @if (Model.IsEmailConfirmed) - { -
- - -
- } - else - { - - - } -
-
- - - -
- -
-
-
- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs deleted file mode 100644 index 6378069c4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Email.cshtml.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public partial class EmailModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IEmailSender _emailSender; - - public EmailModel( - UserManager userManager, - SignInManager signInManager, - IEmailSender emailSender) - { - _userManager = userManager; - _signInManager = signInManager; - _emailSender = emailSender; - } - - public string Username { get; set; } - - public string Email { get; set; } - - public bool IsEmailConfirmed { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - [Display(Name = "New email")] - public string NewEmail { get; set; } - } - - private async Task LoadAsync(User user) - { - var email = await _userManager.GetEmailAsync(user); - Email = email; - - Input = new InputModel - { - NewEmail = email, - }; - - IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await LoadAsync(user); - return Page(); - } - - public async Task OnPostChangeEmailAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadAsync(user); - return Page(); - } - - var email = await _userManager.GetEmailAsync(user); - if (Input.NewEmail != email) - { - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); - var callbackUrl = Url.Page( - "/Account/ConfirmEmailChange", - pageHandler: null, - values: new { userId = userId, email = Input.NewEmail, code = code }, - protocol: Request.Scheme); - await _emailSender.SendEmailAsync( - Input.NewEmail, - "Confirm your email", - $"Please confirm your account by clicking here."); - - StatusMessage = "Confirmation link to change email sent. Please check your email."; - return RedirectToPage(); - } - - StatusMessage = "Your email is unchanged."; - return RedirectToPage(); - } - - public async Task OnPostSendVerificationEmailAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadAsync(user); - return Page(); - } - - var userId = await _userManager.GetUserIdAsync(user); - var email = await _userManager.GetEmailAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = userId, code = code }, - protocol: Request.Scheme); - await _emailSender.SendEmailAsync( - email, - "Confirm your email", - $"Please confirm your account by clicking here."); - - StatusMessage = "Verification email sent. Please check your email."; - return RedirectToPage(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml deleted file mode 100644 index 53c9355f1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml +++ /dev/null @@ -1,63 +0,0 @@ -@page -@model EnableAuthenticatorModel -@{ - ViewData["Title"] = "Configure authenticator app"; - ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; -} - - -

@ViewData["Title"]

-
-

To use an authenticator app go through the following steps:

-
    -
  1. -

    - Download a two-factor authenticator app like Microsoft Authenticator for - Android and - iOS or - Google Authenticator for - Android and - iOS. -

    -
  2. -
  3. -

    Scan the QR Code or enter this key @Model.SharedKey into your two factor authenticator app. Spaces and casing do not matter.

    - -
    -
    -
  4. -
  5. -

    - Once you have scanned the QR code or input the key above, your two factor authentication app will provide you - with a unique code. Enter the code in the confirmation box below. -

    -
    -
    -
    -
    - - - -
    - -
    -
    -
    -
    -
  6. -
-
- -@section Scripts { - - - - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs deleted file mode 100644 index 02e573236..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Collections.Generic; -using System.Text; -using System.Text.Encodings.Web; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class EnableAuthenticatorModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - private readonly UrlEncoder _urlEncoder; - - private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; - - public EnableAuthenticatorModel( - UserManager userManager, - ILogger logger, - UrlEncoder urlEncoder) - { - _userManager = userManager; - _logger = logger; - _urlEncoder = urlEncoder; - } - - public string SharedKey { get; set; } - - public string AuthenticatorUri { get; set; } - - [TempData] - public string[] RecoveryCodes { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Verification Code")] - public string Code { get; set; } - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await LoadSharedKeyAndQrCodeUriAsync(user); - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadSharedKeyAndQrCodeUriAsync(user); - return Page(); - } - - // Strip spaces and hypens - var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); - - var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync( - user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); - - if (!is2faTokenValid) - { - ModelState.AddModelError("Input.Code", "Verification code is invalid."); - await LoadSharedKeyAndQrCodeUriAsync(user); - return Page(); - } - - await _userManager.SetTwoFactorEnabledAsync(user, true); - var userId = await _userManager.GetUserIdAsync(user); - _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); - - StatusMessage = "Your authenticator app has been verified."; - - if (await _userManager.CountRecoveryCodesAsync(user) == 0) - { - var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); - RecoveryCodes = recoveryCodes.ToArray(); - return RedirectToPage("./ShowRecoveryCodes"); - } - else - { - return RedirectToPage("./TwoFactorAuthentication"); - } - } - - private async Task LoadSharedKeyAndQrCodeUriAsync(User user) - { - // Load the authenticator key & QR code URI to display on the form - var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); - if (string.IsNullOrEmpty(unformattedKey)) - { - await _userManager.ResetAuthenticatorKeyAsync(user); - unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user); - } - - SharedKey = FormatKey(unformattedKey); - - var email = await _userManager.GetEmailAsync(user); - AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey); - } - - private string FormatKey(string unformattedKey) - { - var result = new StringBuilder(); - int currentPosition = 0; - while (currentPosition + 4 < unformattedKey.Length) - { - result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" "); - currentPosition += 4; - } - if (currentPosition < unformattedKey.Length) - { - result.Append(unformattedKey.Substring(currentPosition)); - } - - return result.ToString().ToLowerInvariant(); - } - - private string GenerateQrCodeUri(string email, string unformattedKey) - { - return string.Format( - AuthenticatorUriFormat, - _urlEncoder.Encode("ClassifiedAds.IdentityServer"), - _urlEncoder.Encode(email), - unformattedKey); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml deleted file mode 100644 index bdc9813f8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml +++ /dev/null @@ -1,53 +0,0 @@ -@page -@model ExternalLoginsModel -@{ - ViewData["Title"] = "Manage your external logins"; - ViewData["ActivePage"] = ManageNavPages.ExternalLogins; -} - - -@if (Model.CurrentLogins?.Count > 0) -{ -

Registered Logins

- - - @foreach (var login in Model.CurrentLogins) - { - - - - - } - -
@login.ProviderDisplayName - @if (Model.ShowRemoveButton) - { -
-
- - - -
-
- } - else - { - @:   - } -
-} -@if (Model.OtherLogins?.Count > 0) -{ -

Add another service to log in.

-
- -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs deleted file mode 100644 index 12230e93b..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ExternalLoginsModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public ExternalLoginsModel( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - public IList CurrentLogins { get; set; } - - public IList OtherLogins { get; set; } - - public bool ShowRemoveButton { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - CurrentLogins = await _userManager.GetLoginsAsync(user); - OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()) - .Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider)) - .ToList(); - ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1; - return Page(); - } - - public async Task OnPostRemoveLoginAsync(string loginProvider, string providerKey) - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey); - if (!result.Succeeded) - { - StatusMessage = "The external login was not removed."; - return RedirectToPage(); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "The external login was removed."; - return RedirectToPage(); - } - - public async Task OnPostLinkLoginAsync(string provider) - { - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - // Request a redirect to the external login provider to link a login for the current user - var redirectUrl = Url.Page("./ExternalLogins", pageHandler: "LinkLoginCallback"); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); - return new ChallengeResult(provider, properties); - } - - public async Task OnGetLinkLoginCallbackAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - var info = await _signInManager.GetExternalLoginInfoAsync(user.Id.ToString()); - if (info == null) - { - throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'."); - } - - var result = await _userManager.AddLoginAsync(user, info); - if (!result.Succeeded) - { - StatusMessage = "The external login was not added. External logins can only be associated with one account."; - return RedirectToPage(); - } - - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - StatusMessage = "The external login was added."; - return RedirectToPage(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml deleted file mode 100644 index 4a0bc7480..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml +++ /dev/null @@ -1,27 +0,0 @@ -@page -@model GenerateRecoveryCodesModel -@{ - ViewData["Title"] = "Generate two-factor authentication (2FA) recovery codes"; - ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; -} - - -

@ViewData["Title"]

- -
-
- -
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs deleted file mode 100644 index 5d3edda2b..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class GenerateRecoveryCodesModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public GenerateRecoveryCodesModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - [TempData] - public string[] RecoveryCodes { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); - if (!isTwoFactorEnabled) - { - var userId = await _userManager.GetUserIdAsync(user); - throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user); - var userId = await _userManager.GetUserIdAsync(user); - if (!isTwoFactorEnabled) - { - throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled."); - } - - var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); - RecoveryCodes = recoveryCodes.ToArray(); - - _logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); - StatusMessage = "You have generated new recovery codes."; - return RedirectToPage("./ShowRecoveryCodes"); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml deleted file mode 100644 index 732663d98..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml +++ /dev/null @@ -1,30 +0,0 @@ -@page -@model IndexModel -@{ - ViewData["Title"] = "Profile"; - ViewData["ActivePage"] = ManageNavPages.Index; -} - -

@ViewData["Title"]

- -
-
-
-
-
- - -
-
- - - -
- -
-
-
- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs deleted file mode 100644 index 370e0cac1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public partial class IndexModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public IndexModel( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - public string Username { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } - } - - private async Task LoadAsync(User user) - { - var userName = await _userManager.GetUserNameAsync(user); - var phoneNumber = await _userManager.GetPhoneNumberAsync(user); - - Username = userName; - - Input = new InputModel - { - PhoneNumber = phoneNumber - }; - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await LoadAsync(user); - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!ModelState.IsValid) - { - await LoadAsync(user); - return Page(); - } - - var phoneNumber = await _userManager.GetPhoneNumberAsync(user); - if (Input.PhoneNumber != phoneNumber) - { - var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); - if (!setPhoneResult.Succeeded) - { - StatusMessage = "Unexpected error when trying to set phone number."; - return RedirectToPage(); - } - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your profile has been updated"; - return RedirectToPage(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs deleted file mode 100644 index c033438fe..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public static class ManageNavPages - { - public static string Index => "Index"; - - public static string Email => "Email"; - - public static string ChangePassword => "ChangePassword"; - - public static string DownloadPersonalData => "DownloadPersonalData"; - - public static string DeletePersonalData => "DeletePersonalData"; - - public static string ExternalLogins => "ExternalLogins"; - - public static string PersonalData => "PersonalData"; - - public static string TwoFactorAuthentication => "TwoFactorAuthentication"; - - public static string IndexNavClass(ViewContext viewContext) => PageNavClass(viewContext, Index); - - public static string EmailNavClass(ViewContext viewContext) => PageNavClass(viewContext, Email); - - public static string ChangePasswordNavClass(ViewContext viewContext) => PageNavClass(viewContext, ChangePassword); - - public static string DownloadPersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DownloadPersonalData); - - public static string DeletePersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, DeletePersonalData); - - public static string ExternalLoginsNavClass(ViewContext viewContext) => PageNavClass(viewContext, ExternalLogins); - - public static string PersonalDataNavClass(ViewContext viewContext) => PageNavClass(viewContext, PersonalData); - - public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) => PageNavClass(viewContext, TwoFactorAuthentication); - - private static string PageNavClass(ViewContext viewContext, string page) - { - var activePage = viewContext.ViewData["ActivePage"] as string - ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); - return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml deleted file mode 100644 index 0aa5213d2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml +++ /dev/null @@ -1,27 +0,0 @@ -@page -@model PersonalDataModel -@{ - ViewData["Title"] = "Personal Data"; - ViewData["ActivePage"] = ManageNavPages.PersonalData; -} - -

@ViewData["Title"]

- -
-
-

Your account contains personal data that you have given us. This page allows you to download or delete that data.

-

- Deleting this data will permanently remove your account, and this cannot be recovered. -

-
- -
-

- Delete -

-
-
- -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs deleted file mode 100644 index 015b2ee2d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class PersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public PersonalDataModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - return Page(); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml deleted file mode 100644 index ddd368ef0..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml +++ /dev/null @@ -1,24 +0,0 @@ -@page -@model ResetAuthenticatorModel -@{ - ViewData["Title"] = "Reset authenticator key"; - ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; -} - - -

@ViewData["Title"]

- -
-
- -
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs deleted file mode 100644 index f9b742943..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ResetAuthenticatorModel : PageModel - { - UserManager _userManager; - private readonly SignInManager _signInManager; - ILogger _logger; - - public ResetAuthenticatorModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{user.Id}'."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID 'user.Id'."); - } - - await _userManager.SetTwoFactorEnabledAsync(user, false); - await _userManager.ResetAuthenticatorKeyAsync(user); - _logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id); - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key."; - - return RedirectToPage("./EnableAuthenticator"); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml deleted file mode 100644 index 1d10b0848..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml +++ /dev/null @@ -1,35 +0,0 @@ -@page -@model SetPasswordModel -@{ - ViewData["Title"] = "Set password"; - ViewData["ActivePage"] = ManageNavPages.ChangePassword; -} - -

Set your password

- -

- You do not have a local username/password for this site. Add a local - account so you can log in without an external login. -

-
-
-
-
-
- - - -
-
- - - -
- -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs deleted file mode 100644 index 1c0e0a899..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class SetPasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - - public SetPasswordModel( - UserManager userManager, - SignInManager signInManager) - { - _userManager = userManager; - _signInManager = signInManager; - } - - [BindProperty] - public InputModel Input { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public class InputModel - { - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var hasPassword = await _userManager.HasPasswordAsync(user); - - if (hasPassword) - { - return RedirectToPage("./ChangePassword"); - } - - return Page(); - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var addPasswordResult = await _userManager.AddPasswordAsync(user, Input.NewPassword); - if (!addPasswordResult.Succeeded) - { - foreach (var error in addPasswordResult.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your password has been set."; - - return RedirectToPage(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml deleted file mode 100644 index 52225d61d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model ShowRecoveryCodesModel -@{ - ViewData["Title"] = "Recovery codes"; - ViewData["ActivePage"] = "TwoFactorAuthentication"; -} - - -

@ViewData["Title"]

- -
-
- @for (var row = 0; row < Model.RecoveryCodes.Length; row += 2) - { - @Model.RecoveryCodes[row] @Model.RecoveryCodes[row + 1]
- } -
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs deleted file mode 100644 index a0c08c28a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/ShowRecoveryCodes.cshtml.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class ShowRecoveryCodesModel : PageModel - { - [TempData] - public string[] RecoveryCodes { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public IActionResult OnGet() - { - if (RecoveryCodes == null || RecoveryCodes.Length == 0) - { - return RedirectToPage("./TwoFactorAuthentication"); - } - - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml deleted file mode 100644 index 8b301f4ad..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml +++ /dev/null @@ -1,57 +0,0 @@ -@page -@model TwoFactorAuthenticationModel -@{ - ViewData["Title"] = "Two-factor authentication (2FA)"; - ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; -} - - -

@ViewData["Title"]

-@if (Model.Is2faEnabled) -{ - if (Model.RecoveryCodesLeft == 0) - { -
- You have no recovery codes left. -

You must generate a new set of recovery codes before you can log in with a recovery code.

-
- } - else if (Model.RecoveryCodesLeft == 1) - { -
- You have 1 recovery code left. -

You can generate a new set of recovery codes.

-
- } - else if (Model.RecoveryCodesLeft <= 3) - { -
- You have @Model.RecoveryCodesLeft recovery codes left. -

You should generate a new set of recovery codes.

-
- } - - if (Model.IsMachineRemembered) - { -
- -
- } - Disable 2FA - Reset recovery codes -} - -
Authenticator app
-@if (!Model.HasAuthenticator) -{ - Add authenticator app -} -else -{ - Setup authenticator app - Reset authenticator app -} - -@section Scripts { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs deleted file mode 100644 index 173104f52..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage -{ - public class TwoFactorAuthenticationModel : PageModel - { - private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}"; - - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public TwoFactorAuthenticationModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - public bool HasAuthenticator { get; set; } - - public int RecoveryCodesLeft { get; set; } - - [BindProperty] - public bool Is2faEnabled { get; set; } - - public bool IsMachineRemembered { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - HasAuthenticator = await _userManager.GetAuthenticatorKeyAsync(user) != null; - Is2faEnabled = await _userManager.GetTwoFactorEnabledAsync(user); - IsMachineRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user); - RecoveryCodesLeft = await _userManager.CountRecoveryCodesAsync(user); - - return Page(); - } - - public async Task OnPost() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - await _signInManager.ForgetTwoFactorClientAsync(); - StatusMessage = "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code."; - return RedirectToPage(); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml deleted file mode 100644 index 66d5ecd14..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_Layout.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@{ - Layout = "/Areas/Identity/Pages/_Layout.cshtml"; -} - -

Manage your account

- -
-

Change your account settings

-
-
-
- -
-
- @RenderBody() -
-
-
- -@section Scripts { - @RenderSection("Scripts", required: false) -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml deleted file mode 100644 index 5a3f212bf..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml +++ /dev/null @@ -1,15 +0,0 @@ -@inject SignInManager SignInManager -@{ - var hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); -} - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml deleted file mode 100644 index e99684130..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@model string - -@if (!String.IsNullOrEmpty(Model)) -{ - var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml deleted file mode 100644 index 42eafe45a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml +++ /dev/null @@ -1 +0,0 @@ -@using ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account.Manage diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml deleted file mode 100644 index a2b2b00ac..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml +++ /dev/null @@ -1,67 +0,0 @@ -@page -@model RegisterModel -@{ - ViewData["Title"] = "Register"; -} - -

@ViewData["Title"]

- -
-
-
-

Create a new account.

-
-
-
- - - -
-
- - - -
-
- - - -
- -
-
-
-
-

Use another service to register.

-
- @{ - if ((Model.ExternalLogins?.Count ?? 0) == 0) - { -
-

- There are no external authentication services configured. See this article - for details on setting up this ASP.NET application to support logging in via external services. -

-
- } - else - { -
-
-

- @foreach (var provider in Model.ExternalLogins) - { - - } -

-
-
- } - } -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs deleted file mode 100644 index 8c2231d14..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/Register.cshtml.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Extensions.Logging; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class RegisterModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly UserManager _userManager; - private readonly ILogger _logger; - private readonly IEmailSender _emailSender; - - public RegisterModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger, - IEmailSender emailSender) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ReturnUrl { get; set; } - - public IList ExternalLogins { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - ReturnUrl = returnUrl; - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - } - - public async Task OnPostAsync(string returnUrl = null) - { - returnUrl ??= Url.Content("~/"); - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - if (ModelState.IsValid) - { - var user = new User { UserName = Input.Email, Email = Input.Email }; - var result = await _userManager.CreateAsync(user, Input.Password); - if (result.Succeeded) - { - _logger.LogInformation("User created a new account with password."); - - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl }, - protocol: Request.Scheme); - - await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", - $"Please confirm your account by clicking here."); - - if (_userManager.Options.SignIn.RequireConfirmedAccount) - { - return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); - } - else - { - await _signInManager.SignInAsync(user, isPersistent: false); - return LocalRedirect(returnUrl); - } - } - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - // If we got this far, something failed, redisplay form - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml deleted file mode 100644 index f6ac19370..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml +++ /dev/null @@ -1,22 +0,0 @@ -@page -@model RegisterConfirmationModel -@{ - ViewData["Title"] = "Register confirmation"; -} - -

@ViewData["Title"]

-@{ - if (@Model.DisplayConfirmAccountLink) - { -

- This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. - Normally this would be emailed: Click here to confirm your account -

- } - else - { -

- Please check your email to confirm your account. -

- } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs deleted file mode 100644 index 3d363a37b..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using System.Text; -using System.Threading.Tasks; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class RegisterConfirmationModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _sender; - - public RegisterConfirmationModel(UserManager userManager, IEmailSender sender) - { - _userManager = userManager; - _sender = sender; - } - - public string Email { get; set; } - - public bool DisplayConfirmAccountLink { get; set; } - - public string EmailConfirmationUrl { get; set; } - - public async Task OnGetAsync(string email, string returnUrl = null) - { - if (email == null) - { - return RedirectToPage("/Index"); - } - - var user = await _userManager.FindByEmailAsync(email); - if (user == null) - { - return NotFound($"Unable to load user with email '{email}'."); - } - - Email = email; - // Once you add a real email sender, you should remove this code that lets you confirm the account - DisplayConfirmAccountLink = true; - if (DisplayConfirmAccountLink) - { - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - EmailConfirmationUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl }, - protocol: Request.Scheme); - } - - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml deleted file mode 100644 index 8578c2321..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@page -@model ResendEmailConfirmationModel -@{ - ViewData["Title"] = "Resend email confirmation"; -} - -

@ViewData["Title"]

-

Enter your email.

-
-
-
-
-
-
- - - -
- -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs deleted file mode 100644 index 27e5aaeb9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResendEmailConfirmation.cshtml.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; - -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ResendEmailConfirmationModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - - public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender) - { - _userManager = userManager; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public void OnGet() - { - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null) - { - ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email."); - return Page(); - } - - var userId = await _userManager.GetUserIdAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { userId = userId, code = code }, - protocol: Request.Scheme); - await _emailSender.SendEmailAsync( - Input.Email, - "Confirm your email", - $"Please confirm your account by clicking here."); - - ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email."); - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml deleted file mode 100644 index 5a28bd1b9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml +++ /dev/null @@ -1,37 +0,0 @@ -@page -@model ResetPasswordModel -@{ - ViewData["Title"] = "Reset password"; -} - -

@ViewData["Title"]

-

Reset your password.

-
-
-
-
-
- -
- - - -
-
- - - -
-
- - - -
- -
-
-
- -@section Scripts { - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs deleted file mode 100644 index 5ba26f3b4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using ClassifiedAds.Modules.Identity.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.WebUtilities; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ResetPasswordModel : PageModel - { - private readonly UserManager _userManager; - - public ResetPasswordModel(UserManager userManager) - { - _userManager = userManager; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - - public string Code { get; set; } - } - - public IActionResult OnGet(string code = null) - { - if (code == null) - { - return BadRequest("A code must be supplied for password reset."); - } - else - { - Input = new InputModel - { - Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) - }; - return Page(); - } - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null) - { - // Don't reveal that the user does not exist - return RedirectToPage("./ResetPasswordConfirmation"); - } - - var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); - if (result.Succeeded) - { - return RedirectToPage("./ResetPasswordConfirmation"); - } - - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml deleted file mode 100644 index a9972d431..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@page -@model ResetPasswordConfirmationModel -@{ - ViewData["Title"] = "Reset password confirmation"; -} - -

@ViewData["Title"]

-

- Your password has been reset. Please click here to log in. -

diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs deleted file mode 100644 index 3b4e191cb..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ResetPasswordConfirmationModel : PageModel - { - public void OnGet() - { - - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml deleted file mode 100644 index e99684130..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_StatusMessage.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@model string - -@if (!String.IsNullOrEmpty(Model)) -{ - var statusMessageClass = Model.StartsWith("Error") ? "danger" : "success"; - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_ViewImports.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_ViewImports.cshtml deleted file mode 100644 index 865214e7d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Account/_ViewImports.cshtml +++ /dev/null @@ -1 +0,0 @@ -@using ClassifiedAds.IdentityServer.Areas.Identity.Pages.Account \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml deleted file mode 100644 index b1f3143a4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@page -@model ErrorModel -@{ - ViewData["Title"] = "Error"; -} - -

Error.

-

An error occurred while processing your request.

- -@if (Model.ShowRequestId) -{ -

- Request ID: @Model.RequestId -

-} - -

Development Mode

-

- Swapping to Development environment will display more detailed information about the error that occurred. -

-

- Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. -

diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml.cs deleted file mode 100644 index efeac0e21..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/Error.cshtml.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; - -namespace ClassifiedAds.IdentityServer.Areas.Identity.Pages -{ - [AllowAnonymous] - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public class ErrorModel : PageModel - { - public string RequestId { get; set; } - - public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); - - public void OnGet() - { - RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml deleted file mode 100644 index bacc0ae43..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml deleted file mode 100644 index 98b360c0e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewImports.cshtml +++ /dev/null @@ -1,5 +0,0 @@ -@using Microsoft.AspNetCore.Identity -@using ClassifiedAds.IdentityServer.Areas.Identity -@using ClassifiedAds.IdentityServer.Areas.Identity.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@using ClassifiedAds.Modules.Identity.Entities diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewStart.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewStart.cshtml deleted file mode 100644 index 94fd419ef..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Areas/Identity/Pages/_ViewStart.cshtml +++ /dev/null @@ -1,4 +0,0 @@ - -@{ - Layout = "/Views/Shared/_Layout.cshtml"; -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Certs/CreateSelfSignedCertificate.ps1 b/src/ModularMonolith/ClassifiedAds.IdentityServer/Certs/CreateSelfSignedCertificate.ps1 deleted file mode 100644 index 3a011cadb..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Certs/CreateSelfSignedCertificate.ps1 +++ /dev/null @@ -1,6 +0,0 @@ -$date_now = Get-Date -$extended_date = $date_now.AddYears(3) -$cert = New-SelfSignedCertificate -certstorelocation cert:\localmachine\my -dnsname classifiedads.identityserver -notafter $extended_date -$pwd = ConvertTo-SecureString -String 'password1234' -Force -AsPlainText -$path = 'cert:\localMachine\my\' + $cert.thumbprint -Export-PfxCertificate -cert $path -FilePath classifiedads.identityserver.pfx -Password $pwd \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Certs/classifiedads.identityserver.pfx b/src/ModularMonolith/ClassifiedAds.IdentityServer/Certs/classifiedads.identityserver.pfx deleted file mode 100644 index af8891bfa..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/Certs/classifiedads.identityserver.pfx and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj b/src/ModularMonolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj deleted file mode 100644 index 3b52fd7bb..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net6.0 - 1ff63a22-ad40-4b86-9dae-1da800451a0f - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - Always - - - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs deleted file mode 100644 index 85c3ed268..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/AppSettings.cs +++ /dev/null @@ -1,30 +0,0 @@ -using ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin; -using ClassifiedAds.Infrastructure.Caching; -using ClassifiedAds.Infrastructure.Interceptors; -using ClassifiedAds.Infrastructure.Logging; -using ClassifiedAds.Infrastructure.Monitoring; -using System.Collections.Generic; - -namespace ClassifiedAds.IdentityServer.ConfigurationOptions -{ - public class AppSettings - { - public IdentityServerOptions IdentityServer { get; set; } - - public LoggingOptions Logging { get; set; } - - public CachingOptions Caching { get; set; } - - public MonitoringOptions Monitoring { get; set; } - - public Dictionary SecurityHeaders { get; set; } - - public InterceptorsOptions Interceptors { get; set; } - - public ExternalLoginOptions ExternalLogin { get; set; } - - public CookiePolicyOptions CookiePolicyOptions { get; set; } - - public ModulesOptions Modules { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/CookiePolicyOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/CookiePolicyOptions.cs deleted file mode 100644 index 0023eb4d3..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/CookiePolicyOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace ClassifiedAds.IdentityServer.ConfigurationOptions -{ - public class CookiePolicyOptions: Microsoft.AspNetCore.Builder.CookiePolicyOptions - { - public bool IsEnabled { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs deleted file mode 100644 index dbfea9ca9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/AzureActiveDirectoryOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin -{ - public class AzureActiveDirectoryOptions - { - public bool IsEnabled { get; set; } - - public string Authority { get; set; } - - public string ClientId { get; set; } - - public string ClientSecret { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs deleted file mode 100644 index fa4b83f99..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/ExternalLoginOptions.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin -{ - public class ExternalLoginOptions - { - public AzureActiveDirectoryOptions AzureActiveDirectory { get; set; } - - public MicrosoftOptions Microsoft { get; set; } - - public GoogleOptions Google { get; set; } - - public FacebookOptions Facebook { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs deleted file mode 100644 index 8f45346e0..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/FacebookOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin -{ - public class FacebookOptions - { - public bool IsEnabled { get; set; } - - public string AppId { get; set; } - - public string AppSecret { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs deleted file mode 100644 index 21a3cdf0b..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/GoogleOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin -{ - public class GoogleOptions - { - public bool IsEnabled { get; set; } - - public string ClientId { get; set; } - - public string ClientSecret { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs deleted file mode 100644 index 7ef122f5c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ExternalLogin/MicrosoftOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace ClassifiedAds.IdentityServer.ConfigurationOptions.ExternalLogin -{ - public class MicrosoftOptions - { - public bool IsEnabled { get; set; } - - public string ClientId { get; set; } - - public string ClientSecret { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/IdentityServerOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/IdentityServerOptions.cs deleted file mode 100644 index f5930dc74..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/IdentityServerOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using CryptographyHelper.Certificates; - -namespace ClassifiedAds.IdentityServer.ConfigurationOptions -{ - public class IdentityServerOptions : IdentityServer4.Configuration.IdentityServerOptions - { - public CertificateOption Certificate { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ModulesOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ModulesOptions.cs deleted file mode 100644 index cdd488814..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/ConfigurationOptions/ModulesOptions.cs +++ /dev/null @@ -1,15 +0,0 @@ -using ClassifiedAds.Modules.Auth.ConfigurationOptions; -using ClassifiedAds.Modules.Identity.ConfigurationOptions; -using ClassifiedAds.Modules.Notification.ConfigurationOptions; - -namespace ClassifiedAds.IdentityServer.ConfigurationOptions -{ - public class ModulesOptions - { - public AuthModuleOptions Auth { get; set; } - - public IdentityModuleOptions Identity { get; set; } - - public NotificationModuleOptions Notification { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ApiResourceController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ApiResourceController.cs deleted file mode 100644 index 09cb4fac1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ApiResourceController.cs +++ /dev/null @@ -1,245 +0,0 @@ -using ClassifiedAds.IdentityServer.Models.ApiResourceModels; -using IdentityServer4.EntityFramework.DbContexts; -using IdentityServer4.EntityFramework.Entities; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class ApiResourceController : Controller - { - private readonly ConfigurationDbContext _configurationDbContext; - - public ApiResourceController(ConfigurationDbContext configurationDbContext) - { - _configurationDbContext = configurationDbContext; - } - - public IActionResult Index() - { - var apis = _configurationDbContext.ApiResources.ToList(); - return View(apis); - } - - public IActionResult Add() - { - return View(nameof(Edit), new ApiResourceModel()); - } - - public IActionResult Edit(int id) - { - var identity = _configurationDbContext.ApiResources - .Include(x => x.UserClaims) - .FirstOrDefault(x => x.Id == id); - return View(ApiResourceModel.FromEntity(identity)); - } - - [HttpPost] - public IActionResult Edit(ApiResourceModel model) - { - ApiResource api; - if (model.Id == 0) - { - api = new ApiResource - { - UserClaims = new List(), - }; - _configurationDbContext.ApiResources.Add(api); - } - else - { - api = _configurationDbContext.ApiResources - .Include(x => x.UserClaims) - .FirstOrDefault(x => x.Id == model.Id); - api.UserClaims.Clear(); - } - - model.UpdateEntity(api); - - if (!string.IsNullOrEmpty(model.UserClaimsItems)) - { - var userClaims = JsonSerializer.Deserialize>(model.UserClaimsItems); - - api.UserClaims.AddRange(userClaims.Select(x => new ApiResourceClaim - { - Type = x, - })); - } - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Edit), new { id = api.Id }); - } - - public IActionResult Delete(int id) - { - var api = _configurationDbContext.ApiResources - .FirstOrDefault(x => x.Id == id); - return View(ApiResourceModel.FromEntity(api)); - } - - [HttpPost] - public IActionResult Delete(ApiResourceModel model) - { - var api = _configurationDbContext.ApiResources - .FirstOrDefault(x => x.Id == model.Id); - - _configurationDbContext.ApiResources.Remove(api); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Index)); - } - - public IActionResult Properties(int id) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == id); - return View(PropertiesModel.FromEntity(api)); - } - - [HttpPost] - public IActionResult AddProperty(PropertiesModel model) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.ApiResourceId); - - api.Properties.Add(new ApiResourceProperty - { - Key = model.Key, - Value = model.Value, - }); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Properties), new { id = api.Id }); - } - - [HttpGet] - public IActionResult DeleteProperty(int id) - { - var prop = _configurationDbContext.Set() - .Include(x => x.ApiResource) - .FirstOrDefault(x => x.Id == id); - return View(ApiResourcePropertyModel.FromEntity(prop)); - } - - [HttpPost] - public IActionResult DeleteProperty(ApiResourcePropertyModel model) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.ApiResource.Id); - var prop = api.Properties.FirstOrDefault(x => x.Id == model.Id); - api.Properties.Remove(prop); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Properties), new { id = api.Id }); - } - - public IActionResult Secrets(int id) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Secrets) - .FirstOrDefault(x => x.Id == id); - return View(SecretsModel.FromEntity(api)); - } - - [HttpPost] - public IActionResult Secrets(SecretsModel model) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Secrets) - .FirstOrDefault(x => x.Id == model.ApiResourceId); - - var secret = new ApiResourceSecret - { - Created = DateTime.UtcNow, - }; - - model.HashSecret(); - model.UpdateEntity(secret); - api.Secrets.Add(secret); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Secrets), new { id = api.Id }); - } - - [HttpGet] - public IActionResult DeleteSecret(int id) - { - var secret = _configurationDbContext.Set() - .Include(x => x.ApiResource) - .FirstOrDefault(x => x.Id == id); - return View(SecretModel.FromEntity(secret)); - } - - [HttpPost] - public IActionResult DeleteSecret(SecretModel model) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Secrets) - .FirstOrDefault(x => x.Id == model.ApiResourceId); - var secret = api.Secrets.FirstOrDefault(x => x.Id == model.Id); - api.Secrets.Remove(secret); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Secrets), new { id = api.Id }); - } - - public IActionResult Scopes(int id) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Scopes) - .FirstOrDefault(x => x.Id == id); - - var apiScopes = _configurationDbContext.ApiScopes.ToList(); - var model = ScopesModel.FromEntity(api); - model.ApiScopes = apiScopes; - - return View(model); - } - - [HttpPost] - public IActionResult AddScope(ScopeModel model) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Scopes) - .FirstOrDefault(x => x.Id == model.ApiResourceId); - - api.Scopes.Add(new ApiResourceScope { Scope = model.Scope }); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Scopes), new { id = api.Id }); - } - - [HttpGet] - public IActionResult DeleteScope(int apiId, int scopeId) - { - var scope = _configurationDbContext.Set() - .Include(x => x.ApiResource) - .FirstOrDefault(x => x.Id == scopeId); - return View(ScopeModel.FromEntity(scope)); - } - - [HttpPost] - public IActionResult DeleteScope(ScopeModel model) - { - var api = _configurationDbContext.ApiResources - .Include(x => x.Scopes) - .FirstOrDefault(x => x.Id == model.ApiResourceId); - var scope = api.Scopes.FirstOrDefault(x => x.Id == model.Id); - api.Scopes.Remove(scope); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Scopes), new { id = api.Id }); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ApiScopeController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ApiScopeController.cs deleted file mode 100644 index 720454652..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ApiScopeController.cs +++ /dev/null @@ -1,110 +0,0 @@ -using ClassifiedAds.IdentityServer.Models.ApiScopeModels; -using IdentityServer4.EntityFramework.DbContexts; -using IdentityServer4.EntityFramework.Entities; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class ApiScopeController : Controller - { - private readonly ConfigurationDbContext _configurationDbContext; - - public ApiScopeController(ConfigurationDbContext configurationDbContext) - { - _configurationDbContext = configurationDbContext; - } - - public IActionResult Scopes(int id) - { - var api = _configurationDbContext.ApiScopes - .Include(x => x.UserClaims) - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == id); - return View(ApiScopeModel.FromEntity(api)); - } - - public IActionResult Edit(int id) - { - if (id != 0) - { - var api = _configurationDbContext.ApiScopes - .Include(x => x.UserClaims) - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == id); - - return View(ApiScopeModel.FromEntity(api)); - } - else - { - return View(new ApiScopeModel - { - }); - } - } - - [HttpPost] - public IActionResult Edit(ApiScopeModel model) - { - ApiScope api; - - if (model.Id == 0) - { - api = new ApiScope - { - - }; - } - else - { - api = _configurationDbContext.ApiScopes - .Include(x => x.UserClaims) - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.Id); - } - - model.UpdateEntity(api); - - if (!string.IsNullOrEmpty(model.UserClaimsItems)) - { - var userClaims = JsonSerializer.Deserialize>(model.UserClaimsItems); - - api.UserClaims.AddRange(userClaims.Select(x => new ApiScopeClaim - { - Type = x, - })); - } - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Scopes), new { id = api.Id }); - } - - [HttpGet] - public IActionResult Delete(int id) - { - var api = _configurationDbContext.ApiScopes - .Include(x => x.UserClaims) - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == id); - return View(ApiScopeModel.FromEntity(api)); - } - - [HttpPost] - public IActionResult Delete(ApiScopeModel model) - { - var api = _configurationDbContext.ApiScopes - .Include(x => x.UserClaims) - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.Id); - - _configurationDbContext.ApiScopes.Remove(api); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Scopes), new { id = api.Id }); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ClientController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ClientController.cs deleted file mode 100644 index 211513d6f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/ClientController.cs +++ /dev/null @@ -1,343 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.IdentityServer.Models.ClientModels; -using IdentityServer4.EntityFramework.DbContexts; -using IdentityServer4.EntityFramework.Entities; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class ClientController : Controller - { - private readonly ConfigurationDbContext _configurationDbContext; - - public ClientController(ConfigurationDbContext configurationDbContext) - { - _configurationDbContext = configurationDbContext; - } - - public IActionResult Index() - { - var clients = _configurationDbContext.Clients - .Include(x => x.AllowedGrantTypes) - .Include(x => x.RedirectUris) - .Include(x => x.PostLogoutRedirectUris) - .Include(x => x.AllowedScopes) - .Include(x => x.ClientSecrets) - .Include(x => x.Claims) - .Include(x => x.IdentityProviderRestrictions) - .Include(x => x.AllowedCorsOrigins) - .Include(x => x.Properties) - .AsNoTracking() - .ToList(); - - var models = clients.Select(x => ClientModel.FromEntity(x)).ToList(); - - return View(models); - } - - public IActionResult Add() - { - var client = new ClientModel(); - return View(nameof(Edit), client); - } - - public IActionResult Edit(int id) - { - var client = _configurationDbContext.Clients - .Include(x => x.AllowedGrantTypes) - .Include(x => x.RedirectUris) - .Include(x => x.PostLogoutRedirectUris) - .Include(x => x.AllowedScopes) - .Include(x => x.ClientSecrets) - .Include(x => x.Claims) - .Include(x => x.IdentityProviderRestrictions) - .Include(x => x.AllowedCorsOrigins) - .Include(x => x.Properties) - .Where(x => x.Id == id) - .AsNoTracking() - .FirstOrDefault(); - - var model = ClientModel.FromEntity(client); - - return View(model); - } - - [HttpPost] - public IActionResult Edit(ClientModel model) - { - Client client; - if (model.Id == 0) - { - model.SetDefaultValues(); - client = new Client(); - _configurationDbContext.Clients.Add(client); - } - else - { - model.ConvertItemsToList(); - - client = _configurationDbContext.Clients - .Include(x => x.AllowedGrantTypes) - .Include(x => x.RedirectUris) - .Include(x => x.PostLogoutRedirectUris) - .Include(x => x.AllowedScopes) - .Include(x => x.ClientSecrets) - .Include(x => x.Claims) - .Include(x => x.IdentityProviderRestrictions) - .Include(x => x.AllowedCorsOrigins) - .Include(x => x.Properties) - .Where(x => x.Id == model.Id) - .FirstOrDefault(); - client.Updated = DateTime.UtcNow; - } - - model.UpdateEntity(client); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Edit), new { id = client.Id }); - } - - public IActionResult Clone(int id) - { - var client = _configurationDbContext.Clients - .Include(x => x.AllowedGrantTypes) - .Include(x => x.RedirectUris) - .Include(x => x.PostLogoutRedirectUris) - .Include(x => x.AllowedScopes) - .Include(x => x.ClientSecrets) - .Include(x => x.Claims) - .Include(x => x.IdentityProviderRestrictions) - .Include(x => x.AllowedCorsOrigins) - .Include(x => x.Properties) - .Where(x => x.Id == id) - .AsNoTracking() - .FirstOrDefault(); - - var model = ClientModel.FromEntity(client); - model.OriginalClientId = model.ClientId; - model.ClientId = $"Clone_From_{model.ClientId}_{DateTime.Now.ToString("yyyyMMddhhmmssfff")}"; - - return View(model); - } - - [HttpPost] - public IActionResult Clone(ClientModel model) - { - Client client = new Client(); - model.ConvertItemsToList(); - model.UpdateEntity(client); - - _configurationDbContext.Clients.Add(client); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Edit), new { id = client.Id }); - } - - public IActionResult Delete(int id) - { - var client = _configurationDbContext.Clients - .Where(x => x.Id == id) - .AsNoTracking() - .FirstOrDefault(); - - var model = ClientModel.FromEntity(client); - - return View(model); - } - - [HttpPost] - public IActionResult Delete(ClientModel model) - { - var client = _configurationDbContext.Clients - .FirstOrDefault(x => x.Id == model.Id); - - _configurationDbContext.Clients.Remove(client); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Index)); - } - - public IActionResult GetScopes() - { - var identityResources = _configurationDbContext.IdentityResources - .Select(x => x.Name).ToList(); - - var apiScopes = _configurationDbContext.ApiResources - .Select(x => x.Name).ToList(); - - var scopes = identityResources.Concat(apiScopes).ToList(); - - return Ok(scopes); - } - - public IActionResult GetGrantTypes() - { - var allowedGrantypes = new List - { - "implicit", - "client_credentials", - "authorization_code", - "hybrid", - "password", - "urn:ietf:params:oauth:grant-type:device_code", - }; - - return Ok(allowedGrantypes); - } - - public IActionResult Properties(int id) - { - var client = _configurationDbContext.Clients - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == id); - return View(PropertiesModel.FromEntity(client)); - } - - [HttpPost] - public IActionResult AddProperty(PropertiesModel model) - { - var client = _configurationDbContext.Clients - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.Client.Id); - - client.Properties.Add(new ClientProperty - { - Key = model.Key, - Value = model.Value, - }); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Properties), new { id = client.Id }); - } - - [HttpGet] - public IActionResult DeleteProperty(int id) - { - var prop = _configurationDbContext.Set() - .Include(x => x.Client) - .FirstOrDefault(x => x.Id == id); - return View(PropertyModel.FromEntity(prop)); - } - - [HttpPost] - public IActionResult DeleteProperty(PropertyModel model) - { - var client = _configurationDbContext.Clients - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.Client.Id); - var prop = client.Properties.FirstOrDefault(x => x.Id == model.Id); - client.Properties.Remove(prop); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Properties), new { id = client.Id }); - } - - public IActionResult Secrets(int id) - { - var client = _configurationDbContext.Clients - .Include(x => x.ClientSecrets) - .FirstOrDefault(x => x.Id == id); - return View(SecretsModel.FromEntity(client)); - } - - [HttpPost] - public IActionResult Secrets(SecretsModel model) - { - var client = _configurationDbContext.Clients - .Include(x => x.ClientSecrets) - .FirstOrDefault(x => x.Id == model.Client.Id); - - var secret = new ClientSecret - { - Created = DateTime.UtcNow, - }; - - model.HashSecret(); - model.UpdateEntity(secret); - client.ClientSecrets.Add(secret); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Secrets), new { id = client.Id }); - } - - [HttpGet] - public IActionResult DeleteSecret(int id) - { - var secret = _configurationDbContext.Set() - .Include(x => x.Client) - .FirstOrDefault(x => x.Id == id); - return View(SecretModel.FromEntity(secret)); - } - - [HttpPost] - public IActionResult DeleteSecret(SecretModel model) - { - var client = _configurationDbContext.Clients - .Include(x => x.ClientSecrets) - .FirstOrDefault(x => x.Id == model.Client.Id); - var secret = client.ClientSecrets.FirstOrDefault(x => x.Id == model.Id); - client.ClientSecrets.Remove(secret); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Secrets), new { id = client.Id }); - } - - public IActionResult Claims(int id) - { - var client = _configurationDbContext.Clients - .Include(x => x.Claims) - .FirstOrDefault(x => x.Id == id); - - return View(ClaimsModel.FromEntity(client)); - } - - [HttpPost] - public IActionResult Claims(ClaimModel model) - { - var client = _configurationDbContext.Clients - .Include(x => x.Claims) - .FirstOrDefault(x => x.Id == model.Client.Id); - - client.Claims.Add(new ClientClaim - { - Type = model.Type, - Value = model.Value, - }); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Claims), new { id = client.Id }); - } - - public IActionResult DeleteClaim(int id) - { - var claim = _configurationDbContext.Set() - .Include(x => x.Client) - .FirstOrDefault(x => x.Id == id); - - return View(ClaimModel.FromEntity(claim)); - } - - [HttpPost] - public IActionResult DeleteClaim(ClaimModel model) - { - var client = _configurationDbContext.Clients - .Include(x => x.Claims) - .FirstOrDefault(x => x.Id == model.Client.Id); - - var claim = client.Claims.FirstOrDefault(x => x.Id == model.Id); - - client.Claims.Remove(claim); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Claims), new { id = client.Id }); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/GrantController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/GrantController.cs deleted file mode 100644 index 87301bae5..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/GrantController.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class GrantController : Controller - { - public IActionResult Index() - { - return View(); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/IdentityResourceController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/IdentityResourceController.cs deleted file mode 100644 index 443a89ca2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/IdentityResourceController.cs +++ /dev/null @@ -1,170 +0,0 @@ -using ClassifiedAds.IdentityServer.Models.IdentityResourceModels; -using IdentityServer4.EntityFramework.DbContexts; -using IdentityServer4.EntityFramework.Entities; -using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class IdentityResourceController : Controller - { - private readonly ConfigurationDbContext _configurationDbContext; - - public IdentityResourceController(ConfigurationDbContext configurationDbContext) - { - _configurationDbContext = configurationDbContext; - } - - public IActionResult Index() - { - var itentities = _configurationDbContext.IdentityResources.ToList(); - return View(itentities.ToModels().ToList()); - } - - public IActionResult Add() - { - return View(nameof(Edit), new IdentityResourceModel()); - } - - public IActionResult Edit(int id) - { - var identity = _configurationDbContext.IdentityResources - .Include(x => x.UserClaims) - .FirstOrDefault(x => x.Id == id); - return View(identity.ToModel()); - } - - [HttpPost] - public IActionResult Edit(IdentityResourceModel model) - { - IdentityResource identity; - if (model.Id == 0) - { - identity = new IdentityResource - { - UserClaims = new List(), - }; - _configurationDbContext.IdentityResources.Add(identity); - } - else - { - identity = _configurationDbContext.IdentityResources - .Include(x => x.UserClaims) - .FirstOrDefault(x => x.Id == model.Id); - identity.UserClaims.Clear(); - } - - model.UpdateEntity(identity); - - if (!string.IsNullOrEmpty(model.UserClaimsItems)) - { - model.UserClaims = JsonSerializer.Deserialize>(model.UserClaimsItems); - - identity.UserClaims.AddRange(model.UserClaims.Select(x => new IdentityResourceClaim - { - Type = x, - })); - } - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Edit), new { id = identity.Id }); - } - - public IActionResult Delete(int id) - { - var identity = _configurationDbContext.IdentityResources - .Include(x => x.UserClaims) - .FirstOrDefault(x => x.Id == id); - return View(identity.ToModel()); - } - - [HttpPost] - public IActionResult Delete(IdentityResourceModel model) - { - var identity = _configurationDbContext.IdentityResources - .FirstOrDefault(x => x.Id == model.Id); - - _configurationDbContext.IdentityResources.Remove(identity); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Index)); - } - - public IActionResult Properties(int id) - { - var identity = _configurationDbContext.IdentityResources - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == id); - return View(PropertiesModel.FromEntity(identity)); - } - - [HttpPost] - public IActionResult AddProperty(PropertiesModel model) - { - var identity = _configurationDbContext.IdentityResources - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.IdentityResourceId); - - identity.Properties.Add(new IdentityResourceProperty - { - Key = model.Key, - Value = model.Value, - }); - - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Properties), new { id = identity.Id }); - } - - [HttpGet] - public IActionResult DeleteProperty(int id) - { - var prop = _configurationDbContext.Set() - .Include(x => x.IdentityResource) - .FirstOrDefault(x => x.Id == id); - return View(IdentityResourcePropertyModel.FromEntity(prop)); - } - - [HttpPost] - public IActionResult DeleteProperty(IdentityResourcePropertyModel model) - { - var identity = _configurationDbContext.IdentityResources - .Include(x => x.Properties) - .FirstOrDefault(x => x.Id == model.IdentityResource.Id); - var prop = identity.Properties.FirstOrDefault(x => x.Id == model.Id); - identity.Properties.Remove(prop); - _configurationDbContext.SaveChanges(); - - return RedirectToAction(nameof(Properties), new { id = identity.Id }); - } - - public IActionResult GetClaims() - { - // http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims - var standardClaims = new List - { - "name", - "given_name", - "family_name", - "middle_name", - "nickname", - "preferred_username", - "profile", - "picture", - "website", - "gender", - "birthdate", - "zoneinfo", - "locale", - "address", - "updated_at", - }; - - return Ok(standardClaims); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/RoleController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/RoleController.cs deleted file mode 100644 index ea2158cec..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/RoleController.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using ClassifiedAds.Application; -using ClassifiedAds.IdentityServer.Models.RoleModels; -using ClassifiedAds.Modules.Identity.Commands.Roles; -using ClassifiedAds.Modules.Identity.Entities; -using ClassifiedAds.Modules.Identity.Queries.Roles; -using Microsoft.AspNetCore.Mvc; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class RoleController : Controller - { - private readonly Dispatcher _dispatcher; - - public RoleController(Dispatcher dispatcher) - { - _dispatcher = dispatcher; - } - - public async Task Index() - { - var roles = await _dispatcher.DispatchAsync(new GetRolesQuery { AsNoTracking = true }); - return View(roles); - } - - public async Task Edit(Guid id) - { - Role role; - if (id == Guid.Empty) - { - role = new Role(); - } - else - { - role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, AsNoTracking = true }); - } - - var model = role; - - return View(model); - } - - [HttpPost] - public async Task Edit(Role model) - { - Role role; - - if (model.Id == Guid.Empty) - { - role = new Role - { - Name = model.Name, - NormalizedName = model.Name.ToUpper(), - }; - - await _dispatcher.DispatchAsync(new AddUpdateRoleCommand { Role = role }); - } - else - { - role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Id }); - role.Name = model.Name; - role.NormalizedName = model.Name.ToUpper(); - await _dispatcher.DispatchAsync(new AddUpdateRoleCommand { Role = role }); - } - - return RedirectToAction(nameof(Edit), new { role.Id }); - } - - public async Task Delete(Guid id) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, AsNoTracking = true }); - return View(role); - } - - [HttpPost] - public async Task Delete(Role model) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Id }); - await _dispatcher.DispatchAsync(new DeleteRoleCommand { Role = role }); - - return RedirectToAction(nameof(Index)); - } - - public async Task Claims(Guid id) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, IncludeClaims = true, AsNoTracking = true }); - - return View(ClaimsModel.FromEntity(role)); - } - - [HttpPost] - public async Task Claims(ClaimModel model) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Role.Id, IncludeClaims = true }); - - var claim = new RoleClaim - { - Type = model.Type, - Value = model.Value, - }; - - await _dispatcher.DispatchAsync(new AddClaimCommand { Role = role, Claim = claim }); - - return RedirectToAction(nameof(Claims), new { id = role.Id }); - } - - public async Task DeleteClaim(Guid roleId, Guid claimId) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = roleId, IncludeClaims = true, AsNoTracking = true }); - var claim = role.Claims.FirstOrDefault(x => x.Id == claimId); - - return View(ClaimModel.FromEntity(claim)); - } - - [HttpPost] - public async Task DeleteClaim(ClaimModel model) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = model.Role.Id, IncludeClaims = true }); - - var claim = role.Claims.FirstOrDefault(x => x.Id == model.Id); - - await _dispatcher.DispatchAsync(new DeleteClaimCommand { Role = role, Claim = claim }); - - return RedirectToAction(nameof(Claims), new { id = role.Id }); - } - - public async Task Users(Guid id) - { - var role = await _dispatcher.DispatchAsync(new GetRoleQuery { Id = id, IncludeUsers = true, AsNoTracking = true }); - - var users = role.UserRoles.Select(x => x.User).ToList(); - var model = new UsersModel - { - Role = role, - Users = users, - }; - - return View(model); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/UserController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/UserController.cs deleted file mode 100644 index e92a453d1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Controllers/UserController.cs +++ /dev/null @@ -1,229 +0,0 @@ -using ClassifiedAds.Application; -using ClassifiedAds.CrossCuttingConcerns.OS; -using ClassifiedAds.IdentityServer.Models.UserModels; -using ClassifiedAds.Modules.Identity.Commands.Users; -using ClassifiedAds.Modules.Identity.Entities; -using ClassifiedAds.Modules.Identity.Queries.Roles; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Controllers -{ - public class UserController : Controller - { - private readonly Dispatcher _dispatcher; - private readonly UserManager _userManager; - private readonly IDateTimeProvider _dateTimeProvider; - - public UserController(Dispatcher dispatcher, - UserManager userManager, - ILogger logger, - IDateTimeProvider dateTimeProvider) - { - _dispatcher = dispatcher; - _userManager = userManager; - _dateTimeProvider = dateTimeProvider; - logger.LogInformation("UserController"); - } - - public async Task Index() - { - var users = await _dispatcher.DispatchAsync(new GetUsersQuery { AsNoTracking = true }); - return View(users); - } - - public async Task Profile(Guid id) - { - var user = id != Guid.Empty - ? await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, AsNoTracking = true }) - : new User(); - return View(user); - } - - [HttpPost] - public async Task Profile(User model) - { - User user; - if (model.Id != Guid.Empty) - { - user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.Id }); - } - else - { - user = new User(); - } - - user.UserName = model.UserName; - user.NormalizedUserName = model.UserName.ToUpper(); - user.Email = model.Email; - user.NormalizedEmail = model.Email.ToUpper(); - user.EmailConfirmed = model.EmailConfirmed; - user.PhoneNumber = model.PhoneNumber; - user.PhoneNumberConfirmed = model.PhoneNumberConfirmed; - user.TwoFactorEnabled = model.TwoFactorEnabled; - user.LockoutEnabled = model.LockoutEnabled; - user.LockoutEnd = model.LockoutEnd; - user.AccessFailedCount = model.AccessFailedCount; - - _ = model.Id != Guid.Empty - ? await _userManager.UpdateAsync(user) - : await _userManager.CreateAsync(user); - - return RedirectToAction(nameof(Profile), new { user.Id }); - } - - public async Task ChangePassword(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, AsNoTracking = true }); - return View(ChangePasswordModel.FromEntity(user)); - } - - public async Task Delete(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, AsNoTracking = true }); - return View(user); - } - - [HttpPost] - public async Task Delete(User model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.Id }); - await _dispatcher.DispatchAsync(new DeleteUserCommand { User = user }); - return RedirectToAction(nameof(Index)); - } - - [HttpPost] - public async Task ChangePassword(ChangePasswordModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.Id }); - var token = await _userManager.GeneratePasswordResetTokenAsync(user); - var rs = await _userManager.ResetPasswordAsync(user, token, model.ConfirmPassword); - - if (rs.Succeeded) - { - return RedirectToAction(nameof(Profile), new { model.Id }); - } - - foreach (var error in rs.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - - return View(ChangePasswordModel.FromEntity(user)); - } - - public async Task Claims(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, IncludeClaims = true, AsNoTracking = true }); - return View(ClaimsModel.FromEntity(user)); - } - - [HttpPost] - public async Task Claims(ClaimModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeClaims = true }); - await _dispatcher.DispatchAsync(new AddClaimCommand - { - User = user, - Claim = new UserClaim - { - Type = model.Type, - Value = model.Value, - }, - }); - - return RedirectToAction(nameof(Claims), new { id = user.Id }); - } - - public async Task DeleteClaim(Guid userId, Guid claimId) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = userId, IncludeClaims = true, AsNoTracking = true }); - var claim = user.Claims.FirstOrDefault(x => x.Id == claimId); - - return View(ClaimModel.FromEntity(claim)); - } - - [HttpPost] - public async Task DeleteClaim(ClaimModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeClaims = true }); - - var claim = user.Claims.FirstOrDefault(x => x.Id == model.Id); - - await _dispatcher.DispatchAsync(new DeleteClaimCommand - { - User = user, - Claim = claim, - }); - - return RedirectToAction(nameof(Claims), new { id = user.Id }); - } - - public async Task Roles(Guid id) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, IncludeRoles = true, AsNoTracking = true }); - - var roles = await _dispatcher.DispatchAsync(new GetRolesQuery { AsNoTracking = true }); - - var model = new RolesModel - { - User = user, - UserRoles = user.UserRoles.Select(x => new RoleModel { Role = x.Role, RoleId = x.RoleId }).ToList(), - Roles = roles.Where(x => !user.UserRoles.Any(y => y.RoleId == x.Id)).ToList(), - }; - - return View(model); - } - - [HttpPost] - public async Task Roles(RolesModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeUserRoles = true }); - - await _dispatcher.DispatchAsync(new AddRoleCommand - { - User = user, - Role = new UserRole - { - RoleId = model.Role.RoleId, - }, - }); - - return RedirectToAction(nameof(Roles), new { model.User.Id }); - } - - public async Task DeleteRole(Guid id, Guid roleId) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = id, IncludeRoles = true, AsNoTracking = true }); - var role = user.UserRoles.FirstOrDefault(x => x.RoleId == roleId); - var model = new RoleModel { User = user, Role = role.Role }; - - return View(model); - } - - [HttpPost] - public async Task DeleteRole(RoleModel model) - { - var user = await _dispatcher.DispatchAsync(new GetUserQuery { Id = model.User.Id, IncludeUserRoles = true }); - - var role = user.UserRoles.FirstOrDefault(x => x.RoleId == model.Role.Id); - - await _dispatcher.DispatchAsync(new DeleteRoleCommand - { - User = user, - Role = role, - }); - - return RedirectToAction(nameof(Roles), new { model.User.Id }); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Dockerfile b/src/ModularMonolith/ClassifiedAds.IdentityServer/Dockerfile deleted file mode 100644 index 87f894946..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env -WORKDIR /ClassifiedAds.ModularMonolith - -# Copy csproj and restore as distinct layers -COPY ./ClassifiedAds.Application/*.csproj ./ClassifiedAds.Application/ -COPY ./ClassifiedAds.CrossCuttingConcerns/*.csproj ./ClassifiedAds.CrossCuttingConcerns/ -COPY ./ClassifiedAds.Domain/*.csproj ./ClassifiedAds.Domain/ -COPY ./ClassifiedAds.Infrastructure/*.csproj ./ClassifiedAds.Infrastructure/ - -COPY ./ClassifiedAds.Modules.AuditLog/*.csproj ./ClassifiedAds.Modules.AuditLog/ -COPY ./ClassifiedAds.Modules.AuditLog.Contracts/*.csproj ./ClassifiedAds.Modules.AuditLog.Contracts/ -COPY ./ClassifiedAds.Modules.Auth/*.csproj ./ClassifiedAds.Modules.Auth/ -COPY ./ClassifiedAds.Modules.Configuration/*.csproj ./ClassifiedAds.Modules.Configuration/ -COPY ./ClassifiedAds.Modules.Identity/*.csproj ./ClassifiedAds.Modules.Identity/ -COPY ./ClassifiedAds.Modules.Identity.Contracts/*.csproj ./ClassifiedAds.Modules.Identity.Contracts/ -COPY ./ClassifiedAds.Modules.Notification/*.csproj ./ClassifiedAds.Modules.Notification/ -COPY ./ClassifiedAds.Modules.Notification.Contracts/*.csproj ./ClassifiedAds.Modules.Notification.Contracts/ -COPY ./ClassifiedAds.Modules.Product/*.csproj ./ClassifiedAds.Modules.Product/ -COPY ./ClassifiedAds.Modules.Product.EndToEndTests/*.csproj ./ClassifiedAds.Modules.Product.EndToEndTests/ -COPY ./ClassifiedAds.Modules.Product.IntegrationTests/*.csproj ./ClassifiedAds.Modules.Product.IntegrationTests/ -COPY ./ClassifiedAds.Modules.Product.UnitTests/*.csproj ./ClassifiedAds.Modules.Product.UnitTests/ -COPY ./ClassifiedAds.Modules.Storage/*.csproj ./ClassifiedAds.Modules.Storage/ - -COPY ./ClassifiedAds.BackgroundServer/*.csproj ./ClassifiedAds.BackgroundServer/ -COPY ./ClassifiedAds.IdentityServer/*.csproj ./ClassifiedAds.IdentityServer/ -COPY ./ClassifiedAds.Migrator/*.csproj ./ClassifiedAds.Migrator/ -COPY ./ClassifiedAds.WebAPI/*.csproj ./ClassifiedAds.WebAPI/ - -COPY ./ClassifiedAds.ModularMonolith.sln . -RUN dotnet restore - -# Copy everything else and build -COPY . ./ -RUN dotnet publish ./ClassifiedAds.IdentityServer/ClassifiedAds.IdentityServer.csproj -c Release -o out - -# Build runtime image -FROM mcr.microsoft.com/dotnet/aspnet:6.0 -WORKDIR /ClassifiedAds.ModularMonolith -COPY --from=build-env /ClassifiedAds.ModularMonolith/out . - -ENTRYPOINT ["dotnet", "ClassifiedAds.IdentityServer.dll"] \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourceModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourceModel.cs deleted file mode 100644 index e153bd8e3..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourceModel.cs +++ /dev/null @@ -1,55 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class ApiResourceModel - { - public int Id { get; set; } - public bool Enabled { get; set; } - public string Name { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public List Secrets { get; set; } - public List Scopes { get; set; } - public List UserClaims { get; set; } - public string UserClaimsItems { get; set; } - public List Properties { get; set; } - public DateTime Created { get; set; } - public DateTime? Updated { get; set; } - public DateTime? LastAccessed { get; set; } - public bool NonEditable { get; set; } - - public static ApiResourceModel FromEntity(ApiResource apiResource) - { - return new ApiResourceModel - { - Id = apiResource.Id, - Enabled = apiResource.Enabled, - Name = apiResource.Name, - DisplayName = apiResource.DisplayName, - Description = apiResource.Description, - Secrets = apiResource.Secrets, - Scopes = apiResource.Scopes, - UserClaims = apiResource.UserClaims, - Properties = apiResource.Properties, - Created = apiResource.Created, - Updated = apiResource.Updated, - LastAccessed = apiResource.LastAccessed, - NonEditable = apiResource.NonEditable, - }; - } - - public void UpdateEntity(ApiResource entity) - { - entity.Enabled = Enabled; - entity.Name = Name; - entity.DisplayName = DisplayName; - entity.Description = Description; - entity.NonEditable = NonEditable; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs deleted file mode 100644 index 5c9225ae5..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ApiResourcePropertyModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class ApiResourcePropertyModel - { - public int Id { get; set; } - public string Key { get; set; } - public string Value { get; set; } - public ApiResourceModel ApiResource { get; set; } - - public static ApiResourcePropertyModel FromEntity(ApiResourceProperty entity) - { - return new ApiResourcePropertyModel - { - Id = entity.Id, - Key = entity.Key, - Value = entity.Value, - ApiResource = new ApiResourceModel - { - Id = entity.ApiResource.Id, - Name = entity.ApiResource.Name, - }, - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/PropertiesModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/PropertiesModel.cs deleted file mode 100644 index 2ab1657d8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/PropertiesModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class PropertiesModel - { - public int ApiResourceId { get; set; } - public string ApiResourceName { get; set; } - public string Key { get; set; } - public string Value { get; set; } - public List Properties { get; set; } - - public static PropertiesModel FromEntity(ApiResource apiResource) - { - return new PropertiesModel - { - ApiResourceId = apiResource.Id, - ApiResourceName = apiResource.Name, - Properties = apiResource.Properties?.Select(x => new ApiResourcePropertyModel - { - Id = x.Id, - Key = x.Key, - Value = x.Value, - })?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopeModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopeModel.cs deleted file mode 100644 index c2640fa66..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopeModel.cs +++ /dev/null @@ -1,34 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class ScopeModel - { - public int Id { get; set; } - public string Scope { get; set; } - public int ApiResourceId { get; set; } - public ApiResource ApiResource { get; set; } - public string ApiResourceName { get; set; } - - public static ScopeModel FromEntity(ApiResourceScope apiScope) - { - return new ScopeModel - { - Id = apiScope.Id, - Scope = apiScope.Scope, - ApiResourceId = apiScope.ApiResourceId, - ApiResourceName = apiScope.ApiResource.Name, - ApiResource = apiScope.ApiResource, - }; - } - - public void UpdateEntity(ApiResourceScope entity) - { - entity.Scope = Scope; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopesModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopesModel.cs deleted file mode 100644 index 0a442f8ca..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/ScopesModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class ScopesModel : ScopeModel - { - public List Scopes { get; set; } - - public List ApiScopes { get; set; } - - public void UpdateEntity(ApiScope entity) - { - - } - - public static ScopesModel FromEntity(ApiResource apiResource) - { - return new ScopesModel - { - ApiResourceId = apiResource.Id, - ApiResourceName = apiResource.Name, - Scopes = apiResource.Scopes?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretModel.cs deleted file mode 100644 index 8cc38c4ae..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretModel.cs +++ /dev/null @@ -1,37 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class SecretModel - { - public int Id { get; set; } - public string Description { get; set; } - public string Value { get; set; } - public DateTime? Expiration { get; set; } - public string Type { get; set; } - public string HashType { get; set; } - public DateTime Created { get; set; } - public int ApiResourceId { get; set; } - public string ApiResourceName { get; set; } - public ApiResourceModel ApiResource { get; set; } - - public static SecretModel FromEntity(ApiResourceSecret secret) - { - return new SecretModel - { - Id = secret.Id, - Description = secret.Description, - Value = secret.Value, - Expiration = secret.Expiration, - Type = secret.Type, - Created = secret.Created, - ApiResourceId = secret.ApiResource.Id, - ApiResourceName = secret.ApiResource.Name, - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs deleted file mode 100644 index e66ae3d6e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiResourceModels/SecretsModel.cs +++ /dev/null @@ -1,61 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using IdentityServer4.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ApiResourceModels -{ - public class SecretsModel : SecretModel - { - public List Secrets { get; set; } - public List TypeList { get; } = new List - { - "SharedSecret", - "X509Thumbprint", - "X509Name", - "X509CertificateBase64", - }; - public List HashTypes { get; } = new List - { - "Sha256", - "Sha512", - }; - - public void HashSecret() - { - if (Type != "SharedSecret") - { - return; - } - - if (HashType == "Sha256") - { - Value = Value.Sha256(); - } - else if (HashType == "Sha512") - { - Value = Value.Sha512(); - } - } - - public void UpdateEntity(IdentityServer4.EntityFramework.Entities.Secret entity) - { - entity.Description = Description; - entity.Value = Value; - entity.Expiration = Expiration; - entity.Type = Type; - } - - public static SecretsModel FromEntity(IdentityServer4.EntityFramework.Entities.ApiResource apiResource) - { - return new SecretsModel - { - ApiResourceId = apiResource.Id, - ApiResourceName = apiResource.Name, - Secrets = apiResource.Secrets?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiScopeModels/ApiScopeModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiScopeModels/ApiScopeModel.cs deleted file mode 100644 index 6ce2b741d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ApiScopeModels/ApiScopeModel.cs +++ /dev/null @@ -1,50 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ApiScopeModels -{ - public class ApiScopeModel - { - public int Id { get; set; } - public bool Enabled { get; set; } - public string Name { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public bool Required { get; set; } - public bool Emphasize { get; set; } - public bool ShowInDiscoveryDocument { get; set; } - public List UserClaims { get; set; } - public string UserClaimsItems { get; set; } - public List Properties { get; set; } - - public static ApiScopeModel FromEntity(ApiScope apiScope) - { - return new ApiScopeModel - { - Id = apiScope.Id, - Enabled = apiScope.Enabled, - Name = apiScope.Name, - DisplayName = apiScope.DisplayName, - Description = apiScope.Description, - Required = apiScope.Required, - Emphasize = apiScope.Emphasize, - ShowInDiscoveryDocument = apiScope.ShowInDiscoveryDocument, - UserClaims = apiScope.UserClaims, - Properties = apiScope.Properties, - }; - } - - public void UpdateEntity(ApiScope entity) - { - entity.Name = Name; - entity.DisplayName = DisplayName; - entity.Description = Description; - entity.Required = Required; - entity.Emphasize = Emphasize; - entity.ShowInDiscoveryDocument = ShowInDiscoveryDocument; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs deleted file mode 100644 index 117ae9f53..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public class ClaimModel - { - public int Id { get; set; } - public string Type { get; set; } - public string Value { get; set; } - public ClientModel Client { get; set; } - - public static ClaimModel FromEntity(ClientClaim claim) - { - return new ClaimModel - { - Id = claim.Id, - Type = claim.Type, - Value = claim.Value, - Client = ClientModel.FromEntity(claim.Client), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs deleted file mode 100644 index e18b56af4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClaimsModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public class ClaimsModel : ClaimModel - { - public List Claims { get; set; } - - public static ClaimsModel FromEntity(Client client) - { - var clientModel = ClientModel.FromEntity(client); - - return new ClaimsModel - { - Client = clientModel, - Claims = client.Claims?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs deleted file mode 100644 index 722a6b5a3..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/ClientModel.cs +++ /dev/null @@ -1,153 +0,0 @@ -using AutoMapper; -using IdentityServer4.EntityFramework.Entities; -using IdentityServer4.EntityFramework.Mappers; -using IdentityServer4.Models; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public enum ClientType - { - Empty = 0, - WebHybrid = 1, - Spa = 2, - Native = 3, - Machine = 4, - Device = 5, - } - - public class ClientModel : IdentityServer4.Models.Client - { - public int Id { get; set; } - public ClientType ClientType { get; set; } - public string AllowedGrantTypesItems { get; set; } - public string RedirectUrisItems { get; set; } - public string PostLogoutRedirectUrisItems { get; set; } - public string AllowedScopesItems { get; set; } - public string IdentityProviderRestrictionsItems { get; set; } - public string AllowedCorsOriginsItems { get; set; } - - public string OriginalClientId { get; set; } - - public static ClientModel FromEntity(IdentityServer4.EntityFramework.Entities.Client client) - { - var config = new MapperConfiguration(cfg => cfg.CreateMap()); - var mapper = config.CreateMapper(); - var model = mapper.Map(client.ToModel()); - model.Id = client.Id; - return model; - } - - public void SetDefaultValues() - { - switch (ClientType) - { - case ClientType.Empty: - break; - case ClientType.WebHybrid: - AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.Hybrid).ToList(); - break; - case ClientType.Spa: - AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.Code).ToList(); - RequirePkce = true; - RequireClientSecret = false; - break; - case ClientType.Native: - AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.Hybrid).ToList(); - break; - case ClientType.Machine: - AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.ResourceOwnerPasswordAndClientCredentials).ToList(); - break; - case ClientType.Device: - AllowedGrantTypes = AllowedGrantTypes.ToList().Concat(GrantTypes.DeviceFlow).ToList(); - RequireClientSecret = false; - AllowOfflineAccess = true; - break; - } - } - - public void ConvertItemsToList() - { - AllowedGrantTypes = ConvertItems(AllowedGrantTypesItems); - RedirectUris = ConvertItems(RedirectUrisItems); - PostLogoutRedirectUris = ConvertItems(PostLogoutRedirectUrisItems); - AllowedScopes = ConvertItems(AllowedScopesItems); - IdentityProviderRestrictions = ConvertItems(IdentityProviderRestrictionsItems); - AllowedCorsOrigins = ConvertItems(AllowedCorsOriginsItems); - } - - private List ConvertItems(string items) - { - return string.IsNullOrWhiteSpace(items) - ? new List() - : JsonSerializer.Deserialize>(items); - } - - public void UpdateEntity(IdentityServer4.EntityFramework.Entities.Client entity) - { - entity.Enabled = Enabled; - entity.ClientId = ClientId; - entity.ProtocolType = ProtocolType; - entity.RequireClientSecret = RequireClientSecret; - entity.ClientName = ClientName; - entity.Description = Description; - entity.ClientUri = ClientUri; - entity.LogoUri = LogoUri; - entity.RequireConsent = RequireConsent; - entity.AllowRememberConsent = AllowRememberConsent; - entity.AlwaysIncludeUserClaimsInIdToken = AlwaysIncludeUserClaimsInIdToken; - entity.AllowedGrantTypes = AllowedGrantTypes.Select(x => new ClientGrantType - { - GrantType = x, - }).ToList(); - entity.RequirePkce = RequirePkce; - entity.AllowPlainTextPkce = AllowPlainTextPkce; - entity.AllowAccessTokensViaBrowser = AllowAccessTokensViaBrowser; - entity.RedirectUris = RedirectUris.Select(x => new ClientRedirectUri - { - RedirectUri = x, - }).ToList(); - entity.PostLogoutRedirectUris = PostLogoutRedirectUris.Select(x => new ClientPostLogoutRedirectUri - { - PostLogoutRedirectUri = x, - }).ToList(); - entity.FrontChannelLogoutUri = FrontChannelLogoutUri; - entity.FrontChannelLogoutSessionRequired = FrontChannelLogoutSessionRequired; - entity.BackChannelLogoutUri = BackChannelLogoutUri; - entity.BackChannelLogoutSessionRequired = BackChannelLogoutSessionRequired; - entity.AllowOfflineAccess = AllowOfflineAccess; - entity.AllowedScopes = AllowedScopes.Select(x => new ClientScope - { - Scope = x, - }).ToList(); - entity.IdentityTokenLifetime = IdentityTokenLifetime; - entity.AccessTokenLifetime = AccessTokenLifetime; - entity.AuthorizationCodeLifetime = AuthorizationCodeLifetime; - entity.ConsentLifetime = ConsentLifetime; - entity.AbsoluteRefreshTokenLifetime = AbsoluteRefreshTokenLifetime; - entity.SlidingRefreshTokenLifetime = SlidingRefreshTokenLifetime; - entity.RefreshTokenUsage = (int)RefreshTokenUsage; - entity.UpdateAccessTokenClaimsOnRefresh = UpdateAccessTokenClaimsOnRefresh; - entity.RefreshTokenExpiration = (int)RefreshTokenExpiration; - entity.AccessTokenType = (int)AccessTokenType; - entity.EnableLocalLogin = EnableLocalLogin; - entity.IdentityProviderRestrictions = IdentityProviderRestrictions.Select(x => new ClientIdPRestriction - { - Provider = x, - }).ToList(); - entity.IncludeJwtId = IncludeJwtId; - entity.AlwaysSendClientClaims = AlwaysSendClientClaims; - entity.ClientClaimsPrefix = ClientClaimsPrefix; - entity.PairWiseSubjectSalt = PairWiseSubjectSalt; - entity.AllowedCorsOrigins = AllowedCorsOrigins.Select(x => new ClientCorsOrigin - { - Origin = x, - }).ToList(); - entity.UserSsoLifetime = UserSsoLifetime; - entity.UserCodeType = UserCodeType; - entity.DeviceCodeLifetime = DeviceCodeLifetime; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs deleted file mode 100644 index 37baca777..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/PropertiesModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using IdentityServer4.EntityFramework.Mappers; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public class PropertiesModel : PropertyModel - { - public List Properties { get; set; } - - public static PropertiesModel FromEntity(Client client) - { - var clientModel = ClientModel.FromEntity(client); - return new PropertiesModel - { - Client = clientModel, - Properties = client.Properties?.Select(x => new PropertyModel - { - Id = x.Id, - Key = x.Key, - Value = x.Value, - Client = clientModel, - })?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs deleted file mode 100644 index fbdc7ae14..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/PropertyModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public class PropertyModel - { - public int Id { get; set; } - public string Key { get; set; } - public string Value { get; set; } - public ClientModel Client { get; set; } - - public static PropertyModel FromEntity(ClientProperty prop) - { - return new PropertyModel - { - Id = prop.Id, - Key = prop.Key, - Value = prop.Value, - Client = ClientModel.FromEntity(prop.Client), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs deleted file mode 100644 index 9e581ad29..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/SecretModel.cs +++ /dev/null @@ -1,31 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public class SecretModel - { - public int Id { get; set; } - public string Description { get; set; } - public string Value { get; set; } - public DateTime? Expiration { get; set; } - public string Type { get; set; } - public string HashType { get; set; } - public DateTime Created { get; set; } - public ClientModel Client { get; set; } - - public static SecretModel FromEntity(ClientSecret secret) - { - return new SecretModel - { - Id = secret.Id, - Description = secret.Description, - Value = secret.Value, - Expiration = secret.Expiration, - Type = secret.Type, - Created = secret.Created, - Client = ClientModel.FromEntity(secret.Client), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs deleted file mode 100644 index 07df9f198..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ClientModels/SecretsModel.cs +++ /dev/null @@ -1,57 +0,0 @@ -using IdentityServer4.Models; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.ClientModels -{ - public class SecretsModel : SecretModel - { - public List Secrets { get; set; } - public List TypeList { get; } = new List - { - "SharedSecret", - "X509Thumbprint", - "X509Name", - "X509CertificateBase64", - }; - public List HashTypes { get; } = new List - { - "Sha256", - "Sha512", - }; - - public void HashSecret() - { - if (Type != "SharedSecret") - { - return; - } - - if (HashType == "Sha256") - { - Value = Value.Sha256(); - } - else if (HashType == "Sha512") - { - Value = Value.Sha512(); - } - } - - public void UpdateEntity(IdentityServer4.EntityFramework.Entities.Secret entity) - { - entity.Description = Description; - entity.Value = Value; - entity.Expiration = Expiration; - entity.Type = Type; - } - - public static SecretsModel FromEntity(IdentityServer4.EntityFramework.Entities.Client client) - { - return new SecretsModel - { - Client = ClientModel.FromEntity(client), - Secrets = client.ClientSecrets?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ForgotPasswordModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ForgotPasswordModel.cs deleted file mode 100644 index 73dc6e77d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ForgotPasswordModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace ClassifiedAds.IdentityServer.Models -{ - public class ForgotPasswordModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourceModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourceModel.cs deleted file mode 100644 index 423e019dd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourceModel.cs +++ /dev/null @@ -1,66 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.IdentityResourceModels -{ - public class IdentityResourceModel - { - public int Id { get; set; } - public bool Enabled { get; set; } - public string Name { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public bool Required { get; set; } - public bool Emphasize { get; set; } - public bool ShowInDiscoveryDocument { get; set; } - public List UserClaims { get; set; } - public string UserClaimsItems { get; set; } - public List Properties { get; set; } - public DateTime Created { get; set; } - public DateTime? Updated { get; set; } - public bool NonEditable { get; set; } - - public void UpdateEntity(IdentityResource entity) - { - entity.Enabled = Enabled; - entity.Name = Name; - entity.DisplayName = DisplayName; - entity.Description = Description; - entity.Required = Required; - entity.Emphasize = Emphasize; - entity.ShowInDiscoveryDocument = ShowInDiscoveryDocument; - entity.Created = Created; - entity.Updated = Updated; - entity.NonEditable = NonEditable; - } - } - - public static class IdentityResourceMappingExtension - { - public static IdentityResourceModel ToModel(this IdentityResource entity) - { - return new IdentityResourceModel - { - Id = entity.Id, - Enabled = entity.Enabled, - Name = entity.Name, - DisplayName = entity.DisplayName, - Description = entity.Description, - Required = entity.Required, - Emphasize = entity.Emphasize, - ShowInDiscoveryDocument = entity.ShowInDiscoveryDocument, - Created = entity.Created, - Updated = entity.Updated, - NonEditable = entity.NonEditable, - UserClaims = entity.UserClaims?.Select(x => x.Type)?.ToList(), - }; - } - - public static IEnumerable ToModels(this IEnumerable entities) - { - return entities?.Select(x => x.ToModel()); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs deleted file mode 100644 index bafd5ac8d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/IdentityResourcePropertyModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; - -namespace ClassifiedAds.IdentityServer.Models.IdentityResourceModels -{ - public class IdentityResourcePropertyModel - { - public int Id { get; set; } - public string Key { get; set; } - public string Value { get; set; } - public IdentityResourceModel IdentityResource { get; set; } - - public static IdentityResourcePropertyModel FromEntity(IdentityResourceProperty entity) - { - return new IdentityResourcePropertyModel - { - Id = entity.Id, - Key = entity.Key, - Value = entity.Value, - IdentityResource = new IdentityResourceModel - { - Id = entity.IdentityResource.Id, - Name = entity.IdentityResource.Name, - }, - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/PropertiesModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/PropertiesModel.cs deleted file mode 100644 index 84ab08b32..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/IdentityResourceModels/PropertiesModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using IdentityServer4.EntityFramework.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.IdentityResourceModels -{ - public class PropertiesModel - { - public int IdentityResourceId { get; set; } - public string IdentityResourceName { get; set; } - public string Key { get; set; } - public string Value { get; set; } - public List Properties { get; set; } - - public static PropertiesModel FromEntity(IdentityResource identityResource) - { - return new PropertiesModel - { - IdentityResourceId = identityResource.Id, - IdentityResourceName = identityResource.Name, - Properties = identityResource.Properties?.Select(x => new IdentityResourcePropertyModel - { - Id = x.Id, - Key = x.Key, - Value = x.Value, - })?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RegisterModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RegisterModel.cs deleted file mode 100644 index 5f4fc6c5f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RegisterModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace ClassifiedAds.IdentityServer.Models -{ - public class RegisterModel - { - public string UserName { get; set; } - - [DataType(DataType.Password)] - public string Password { get; set; } - - [Compare("Password")] - [DataType(DataType.Password)] - public string ConfirmPassword { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ResetPasswordModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ResetPasswordModel.cs deleted file mode 100644 index f28a1127c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/ResetPasswordModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace ClassifiedAds.IdentityServer.Models -{ - public class ResetPasswordModel - { - public string Token { get; set; } - - public string Email { get; set; } - - [DataType(DataType.Password)] - public string Password { get; set; } - - [Compare("Password")] - [DataType(DataType.Password)] - public string ConfirmPassword { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs deleted file mode 100644 index 2aa3afe67..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class ClaimModel - { - public Guid Id { get; set; } - public string Type { get; set; } - public string Value { get; set; } - public Role Role { get; set; } - - public static ClaimModel FromEntity(RoleClaim claim) - { - return new ClaimModel - { - Id = claim.Id, - Type = claim.Type, - Value = claim.Value, - Role = claim.Role, - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs deleted file mode 100644 index 4f40a1a23..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/ClaimsModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class ClaimsModel : ClaimModel - { - public List Claims { get; set; } - - public static ClaimsModel FromEntity(Role role) - { - return new ClaimsModel - { - Role = role, - Claims = role.Claims?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs deleted file mode 100644 index a1640bd4d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/RoleModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class RoleModel - { - public Guid Id { get; set; } - - public string Name { get; set; } - - public static RoleModel FromEntity(Role role) - { - return new RoleModel { Id = role.Id, Name = role.Name }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs deleted file mode 100644 index 53167267e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/RoleModels/UsersModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System.Collections.Generic; - -namespace ClassifiedAds.IdentityServer.Models.RoleModels -{ - public class UsersModel - { - public Role Role { get; set; } - - public List Users { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs deleted file mode 100644 index 21d40669c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ChangePasswordModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System; -using System.ComponentModel.DataAnnotations; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class ChangePasswordModel - { - public Guid Id { get; set; } - public string UserName { get; set; } - public string Password { get; set; } - - [Compare(nameof(Password))] - public string ConfirmPassword { get; set; } - - public static ChangePasswordModel FromEntity(User user) - { - return new ChangePasswordModel - { - Id = user.Id, - UserName = user.UserName, - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs deleted file mode 100644 index 368ba4fb4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class ClaimModel - { - public Guid Id { get; set; } - public string Type { get; set; } - public string Value { get; set; } - public User User { get; set; } - - public static ClaimModel FromEntity(UserClaim claim) - { - return new ClaimModel - { - Id = claim.Id, - Type = claim.Type, - Value = claim.Value, - User = claim.User, - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs deleted file mode 100644 index 034344625..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/ClaimsModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System.Collections.Generic; -using System.Linq; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class ClaimsModel : ClaimModel - { - public List Claims { get; set; } - - public static ClaimsModel FromEntity(User user) - { - return new ClaimsModel - { - User = user, - Claims = user.Claims?.Select(x => FromEntity(x))?.ToList(), - }; - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs deleted file mode 100644 index 3fc6c95c6..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/RoleModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class RoleModel - { - public Guid RoleId { get; set; } - - public Guid UserId { get; set; } - - public User User { get; set; } - - public Role Role { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs deleted file mode 100644 index 81e8be0d9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/RolesModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using System.Collections.Generic; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class RolesModel - { - public User User { get; set; } - - public RoleModel Role { get; set; } - - public List Roles { get; set; } - - public List UserRoles { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs deleted file mode 100644 index 4bd3ba2cf..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Models/UserModels/UserModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Models.UserModels -{ - public class UserModel - { - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Program.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Program.cs deleted file mode 100644 index 699ac5441..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using ClassifiedAds.IdentityServer.ConfigurationOptions; -using ClassifiedAds.Infrastructure.Logging; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace ClassifiedAds.IdentityServer -{ - public class Program - { - public static void Main(string[] args) - { - CreateWebHostBuilder(args).Build().Run(); - } - - public static IWebHostBuilder CreateWebHostBuilder(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .UseClassifiedAdsLogger(configuration => - { - var appSettings = new AppSettings(); - configuration.Bind(appSettings); - return appSettings.Logging; - }); - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Properties/launchSettings.json b/src/ModularMonolith/ClassifiedAds.IdentityServer/Properties/launchSettings.json deleted file mode 100644 index cf5c38e1a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Properties/launchSettings.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "https://localhost:44367/", - "sslPort": 44367 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "ClassifiedAds.IdentityServer": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:5001;http://localhost:5000" - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs deleted file mode 100644 index 30e1f5700..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountController.cs +++ /dev/null @@ -1,745 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using ClassifiedAds.Application; -using ClassifiedAds.IdentityServer.Models; -using ClassifiedAds.IdentityServer.Quickstart.Account; -using ClassifiedAds.Modules.Identity.Entities; -using ClassifiedAds.Modules.Notification.Entities; -using IdentityModel; -using IdentityServer4; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Quickstart.UI; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.Extensions.Logging; -using System; -using System.Linq; -using System.Threading.Tasks; - -namespace IdentityServerHost.Quickstart.UI -{ - /// - /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. - /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! - /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval - /// - [SecurityHeaders] - [AllowAnonymous] - public class AccountController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IAuthenticationSchemeProvider _schemeProvider; - private readonly IEventService _events; - - private readonly ILogger _logger; - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly Dispatcher _dispatcher; - - public AccountController( - ILogger logger, - UserManager userManager, - SignInManager signInManager, - Dispatcher dispatcher, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IAuthenticationSchemeProvider schemeProvider, - IEventService events) - { - _logger = logger; - _userManager = userManager; - _signInManager = signInManager; - _dispatcher = dispatcher; - _interaction = interaction; - _clientStore = clientStore; - _schemeProvider = schemeProvider; - _events = events; - } - - /// - /// Entry point into the login workflow - /// - [HttpGet] - public async Task Login(string returnUrl) - { - // build a model so we know what to show on the login page - var vm = await BuildLoginViewModelAsync(returnUrl); - - if (vm.IsExternalLoginOnly) - { - // we only have one option for logging in and it's an external provider - return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl }); - } - - return View(vm); - } - - /// - /// Handle postback from username/password login - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Login(LoginInputModel model, string button) - { - // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - - // the user clicked the "cancel" button - if (button != "login") - { - if (context != null) - { - // if the user cancels, send a result back into IdentityServer as if they - // denied the consent (even if this client does not require consent). - // this will send back an access denied OIDC error response to the client. - await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - return Redirect(model.ReturnUrl); - } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } - } - - if (ModelState.IsValid) - { - var user = await _userManager.FindByNameAsync(model.Username); - - if (user != null && user.TwoFactorEnabled) - { - var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: false); - if (result.Succeeded) - { - _logger.LogInformation(1, "User logged in."); - return RedirectToLocal(model.ReturnUrl); - } - - if (result.RequiresTwoFactor) - { - return RedirectToAction(nameof(SendCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberLogin }); - } - - if (result.IsLockedOut) - { - _logger.LogWarning(2, "User account locked out."); - return View("Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return View(await BuildLoginViewModelAsync(model)); - } - } - - if (user != null && await _userManager.CheckPasswordAsync(user, model.Password)) - { - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, clientId: context?.Client.ClientId)); - - // only set explicit expiration here if user chooses "remember me". - // otherwise we rely upon expiration configured in cookie middleware. - AuthenticationProperties props = null; - if (AccountOptions.AllowRememberLogin && model.RememberLogin) - { - props = new AuthenticationProperties - { - IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) - }; - }; - - // issue authentication cookie with subject ID and username - var isuser = new IdentityServerUser(user.Id.ToString()) - { - DisplayName = user.UserName, - }; - - await HttpContext.SignInAsync(isuser, props); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - return Redirect(model.ReturnUrl); - } - - // request for a local page - if (Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - } - - await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); - ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); - } - - // something went wrong, show form with error - var vm = await BuildLoginViewModelAsync(model); - return View(vm); - } - - // - // GET: /Account/SendCode - [HttpGet] - [AllowAnonymous] - public async Task SendCode(string returnUrl = null, bool rememberMe = false) - { - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - return View("Error"); - } - - var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); - var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList(); - return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe }); - } - - // - // POST: /Account/SendCode - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task SendCode(SendCodeViewModel model) - { - if (!ModelState.IsValid) - { - return View(); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - return View("Error"); - } - - if (model.SelectedProvider == "Authenticator") - { - return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); - } - - // Generate the token and send it - var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); - if (string.IsNullOrWhiteSpace(code)) - { - return View("Error"); - } - - var message = "Your security code is: " + code; - if (model.SelectedProvider == "Email") - { - await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand(new EmailMessage - { - From = "phong@gmail.com", - Tos = user.Email, - Subject = "Security Code", - Body = message, - })); - } - else if (model.SelectedProvider == "Phone") - { - await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand(new SmsMessage - { - PhoneNumber = user.PhoneNumber, - Message = message, - })); - } - - return RedirectToAction(nameof(VerifyCode), new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); - } - - // - // GET: /Account/VerifyCode - [HttpGet] - [AllowAnonymous] - public async Task VerifyCode(string provider, bool rememberMe, string returnUrl = null) - { - // Require that the user has already logged in via username/password or external login - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - if (user == null) - { - return View("Error"); - } - - return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); - } - - // - // POST: /Account/VerifyCode - [HttpPost] - [AllowAnonymous] - [ValidateAntiForgeryToken] - public async Task VerifyCode(VerifyCodeViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); - - // The following code protects for brute force attacks against the two factor codes. - // If a user enters incorrect codes for a specified amount of time then the user account - // will be locked out for a specified amount of time. - var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); - if (result.Succeeded) - { - // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName, clientId: context?.Client.ClientId)); - - // only set explicit expiration here if user chooses "remember me". - // otherwise we rely upon expiration configured in cookie middleware. - AuthenticationProperties props = null; - if (AccountOptions.AllowRememberLogin && model.RememberMe) - { - props = new AuthenticationProperties - { - IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) - }; - } - - // issue authentication cookie with subject ID and username - var isuser = new IdentityServerUser(user.Id.ToString()) - { - DisplayName = user.UserName, - }; - - await HttpContext.SignInAsync(isuser, props); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", model.ReturnUrl); - } - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - return Redirect(model.ReturnUrl); - } - - // request for a local page - if (Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - return RedirectToLocal(model.ReturnUrl); - } - - if (result.IsLockedOut) - { - _logger.LogWarning(7, "User account locked out."); - return View("Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid code."); - return View(model); - } - } - - - /// - /// Show logout page - /// - [HttpGet] - public async Task Logout(string logoutId) - { - // build a model so the logout page knows what to display - var vm = await BuildLogoutViewModelAsync(logoutId); - - if (vm.ShowLogoutPrompt == false) - { - // if the request for logout was properly authenticated from IdentityServer, then - // we don't need to show the prompt and can just log the user out directly. - return await Logout(vm); - } - - return View(vm); - } - - /// - /// Handle logout page postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Logout(LogoutInputModel model) - { - // build a model so the logged out page knows what to display - var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); - - if (User?.Identity.IsAuthenticated == true) - { - // delete local authentication cookie - await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme); - - // raise the logout event - await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); - } - - // check if we need to trigger sign-out at an upstream identity provider - if (vm.TriggerExternalSignout) - { - // build a return URL so the upstream provider will redirect back - // to us after the user has logged out. this allows us to then - // complete our single sign-out processing. - string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); - - // this triggers a redirect to the external provider for sign-out - return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); - } - - return View("LoggedOut", vm); - } - - [HttpGet] - public IActionResult AccessDenied() - { - return View(); - } - - - /*****************************************/ - /* helper APIs for the AccountController */ - /*****************************************/ - private async Task BuildLoginViewModelAsync(string returnUrl) - { - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) - { - var local = context.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider; - - // this is meant to short circuit the UI and only trigger the one external IdP - var vm = new LoginViewModel - { - EnableLocalLogin = local, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - }; - - if (!local) - { - vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; - } - - return vm; - } - - var schemes = await _schemeProvider.GetAllSchemesAsync(); - - var providers = schemes - .Where(x => x.DisplayName != null) - .Select(x => new ExternalProvider - { - DisplayName = x.DisplayName ?? x.Name, - AuthenticationScheme = x.Name, - }).ToList(); - - var allowLocal = true; - if (context?.Client.ClientId != null) - { - var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); - if (client != null) - { - allowLocal = client.EnableLocalLogin; - - if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) - { - providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); - } - } - } - - return new LoginViewModel - { - AllowRememberLogin = AccountOptions.AllowRememberLogin, - EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - ExternalProviders = providers.ToArray(), - }; - } - - private async Task BuildLoginViewModelAsync(LoginInputModel model) - { - var vm = await BuildLoginViewModelAsync(model.ReturnUrl); - vm.Username = model.Username; - vm.RememberLogin = model.RememberLogin; - return vm; - } - - private async Task BuildLogoutViewModelAsync(string logoutId) - { - var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; - - if (User?.Identity.IsAuthenticated != true) - { - // if the user is not authenticated, then just show logged out page - vm.ShowLogoutPrompt = false; - return vm; - } - - var context = await _interaction.GetLogoutContextAsync(logoutId); - if (context?.ShowSignoutPrompt == false) - { - // it's safe to automatically sign-out - vm.ShowLogoutPrompt = false; - return vm; - } - - // show the logout prompt. this prevents attacks where the user - // is automatically signed out by another malicious web page. - return vm; - } - - private async Task BuildLoggedOutViewModelAsync(string logoutId) - { - // get context information (client name, post logout redirect URI and iframe for federated signout) - var logout = await _interaction.GetLogoutContextAsync(logoutId); - - var vm = new LoggedOutViewModel - { - AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, - PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, - ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, - SignOutIframeUrl = logout?.SignOutIFrameUrl, - LogoutId = logoutId, - }; - - if (User?.Identity.IsAuthenticated == true) - { - var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; - if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider) - { - var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); - if (providerSupportsSignout) - { - if (vm.LogoutId == null) - { - // if there's no current logout context, we need to create one - // this captures necessary info from the current logged in user - // before we signout and redirect away to the external IdP for signout - vm.LogoutId = await _interaction.CreateLogoutContextAsync(); - } - - vm.ExternalAuthenticationScheme = idp; - } - } - } - - return vm; - } - - [HttpGet] - public IActionResult Register() - { - return View(); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Register(RegisterModel model) - { - if (!ModelState.IsValid) - { - return View(); - } - - var user = await _userManager.FindByNameAsync(model.UserName); - - if (user != null) - { - return View("Success"); - } - - user = new User - { - UserName = model.UserName, - Email = model.UserName, - }; - - var result = await _userManager.CreateAsync(user, model.Password); - - if (!result.Succeeded) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - - return View(); - } - - var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var confirmationEmail = Url.Action("ConfirmEmailAddress", "Account", - new { token = token, email = user.Email }, Request.Scheme); - - await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand(new EmailMessage - { - From = "phong@gmail.com", - Tos = user.Email, - Subject = "Confirmation Email", - Body = string.Format("Confirmation Email: {0}", confirmationEmail), - } - )); - - return View("Success"); - } - - [HttpGet] - public async Task ConfirmEmailAddress(string token, string email) - { - var user = await _userManager.FindByEmailAsync(email); - - if (user == null) - { - return View("Error"); - } - - var result = await _userManager.ConfirmEmailAsync(user, token); - - if (result.Succeeded) - { - return View("Success"); - } - - return View("Error"); - } - - [HttpGet] - public IActionResult ForgotPassword() - { - return View(); - } - - [HttpPost] - public async Task ForgotPassword(ForgotPasswordModel model) - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(model.Email); - - if (user != null) - { - var token = await _userManager.GeneratePasswordResetTokenAsync(user); - var resetUrl = Url.Action("ResetPassword", "Account", - new { token = token, email = user.Email }, Request.Scheme); - - await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand(new EmailMessage - { - From = "phong@gmail.com", - Tos = user.Email, - Subject = "Forgot Password", - Body = string.Format("Reset Url: {0}", resetUrl), - })); - } - else - { - // email user and inform them that they do not have an account - } - - return View("Success"); - } - - return View(); - } - - [HttpGet] - public IActionResult ResetPassword(string token, string email) - { - return View(new ResetPasswordModel { Token = token, Email = email }); - } - - [HttpPost] - public async Task ResetPassword(ResetPasswordModel model) - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(model.Email); - - if (user != null) - { - var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password); - - if (!result.Succeeded) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - - return View(); - } - - return View("Success"); - } - - ModelState.AddModelError(string.Empty, "Invalid Request"); - } - - return View(); - } - - private IActionResult RedirectToLocal(string returnUrl) - { - if (Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - else - { - return RedirectToAction(nameof(HomeController.Index), "Home"); - } - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountOptions.cs deleted file mode 100644 index 5a8cafff8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/AccountOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; - -namespace IdentityServerHost.Quickstart.UI -{ - public class AccountOptions - { - public static bool AllowLocalLogin = true; - public static bool AllowRememberLogin = true; - public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); - - public static bool ShowLogoutPrompt = true; - public static bool AutomaticRedirectAfterSignOut = true; - - public static string InvalidCredentialsErrorMessage = "Invalid username or password"; - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs deleted file mode 100644 index e7dd21bf1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalController.cs +++ /dev/null @@ -1,218 +0,0 @@ -using ClassifiedAds.Modules.Identity.Entities; -using IdentityModel; -using IdentityServer4; -using IdentityServer4.Events; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [AllowAnonymous] - public class ExternalController : Controller - { - private readonly UserManager _userManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly ILogger _logger; - private readonly IEventService _events; - - public ExternalController( - UserManager userManager, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IEventService events, - ILogger logger) - { - _userManager = userManager; - _interaction = interaction; - _clientStore = clientStore; - _logger = logger; - _events = events; - } - - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public IActionResult Challenge(string scheme, string returnUrl) - { - if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; - - // validate returnUrl - either it is a valid OIDC URL or back to a local page - if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - // start challenge and roundtrip the return URL and scheme - var props = new AuthenticationProperties - { - RedirectUri = Url.Action(nameof(Callback)), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", scheme }, - } - }; - - return Challenge(props, scheme); - - } - - /// - /// Post processing of external authentication - /// - [HttpGet] - public async Task Callback() - { - // read external identity from the temporary cookie - var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); - if (result?.Succeeded != true) - { - throw new Exception("External authentication error"); - } - - if (_logger.IsEnabled(LogLevel.Debug)) - { - var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); - _logger.LogDebug("External claims: {@claims}", externalClaims); - } - - // lookup our user and external provider info - var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); - if (user == null) - { - // this might be where you might initiate a custom workflow for user registration - // in this sample we don't show how that would be done, as our sample implementation - // simply auto-provisions new external user - user = AutoProvisionUser(provider, providerUserId, claims); - } - - // this allows us to collect any additional claims or properties - // for the specific protocols used and store them in the local auth cookie. - // this is typically used to store data needed for signout from those protocols. - var additionalLocalClaims = new List(); - var localSignInProps = new AuthenticationProperties(); - ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); - - // issue authentication cookie for user - var isuser = new IdentityServerUser(user.Id.ToString()) - { - DisplayName = user.UserName, - IdentityProvider = provider, - AdditionalClaims = additionalLocalClaims, - }; - - await HttpContext.SignInAsync(isuser, localSignInProps); - - // delete temporary cookie used during external authentication - await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); - - // retrieve return URL - var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; - - // check if external login is in the context of an OIDC request - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id.ToString(), user.UserName, true, context?.Client.ClientId)); - - if (context != null) - { - if (context.IsNativeClient()) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", returnUrl); - } - } - - return Redirect(returnUrl); - } - - private (User user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) - { - var externalUser = result.Principal; - var provider = result.Properties.Items["scheme"]; - - // try to determine the unique id of the external user (issued by the provider) - // the most common claim type for that are the sub claim and the NameIdentifier - // depending on the external provider, some other claim type might be used - var emailClaim = externalUser.FindFirst(ClaimTypes.Email) ?? - externalUser.FindFirst(JwtClaimTypes.Subject) ?? - externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? - throw new Exception("Unknown userid"); - - if (provider == "AAD") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Upn); - } - else if (provider == "Microsoft") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Email); - } - else if (provider == "Google") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Email); - } - else if (provider == "Facebook") - { - emailClaim = externalUser.FindFirst(ClaimTypes.Email); - } - - // remove the user id claim so we don't include it as an extra claim if/when we provision the user - var claims = externalUser.Claims.ToList(); - claims.Remove(emailClaim); - - var providerUserEmail = emailClaim.Value; - - // find external user - var user = _userManager.FindByEmailAsync(providerUserEmail).GetAwaiter().GetResult(); - - return (user, provider, providerUserEmail, claims); - } - - private User AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) - { - var user = new User - { - UserName = providerUserId, - Email = providerUserId, - }; - var rs = _userManager.CreateAsync(user, "4SBbS]#Nc3*Dca").GetAwaiter().GetResult(); - return user; - } - - // if the external login is OIDC-based, there are certain things we need to preserve to make logout work - // this will be different for WS-Fed, SAML2p or other protocols - private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - // if the external system sent a session id claim, copy it over - // so we can use it for single sign-out - var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); - if (sid != null) - { - localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); - } - - // if the external provider issued an id_token, we'll keep it for signout - var idToken = externalResult.Properties.GetTokenValue("id_token"); - if (idToken != null) - { - localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); - } - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalProvider.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalProvider.cs deleted file mode 100644 index a18311372..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/ExternalProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class ExternalProvider - { - public string DisplayName { get; set; } - public string AuthenticationScheme { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoggedOutViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoggedOutViewModel.cs deleted file mode 100644 index 63688328c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoggedOutViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class LoggedOutViewModel - { - public string PostLogoutRedirectUri { get; set; } - public string ClientName { get; set; } - public string SignOutIframeUrl { get; set; } - - public bool AutomaticRedirectAfterSignOut { get; set; } - - public string LogoutId { get; set; } - public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; - public string ExternalAuthenticationScheme { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoginInputModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoginInputModel.cs deleted file mode 100644 index bcb7853a1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoginInputModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.ComponentModel.DataAnnotations; - -namespace IdentityServerHost.Quickstart.UI -{ - public class LoginInputModel - { - [Required] - public string Username { get; set; } - [Required] - public string Password { get; set; } - public bool RememberLogin { get; set; } - public string ReturnUrl { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoginViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoginViewModel.cs deleted file mode 100644 index 9bf6c8f3f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LoginViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IdentityServerHost.Quickstart.UI -{ - public class LoginViewModel : LoginInputModel - { - public bool AllowRememberLogin { get; set; } = true; - public bool EnableLocalLogin { get; set; } = true; - - public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); - public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); - - public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; - public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutInputModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutInputModel.cs deleted file mode 100644 index bb74202e2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutInputModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class LogoutInputModel - { - public string LogoutId { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutViewModel.cs deleted file mode 100644 index 236cd6c88..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/LogoutViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class LogoutViewModel : LogoutInputModel - { - public bool ShowLogoutPrompt { get; set; } = true; - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/RedirectViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/RedirectViewModel.cs deleted file mode 100644 index 904ae2060..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/RedirectViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - - -namespace IdentityServerHost.Quickstart.UI -{ - public class RedirectViewModel - { - public string RedirectUrl { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/SendCodeViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/SendCodeViewModel.cs deleted file mode 100644 index ad162c695..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/SendCodeViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Rendering; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Quickstart.Account -{ - public class SendCodeViewModel - { - public string SelectedProvider { get; set; } - - public ICollection Providers { get; set; } - - public string ReturnUrl { get; set; } - - public bool RememberMe { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/VerifyCodeViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/VerifyCodeViewModel.cs deleted file mode 100644 index 945feda63..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Account/VerifyCodeViewModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Quickstart.Account -{ - public class VerifyCodeViewModel - { - [Required] - public string Provider { get; set; } - - [Required] - public string Code { get; set; } - - public string ReturnUrl { get; set; } - - [Display(Name = "Remember this browser?")] - public bool RememberBrowser { get; set; } - - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentController.cs deleted file mode 100644 index d14c2f1f3..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentController.cs +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Events; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Extensions; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Validation; -using System.Collections.Generic; -using System; - -namespace IdentityServerHost.Quickstart.UI -{ - /// - /// This controller processes the consent UI - /// - [SecurityHeaders] - [Authorize] - public class ConsentController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IEventService _events; - private readonly ILogger _logger; - - public ConsentController( - IIdentityServerInteractionService interaction, - IEventService events, - ILogger logger) - { - _interaction = interaction; - _events = events; - _logger = logger; - } - - /// - /// Shows the consent screen - /// - /// - /// - [HttpGet] - public async Task Index(string returnUrl) - { - var vm = await BuildViewModelAsync(returnUrl); - if (vm != null) - { - return View("Index", vm); - } - - return View("Error"); - } - - /// - /// Handles the consent screen postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Index(ConsentInputModel model) - { - var result = await ProcessConsent(model); - - if (result.IsRedirect) - { - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - if (context?.IsNativeClient() == true) - { - // The client is native, so this change in how to - // return the response is for better UX for the end user. - return this.LoadingPage("Redirect", result.RedirectUri); - } - - return Redirect(result.RedirectUri); - } - - if (result.HasValidationError) - { - ModelState.AddModelError(string.Empty, result.ValidationError); - } - - if (result.ShowView) - { - return View("Index", result.ViewModel); - } - - return View("Error"); - } - - /*****************************************/ - /* helper APIs for the ConsentController */ - /*****************************************/ - private async Task ProcessConsent(ConsentInputModel model) - { - var result = new ProcessConsentResult(); - - // validate return url is still valid - var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - if (request == null) return result; - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model?.Button == "no") - { - grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; - - // emit event - await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); - } - // user clicked 'yes' - validate the data - else if (model?.Button == "yes") - { - // if the user consented to some scope, build the response model - if (model.ScopesConsented != null && model.ScopesConsented.Any()) - { - var scopes = model.ScopesConsented; - if (ConsentOptions.EnableOfflineAccess == false) - { - scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); - } - - grantedConsent = new ConsentResponse - { - RememberConsent = model.RememberConsent, - ScopesValuesConsented = scopes.ToArray(), - Description = model.Description, - }; - - // emit event - await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // communicate outcome of consent back to identityserver - await _interaction.GrantConsentAsync(request, grantedConsent); - - // indicate that's it ok to redirect back to authorization endpoint - result.RedirectUri = model.ReturnUrl; - result.Client = request.Client; - } - else - { - // we need to redisplay the consent UI - result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); - } - - return result; - } - - private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) - { - var request = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (request != null) - { - return CreateConsentViewModel(model, returnUrl, request); - } - else - { - _logger.LogError("No consent request matching request: {0}", returnUrl); - } - - return null; - } - - private ConsentViewModel CreateConsentViewModel( - ConsentInputModel model, string returnUrl, - AuthorizationRequest request) - { - var vm = new ConsentViewModel - { - RememberConsent = model?.RememberConsent ?? true, - ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - Description = model?.Description, - - ReturnUrl = returnUrl, - - ClientName = request.Client.ClientName ?? request.Client.ClientId, - ClientUrl = request.Client.ClientUri, - ClientLogoUrl = request.Client.LogoUri, - AllowRememberConsent = request.Client.AllowRememberConsent - }; - - vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - - var apiScopes = new List(); - foreach(var parsedScope in request.ValidatedResources.ParsedScopes) - { - var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); - if (apiScope != null) - { - var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); - apiScopes.Add(scopeVm); - } - } - if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) - { - apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); - } - vm.ApiScopes = apiScopes; - - return vm; - } - - private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) - { - return new ScopeViewModel - { - Value = identity.Name, - DisplayName = identity.DisplayName ?? identity.Name, - Description = identity.Description, - Emphasize = identity.Emphasize, - Required = identity.Required, - Checked = check || identity.Required - }; - } - - public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) - { - var displayName = apiScope.DisplayName ?? apiScope.Name; - if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) - { - displayName += ":" + parsedScopeValue.ParsedParameter; - } - - return new ScopeViewModel - { - Value = parsedScopeValue.RawValue, - DisplayName = displayName, - Description = apiScope.Description, - Emphasize = apiScope.Emphasize, - Required = apiScope.Required, - Checked = check || apiScope.Required - }; - } - - private ScopeViewModel GetOfflineAccessScope(bool check) - { - return new ScopeViewModel - { - Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, - DisplayName = ConsentOptions.OfflineAccessDisplayName, - Description = ConsentOptions.OfflineAccessDescription, - Emphasize = true, - Checked = check - }; - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentInputModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentInputModel.cs deleted file mode 100644 index f608fe3bb..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentInputModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ConsentInputModel - { - public string Button { get; set; } - public IEnumerable ScopesConsented { get; set; } - public bool RememberConsent { get; set; } - public string ReturnUrl { get; set; } - public string Description { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentOptions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentOptions.cs deleted file mode 100644 index 998c51dca..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class ConsentOptions - { - public static bool EnableOfflineAccess = true; - public static string OfflineAccessDisplayName = "Offline Access"; - public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; - - public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; - public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentViewModel.cs deleted file mode 100644 index af4b9c5c9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ConsentViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ConsentViewModel : ConsentInputModel - { - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public bool AllowRememberConsent { get; set; } - - public IEnumerable IdentityScopes { get; set; } - public IEnumerable ApiScopes { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ProcessConsentResult.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ProcessConsentResult.cs deleted file mode 100644 index 1d331df04..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ProcessConsentResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; - -namespace IdentityServerHost.Quickstart.UI -{ - public class ProcessConsentResult - { - public bool IsRedirect => RedirectUri != null; - public string RedirectUri { get; set; } - public Client Client { get; set; } - - public bool ShowView => ViewModel != null; - public ConsentViewModel ViewModel { get; set; } - - public bool HasValidationError => ValidationError != null; - public string ValidationError { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ScopeViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ScopeViewModel.cs deleted file mode 100644 index 532d1b1a8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Consent/ScopeViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class ScopeViewModel - { - public string Value { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public bool Emphasize { get; set; } - public bool Required { get; set; } - public bool Checked { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs deleted file mode 100644 index a221181e8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class DeviceAuthorizationInputModel : ConsentInputModel - { - public string UserCode { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs deleted file mode 100644 index 3e8857f65..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServerHost.Quickstart.UI -{ - public class DeviceAuthorizationViewModel : ConsentViewModel - { - public string UserCode { get; set; } - public bool ConfirmUserCode { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceController.cs deleted file mode 100644 index d7d07aeb9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Device/DeviceController.cs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Configuration; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Validation; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace IdentityServerHost.Quickstart.UI -{ - [Authorize] - [SecurityHeaders] - public class DeviceController : Controller - { - private readonly IDeviceFlowInteractionService _interaction; - private readonly IEventService _events; - private readonly IOptions _options; - private readonly ILogger _logger; - - public DeviceController( - IDeviceFlowInteractionService interaction, - IEventService eventService, - IOptions options, - ILogger logger) - { - _interaction = interaction; - _events = eventService; - _options = options; - _logger = logger; - } - - [HttpGet] - public async Task Index() - { - string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; - string userCode = Request.Query[userCodeParamName]; - if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); - - var vm = await BuildViewModelAsync(userCode); - if (vm == null) return View("Error"); - - vm.ConfirmUserCode = true; - return View("UserCodeConfirmation", vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task UserCodeCapture(string userCode) - { - var vm = await BuildViewModelAsync(userCode); - if (vm == null) return View("Error"); - - return View("UserCodeConfirmation", vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Callback(DeviceAuthorizationInputModel model) - { - if (model == null) throw new ArgumentNullException(nameof(model)); - - var result = await ProcessConsent(model); - if (result.HasValidationError) return View("Error"); - - return View("Success"); - } - - private async Task ProcessConsent(DeviceAuthorizationInputModel model) - { - var result = new ProcessConsentResult(); - - var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); - if (request == null) return result; - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model.Button == "no") - { - grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; - - // emit event - await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); - } - // user clicked 'yes' - validate the data - else if (model.Button == "yes") - { - // if the user consented to some scope, build the response model - if (model.ScopesConsented != null && model.ScopesConsented.Any()) - { - var scopes = model.ScopesConsented; - if (ConsentOptions.EnableOfflineAccess == false) - { - scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); - } - - grantedConsent = new ConsentResponse - { - RememberConsent = model.RememberConsent, - ScopesValuesConsented = scopes.ToArray(), - Description = model.Description - }; - - // emit event - await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // communicate outcome of consent back to identityserver - await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); - - // indicate that's it ok to redirect back to authorization endpoint - result.RedirectUri = model.ReturnUrl; - result.Client = request.Client; - } - else - { - // we need to redisplay the consent UI - result.ViewModel = await BuildViewModelAsync(model.UserCode, model); - } - - return result; - } - - private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) - { - var request = await _interaction.GetAuthorizationContextAsync(userCode); - if (request != null) - { - return CreateConsentViewModel(userCode, model, request); - } - - return null; - } - - private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) - { - var vm = new DeviceAuthorizationViewModel - { - UserCode = userCode, - Description = model?.Description, - - RememberConsent = model?.RememberConsent ?? true, - ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - - ClientName = request.Client.ClientName ?? request.Client.ClientId, - ClientUrl = request.Client.ClientUri, - ClientLogoUrl = request.Client.LogoUri, - AllowRememberConsent = request.Client.AllowRememberConsent - }; - - vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - - var apiScopes = new List(); - foreach (var parsedScope in request.ValidatedResources.ParsedScopes) - { - var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); - if (apiScope != null) - { - var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); - apiScopes.Add(scopeVm); - } - } - if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) - { - apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); - } - vm.ApiScopes = apiScopes; - - return vm; - } - - private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) - { - return new ScopeViewModel - { - Value = identity.Name, - DisplayName = identity.DisplayName ?? identity.Name, - Description = identity.Description, - Emphasize = identity.Emphasize, - Required = identity.Required, - Checked = check || identity.Required - }; - } - - public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) - { - return new ScopeViewModel - { - Value = parsedScopeValue.RawValue, - // todo: use the parsed scope value in the display? - DisplayName = apiScope.DisplayName ?? apiScope.Name, - Description = apiScope.Description, - Emphasize = apiScope.Emphasize, - Required = apiScope.Required, - Checked = check || apiScope.Required - }; - } - private ScopeViewModel GetOfflineAccessScope(bool check) - { - return new ScopeViewModel - { - Value = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, - DisplayName = ConsentOptions.OfflineAccessDisplayName, - Description = ConsentOptions.OfflineAccessDescription, - Emphasize = true, - Checked = check - }; - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs deleted file mode 100644 index 57c2f55f9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace IdentityServerHost.Quickstart.UI -{ - [SecurityHeaders] - [Authorize] - public class DiagnosticsController : Controller - { - public async Task Index() - { - var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; - if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) - { - return NotFound(); - } - - var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); - return View(model); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs deleted file mode 100644 index 70b5c0578..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityModel; -using Microsoft.AspNetCore.Authentication; -using System.Collections.Generic; -using System.Text; -using System.Text.Json; - -namespace IdentityServerHost.Quickstart.UI -{ - public class DiagnosticsViewModel - { - public DiagnosticsViewModel(AuthenticateResult result) - { - AuthenticateResult = result; - - if (result.Properties.Items.ContainsKey("client_list")) - { - var encoded = result.Properties.Items["client_list"]; - var bytes = Base64Url.Decode(encoded); - var value = Encoding.UTF8.GetString(bytes); - - Clients = JsonSerializer.Deserialize(value); - } - } - - public AuthenticateResult AuthenticateResult { get; } - public IEnumerable Clients { get; } = new List(); - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Extensions.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Extensions.cs deleted file mode 100644 index 6c720b707..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Extensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using IdentityServer4.Models; -using Microsoft.AspNetCore.Mvc; - -namespace IdentityServerHost.Quickstart.UI -{ - public static class Extensions - { - /// - /// Checks if the redirect URI is for a native client. - /// - /// - public static bool IsNativeClient(this AuthorizationRequest context) - { - return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) - && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); - } - - public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) - { - controller.HttpContext.Response.StatusCode = 200; - controller.HttpContext.Response.Headers["Location"] = ""; - - return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsController.cs deleted file mode 100644 index 128ce5921..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsController.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using IdentityServer4.Events; -using IdentityServer4.Extensions; - -namespace IdentityServerHost.Quickstart.UI -{ - /// - /// This sample controller allows a user to revoke grants given to clients - /// - [SecurityHeaders] - [Authorize] - public class GrantsController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clients; - private readonly IResourceStore _resources; - private readonly IEventService _events; - - public GrantsController(IIdentityServerInteractionService interaction, - IClientStore clients, - IResourceStore resources, - IEventService events) - { - _interaction = interaction; - _clients = clients; - _resources = resources; - _events = events; - } - - /// - /// Show list of grants - /// - [HttpGet] - public async Task Index() - { - return View("Index", await BuildViewModelAsync()); - } - - /// - /// Handle postback to revoke a client - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Revoke(string clientId) - { - await _interaction.RevokeUserConsentAsync(clientId); - await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - - return RedirectToAction("Index"); - } - - private async Task BuildViewModelAsync() - { - var grants = await _interaction.GetAllUserGrantsAsync(); - - var list = new List(); - foreach(var grant in grants) - { - var client = await _clients.FindClientByIdAsync(grant.ClientId); - if (client != null) - { - var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); - - var item = new GrantViewModel() - { - ClientId = client.ClientId, - ClientName = client.ClientName ?? client.ClientId, - ClientLogoUrl = client.LogoUri, - ClientUrl = client.ClientUri, - Description = grant.Description, - Created = grant.CreationTime, - Expires = grant.Expiration, - IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), - ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() - }; - - list.Add(item); - } - } - - return new GrantsViewModel - { - Grants = list - }; - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsViewModel.cs deleted file mode 100644 index 2114deebf..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Grants/GrantsViewModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; - -namespace IdentityServerHost.Quickstart.UI -{ - public class GrantsViewModel - { - public IEnumerable Grants { get; set; } - } - - public class GrantViewModel - { - public string ClientId { get; set; } - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public string Description { get; set; } - public DateTime Created { get; set; } - public DateTime? Expires { get; set; } - public IEnumerable IdentityGrantNames { get; set; } - public IEnumerable ApiGrantNames { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Home/ErrorViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Home/ErrorViewModel.cs deleted file mode 100644 index c1a27b8e6..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Home/ErrorViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; - -namespace IdentityServer4.Quickstart.UI -{ - public class ErrorViewModel - { - public ErrorMessage Error { get; set; } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Home/HomeController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Home/HomeController.cs deleted file mode 100644 index e14648bac..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Home/HomeController.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using IdentityServerHost.Quickstart.UI; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; - -namespace IdentityServer4.Quickstart.UI -{ - [SecurityHeaders] - [AllowAnonymous] - public class HomeController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IHostingEnvironment _environment; - private readonly ILogger _logger; - - public HomeController(IIdentityServerInteractionService interaction, IHostingEnvironment environment, ILogger logger) - { - _interaction = interaction; - _environment = environment; - _logger = logger; - } - - public IActionResult Index() - { - return View(); - } - - /// - /// Shows the error page - /// - public async Task Error(string errorId) - { - var vm = new ErrorViewModel(); - - // retrieve error details from identityserver - var message = await _interaction.GetErrorContextAsync(errorId); - if (message != null) - { - vm.Error = message; - - if (!_environment.IsDevelopment()) - { - // only show in development - message.ErrorDescription = null; - } - } - - return View("Error", vm); - } - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/AddPhoneNumberViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/AddPhoneNumberViewModel.cs deleted file mode 100644 index e863df704..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/AddPhoneNumberViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class AddPhoneNumberViewModel - { - [Required] - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ChangePasswordViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ChangePasswordViewModel.cs deleted file mode 100644 index 67df14cfa..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ChangePasswordViewModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class ChangePasswordViewModel - { - [Required] - [DataType(DataType.Password)] - [Display(Name = "Current password")] - public string OldPassword { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs deleted file mode 100644 index be7ca0d8c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ConfigureTwoFactorViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class ConfigureTwoFactorViewModel - { - public string SelectedProvider { get; set; } - - public ICollection Providers { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs deleted file mode 100644 index a6b23ce1e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/DisplayRecoveryCodesViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class DisplayRecoveryCodesViewModel - { - [Required] - public IEnumerable Codes { get; set; } - - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/FactorViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/FactorViewModel.cs deleted file mode 100644 index f5fb884a8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/FactorViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class FactorViewModel - { - public string Purpose { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/IndexViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/IndexViewModel.cs deleted file mode 100644 index 231d202df..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/IndexViewModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Identity; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class IndexViewModel - { - public bool HasPassword { get; set; } - - public IList Logins { get; set; } - - public string PhoneNumber { get; set; } - - public bool TwoFactor { get; set; } - - public bool BrowserRemembered { get; set; } - - public string AuthenticatorKey { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs deleted file mode 100644 index ce0e81b3f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageController.cs +++ /dev/null @@ -1,382 +0,0 @@ -using ClassifiedAds.Application; -using ClassifiedAds.IdentityServer.Manage.Models; -using ClassifiedAds.Modules.Identity.Entities; -using ClassifiedAds.Modules.Notification.Entities; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Quickstart.Manage -{ - [Authorize] - public class ManageController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly Dispatcher _dispatcher; - private readonly ILogger _logger; - - public ManageController( - UserManager userManager, - SignInManager signInManager, - ILoggerFactory loggerFactory, - Dispatcher dispatcher) - { - _userManager = userManager; - _signInManager = signInManager; - _dispatcher = dispatcher; - _logger = loggerFactory.CreateLogger(); - } - - // - // GET: /Manage/Index - [HttpGet] - public async Task Index(ManageMessageId? message = null) - { - ViewData["StatusMessage"] = - message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed." - : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set." - : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set." - : message == ManageMessageId.Error ? "An error has occurred." - : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added." - : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed." - : ""; - - var user = await GetCurrentUserAsync(); - var model = new IndexViewModel - { - HasPassword = await _userManager.HasPasswordAsync(user), - PhoneNumber = await _userManager.GetPhoneNumberAsync(user), - TwoFactor = await _userManager.GetTwoFactorEnabledAsync(user), - //Logins = await _userManager.GetLoginsAsync(user), - Logins = new List(), - BrowserRemembered = await _signInManager.IsTwoFactorClientRememberedAsync(user), - AuthenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user) - }; - return View(model); - } - - // - // POST: /Manage/RemoveLogin - [HttpPost] - [ValidateAntiForgeryToken] - public async Task RemoveLogin(RemoveLoginViewModel account) - { - ManageMessageId? message = ManageMessageId.Error; - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.RemoveLoginAsync(user, account.LoginProvider, account.ProviderKey); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - message = ManageMessageId.RemoveLoginSuccess; - } - } - return RedirectToAction(nameof(ManageLogins), new { Message = message }); - } - - // - // GET: /Manage/AddPhoneNumber - public IActionResult AddPhoneNumber() - { - return View(); - } - - // - // POST: /Manage/AddPhoneNumber - [HttpPost] - [ValidateAntiForgeryToken] - public async Task AddPhoneNumber(AddPhoneNumberViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - // Generate the token and send it - var user = await GetCurrentUserAsync(); - var code = await _userManager.GenerateChangePhoneNumberTokenAsync(user, model.PhoneNumber); - await _dispatcher.DispatchAsync(new AddOrUpdateEntityCommand(new SmsMessage - { - PhoneNumber = model.PhoneNumber, - Message = "Your security code is: " + code, - })); - return RedirectToAction(nameof(VerifyPhoneNumber), new { PhoneNumber = model.PhoneNumber }); - } - - // - // POST: /Manage/ResetAuthenticatorKey - [HttpPost] - [ValidateAntiForgeryToken] - public async Task ResetAuthenticatorKey() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - await _userManager.ResetAuthenticatorKeyAsync(user); - _logger.LogInformation(1, "User reset authenticator key."); - } - return RedirectToAction(nameof(Index), "Manage"); - } - - // - // POST: /Manage/GenerateRecoveryCode - [HttpPost] - [ValidateAntiForgeryToken] - public async Task GenerateRecoveryCode() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - var codes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 5); - _logger.LogInformation(1, "User generated new recovery code."); - return View("DisplayRecoveryCodes", new DisplayRecoveryCodesViewModel { Codes = codes }); - } - return View("Error"); - } - - // - // POST: /Manage/EnableTwoFactorAuthentication - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EnableTwoFactorAuthentication() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - await _userManager.SetTwoFactorEnabledAsync(user, true); - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation(1, "User enabled two-factor authentication."); - } - return RedirectToAction(nameof(Index), "Manage"); - } - - // - // POST: /Manage/DisableTwoFactorAuthentication - [HttpPost] - [ValidateAntiForgeryToken] - public async Task DisableTwoFactorAuthentication() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - await _userManager.SetTwoFactorEnabledAsync(user, false); - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation(2, "User disabled two-factor authentication."); - } - return RedirectToAction(nameof(Index), "Manage"); - } - - // - // GET: /Manage/VerifyPhoneNumber - [HttpGet] - public async Task VerifyPhoneNumber(string phoneNumber) - { - var code = await _userManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber); - // Send an SMS to verify the phone number - return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber }); - } - - // - // POST: /Manage/VerifyPhoneNumber - [HttpPost] - [ValidateAntiForgeryToken] - public async Task VerifyPhoneNumber(VerifyPhoneNumberViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AddPhoneSuccess }); - } - } - // If we got this far, something failed, redisplay the form - ModelState.AddModelError(string.Empty, "Failed to verify phone number"); - return View(model); - } - - // - // GET: /Manage/RemovePhoneNumber - [HttpPost] - [ValidateAntiForgeryToken] - public async Task RemovePhoneNumber() - { - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.SetPhoneNumberAsync(user, null); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess }); - } - } - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); - } - - // - // GET: /Manage/ChangePassword - [HttpGet] - public IActionResult ChangePassword() - { - return View(); - } - - // - // POST: /Manage/ChangePassword - [HttpPost] - [ValidateAntiForgeryToken] - public async Task ChangePassword(ChangePasswordViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - _logger.LogInformation(3, "User changed their password successfully."); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangePasswordSuccess }); - } - AddErrors(result); - return View(model); - } - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); - } - - // - // GET: /Manage/SetPassword - [HttpGet] - public IActionResult SetPassword() - { - return View(); - } - - // - // POST: /Manage/SetPassword - [HttpPost] - [ValidateAntiForgeryToken] - public async Task SetPassword(SetPasswordViewModel model) - { - if (!ModelState.IsValid) - { - return View(model); - } - - var user = await GetCurrentUserAsync(); - if (user != null) - { - var result = await _userManager.AddPasswordAsync(user, model.NewPassword); - if (result.Succeeded) - { - await _signInManager.SignInAsync(user, isPersistent: false); - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetPasswordSuccess }); - } - AddErrors(result); - return View(model); - } - return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error }); - } - - //GET: /Manage/ManageLogins - [HttpGet] - public async Task ManageLogins(ManageMessageId? message = null) - { - ViewData["StatusMessage"] = - message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed." - : message == ManageMessageId.AddLoginSuccess ? "The external login was added." - : message == ManageMessageId.Error ? "An error has occurred." - : ""; - var user = await GetCurrentUserAsync(); - if (user == null) - { - return View("Error"); - } - var userLogins = await _userManager.GetLoginsAsync(user); - var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync(); - var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList(); - ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1; - return View(new ManageLoginsViewModel - { - CurrentLogins = userLogins, - OtherLogins = otherLogins - }); - } - - // - // POST: /Manage/LinkLogin - [HttpPost] - [ValidateAntiForgeryToken] - public IActionResult LinkLogin(string provider) - { - // Request a redirect to the external login provider to link a login for the current user - var redirectUrl = Url.Action("LinkLoginCallback", "Manage"); - var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User)); - return Challenge(properties, provider); - } - - // - // GET: /Manage/LinkLoginCallback - [HttpGet] - public async Task LinkLoginCallback() - { - var user = await GetCurrentUserAsync(); - if (user == null) - { - return View("Error"); - } - var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user)); - if (info == null) - { - return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error }); - } - var result = await _userManager.AddLoginAsync(user, info); - var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error; - return RedirectToAction(nameof(ManageLogins), new { Message = message }); - } - - #region Helpers - - private void AddErrors(IdentityResult result) - { - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - public enum ManageMessageId - { - AddPhoneSuccess, - AddLoginSuccess, - ChangePasswordSuccess, - SetTwoFactorSuccess, - SetPasswordSuccess, - RemoveLoginSuccess, - RemovePhoneSuccess, - Error - } - - private Task GetCurrentUserAsync() - { - return _userManager.GetUserAsync(HttpContext.User); - } - - #endregion - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageLoginsViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageLoginsViewModel.cs deleted file mode 100644 index ad361a6f9..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/ManageLoginsViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class ManageLoginsViewModel - { - public IList CurrentLogins { get; set; } - - public IList OtherLogins { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/RemoveLoginViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/RemoveLoginViewModel.cs deleted file mode 100644 index 7e7cd720a..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/RemoveLoginViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class RemoveLoginViewModel - { - public string LoginProvider { get; set; } - public string ProviderKey { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/SetPasswordViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/SetPasswordViewModel.cs deleted file mode 100644 index f551e7175..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/SetPasswordViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class SetPasswordViewModel - { - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs deleted file mode 100644 index ea46ef04e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/Manage/VerifyPhoneNumberViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; - -namespace ClassifiedAds.IdentityServer.Manage.Models -{ - public class VerifyPhoneNumberViewModel - { - [Required] - public string Code { get; set; } - - [Required] - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/SecurityHeadersAttribute.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/SecurityHeadersAttribute.cs deleted file mode 100644 index 93ca67ffc..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/SecurityHeadersAttribute.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace IdentityServerHost.Quickstart.UI -{ - public class SecurityHeadersAttribute : ActionFilterAttribute - { - public override void OnResultExecuting(ResultExecutingContext context) - { - var result = context.Result; - if (result is ViewResult) - { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options - if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) - { - context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff"); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options - if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) - { - context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy - var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; - // also consider adding upgrade-insecure-requests once you have HTTPS in place for production - //csp += "upgrade-insecure-requests;"; - // also an example if you need client images to be displayed from twitter - // csp += "img-src 'self' https://pbs.twimg.com;"; - - // once for standards compliant browsers - if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) - { - context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp); - } - // and once again for IE - if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) - { - context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - var referrer_policy = "no-referrer"; - if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) - { - context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy); - } - } - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/TestUsers.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/TestUsers.cs deleted file mode 100644 index ba81fa537..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Quickstart/TestUsers.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityModel; -using IdentityServer4.Test; -using System.Collections.Generic; -using System.Security.Claims; - -namespace IdentityServer4.Quickstart.UI -{ - public class TestUsers - { - public static List Users = new List - { - new TestUser{SubjectId = "818727", Username = "alice", Password = "alice", - Claims = - { - new Claim(JwtClaimTypes.Name, "Alice Smith"), - new Claim(JwtClaimTypes.GivenName, "Alice"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), - new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), - new Claim(JwtClaimTypes.WebSite, "http://alice.com"), - new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json) - } - }, - new TestUser{SubjectId = "88421113", Username = "bob", Password = "bob", - Claims = - { - new Claim(JwtClaimTypes.Name, "Bob Smith"), - new Claim(JwtClaimTypes.GivenName, "Bob"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), - new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), - new Claim(JwtClaimTypes.WebSite, "http://bob.com"), - new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json), - new Claim("location", "somewhere") - } - } - }; - } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Startup.cs b/src/ModularMonolith/ClassifiedAds.IdentityServer/Startup.cs deleted file mode 100644 index 517ce74d6..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Startup.cs +++ /dev/null @@ -1,124 +0,0 @@ -using ClassifiedAds.IdentityServer.ConfigurationOptions; -using ClassifiedAds.Infrastructure.Monitoring; -using ClassifiedAds.Modules.Identity.Entities; -using ClassifiedAds.Modules.Identity.Repositories; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.DataProtection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.HttpOverrides; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace ClassifiedAds.IdentityServer -{ - public class Startup - { - public Startup(IConfiguration configuration, IWebHostEnvironment env) - { - Configuration = configuration; - - AppSettings = new AppSettings(); - Configuration.Bind(AppSettings); - } - - public IConfiguration Configuration { get; } - - private AppSettings AppSettings { get; set; } - - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - services.Configure(options => - { - options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; - options.KnownNetworks.Clear(); - options.KnownProxies.Clear(); - }); - - if (AppSettings.CookiePolicyOptions?.IsEnabled ?? false) - { - services.Configure(options => - { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.CheckConsentNeeded = context => true; - options.MinimumSameSitePolicy = AppSettings.CookiePolicyOptions.MinimumSameSitePolicy; - options.Secure = AppSettings.CookiePolicyOptions.Secure; - }); - } - - services.AddControllersWithViews(); - services.AddRazorPages(); - - services.AddCors(); - - services.AddDateTimeProvider(); - - services.AddIdentityModule(opt => Configuration.GetSection("Modules:Identity").Bind(opt)) - .AddNotificationModule(opt => Configuration.GetSection("Modules:Notification").Bind(opt)) - .AddApplicationServices(); - - services.AddIdentityServer(options => - { - if (!string.IsNullOrWhiteSpace(AppSettings.IdentityServer.IssuerUri)) - { - options.IssuerUri = AppSettings.IdentityServer.IssuerUri; - } - - options.InputLengthRestrictions.Password = int.MaxValue; - options.InputLengthRestrictions.UserName = int.MaxValue; - }) - .AddSigningCredential(AppSettings.IdentityServer.Certificate.FindCertificate()) - .AddAspNetIdentity() - .AddIdServerPersistence(AppSettings.Modules.Auth.ConnectionStrings.Default); - - services.AddDataProtection() - .PersistKeysToDbContext() - .SetApplicationName("ClassifiedAds"); - - services.AddCaches(AppSettings.Caching) - .AddMonitoringServices(AppSettings.Monitoring); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - app.UseForwardedHeaders(); - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseStaticFiles(); - - if (AppSettings.CookiePolicyOptions?.IsEnabled ?? false) - { - app.UseCookiePolicy(); - } - - app.UseRouting(); - - app.UseCors( - builder => builder - .AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod() - ); - - app.UseSecurityHeaders(AppSettings.SecurityHeaders); - - app.UseIdentityServer(); - app.UseAuthorization(); - - app.UseMonitoringServices(AppSettings.Monitoring); - - app.UseEndpoints(endpoints => - { - endpoints.MapDefaultControllerRoute(); - endpoints.MapRazorPages(); - }); - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/AccessDenied.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/AccessDenied.cshtml deleted file mode 100644 index 0745189e4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/AccessDenied.cshtml +++ /dev/null @@ -1,7 +0,0 @@ - -
-
-

Access Denied

-

You do not have access to that resource.

-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml deleted file mode 100644 index 5377f8e64..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/ForgotPassword.cshtml +++ /dev/null @@ -1,20 +0,0 @@ -@model ForgotPasswordModel - -@await Html.PartialAsync("_ValidationSummary") - -

Forgot Password

-

Enter your email

- -
-
-
-
-
- - - -
- -
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/LoggedOut.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/LoggedOut.cshtml deleted file mode 100644 index dc9fbf7ab..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/LoggedOut.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@model LoggedOutViewModel - -@{ - // set this so the layout rendering sees an anonymous user - ViewData["signed-out"] = true; -} - -
-

- Logout - You are now logged out -

- - @if (Model.PostLogoutRedirectUri != null) - { -
- Click here to return to the - @Model.ClientName application. -
- } - - @if (Model.SignOutIframeUrl != null) - { - - } -
- -@section scripts -{ - @if (Model.AutomaticRedirectAfterSignOut) - { - - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml deleted file mode 100644 index e34810b8c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Login.cshtml +++ /dev/null @@ -1,108 +0,0 @@ -@model LoginViewModel - - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Logout.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Logout.cshtml deleted file mode 100644 index b49dee09c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Logout.cshtml +++ /dev/null @@ -1,15 +0,0 @@ -@model LogoutViewModel - -
-
-

Logout

-

Would you like to logut of IdentityServer?

-
- -
- -
- -
-
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Register.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Register.cshtml deleted file mode 100644 index 68de1a4d4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Register.cshtml +++ /dev/null @@ -1,36 +0,0 @@ -@model RegisterModel - -

Register

- -@await Html.PartialAsync("_ValidationSummary") - -
-

Create a new account.

-
-
- -
- - -
-
-
- -
- - -
-
-
- -
- - -
-
-
-
- -
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/ResetPassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/ResetPassword.cshtml deleted file mode 100644 index 0f34031f5..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/ResetPassword.cshtml +++ /dev/null @@ -1,31 +0,0 @@ -@model ResetPasswordModel - -

Reset Password

- -@await Html.PartialAsync("_ValidationSummary") - -
-

Enter new password

-
- - -
- -
- - -
-
-
- -
- - -
-
-
-
- -
-
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/SendCode.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/SendCode.cshtml deleted file mode 100644 index 80c7dda30..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/SendCode.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@using ClassifiedAds.IdentityServer.Quickstart.Account - -@model SendCodeViewModel -@{ - ViewData["Title"] = "Send Verification Code"; -} - -

@ViewData["Title"].

- -
- -
-
- Select Two-Factor Authentication Provider: - - -
-
-
- -@section Scripts { - @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Success.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Success.cshtml deleted file mode 100644 index fbbc5f754..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/Success.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -
-

Success!

-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/VerifyCode.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/VerifyCode.cshtml deleted file mode 100644 index 6df108822..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Account/VerifyCode.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@using ClassifiedAds.IdentityServer.Quickstart.Account - -@model VerifyCodeViewModel -@{ - ViewData["Title"] = "Verify"; -} - -

@ViewData["Title"].

- -
-
- - -

@ViewData["Status"]

-
-
- -
- - -
-
-
-
-
- - -
-
-
-
-
- -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Delete.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Delete.cshtml deleted file mode 100644 index e4c3f6ff4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Delete.cshtml +++ /dev/null @@ -1,48 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model ApiResourceModel - -
-
- -
- -
-

Delete Api Resource

-
-
- -
- - - -
-
Delete Api Resource
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteProperty.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteProperty.cshtml deleted file mode 100644 index 4dfc3c2c8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteProperty.cshtml +++ /dev/null @@ -1,60 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model ApiResourcePropertyModel - -
-
- -
- -
- -
-

Delete Property

- - - - - -
-
Delete Property
-
- -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteScope.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteScope.cshtml deleted file mode 100644 index 6a2f2888f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteScope.cshtml +++ /dev/null @@ -1,56 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model ScopeModel - - -
-
- -
- -
-

Delete Scope

-
-
- -
- - - - - - - -
-
Delete Scope
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
- -
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteSecret.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteSecret.cshtml deleted file mode 100644 index c3b7757c2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/DeleteSecret.cshtml +++ /dev/null @@ -1,63 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model SecretModel - -
-
- -
- -
- -
-

Delete Secret

- - - - - - - -
-
Delete Secret
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Edit.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Edit.cshtml deleted file mode 100644 index b17ac3b8c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Edit.cshtml +++ /dev/null @@ -1,148 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels; -@model ApiResourceModel - -
-
- -
- -
-

Api Resources

-
-
- -
- -
- - - - -
-
Api Resources
-
- - -
- -
- - -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- - - -
-
- - @if (Model.Id != 0) - { - -
- - -
- -
- - -
- -
- - -
- } - - -
- -
- - -
-
- - -
- -
- - @if (Model.Id != 0) - { - Delete - } -
-
-
-
- -
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Index.cshtml deleted file mode 100644 index a2e80a243..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Index.cshtml +++ /dev/null @@ -1,54 +0,0 @@ -@model List - -
- -
-

Api Resources

-
- - - -
- @*@await Html.PartialAsync("Common/Search", new Search() { Action = "ApiResources", Controller = "Configuration" })*@ -
-
-
-
-
- - - - - - - - - - - - - @foreach (var api in Model) - { - - - - - - - - } - -
NameDisplay NameDescription
Edit@api.Name@api.DisplayName@api.Description - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiResources", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = @ViewBag.Search })*@ -
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Properties.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Properties.cshtml deleted file mode 100644 index e7c8e418e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Properties.cshtml +++ /dev/null @@ -1,104 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model PropertiesModel - -
-
- -
- -
- -
- -

Properties

- -
- - - - - - - -
-
Add Property
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
-
-
- -
-
-
-
Properties
-
-
- - - - - - - - - - @foreach (var ApiResourceProperty in Model.Properties) - { - - - - - - } - -
KeyValue
@ApiResourceProperty.Key@ApiResourceProperty.ValueRemove
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiResourceProperties", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Scopes.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Scopes.cshtml deleted file mode 100644 index 9c5d0e899..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Scopes.cshtml +++ /dev/null @@ -1,98 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model ScopesModel - -
-
- -
- -
-

Add Scope

- -
-
- - - - -
-
Add Scope
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
- -
-
-
-
Scopes
-
-
- - - - - - - - - - @foreach (var scope in Model.Scopes) - { - - - - - } - -
Name
@scope.ScopeRemove
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager() { Action = "ApiScopes", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
- -
-
- -
-
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Secrets.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Secrets.cshtml deleted file mode 100644 index 1f25999ba..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/Secrets.cshtml +++ /dev/null @@ -1,174 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiResourceModels -@model SecretsModel - -
-
- -
- -
- -
-

Api Secrets

-
- -
-
-
- -
-
-
Add Api Secrets
-
- - - - - -
- -
- -
-
- - -
- -
- -
- -
- -
-
- -
-
- - -
- -
- - - -
-
- - -
- -
-
- -
- -
-
-
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
- -
-
-
- -
-
-
-
Api Secrets
-
- -
- - - - - - - - - - - - - - @foreach (var secret in Model.Secrets) - { - - - - - - - - - } - -
TypeValueDescriptionExpirationCreated
Remove@secret.Type@secret.Description@(secret.Expiration.HasValue ? secret.Expiration.Value.Date.ToShortDateString() : string.Empty)@secret.Created.Date.ToShortDateString()
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiSecrets", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
- - -
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/_Label.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/_Label.cshtml deleted file mode 100644 index dcfaabab2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiResource/_Label.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@model string - -@Model - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiScope/Edit.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiScope/Edit.cshtml deleted file mode 100644 index e4d35d200..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/ApiScope/Edit.cshtml +++ /dev/null @@ -1,133 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ApiScopeModels -@model ApiScopeModel - -
-
- -
- -
-

Scopes

-
-
- -
- -
- - - - -
-
Scope
-
- -
- -
- - -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
- -
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml deleted file mode 100644 index 4f7c2c7f3..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Claims.cshtml +++ /dev/null @@ -1,127 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClaimsModel - -
-
- -
- -
- -
-

Claims

- -
- - - - -
-
Add Claim
-
- - -
- -
- - - - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
-
-
- -
-
-
-
Claims
-
- -
- - - - - - - - - - - @foreach (var clientClaim in Model.Claims) - { - - - - - - } - -
TypeValue
@clientClaim.Type@clientClaim.ValueRemove
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ClientClaims", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
-
-
-
-
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml deleted file mode 100644 index be4197994..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Clone.cshtml +++ /dev/null @@ -1,88 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
- -
-
- -
-
-

Clone @Model.OriginalClientId

-
-
-
-
-
- -
-
- -
-
- -
-
-

Settings

- - -
-
-
-
- -
-
-
- -@section scripts - { - -} - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml deleted file mode 100644 index 408837e56..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Delete.cshtml +++ /dev/null @@ -1,56 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
-
- -
-
- -
- - - - -

Delete

- -
-
Delete
-
- -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml deleted file mode 100644 index f5ff01d66..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteClaim.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClaimModel - -
-
- -
- -
- -
-

Delete Claim

- - - - - - -
-
Delete Claim
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml deleted file mode 100644 index a6b4080bf..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteProperty.cshtml +++ /dev/null @@ -1,60 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model PropertyModel - -
-
- -
- -
- -
-

Delete Property

- - - - - -
-
Delete Property
-
- -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml deleted file mode 100644 index c314f4bc5..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/DeleteSecret.cshtml +++ /dev/null @@ -1,63 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model SecretModel - -
-
- -
- -
- -
-

Delete Secret

- - - - - - - -
-
Delete Secret
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml deleted file mode 100644 index a0fcf8e7f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Edit.cshtml +++ /dev/null @@ -1,97 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
- -
-
- -
- - @if (Model.Id != 0) - { -
-

Edit @Model.ClientId

-
- } - -
-
-
-
- - @if (Model.Id != 0) - { -
- @await Html.PartialAsync("_Actions") -
- } - -
-
-

Settings

- - -
-
-
- @await Html.PartialAsync("_Actions") -
-
- -@section scripts - { - -} - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml deleted file mode 100644 index 21e388224..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Index.cshtml +++ /dev/null @@ -1,52 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model List - -
-
-

Clients

-
- - - -
- @*@await Html.PartialAsync("Common/Search", new Search() { Action = "Clients", Controller = "Configuration" })*@ -
-
-
-
-
- - - - - - - - - - - @foreach (var client in Model) - { - - - - - - - } - -
Client IdClient Name
Edit@client.ClientId@client.ClientName - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Clients", PageSize = Model.PageSize, TotalCount = Model.TotalCount, Search = ViewBag.Search, EnableSearch = true })*@ -
-
- diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml deleted file mode 100644 index 45491a159..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Properties.cshtml +++ /dev/null @@ -1,101 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model PropertiesModel - -
-
- -
- -
- -
- -

Properties

- -
- - - - -
-
Add Property
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
-
-
- -
-
-
-
Properties
-
-
- - - - - - - - - - @foreach (var ApiResourceProperty in Model.Properties) - { - - - - - - } - -
KeyValue
@ApiResourceProperty.Key@ApiResourceProperty.ValueRemove
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiResourceProperties", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml deleted file mode 100644 index b72a29a27..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/Secrets.cshtml +++ /dev/null @@ -1,174 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model SecretsModel - -
-
- -
- -
- -
-

Client Secrets

-
- -
-
-
- -
-
-
Add Client Secrets
-
- - - - - -
- -
- -
-
- - -
- -
- -
- -
- -
-
- -
-
- - -
- -
- - - -
-
- - -
- -
-
- -
- -
-
-
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
- -
-
-
- -
-
-
-
Client Secrets
-
- -
- - - - - - - - - - - - - - @foreach (var secret in Model.Secrets) - { - - - - - - - - - } - -
TypeValueDescriptionExpirationCreated
Remove@secret.Type@secret.Description@(secret.Expiration.HasValue ? secret.Expiration.Value.Date.ToShortDateString() : string.Empty)@secret.Created.Date.ToShortDateString()
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "ApiSecrets", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
- - -
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml deleted file mode 100644 index 8091610d8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Actions.cshtml +++ /dev/null @@ -1,12 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
- - - @if (Model.Id != 0) - { - Clone - Delete - } -
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml deleted file mode 100644 index 120638006..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Authentication.cshtml +++ /dev/null @@ -1,111 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
-
Authentication
-
- -
- -
- -
-
- - -
- -
- - - -
-
- - -
- -
- -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml deleted file mode 100644 index cef1c223b..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Basics.cshtml +++ /dev/null @@ -1,186 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel -@{ - var protocolTypes = new List> { new KeyValuePair("oidc", "OpenID Connect") }; -} - - - - -
-
Basics
-
- -
- -
- - - -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - -
-
- - - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- - -
- - - -
- - -
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml deleted file mode 100644 index 943a332d7..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Consent.cshtml +++ /dev/null @@ -1,51 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
-
Consent
-
- -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml deleted file mode 100644 index 42e8085db..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_DeviceFlow.cshtml +++ /dev/null @@ -1,27 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
-
Device Flow
-
- -
- -
- -
-
- - -
- -
- -
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Label.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Label.cshtml deleted file mode 100644 index dcfaabab2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Label.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@model string - -@Model - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml deleted file mode 100644 index 58a62ad0e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Name.cshtml +++ /dev/null @@ -1,102 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
-
Name
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - @if (Model.Id == 0) - { -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
- } -
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml deleted file mode 100644 index 099f3d24f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Client/_Token.cshtml +++ /dev/null @@ -1,184 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.ClientModels -@model ClientModel - -
-
Token
-
- -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- - -
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Consent/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Consent/Index.cshtml deleted file mode 100644 index 9aa6c9e25..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Consent/Index.cshtml +++ /dev/null @@ -1,110 +0,0 @@ -@model ConsentViewModel - - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/Success.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/Success.cshtml deleted file mode 100644 index 050dd91c5..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/Success.cshtml +++ /dev/null @@ -1,7 +0,0 @@ - -
-
-

Success

-

You have successfully authorized the device

-
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/UserCodeCapture.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/UserCodeCapture.cshtml deleted file mode 100644 index 6d412613d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/UserCodeCapture.cshtml +++ /dev/null @@ -1,23 +0,0 @@ -@model string - -
-
-

User Code

-

Please enter the code displayed on your device.

-
- - - -
-
-
-
- - -
- - -
-
-
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/UserCodeConfirmation.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/UserCodeConfirmation.cshtml deleted file mode 100644 index e1d3b1968..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Device/UserCodeConfirmation.cshtml +++ /dev/null @@ -1,108 +0,0 @@ -@model DeviceAuthorizationViewModel - -
-
- @if (Model.ClientLogoUrl != null) - { - - } -

- @Model.ClientName - is requesting your permission -

- @if (Model.ConfirmUserCode) - { -

Please confirm that the authorization request quotes the code: @Model.UserCode.

- } -

Uncheck the permissions you do not wish to grant.

-
- -
-
- -
-
- -
- -
-
- @if (Model.IdentityScopes.Any()) - { -
-
-
- - Personal Information -
-
    - @foreach (var scope in Model.IdentityScopes) - { - - } -
-
-
- } - - @if (Model.ApiScopes.Any()) - { -
-
-
- - Application Access -
-
    - @foreach (var scope in Model.ApiScopes) - { - - } -
-
-
- } - -
-
-
- - Description -
-
- -
-
-
- - @if (Model.AllowRememberConsent) - { -
-
- - -
-
- } -
-
- -
-
- - -
-
- @if (Model.ClientUrl != null) - { - - - @Model.ClientName - - } -
-
-
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Diagnostics/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Diagnostics/Index.cshtml deleted file mode 100644 index e939c0d88..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Diagnostics/Index.cshtml +++ /dev/null @@ -1,64 +0,0 @@ -@model DiagnosticsViewModel - -
-
-

Authentication Cookie

-
- -
-
-
-
-

Claims

-
-
-
- @foreach (var claim in Model.AuthenticateResult.Principal.Claims) - { -
@claim.Type
-
@claim.Value
- } -
-
-
-
- -
-
-
-

Properties

-
-
-
- @foreach (var prop in Model.AuthenticateResult.Properties.Items) - { -
@prop.Key
-
@prop.Value
- } - @if (Model.Clients.Any()) - { -
Clients
-
- @{ - var clients = Model.Clients.ToArray(); - for(var i = 0; i < clients.Length; i++) - { - @clients[i] - if (i < clients.Length - 1) - { - , - } - } - } -
- } -
-
-
-
-
-
- - - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Grants/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Grants/Index.cshtml deleted file mode 100644 index a60f22611..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Grants/Index.cshtml +++ /dev/null @@ -1,87 +0,0 @@ -@model GrantsViewModel - -
-
-

Client Application Permissions

-

Below is the list of applications you have given permission to and the resources they have access to.

-
- - @if (Model.Grants.Any() == false) - { -
-
-
- You have not given access to any applications -
-
-
- } - else - { - foreach (var grant in Model.Grants) - { -
-
-
-
- @if (grant.ClientLogoUrl != null) - { - - } - @grant.ClientName -
- -
-
- - -
-
-
-
- -
    - @if (grant.Description != null) - { -
  • - @grant.Description -
  • - } -
  • - @grant.Created.ToString("yyyy-MM-dd") -
  • - @if (grant.Expires.HasValue) - { -
  • - @grant.Expires.Value.ToString("yyyy-MM-dd") -
  • - } - @if (grant.IdentityGrantNames.Any()) - { -
  • - -
      - @foreach (var name in grant.IdentityGrantNames) - { -
    • @name
    • - } -
    -
  • - } - @if (grant.ApiGrantNames.Any()) - { -
  • - -
      - @foreach (var name in grant.ApiGrantNames) - { -
    • @name
    • - } -
    -
  • - } -
-
- } - } -
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Home/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Home/Index.cshtml deleted file mode 100644 index fb86a3b0f..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Home/Index.cshtml +++ /dev/null @@ -1,93 +0,0 @@ -@{ - var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString(); -} - -
-

- - Welcome to IdentityServer4 (version @version) -

-

- IdentityServer publishes a - discovery document - where you can find metadata and links to all the endpoints, key material, etc. -

-
- - -
- -
-
-

Clients

-
-
-

- -

- Manage -
-
- -
-
-

Identity Resources

-
-
-

- -

- Manage -
-
- -
-
-

Api Resources

-
-
-

- -

- Manage -
-
-
-
- -
-
-

Persisted Grants

-
-
-

- -

- Manage -
-
- -
-
-

Users

-
-
-

- -

- Manage -
-
- -
-
-

Roles

-
-
-

- -

- Manage -
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Delete.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Delete.cshtml deleted file mode 100644 index 31ca3c8ab..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Delete.cshtml +++ /dev/null @@ -1,48 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.IdentityResourceModels -@model IdentityResourceModel - -
-
- -
- -
-

Delete Identity Resource

-
-
- -
- - - -
-
Delete Identity Resource
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/DeleteProperty.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/DeleteProperty.cshtml deleted file mode 100644 index 59ea2ac84..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/DeleteProperty.cshtml +++ /dev/null @@ -1,60 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.IdentityResourceModels -@model IdentityResourcePropertyModel - -
-
- -
- -
- -
-

Delete Property

- - - - - -
-
Delete Property
-
- -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml deleted file mode 100644 index 68ac23024..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Edit.cshtml +++ /dev/null @@ -1,166 +0,0 @@ -@model ClassifiedAds.IdentityServer.Models.IdentityResourceModels.IdentityResourceModel - -
-
- -
- -
-

Identity Resource

-
-
- -
- -
- - - - -
-
Name
-
- -
- -
- - -
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - - -
-
- - -
- -
- - -
-
- - @if (Model.Id != 0) - { - -
- - -
- } - - -
- -
- - @if (Model.Id != 0) - { - Delete - } -
-
-
-
- -
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml deleted file mode 100644 index 74619b626..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Index.cshtml +++ /dev/null @@ -1,53 +0,0 @@ -@model List - -
-
-

Identity Resources

-
- - - -
- @*@await Html.PartialAsync("Common/Search", new Search { Action = "IdentityResources", Controller = "Configuration" })*@ -
-
- -
-
-
- - - - - - - - - - - - @foreach (var identity in Model) - { - - - - - - - - } - -
NameDisplay NameDescription
Edit@identity.Name@identity.DisplayName@identity.Description - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "IdentityResources", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ -
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Properties.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Properties.cshtml deleted file mode 100644 index d18a6b0b6..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/Properties.cshtml +++ /dev/null @@ -1,104 +0,0 @@ -@model PropertiesModel -@using ClassifiedAds.IdentityServer.Models.IdentityResourceModels - -
-
- -
- -
- -
- -

Properties

- -
- - - - - - - -
-
Add Property
-
- - -
- -
- - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
-
-
- -
-
-
-
Properties
-
-
- - - - - - - - - - @foreach (var identityResourceProperty in Model.Properties) - { - - - - - - } - -
KeyValue
@identityResourceProperty.Key@identityResourceProperty.ValueRemove
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "IdentityResourceProperties", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml deleted file mode 100644 index dcfaabab2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/IdentityResource/_Label.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@model string - -@Model - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/AddPhoneNumber.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/AddPhoneNumber.cshtml deleted file mode 100644 index 40885f383..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/AddPhoneNumber.cshtml +++ /dev/null @@ -1,27 +0,0 @@ -@model AddPhoneNumberViewModel -@{ - ViewData["Title"] = "Add Phone Number"; -} - -

@ViewData["Title"].

-
-

Add a phone number.

-
-
-
- -
- - -
-
-
-
- -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/ChangePassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/ChangePassword.cshtml deleted file mode 100644 index a8d076821..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/ChangePassword.cshtml +++ /dev/null @@ -1,42 +0,0 @@ -@model ChangePasswordViewModel -@{ - ViewData["Title"] = "Change Password"; -} - -

@ViewData["Title"].

- -
-

Change Password Form

-
-
-
- -
- - -
-
-
- -
- - -
-
-
- -
- - -
-
-
-
- -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/DisplayRecoveryCodes.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/DisplayRecoveryCodes.cshtml deleted file mode 100644 index 080ccc10e..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/DisplayRecoveryCodes.cshtml +++ /dev/null @@ -1,21 +0,0 @@ -@model DisplayRecoveryCodesViewModel -@{ - ViewData["Title"] = "Your recovery codes:"; -} - -

@ViewData["Title"].

-

@ViewData["StatusMessage"]

- -
-

Here are your new recovery codes

-
-
-
Codes:
- @foreach (var code in Model.Codes) - { -
- @code -
- } -
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/Index.cshtml deleted file mode 100644 index b91d3d8fd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/Index.cshtml +++ /dev/null @@ -1,86 +0,0 @@ -@model IndexViewModel -@{ - ViewData["Title"] = "Manage your account"; -} - -

@ViewData["Title"].

-

@ViewData["StatusMessage"]

- -
-

Change your account settings

-
-
-
Password:
-
- @if (Model.HasPassword) - { - [  Change  ] - } - else - { - [  Create  ] - } -
-
External Logins:
-
- @Model.Logins.Count [  Manage  ] -
-
Phone Number:
-
-

- Phone Numbers can used as a second factor of verification in two-factor authentication. - See this article - for details on setting up this ASP.NET application to support two-factor authentication using SMS. -

- @(Model.PhoneNumber ?? "None") - @if (Model.PhoneNumber != null) - { -
- [  Change  ] -
- [] -
- } - else - { - [  Add  ] - } -
- -
Two-Factor Authentication:
-
- - @if (Model.TwoFactor) - { -
- Enabled [] -
- } - else - { -
- [] Disabled -
- } -
-
Authentication App:
-
- @if (Model.AuthenticatorKey == null) - { -
- Generate [] -
- } - else - { - Your key is: @Model.AuthenticatorKey -
- Generate [] -
- } -
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/ManageLogins.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/ManageLogins.cshtml deleted file mode 100644 index 60c7811fa..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/ManageLogins.cshtml +++ /dev/null @@ -1,54 +0,0 @@ -@model ManageLoginsViewModel -@using Microsoft.AspNetCore.Authentication -@{ - ViewData["Title"] = "Manage your external logins"; -} - -

@ViewData["Title"].

- -

@ViewData["StatusMessage"]

-@if (Model.CurrentLogins.Count > 0) -{ -

Registered Logins

- - - @for (var index = 0; index < Model.CurrentLogins.Count; index++) - { - - - - - } - -
@Model.CurrentLogins[index].ProviderDisplayName - @if ((bool)ViewData["ShowRemoveButton"]) - { -
-
- - - -
-
- } - else - { - @:   - } -
-} -@if (Model.OtherLogins.Count > 0) -{ -

Add another service to log in.

-
-
-
-

- @foreach (var provider in Model.OtherLogins) - { - - } -

-
-
-} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/SetPassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/SetPassword.cshtml deleted file mode 100644 index d1a1b77a4..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/SetPassword.cshtml +++ /dev/null @@ -1,38 +0,0 @@ -@model SetPasswordViewModel -@{ - ViewData["Title"] = "Set Password"; -} - -

- You do not have a local username/password for this site. Add a local - account so you can log in without an external login. -

- -
-

Set your password

-
-
-
- -
- - -
-
-
- -
- - -
-
-
-
- -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/VerifyPhoneNumber.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/VerifyPhoneNumber.cshtml deleted file mode 100644 index 076c20562..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Manage/VerifyPhoneNumber.cshtml +++ /dev/null @@ -1,30 +0,0 @@ -@model VerifyPhoneNumberViewModel -@{ - ViewData["Title"] = "Verify Phone Number"; -} - -

@ViewData["Title"].

- -
- -

Add a phone number.

-
@ViewData["Status"]
-
-
-
- -
- - -
-
-
-
- -
-
-
- -@section Scripts { - @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml deleted file mode 100644 index 72b32d8a8..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Claims.cshtml +++ /dev/null @@ -1,127 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.RoleModels -@model ClaimsModel - -
-
- -
- -
- -
-

Claims

- -
- - - - -
-
Add Claim
-
- - -
- -
- - - - -
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
-
-
- -
-
-
-
Claims
-
- -
- - - - - - - - - - - @foreach (var claim in Model.Claims) - { - - - - - - } - -
TypeValue
@claim.Type@claim.ValueRemove
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "RoleClaims", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
-
-
-
-
- -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml deleted file mode 100644 index e8b8b084b..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Delete.cshtml +++ /dev/null @@ -1,46 +0,0 @@ -@using ClassifiedAds.Modules.Identity.Entities; -@model Role - -
-
- -
-
- -
- -

Delete

- - - -
-
Delete
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml deleted file mode 100644 index d8a441369..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/DeleteClaim.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.RoleModels -@model ClaimModel - -
-
- -
- -
- -
-

Delete Claim

- - - - - - -
-
Delete Claim
-
- - -
- -
- -
-
- - -
- -
- -
-
- - -
- -
- -
-
-
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml deleted file mode 100644 index 946b782ef..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Edit.cshtml +++ /dev/null @@ -1,63 +0,0 @@ -@using ClassifiedAds.Modules.Identity.Entities; -@model Role - -
-
- -
- -
-

Role

-
-
- - -
- -
- - @if (Model.Id != Guid.Empty) - { - -
- -
- } - - - -
-
Role
-
- - -
- -
- - -
-
- - -
- -
- -
-
-
-
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml deleted file mode 100644 index e4c222105..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Index.cshtml +++ /dev/null @@ -1,49 +0,0 @@ -@using ClassifiedAds.Modules.Identity.Entities; -@model IEnumerable - -

Roles

- - Add Role - -
-
- @*@await Html.PartialAsync("Common/Search", new Search { Action = "Search", Controller = "Role" })*@ -
-
- -
-
-
- - - - - - - - - - @foreach (var role in Model) - { - - - - - - } - -
Name
- Edit - Users - @role.Name - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Roles", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml deleted file mode 100644 index 8dda9c830..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/Users.cshtml +++ /dev/null @@ -1,78 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.RoleModels -@model UsersModel - -
-
- -
- -
-

Users (@Model.Role.Name)

-
-
- -@*
-
-
-
-
- - -
-
- -
-
- -
-
-
-
-
*@ - -
-
-
- - - - - - - - - - - - - @foreach (var user in Model.Users) - { - - - - - - - - } - -
User IdUser NameEmail
- - @user.Id@user.UserName@user.Email - -
-
-
-
- -
-
- @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "RoleUsers", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ -
-
\ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml deleted file mode 100644 index dcfaabab2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Role/_Label.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@model string - -@Model - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/Error.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/Error.cshtml deleted file mode 100644 index 4c746e771..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/Error.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@model ErrorViewModel - -@{ - var error = Model?.Error?.Error; - var errorDescription = Model?.Error?.ErrorDescription; - var request_id = Model?.Error?.RequestId; -} - -
-
-

Error

-
- -
-
-
- Sorry, there was an error - - @if (error != null) - { - - - : @error - - - - if (errorDescription != null) - { -
@errorDescription
- } - } -
- - @if (request_id != null) - { -
Request Id: @request_id
- } -
-
-
diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/Redirect.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/Redirect.cshtml deleted file mode 100644 index ecc31c106..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/Redirect.cshtml +++ /dev/null @@ -1,11 +0,0 @@ -@model RedirectViewModel - -
-
-

You are now being returned to the application

-

Once complete, you may close this tab.

-
-
- - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml deleted file mode 100644 index 30c8cda80..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,71 +0,0 @@ -@using IdentityServer4.Extensions -@{ - string name = null; - if (!true.Equals(ViewData["signed-out"])) - { - name = Context.User?.GetDisplayName(); - } -} - - - - - - - IdentityServer4 - - - - - - - -
- @RenderBody() -
- - - @RenderSection("scripts", required: false) - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml deleted file mode 100644 index 0c3405064..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_LoginPartial.cshtml +++ /dev/null @@ -1,26 +0,0 @@ -@using Microsoft.AspNetCore.Identity -@using ClassifiedAds.Modules.Identity.Entities; - -@inject SignInManager SignInManager -@inject UserManager UserManager - -@if (SignInManager.IsSignedIn(User)) -{ - -} -else -{ - -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ScopeListItem.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ScopeListItem.cshtml deleted file mode 100644 index c1e3496ad..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ScopeListItem.cshtml +++ /dev/null @@ -1,40 +0,0 @@ -@model ScopeViewModel - -
  • -
    - - - -
    -
    - @if (Model.Required) - { - - } - @Model.DisplayName - @if (Model.Emphasize) - { - - } -
    - @if (Model.Required) - { -
    - (required) -
    - } - @if (Model.Description != null) - { - - } -
  • \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ValidationScriptsPartial.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ValidationScriptsPartial.cshtml deleted file mode 100644 index 6cd4eed86..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ValidationScriptsPartial.cshtml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ValidationSummary.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ValidationSummary.cshtml deleted file mode 100644 index 674d68d87..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/Shared/_ValidationSummary.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@if (ViewContext.ModelState.IsValid == false) -{ -
    - Error -
    -
    -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml deleted file mode 100644 index f272848fd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/ChangePassword.cshtml +++ /dev/null @@ -1,74 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.UserModels -@model ChangePasswordModel - -
    -
    - -
    - -
    - -
    -

    Change Password

    - -
    - - - - -
    -
    Change Password
    -
    - - -
    - -
    - -

    @Model.UserName

    -
    -
    - - -
    - -
    - - -
    -
    - - -
    - -
    - - -
    -
    - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml deleted file mode 100644 index e3b6ed227..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Claims.cshtml +++ /dev/null @@ -1,127 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.UserModels -@model ClaimsModel - -
    -
    - -
    - -
    - -
    -

    Claims

    - -
    - - - - -
    -
    Add Claim
    -
    - - -
    - -
    - - - - -
    -
    - - -
    - -
    - - -
    -
    - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    Claims
    -
    - -
    - - - - - - - - - - - @foreach (var claim in Model.Claims) - { - - - - - - } - -
    TypeValue
    @claim.Type@claim.ValueRemove
    -
    - -
    -
    - @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "UserClaims", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
    -
    -
    -
    -
    -
    - -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml deleted file mode 100644 index 6cec96dcd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Delete.cshtml +++ /dev/null @@ -1,46 +0,0 @@ -@using ClassifiedAds.Modules.Identity.Entities; -@model User - -
    -
    - -
    -
    - -
    - -

    Delete

    - - - -
    -
    Delete
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    - -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml deleted file mode 100644 index 1d29a90fa..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/DeleteClaim.cshtml +++ /dev/null @@ -1,62 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.UserModels -@model ClaimModel - -
    -
    - -
    - -
    - -
    -

    Delete Claim

    - - - - - - -
    -
    Delete Claim
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml deleted file mode 100644 index d00e0ff3c..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/DeleteRole.cshtml +++ /dev/null @@ -1,55 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.UserModels -@model RoleModel - -
    -
    - -
    - -
    - - -
    - -

    Delete Role

    - - - - - - - -
    -
    Delete Role
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Index.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Index.cshtml deleted file mode 100644 index ed055d468..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Index.cshtml +++ /dev/null @@ -1,55 +0,0 @@ -@using ClassifiedAds.Modules.Identity.Entities; -@model IEnumerable - - Add User - -
    -
    - @*@await Html.PartialAsync("Common/Search", new Search() { Action = "Users", Controller = "Identity" })*@ -
    -
    - -
    -
    -
    - - - - - - - - - - - - - - @foreach (var user in Model) - { - - - - - - - - - } - -
    User IdUser NameEmail
    - Edit - - - @user.Id@user.UserName@user.Email - -
    -
    -
    -
    - -
    -
    - @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "Users", PageSize = Model.PageSize, TotalCount = Model.TotalCount, EnableSearch = true, Search = ViewBag.Search })*@ -
    -
    \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml deleted file mode 100644 index a17702fff..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Profile.cshtml +++ /dev/null @@ -1,175 +0,0 @@ -@using ClassifiedAds.Modules.Identity.Entities; -@model User - -
    -
    - -
    - -
    -

    User Profile

    -
    -
    - -
    - -
    - - @if (Model.Id != Guid.Empty) - { - - - } - - - - -
    -
    Profile
    -
    - -
    -
    - -
    -
    - -
    - -
    - - -
    -
    - - -
    - -
    - - -
    -
    - - -
    - -
    - - - -
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    - - - -
    -
    - - -
    - -
    - - - -
    -
    - - -
    - -
    - - - -
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    - - -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - -@section scripts - { - -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml deleted file mode 100644 index 9c84004cd..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/Roles.cshtml +++ /dev/null @@ -1,88 +0,0 @@ -@using ClassifiedAds.IdentityServer.Models.UserModels -@model RolesModel - -
    -
    - -
    - -
    - -
    -

    Roles

    - -
    - - - - -
    -
    Roles
    -
    - - -
    - -
    - -
    -
    - - -
    - -
    - -
    -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    Roles
    -
    - -
    - - - - - - - - - @foreach (var role in Model.UserRoles) - { - - - - - } - -
    Name
    @role.Role.NameRemove
    -
    - -
    -
    - @*@await Html.PartialAsync("Common/Pager", new Pager { Action = "UserRoles", PageSize = Model.PageSize, TotalCount = Model.TotalCount })*@ -
    -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml deleted file mode 100644 index dcfaabab2..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/User/_Label.cshtml +++ /dev/null @@ -1,4 +0,0 @@ -@model string - -@Model - \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/_ViewStart.cshtml b/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/_ViewStart.cshtml deleted file mode 100644 index a5f10045d..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "_Layout"; -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/appsettings.Development.json b/src/ModularMonolith/ClassifiedAds.IdentityServer/appsettings.Development.json deleted file mode 100644 index e203e9407..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/appsettings.json b/src/ModularMonolith/ClassifiedAds.IdentityServer/appsettings.json deleted file mode 100644 index 2a2ff6182..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/appsettings.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "IdentityServer": { - "IssuerUri": "", - "Certificate": { - "Thumbprint": null, - "Path": "Certs/classifiedads.identityserver.pfx", - "Password": "password1234" - } - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "System": "Warning", - "Microsoft": "Warning" - }, - "File": { - "MinimumLogEventLevel": "Debug" - }, - "Elasticsearch": { - "IsEnabled": false, - "Host": "http://localhost:9200", - "IndexFormat": "classifiedads", - "MinimumLogEventLevel": "Debug" - } - }, - "Caching": { - "InMemory": { - "SizeLimit": null - }, - "Distributed": { - "Provider": "InMemory", - "InMemory": { - "SizeLimit": null - }, - "Redis": { - "Configuration": "", - "InstanceName": "" - }, - "SqlServer": { - "ConnectionString": "", - "SchemaName": "", - "TableName": "" - } - } - }, - "Monitoring": { - "MiniProfiler": { - "IsEnabled": true - }, - "AzureApplicationInsights": { - "IsEnabled": false - } - }, - "CookiePolicyOptions": { - - }, - "SecurityHeaders": { - "Content-Security-Policy": "frame-ancestors 'none'", - "Feature-Policy": "camera 'none'", - "Referrer-Policy": "strict-origin-when-cross-origin", - "X-Content-Type-Options": "nosniff", - "X-Frame-Options": "DENY", - "X-XSS-Protection": "1; mode=block", - "Cache-Control": "no-cache, no-store, must-revalidate", - "Pragma": "no-cache", - "Expires": "0" - }, - "Interceptors": { - "LoggingInterceptor": true, - "ErrorCatchingInterceptor": false - }, - "ExternalLogin": { - "AzureActiveDirectory": { - "IsEnabled": true, - "Authority": "https://login.microsoftonline.com/", - "ClientId": "code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.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,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.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-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.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-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 td,.table th{padding:.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 td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{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>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{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>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{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>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{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>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{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>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{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>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{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>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{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>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(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 td,.table-dark th,.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,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.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 + .75rem + 2px);padding:.375rem .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:.25rem;transition:border-color .15s ease-in-out,box-shadow .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 .2rem rgba(0,123,255,.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(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.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-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.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:.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:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .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(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .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 .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(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .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(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .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 .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(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.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 .custom-select,.form-inline .input-group{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:.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:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .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 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.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 .2rem rgba(38,143,255,.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 .2rem rgba(38,143,255,.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 .2rem rgba(130,138,145,.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 .2rem rgba(130,138,145,.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 .2rem rgba(72,180,97,.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 .2rem rgba(72,180,97,.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 .2rem rgba(58,176,195,.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 .2rem rgba(58,176,195,.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 .2rem rgba(222,170,12,.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 .2rem rgba(222,170,12,.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 .2rem rgba(225,83,97,.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 .2rem rgba(225,83,97,.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 .2rem rgba(216,217,219,.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 .2rem rgba(216,217,219,.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 .2rem rgba(82,88,93,.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 .2rem rgba(82,88,93,.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 .2rem rgba(0,123,255,.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 .2rem rgba(0,123,255,.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 .2rem rgba(108,117,125,.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 .2rem rgba(108,117,125,.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 .2rem rgba(40,167,69,.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 .2rem rgba(40,167,69,.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 .2rem rgba(23,162,184,.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 .2rem rgba(23,162,184,.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 .2rem rgba(255,193,7,.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 .2rem rgba(255,193,7,.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 .2rem rgba(220,53,69,.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 .2rem rgba(220,53,69,.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 .2rem rgba(248,249,250,.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 .2rem rgba(248,249,250,.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 .2rem rgba(52,58,64,.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 .2rem rgba(52,58,64,.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-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .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 .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.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:.5rem 0;margin:.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,.15);border-radius:.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:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.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:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.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:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{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:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{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-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.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-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){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=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{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>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control: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-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{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:.375rem .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:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.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),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{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:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{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 .2rem rgba(0,123,255,.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:.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:.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:.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,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.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,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .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(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .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 .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.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 + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.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 + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .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:.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 .15s ease-in-out,border-color .15s ease-in-out,box-shadow .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:.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:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .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:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.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 .15s ease-in-out,border-color .15s ease-in-out,box-shadow .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:.5rem 1rem}.nav-link:focus,.nav-link:hover{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:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{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:.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:.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:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{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:.5rem;padding-bottom:.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:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{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:.5rem;padding-left:.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:.5rem;padding-left:.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:.5rem;padding-left:.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:.5rem;padding-left:.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:.5rem;padding-left:.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,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(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,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.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,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{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,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.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:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.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-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{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-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.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:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.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:.25rem}.page-link{position:relative;display:block;padding:.5rem .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 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.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:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.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:.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:.75rem;background-color:#e9ecef;border-radius:.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 .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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:focus,.list-group-item-action:hover{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:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.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:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.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:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.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:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.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:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.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:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.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:focus,.list-group-item-primary.list-group-item-action:hover{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:focus,.list-group-item-secondary.list-group-item-action:hover{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:focus,.list-group-item-success.list-group-item-action:hover{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:focus,.list-group-item-info.list-group-item-action:hover{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:focus,.list-group-item-warning.list-group-item-action:hover{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:focus,.list-group-item-danger.list-group-item-action:hover{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:focus,.list-group-item-light.list-group-item-action:hover{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:focus,.list-group-item-dark.list-group-item-action:hover{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):focus,.close:not(:disabled):not(.disabled):hover{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:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.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:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.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:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .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-footer,.modal-dialog-scrollable .modal-header{-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,.2);border-radius:.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:.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:.3rem;border-top-right-radius:.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:.3rem;border-bottom-left-radius:.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:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.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:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .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 .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-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-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{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:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-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 .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:.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:.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:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{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:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.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 embed,.embed-responsive iframe,.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 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(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:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.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:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.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:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.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:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.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:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.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:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.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:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.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:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.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:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.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:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.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:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.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:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.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:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.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:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.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:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.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:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.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{*,::after,::before{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}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{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 td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -.toast-title{font-weight:700}.toast-message{-ms-word-wrap:break-word;word-wrap:break-word}.toast-message a,.toast-message label{color:#fff}.toast-message a:hover{color:#ccc;text-decoration:none}.toast-close-button{position:relative;right:-.3em;top:-.3em;float:right;font-size:20px;font-weight:700;color:#fff;-webkit-text-shadow:0 1px 0 #fff;text-shadow:0 1px 0 #fff;opacity:.8;line-height:1}.toast-close-button:focus,.toast-close-button:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4}.rtl .toast-close-button{left:-.3em;float:left;right:.3em}button.toast-close-button{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.toast-top-center{top:0;right:0;width:100%}.toast-bottom-center{bottom:0;right:0;width:100%}.toast-top-full-width{top:0;right:0;width:100%}.toast-bottom-full-width{bottom:0;right:0;width:100%}.toast-top-left{top:12px;left:12px}.toast-top-right{top:12px;right:12px}.toast-bottom-right{right:12px;bottom:12px}.toast-bottom-left{bottom:12px;left:12px}#toast-container{position:fixed;z-index:999999;pointer-events:none}#toast-container *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}#toast-container>div{position:relative;pointer-events:auto;overflow:hidden;margin:0 0 6px;padding:15px 15px 15px 50px;width:300px;-moz-border-radius:3px 3px 3px 3px;-webkit-border-radius:3px 3px 3px 3px;border-radius:3px 3px 3px 3px;background-position:15px center;background-repeat:no-repeat;-moz-box-shadow:0 0 12px #999;-webkit-box-shadow:0 0 12px #999;box-shadow:0 0 12px #999;color:#fff;opacity:.8}#toast-container>div.rtl{direction:rtl;padding:15px 50px 15px 15px;background-position:right 15px center}#toast-container>div:hover{-moz-box-shadow:0 0 12px #000;-webkit-box-shadow:0 0 12px #000;box-shadow:0 0 12px #000;opacity:1;cursor:pointer}#toast-container>.toast-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=)!important}#toast-container>.toast-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=)!important}#toast-container>.toast-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==)!important}#toast-container>.toast-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=)!important}#toast-container.toast-bottom-center>div,#toast-container.toast-top-center>div{width:300px;margin-left:auto;margin-right:auto}#toast-container.toast-bottom-full-width>div,#toast-container.toast-top-full-width>div{width:96%;margin-left:auto;margin-right:auto}.toast{background-color:#030303}.toast-success{background-color:#51a351}.toast-error{background-color:#bd362f}.toast-info{background-color:#2f96b4}.toast-warning{background-color:#f89406}.toast-progress{position:absolute;left:0;bottom:0;height:4px;background-color:#000;opacity:.4}@media all and (max-width:240px){#toast-container>div{padding:8px 8px 8px 50px;width:11em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:241px) and (max-width:480px){#toast-container>div{padding:8px 8px 8px 50px;width:18em}#toast-container>div.rtl{padding:8px 50px 8px 8px}#toast-container .toast-close-button{right:-.2em;top:-.2em}#toast-container .rtl .toast-close-button{left:-.2em;right:.2em}}@media all and (min-width:481px) and (max-width:768px){#toast-container>div{padding:15px 15px 15px 50px;width:25em}#toast-container>div.rtl{padding:15px 50px 15px 15px}} -@font-face{font-family:Icons;src:url(../fonts/open-iconic.eot);src:url(../fonts/open-iconic.eot?#iconic-sm) format('embedded-opentype'),url(../fonts/open-iconic.woff) format('woff'),url(../fonts/open-iconic.ttf) format('truetype'),url(../fonts/open-iconic.otf) format('opentype'),url(../fonts/open-iconic.svg#iconic-sm) format('svg');font-weight:400;font-style:normal}.oi{position:relative;top:1px;display:inline-block;speak:none;font-family:Icons;font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.oi:empty:before{width:1em;text-align:center;box-sizing:content-box}.oi.oi-align-center:before{text-align:center}.oi.oi-align-left:before{text-align:left}.oi.oi-align-right:before{text-align:right}.oi.oi-flip-horizontal:before{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.oi.oi-flip-vertical:before{-webkit-transform:scale(1,-1);-ms-transform:scale(-1,1);transform:scale(1,-1)}.oi.oi-flip-horizontal-vertical:before{-webkit-transform:scale(-1,-1);-ms-transform:scale(-1,1);transform:scale(-1,-1)}.oi-account-login:before{content:'\e000'}.oi-account-logout:before{content:'\e001'}.oi-action-redo:before{content:'\e002'}.oi-action-undo:before{content:'\e003'}.oi-align-center:before{content:'\e004'}.oi-align-left:before{content:'\e005'}.oi-align-right:before{content:'\e006'}.oi-aperture:before{content:'\e007'}.oi-arrow-bottom:before{content:'\e008'}.oi-arrow-circle-bottom:before{content:'\e009'}.oi-arrow-circle-left:before{content:'\e00a'}.oi-arrow-circle-right:before{content:'\e00b'}.oi-arrow-circle-top:before{content:'\e00c'}.oi-arrow-left:before{content:'\e00d'}.oi-arrow-right:before{content:'\e00e'}.oi-arrow-thick-bottom:before{content:'\e00f'}.oi-arrow-thick-left:before{content:'\e010'}.oi-arrow-thick-right:before{content:'\e011'}.oi-arrow-thick-top:before{content:'\e012'}.oi-arrow-top:before{content:'\e013'}.oi-audio-spectrum:before{content:'\e014'}.oi-audio:before{content:'\e015'}.oi-badge:before{content:'\e016'}.oi-ban:before{content:'\e017'}.oi-bar-chart:before{content:'\e018'}.oi-basket:before{content:'\e019'}.oi-battery-empty:before{content:'\e01a'}.oi-battery-full:before{content:'\e01b'}.oi-beaker:before{content:'\e01c'}.oi-bell:before{content:'\e01d'}.oi-bluetooth:before{content:'\e01e'}.oi-bold:before{content:'\e01f'}.oi-bolt:before{content:'\e020'}.oi-book:before{content:'\e021'}.oi-bookmark:before{content:'\e022'}.oi-box:before{content:'\e023'}.oi-briefcase:before{content:'\e024'}.oi-british-pound:before{content:'\e025'}.oi-browser:before{content:'\e026'}.oi-brush:before{content:'\e027'}.oi-bug:before{content:'\e028'}.oi-bullhorn:before{content:'\e029'}.oi-calculator:before{content:'\e02a'}.oi-calendar:before{content:'\e02b'}.oi-camera-slr:before{content:'\e02c'}.oi-caret-bottom:before{content:'\e02d'}.oi-caret-left:before{content:'\e02e'}.oi-caret-right:before{content:'\e02f'}.oi-caret-top:before{content:'\e030'}.oi-cart:before{content:'\e031'}.oi-chat:before{content:'\e032'}.oi-check:before{content:'\e033'}.oi-chevron-bottom:before{content:'\e034'}.oi-chevron-left:before{content:'\e035'}.oi-chevron-right:before{content:'\e036'}.oi-chevron-top:before{content:'\e037'}.oi-circle-check:before{content:'\e038'}.oi-circle-x:before{content:'\e039'}.oi-clipboard:before{content:'\e03a'}.oi-clock:before{content:'\e03b'}.oi-cloud-download:before{content:'\e03c'}.oi-cloud-upload:before{content:'\e03d'}.oi-cloud:before{content:'\e03e'}.oi-cloudy:before{content:'\e03f'}.oi-code:before{content:'\e040'}.oi-cog:before{content:'\e041'}.oi-collapse-down:before{content:'\e042'}.oi-collapse-left:before{content:'\e043'}.oi-collapse-right:before{content:'\e044'}.oi-collapse-up:before{content:'\e045'}.oi-command:before{content:'\e046'}.oi-comment-square:before{content:'\e047'}.oi-compass:before{content:'\e048'}.oi-contrast:before{content:'\e049'}.oi-copywriting:before{content:'\e04a'}.oi-credit-card:before{content:'\e04b'}.oi-crop:before{content:'\e04c'}.oi-dashboard:before{content:'\e04d'}.oi-data-transfer-download:before{content:'\e04e'}.oi-data-transfer-upload:before{content:'\e04f'}.oi-delete:before{content:'\e050'}.oi-dial:before{content:'\e051'}.oi-document:before{content:'\e052'}.oi-dollar:before{content:'\e053'}.oi-double-quote-sans-left:before{content:'\e054'}.oi-double-quote-sans-right:before{content:'\e055'}.oi-double-quote-serif-left:before{content:'\e056'}.oi-double-quote-serif-right:before{content:'\e057'}.oi-droplet:before{content:'\e058'}.oi-eject:before{content:'\e059'}.oi-elevator:before{content:'\e05a'}.oi-ellipses:before{content:'\e05b'}.oi-envelope-closed:before{content:'\e05c'}.oi-envelope-open:before{content:'\e05d'}.oi-euro:before{content:'\e05e'}.oi-excerpt:before{content:'\e05f'}.oi-expand-down:before{content:'\e060'}.oi-expand-left:before{content:'\e061'}.oi-expand-right:before{content:'\e062'}.oi-expand-up:before{content:'\e063'}.oi-external-link:before{content:'\e064'}.oi-eye:before{content:'\e065'}.oi-eyedropper:before{content:'\e066'}.oi-file:before{content:'\e067'}.oi-fire:before{content:'\e068'}.oi-flag:before{content:'\e069'}.oi-flash:before{content:'\e06a'}.oi-folder:before{content:'\e06b'}.oi-fork:before{content:'\e06c'}.oi-fullscreen-enter:before{content:'\e06d'}.oi-fullscreen-exit:before{content:'\e06e'}.oi-globe:before{content:'\e06f'}.oi-graph:before{content:'\e070'}.oi-grid-four-up:before{content:'\e071'}.oi-grid-three-up:before{content:'\e072'}.oi-grid-two-up:before{content:'\e073'}.oi-hard-drive:before{content:'\e074'}.oi-header:before{content:'\e075'}.oi-headphones:before{content:'\e076'}.oi-heart:before{content:'\e077'}.oi-home:before{content:'\e078'}.oi-image:before{content:'\e079'}.oi-inbox:before{content:'\e07a'}.oi-infinity:before{content:'\e07b'}.oi-info:before{content:'\e07c'}.oi-italic:before{content:'\e07d'}.oi-justify-center:before{content:'\e07e'}.oi-justify-left:before{content:'\e07f'}.oi-justify-right:before{content:'\e080'}.oi-key:before{content:'\e081'}.oi-laptop:before{content:'\e082'}.oi-layers:before{content:'\e083'}.oi-lightbulb:before{content:'\e084'}.oi-link-broken:before{content:'\e085'}.oi-link-intact:before{content:'\e086'}.oi-list-rich:before{content:'\e087'}.oi-list:before{content:'\e088'}.oi-location:before{content:'\e089'}.oi-lock-locked:before{content:'\e08a'}.oi-lock-unlocked:before{content:'\e08b'}.oi-loop-circular:before{content:'\e08c'}.oi-loop-square:before{content:'\e08d'}.oi-loop:before{content:'\e08e'}.oi-magnifying-glass:before{content:'\e08f'}.oi-map-marker:before{content:'\e090'}.oi-map:before{content:'\e091'}.oi-media-pause:before{content:'\e092'}.oi-media-play:before{content:'\e093'}.oi-media-record:before{content:'\e094'}.oi-media-skip-backward:before{content:'\e095'}.oi-media-skip-forward:before{content:'\e096'}.oi-media-step-backward:before{content:'\e097'}.oi-media-step-forward:before{content:'\e098'}.oi-media-stop:before{content:'\e099'}.oi-medical-cross:before{content:'\e09a'}.oi-menu:before{content:'\e09b'}.oi-microphone:before{content:'\e09c'}.oi-minus:before{content:'\e09d'}.oi-monitor:before{content:'\e09e'}.oi-moon:before{content:'\e09f'}.oi-move:before{content:'\e0a0'}.oi-musical-note:before{content:'\e0a1'}.oi-paperclip:before{content:'\e0a2'}.oi-pencil:before{content:'\e0a3'}.oi-people:before{content:'\e0a4'}.oi-person:before{content:'\e0a5'}.oi-phone:before{content:'\e0a6'}.oi-pie-chart:before{content:'\e0a7'}.oi-pin:before{content:'\e0a8'}.oi-play-circle:before{content:'\e0a9'}.oi-plus:before{content:'\e0aa'}.oi-power-standby:before{content:'\e0ab'}.oi-print:before{content:'\e0ac'}.oi-project:before{content:'\e0ad'}.oi-pulse:before{content:'\e0ae'}.oi-puzzle-piece:before{content:'\e0af'}.oi-question-mark:before{content:'\e0b0'}.oi-rain:before{content:'\e0b1'}.oi-random:before{content:'\e0b2'}.oi-reload:before{content:'\e0b3'}.oi-resize-both:before{content:'\e0b4'}.oi-resize-height:before{content:'\e0b5'}.oi-resize-width:before{content:'\e0b6'}.oi-rss-alt:before{content:'\e0b7'}.oi-rss:before{content:'\e0b8'}.oi-script:before{content:'\e0b9'}.oi-share-boxed:before{content:'\e0ba'}.oi-share:before{content:'\e0bb'}.oi-shield:before{content:'\e0bc'}.oi-signal:before{content:'\e0bd'}.oi-signpost:before{content:'\e0be'}.oi-sort-ascending:before{content:'\e0bf'}.oi-sort-descending:before{content:'\e0c0'}.oi-spreadsheet:before{content:'\e0c1'}.oi-star:before{content:'\e0c2'}.oi-sun:before{content:'\e0c3'}.oi-tablet:before{content:'\e0c4'}.oi-tag:before{content:'\e0c5'}.oi-tags:before{content:'\e0c6'}.oi-target:before{content:'\e0c7'}.oi-task:before{content:'\e0c8'}.oi-terminal:before{content:'\e0c9'}.oi-text:before{content:'\e0ca'}.oi-thumb-down:before{content:'\e0cb'}.oi-thumb-up:before{content:'\e0cc'}.oi-timer:before{content:'\e0cd'}.oi-transfer:before{content:'\e0ce'}.oi-trash:before{content:'\e0cf'}.oi-underline:before{content:'\e0d0'}.oi-vertical-align-bottom:before{content:'\e0d1'}.oi-vertical-align-center:before{content:'\e0d2'}.oi-vertical-align-top:before{content:'\e0d3'}.oi-video:before{content:'\e0d4'}.oi-volume-high:before{content:'\e0d5'}.oi-volume-low:before{content:'\e0d6'}.oi-volume-off:before{content:'\e0d7'}.oi-warning:before{content:'\e0d8'}.oi-wifi:before{content:'\e0d9'}.oi-wrench:before{content:'\e0da'}.oi-x:before{content:'\e0db'}.oi-yen:before{content:'\e0dc'}.oi-zoom-in:before{content:'\e0dd'}.oi-zoom-out:before{content:'\e0de'} -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;src:url(../fonts/fontawesome-webfont.eot?v=4.7.0);src:url(../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0) format('embedded-opentype'),url(../fonts/fontawesome-webfont.woff2?v=4.7.0) format('woff2'),url(../fonts/fontawesome-webfont.woff?v=4.7.0) format('woff'),url(../fonts/fontawesome-webfont.ttf?v=4.7.0) format('truetype'),url(../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular) format('svg');font-weight:400;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-rotate-90{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-close:before,.fa-remove:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-repeat:before,.fa-rotate-right:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-image:before,.fa-photo:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-exclamation-triangle:before,.fa-warning:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-floppy-o:before,.fa-save:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before,.fa-navicon:before,.fa-reorder:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-bolt:before,.fa-flash:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-chain-broken:before,.fa-unlink:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-caret-square-o-down:before,.fa-toggle-down:before{content:"\f150"}.fa-caret-square-o-up:before,.fa-toggle-up:before{content:"\f151"}.fa-caret-square-o-right:before,.fa-toggle-right:before{content:"\f152"}.fa-eur:before,.fa-euro:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-inr:before,.fa-rupee:before{content:"\f156"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble:before{content:"\f158"}.fa-krw:before,.fa-won:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-caret-square-o-left:before,.fa-toggle-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-try:before,.fa-turkish-lira:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-bank:before,.fa-institution:before,.fa-university:before{content:"\f19c"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-image-o:before,.fa-file-photo-o:before,.fa-file-picture-o:before{content:"\f1c5"}.fa-file-archive-o:before,.fa-file-zip-o:before{content:"\f1c6"}.fa-file-audio-o:before,.fa-file-sound-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-ring:before,.fa-life-saver:before,.fa-support:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before,.fa-resistance:before{content:"\f1d0"}.fa-empire:before,.fa-ge:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before,.fa-y-combinator-square:before,.fa-yc-square:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-paper-plane:before,.fa-send:before{content:"\f1d8"}.fa-paper-plane-o:before,.fa-send-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-futbol-o:before,.fa-soccer-ball-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-ils:before,.fa-shekel:before,.fa-sheqel:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-bed:before,.fa-hotel:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-y-combinator:before,.fa-yc:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-paper-o:before,.fa-hand-stop-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-television:before,.fa-tv:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before{content:"\f2a3"}.fa-deaf:before,.fa-deafness:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-address-card:before,.fa-vcard:before{content:"\f2bb"}.fa-address-card-o:before,.fa-vcard-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer-full:before,.fa-thermometer:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bath:before,.fa-bathtub:before,.fa-s15:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} -.bootstrap-datetimepicker-widget .btn[data-action=clear]::after,.bootstrap-datetimepicker-widget .btn[data-action=decrementHours]::after,.bootstrap-datetimepicker-widget .btn[data-action=decrementMinutes]::after,.bootstrap-datetimepicker-widget .btn[data-action=incrementHours]::after,.bootstrap-datetimepicker-widget .btn[data-action=incrementMinutes]::after,.bootstrap-datetimepicker-widget .btn[data-action=showHours]::after,.bootstrap-datetimepicker-widget .btn[data-action=showMinutes]::after,.bootstrap-datetimepicker-widget .btn[data-action=today]::after,.bootstrap-datetimepicker-widget .btn[data-action=togglePeriod]::after,.bootstrap-datetimepicker-widget .picker-switch::after,.bootstrap-datetimepicker-widget table th.next::after,.bootstrap-datetimepicker-widget table th.prev::after,.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.bootstrap-datetimepicker-widget{list-style:none}.bootstrap-datetimepicker-widget.dropdown-menu{display:block;margin:2px 0;padding:4px;width:14rem}@media (min-width:576px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}@media (min-width:768px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}@media (min-width:992px){.bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs{width:38em}}.bootstrap-datetimepicker-widget.dropdown-menu:after,.bootstrap-datetimepicker-widget.dropdown-menu:before{content:'';display:inline-block;position:absolute}.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before{border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,.2);top:-7px;left:7px}.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after{border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;top:-6px;left:8px}.bootstrap-datetimepicker-widget.dropdown-menu.top:before{border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,.2);bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.dropdown-menu.top:after{border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget.dropdown-menu.float-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.dropdown-menu.float-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget.dropdown-menu.wider{width:16rem}.bootstrap-datetimepicker-widget .list-unstyled{margin:0}.bootstrap-datetimepicker-widget a[data-action]{padding:6px 0}.bootstrap-datetimepicker-widget a[data-action]:active{box-shadow:none}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:54px;font-weight:700;font-size:1.2em;margin:0}.bootstrap-datetimepicker-widget button[data-action]{padding:6px}.bootstrap-datetimepicker-widget .btn[data-action=incrementHours]::after{content:"Increment Hours"}.bootstrap-datetimepicker-widget .btn[data-action=incrementMinutes]::after{content:"Increment Minutes"}.bootstrap-datetimepicker-widget .btn[data-action=decrementHours]::after{content:"Decrement Hours"}.bootstrap-datetimepicker-widget .btn[data-action=decrementMinutes]::after{content:"Decrement Minutes"}.bootstrap-datetimepicker-widget .btn[data-action=showHours]::after{content:"Show Hours"}.bootstrap-datetimepicker-widget .btn[data-action=showMinutes]::after{content:"Show Minutes"}.bootstrap-datetimepicker-widget .btn[data-action=togglePeriod]::after{content:"Toggle AM/PM"}.bootstrap-datetimepicker-widget .btn[data-action=clear]::after{content:"Clear the picker"}.bootstrap-datetimepicker-widget .btn[data-action=today]::after{content:"Set the date to today"}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget .picker-switch::after{content:"Toggle Date and Time Screens"}.bootstrap-datetimepicker-widget .picker-switch td{padding:0;margin:0;height:auto;width:auto;line-height:inherit}.bootstrap-datetimepicker-widget .picker-switch td span{line-height:2.5;height:2.5em;width:100%}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget table td,.bootstrap-datetimepicker-widget table th{text-align:center;border-radius:.25rem}.bootstrap-datetimepicker-widget table th{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget table th.picker-switch{width:145px}.bootstrap-datetimepicker-widget table th.disabled,.bootstrap-datetimepicker-widget table th.disabled:hover{background:0 0;color:#6c757d;cursor:not-allowed}.bootstrap-datetimepicker-widget table th.prev::after{content:"Previous Month"}.bootstrap-datetimepicker-widget table th.next::after{content:"Next Month"}.bootstrap-datetimepicker-widget table thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget table thead tr:first-child th:hover{background:#e9ecef}.bootstrap-datetimepicker-widget table td{height:54px;line-height:54px;width:54px}.bootstrap-datetimepicker-widget table td.cw{font-size:.8em;height:20px;line-height:20px;color:#6c757d}.bootstrap-datetimepicker-widget table td.day{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget table td.day:hover,.bootstrap-datetimepicker-widget table td.hour:hover,.bootstrap-datetimepicker-widget table td.minute:hover,.bootstrap-datetimepicker-widget table td.second:hover{background:#e9ecef;cursor:pointer}.bootstrap-datetimepicker-widget table td.new,.bootstrap-datetimepicker-widget table td.old{color:#6c757d}.bootstrap-datetimepicker-widget table td.today{position:relative}.bootstrap-datetimepicker-widget table td.today:before{content:'';display:inline-block;border:solid transparent;border-width:0 0 7px 7px;border-bottom-color:#007bff;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget table td.active,.bootstrap-datetimepicker-widget table td.active:hover{background-color:#007bff;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget table td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget table td.disabled,.bootstrap-datetimepicker-widget table td.disabled:hover{background:0 0;color:#6c757d;cursor:not-allowed}.bootstrap-datetimepicker-widget table td span{display:inline-block;width:54px;height:54px;line-height:54px;margin:2px 1.5px;cursor:pointer;border-radius:.25rem}.bootstrap-datetimepicker-widget table td span:hover{background:#e9ecef}.bootstrap-datetimepicker-widget table td span.active{background-color:#007bff;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget table td span.old{color:#6c757d}.bootstrap-datetimepicker-widget table td span.disabled,.bootstrap-datetimepicker-widget table td span.disabled:hover{background:0 0;color:#6c757d;cursor:not-allowed}.bootstrap-datetimepicker-widget.usetwentyfour td.hour{height:27px;line-height:27px}.input-group [data-toggle=datetimepicker]{cursor:pointer} -/*! - * Datepicker for Bootstrap v1.9.0 (https://github.com/uxsolutions/bootstrap-datepicker) - * - * Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0) - */.datepicker{padding:4px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;direction:ltr}.datepicker-inline{width:220px}.datepicker-rtl{direction:rtl}.datepicker-rtl.dropdown-menu{left:auto}.datepicker-rtl table tr td span{float:right}.datepicker-dropdown{top:0;left:0}.datepicker-dropdown:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #999;border-top:0;border-bottom-color:rgba(0,0,0,.2);position:absolute}.datepicker-dropdown:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;border-top:0;position:absolute}.datepicker-dropdown.datepicker-orient-left:before{left:6px}.datepicker-dropdown.datepicker-orient-left:after{left:7px}.datepicker-dropdown.datepicker-orient-right:before{right:6px}.datepicker-dropdown.datepicker-orient-right:after{right:7px}.datepicker-dropdown.datepicker-orient-bottom:before{top:-7px}.datepicker-dropdown.datepicker-orient-bottom:after{top:-6px}.datepicker-dropdown.datepicker-orient-top:before{bottom:-7px;border-bottom:0;border-top:7px solid #999}.datepicker-dropdown.datepicker-orient-top:after{bottom:-6px;border-bottom:0;border-top:6px solid #fff}.datepicker table{margin:0;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.datepicker td,.datepicker th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;border:none}.table-striped .datepicker table tr td,.table-striped .datepicker table tr th{background-color:transparent}.datepicker table tr td.day.focused,.datepicker table tr td.day:hover{background:#eee;cursor:pointer}.datepicker table tr td.new,.datepicker table tr td.old{color:#999}.datepicker table tr td.disabled,.datepicker table tr td.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td.highlighted{background:#d9edf7;border-radius:0}.datepicker table tr td.today,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today:hover{background-color:#fde19a;background-image:-moz-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-ms-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fdd49a),to(#fdf59a));background-image:-webkit-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:-o-linear-gradient(to bottom,#fdd49a,#fdf59a);background-image:linear-gradient(to bottom,#fdd49a,#fdf59a);background-repeat:repeat-x;border-color:#fdf59a #fdf59a #fbed50;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#000}.datepicker table tr td.today.active,.datepicker table tr td.today.disabled,.datepicker table tr td.today.disabled.active,.datepicker table tr td.today.disabled.disabled,.datepicker table tr td.today.disabled:active,.datepicker table tr td.today.disabled:hover,.datepicker table tr td.today.disabled:hover.active,.datepicker table tr td.today.disabled:hover.disabled,.datepicker table tr td.today.disabled:hover:active,.datepicker table tr td.today.disabled:hover:hover,.datepicker table tr td.today.disabled:hover[disabled],.datepicker table tr td.today.disabled[disabled],.datepicker table tr td.today:active,.datepicker table tr td.today:hover,.datepicker table tr td.today:hover.active,.datepicker table tr td.today:hover.disabled,.datepicker table tr td.today:hover:active,.datepicker table tr td.today:hover:hover,.datepicker table tr td.today:hover[disabled],.datepicker table tr td.today[disabled]{background-color:#fdf59a}.datepicker table tr td.today:hover:hover{color:#000}.datepicker table tr td.today.active:hover{color:#fff}.datepicker table tr td.range,.datepicker table tr td.range.disabled,.datepicker table tr td.range.disabled:hover,.datepicker table tr td.range:hover{background:#eee;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.datepicker table tr td.range.today,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today:hover{background-color:#f3d17a;background-image:-moz-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-ms-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f3c17a),to(#f3e97a));background-image:-webkit-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:-o-linear-gradient(to bottom,#f3c17a,#f3e97a);background-image:linear-gradient(to bottom,#f3c17a,#f3e97a);background-repeat:repeat-x;border-color:#f3e97a #f3e97a #edde34;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.datepicker table tr td.range.today.active,.datepicker table tr td.range.today.disabled,.datepicker table tr td.range.today.disabled.active,.datepicker table tr td.range.today.disabled.disabled,.datepicker table tr td.range.today.disabled:active,.datepicker table tr td.range.today.disabled:hover,.datepicker table tr td.range.today.disabled:hover.active,.datepicker table tr td.range.today.disabled:hover.disabled,.datepicker table tr td.range.today.disabled:hover:active,.datepicker table tr td.range.today.disabled:hover:hover,.datepicker table tr td.range.today.disabled:hover[disabled],.datepicker table tr td.range.today.disabled[disabled],.datepicker table tr td.range.today:active,.datepicker table tr td.range.today:hover,.datepicker table tr td.range.today:hover.active,.datepicker table tr td.range.today:hover.disabled,.datepicker table tr td.range.today:hover:active,.datepicker table tr td.range.today:hover:hover,.datepicker table tr td.range.today:hover[disabled],.datepicker table tr td.range.today[disabled]{background-color:#f3e97a}.datepicker table tr td.selected,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected:hover{background-color:#9e9e9e;background-image:-moz-linear-gradient(to bottom,#b3b3b3,grey);background-image:-ms-linear-gradient(to bottom,#b3b3b3,grey);background-image:-webkit-gradient(linear,0 0,0 100%,from(#b3b3b3),to(grey));background-image:-webkit-linear-gradient(to bottom,#b3b3b3,grey);background-image:-o-linear-gradient(to bottom,#b3b3b3,grey);background-image:linear-gradient(to bottom,#b3b3b3,grey);background-repeat:repeat-x;border-color:grey grey #595959;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.selected.active,.datepicker table tr td.selected.disabled,.datepicker table tr td.selected.disabled.active,.datepicker table tr td.selected.disabled.disabled,.datepicker table tr td.selected.disabled:active,.datepicker table tr td.selected.disabled:hover,.datepicker table tr td.selected.disabled:hover.active,.datepicker table tr td.selected.disabled:hover.disabled,.datepicker table tr td.selected.disabled:hover:active,.datepicker table tr td.selected.disabled:hover:hover,.datepicker table tr td.selected.disabled:hover[disabled],.datepicker table tr td.selected.disabled[disabled],.datepicker table tr td.selected:active,.datepicker table tr td.selected:hover,.datepicker table tr td.selected:hover.active,.datepicker table tr td.selected:hover.disabled,.datepicker table tr td.selected:hover:active,.datepicker table tr td.selected:hover:hover,.datepicker table tr td.selected:hover[disabled],.datepicker table tr td.selected[disabled]{background-color:grey}.datepicker table tr td.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active:hover{background-color:#006dcc;background-image:-moz-linear-gradient(to bottom,#08c,#04c);background-image:-ms-linear-gradient(to bottom,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(to bottom,#08c,#04c);background-image:-o-linear-gradient(to bottom,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td.active.active,.datepicker table tr td.active.disabled,.datepicker table tr td.active.disabled.active,.datepicker table tr td.active.disabled.disabled,.datepicker table tr td.active.disabled:active,.datepicker table tr td.active.disabled:hover,.datepicker table tr td.active.disabled:hover.active,.datepicker table tr td.active.disabled:hover.disabled,.datepicker table tr td.active.disabled:hover:active,.datepicker table tr td.active.disabled:hover:hover,.datepicker table tr td.active.disabled:hover[disabled],.datepicker table tr td.active.disabled[disabled],.datepicker table tr td.active:active,.datepicker table tr td.active:hover,.datepicker table tr td.active:hover.active,.datepicker table tr td.active:hover.disabled,.datepicker table tr td.active:hover:active,.datepicker table tr td.active:hover:hover,.datepicker table tr td.active:hover[disabled],.datepicker table tr td.active[disabled]{background-color:#04c}.datepicker table tr td span{display:block;width:23%;height:54px;line-height:54px;float:left;margin:1%;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.datepicker table tr td span.focused,.datepicker table tr td span:hover{background:#eee}.datepicker table tr td span.disabled,.datepicker table tr td span.disabled:hover{background:0 0;color:#999;cursor:default}.datepicker table tr td span.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active:hover{background-color:#006dcc;background-image:-moz-linear-gradient(to bottom,#08c,#04c);background-image:-ms-linear-gradient(to bottom,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(to bottom,#08c,#04c);background-image:-o-linear-gradient(to bottom,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;border-color:#04c #04c #002a80;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.datepicker table tr td span.active.active,.datepicker table tr td span.active.disabled,.datepicker table tr td span.active.disabled.active,.datepicker table tr td span.active.disabled.disabled,.datepicker table tr td span.active.disabled:active,.datepicker table tr td span.active.disabled:hover,.datepicker table tr td span.active.disabled:hover.active,.datepicker table tr td span.active.disabled:hover.disabled,.datepicker table tr td span.active.disabled:hover:active,.datepicker table tr td span.active.disabled:hover:hover,.datepicker table tr td span.active.disabled:hover[disabled],.datepicker table tr td span.active.disabled[disabled],.datepicker table tr td span.active:active,.datepicker table tr td span.active:hover,.datepicker table tr td span.active:hover.active,.datepicker table tr td span.active:hover.disabled,.datepicker table tr td span.active:hover:active,.datepicker table tr td span.active:hover:hover,.datepicker table tr td span.active:hover[disabled],.datepicker table tr td span.active[disabled]{background-color:#04c}.datepicker table tr td span.new,.datepicker table tr td span.old{color:#999}.datepicker .datepicker-switch{width:145px}.datepicker .datepicker-switch,.datepicker .next,.datepicker .prev,.datepicker tfoot tr th{cursor:pointer}.datepicker .datepicker-switch:hover,.datepicker .next:hover,.datepicker .prev:hover,.datepicker tfoot tr th:hover{background:#eee}.datepicker .next.disabled,.datepicker .prev.disabled{visibility:hidden}.datepicker .cw{font-size:10px;width:12px;padding:0 2px 0 5px;vertical-align:middle}.input-append.date .add-on,.input-prepend.date .add-on{cursor:pointer}.input-append.date .add-on i,.input-prepend.date .add-on i{margin-top:3px}.input-daterange input{text-align:center}.input-daterange input:first-child{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px}.input-daterange input:last-child{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0}.input-daterange .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:400;line-height:18px;text-align:center;text-shadow:0 1px 0 #fff;vertical-align:middle;background-color:#eee;border:1px solid #ccc;margin-left:-5px;margin-right:-5px} -.jstValue{white-space:pre-wrap;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;font-weight:400;font-stretch:normal;font-size:13px;line-height:18px;font-family:monospace}.jstComma{white-space:pre-wrap}.jstProperty{color:#000;word-wrap:break-word}.jstBracket{white-space:pre-wrap}.jstBool{color:#1a01cc;font-weight:700}.jstNum{color:#d036d0}.jstNull{color:gray}.jstStr{color:#0b7500}.jstFold:after{content:' -';cursor:pointer}.jstExpand{white-space:normal}.jstExpand:after{content:' +';cursor:pointer}.jstFolded{white-space:normal!important}.jstHiddenBlock{display:none} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.css b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.css deleted file mode 100644 index a0bbbeda6..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.css +++ /dev/null @@ -1,82 +0,0 @@ -body { - margin-top: 65px; -} -.navbar-header { - position: relative; - top: -4px; -} -.navbar-brand > .icon-banner { - position: relative; - top: -2px; - display: inline; -} -.icon { - position: relative; - top: -10px; -} -.logged-out iframe { - display: none; - width: 0; - height: 0; -} -.page-consent .client-logo { - float: left; -} -.page-consent .client-logo img { - width: 80px; - height: 80px; -} -.page-consent .consent-buttons { - margin-top: 25px; -} -.page-consent .consent-form .consent-scopecheck { - display: inline-block; - margin-right: 5px; -} -.page-consent .consent-form .consent-description { - margin-left: 25px; -} -.page-consent .consent-form .consent-description label { - font-weight: normal; -} -.page-consent .consent-form .consent-remember { - padding-left: 16px; -} -.grants .page-header { - margin-bottom: 10px; -} -.grants .grant { - margin-top: 20px; - padding-bottom: 20px; - border-bottom: 1px solid lightgray; -} -.grants .grant img { - width: 100px; - height: 100px; -} -.grants .grant .clientname { - font-size: 140%; - font-weight: bold; -} -.grants .grant .granttype { - font-size: 120%; - font-weight: bold; -} -.grants .grant .created { - font-size: 120%; - font-weight: bold; -} -.grants .grant .expires { - font-size: 120%; - font-weight: bold; -} -.grants .grant li { - list-style-type: none; - display: inline; -} -.grants .grant li:after { - content: ', '; -} -.grants .grant li:last-child:after { - content: ''; -} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.less b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.less deleted file mode 100644 index 09f514107..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.less +++ /dev/null @@ -1,116 +0,0 @@ -body { - margin-top: 65px; -} - -.navbar-header { - position: relative; - top: -4px; -} - -.navbar-brand > .icon-banner { - position: relative; - top: -2px; - display: inline; -} - -.icon { - position: relative; - top: -10px; -} - -.logged-out iframe { - display: none; - width: 0; - height: 0; -} - -.page-consent { - .client-logo { - float: left; - - img { - width: 80px; - height: 80px; - } - } - - .consent-buttons { - margin-top: 25px; - } - - .consent-form { - .consent-scopecheck { - display: inline-block; - margin-right: 5px; - } - - .consent-scopecheck[disabled] { - //visibility:hidden; - } - - .consent-description { - margin-left: 25px; - - label { - font-weight: normal; - } - } - - .consent-remember { - padding-left: 16px; - } - } -} - -.grants { - .page-header { - margin-bottom: 10px; - } - - .grant { - margin-top: 20px; - padding-bottom: 20px; - border-bottom: 1px solid lightgray; - - img { - width: 100px; - height: 100px; - } - - .clientname { - font-size: 140%; - font-weight: bold; - } - - .granttype { - font-size: 120%; - font-weight: bold; - } - - .created { - font-size: 120%; - font-weight: bold; - } - - .expires { - font-size: 120%; - font-weight: bold; - } - - li { - list-style-type: none; - display: inline; - - &:after { - content: ', '; - } - - &:last-child:after { - content: ''; - } - - .displayname { - } - } - } -} diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.min.css b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.min.css deleted file mode 100644 index 84ae0c400..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/site.min.css +++ /dev/null @@ -1 +0,0 @@ -body{margin-top:65px;}.navbar-header{position:relative;top:-4px;}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline;}.icon{position:relative;top:-10px;}.logged-out iframe{display:none;width:0;height:0;}.page-consent .client-logo{float:left;}.page-consent .client-logo img{width:80px;height:80px;}.page-consent .consent-buttons{margin-top:25px;}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px;}.page-consent .consent-form .consent-description{margin-left:25px;}.page-consent .consent-form .consent-description label{font-weight:normal;}.page-consent .consent-form .consent-remember{padding-left:16px;}.grants .page-header{margin-bottom:10px;}.grants .grant{margin-top:20px;padding-bottom:20px;border-bottom:1px solid #d3d3d3;}.grants .grant img{width:100px;height:100px;}.grants .grant .clientname{font-size:140%;font-weight:bold;}.grants .grant .granttype{font-size:120%;font-weight:bold;}.grants .grant .created{font-size:120%;font-weight:bold;}.grants .grant .expires{font-size:120%;font-weight:bold;}.grants .grant li{list-style-type:none;display:inline;}.grants .grant li:after{content:', ';}.grants .grant li:last-child:after{content:'';} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/web.css b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/web.css deleted file mode 100644 index 073e75604..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/web.css +++ /dev/null @@ -1,495 +0,0 @@ -/*Common*/ -@media (max-width: 767px) { - .menu { - text-align: center; } - .menu-logo { - font-size: 1.2em; } - .menu-item { - display: none; } - .menu-button { - display: block; } - .welcome-block h1 { - font-size: 2em; } } - -@media (min-width: 768px) { - .menu-item { - display: block; } - .menu-button { - display: none; } } - -.gravatar-image { - max-width: none; } - -.border-top { - border-top: 1px solid #e5e5e5; } - -.border-bottom { - border-bottom: 1px solid #e5e5e5; } - -.box-shadow { - box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.05); } - -.welcome-block { - max-width: 700px; } - -.logo, .logo:hover { - color: black; - text-decoration: none; } - -.col-m-5 { - margin: 5px; } - -.col-m-10 { - margin: 10px; } - -.col-m-15 { - margin: 15px; } - -.col-m-20 { - margin: 20px; } - -.col-m-25 { - margin: 25px; } - -.col-m-30 { - margin: 30px; } - -.col-m-35 { - margin: 35px; } - -.col-m-40 { - margin: 40px; } - -.col-m-45 { - margin: 45px; } - -.col-m-50 { - margin: 50px; } - -.col-m-b-5 { - margin-bottom: 5px; } - -.col-m-b-10 { - margin-bottom: 10px; } - -.col-m-b-15 { - margin-bottom: 15px; } - -.col-m-b-20 { - margin-bottom: 20px; } - -.col-m-b-25 { - margin-bottom: 25px; } - -.col-m-b-30 { - margin-bottom: 30px; } - -.col-m-b-35 { - margin-bottom: 35px; } - -.col-m-b-40 { - margin-bottom: 40px; } - -.col-m-b-45 { - margin-bottom: 45px; } - -.col-m-b-50 { - margin-bottom: 50px; } - -.col-m-t-5 { - margin-top: 5px; } - -.col-m-t-10 { - margin-top: 10px; } - -.col-m-t-15 { - margin-top: 15px; } - -.col-m-t-20 { - margin-top: 20px; } - -.col-m-t-25 { - margin-top: 25px; } - -.col-m-t-30 { - margin-top: 30px; } - -.col-m-t-35 { - margin-top: 35px; } - -.col-m-t-40 { - margin-top: 40px; } - -.col-m-t-45 { - margin-top: 45px; } - -.col-m-t-50 { - margin-top: 50px; } - -.col-m-l-5 { - margin-left: 5px; } - -.col-m-l-10 { - margin-left: 10px; } - -.col-m-l-15 { - margin-left: 15px; } - -.col-m-l-20 { - margin-left: 20px; } - -.col-m-l-25 { - margin-left: 25px; } - -.col-m-l-30 { - margin-left: 30px; } - -.col-m-l-35 { - margin-left: 35px; } - -.col-m-l-40 { - margin-left: 40px; } - -.col-m-l-45 { - margin-left: 45px; } - -.col-m-l-50 { - margin-left: 50px; } - -.col-m-r-5 { - margin-right: 5px; } - -.col-m-r-10 { - margin-right: 10px; } - -.col-m-r-15 { - margin-right: 15px; } - -.col-m-r-20 { - margin-right: 20px; } - -.col-m-r-25 { - margin-right: 25px; } - -.col-m-r-30 { - margin-right: 30px; } - -.col-m-r-35 { - margin-right: 35px; } - -.col-m-r-40 { - margin-right: 40px; } - -.col-m-r-45 { - margin-right: 45px; } - -.col-m-r-50 { - margin-right: 50px; } - -.col-p-5 { - padding: 5px; } - -.col-p-10 { - padding: 10px; } - -.col-p-15 { - padding: 15px; } - -.col-p-20 { - padding: 20px; } - -.col-p-25 { - padding: 25px; } - -.col-p-30 { - padding: 30px; } - -.col-p-35 { - padding: 35px; } - -.col-p-40 { - padding: 40px; } - -.col-p-45 { - padding: 45px; } - -.col-p-50 { - padding: 50px; } - -.col-p-b-5 { - padding-bottom: 5px; } - -.col-p-b-10 { - padding-bottom: 10px; } - -.col-p-b-15 { - padding-bottom: 15px; } - -.col-p-b-20 { - padding-bottom: 20px; } - -.col-p-b-25 { - padding-bottom: 25px; } - -.col-p-b-30 { - padding-bottom: 30px; } - -.col-p-b-35 { - padding-bottom: 35px; } - -.col-p-b-40 { - padding-bottom: 40px; } - -.col-p-b-45 { - padding-bottom: 45px; } - -.col-p-b-50 { - padding-bottom: 50px; } - -.col-p-t-5 { - padding-top: 5px; } - -.col-p-t-10 { - padding-top: 10px; } - -.col-p-t-15 { - padding-top: 15px; } - -.col-p-t-20 { - padding-top: 20px; } - -.col-p-t-25 { - padding-top: 25px; } - -.col-p-t-30 { - padding-top: 30px; } - -.col-p-t-35 { - padding-top: 35px; } - -.col-p-t-40 { - padding-top: 40px; } - -.col-p-t-45 { - padding-top: 45px; } - -.col-p-t-50 { - padding-top: 50px; } - -.col-p-l-5 { - padding-left: 5px; } - -.col-p-l-10 { - padding-left: 10px; } - -.col-p-l-15 { - padding-left: 15px; } - -.col-p-l-20 { - padding-left: 20px; } - -.col-p-l-25 { - padding-left: 25px; } - -.col-p-l-30 { - padding-left: 30px; } - -.col-p-l-35 { - padding-left: 35px; } - -.col-p-l-40 { - padding-left: 40px; } - -.col-p-l-45 { - padding-left: 45px; } - -.col-p-l-50 { - padding-left: 50px; } - -.col-p-r-5 { - padding-right: 5px; } - -.col-p-r-10 { - padding-right: 10px; } - -.col-p-r-15 { - padding-right: 15px; } - -.col-p-r-20 { - padding-right: 20px; } - -.col-p-r-25 { - padding-right: 25px; } - -.col-p-r-30 { - padding-right: 30px; } - -.col-p-r-35 { - padding-right: 35px; } - -.col-p-r-40 { - padding-right: 40px; } - -.col-p-r-45 { - padding-right: 45px; } - -.col-p-r-50 { - padding-right: 50px; } - -.col-form-label { - text-align: right; - font-weight: bold; } - -@media (max-width: 575px) { - .col-form-label { - text-align: left; } } - -/*Controls*/ -.navbar-brand > img { - max-height: 31px; } - -.navbar-brand.logo { - padding: 10px 10px; } - -.navbar-brand-image-mobile { - display: none; } - -@media (max-width: 767px) { - .navbar-brand-image { - display: none; } - .navbar-brand-image-mobile { - display: block; - padding-left: 0; - padding-right: 0; } } - -.validation-summary-errors { - color: #ff3834; - border: 1px solid #ffd3d3; - background: none; - padding-top: 9px; - -ms-border-radius: 4px; - border-radius: 4px; - margin-bottom: 15px; - margin-top: 20px; } - -.switch { - display: inline-block; - height: 28px; - position: relative; - width: 60px; - margin-top: 5px; } - -.switch input { - display: none; } - -.slider { - background-color: #ccc; - bottom: 0; - cursor: pointer; - left: 0; - position: absolute; - right: 0; - top: 0; - transition: .4s; } - -.slider:before { - background-color: #fff; - bottom: 4px; - content: ""; - height: 20px; - left: 7px; - position: absolute; - transition: .4s; - width: 20px; } - -input:checked + .slider { - background-color: #007bff; } - -input:checked + .slider:before { - transform: translateX(26px); } - -.slider.round { - border-radius: 34px; } - -.slider.round:before { - border-radius: 50%; } - -.picker hr { - margin-bottom: 5px; } - -.picker .button__text { - display: inline-block; - text-align: center; - vertical-align: middle; - margin: 3px; - border: 1px solid transparent; - padding: .375rem .75rem; - font-size: 1rem; - line-height: 1.5; - border-radius: .25rem; - border-color: #007bff; - color: #007bff; } - -.picker .button__add { - margin-top: 5px; - margin-bottom: 5px; - margin-right: 5px; - white-space: normal; - text-transform: none; } - -.picker .button__delete, .picker .button__update { - margin-top: 5px; - margin-bottom: 5px; - margin-right: 5px; - white-space: normal; - text-transform: none; } - -.picker .button__show-all { - margin-top: 5px; - margin-bottom: 5px; - margin-right: 5px; - white-space: normal; - text-transform: none; } - -.picker .block__buttons__add { - margin-top: 4px; } - -.picker .search-title { - color: gray; - font-size: 12px; } - -#toast-container > div { - opacity: 1; - -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); - filter: alpha(opacity=100); } - -label.radio-img > input { - visibility: hidden; - position: absolute; } - -label.radio-img > input + img { - cursor: pointer; - border: 2px solid transparent; } - -label.radio-img > input:checked + img { - background-color: #ffe7ac; - -ms-border-radius: 15px; - border-radius: 15px; } - -label.radio-img > input:checked ~ h3, label.radio-img > input:checked ~ h4 { - color: #007bff; - text-decoration: underline; } - -label.radio-img > h3, label > h4 { - cursor: pointer; } - -/*Pages*/ -.client .action-butons { - margin: 20px 0 20px 0; } - -.audit-log-container .modal-dialog { - overflow-y: initial !important; } - -.audit-log-container .modal-body { - max-height: calc(100vh - 200px); - overflow-y: auto; } diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/web.min.css b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/web.min.css deleted file mode 100644 index f27958161..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/css/web.min.css +++ /dev/null @@ -1 +0,0 @@ -@media (max-width:767px){.menu{text-align:center}.menu-logo{font-size:1.2em}.menu-item{display:none}.menu-button{display:block}.welcome-block h1{font-size:2em}}@media (min-width:768px){.menu-item{display:block}.menu-button{display:none}}.gravatar-image{max-width:none}.border-top{border-top:1px solid #e5e5e5}.border-bottom{border-bottom:1px solid #e5e5e5}.box-shadow{box-shadow:0 .25rem .75rem rgba(0,0,0,.05)}.welcome-block{max-width:700px}.logo,.logo:hover{color:#000;text-decoration:none}.col-m-5{margin:5px}.col-m-10{margin:10px}.col-m-15{margin:15px}.col-m-20{margin:20px}.col-m-25{margin:25px}.col-m-30{margin:30px}.col-m-35{margin:35px}.col-m-40{margin:40px}.col-m-45{margin:45px}.col-m-50{margin:50px}.col-m-b-5{margin-bottom:5px}.col-m-b-10{margin-bottom:10px}.col-m-b-15{margin-bottom:15px}.col-m-b-20{margin-bottom:20px}.col-m-b-25{margin-bottom:25px}.col-m-b-30{margin-bottom:30px}.col-m-b-35{margin-bottom:35px}.col-m-b-40{margin-bottom:40px}.col-m-b-45{margin-bottom:45px}.col-m-b-50{margin-bottom:50px}.col-m-t-5{margin-top:5px}.col-m-t-10{margin-top:10px}.col-m-t-15{margin-top:15px}.col-m-t-20{margin-top:20px}.col-m-t-25{margin-top:25px}.col-m-t-30{margin-top:30px}.col-m-t-35{margin-top:35px}.col-m-t-40{margin-top:40px}.col-m-t-45{margin-top:45px}.col-m-t-50{margin-top:50px}.col-m-l-5{margin-left:5px}.col-m-l-10{margin-left:10px}.col-m-l-15{margin-left:15px}.col-m-l-20{margin-left:20px}.col-m-l-25{margin-left:25px}.col-m-l-30{margin-left:30px}.col-m-l-35{margin-left:35px}.col-m-l-40{margin-left:40px}.col-m-l-45{margin-left:45px}.col-m-l-50{margin-left:50px}.col-m-r-5{margin-right:5px}.col-m-r-10{margin-right:10px}.col-m-r-15{margin-right:15px}.col-m-r-20{margin-right:20px}.col-m-r-25{margin-right:25px}.col-m-r-30{margin-right:30px}.col-m-r-35{margin-right:35px}.col-m-r-40{margin-right:40px}.col-m-r-45{margin-right:45px}.col-m-r-50{margin-right:50px}.col-p-5{padding:5px}.col-p-10{padding:10px}.col-p-15{padding:15px}.col-p-20{padding:20px}.col-p-25{padding:25px}.col-p-30{padding:30px}.col-p-35{padding:35px}.col-p-40{padding:40px}.col-p-45{padding:45px}.col-p-50{padding:50px}.col-p-b-5{padding-bottom:5px}.col-p-b-10{padding-bottom:10px}.col-p-b-15{padding-bottom:15px}.col-p-b-20{padding-bottom:20px}.col-p-b-25{padding-bottom:25px}.col-p-b-30{padding-bottom:30px}.col-p-b-35{padding-bottom:35px}.col-p-b-40{padding-bottom:40px}.col-p-b-45{padding-bottom:45px}.col-p-b-50{padding-bottom:50px}.col-p-t-5{padding-top:5px}.col-p-t-10{padding-top:10px}.col-p-t-15{padding-top:15px}.col-p-t-20{padding-top:20px}.col-p-t-25{padding-top:25px}.col-p-t-30{padding-top:30px}.col-p-t-35{padding-top:35px}.col-p-t-40{padding-top:40px}.col-p-t-45{padding-top:45px}.col-p-t-50{padding-top:50px}.col-p-l-5{padding-left:5px}.col-p-l-10{padding-left:10px}.col-p-l-15{padding-left:15px}.col-p-l-20{padding-left:20px}.col-p-l-25{padding-left:25px}.col-p-l-30{padding-left:30px}.col-p-l-35{padding-left:35px}.col-p-l-40{padding-left:40px}.col-p-l-45{padding-left:45px}.col-p-l-50{padding-left:50px}.col-p-r-5{padding-right:5px}.col-p-r-10{padding-right:10px}.col-p-r-15{padding-right:15px}.col-p-r-20{padding-right:20px}.col-p-r-25{padding-right:25px}.col-p-r-30{padding-right:30px}.col-p-r-35{padding-right:35px}.col-p-r-40{padding-right:40px}.col-p-r-45{padding-right:45px}.col-p-r-50{padding-right:50px}.col-form-label{text-align:right;font-weight:700}@media (max-width:575px){.col-form-label{text-align:left}}.navbar-brand>img{max-height:31px}.navbar-brand.logo{padding:10px 10px}.navbar-brand-image-mobile{display:none}@media (max-width:767px){.navbar-brand-image{display:none}.navbar-brand-image-mobile{display:block;padding-left:0;padding-right:0}}.validation-summary-errors{color:#ff3834;border:1px solid #ffd3d3;background:0 0;padding-top:9px;-ms-border-radius:4px;border-radius:4px;margin-bottom:15px;margin-top:20px}.switch{display:inline-block;height:28px;position:relative;width:60px;margin-top:5px}.switch input{display:none}.slider{background-color:#ccc;bottom:0;cursor:pointer;left:0;position:absolute;right:0;top:0;transition:.4s}.slider:before{background-color:#fff;bottom:4px;content:"";height:20px;left:7px;position:absolute;transition:.4s;width:20px}input:checked+.slider{background-color:#007bff}input:checked+.slider:before{transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.picker hr{margin-bottom:5px}.picker .button__text{display:inline-block;text-align:center;vertical-align:middle;margin:3px;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;border-color:#007bff;color:#007bff}.picker .button__add{margin-top:5px;margin-bottom:5px;margin-right:5px;white-space:normal;text-transform:none}.picker .button__delete,.picker .button__update{margin-top:5px;margin-bottom:5px;margin-right:5px;white-space:normal;text-transform:none}.picker .button__show-all{margin-top:5px;margin-bottom:5px;margin-right:5px;white-space:normal;text-transform:none}.picker .block__buttons__add{margin-top:4px}.picker .search-title{color:gray;font-size:12px}#toast-container>div{opacity:1}label.radio-img>input{visibility:hidden;position:absolute}label.radio-img>input+img{cursor:pointer;border:2px solid transparent}label.radio-img>input:checked+img{background-color:#ffe7ac;-ms-border-radius:15px;border-radius:15px}label.radio-img>input:checked~h3,label.radio-img>input:checked~h4{color:#007bff;text-decoration:underline}label.radio-img>h3,label>h4{cursor:pointer}.client .action-butons{margin:20px 0 20px 0}.audit-log-container .modal-dialog{overflow-y:initial!important}.audit-log-container .modal-body{max-height:calc(100vh - 200px);overflow-y:auto} \ No newline at end of file diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/favicon.ico b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/favicon.ico deleted file mode 100644 index ee470e42f..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/favicon.ico and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/FontAwesome.otf b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/FontAwesome.otf deleted file mode 100644 index 401ec0f36..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/FontAwesome.otf and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.eot b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.eot deleted file mode 100644 index e9f60ca95..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.svg b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.svg deleted file mode 100644 index 855c845e5..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,2671 +0,0 @@ - - - - -Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 - By ,,, -Copyright Dave Gandy 2016. All rights reserved. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.ttf b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 35acda2fa..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff deleted file mode 100644 index 400014a4b..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff2 b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff2 deleted file mode 100644 index 4d13fc604..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/fontawesome-webfont.woff2 and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.eot b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.eot deleted file mode 100644 index f98177dbf..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.eot and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.otf b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.otf deleted file mode 100644 index f6bd6846a..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.otf and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.svg b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.svg deleted file mode 100644 index 32b2c4e99..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.svg +++ /dev/null @@ -1,543 +0,0 @@ - - - - - -Created by FontForge 20120731 at Tue Jul 1 20:39:22 2014 - By P.J. Onori -Created by P.J. Onori with FontForge 2.0 (http://fontforge.sf.net) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.ttf b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.ttf deleted file mode 100644 index fab604866..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.ttf and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.woff b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.woff deleted file mode 100644 index f9309988a..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/fonts/open-iconic.woff and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/icon.jpg b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/icon.jpg deleted file mode 100644 index e6525028a..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/icon.jpg and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/icon.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/icon.png deleted file mode 100644 index cd386d517..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/icon.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/device-client.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/device-client.png deleted file mode 100644 index 1728196a6..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/device-client.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/empty-client.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/empty-client.png deleted file mode 100644 index 174d9f867..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/empty-client.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/native-client.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/native-client.png deleted file mode 100644 index a3ce53cce..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/native-client.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/server-client.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/server-client.png deleted file mode 100644 index d33be20a9..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/server-client.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/spa-client.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/spa-client.png deleted file mode 100644 index cc81882f7..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/spa-client.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/web-client.png b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/web-client.png deleted file mode 100644 index d89ef174c..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/icons/web-client.png and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/loading.gif b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/loading.gif deleted file mode 100644 index ed05ae59b..000000000 Binary files a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/images/loading.gif and /dev/null differ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/js/bundle.min.js b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/js/bundle.min.js deleted file mode 100644 index ec05d5583..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/js/bundle.min.js +++ /dev/null @@ -1 +0,0 @@ -if(function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(x,e){"use strict";function m(e){return null!=e&&e===e.window}var t=[],C=x.document,i=Object.getPrototypeOf,s=t.slice,g=t.concat,l=t.push,r=t.indexOf,n={},a=n.toString,v=n.hasOwnProperty,o=v.toString,u=o.call(Object),y={},b=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},c={type:!0,src:!0,nonce:!0,noModule:!0};function _(e,t,n){var i,r,a=(n=n||C).createElement("script");if(a.text=e,t)for(i in c)(r=t[i]||t.getAttribute&&t.getAttribute(i))&&a.setAttribute(i,r);n.head.appendChild(a).parentNode.removeChild(a)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[a.call(e)]||"object":typeof e}var d="3.4.1",k=function(e,t){return new k.fn.init(e,t)},h=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function f(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!b(e)&&!m(e)&&("array"===n||0===t||"number"==typeof t&&0>10|55296,1023&i|56320)}function r(){D()}var e,f,_,a,o,p,h,m,w,l,u,D,x,s,C,g,c,v,y,k="sizzle"+1*new Date,b=n.document,T=0,i=0,S=le(),E=le(),M=le(),A=le(),O=function(e,t){return e===t&&(u=!0),0},N={}.hasOwnProperty,t=[],I=t.pop,j=t.push,P=t.push,L=t.slice,H=function(e,t){for(var n=0,i=e.length;n+~]|"+R+")"+R+"*"),z=new RegExp(R+"|>"),G=new RegExp(U),K=new RegExp("^"+Y+"$"),Q={ID:new RegExp("^#("+Y+")"),CLASS:new RegExp("^\\.("+Y+")"),TAG:new RegExp("^("+Y+"|[*])"),ATTR:new RegExp("^"+V),PSEUDO:new RegExp("^"+U),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+F+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},J=/HTML$/i,Z=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,ee=/^[^{]+\{\s*\[native \w/,te=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ne=/[+~]/,ie=new RegExp("\\\\([\\da-f]{1,6}"+R+"?|("+R+")|.)","ig"),re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ae=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=_e(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{P.apply(t=L.call(b.childNodes),b.childNodes),t[b.childNodes.length].nodeType}catch(e){P={apply:t.length?function(e,t){j.apply(e,L.call(t))}:function(e,t){for(var n=e.length,i=0;e[n++]=t[i++];);e.length=n-1}}}function se(t,e,n,i){var r,a,o,s,l,u,c,d=e&&e.ownerDocument,h=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==h&&9!==h&&11!==h)return n;if(!i&&((e?e.ownerDocument||e:b)!==x&&D(e),e=e||x,C)){if(11!==h&&(l=te.exec(t)))if(r=l[1]){if(9===h){if(!(o=e.getElementById(r)))return n;if(o.id===r)return n.push(o),n}else if(d&&(o=d.getElementById(r))&&y(e,o)&&o.id===r)return n.push(o),n}else{if(l[2])return P.apply(n,e.getElementsByTagName(t)),n;if((r=l[3])&&f.getElementsByClassName&&e.getElementsByClassName)return P.apply(n,e.getElementsByClassName(r)),n}if(f.qsa&&!A[t+" "]&&(!g||!g.test(t))&&(1!==h||"object"!==e.nodeName.toLowerCase())){if(c=t,d=e,1===h&&z.test(t)){for((s=e.getAttribute("id"))?s=s.replace(re,ae):e.setAttribute("id",s=k),a=(u=p(t)).length;a--;)u[a]="#"+s+" "+be(u[a]);c=u.join(","),d=ne.test(t)&&ve(e.parentNode)||e}try{return P.apply(n,d.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return m(t.replace(q,"$1"),e,n,i)}function le(){var i=[];return function e(t,n){return i.push(t+" ")>_.cacheLength&&delete e[i.shift()],e[t+" "]=n}}function ue(e){return e[k]=!0,e}function ce(e){var t=x.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function de(e,t){for(var n=e.split("|"),i=n.length;i--;)_.attrHandle[n[i]]=t}function he(e,t){var n=t&&e,i=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(i)return i;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function fe(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function pe(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function me(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&oe(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ge(o){return ue(function(a){return a=+a,ue(function(e,t){for(var n,i=o([],e.length,a),r=i.length;r--;)e[n=i[r]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&void 0!==e.getElementsByTagName&&e}for(e in f=se.support={},o=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!J.test(t||n&&n.nodeName||"HTML")},D=se.setDocument=function(e){var t,n,i=e?e.ownerDocument||e:b;return i!==x&&9===i.nodeType&&i.documentElement&&(s=(x=i).documentElement,C=!o(x),b!==x&&(n=x.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",r,!1):n.attachEvent&&n.attachEvent("onunload",r)),f.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),f.getElementsByTagName=ce(function(e){return e.appendChild(x.createComment("")),!e.getElementsByTagName("*").length}),f.getElementsByClassName=ee.test(x.getElementsByClassName),f.getById=ce(function(e){return s.appendChild(e).id=k,!x.getElementsByName||!x.getElementsByName(k).length}),f.getById?(_.filter.ID=function(e){var t=e.replace(ie,d);return function(e){return e.getAttribute("id")===t}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(_.filter.ID=function(e){var n=e.replace(ie,d);return function(e){var t=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},_.find.ID=function(e,t){if(void 0!==t.getElementById&&C){var n,i,r,a=t.getElementById(e);if(a){if((n=a.getAttributeNode("id"))&&n.value===e)return[a];for(r=t.getElementsByName(e),i=0;a=r[i++];)if((n=a.getAttributeNode("id"))&&n.value===e)return[a]}return[]}}),_.find.TAG=f.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):f.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,i=[],r=0,a=t.getElementsByTagName(e);if("*"!==e)return a;for(;n=a[r++];)1===n.nodeType&&i.push(n);return i},_.find.CLASS=f.getElementsByClassName&&function(e,t){if(void 0!==t.getElementsByClassName&&C)return t.getElementsByClassName(e)},c=[],g=[],(f.qsa=ee.test(x.querySelectorAll))&&(ce(function(e){s.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&g.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||g.push("\\["+R+"*(?:value|"+F+")"),e.querySelectorAll("[id~="+k+"-]").length||g.push("~="),e.querySelectorAll(":checked").length||g.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||g.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=x.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&g.push("name"+R+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&g.push(":enabled",":disabled"),s.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(f.matchesSelector=ee.test(v=s.matches||s.webkitMatchesSelector||s.mozMatchesSelector||s.oMatchesSelector||s.msMatchesSelector))&&ce(function(e){f.disconnectedMatch=v.call(e,"*"),v.call(e,"[s!='']:x"),c.push("!=",U)}),g=g.length&&new RegExp(g.join("|")),c=c.length&&new RegExp(c.join("|")),t=ee.test(s.compareDocumentPosition),y=t||ee.test(s.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,i=t&&t.parentNode;return e===i||!(!i||1!==i.nodeType||!(n.contains?n.contains(i):e.compareDocumentPosition&&16&e.compareDocumentPosition(i)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},O=t?function(e,t){if(e===t)return u=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!f.sortDetached&&t.compareDocumentPosition(e)===n?e===x||e.ownerDocument===b&&y(b,e)?-1:t===x||t.ownerDocument===b&&y(b,t)?1:l?H(l,e)-H(l,t):0:4&n?-1:1)}:function(e,t){if(e===t)return u=!0,0;var n,i=0,r=e.parentNode,a=t.parentNode,o=[e],s=[t];if(!r||!a)return e===x?-1:t===x?1:r?-1:a?1:l?H(l,e)-H(l,t):0;if(r===a)return he(e,t);for(n=e;n=n.parentNode;)o.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;o[i]===s[i];)i++;return i?he(o[i],s[i]):o[i]===b?-1:s[i]===b?1:0}),x},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==x&&D(e),f.matchesSelector&&C&&!A[t+" "]&&(!c||!c.test(t))&&(!g||!g.test(t)))try{var n=v.call(e,t);if(n||f.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ie,d),e[3]=(e[3]||e[4]||e[5]||"").replace(ie,d),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&G.test(n)&&(t=p(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(ie,d).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=S[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&S(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,i,r){return function(e){var t=se.attr(e,n);return null==t?"!="===i:!i||(t+="","="===i?t===r:"!="===i?t!==r:"^="===i?r&&0===t.indexOf(r):"*="===i?r&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function A(e,n,i){return b(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==i}):n.nodeType?k.grep(e,function(e){return e===n!==i}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var i,r;if(!e)return this;if(n=n||O,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):b(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this);if(!(i="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:N.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),M.test(i[1])&&k.isPlainObject(t))for(i in t)b(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(r=C.getElementById(i[2]))&&(this[0]=r,this.length=1),this}).prototype=k.fn,O=k(C);var I=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,pe=/^$|^module$|\/(?:java|ecma)script/i,me={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ge(e,t){var n;return n=void 0!==e.getElementsByTagName?e.getElementsByTagName(t||"*"):void 0!==e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&E(e,t)?k.merge([e],n):n}function ve(e,t){for(var n=0,i=e.length;nx",y.noCloneChecked=!!ye.cloneNode(!0).lastChild.defaultValue;var De=/^key/,xe=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Te(){return!1}function Se(e,t){return e===function(){try{return C.activeElement}catch(e){}}()==("focus"===t)}function Ee(e,t,n,i,r,a){var o,s;if("object"==typeof t){for(s in"string"!=typeof n&&(i=i||n,n=void 0),t)Ee(e,s,n,i,t[s],a);return e}if(null==i&&null==r?(r=n,i=n=void 0):null==r&&("string"==typeof n?(r=i,i=void 0):(r=i,i=n,n=void 0)),!1===r)r=Te;else if(!r)return e;return 1===a&&(o=r,(r=function(e){return k().off(e),o.apply(this,arguments)}).guid=o.guid||(o.guid=k.guid++)),e.each(function(){k.event.add(this,t,r,i,n)})}function Me(e,r,a){a?(Q.set(e,r,!1),k.event.add(e,r,{namespace:!1,handler:function(e){var t,n,i=Q.get(this,r);if(1&e.isTrigger&&this[r]){if(i.length)(k.event.special[r]||{}).delegateType&&e.stopPropagation();else if(i=s.call(arguments),Q.set(this,r,i),t=a(this,r),this[r](),i!==(n=Q.get(this,r))||t?Q.set(this,r,!1):n={},i!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else i.length&&(Q.set(this,r,{value:k.event.trigger(k.extend(i[0],k.Event.prototype),i.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,r)&&k.event.add(e,r,ke)}k.event={global:{},add:function(t,e,n,i,r){var a,o,s,l,u,c,d,h,f,p,m,g=Q.get(t);if(g)for(n.handler&&(n=(a=n).handler,r=a.selector),r&&k.find.matchesSelector(re,r),n.guid||(n.guid=k.guid++),(l=g.events)||(l=g.events={}),(o=g.handle)||(o=g.handle=function(e){return void 0!==k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),u=(e=(e||"").match(L)||[""]).length;u--;)f=m=(s=Ce.exec(e[u])||[])[1],p=(s[2]||"").split(".").sort(),f&&(d=k.event.special[f]||{},f=(r?d.delegateType:d.bindType)||f,d=k.event.special[f]||{},c=k.extend({type:f,origType:m,data:i,handler:n,guid:n.guid,selector:r,needsContext:r&&k.expr.match.needsContext.test(r),namespace:p.join(".")},a),(h=l[f])||((h=l[f]=[]).delegateCount=0,d.setup&&!1!==d.setup.call(t,i,p,o)||t.addEventListener&&t.addEventListener(f,o)),d.add&&(d.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),r?h.splice(h.delegateCount++,0,c):h.push(c),k.event.global[f]=!0)},remove:function(e,t,n,i,r){var a,o,s,l,u,c,d,h,f,p,m,g=Q.hasData(e)&&Q.get(e);if(g&&(l=g.events)){for(u=(t=(t||"").match(L)||[""]).length;u--;)if(f=m=(s=Ce.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),f){for(d=k.event.special[f]||{},h=l[f=(i?d.delegateType:d.bindType)||f]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),o=a=h.length;a--;)c=h[a],!r&&m!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||i&&i!==c.selector&&("**"!==i||!c.selector)||(h.splice(a,1),c.selector&&h.delegateCount--,d.remove&&d.remove.call(e,c));o&&!h.length&&(d.teardown&&!1!==d.teardown.call(e,p,g.handle)||k.removeEvent(e,f,g.handle),delete l[f])}else for(f in l)k.event.remove(e,f+t[u],n,i,!0);k.isEmptyObject(l)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,i,r,a,o,s=k.event.fix(e),l=new Array(arguments.length),u=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(l[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,Oe=/\s*$/g;function je(e,t){return E(e,"table")&&E(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Le(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function He(e,t){var n,i,r,a,o,s,l,u;if(1===t.nodeType){if(Q.hasData(e)&&(a=Q.access(e),o=Q.set(t,a),u=a.events))for(r in delete o.handle,o.events={},u)for(n=0,i=u[r].length;n")},clone:function(e,t,n){var i,r,a,o,s,l,u,c=e.cloneNode(!0),d=ae(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(o=ge(c),i=0,r=(a=ge(e)).length;i").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",r=function(e){i.remove(),r=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(i[0])},abort:function(){r&&r()}}});var nn,rn=[],an=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=rn.pop()||k.expando+"_"+jt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var i,r,a,o=!1!==e.jsonp&&(an.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&an.test(e.data)&&"data");if(o||"jsonp"===e.dataTypes[0])return i=e.jsonpCallback=b(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,o?e[o]=e[o].replace(an,"$1"+i):!1!==e.jsonp&&(e.url+=(Pt.test(e.url)?"&":"?")+e.jsonp+"="+i),e.converters["script json"]=function(){return a||k.error(i+" was not called"),a[0]},e.dataTypes[0]="json",r=x[i],x[i]=function(){a=arguments},n.always(function(){void 0===r?k(x).removeProp(i):x[i]=r,e[i]&&(e.jsonpCallback=t.jsonpCallback,rn.push(i)),a&&b(r)&&r(a[0]),a=r=void 0}),"script"}),y.createHTMLDocument=((nn=C.implementation.createHTMLDocument("").body).innerHTML="
    ",2===nn.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((i=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(i)):t=C),a=!n&&[],(r=M.exec(e))?[t.createElement(r[1])]:(r=we([e],t,a),a&&a.length&&k(a).remove(),k.merge([],r.childNodes)));var i,r,a},k.fn.load=function(e,t,n){var i,r,a,o=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(i):e)}).always(n&&function(e,t){o.each(function(){n.apply(this,a||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var i,r,a,o,s,l,u=k.css(e,"position"),c=k(e),d={};"static"===u&&(e.style.position="relative"),s=c.offset(),a=k.css(e,"top"),l=k.css(e,"left"),r=("absolute"===u||"fixed"===u)&&-1<(a+l).indexOf("auto")?(o=(i=c.position()).top,i.left):(o=parseFloat(a)||0,parseFloat(l)||0),b(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(d.top=t.top-s.top+o),null!=t.left&&(d.left=t.left-s.left+r),"using"in t?t.using.call(e,d):c.css(d)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,i=this[0];return i?i.getClientRects().length?(e=i.getBoundingClientRect(),n=i.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,i=this[0],r={top:0,left:0};if("fixed"===k.css(i,"position"))t=i.getBoundingClientRect();else{for(t=this.offset(),n=i.ownerDocument,e=i.offsetParent||n.documentElement;e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position");)e=e.parentNode;e&&e!==i&&1===e.nodeType&&((r=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),r.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-r.top-k.css(i,"marginTop",!0),left:t.left-r.left-k.css(i,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent;e&&"static"===k.css(e,"position");)e=e.offsetParent;return e||re})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,r){var a="pageYOffset"===r;k.fn[t]=function(e){return W(this,function(e,t,n){var i;if(m(e)?i=e:9===e.nodeType&&(i=e.defaultView),void 0===n)return i?i[r]:e[t];i?i.scrollTo(a?i.pageXOffset:n,a?n:i.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=Xe(y.pixelPosition,function(e,t){if(t)return t=Ze(e,n),ze.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(o,s){k.each({padding:"inner"+o,content:s,"":"outer"+o},function(i,a){k.fn[a]=function(e,t){var n=arguments.length&&(i||"boolean"!=typeof e),r=i||(!0===e||!0===t?"margin":"border");return W(this,function(e,t,n){var i;return m(e)?0===a.indexOf("outer")?e["inner"+o]:e.document.documentElement["client"+o]:9===e.nodeType?(i=e.documentElement,Math.max(e.body["scroll"+o],i["scroll"+o],e.body["offset"+o],i["offset"+o],i["client"+o])):void 0===n?k.css(e,t,r):k.style(e,t,n,r)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0").attr("name",i.submitButton.name).val(c(i.submitButton).val()).appendTo(i.currentForm)),!(i.settings.submitHandler&&!i.settings.debug)||(t=i.settings.submitHandler.call(i,i.currentForm,n),e&&e.remove(),void 0!==t&&t)}return i.settings.debug&&n.preventDefault(),i.cancelSubmit?(i.cancelSubmit=!1,e()):i.form()?i.pendingRequest?!(i.formSubmitted=!0):e():(i.focusInvalid(),!1)})),i)}e&&e.debug&&window.console&&console.warn("Nothing selected, can't validate, returning nothing.")},valid:function(){var e,t,n;return c(this[0]).is("form")?e=this.validate().form():(n=[],e=!0,t=c(this[0].form).validate(),this.each(function(){(e=t.element(this)&&e)||(n=n.concat(t.errorList))}),t.errorList=n),e},rules:function(e,t){var n,i,r,a,o,s,l=this[0],u=void 0!==this.attr("contenteditable")&&"false"!==this.attr("contenteditable");if(null!=l&&(!l.form&&u&&(l.form=this.closest("form")[0],l.name=this.attr("name")),null!=l.form)){if(e)switch(i=(n=c.data(l.form,"validator").settings).rules,r=c.validator.staticRules(l),e){case"add":c.extend(r,c.validator.normalizeRule(t)),delete r.messages,i[l.name]=r,t.messages&&(n.messages[l.name]=c.extend(n.messages[l.name],t.messages));break;case"remove":return t?(s={},c.each(t.split(/\s/),function(e,t){s[t]=r[t],delete r[t]}),s):(delete i[l.name],r)}return(a=c.validator.normalizeRules(c.extend({},c.validator.classRules(l),c.validator.attributeRules(l),c.validator.dataRules(l),c.validator.staticRules(l)),l)).required&&(o=a.required,delete a.required,a=c.extend({required:o},a)),a.remote&&(o=a.remote,delete a.remote,a=c.extend(a,{remote:o})),a}}}),c.extend(c.expr.pseudos||c.expr[":"],{blank:function(e){return!c.trim(""+c(e).val())},filled:function(e){var t=c(e).val();return null!==t&&!!c.trim(""+t)},unchecked:function(e){return!c(e).prop("checked")}}),c.validator=function(e,t){this.settings=c.extend(!0,{},c.validator.defaults,e),this.currentForm=t,this.init()},c.validator.format=function(n,e){return 1===arguments.length?function(){var e=c.makeArray(arguments);return e.unshift(n),c.validator.format.apply(this,e)}:(void 0===e||(2Warning: No message defined for "+e.name+""),i=/\$?\{(\d+)\}/g;return"function"==typeof n?n=n.call(this,t.parameters,e):i.test(n)&&(n=c.validator.format(n.replace(i,"{$1}"),t.parameters)),n},formatAndAdd:function(e,t){var n=this.defaultMessage(e,t);this.errorList.push({message:n,element:e,method:t.method}),this.errorMap[e.name]=n,this.submitted[e.name]=n},addWrapper:function(e){return this.settings.wrapper&&(e=e.add(e.parent(this.settings.wrapper))),e},defaultShowErrors:function(){var e,t,n;for(e=0;this.errorList[e];e++)n=this.errorList[e],this.settings.highlight&&this.settings.highlight.call(this,n.element,this.settings.errorClass,this.settings.validClass),this.showLabel(n.element,n.message);if(this.errorList.length&&(this.toShow=this.toShow.add(this.containers)),this.settings.success)for(e=0;this.successList[e];e++)this.showLabel(this.successList[e]);if(this.settings.unhighlight)for(e=0,t=this.validElements();t[e];e++)this.settings.unhighlight.call(this,t[e],this.settings.errorClass,this.settings.validClass);this.toHide=this.toHide.not(this.toShow),this.hideErrors(),this.addWrapper(this.toShow).show()},validElements:function(){return this.currentElements.not(this.invalidElements())},invalidElements:function(){return c(this.errorList).map(function(){return this.element})},showLabel:function(e,t){var n,i,r,a,o=this.errorsFor(e),s=this.idOrName(e),l=c(e).attr("aria-describedby");o.length?(o.removeClass(this.settings.validClass).addClass(this.settings.errorClass),o.html(t)):(n=o=c("<"+this.settings.errorElement+">").attr("id",s+"-error").addClass(this.settings.errorClass).html(t||""),this.settings.wrapper&&(n=o.hide().show().wrap("<"+this.settings.wrapper+"/>").parent()),this.labelContainer.length?this.labelContainer.append(n):this.settings.errorPlacement?this.settings.errorPlacement.call(this,n,c(e)):n.insertAfter(e),o.is("label")?o.attr("for",s):0===o.parents("label[for='"+this.escapeCssMeta(s)+"']").length&&(r=o.attr("id"),l?l.match(new RegExp("\\b"+this.escapeCssMeta(r)+"\\b"))||(l+=" "+r):l=r,c(e).attr("aria-describedby",l),(i=this.groups[e.name])&&(a=this,c.each(a.groups,function(e,t){t===i&&c("[name='"+a.escapeCssMeta(e)+"']",a.currentForm).attr("aria-describedby",o.attr("id"))})))),!t&&this.settings.success&&(o.text(""),"string"==typeof this.settings.success?o.addClass(this.settings.success):this.settings.success(o,e)),this.toShow=this.toShow.add(o)},errorsFor:function(e){var t=this.escapeCssMeta(this.idOrName(e)),n=c(e).attr("aria-describedby"),i="label[for='"+t+"'], label[for='"+t+"'] *";return n&&(i=i+", #"+this.escapeCssMeta(n).replace(/\s+/g,", #")),this.errors().filter(i)},escapeCssMeta:function(e){return e.replace(/([\\!"#$%&'()*+,./:;<=>?@\[\]^`{|}~])/g,"\\$1")},idOrName:function(e){return this.groups[e.name]||(this.checkable(e)?e.name:e.id||e.name)},validationTargetFor:function(e){return this.checkable(e)&&(e=this.findByName(e.name)),c(e).not(this.settings.ignore)[0]},checkable:function(e){return/radio|checkbox/i.test(e.type)},findByName:function(e){return c(this.currentForm).find("[name='"+this.escapeCssMeta(e)+"']")},getLength:function(e,t){switch(t.nodeName.toLowerCase()){case"select":return c("option:selected",t).length;case"input":if(this.checkable(t))return this.findByName(t.name).filter(":checked").length}return e.length},depend:function(e,t){return!this.dependTypes[typeof e]||this.dependTypes[typeof e](e,t)},dependTypes:{boolean:function(e){return e},string:function(e,t){return!!c(e,t.form).length},function:function(e,t){return e(t)}},optional:function(e){var t=this.elementValue(e);return!c.validator.methods.required.call(this,t,e)&&"dependency-mismatch"},startRequest:function(e){this.pending[e.name]||(this.pendingRequest++,c(e).addClass(this.settings.pendingClass),this.pending[e.name]=!0)},stopRequest:function(e,t){this.pendingRequest--,this.pendingRequest<0&&(this.pendingRequest=0),delete this.pending[e.name],c(e).removeClass(this.settings.pendingClass),t&&0===this.pendingRequest&&this.formSubmitted&&this.form()?(c(this.currentForm).submit(),this.submitButton&&c("input:hidden[name='"+this.submitButton.name+"']",this.currentForm).remove(),this.formSubmitted=!1):!t&&0===this.pendingRequest&&this.formSubmitted&&(c(this.currentForm).triggerHandler("invalid-form",[this]),this.formSubmitted=!1)},previousValue:function(e,t){return t="string"==typeof t&&t||"remote",c.data(e,"previousValue")||c.data(e,"previousValue",{old:null,valid:!0,message:this.defaultMessage(e,{method:t})})},destroy:function(){this.resetForm(),c(this.currentForm).off(".validate").removeData("validator").find(".validate-equalTo-blur").off(".validate-equalTo").removeClass("validate-equalTo-blur").find(".validate-lessThan-blur").off(".validate-lessThan").removeClass("validate-lessThan-blur").find(".validate-lessThanEqual-blur").off(".validate-lessThanEqual").removeClass("validate-lessThanEqual-blur").find(".validate-greaterThanEqual-blur").off(".validate-greaterThanEqual").removeClass("validate-greaterThanEqual-blur").find(".validate-greaterThan-blur").off(".validate-greaterThan").removeClass("validate-greaterThan-blur")}},classRuleSettings:{required:{required:!0},email:{email:!0},url:{url:!0},date:{date:!0},dateISO:{dateISO:!0},number:{number:!0},digits:{digits:!0},creditcard:{creditcard:!0}},addClassRules:function(e,t){e.constructor===String?this.classRuleSettings[e]=t:c.extend(this.classRuleSettings,e)},classRules:function(e){var t={},n=c(e).attr("class");return n&&c.each(n.split(" "),function(){this in c.validator.classRuleSettings&&c.extend(t,c.validator.classRuleSettings[this])}),t},normalizeAttributeRule:function(e,t,n,i){/min|max|step/.test(n)&&(null===t||/number|range|text/.test(t))&&(i=Number(i),isNaN(i)&&(i=void 0)),i||0===i?e[n]=i:t===n&&"range"!==t&&(e[n]=!0)},attributeRules:function(e){var t,n,i={},r=c(e),a=e.getAttribute("type");for(t in c.validator.methods)n="required"===t?(""===(n=e.getAttribute(t))&&(n=!0),!!n):r.attr(t),this.normalizeAttributeRule(i,a,t,n);return i.maxlength&&/-1|2147483647|524288/.test(i.maxlength)&&delete i.maxlength,i},dataRules:function(e){var t,n,i={},r=c(e),a=e.getAttribute("type");for(t in c.validator.methods)""===(n=r.data("rule"+t.charAt(0).toUpperCase()+t.substring(1).toLowerCase()))&&(n=!0),this.normalizeAttributeRule(i,a,t,n);return i},staticRules:function(e){var t={},n=c.data(e.form,"validator");return n.settings.rules&&(t=c.validator.normalizeRule(n.settings.rules[e.name])||{}),t},normalizeRules:function(i,r){return c.each(i,function(e,t){if(!1!==t){if(t.param||t.depends){var n=!0;switch(typeof t.depends){case"string":n=!!c(t.depends,r.form).length;break;case"function":n=t.depends.call(r,r)}n?i[e]=void 0===t.param||t.param:(c.data(r.form,"validator").resetElements(c(r)),delete i[e])}}else delete i[e]}),c.each(i,function(e,t){i[e]=c.isFunction(t)&&"normalizer"!==e?t(r):t}),c.each(["minlength","maxlength"],function(){i[this]&&(i[this]=Number(i[this]))}),c.each(["rangelength","range"],function(){var e;i[this]&&(c.isArray(i[this])?i[this]=[Number(i[this][0]),Number(i[this][1])]:"string"==typeof i[this]&&(e=i[this].replace(/[\[\]]/g,"").split(/[\s,]+/),i[this]=[Number(e[0]),Number(e[1])]))}),c.validator.autoCreateRanges&&(null!=i.min&&null!=i.max&&(i.range=[i.min,i.max],delete i.min,delete i.max),null!=i.minlength&&null!=i.maxlength&&(i.rangelength=[i.minlength,i.maxlength],delete i.minlength,delete i.maxlength)),i},normalizeRule:function(e){if("string"==typeof e){var t={};c.each(e.split(/\s/),function(){t[this]=!0}),e=t}return e},addMethod:function(e,t,n){c.validator.methods[e]=t,c.validator.messages[e]=void 0!==n?n:c.validator.messages[e],t.length<3&&c.validator.addClassRules(e,c.validator.normalizeRule(e))},methods:{required:function(e,t,n){if(!this.depend(n,t))return"dependency-mismatch";if("select"!==t.nodeName.toLowerCase())return this.checkable(t)?0=n[0]&&i<=n[1]},min:function(e,t,n){return this.optional(t)||n<=e},max:function(e,t,n){return this.optional(t)||e<=n},range:function(e,t,n){return this.optional(t)||e>=n[0]&&e<=n[1]},step:function(e,t,n){function i(e){var t=(""+e).match(/(?:\.(\d+))?$/);return t&&t[1]?t[1].length:0}function r(e){return Math.round(e*Math.pow(10,a))}var a,o=c(t).attr("type"),s="Step attribute on input type "+o+" is not supported.",l=new RegExp("\\b"+o+"\\b"),u=!0;if(o&&!l.test(["text","number","range"].join()))throw new Error(s);return a=i(n),(i(e)>a||r(e)%r(n)!=0)&&(u=!1),this.optional(t)||u},equalTo:function(e,t,n){var i=c(n);return this.settings.onfocusout&&i.not(".validate-equalTo-blur").length&&i.addClass("validate-equalTo-blur").on("blur.validate-equalTo",function(){c(t).valid()}),e===i.val()},remote:function(a,o,e,s){if(this.optional(o))return"dependency-mismatch";s="string"==typeof s&&s||"remote";var l,t,n,u=this.previousValue(o,s);return this.settings.messages[o.name]||(this.settings.messages[o.name]={}),u.originalMessage=u.originalMessage||this.settings.messages[o.name][s],this.settings.messages[o.name][s]=u.message,e="string"==typeof e&&{url:e}||e,n=c.param(c.extend({data:a},e.data)),u.old===n?u.valid:(u.old=n,(l=this).startRequest(o),(t={})[o.name]=a,c.ajax(c.extend(!0,{mode:"abort",port:"validate"+o.name,dataType:"json",data:t,context:l.currentForm,success:function(e){var t,n,i,r=!0===e||"true"===e;l.settings.messages[o.name][s]=u.originalMessage,r?(i=l.formSubmitted,l.resetInternals(),l.toHide=l.errorsFor(o),l.formSubmitted=i,l.successList.push(o),l.invalid[o.name]=!1,l.showErrors()):(t={},n=e||l.defaultMessage(o,{method:s,parameters:a}),t[o.name]=u.message=n,l.invalid[o.name]=!0,l.showErrors(t)),u.valid=r,l.stopRequest(o,r)}},e)),"pending")}}});var i,r={};return c.ajaxPrefilter?c.ajaxPrefilter(function(e,t,n){var i=e.port;"abort"===e.mode&&(r[i]&&r[i].abort(),r[i]=n)}):(i=c.ajax,c.ajax=function(e){var t=("mode"in e?e:c.ajaxSettings).mode,n=("port"in e?e:c.ajaxSettings).port;return"abort"===t?(r[n]&&r[n].abort(),r[n]=i.apply(this,arguments),r[n]):i.apply(this,arguments)}),c}),function(e){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],e):"object"==typeof module&&module.exports?module.exports=e(require("jquery-validation")):jQuery.validator.unobtrusive=e(jQuery)}(function(l){var e,o=l.validator,s="unobtrusiveValidation";function u(e,t,n){e.rules[t]=n,e.message&&(e.messages[t]=e.message)}function c(e){return e.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function d(e){return e.substr(0,e.lastIndexOf(".")+1)}function h(e,t){return 0===e.indexOf("*.")&&(e=e.replace("*.",t)),e}function f(e){var t=l(this),n="__jquery_unobtrusive_validation_form_reset";if(!t.data(n)){t.data(n,!0);try{t.data("validator").resetForm()}finally{t.removeData(n)}t.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),t.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function p(i){function e(e,t){var n=a[e];n&&l.isFunction(n)&&n.apply(i,t)}var t=l(i),n=t.data(s),r=l.proxy(f,i),a=o.unobtrusive.options||{};return n||(n={options:{errorClass:a.errorClass||"input-validation-error",errorElement:a.errorElement||"span",errorPlacement:function(){(function(e,t){var n=l(this).find("[data-valmsg-for='"+c(t[0].name)+"']"),i=n.attr("data-valmsg-replace"),r=i?!1!==l.parseJSON(i):null;n.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",n),r?(n.empty(),e.removeClass("input-validation-error").appendTo(n)):e.hide()}).apply(i,arguments),e("errorPlacement",arguments)},invalidHandler:function(){(function(e,t){var n=l(this).find("[data-valmsg-summary=true]"),i=n.find("ul");i&&i.length&&t.errorList.length&&(i.empty(),n.addClass("validation-summary-errors").removeClass("validation-summary-valid"),l.each(t.errorList,function(){l("
  • ").html(this.message).appendTo(i)}))}).apply(i,arguments),e("invalidHandler",arguments)},messages:{},rules:{},success:function(){(function(e){var t=e.data("unobtrusiveContainer");if(t){var n=t.attr("data-valmsg-replace"),i=n?l.parseJSON(n):null;t.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),i&&t.empty()}}).apply(i,arguments),e("success",arguments)}},attachValidation:function(){t.off("reset."+s,r).on("reset."+s,r).validate(this.options)},validate:function(){return t.validate(),t.valid()}},t.data(s,n)),n}return o.unobtrusive={adapters:[],parseElement:function(i,e){var t,r,a,o=l(i),s=o.parents("form")[0];s&&((t=p(s)).options.rules[i.name]=r={},t.options.messages[i.name]=a={},l.each(this.adapters,function(){var e="data-val-"+this.name,t=o.attr(e),n={};void 0!==t&&(e+="-",l.each(this.params,function(){n[this]=o.attr(e+this)}),this.adapt({element:i,form:s,message:t,params:n,rules:r,messages:a}))}),l.extend(r,{__dummy__:!0}),e||t.attachValidation())},parse:function(e){var t=l(e),n=t.parents().addBack().filter("form").add(t.find("form")).has("[data-val=true]");t.find("[data-val=true]").each(function(){o.unobtrusive.parseElement(this,!0)}),n.each(function(){var e=p(this);e&&e.attachValidation()})}},(e=o.unobtrusive.adapters).add=function(e,t,n){return n||(n=t,t=[]),this.push({name:e,params:t,adapt:n}),this},e.addBool=function(t,n){return this.add(t,function(e){u(e,n||t,!0)})},e.addMinMax=function(e,i,r,a,t,n){return this.add(e,[t||"min",n||"max"],function(e){var t=e.params.min,n=e.params.max;t&&n?u(e,a,[t,n]):t?u(e,i,t):n&&u(e,r,n)})},e.addSingleVal=function(t,n,i){return this.add(t,[n||"val"],function(e){u(e,i||t,e.params[n])})},o.addMethod("__dummy__",function(e,t,n){return!0}),o.addMethod("regex",function(e,t,n){var i;return!!this.optional(t)||(i=new RegExp(n).exec(e))&&0===i.index&&i[0].length===e.length}),o.addMethod("nonalphamin",function(e,t,n){var i;return n&&(i=(i=e.match(/\W/g))&&i.length>=n),i}),o.methods.extension?(e.addSingleVal("accept","mimtype"),e.addSingleVal("extension","extension")):e.addSingleVal("extension","extension","accept"),e.addSingleVal("regex","pattern"),e.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),e.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),e.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),e.add("equalto",["other"],function(e){var t=d(e.element.name),n=h(e.params.other,t);u(e,"equalTo",l(e.form).find(":input").filter("[name='"+c(n)+"']")[0])}),e.add("required",function(e){"INPUT"===e.element.tagName.toUpperCase()&&"CHECKBOX"===e.element.type.toUpperCase()||u(e,"required",!0)}),e.add("remote",["url","type","additionalfields"],function(i){var r={url:i.params.url,type:i.params.type||"GET",data:{}},a=d(i.element.name);l.each(function(e){return e.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}(i.params.additionalfields||i.element.name),function(e,t){var n=h(t,a);r.data[n]=function(){var e=l(i.form).find(":input").filter("[name='"+c(n)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),u(i,"remote",r)}),e.add("password",["min","nonalphamin","regex"],function(e){e.params.min&&u(e,"minlength",e.params.min),e.params.nonalphamin&&u(e,"nonalphamin",e.params.nonalphamin),e.params.regex&&u(e,"regex",e.params.regex)}),e.add("fileextensions",["extensions"],function(e){u(e,"extension",e.params.extensions)}),l(function(){o.unobtrusive.parse(document)}),o.unobtrusive}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Popper=t()}(this,function(){"use strict";for(var e=["native code","[object MutationObserverConstructor]"],t="undefined"!=typeof window,n=["Edge","Trident","Firefox"],i=0,r=0;rr[e]&&!i.escapeWithReference&&(n=Math.min(a[t],r[e]-("right"===e?a.width:a.height))),w({},t,n)}};return n.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";a=D({},a,o[t](e))}),e.offsets.popper=a,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=x(e.offsets.popper),n=e.offsets.reference,i=e.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(i),o=a?"right":"bottom",s=a?"left":"top",l=a?"width":"height";return t[o]r(n[o])&&(e.offsets.popper[s]=r(n[o])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){if(!Y(e.instance.modifiers,"arrow","keepTogether"))return e;var n=t.element;if("string"==typeof n){if(!(n=e.instance.popper.querySelector(n)))return e}else if(!e.instance.popper.contains(n))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var i=e.placement.split("-")[0],r=x(e.offsets.popper),a=e.offsets.reference,o=-1!==["left","right"].indexOf(i),s=o?"height":"width",l=o?"top":"left",u=o?"left":"top",c=o?"bottom":"right",d=M(n)[s];a[c]-dr[c]&&(e.offsets.popper[l]+=a[l]+d-r[c]);var h=a[l]+a[s]/2-d/2-x(e.offsets.popper)[l];return h=Math.max(Math.min(r[s]-d,h),0),e.arrowElement=n,e.offsets.arrow={},e.offsets.arrow[l]=h,e.offsets.arrow[u]="",e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(f,p){if(L(f.instance.modifiers,"inner"))return f;if(f.flipped&&f.placement===f.originalPlacement)return f;var m=H(f.instance.popper,f.instance.reference,p.padding,p.boundariesElement),g=f.placement.split("-")[0],v=A(g),y=f.placement.split("-")[1]||"",b=[];switch(p.behavior){case W:b=[g,v];break;case q:b=U(g);break;case B:b=U(g,!0);break;default:b=p.behavior}return b.forEach(function(e,t){if(g!==e||b.length===t+1)return f;g=f.placement.split("-")[0],v=A(g);var n=x(f.offsets.popper),i=f.offsets.reference,r=Math.floor,a="left"===g&&r(n.right)>r(i.left)||"right"===g&&r(n.left)r(i.top)||"bottom"===g&&r(n.top)r(m.right),l=r(n.top)r(m.bottom),c="left"===g&&o||"right"===g&&s||"top"===g&&l||"bottom"===g&&u,d=-1!==["top","bottom"].indexOf(g),h=!!p.flipVariations&&(d&&"start"===y&&o||d&&"end"===y&&s||!d&&"start"===y&&l||!d&&"end"===y&&u);(a||c||h)&&(f.flipped=!0,(a||c)&&(g=b[t+1]),h&&(y=function(e){return"end"===e?"start":"start"===e?"end":e}(y)),f.placement=g+(y?"-"+y:""),f.offsets.popper=O(f.instance.state.position,f.instance.popper,f.offsets.reference,f.placement),f=P(f.instance.modifiers,f,"flip"))}),f},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=x(e.offsets.popper),r=x(e.offsets.reference),a=-1!==["left","right"].indexOf(n),o=-1===["top","left"].indexOf(n);return i[a?"left":"top"]=r[t]-(o?i[a?"width":"height"]:0),e.placement=A(t),e.offsets.popper=x(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!Y(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=j(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightthis._items.length-1||e<0))if(this._isSliding)p(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
    ',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:_t},jt="show",Pt="out",Lt={HIDE:"hide"+Tt,HIDDEN:"hidden"+Tt,SHOW:"show"+Tt,SHOWN:"shown"+Tt,INSERTED:"inserted"+Tt,CLICK:"click"+Tt,FOCUSIN:"focusin"+Tt,FOCUSOUT:"focusout"+Tt,MOUSEENTER:"mouseenter"+Tt,MOUSELEAVE:"mouseleave"+Tt},Ht="fade",Ft="show",Rt=".tooltip-inner",Yt=".arrow",Vt="hover",Ut="focus",Wt="click",qt="manual",Bt=function(){function i(e,t){if(void 0===d)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=p(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(p(this.getTipElement()).hasClass(Ft))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),p.removeData(this.element,this.constructor.DATA_KEY),p(this.element).off(this.constructor.EVENT_KEY),p(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&p(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===p(this.element).css("display"))throw new Error("Please use show on visible elements");var e=p.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){p(this.element).trigger(e);var n=m.findShadowRoot(this.element),i=p.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var r=this.getTipElement(),a=m.getUID(this.constructor.NAME);r.setAttribute("id",a),this.element.setAttribute("aria-describedby",a),this.setContent(),this.config.animation&&p(r).addClass(Ht);var o="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var l=this._getContainer();p(r).data(this.constructor.DATA_KEY,this),p.contains(this.element.ownerDocument.documentElement,this.tip)||p(r).appendTo(l),p(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new d(this.element,r,{placement:s,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Yt},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){return t._handlePopperPlacementChange(e)}}),p(r).addClass(Ft),"ontouchstart"in document.documentElement&&p(document.body).children().on("mouseover",null,p.noop);var u=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,p(t.element).trigger(t.constructor.Event.SHOWN),e===Pt&&t._leave(null,t)};if(p(this.tip).hasClass(Ht)){var c=m.getTransitionDurationFromElement(this.tip);p(this.tip).one(m.TRANSITION_END,u).emulateTransitionEnd(c)}else u()}},e.hide=function(e){function t(){n._hoverState!==jt&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),p(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),e&&e()}var n=this,i=this.getTipElement(),r=p.Event(this.constructor.Event.HIDE);if(p(this.element).trigger(r),!r.isDefaultPrevented()){if(p(i).removeClass(Ft),"ontouchstart"in document.documentElement&&p(document.body).children().off("mouseover",null,p.noop),this._activeTrigger[Wt]=!1,this._activeTrigger[Ut]=!1,this._activeTrigger[Vt]=!1,p(this.tip).hasClass(Ht)){var a=m.getTransitionDurationFromElement(i);p(i).one(m.TRANSITION_END,t).emulateTransitionEnd(a)}else t();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){p(this.getTipElement()).addClass(Et+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(p(e.querySelectorAll(Rt)),this.getTitle()),p(e).removeClass(Ht+" "+Ft)},e.setElementContent=function(e,t){"object"!=typeof t||!t.nodeType&&!t.jquery?this.config.html?(this.config.sanitize&&(t=xt(t,this.config.whiteList,this.config.sanitizeFn)),e.html(t)):e.text(t):this.config.html?p(t).parent().is(e)||e.empty().append(t):e.text(p(t).text())},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e=e||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:m.isElement(this.config.container)?p(this.config.container):p(document).find(this.config.container)},e._getAttachment=function(e){return Nt[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)p(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==qt){var t=e===Vt?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Vt?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;p(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}}),p(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==e||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Ut:Vt]=!0),p(t.getTipElement()).hasClass(Ft)||t._hoverState===jt?t._hoverState=jt:(clearTimeout(t._timeout),t._hoverState=jt,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===jt&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||p(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),p(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Ut:Vt]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Pt,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Pt&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){var t=p(this.element).data();return Object.keys(t).forEach(function(e){-1!==At.indexOf(e)&&delete t[e]}),"number"==typeof(e=l({},this.constructor.Default,t,"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),m.typeCheckConfig(Ct,e,this.constructor.DefaultType),e.sanitize&&(e.template=xt(e.template,e.whiteList,e.sanitizeFn)),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(Mt);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(p(e).removeClass(Ht),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=p(this).data(kt),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),p(this).data(kt,e)),"string"==typeof n)){if(void 0===e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},o(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return It}},{key:"NAME",get:function(){return Ct}},{key:"DATA_KEY",get:function(){return kt}},{key:"Event",get:function(){return Lt}},{key:"EVENT_KEY",get:function(){return Tt}},{key:"DefaultType",get:function(){return Ot}}]),i}();p.fn[Ct]=Bt._jQueryInterface,p.fn[Ct].Constructor=Bt,p.fn[Ct].noConflict=function(){return p.fn[Ct]=St,Bt._jQueryInterface};var $t="popover",zt="bs.popover",Gt="."+zt,Kt=p.fn[$t],Qt="bs-popover",Jt=new RegExp("(^|\\s)"+Qt+"\\S+","g"),Zt=l({},Bt.Default,{placement:"right",trigger:"click",content:"",template:''}),Xt=l({},Bt.DefaultType,{content:"(string|element|function)"}),en="fade",tn="show",nn=".popover-header",rn=".popover-body",an={HIDE:"hide"+Gt,HIDDEN:"hidden"+Gt,SHOW:"show"+Gt,SHOWN:"shown"+Gt,INSERTED:"inserted"+Gt,CLICK:"click"+Gt,FOCUSIN:"focusin"+Gt,FOCUSOUT:"focusout"+Gt,MOUSEENTER:"mouseenter"+Gt,MOUSELEAVE:"mouseleave"+Gt},on=function(e){function i(){return e.apply(this,arguments)||this}!function(e,t){e.prototype=Object.create(t.prototype),(e.prototype.constructor=e).__proto__=t}(i,e);var t=i.prototype;return t.isWithContent=function(){return this.getTitle()||this._getContent()},t.addAttachmentClass=function(e){p(this.getTipElement()).addClass(Qt+"-"+e)},t.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},t.setContent=function(){var e=p(this.getTipElement());this.setElementContent(e.find(nn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find(rn),t),e.removeClass(en+" "+tn)},t._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},t._cleanTipClass=function(){var e=p(this.getTipElement()),t=e.attr("class").match(Jt);null!==t&&0=this._offsets[r]&&(void 0===this._offsets[r+1]||e>>0;if("function"!=typeof e)throw TypeError();var i,r=arguments[1];for(i=0;i>16&255)),n.push(String.fromCharCode(i>>8&255)),n.push(String.fromCharCode(255&i)),i=r=0),t+=1;return 12===r?(i>>=4,n.push(String.fromCharCode(255&i))):18===r&&(i>>=2,n.push(String.fromCharCode(i>>8&255)),n.push(String.fromCharCode(255&i))),n.join("")},e.btoa=e.btoa||function(e){e=String(e);var t,n,i,r,a,o,s,l=0,u=[];if(/[^\x00-\xFF]/.test(e))throw Error("InvalidCharacterError");for(;l>2,a=(3&t)<<4|(n=e.charCodeAt(l++))>>4,o=(15&n)<<2|(i=e.charCodeAt(l++))>>6,s=63&i,l===e.length+2?s=o=64:l===e.length+1&&(s=64),u.push(c.charAt(r),c.charAt(a),c.charAt(o),c.charAt(s));return u.join("")},Object.prototype.hasOwnProperty||(Object.prototype.hasOwnProperty=function(e){var t=this.__proto__||this.constructor.prototype;return e in this&&(!(e in t)||t[e]!==this[e])}),function(){if("performance"in r==!1&&(r.performance={}),Date.now=Date.now||function(){return(new Date).getTime()},"now"in r.performance==!1){var e=Date.now();performance.timing&&performance.timing.navigationStart&&(e=performance.timing.navigationStart),r.performance.now=function(){return Date.now()-e}}}(),r.requestAnimationFrame||(r.webkitRequestAnimationFrame&&r.webkitCancelAnimationFrame?((i=r).requestAnimationFrame=function(e){return webkitRequestAnimationFrame(function(){e(i.performance.now())})},i.cancelAnimationFrame=i.webkitCancelAnimationFrame):r.mozRequestAnimationFrame&&r.mozCancelAnimationFrame?((n=r).requestAnimationFrame=function(e){return mozRequestAnimationFrame(function(){e(n.performance.now())})},n.cancelAnimationFrame=n.mozCancelAnimationFrame):((t=r).requestAnimationFrame=function(e){return t.setTimeout(e,1e3/60)},t.cancelAnimationFrame=t.clearTimeout))}}(this),function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.Holder=t():e.Holder=t()}(this,function(){return r={},n.m=i=[function(e,t,n){e.exports=n(1)},function(s,e,O){(function(u){var e=O(2),h=O(3),k=O(6),g=O(7),v=O(8),y=O(9),T=O(10),t=O(11),c=O(12),d=O(15),m=g.extend,b=g.dimensionCheck,_=t.svg_ns,i={version:t.version,addTheme:function(e,t){return null!=e&&null!=t&&(S.settings.themes[e]=t),delete S.vars.cache.themeKeys,this},addImage:function(i,e){return y.getNodeArray(e).forEach(function(e){var t=y.newEl("img"),n={};n[S.setup.dataAttr]=i,y.setAttr(t,n),e.appendChild(t)}),this},setResizeUpdate:function(e,t){e.holderData&&(e.holderData.resizeUpdate=!!t,e.holderData.resizeUpdate&&D(e))},run:function(e){e=e||{};var c={},d=m(S.settings,e);S.vars.preempted=!0,S.vars.dataAttr=d.dataAttr||S.setup.dataAttr,c.renderer=d.renderer?d.renderer:S.setup.renderer,-1===S.setup.renderers.join(",").indexOf(c.renderer)&&(c.renderer=S.setup.supportsSVG?"svg":S.setup.supportsCanvas?"canvas":"html");var t=y.getNodeArray(d.images),n=y.getNodeArray(d.bgnodes),i=y.getNodeArray(d.stylenodes),r=y.getNodeArray(d.objects);return c.stylesheets=[],c.svgXMLStylesheet=!0,c.noFontFallback=!!d.noFontFallback,c.noBackgroundSize=!!d.noBackgroundSize,i.forEach(function(e){if(e.attributes.rel&&e.attributes.href&&"stylesheet"==e.attributes.rel.value){var t=e.attributes.href.value,n=y.newEl("a");n.href=t;var i=n.protocol+"//"+n.host+n.pathname+n.search;c.stylesheets.push(i)}}),n.forEach(function(e){if(u.getComputedStyle){var t=u.getComputedStyle(e,null).getPropertyValue("background-image"),n=e.getAttribute("data-background-src")||t,i=null,r=d.domain+"/",a=n.indexOf(r);if(0===a)i=n;else if(1===a&&"?"===n[0])i=n.slice(1);else{var o=n.substr(a).match(/([^\"]*)"?\)/);if(null!==o)i=o[1];else if(0===n.indexOf("url("))throw"Holder: unable to parse background URL: "+n}if(i){var s=l(i,d);s&&p({mode:"background",el:e,flags:s,engineSettings:c})}}}),r.forEach(function(e){var t={};try{t.data=e.getAttribute("data"),t.dataSrc=e.getAttribute(S.vars.dataAttr)}catch(e){}var n=null!=t.data&&0===t.data.indexOf(d.domain),i=null!=t.dataSrc&&0===t.dataSrc.indexOf(d.domain);n?f(d,c,t.data,e):i&&f(d,c,t.dataSrc,e)}),t.forEach(function(e){var t={};try{t.src=e.getAttribute("src"),t.dataSrc=e.getAttribute(S.vars.dataAttr),t.rendered=e.getAttribute("data-holder-rendered")}catch(e){}var n,i,r,a,o,s=null!=t.src,l=null!=t.dataSrc&&0===t.dataSrc.indexOf(d.domain),u=null!=t.rendered&&"true"==t.rendered;s?0===t.src.indexOf(d.domain)?f(d,c,t.src,e):l&&(u?f(d,c,t.dataSrc,e):(n=t.src,i=d,r=c,a=t.dataSrc,o=e,g.imageExists(n,function(e){e||f(i,r,a,o)}))):l&&f(d,c,t.dataSrc,e)}),this}},S={settings:{domain:"holder.js",images:"img",objects:"object",bgnodes:"body .holderjs",stylenodes:"head link.holderjs",themes:{gray:{bg:"#EEEEEE",fg:"#AAAAAA"},social:{bg:"#3a5a97",fg:"#FFFFFF"},industrial:{bg:"#434A52",fg:"#C2F200"},sky:{bg:"#0D8FDB",fg:"#FFFFFF"},vine:{bg:"#39DBAC",fg:"#1E292C"},lava:{bg:"#F8591A",fg:"#1C2846"}}},defaults:{size:10,units:"pt",scale:1/16}};function f(e,t,n,i){var r=l(n.substr(n.lastIndexOf(e.domain)),e);r&&p({mode:null,el:i,flags:r,engineSettings:t})}function l(e,t){var n={theme:m(S.settings.themes.gray,null),stylesheets:t.stylesheets,instanceOptions:t},i=e.indexOf("?"),r=[e];-1!==i&&(r=[e.slice(0,i),e.slice(i+1)]);var a=r[0].split("/");n.holderURL=e;var o=a[1],s=o.match(/([\d]+p?)x([\d]+p?)/);if(!s)return!1;if(n.fluid=-1!==o.indexOf("p"),n.dimensions={width:s[1].replace("p","%"),height:s[2].replace("p","%")},2===r.length){var l=h.parse(r[1]);if(g.truthy(l.ratio)){n.fluid=!0;var u=parseFloat(n.dimensions.width.replace("%","")),c=parseFloat(n.dimensions.height.replace("%",""));c=Math.floor(c/u*100),u=100,n.dimensions.width=u+"%",n.dimensions.height=c+"%"}if(n.auto=g.truthy(l.auto),l.bg&&(n.theme.bg=g.parseColor(l.bg)),l.fg&&(n.theme.fg=g.parseColor(l.fg)),l.bg&&!l.fg&&(n.autoFg=!0),l.theme&&n.instanceOptions.themes.hasOwnProperty(l.theme)&&(n.theme=m(n.instanceOptions.themes[l.theme],null)),l.text&&(n.text=l.text),l.textmode&&(n.textmode=l.textmode),l.size&&parseFloat(l.size)&&(n.size=parseFloat(l.size)),l.font&&(n.font=l.font),l.align&&(n.align=l.align),l.lineWrap&&(n.lineWrap=l.lineWrap),n.nowrap=g.truthy(l.nowrap),n.outline=g.truthy(l.outline),g.truthy(l.random)){S.vars.cache.themeKeys=S.vars.cache.themeKeys||Object.keys(n.instanceOptions.themes);var d=S.vars.cache.themeKeys[0|Math.random()*S.vars.cache.themeKeys.length];n.theme=m(n.instanceOptions.themes[d],null)}}return n}function p(e){var t=e.mode,n=e.el,i=e.flags,r=e.engineSettings,a=i.dimensions,o=i.theme,s=a.width+"x"+a.height;t=null==t?i.fluid?"fluid":"image":t;if(null!=i.text&&(o.text=i.text,"object"===n.nodeName.toLowerCase())){for(var l=o.text.split("\\n"),u=0;u=r||!0==C)&&(v(f,g,b,f.properties.leading),f.add(g),b=0,_+=f.properties.leading,w+=1,(g=new o.Group("line"+w)).y=_),!0!=C&&(m.moveTo(b,0),b+=p.spaceWidth+x.width,g.add(m))}if(v(f,g,b,f.properties.leading),f.add(g),"left"===e.align)f.moveTo(e.width-i,null,null);else if("right"===e.align){for(y in f.children)(g=f.children[y]).moveTo(e.width-g.width,null,null);f.moveTo(0-(e.width-i),null,null)}else{for(y in f.children)(g=f.children[y]).moveTo((f.width-g.width)/2,null,null);f.moveTo((e.width-f.width)/2,null,null)}f.moveTo(null,(e.height-f.height)/2,null),(e.height-f.height)/2<0&&f.moveTo(null,0,null)}else m=new o.Text(e.text),(g=new o.Group("line0")).add(m),f.add(g),"left"===e.align?f.moveTo(e.width-i,null,null):"right"===e.align?f.moveTo(0-(e.width-i),null,null):f.moveTo((e.width-p.boundingBox.width)/2,null,null),f.moveTo(null,(e.height-p.boundingBox.height)/2,null);return a}(o);function l(){var e=null;switch(a.renderer){case"canvas":e=d(s,t);break;case"svg":e=c(s,t);break;default:throw"Holder: invalid renderer: "+a.renderer}return e}if(null==(e=l()))throw"Holder: couldn't render placeholder";"background"==n?(i.style.backgroundImage="url("+e+")",a.noBackgroundSize||(i.style.backgroundSize=o.width+"px "+o.height+"px")):("img"===i.nodeName.toLowerCase()?y.setAttr(i,{src:e}):"object"===i.nodeName.toLowerCase()&&y.setAttr(i,{data:e,type:"image/svg+xml"}),a.reRender&&u.setTimeout(function(){var e=l();if(null==e)throw"Holder: couldn't render placeholder";"img"===i.nodeName.toLowerCase()?y.setAttr(i,{src:e}):"object"===i.nodeName.toLowerCase()&&y.setAttr(i,{data:e,type:"image/svg+xml"})},150)),y.setAttr(i,{"data-holder-rendered":!0})}function D(e){for(var t,n=0,i=(t=null==e||null==e.nodeType?S.vars.resizableImages:[e]).length;n","application/xml")},t.getNodeArray=function(e){var t=null;return"string"==typeof e?t=document.querySelectorAll(e):n.NodeList&&e instanceof n.NodeList?t=e:n.Node&&e instanceof n.Node?t=[e]:n.HTMLCollection&&e instanceof n.HTMLCollection?t=e:e instanceof Array?t=e:null===e&&(t=[]),t=Array.prototype.slice.call(t)}}).call(t,function(){return this}())},function(e,t){function o(e,t){"string"==typeof e&&("#"===(this.original=e).charAt(0)&&(e=e.slice(1)),/[^a-f0-9]+/i.test(e)||(3===e.length&&(e=e.replace(/./g,"$&$&")),6===e.length&&(this.alpha=1,t&&t.alpha&&(this.alpha=t.alpha),this.set(parseInt(e,16)))))}o.rgb2hex=function(e,t,n){return[e,t,n].map(function(e){var t=(0|e).toString(16);return e<16&&(t="0"+t),t}).join("")},o.hsl2rgb=function(e,t,n){var i=e/60,r=(1-Math.abs(2*n-1))*t,a=r*(1-Math.abs(parseInt(i)%2-1)),o=n-r/2,s=0,l=0,u=0;return 0<=i&&i<1?(s=r,l=a):1<=i&&i<2?(s=a,l=r):2<=i&&i<3?(l=r,u=a):3<=i&&i<4?(l=a,u=r):4<=i&&i<5?(s=a,u=r):5<=i&&i<6&&(s=r,u=a),s+=o,l+=o,u+=o,[s=parseInt(255*s),l=parseInt(255*l),u=parseInt(255*u)]},o.prototype.set=function(e){this.raw=e;var t=(16711680&this.raw)>>16,n=(65280&this.raw)>>8,i=255&this.raw,r=.2126*t+.7152*n+.0722*i,a=-.09991*t-.33609*n+.436*i,o=.615*t-.55861*n-.05639*i;return this.rgb={r:t,g:n,b:i},this.yuv={y:r,u:a,v:o},this},o.prototype.lighten=function(e){var t=255*(Math.min(1,Math.max(0,Math.abs(e)))*(e<0?-1:1))|0,n=Math.min(255,Math.max(0,this.rgb.r+t)),i=Math.min(255,Math.max(0,this.rgb.g+t)),r=Math.min(255,Math.max(0,this.rgb.b+t)),a=o.rgb2hex(n,i,r);return new o(a)},o.prototype.toHex=function(e){return(e?"#":"")+this.raw.toString(16)},o.prototype.lighterThan=function(e){return e instanceof o||(e=new o(e)),this.yuv.y>e.yuv.y},o.prototype.blendAlpha=function(e){e instanceof o||(e=new o(e));var t=e,n=t.alpha*t.rgb.r+(1-t.alpha)*this.rgb.r,i=t.alpha*t.rgb.g+(1-t.alpha)*this.rgb.g,r=t.alpha*t.rgb.b+(1-t.alpha)*this.rgb.b;return new o(o.rgb2hex(n,i,r))},e.exports=o},function(e,t){e.exports={version:"2.9.6",svg_ns:"http://www.w3.org/2000/svg"}},function(e,t,n){var y=n(13),b=n(8),i=n(11),_=n(7),w=i.svg_ns,D=function(e){var t=e.tag,n=e.content||"";return delete e.tag,delete e.content,[t,n,e]};e.exports=function(e,t){var n=t.engineSettings.stylesheets.map(function(e){return''}).join("\n"),i="holder_"+Number(new Date).toString(16),r=e.root,o=r.children.holderTextGroup,a="#"+i+" text { "+function(e){return _.cssProps({fill:e.fill,"font-weight":e.font.weight,"font-family":e.font.family+", monospace","font-size":e.font.size+e.font.units})}(o.properties)+" } ";o.y+=.8*o.textPositionData.boundingBox.height;var s=[];Object.keys(o.children).forEach(function(e){var a=o.children[e];Object.keys(a.children).forEach(function(e){var t=a.children[e],n=o.x+a.x+t.x,i=o.y+a.y+t.y,r=D({tag:"text",content:t.properties.text,x:n,y:i});s.push(r)})});var l=D({tag:"g",content:s}),u=null;if(r.children.holderBg.properties.outline){var c=r.children.holderBg.properties.outline;u=D({tag:"path",d:function(e,t,n){var i=n/2;return["M",i,i,"H",e-i,"V",t-i,"H",i,"V",0,"M",0,i,"L",e,t-i,"M",0,t-i,"L",e,i].join(" ")}(r.children.holderBg.width,r.children.holderBg.height,c.width),"stroke-width":c.width,stroke:c.fill,fill:"none"})}var d=function(e,t){return D({tag:t,width:e.width,height:e.height,fill:e.properties.fill})}(r.children.holderBg,"rect"),h=[];h.push(d),c&&h.push(u),h.push(l);var f=D({tag:"g",id:i,content:h}),p=D({tag:"style",content:a,type:"text/css"}),m=D({tag:"defs",content:p}),g=D({tag:"svg",content:[m,f],width:r.properties.width,height:r.properties.height,xmlns:w,viewBox:[0,0,r.properties.width,r.properties.height].join(" "),preserveAspectRatio:"none"}),v=y(g);return/\&(x)?#[0-9A-Fa-f]/.test(v[0])&&(v[0]=v[0].replace(/&#/gm,"&#")),v=n+v[0],b.svgStringToDataURI(v,"background"===t.mode)}},function(e,t,n){n(14);e.exports=function e(t,n,i){"use strict";var r,a,o,s,l,u,c,d,h,f,p,m,g=1,v=!0;function y(e,t){if(null!==t&&!1!==t&&void 0!==t)return"string"!=typeof t&&"object"!=typeof t?String(t):t}if(i=i||{},"string"==typeof t[0])t[0]=(l=t[0],u=l.match(/^[\w-]+/),c={tag:u?u[0]:"div",attr:{},children:[]},d=l.match(/#([\w-]+)/),h=l.match(/\$([\w-]+)/),f=l.match(/\.[\w-]+/g),d&&(c.attr.id=d[1],i[d[1]]=c),h&&(i[h[1]]=c),f&&(c.attr.class=f.join(" ").replace(/\./g,"")),l.match(/&$/g)&&(v=!1),c);else{if(!Array.isArray(t[0]))throw new Error("First element of array must be a string, or an array and not "+JSON.stringify(t[0]));g=0}for(;g/g,">"))),t[0].children.push(t[g]);else if("number"==typeof t[g])t[0].children.push(t[g]);else if(Array.isArray(t[g])){if(Array.isArray(t[g][0])){if(t[g].reverse().forEach(function(e){t.splice(g+1,0,e)}),0!==g)continue;g++}e(t[g],n,i),t[g][0]&&t[0].children.push(t[g][0])}else if("function"==typeof t[g])o=t[g];else{if("object"!=typeof t[g])throw new TypeError('"'+t[g]+'" is not allowed as a value.');for(a in t[g])t[g].hasOwnProperty(a)&&null!==t[g][a]&&!1!==t[g][a]&&("style"===a&&"object"==typeof t[g][a]?t[0].attr[a]=JSON.stringify(t[g][a],y).slice(2,-2).replace(/","/g,";").replace(/":"/g,":").replace(/\\"/g,"'"):t[0].attr[a]=t[g][a])}}if(!1!==t[0]){for(s in r="<"+t[0].tag,t[0].attr)t[0].attr.hasOwnProperty(s)&&(r+=" "+s+'="'+((m=t[0].attr[s])||0===m?String(m).replace(/&/g,"&").replace(/"/g,"""):"")+'"');r+=">",t[0].children.forEach(function(e){r+=e}),r+="",t[0]=r}return i[0]=t[0],o&&o(t[0]),i}},function(e,t){"use strict";var s=/["'&<>]/;e.exports=function(e){var t,n=""+e,i=s.exec(n);if(!i)return n;var r="",a=0,o=0;for(a=i.index;ae.length)&&e.substring(0,t.length)===t},ud:function(e,t){if(e===t)return!0;if(11===e.nodeType)return!1;if(t.contains)return t.contains(1!==e.nodeType?e.parentNode:e);if(t.compareDocumentPosition)return 16==(16&t.compareDocumentPosition(e));for(;e&&e!=t;)e=e.parentNode;return!!e},Rb:function(e){return E.a.ud(e,e.ownerDocument.documentElement)},jd:function(e){return!!E.a.Lb(e,E.a.Rb)},P:function(e){return e&&e.tagName&&e.tagName.toLowerCase()},zc:function(e){return E.onError?function(){try{return e.apply(this,arguments)}catch(e){throw E.onError&&E.onError(e),e}}:e},setTimeout:function(e,t){return setTimeout(E.a.zc(e),t)},Fc:function(e){setTimeout(function(){throw E.onError&&E.onError(e),e},0)},H:function(t,e,n){var i=E.a.zc(n);if(n=u[e],E.options.useOnlyNativeEvents||n||!lna)if(n||"function"!=typeof t.addEventListener){if(void 0===t.attachEvent)throw Error("Browser doesn't support addEventListener or attachEvent");function r(e){i.call(t,e)}var a="on"+e;t.attachEvent(a,r),E.a.I.za(t,function(){t.detachEvent(a,r)})}else t.addEventListener(e,i,!1);else l=l||("function"==typeof lna(t).on?"on":"bind"),lna(t)[l](e,i)},Fb:function(e,t){if(!e||!e.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var n;if(n=!("input"!==E.a.P(e)||!e.type||"click"!=t.toLowerCase()||"checkbox"!=(n=e.type)&&"radio"!=n),E.options.useOnlyNativeEvents||!lna||n)if("function"==typeof jna.createEvent){if("function"!=typeof e.dispatchEvent)throw Error("The supplied element doesn't support dispatchEvent");(n=jna.createEvent(s[t]||"HTMLEvents")).initEvent(t,!0,!0,ina,0,0,0,0,0,!1,!1,!1,!1,0,e),e.dispatchEvent(n)}else if(n&&e.click)e.click();else{if(void 0===e.fireEvent)throw Error("Browser doesn't support triggering events");e.fireEvent("on"+t)}else lna(e).trigger(t)},c:function(e){return E.N(e)?e():e},$b:function(e){return E.N(e)?e.w():e},Eb:function(t,e,n){var i;e&&("object"==typeof t.classList?(i=t.classList[n?"add":"remove"],E.a.C(e.match(h),function(e){i.call(t.classList,e)})):"string"==typeof t.className.baseVal?r(t.className,"baseVal",e,n):r(t,"className",e,n))},Ab:function(e,t){var n=E.a.c(t);null!==n&&n!==hna||(n="");var i=E.h.firstChild(e);!i||3!=i.nodeType||E.h.nextSibling(i)?E.h.ua(e,[e.ownerDocument.createTextNode(n)]):i.data=n,E.a.zd(e)},Xc:function(e,t){if(e.name=t,c<=7)try{var n=e.name.replace(/[&<>'"]/g,function(e){return"&#"+e.charCodeAt(0)+";"});e.mergeAttributes(jna.createElement(""),!1)}catch(e){}},zd:function(e){9<=c&&(e=1==e.nodeType?e:e.parentNode).style&&(e.style.zoom=e.style.zoom)},vd:function(e){if(c){var t=e.style.width;e.style.width=0,e.style.width=t}},Od:function(e,t){e=E.a.c(e),t=E.a.c(t);for(var n=[],i=e;i<=t;i++)n.push(i);return n},la:function(e){for(var t=[],n=0,i=e.length;n",""],tbody:t,tfoot:t,tr:[2,"","
    "],td:c=[3,"","
    "],th:c,option:d=[1,""],optgroup:d},f=E.a.W<=8,E.a.ta=function(e,t){var n;if(lna){if(lna.parseHTML)n=lna.parseHTML(e,t)||[];else if((n=lna.clean([e],t))&&n[0]){for(var i=n[0];i.parentNode&&11!==i.parentNode.nodeType;)i=i.parentNode;i.parentNode&&i.parentNode.removeChild(i)}}else{(n=t)||(n=jna),i=n.parentWindow||n.defaultView||ina;var r,a=E.a.Cb(e).toLowerCase(),o=n.createElement("div");for(a=(r=(a=a.match(/^(?:\x3c!--.*?--\x3e\s*?)*?<([a-z]+)[\s>]/))&&h[a[1]]||l)[0],r="ignored
    "+r[1]+e+r[2]+"
    ","function"==typeof i.innerShiv?o.appendChild(i.innerShiv(r)):(f&&n.body.appendChild(o),o.innerHTML=r,f&&o.parentNode.removeChild(o));a--;)o=o.lastChild;n=E.a.la(o.lastChild.childNodes)}return n},E.a.Ld=function(e,t){var n=E.a.ta(e,t);return n.length&&n[0].parentElement||E.a.Xb(n)},E.a.dc=function(e,t){if(E.a.Sb(e),null!==(t=E.a.c(t))&&t!==hna)if("string"!=typeof t&&(t=t.toString()),lna)lna(e).html(t);else for(var n=E.a.ta(t,e.ownerDocument),i=0;i]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,he=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g,{wd:function(e,t,n){t.isTemplateRewritten(e,n)||t.rewriteTemplate(e,function(e){return E.ic.Kd(e,t)},n)},Kd:function(e,a){return e.replace(de,function(e,t,n,i,r){return ge(r,t,n,a)}).replace(he,function(e,t){return ge(t,"\x3c!-- ko --\x3e","#comment",a)})},ld:function(i,r){return E.aa.Wb(function(e,t){var n=e.nextSibling;n&&n.nodeName.toLowerCase()===r&&E.eb(n,i,t)})}}),E.b("__tr_ambtns",E.ic.ld),function(){E.B={},E.B.D=function(e){if(this.D=e){var t=E.a.P(e);this.Db="script"===t?1:"textarea"===t?2:"template"==t&&e.content&&11===e.content.nodeType?3:4}},E.B.D.prototype.text=function(){var e=1===this.Db?"text":2===this.Db?"value":"innerHTML";if(0==arguments.length)return this.D[e];var t=arguments[0];"innerHTML"==e?E.a.dc(this.D,t):this.D[e]=t};var t=E.a.g.Z()+"_";E.B.D.prototype.data=function(e){if(1===arguments.length)return E.a.g.get(this.D,t+e);E.a.g.set(this.D,t+e,arguments[1])};var i=E.a.g.Z();E.B.D.prototype.nodes=function(){var e=this.D;if(0==arguments.length){var t=E.a.g.get(e,i)||{},n=t.jb||(3===this.Db?e.content:4===this.Db?e:hna);return n&&!t.hd||(t=this.text())&&(n=E.a.Ld(t,e.ownerDocument),this.text(""),E.a.g.set(e,i,{jb:n,hd:!0})),n}E.a.g.set(e,i,{jb:arguments[0]})},E.B.ia=function(e){this.D=e},E.B.ia.prototype=new E.B.D,E.B.ia.prototype.constructor=E.B.ia,E.B.ia.prototype.text=function(){if(0==arguments.length){var e=E.a.g.get(this.D,i)||{};return e.jc===hna&&e.jb&&(e.jc=e.jb.innerHTML),e.jc}E.a.g.set(this.D,i,{jc:arguments[0]})},E.b("templateSources",E.B),E.b("templateSources.domElement",E.B.D),E.b("templateSources.anonymousTemplate",E.B.ia)}(),function(){function i(e,t,n){var i;for(t=E.h.nextSibling(t);e&&(i=e)!==t;)n(i,e=E.h.nextSibling(i))}function h(e,t){if(e.length){var r=e[0],a=e[e.length-1],n=r.parentNode,o=E.ga.instance,s=o.preprocessNode;if(s){if(i(r,a,function(e,t){var n=e.previousSibling,i=s.call(o,e);i&&(e===r&&(r=i[0]||t),e===a&&(a=i[i.length-1]||n))}),e.length=0,!r)return;r===a?e.push(r):(e.push(r,a),E.a.Ua(e,n))}i(r,a,function(e){1!==e.nodeType&&8!==e.nodeType||E.uc(t,e)}),i(r,a,function(e){1!==e.nodeType&&8!==e.nodeType||E.aa.bd(e,[t])}),E.a.Ua(e,n)}}function l(e){return e.nodeType?e:0"+t+"<\/script>")},0").attr("id",e.containerId).addClass(e.positionClass)).appendTo(g(e.target)),w}(e)),w}function i(e,t,n){var i=!(!n||!n.force)&&n.force;return!(!e||!i&&0!==g(":focus",e).length||(e[t.hideMethod]({duration:t.hideDuration,easing:t.hideEasing,complete:function(){_(e)}}),0))}function y(e){t&&t(e)}function r(t){var r=b(),e=t.iconClass||r.iconClass;if(void 0!==t.optionsOverride&&(r=g.extend(r,t.optionsOverride),e=t.optionsOverride.iconClass||e),!function(e,t){if(e.preventDuplicates){if(t.message===D)return!0;D=t.message}return!1}(r,t)){x++,w=v(r,!0);var a=null,o=g("
    "),n=g("
    "),i=g("
    "),s=g("
    "),l=g(r.closeHtml),u={intervalId:null,hideEta:null,maxHideTime:null},c={toastId:x,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&o.addClass(r.toastClass).addClass(e),function(){if(t.title){var e=t.title;r.escapeHtml&&(e=d(t.title)),n.append(e).addClass(r.titleClass),o.append(n)}}(),function(){if(t.message){var e=t.message;r.escapeHtml&&(e=d(t.message)),i.append(e).addClass(r.messageClass),o.append(i)}}(),r.closeButton&&(l.addClass(r.closeClass).attr("role","button"),o.prepend(l)),r.progressBar&&(s.addClass(r.progressClass),o.prepend(s)),r.rtl&&o.addClass("rtl"),r.newestOnTop?w.prepend(o):w.append(o),function(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}o.attr("aria-live",e)}(),o.hide(),o[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),0/g,">")}function h(e){var t=e&&!1!==r.closeMethod?r.closeMethod:r.hideMethod,n=e&&!1!==r.closeDuration?r.closeDuration:r.hideDuration,i=e&&!1!==r.closeEasing?r.closeEasing:r.hideEasing;if(!g(":focus",o).length||e)return clearTimeout(u.intervalId),o[t]({duration:n,easing:i,complete:function(){_(o),clearTimeout(a),r.onHidden&&"hidden"!==c.state&&r.onHidden(),c.state="hidden",c.endTime=new Date,y(c)}})}function f(){(0×',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1},e.options)}function _(e){w=w||v(),e.is(":visible")||(e.remove(),e=null,0===w.children().length&&(w.remove(),D=void 0))}var w,t,D,x,a,o,s,l,e}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,r;function y(){return e.apply(null,arguments)}function s(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function l(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function u(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function h(e,t){var n,i=[];for(n=0;n>>0,i=0;iDe(e)?(a=e+1,s-De(e)):(a=e,s),{year:a,dayOfYear:o}}function Ve(e,t,n){var i,r,a=Re(e.year(),t,n),o=Math.floor((e.dayOfYear()-a-1)/7)+1;return o<1?i=o+Ue(r=e.year()-1,t,n):o>Ue(e.year(),t,n)?(i=o-Ue(e.year(),t,n),r=e.year()+1):(r=e.year(),i=o),{week:i,year:r}}function Ue(e,t,n){var i=Re(e,t,n),r=Re(e+1,t,n);return(De(e)-i+r)/7}function We(e,t){return e.slice(t,7).concat(e.slice(0,t))}U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"),N("week","w"),N("isoWeek","W"),L("week",5),L("isoWeek",5),le("w",Q),le("ww",Q,$),le("W",Q),le("WW",Q,$),he(["w","ww","W","WW"],function(e,t,n,i){t[i.substr(0,1)]=C(e)}),U("d",0,"do","day"),U("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),U("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),U("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"),N("day","d"),N("weekday","e"),N("isoWeekday","E"),L("day",11),L("weekday",11),L("isoWeekday",11),le("d",Q),le("e",Q),le("E",Q),le("dd",function(e,t){return t.weekdaysMinRegex(e)}),le("ddd",function(e,t){return t.weekdaysShortRegex(e)}),le("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,i){var r=n._locale.weekdaysParse(e,i,n._strict);null!=r?t.d=r:_(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,i){t[i]=C(e)});var qe="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Be="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),$e="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ze=oe,Ge=oe,Ke=oe;function Qe(){function e(e,t){return t.length-e.length}var t,n,i,r,a,o=[],s=[],l=[],u=[];for(t=0;t<7;t++)n=p([2e3,1]).day(t),i=this.weekdaysMin(n,""),r=this.weekdaysShort(n,""),a=this.weekdays(n,""),o.push(i),s.push(r),l.push(a),u.push(i),u.push(r),u.push(a);for(o.sort(e),s.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)s[t]=ue(s[t]),l[t]=ue(l[t]),u[t]=ue(u[t]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function Je(){return this.hours()%12||12}function Ze(e,t){U(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Xe(e,t){return t._meridiemParse}U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Je),U("k",["kk",2],0,function(){return this.hours()||24}),U("hmm",0,0,function(){return""+Je.apply(this)+H(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Je.apply(this)+H(this.minutes(),2)+H(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+H(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+H(this.minutes(),2)+H(this.seconds(),2)}),Ze("a",!0),Ze("A",!1),N("hour","h"),L("hour",13),le("a",Xe),le("A",Xe),le("H",Q),le("h",Q),le("k",Q),le("HH",Q,$),le("hh",Q,$),le("kk",Q,$),le("hmm",J),le("hmmss",Z),le("Hmm",J),le("Hmmss",Z),de(["H","HH"],ge),de(["k","kk"],function(e,t,n){var i=C(e);t[ge]=24===i?0:i}),de(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),de(["h","hh"],function(e,t,n){t[ge]=C(e),_(n).bigHour=!0}),de("hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i)),_(n).bigHour=!0}),de("hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r)),_(n).bigHour=!0}),de("Hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i))}),de("Hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r))});var et,tt=Te("Hours",!0),nt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Oe,monthsShort:Ne,week:{dow:0,doy:6},weekdays:qe,weekdaysMin:$e,weekdaysShort:Be,meridiemParse:/[ap]\.?m?\.?/i},it={},rt={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=et._abbr,require("./locale/"+e),st(t)}catch(e){}return it[e]}function st(e,t){var n;return e&&((n=u(t)?ut(e):lt(e,t))?et=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),et._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,i=nt;if(t.abbr=e,null!=it[e])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])i=it[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return rt[t.parentLocale]||(rt[t.parentLocale]=[]),rt[t.parentLocale].push({name:e,config:t}),null;i=n._config}return it[e]=new A(M(i,t)),rt[e]&&rt[e].forEach(function(e){lt(e.name,e.config)}),st(e),it[e]}function ut(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return et;if(!s(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,i,r,a=0;a=t&&o(r,n,!0)>=t-1)break;t--}a++}return et}(e)}function ct(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[pe]<0||11Me(n[fe],n[pe])?me:n[ge]<0||24Ue(n,a,o)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(s=Ye(n,i,r,a,o),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(a=dt(e._a[fe],i[fe]),(e._dayOfYear>De(a)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Fe(a,0,e._dayOfYear),e._a[pe]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[ye]&&0===e._a[be]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Fe:function(e,t,n,i,r,a,o){var s;return e<100&&0<=e?(s=new Date(e+400,t,n,i,r,a,o),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,i,r,a,o),s}).apply(null,o),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(_(e).weekdayMismatch=!0)}}var ft=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,pt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mt=/Z|[+-]\d\d(?::?\d\d)?/,gt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],yt=/^\/?Date\((\-?\d+)/i;function bt(e){var t,n,i,r,a,o,s=e._i,l=ft.exec(s)||pt.exec(s);if(l){for(_(e).iso=!0,t=0,n=gt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},dn.isLocal=function(){return!!this.isValid()&&!this._isUTC},dn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},dn.isUtc=Rt,dn.isUTC=Rt,dn.zoneAbbr=function(){return this._isUTC?"UTC":""},dn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},dn.dates=n("dates accessor is deprecated. Use date instead.",an),dn.months=n("months accessor is deprecated. Use month instead",je),dn.years=n("years accessor is deprecated. Use year instead",ke),dn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),dn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!u(this._isDSTShifted))return this._isDSTShifted;var e={};if(v(e,this),(e=Ct(e))._a){var t=e._isUTC?p(e._a):Tt(e._a);this._isDSTShifted=this.isValid()&&0 div").hide().filter(".datepicker-"+h[this.currentViewMode].CLASS_NAME).show())},y.prototype._isInDisabledDates=function(e){return!0===this._options.disabledDates[e.format("YYYY-MM-DD")]},y.prototype._isInEnabledDates=function(e){return!0===this._options.enabledDates[e.format("YYYY-MM-DD")]},y.prototype._isInDisabledHours=function(e){return!0===this._options.disabledHours[e.format("H")]},y.prototype._isInEnabledHours=function(e){return!0===this._options.enabledHours[e.format("H")]},y.prototype._isValid=function(e,t){if(!e.isValid())return!1;if(this._options.disabledDates&&"d"===t&&this._isInDisabledDates(e))return!1;if(this._options.enabledDates&&"d"===t&&!this._isInEnabledDates(e))return!1;if(this._options.minDate&&e.isBefore(this._options.minDate,t))return!1;if(this._options.maxDate&&e.isAfter(this._options.maxDate,t))return!1;if(this._options.daysOfWeekDisabled&&"d"===t&&-1!==this._options.daysOfWeekDisabled.indexOf(e.day()))return!1;if(this._options.disabledHours&&("h"===t||"m"===t||"s"===t)&&this._isInDisabledHours(e))return!1;if(this._options.enabledHours&&("h"===t||"m"===t||"s"===t)&&!this._isInEnabledHours(e))return!1;if(this._options.disabledTimeIntervals&&("h"===t||"m"===t||"s"===t)){var n=!1;if(o.each(this._options.disabledTimeIntervals,function(){if(e.isBetween(this[0],this[1]))return!(n=!0)}),n)return!1}return!0},y.prototype._parseInputDate=function(e){return void 0===this._options.parseInputDate?n.isMoment(e)||(e=this.getMoment(e)):e=this._options.parseInputDate(e),e},y.prototype._keydown=function(e){var t=null,n=void 0,i=void 0,r=void 0,a=void 0,o=[],s={},l=e.which;for(n in m[l]="p",m)m.hasOwnProperty(n)&&"p"===m[n]&&(o.push(n),parseInt(n,10)!==l&&(s[n]=!0));for(n in this._options.keyBinds)if(this._options.keyBinds.hasOwnProperty(n)&&"function"==typeof this._options.keyBinds[n]&&(r=n.split(" ")).length===o.length&&f[l]===r[r.length-1]){for(a=!0,i=r.length-2;0<=i;i--)if(!(f[r[i]]in s)){a=!1;break}if(a){t=this._options.keyBinds[n];break}}t&&t.call(this)&&(e.stopPropagation(),e.preventDefault())},y.prototype._keyup=function(e){m[e.which]="r",g[e.which]&&(g[e.which]=!1,e.stopPropagation(),e.preventDefault())},y.prototype._indexGivenDates=function(e){var t={},n=this;return o.each(e,function(){var e=n._parseInputDate(this);e.isValid()&&(t[e.format("YYYY-MM-DD")]=!0)}),!!Object.keys(t).length&&t},y.prototype._indexGivenHours=function(e){var t={};return o.each(e,function(){t[this]=!0}),!!Object.keys(t).length&&t},y.prototype._initFormatting=function(){var e=this._options.format||"L LT",t=this;this.actualFormat=e.replace(/(\[[^\[]*])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(e){return t._dates[0].localeData().longDateFormat(e)||e}),this.parseFormats=this._options.extraFormats?this._options.extraFormats.slice():[],this.parseFormats.indexOf(e)<0&&this.parseFormats.indexOf(this.actualFormat)<0&&this.parseFormats.push(this.actualFormat),this.use24Hours=this.actualFormat.toLowerCase().indexOf("a")<1&&this.actualFormat.replace(/\[.*?]/g,"").indexOf("h")<1,this._isEnabled("y")&&(this.MinViewModeNumber=2),this._isEnabled("M")&&(this.MinViewModeNumber=1),this._isEnabled("d")&&(this.MinViewModeNumber=0),this.currentViewMode=Math.max(this.MinViewModeNumber,this.currentViewMode),this.unset||this._setValue(this._dates[0],0)},y.prototype._getLastPickedDate=function(){return this._dates[this._getLastPickedDateIndex()]},y.prototype._getLastPickedDateIndex=function(){return this._dates.length-1},y.prototype.getMoment=function(e){var t=void 0;return t=null==e?n():this._hasTimeZone()?n.tz(e,this.parseFormats,this._options.locale,this._options.useStrict,this._options.timeZone):n(e,this.parseFormats,this._options.locale,this._options.useStrict),this._hasTimeZone()&&t.tz(this._options.timeZone),t},y.prototype.toggle=function(){return this.widget?this.hide():this.show()},y.prototype.ignoreReadonly=function(e){if(0===arguments.length)return this._options.ignoreReadonly;if("boolean"!=typeof e)throw new TypeError("ignoreReadonly () expects a boolean parameter");this._options.ignoreReadonly=e},y.prototype.options=function(e){if(0===arguments.length)return o.extend(!0,{},this._options);if(!(e instanceof Object))throw new TypeError("options() this.options parameter should be an object");o.extend(!0,this._options,e);var n=this;o.each(this._options,function(e,t){void 0!==n[e]&&n[e](t)})},y.prototype.date=function(e,t){if(t=t||0,0===arguments.length)return this.unset?null:this._options.allowMultidate?this._dates.join(this._options.multidateSeparator):this._dates[t].clone();if(!(null===e||"string"==typeof e||n.isMoment(e)||e instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");this._setValue(null===e?null:this._parseInputDate(e),t)},y.prototype.format=function(e){if(0===arguments.length)return this._options.format;if("string"!=typeof e&&("boolean"!=typeof e||!1!==e))throw new TypeError("format() expects a string or boolean:false parameter "+e);this._options.format=e,this.actualFormat&&this._initFormatting()},y.prototype.timeZone=function(e){if(0===arguments.length)return this._options.timeZone;if("string"!=typeof e)throw new TypeError("newZone() expects a string parameter");this._options.timeZone=e},y.prototype.dayViewHeaderFormat=function(e){if(0===arguments.length)return this._options.dayViewHeaderFormat;if("string"!=typeof e)throw new TypeError("dayViewHeaderFormat() expects a string parameter");this._options.dayViewHeaderFormat=e},y.prototype.extraFormats=function(e){if(0===arguments.length)return this._options.extraFormats;if(!1!==e&&!(e instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");this._options.extraFormats=e,this.parseFormats&&this._initFormatting()},y.prototype.disabledDates=function(e){if(0===arguments.length)return this._options.disabledDates?o.extend({},this._options.disabledDates):this._options.disabledDates;if(!e)return this._options.disabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("disabledDates() expects an array parameter");this._options.disabledDates=this._indexGivenDates(e),this._options.enabledDates=!1,this._update()},y.prototype.enabledDates=function(e){if(0===arguments.length)return this._options.enabledDates?o.extend({},this._options.enabledDates):this._options.enabledDates;if(!e)return this._options.enabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("enabledDates() expects an array parameter");this._options.enabledDates=this._indexGivenDates(e),this._options.disabledDates=!1,this._update()},y.prototype.daysOfWeekDisabled=function(e){if(0===arguments.length)return this._options.daysOfWeekDisabled.splice(0);if("boolean"==typeof e&&!e)return this._options.daysOfWeekDisabled=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(this._options.daysOfWeekDisabled=e.reduce(function(e,t){return 6<(t=parseInt(t,10))||t<0||isNaN(t)||-1===e.indexOf(t)&&e.push(t),e},[]).sort(),this._options.useCurrent&&!this._options.keepInvalid)for(var t=0;t").append(S("").append(S("").addClass("prev").attr("data-action","previous").append(S("").addClass(this._options.icons.previous))).append(S("").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",this._options.calendarWeeks?"6":"5")).append(S("").addClass("next").attr("data-action","next").append(S("").addClass(this._options.icons.next)))),t=S("").append(S("").append(S("").attr("colspan",this._options.calendarWeeks?"8":"7")));return[S("
    ").addClass("datepicker-days").append(S("").addClass("table table-sm").append(e).append(S(""))),S("
    ").addClass("datepicker-months").append(S("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),S("
    ").addClass("datepicker-years").append(S("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),S("
    ").addClass("datepicker-decades").append(S("
    ").addClass("table-condensed").append(e.clone()).append(t.clone()))]},E.prototype._getTimePickerMainTemplate=function(){var e=S(""),t=S(""),n=S("");return this._isEnabled("h")&&(e.append(S("").append(S("").append(S("").append(S("
    ").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(S("").addClass(this._options.icons.up)))),t.append(S("").append(S("").addClass("timepicker-hour").attr({"data-time-component":"hours",title:this._options.tooltips.pickHour}).attr("data-action","showHours"))),n.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(S("").addClass(this._options.icons.down))))),this._isEnabled("m")&&(this._isEnabled("h")&&(e.append(S("").addClass("separator")),t.append(S("").addClass("separator").html(":")),n.append(S("").addClass("separator"))),e.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(S("").addClass(this._options.icons.up)))),t.append(S("").append(S("").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:this._options.tooltips.pickMinute}).attr("data-action","showMinutes"))),n.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(S("").addClass(this._options.icons.down))))),this._isEnabled("s")&&(this._isEnabled("m")&&(e.append(S("").addClass("separator")),t.append(S("").addClass("separator").html(":")),n.append(S("").addClass("separator"))),e.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(S("").addClass(this._options.icons.up)))),t.append(S("").append(S("").addClass("timepicker-second").attr({"data-time-component":"seconds",title:this._options.tooltips.pickSecond}).attr("data-action","showSeconds"))),n.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(S("").addClass(this._options.icons.down))))),this.use24Hours||(e.append(S("").addClass("separator")),t.append(S("").append(S("").addClass("separator"))),S("
    ").addClass("timepicker-picker").append(S("").addClass("table-condensed").append([e,t,n]))},E.prototype._getTimePickerTemplate=function(){var e=S("
    ").addClass("timepicker-hours").append(S("
    ").addClass("table-condensed")),t=S("
    ").addClass("timepicker-minutes").append(S("
    ").addClass("table-condensed")),n=S("
    ").addClass("timepicker-seconds").append(S("
    ").addClass("table-condensed")),i=[this._getTimePickerMainTemplate()];return this._isEnabled("h")&&i.push(e),this._isEnabled("m")&&i.push(t),this._isEnabled("s")&&i.push(n),i},E.prototype._getToolbar=function(){var e=[];if(this._options.buttons.showToday&&e.push(S("
    ").append(S("").attr({href:"#",tabindex:"-1","data-action":"today",title:this._options.tooltips.today}).append(S("").addClass(this._options.icons.today)))),!this._options.sideBySide&&this._hasDate()&&this._hasTime()){var t=void 0,n=void 0;n="times"===this._options.viewMode?(t=this._options.tooltips.selectDate,this._options.icons.date):(t=this._options.tooltips.selectTime,this._options.icons.time),e.push(S("").append(S("").attr({href:"#",tabindex:"-1","data-action":"togglePicker",title:t}).append(S("").addClass(n))))}return this._options.buttons.showClear&&e.push(S("").append(S("").attr({href:"#",tabindex:"-1","data-action":"clear",title:this._options.tooltips.clear}).append(S("").addClass(this._options.icons.clear)))),this._options.buttons.showClose&&e.push(S("").append(S("").attr({href:"#",tabindex:"-1","data-action":"close",title:this._options.tooltips.close}).append(S("").addClass(this._options.icons.close)))),0===e.length?"":S("").addClass("table-condensed").append(S("").append(S("").append(e)))},E.prototype._getTemplate=function(){var e=S("
    ").addClass("bootstrap-datetimepicker-widget dropdown-menu"),t=S("
    ").addClass("datepicker").append(this._getDatePickerTemplate()),n=S("
    ").addClass("timepicker").append(this._getTimePickerTemplate()),i=S("
      ").addClass("list-unstyled"),r=S("
    • ").addClass("picker-switch"+(this._options.collapse?" accordion-toggle":"")).append(this._getToolbar());return this._options.inline&&e.removeClass("dropdown-menu"),this.use24Hours&&e.addClass("usetwentyfour"),this._isEnabled("s")&&!this.use24Hours&&e.addClass("wider"),this._options.sideBySide&&this._hasDate()&&this._hasTime()?(e.addClass("timepicker-sbs"),"top"===this._options.toolbarPlacement&&e.append(r),e.append(S("
      ").addClass("row").append(t.addClass("col-md-6")).append(n.addClass("col-md-6"))),"bottom"!==this._options.toolbarPlacement&&"default"!==this._options.toolbarPlacement||e.append(r),e):("top"===this._options.toolbarPlacement&&i.append(r),this._hasDate()&&i.append(S("
    • ").addClass(this._options.collapse&&this._hasTime()?"collapse":"").addClass(this._options.collapse&&this._hasTime()&&"times"===this._options.viewMode?"":"show").append(t)),"default"===this._options.toolbarPlacement&&i.append(r),this._hasTime()&&i.append(S("
    • ").addClass(this._options.collapse&&this._hasDate()?"collapse":"").addClass(this._options.collapse&&this._hasDate()&&"times"===this._options.viewMode?"show":"").append(n)),"bottom"===this._options.toolbarPlacement&&i.append(r),e.append(i))},E.prototype._place=function(e){var t=e&&e.data&&e.data.picker||this,n=t._options.widgetPositioning.vertical,i=t._options.widgetPositioning.horizontal,r=void 0,a=(t.component&&t.component.length?t.component:t._element).position(),o=(t.component&&t.component.length?t.component:t._element).offset();if(t._options.widgetParent)r=t._options.widgetParent.append(t.widget);else if(t._element.is("input"))r=t._element.after(t.widget).parent();else{if(t._options.inline)return void(r=t._element.append(t.widget));r=t._element,t._element.children().first().after(t.widget)}if("auto"===n&&(n=o.top+1.5*t.widget.height()>=S(window).height()+S(window).scrollTop()&&t.widget.height()+t._element.outerHeight()S(window).width()?"right":"left"),"top"===n?t.widget.addClass("top").removeClass("bottom"):t.widget.addClass("bottom").removeClass("top"),"right"===i?t.widget.addClass("float-right"):t.widget.removeClass("float-right"),"relative"!==r.css("position")&&(r=r.parents().filter(function(){return"relative"===S(this).css("position")}).first()),0===r.length)throw new Error("datetimepicker component should be placed within a relative positioned container");t.widget.css({top:"top"===n?"auto":a.top+t._element.outerHeight()+"px",bottom:"top"===n?r.outerHeight()-(r===t._element?0:a.top)+"px":"auto",left:"left"===i?(r===t._element?0:a.left)+"px":"auto",right:"left"===i?"auto":r.outerWidth()-t._element.outerWidth()-(r===t._element?0:a.left)+"px"})},E.prototype._fillDow=function(){var e=S("
    "),t=this._viewDate.clone().startOf("w").startOf("d");for(!0===this._options.calendarWeeks&&e.append(S(""),this._options.calendarWeeks&&r.append('"),n.push(r)),a="",i.isBefore(this._viewDate,"M")&&(a+=" old"),i.isAfter(this._viewDate,"M")&&(a+=" new"),this._options.allowMultidate){var s=this._datesFormatted.indexOf(i.format("YYYY-MM-DD"));-1!==s&&i.isSame(this._datesFormatted[s],"d")&&!this.unset&&(a+=" active")}else i.isSame(this._getLastPickedDate(),"d")&&!this.unset&&(a+=" active");this._isValid(i,"d")||(a+=" disabled"),i.isSame(this.getMoment(),"d")&&(a+=" today"),0!==i.day()&&6!==i.day()||(a+=" weekend"),r.append('"),i.add(1,"d")}e.find("tbody").empty().append(n),this._updateMonths(),this._updateYears(),this._updateDecades()}},E.prototype._fillHours=function(){var e=this.widget.find(".timepicker-hours table"),t=this._viewDate.clone().startOf("d"),n=[],i=S("");for(11"),n.push(i)),i.append('"),t.add(1,"h");e.empty().append(n)},E.prototype._fillMinutes=function(){for(var e=this.widget.find(".timepicker-minutes table"),t=this._viewDate.clone().startOf("h"),n=[],i=1===this._options.stepping?5:this._options.stepping,r=S("");this._viewDate.isSame(t,"h");)t.minute()%(4*i)==0&&(r=S(""),n.push(r)),r.append('"),t.add(i,"m");e.empty().append(n)},E.prototype._fillSeconds=function(){for(var e=this.widget.find(".timepicker-seconds table"),t=this._viewDate.clone().startOf("m"),n=[],i=S("");this._viewDate.isSame(t,"m");)t.second()%20==0&&(i=S(""),n.push(i)),i.append('"),t.add(5,"s");e.empty().append(n)},E.prototype._fillTime=function(){var e=void 0,t=void 0,n=this.widget.find(".timepicker span[data-time-component]");this.use24Hours||(e=this.widget.find(".timepicker [data-action=togglePeriod]"),t=this._getLastPickedDate().clone().add(12<=this._getLastPickedDate().hours()?-12:12,"h"),e.text(this._getLastPickedDate().format("A")),this._isValid(t,"h")?e.removeClass("disabled"):e.addClass("disabled")),n.filter("[data-time-component=hours]").text(this._getLastPickedDate().format(this.use24Hours?"HH":"hh")),n.filter("[data-time-component=minutes]").text(this._getLastPickedDate().format("mm")),n.filter("[data-time-component=seconds]").text(this._getLastPickedDate().format("ss")),this._fillHours(),this._fillMinutes(),this._fillSeconds()},E.prototype._doAction=function(e,t){var n=this._getLastPickedDate();if(S(e.currentTarget).is(".disabled"))return!1;switch(t=t||S(e.currentTarget).data("action")){case"next":var i=T.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.add(T.DatePickerModes[this.currentViewMode].NAV_STEP,i),this._fillDate(),this._viewUpdate(i);break;case"previous":var r=T.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.subtract(T.DatePickerModes[this.currentViewMode].NAV_STEP,r),this._fillDate(),this._viewUpdate(r);break;case"pickerSwitch":this._showMode(1);break;case"selectMonth":var a=S(e.target).closest("tbody").find("span").index(S(e.target));this._viewDate.month(a),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()).month(this._viewDate.month()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("M");break;case"selectYear":var o=parseInt(S(e.target).text(),10)||0;this._viewDate.year(o),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDecade":var s=parseInt(S(e.target).data("selection"),10)||0;this._viewDate.year(s),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDay":var l=this._viewDate.clone();S(e.target).is(".old")&&l.subtract(1,"M"),S(e.target).is(".new")&&l.add(1,"M");var u=l.date(parseInt(S(e.target).text(),10)),c=0;this._options.allowMultidate?-1!==(c=this._datesFormatted.indexOf(u.format("YYYY-MM-DD")))?this._setValue(null,c):this._setValue(u,this._getLastPickedDateIndex()+1):this._setValue(u,this._getLastPickedDateIndex()),this._hasTime()||this._options.keepOpen||this._options.inline||this._options.allowMultidate||this.hide();break;case"incrementHours":var d=n.clone().add(1,"h");this._isValid(d,"h")&&this._setValue(d,this._getLastPickedDateIndex());break;case"incrementMinutes":var h=n.clone().add(this._options.stepping,"m");this._isValid(h,"m")&&this._setValue(h,this._getLastPickedDateIndex());break;case"incrementSeconds":var f=n.clone().add(1,"s");this._isValid(f,"s")&&this._setValue(f,this._getLastPickedDateIndex());break;case"decrementHours":var p=n.clone().subtract(1,"h");this._isValid(p,"h")&&this._setValue(p,this._getLastPickedDateIndex());break;case"decrementMinutes":var m=n.clone().subtract(this._options.stepping,"m");this._isValid(m,"m")&&this._setValue(m,this._getLastPickedDateIndex());break;case"decrementSeconds":var g=n.clone().subtract(1,"s");this._isValid(g,"s")&&this._setValue(g,this._getLastPickedDateIndex());break;case"togglePeriod":this._setValue(n.clone().add(12<=n.hours()?-12:12,"h"),this._getLastPickedDateIndex());break;case"togglePicker":var v=S(e.target),y=v.closest("a"),b=v.closest("ul"),_=b.find(".show"),w=b.find(".collapse:not(.show)"),D=v.is("span")?v:v.find("span"),x=void 0;if(_&&_.length){if((x=_.data("collapse"))&&x.transitioning)return!0;_.collapse?(_.collapse("hide"),w.collapse("show")):(_.removeClass("show"),w.addClass("show")),D.toggleClass(this._options.icons.time+" "+this._options.icons.date),D.hasClass(this._options.icons.date)?y.attr("title",this._options.tooltips.selectDate):y.attr("title",this._options.tooltips.selectTime)}break;case"showPicker":this.widget.find(".timepicker > div:not(.timepicker-picker)").hide(),this.widget.find(".timepicker .timepicker-picker").show();break;case"showHours":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-hours").show();break;case"showMinutes":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-minutes").show();break;case"showSeconds":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-seconds").show();break;case"selectHour":var C=parseInt(S(e.target).text(),10);this.use24Hours||(12<=n.hours()?12!==C&&(C+=12):12===C&&(C=0)),this._setValue(n.clone().hours(C),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("m")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectMinute":this._setValue(n.clone().minutes(parseInt(S(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("s")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectSecond":this._setValue(n.clone().seconds(parseInt(S(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"clear":this.clear();break;case"close":this.hide();break;case"today":var k=this.getMoment();this._isValid(k,"d")&&this._setValue(k,this._getLastPickedDateIndex())}return!1},E.prototype.hide=function(){var t=!1;this.widget&&(this.widget.find(".collapse").each(function(){var e=S(this).data("collapse");return!e||!e.transitioning||!(t=!0)}),t||(this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this.widget.hide(),S(window).off("resize",this._place()),this.widget.off("click","[data-action]"),this.widget.off("mousedown",!1),this.widget.remove(),this.widget=!1,this._notifyEvent({type:T.Event.HIDE,date:this._getLastPickedDate().clone()}),void 0!==this.input&&this.input.blur(),this._viewDate=this._getLastPickedDate().clone()))},E.prototype.show=function(){var e=void 0,t={year:function(e){return e.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(e){return e.date(1).hours(0).seconds(0).minutes(0)},day:function(e){return e.hours(0).seconds(0).minutes(0)},hour:function(e){return e.seconds(0).minutes(0)},minute:function(e){return e.seconds(0)}};if(void 0!==this.input){if(this.input.prop("disabled")||!this._options.ignoreReadonly&&this.input.prop("readonly")||this.widget)return;void 0!==this.input.val()&&0!==this.input.val().trim().length?this._setValue(this._parseInputDate(this.input.val().trim()),0):this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0))}else this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0));this.widget=this._getTemplate(),this._fillDow(),this._fillMonths(),this.widget.find(".timepicker-hours").hide(),this.widget.find(".timepicker-minutes").hide(),this.widget.find(".timepicker-seconds").hide(),this._update(),this._showMode(),S(window).on("resize",{picker:this},this._place),this.widget.on("click","[data-action]",S.proxy(this._doAction,this)),this.widget.on("mousedown",!1),this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this._place(),this.widget.show(),void 0!==this.input&&this._options.focusOnShow&&!this.input.is(":focus")&&this.input.focus(),this._notifyEvent({type:T.Event.SHOW})},E.prototype.destroy=function(){this.hide(),this._element.removeData(T.DATA_KEY),this._element.removeData("date")},E.prototype.disable=function(){this.hide(),this.component&&this.component.hasClass("btn")&&this.component.addClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!0)},E.prototype.enable=function(){this.component&&this.component.hasClass("btn")&&this.component.removeClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!1)},E.prototype.toolbarPlacement=function(e){if(0===arguments.length)return this._options.toolbarPlacement;if("string"!=typeof e)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===x.indexOf(e))throw new TypeError("toolbarPlacement() parameter must be one of ("+x.join(", ")+") value");this._options.toolbarPlacement=e,this.widget&&(this.hide(),this.show())},E.prototype.widgetPositioning=function(e){if(0===arguments.length)return S.extend({},this._options.widgetPositioning);if("[object Object]"!=={}.toString.call(e))throw new TypeError("widgetPositioning() expects an object variable");if(e.horizontal){if("string"!=typeof e.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(e.horizontal=e.horizontal.toLowerCase(),-1===D.indexOf(e.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+D.join(", ")+")");this._options.widgetPositioning.horizontal=e.horizontal}if(e.vertical){if("string"!=typeof e.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(e.vertical=e.vertical.toLowerCase(),-1===w.indexOf(e.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+w.join(", ")+")");this._options.widgetPositioning.vertical=e.vertical}this._update()},E.prototype.widgetParent=function(e){if(0===arguments.length)return this._options.widgetParent;if("string"==typeof e&&(e=S(e)),null!==e&&"string"!=typeof e&&!(e instanceof S))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");this._options.widgetParent=e,this.widget&&(this.hide(),this.show())},E._jQueryHandleThis=function(e,t,n){var i=S(e).data(T.DATA_KEY);if("object"===(void 0===t?"undefined":r(t))&&S.extend({},T.Default,t),i||(i=new E(S(e),t),S(e).data(T.DATA_KEY,i)),"string"==typeof t){if(void 0===i[t])throw new Error('No method named "'+t+'"');return void 0===n?i[t]():i[t](n)}},E._jQueryInterface=function(e,t){return 1===this.length?E._jQueryHandleThis(this[0],e,t):this.each(function(){E._jQueryHandleThis(this,e,t)})},C=E,S(document).on(T.Event.CLICK_DATA_API,T.Selector.DATA_TOGGLE,function(){var e=k(S(this));0!==e.length&&C._jQueryInterface.call(e,"toggle")}).on(T.Event.CHANGE,"."+T.ClassName.INPUT,function(e){var t=k(S(this));0!==t.length&&C._jQueryInterface.call(t,"_change",e)}).on(T.Event.BLUR,"."+T.ClassName.INPUT,function(e){var t=k(S(this)),n=t.data(T.DATA_KEY);0!==t.length&&(n._options.debug||window.debug||C._jQueryInterface.call(t,"hide",e))}).on(T.Event.KEYDOWN,"."+T.ClassName.INPUT,function(e){var t=k(S(this));0!==t.length&&C._jQueryInterface.call(t,"_keydown",e)}).on(T.Event.KEYUP,"."+T.ClassName.INPUT,function(e){var t=k(S(this));0!==t.length&&C._jQueryInterface.call(t,"_keyup",e)}).on(T.Event.FOCUS,"."+T.ClassName.INPUT,function(e){var t=k(S(this)),n=t.data(T.DATA_KEY);0!==t.length&&n._options.allowInputToggle&&C._jQueryInterface.call(t,"show",e)}),S.fn[T.NAME]=C._jQueryInterface,S.fn[T.NAME].Constructor=C,S.fn[T.NAME].noConflict=function(){return S.fn[T.NAME]=_,C._jQueryInterface};function k(e){var t=e.data("target"),n=void 0;return t||(t=e.attr("href")||"",t=/^#[a-z]/i.test(t)?t:null),0===(n=S(t)).length||n.data(T.DATA_KEY)||S.extend({},n.data(),S(this).data()),n}function E(e,t){a(this,E);var n=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,b.call(this,e,t));return n._init(),n}}(),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(E,M){function A(){return new Date(Date.UTC.apply(Date,arguments))}function O(){var e=new Date;return A(e.getFullYear(),e.getMonth(),e.getDate())}function a(e,t){return e.getUTCFullYear()===t.getUTCFullYear()&&e.getUTCMonth()===t.getUTCMonth()&&e.getUTCDate()===t.getUTCDate()}function e(e,t){return function(){return t!==M&&E.fn.datepicker.deprecated(t),this[e].apply(this,arguments)}}function w(e,t){E.data(e,"datepicker",this),this._events=[],this._secondaryEvents=[],this._process_options(t),this.dates=new n,this.viewDate=this.o.defaultViewDate,this.focusDate=null,this.element=E(e),this.isInput=this.element.is("input"),this.inputField=this.isInput?this.element:this.element.find("input"),this.component=!!this.element.hasClass("date")&&this.element.find(".add-on, .input-group-addon, .input-group-append, .input-group-prepend, .btn"),this.component&&0===this.component.length&&(this.component=!1),this.isInline=!this.component&&this.element.is("div"),this.picker=E(I.template),this._check_template(this.o.templates.leftArrow)&&this.picker.find(".prev").html(this.o.templates.leftArrow),this._check_template(this.o.templates.rightArrow)&&this.picker.find(".next").html(this.o.templates.rightArrow),this._buildEvents(),this._attachEvents(),this.isInline?this.picker.addClass("datepicker-inline").appendTo(this.element):this.picker.addClass("datepicker-dropdown dropdown-menu"),this.o.rtl&&this.picker.addClass("datepicker-rtl"),this.o.calendarWeeks&&this.picker.find(".datepicker-days .datepicker-switch, thead .datepicker-title, tfoot .today, tfoot .clear").attr("colspan",function(e,t){return Number(t)+1}),this._process_options({startDate:this._o.startDate,endDate:this._o.endDate,daysOfWeekDisabled:this.o.daysOfWeekDisabled,daysOfWeekHighlighted:this.o.daysOfWeekHighlighted,datesDisabled:this.o.datesDisabled}),this._allow_update=!1,this.setViewMode(this.o.startView),this._allow_update=!0,this.fillDow(),this.fillMonths(),this.update(),this.isInline&&this.show()}var t,n=(t={get:function(e){return this.slice(e)[0]},contains:function(e){for(var t=e&&e.valueOf(),n=0,i=this.length;n]/g)||[]).length<=0||0this.o.endDate?this.viewDate=new Date(this.o.endDate):this.viewDate=this.o.defaultViewDate),t?(this.setValue(),this.element.change()):this.dates.length&&String(e)!==String(this.dates)&&t&&(this._trigger("changeDate"),this.element.change()),!this.dates.length&&e.length&&(this._trigger("clearDate"),this.element.change()),this.fill(),this},fillDow:function(){if(this.o.showWeekDays){var e=this.o.weekStart,t="";for(this.o.calendarWeeks&&(t+='');e";t+="",this.picker.find(".datepicker-days thead").append(t)}},fillMonths:function(){for(var e=this._utc_to_local(this.viewDate),t="",n=0;n<12;n++)t+=''+N[this.o.language].monthsShort[n]+"";this.picker.find(".datepicker-months td").html(t)},setRange:function(e){e&&e.length?this.range=E.map(e,function(e){return e.valueOf()}):delete this.range,this.fill()},getClassNames:function(e){var t=[],n=this.viewDate.getUTCFullYear(),i=this.viewDate.getUTCMonth(),r=O();return e.getUTCFullYear()n||e.getUTCFullYear()===n&&e.getUTCMonth()>i)&&t.push("new"),this.focusDate&&e.valueOf()===this.focusDate.valueOf()&&t.push("focused"),this.o.todayHighlight&&a(e,r)&&t.push("today"),-1!==this.dates.contains(e)&&t.push("active"),this.dateWithinRange(e)||t.push("disabled"),this.dateIsDisabled(e)&&t.push("disabled","disabled-date"),-1!==E.inArray(e.getUTCDay(),this.o.daysOfWeekHighlighted)&&t.push("highlighted"),this.range&&(e>this.range[0]&&e"+v+"";h.find(".datepicker-switch").text(f+"-"+p),h.find("td").html(c)},fill:function(){var e,t,n=new Date(this.viewDate),r=n.getUTCFullYear(),i=n.getUTCMonth(),a=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,o=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,s=this.o.endDate!==1/0?this.o.endDate.getUTCFullYear():1/0,l=this.o.endDate!==1/0?this.o.endDate.getUTCMonth():1/0,u=N[this.o.language].today||N.en.today||"",c=N[this.o.language].clear||N.en.clear||"",d=N[this.o.language].titleFormat||N.en.titleFormat,h=O(),f=(!0===this.o.todayBtn||"linked"===this.o.todayBtn)&&h>=this.o.startDate&&h<=this.o.endDate&&!this.weekOfDateIsDisabled(h);if(!isNaN(r)&&!isNaN(i)){this.picker.find(".datepicker-days .datepicker-switch").text(I.formatDate(n,d,this.o.language)),this.picker.find("tfoot .today").text(u).css("display",f?"table-cell":"none"),this.picker.find("tfoot .clear").text(c).css("display",!0===this.o.clearBtn?"table-cell":"none"),this.picker.find("thead .datepicker-title").text(this.o.title).css("display","string"==typeof this.o.title&&""!==this.o.title?"table-cell":"none"),this.updateNavArrows(),this.fillMonths();var p=A(r,i,0),m=p.getUTCDate();p.setUTCDate(m-(p.getUTCDay()-this.o.weekStart+7)%7);var g=new Date(p);p.getUTCFullYear()<100&&g.setUTCFullYear(p.getUTCFullYear()),g.setUTCDate(g.getUTCDate()+42),g=g.valueOf();for(var v,y,b=[];p.valueOf()"),this.o.calendarWeeks)){var _=new Date(+p+(this.o.weekStart-v-7)%7*864e5),w=new Date(Number(_)+(11-_.getUTCDay())%7*864e5),D=new Date(Number(D=A(w.getUTCFullYear(),0,1))+(11-D.getUTCDay())%7*864e5),x=(w-D)/864e5/7+1;b.push('")}(y=this.getClassNames(p)).push("day");var C=p.getUTCDate();this.o.beforeShowDay!==E.noop&&((t=this.o.beforeShowDay(this._utc_to_local(p)))===M?t={}:"boolean"==typeof t?t={enabled:t}:"string"==typeof t&&(t={classes:t}),!1===t.enabled&&y.push("disabled"),t.classes&&(y=y.concat(t.classes.split(/\s+/))),t.tooltip&&(e=t.tooltip),t.content&&(C=t.content)),y=E.isFunction(E.uniqueSort)?E.uniqueSort(y):E.unique(y),b.push('"),e=null,v===this.o.weekEnd&&b.push(""),p.setUTCDate(p.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").html(b.join(""));var k=N[this.o.language].monthsTitle||N.en.monthsTitle||"Months",T=this.picker.find(".datepicker-months").find(".datepicker-switch").text(this.o.maxViewMode<2?k:r).end().find("tbody span").removeClass("active");if(E.each(this.dates,function(e,t){t.getUTCFullYear()===r&&T.eq(t.getUTCMonth()).addClass("active")}),(rs;break;case 0:e=i<=a&&r<=o,t=s<=i&&l<=r}this.picker.find(".prev").toggleClass("disabled",e),this.picker.find(".next").toggleClass("disabled",t)}},click:function(e){var t,n,i;e.preventDefault(),e.stopPropagation(),(t=E(e.target)).hasClass("datepicker-switch")&&this.viewMode!==this.o.maxViewMode&&this.setViewMode(this.viewMode+1),t.hasClass("today")&&!t.hasClass("day")&&(this.setViewMode(0),this._setDate(O(),"linked"===this.o.todayBtn?null:"view")),t.hasClass("clear")&&this.clearDates(),t.hasClass("disabled")||(t.hasClass("month")||t.hasClass("year")||t.hasClass("decade")||t.hasClass("century"))&&(this.viewDate.setUTCDate(1),1===this.viewMode?(i=t.parent().find("span").index(t),n=this.viewDate.getUTCFullYear(),this.viewDate.setUTCMonth(i)):(i=0,n=Number(t.text()),this.viewDate.setUTCFullYear(n)),this._trigger(I.viewModes[this.viewMode-1].e,this.viewDate),this.viewMode===this.o.minViewMode?this._setDate(A(n,i,1)):(this.setViewMode(this.viewMode-1),this.fill())),this.picker.is(":visible")&&this._focused_from&&this._focused_from.focus(),delete this._focused_from},dayCellClick:function(e){var t=E(e.currentTarget).data("date"),n=new Date(t);this.o.updateViewDate&&(n.getUTCFullYear()!==this.viewDate.getUTCFullYear()&&this._trigger("changeYear",this.viewDate),n.getUTCMonth()!==this.viewDate.getUTCMonth()&&this._trigger("changeMonth",this.viewDate)),this._setDate(n)},navArrowsClick:function(e){var t=E(e.currentTarget).hasClass("prev")?-1:1;0!==this.viewMode&&(t*=12*I.viewModes[this.viewMode].navStep),this.viewDate=this.moveMonth(this.viewDate,t),this._trigger(I.viewModes[this.viewMode].e,this.viewDate),this.fill()},_toggle_multidate:function(e){var t=this.dates.contains(e);if(e||this.dates.clear(),-1!==t?(!0===this.o.multidate||1this.o.multidate;)this.dates.remove(0)},_setDate:function(e,t){t&&"date"!==t||this._toggle_multidate(e&&new Date(e)),(!t&&this.o.updateViewDate||"view"===t)&&(this.viewDate=e&&new Date(e)),this.fill(),this.setValue(),t&&"view"===t||this._trigger("changeDate"),this.inputField.trigger("change"),!this.o.autoclose||t&&"date"!==t||this.hide()},moveDay:function(e,t){var n=new Date(e);return n.setUTCDate(e.getUTCDate()+t),n},moveWeek:function(e,t){return this.moveDay(e,7*t)},moveMonth:function(e,t){if(!function(e){return e&&!isNaN(e.getTime())}(e))return this.o.defaultViewDate;if(!t)return e;var n,i,r=new Date(e.valueOf()),a=r.getUTCDate(),o=r.getUTCMonth(),s=Math.abs(t);if(t=0=this.o.startDate&&e<=this.o.endDate},keydown:function(e){if(this.picker.is(":visible")){var t,n,i=!1,r=this.focusDate||this.viewDate;switch(e.keyCode){case 27:this.focusDate?(this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill()):this.hide(),e.preventDefault(),e.stopPropagation();break;case 37:case 38:case 39:case 40:if(!this.o.keyboardNavigation||7===this.o.daysOfWeekDisabled.length)break;t=37===e.keyCode||38===e.keyCode?-1:1,0===this.viewMode?e.ctrlKey?(n=this.moveAvailableDate(r,t,"moveYear"))&&this._trigger("changeYear",this.viewDate):e.shiftKey?(n=this.moveAvailableDate(r,t,"moveMonth"))&&this._trigger("changeMonth",this.viewDate):37===e.keyCode||39===e.keyCode?n=this.moveAvailableDate(r,t,"moveDay"):this.weekOfDateIsDisabled(r)||(n=this.moveAvailableDate(r,t,"moveWeek")):1===this.viewMode?(38!==e.keyCode&&40!==e.keyCode||(t*=4),n=this.moveAvailableDate(r,t,"moveMonth")):2===this.viewMode&&(38!==e.keyCode&&40!==e.keyCode||(t*=4),n=this.moveAvailableDate(r,t,"moveYear")),n&&(this.focusDate=this.viewDate=n,this.setValue(),this.fill(),e.preventDefault());break;case 13:if(!this.o.forceParse)break;r=this.focusDate||this.dates.get(-1)||this.viewDate,this.o.keyboardNavigation&&(this._toggle_multidate(r),i=!0),this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.setValue(),this.fill(),this.picker.is(":visible")&&(e.preventDefault(),e.stopPropagation(),this.o.autoclose&&this.hide());break;case 9:this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill(),this.hide()}i&&(this.dates.length?this._trigger("changeDate"):this._trigger("clearDate"),this.inputField.trigger("change"))}else 40!==e.keyCode&&27!==e.keyCode||(this.show(),e.stopPropagation())},setViewMode:function(e){this.viewMode=e,this.picker.children("div").hide().filter(".datepicker-"+I.viewModes[this.viewMode].clsName).show(),this.updateNavArrows(),this._trigger("changeViewMode",new Date(this.viewDate))}};function u(e,t){E.data(e,"datepicker",this),this.element=E(e),this.inputs=E.map(t.inputs,function(e){return e.jquery?e[0]:e}),delete t.inputs,this.keepEmptyValues=t.keepEmptyValues,delete t.keepEmptyValues,r.call(E(this.inputs),t).on("changeDate",E.proxy(this.dateUpdated,this)),this.pickers=E.map(this.inputs,function(e){return E.data(e,"datepicker")}),this.updateDates()}u.prototype={updateDates:function(){this.dates=E.map(this.pickers,function(e){return e.getUTCDate()}),this.updateRanges()},updateRanges:function(){var n=E.map(this.dates,function(e){return e.valueOf()});E.each(this.pickers,function(e,t){t.setRange(n)})},clearDates:function(){E.each(this.pickers,function(e,t){t.clearDates()})},dateUpdated:function(e){if(!this.updating){this.updating=!0;var n=E.data(e.target,"datepicker");if(n!==M){var i=n.getUTCDate(),r=this.keepEmptyValues,t=E.inArray(e.target,this.inputs),a=t-1,o=t+1,s=this.inputs.length;if(-1!==t){if(E.each(this.pickers,function(e,t){t.getUTCDate()||t!==n&&r||t.setUTCDate(i)}),ithis.dates[o])for(;othis.dates[o];)this.pickers[o++].setUTCDate(i);this.updateDates(),delete this.updating}}}},destroy:function(){E.map(this.pickers,function(e){e.destroy()}),E(this.inputs).off("changeDate",this.dateUpdated),delete this.element.data().datepicker},remove:e("destroy","Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead")};var i=E.fn.datepicker,r=function(o){var s,l=Array.apply(null,arguments);if(l.shift(),this.each(function(){var e=E(this),t=e.data("datepicker"),n="object"==typeof o&&o;if(!t){var i=function(e,t){function n(e,t){return t.toLowerCase()}var i=E(e).data(),r={},a=new RegExp("^"+t.toLowerCase()+"([A-Z])");for(var o in t=new RegExp("^"+t.toLowerCase()),i)t.test(o)&&(r[o.replace(a,n)]=i[o]);return r}(this,"date"),r=function(e){var n={};if(N[e]||(e=e.split("-")[0],N[e])){var i=N[e];return E.each(d,function(e,t){t in i&&(n[t]=i[t])}),n}}(E.extend({},c,i,n).language),a=E.extend({},c,r,i,n);t=e.hasClass("input-daterange")||a.inputs?(E.extend(a,{inputs:a.inputs||e.find("input").toArray()}),new u(this,a)):new w(this,a),e.data("datepicker",t)}"string"==typeof o&&"function"==typeof t[o]&&(s=t[o].apply(t,l))}),s===M||s instanceof w||s instanceof u)return this;if(1(new Date).getFullYear()+t&&(e-=100),e}(t,i):t)},m:function(e,t){if(isNaN(e))return e;for(t-=1;t<0;)t+=12;for(t%=12,e.setUTCMonth(t);e.getUTCMonth()!==t;)e.setUTCDate(e.getUTCDate()-1);return e},d:function(e,t){return e.setUTCDate(t)}};g.yy=g.yyyy,g.M=g.MM=g.mm=g.m,g.dd=g.d,e=O();var v=t.parts.slice();if(a.length!==v.length&&(v=E(v).filter(function(e,t){return-1!==E.inArray(t,m)}).toArray()),a.length===v.length){var y,b,_;for(l=0,y=v.length;l",contTemplate:'',footTemplate:''};I.template='
    ").addClass("cw").text("#"));t.isBefore(this._viewDate.clone().endOf("w"));)e.append(S("").addClass("dow").text(t.format("dd"))),t.add(1,"d");this.widget.find(".datepicker-days thead").append(e)},E.prototype._fillMonths=function(){for(var e=[],t=this._viewDate.clone().startOf("y").startOf("d");t.isSame(this._viewDate,"y");)e.push(S("").attr("data-action","selectMonth").addClass("month").text(t.format("MMM"))),t.add(1,"M");this.widget.find(".datepicker-months td").empty().append(e)},E.prototype._updateMonths=function(){var e=this.widget.find(".datepicker-months"),t=e.find("th"),n=e.find("tbody").find("span"),i=this;t.eq(0).find("span").attr("title",this._options.tooltips.prevYear),t.eq(1).attr("title",this._options.tooltips.selectYear),t.eq(2).find("span").attr("title",this._options.tooltips.nextYear),e.find(".disabled").removeClass("disabled"),this._isValid(this._viewDate.clone().subtract(1,"y"),"y")||t.eq(0).addClass("disabled"),t.eq(1).text(this._viewDate.year()),this._isValid(this._viewDate.clone().add(1,"y"),"y")||t.eq(2).addClass("disabled"),n.removeClass("active"),this._getLastPickedDate().isSame(this._viewDate,"y")&&!this.unset&&n.eq(this._getLastPickedDate().month()).addClass("active"),n.each(function(e){i._isValid(i._viewDate.clone().month(e),"M")||S(this).addClass("disabled")})},E.prototype._getStartEndYear=function(e,t){var n=e/10,i=Math.floor(t/e)*e;return[i,i+9*n,Math.floor(t/n)*n]},E.prototype._updateYears=function(){var e=this.widget.find(".datepicker-years"),t=e.find("th"),n=this._getStartEndYear(10,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),a="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevDecade),t.eq(1).attr("title",this._options.tooltips.selectDecade),t.eq(2).find("span").attr("title",this._options.tooltips.nextDecade),e.find(".disabled").removeClass("disabled"),this._options.minDate&&this._options.minDate.isAfter(i,"y")&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),a+=''+(i.year()-1)+"";!i.isAfter(r,"y");)a+=''+i.year()+"",i.add(1,"y");a+=''+i.year()+"",e.find("td").html(a)},E.prototype._updateDecades=function(){var e=this.widget.find(".datepicker-decades"),t=e.find("th"),n=this._getStartEndYear(100,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),a=!1,o=!1,s=void 0,l="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevCentury),t.eq(2).find("span").attr("title",this._options.tooltips.nextCentury),e.find(".disabled").removeClass("disabled"),(0===i.year()||this._options.minDate&&this._options.minDate.isAfter(i,"y"))&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),i.year()-10<0?l+=" ":l+=''+(i.year()-10)+"";!i.isAfter(r,"y");)s=i.year()+11,a=this._options.minDate&&this._options.minDate.isAfter(i,"y")&&this._options.minDate.year()<=s,o=this._options.maxDate&&this._options.maxDate.isAfter(i,"y")&&this._options.maxDate.year()<=s,l+=''+i.year()+"",i.add(10,"y");l+=''+i.year()+"",e.find("td").html(l)},E.prototype._fillDate=function(){var e=this.widget.find(".datepicker-days"),t=e.find("th"),n=[],i=void 0,r=void 0,a=void 0,o=void 0;if(this._hasDate()){for(t.eq(0).find("span").attr("title",this._options.tooltips.prevMonth),t.eq(1).attr("title",this._options.tooltips.selectMonth),t.eq(2).find("span").attr("title",this._options.tooltips.nextMonth),e.find(".disabled").removeClass("disabled"),t.eq(1).text(this._viewDate.format(this._options.dayViewHeaderFormat)),this._isValid(this._viewDate.clone().subtract(1,"M"),"M")||t.eq(0).addClass("disabled"),this._isValid(this._viewDate.clone().add(1,"M"),"M")||t.eq(2).addClass("disabled"),i=this._viewDate.clone().startOf("M").startOf("w").startOf("d"),o=0;o<42;o++){if(0===i.weekday()&&(r=S("
    '+i.week()+"'+i.date()+"
    '+t.format(this.use24Hours?"HH":"hh")+"
    '+t.format("mm")+"
    '+t.format("ss")+"
     
    '+x+"'+C+"
    '+c.templates.leftArrow+''+c.templates.rightArrow+"
    '+I.headTemplate+""+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+"
    ",E.fn.datepicker.DPGlobal=I,E.fn.datepicker.noConflict=function(){return E.fn.datepicker=i,this},E.fn.datepicker.version="1.9.0",E.fn.datepicker.deprecated=function(e){var t=window.console;t&&t.warn&&t.warn("DEPRECATED: "+e)},E(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(e){var t=E(this);t.data("datepicker")||(e.preventDefault(),r.call(t,"show"))}),E(function(){r.call(E('[data-provide="datepicker-inline"]'))})}),jQuery.fn.datepicker.dates.fa={days:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"],daysShort:["یک","دو","سه","چهار","پنج","جمعه","شنبه","یک"],daysMin:["ی","د","س","چ","پ","ج","ش","ی"],months:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthsShort:["ژان","فور","مار","آور","مه","ژون","ژوی","اوت","سپت","اکت","نوا","دسا"],today:"امروز",clear:"پاک کن",weekStart:1,format:"yyyy/mm/dd"},jQuery.fn.datepicker.dates.fr={days:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],daysShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],daysMin:["d","l","ma","me","j","v","s"],months:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthsShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd/mm/yyyy"},jQuery.fn.datepicker.dates.ru={days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вск","Пнд","Втр","Срд","Чтв","Птн","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Месяцы"},jQuery.fn.datepicker.dates.sv={days:["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"],daysShort:["sön","mån","tis","ons","tor","fre","lör"],daysMin:["sö","må","ti","on","to","fr","lö"],months:["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],today:"Idag",format:"yyyy-mm-dd",weekStart:1,clear:"Rensa"},jQuery.fn.datepicker.dates["zh-CN"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["周日","周一","周二","周三","周四","周五","周六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今天",monthsTitle:"选择月份",clear:"清除",format:"yyyy-mm-dd",titleFormat:"yyyy年mm月",weekStart:1};var Menu={init:function(){$(function(){Menu.itemClick()})},itemClick:function(){$(".menu-button").click(function(e){e.preventDefault(),$(".menu-item").is(":visible")?$(".menu-item").css("display",""):$(".menu-item").show()})}};Menu.init(),ko.bindingHandlers.modal={init:function(e,t){$(e).modal({show:!1});var n=t();ko.isObservable(n)&&$(e).on("hidden.bs.modal",function(){n(!1)})},update:function(e,t){var n=t();ko.utils.unwrapObservable(n)?$(e).modal("show"):$(e).modal("hide")}},ko.components.register("picker",{viewModel:function(n){var i=this;this.textTerm=ko.observable("").extend({rateLimit:500}),this.minSearchText=ko.observable(n.minSearchText||2),this.multipleSelect=ko.observable(n.multipleSelect||!1),this.searchInputPlaceholder=ko.observable(n.searchInputPlaceholder||"Enter "+this.minSearchText()+" or more characters"),this.selectedItemsTitle=ko.observable(n.selectedItemsTitle||"Selected: "),this.searchResultTitle=ko.observable(n.searchResultTitle||"Search result: "),this.suggestedItemsTitle=ko.observable(n.suggestedItemsTitle||"Suggested items: "),this.noItemSelectedTitle=ko.observable(n.noItemSelectedTitle||"No item/s selected"),this.showAllItemsTitle=ko.observable(n.showAllItemsTitle||"more"),this.allowSuggestedItems=ko.observable(n.allowSuggestedItems&&n.url||!1),this.topSuggestedItems=ko.observable(n.topSuggestedItems||5),this.allowItemAlreadySelectedNotification=ko.observable(n.allowItemAlreadySelectedNotification||!0),this.itemAlreadySelectedTitle=ko.observable(n.itemAlreadySelectedTitle||"item already selected"),this.searchResult=ko.observableArray([]),this.selectedResult=ko.observableArray(n.selectedItems||[]),this.suggestedResult=ko.observableArray([]),this.loading=ko.observable(!1),this.isVisibleEditDialog=ko.observable(!1),this.editedItem=ko.observable(""),this.editedItemOriginal=ko.observable("");var e=ko.toJSON(this.selectedResult);!0===this.multipleSelect()?0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(e):0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(this.selectedResult()[0]),this.textTerm.subscribe(function(t){""===t.trim()&&i.searchResult([]),""!==t.trim()&&t.trim().length>=i.minSearchText()&&(n.url?(i.loading(!0),$.get(n.url+"="+t,function(e){-1===e.indexOf(t)&&e.push(t),i.searchResult(e),i.loading(!1)})):i.searchResult([t]))}),this.notify=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.info(e+" "+this.itemAlreadySelectedTitle())},this.notifyError=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.error(e)},this.add=function(e){e=e.replace(/'/g,"").replace(/"/g,""),-1

    Loading..

    \x3c!-- ko foreach: suggestedResult --\x3e\x3c!-- /ko --\x3e
    '}),ko.applyBindings(),Holder.addTheme("thumb",{bg:"#55595c",fg:"#eceeef",text:"Thumbnail"});var FormMvc={allowValidateHiddenField:function(e){e.data("validator").settings.ignore=""},disableEnter:function(e){e.on("keyup keypress",function(e){if(13===(e.keyCode||e.which))return e.preventDefault(),!1})}};$(function(){$(".single-select").removeAttr("multiple"),$('[data-toggle="tooltip"]').tooltip()});var JSONTree=function(){var t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},e=0,n=0;this.create=function(e,t){return n+=1,p(s(e,0,!1),{class:"jstValue"})};function a(e){return e.replace(/[&<>'"]/g,function(e){return t[e]})}function o(){return n+"_"+e++}var s=function(e,t,n){if(null===e)return d(n?t:0);switch(typeof e){case"boolean":return c(e,n?t:0);case"number":return u(e,n?t:0);case"string":return l(e,n?t:0);default:return e instanceof Array?r(e,t,n):i(e,t,n)}},i=function(t,n,e){var i=o(),r=Object.keys(t).map(function(e){return h(e,t[e],n+1,!0)}).join(f()),a=[g("{",e?n:0,i),p(r,{id:i}),v("}",n)].join("\n");return p(a,{})},r=function(e,t,n){var i=o(),r=e.map(function(e){return s(e,t+1,!0)}).join(f());return[g("[",n?t:0,i),p(r,{id:i}),v("]",t)].join("\n")},l=function(e,t){var n=a(JSON.stringify(e));return p(y(n,t),{class:"jstStr"})},u=function(e,t){return p(y(e,t),{class:"jstNum"})},c=function(e,t){return p(y(e,t),{class:"jstBool"})},d=function(e){return p(y("null",e),{class:"jstNull"})},h=function(e,t,n){var i=y(a(JSON.stringify(e))+": ",n),r=p(s(t,n,!1),{});return p(i+r,{class:"jstProperty"})},f=function(){return p(",\n",{class:"jstComma"})},p=function(e,t){return m("span",t,e)},m=function(e,t,n){return"<"+e+Object.keys(t).map(function(e){return" "+e+'="'+t[e]+'"'}).join("")+">"+n+""},g=function(e,t,n){return p(y(e,t),{class:"jstBracket"})+p("",{class:"jstFold",onclick:"JSONTree.toggle('"+n+"')"})};this.toggle=function(e){var t=document.getElementById(e),n=t.parentNode,i=t.previousElementSibling;""===t.className?(t.className="jstHiddenBlock",n.className="jstFolded",i.className="jstExpand"):(t.className="",n.className="",i.className="jstFold")};var v=function(e,t){return p(y(e,t),{})},y=function(e,t){return Array(2*t+1).join(" ")+e};return this}();$(function(){$(".local-datetime").each(function(){var e=$(this),t=parseInt(e.attr("data-utc"),10)||0;if(t){var n=moment.utc(t).local().format("DD MMM YYYY HH:mm");e.text(n)}}),$('[data-toggle="tooltip"]').tooltip()});var errorLog={eventHandlers:function(){$(".error-log-delete-button").click(function(){return $(".error-log-form").validate(),$(".error-log-form").validate().form()?$("#deleteLogsModal").modal("show"):$(this).submit(),!1}),$(".row-error-detail>td").each(function(){var t,n=$(this).data("error-json");try{t=JSONTree.create(JSON.parse(n))}catch(e){t=JSONTree.create(n)}$(this).html(t)}),$(".btn-error-detail").click(function(e){e.preventDefault();var t=$(this).data("error-id");return $(".row-error-detail[data-error-id="+t+"]").is(":visible")?$(".row-error-detail[data-error-id="+t+"]").addClass("d-none"):$(".row-error-detail[data-error-id="+t+"]").removeClass("d-none"),!1})},init:function(){$(function(){errorLog.eventHandlers()})}};errorLog.init();var auditLog={createJsonTree:function(t){var n;try{n=JSONTree.create(JSON.parse(t))}catch(e){n=JSONTree.create(t)}return n},initJsonTrees:function(){$(".json-tree").each(function(){var e=$(this).data("json-tree"),t=auditLog.createJsonTree(e);$(this).html(t)})},eventHandlers:function(){$(".audit-subject-button").click(function(){var e=$(this).data("subject-identifier"),t=$(this).data("subject-name"),n=$(this).data("subject-type"),i=$(this).data("subject-additional-data");$(".modal-title").html(t+" - "+e+" - ("+n+")"),$(".audit-modal-value").html(auditLog.createJsonTree(i)),$(".audit-modal").modal("show")}),$(".audit-action-button").click(function(){var e=$(this).data("action"),t=$(this).data("action-title");$(".modal-title").html(t),$(".audit-modal-value").html(auditLog.createJsonTree(e)),$(".audit-modal").modal("show")}),$(".audit-log-delete-button").click(function(){return $(".audit-log-form").validate(),$(".audit-log-form").validate().form()?$("#deleteLogsModal").modal("show"):$(this).submit(),!1})},init:function(){$(function(){auditLog.eventHandlers(),auditLog.initJsonTrees()})}};auditLog.init(),$(function(){var e={guid:function(){return"ss-s-s-s-sss".replace(/s/g,e.s4)},s4:function(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)},eventHandlers:function(){$("#generate-guid-button").click(function(){$("#secret-input").val(e.guid())}),$(".secret-value-button").click(function(){var e=$(this).data("secret-value");$(".modal-secret-value").html(e),$(".secret-modal").modal("show")})},init:function(){e.eventHandlers()}};e.init()}),$(function(){var t={getCookie:function(e){for(var t=e+"=",n=document.cookie.split(";"),i=0;i .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - 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 #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\2a"; -} -.glyphicon-plus:before { - content: "\2b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .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 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .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 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .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 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 14.333333px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled, -.btn-default[disabled], -fieldset[disabled] .btn-default, -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus, -.btn-default.disabled:active, -.btn-default[disabled]:active, -fieldset[disabled] .btn-default:active, -.btn-default.disabled.active, -.btn-default[disabled].active, -fieldset[disabled] .btn-default.active { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled, -.btn-primary[disabled], -fieldset[disabled] .btn-primary, -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus, -.btn-primary.disabled:active, -.btn-primary[disabled]:active, -fieldset[disabled] .btn-primary:active, -.btn-primary.disabled.active, -.btn-primary[disabled].active, -fieldset[disabled] .btn-primary.active { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled, -.btn-success[disabled], -fieldset[disabled] .btn-success, -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus, -.btn-success.disabled:active, -.btn-success[disabled]:active, -fieldset[disabled] .btn-success:active, -.btn-success.disabled.active, -.btn-success[disabled].active, -fieldset[disabled] .btn-success.active { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled, -.btn-info[disabled], -fieldset[disabled] .btn-info, -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus, -.btn-info.disabled:active, -.btn-info[disabled]:active, -fieldset[disabled] .btn-info:active, -.btn-info.disabled.active, -.btn-info[disabled].active, -fieldset[disabled] .btn-info.active { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled, -.btn-warning[disabled], -fieldset[disabled] .btn-warning, -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus, -.btn-warning.disabled:active, -.btn-warning[disabled]:active, -fieldset[disabled] .btn-warning:active, -.btn-warning.disabled.active, -.btn-warning[disabled].active, -fieldset[disabled] .btn-warning.active { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled, -.btn-danger[disabled], -fieldset[disabled] .btn-danger, -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus, -.btn-danger.disabled:active, -.btn-danger[disabled]:active, -fieldset[disabled] .btn-danger:active, -.btn-danger.disabled.active, -.btn-danger[disabled].active, -fieldset[disabled] .btn-danger.active { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 3; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 2; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.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-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - min-height: 16.42857143px; - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -15px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -15px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -15px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map b/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map deleted file mode 100644 index 9f60ed2b1..000000000 --- a/src/ModularMonolith/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,eAAA;CH8O9C;AG7OmC;EAAW,eAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EErDA,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNqkCD;AIxgCD;EACE,UAAA;CJ0gCD;AIpgCD;EACE,uBAAA;CJsgCD;AIlgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CPglCD;AItgCD;EACE,mBAAA;CJwgCD;AIlgCD;EACE,aAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CPgmCD;AIlgCD;EACE,mBAAA;CJogCD;AI9/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJggCD;AIx/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJ0/BD;AIl/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJo/BH;AIz+BD;EACE,gBAAA;CJ2+BD;AQloCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR8oCD;AQnpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRoqCH;AQhqCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRqqCD;AQzqCD;;;;;;;;;;;;EAQI,eAAA;CR+qCH;AQ5qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRirCD;AQrrCD;;;;;;;;;;;;EAQI,eAAA;CR2rCH;AQvrCD;;EAAU,gBAAA;CR2rCT;AQ1rCD;;EAAU,gBAAA;CR8rCT;AQ7rCD;;EAAU,gBAAA;CRisCT;AQhsCD;;EAAU,gBAAA;CRosCT;AQnsCD;;EAAU,gBAAA;CRusCT;AQtsCD;;EAAU,gBAAA;CR0sCT;AQpsCD;EACE,iBAAA;CRssCD;AQnsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRqsCD;AQhsCD;EAAA;IAFI,gBAAA;GRssCD;CACF;AQ9rCD;;EAEE,eAAA;CRgsCD;AQ7rCD;;EAEE,0BAAA;EACA,cAAA;CR+rCD;AQ3rCD;EAAuB,iBAAA;CR8rCtB;AQ7rCD;EAAuB,kBAAA;CRgsCtB;AQ/rCD;EAAuB,mBAAA;CRksCtB;AQjsCD;EAAuB,oBAAA;CRosCtB;AQnsCD;EAAuB,oBAAA;CRssCtB;AQnsCD;EAAuB,0BAAA;CRssCtB;AQrsCD;EAAuB,0BAAA;CRwsCtB;AQvsCD;EAAuB,2BAAA;CR0sCtB;AQvsCD;EACE,eAAA;CRysCD;AQvsCD;ECrGE,eAAA;CT+yCD;AS9yCC;;EAEE,eAAA;CTgzCH;AQ3sCD;ECxGE,eAAA;CTszCD;ASrzCC;;EAEE,eAAA;CTuzCH;AQ/sCD;EC3GE,eAAA;CT6zCD;AS5zCC;;EAEE,eAAA;CT8zCH;AQntCD;EC9GE,eAAA;CTo0CD;ASn0CC;;EAEE,eAAA;CTq0CH;AQvtCD;ECjHE,eAAA;CT20CD;AS10CC;;EAEE,eAAA;CT40CH;AQvtCD;EAGE,YAAA;EE3HA,0BAAA;CVm1CD;AUl1CC;;EAEE,0BAAA;CVo1CH;AQztCD;EE9HE,0BAAA;CV01CD;AUz1CC;;EAEE,0BAAA;CV21CH;AQ7tCD;EEjIE,0BAAA;CVi2CD;AUh2CC;;EAEE,0BAAA;CVk2CH;AQjuCD;EEpIE,0BAAA;CVw2CD;AUv2CC;;EAEE,0BAAA;CVy2CH;AQruCD;EEvIE,0BAAA;CV+2CD;AU92CC;;EAEE,0BAAA;CVg3CH;AQpuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRsuCD;AQ9tCD;;EAEE,cAAA;EACA,oBAAA;CRguCD;AQnuCD;;;;EAMI,iBAAA;CRmuCH;AQ5tCD;EACE,gBAAA;EACA,iBAAA;CR8tCD;AQ1tCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR6tCD;AQ/tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR6tCH;AQxtCD;EACE,cAAA;EACA,oBAAA;CR0tCD;AQxtCD;;EAEE,wBAAA;CR0tCD;AQxtCD;EACE,kBAAA;CR0tCD;AQxtCD;EACE,eAAA;CR0tCD;AQjsCD;EAAA;IAVM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXs6CC;EQ3sCH;IAHM,mBAAA;GRitCH;CACF;AQxsCD;;EAGE,aAAA;EACA,kCAAA;CRysCD;AQvsCD;EACE,eAAA;EA9IqB,0BAAA;CRw1CtB;AQrsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRusCD;AQlsCG;;;EACE,iBAAA;CRssCL;AQhtCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRksCH;AQhsCG;;;EACE,uBAAA;CRosCL;AQ5rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR8rCD;AQxrCG;;;;;;EAAW,YAAA;CRgsCd;AQ/rCG;;;;;;EACE,uBAAA;CRssCL;AQhsCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRksCD;AYx+CD;;;;EAIE,+DAAA;CZ0+CD;AYt+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZw+CD;AYp+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZs+CD;AY5+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZs+CH;AYj+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;CZm+CD;AY9+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZk+CH;AY79CD;EACE,kBAAA;EACA,mBAAA;CZ+9CD;AazhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd+hDD;AazhDC;EAAA;IAFE,aAAA;Gb+hDD;CACF;Aa3hDC;EAAA;IAFE,aAAA;GbiiDD;CACF;Aa7hDD;EAAA;IAFI,cAAA;GbmiDD;CACF;Aa1hDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdojDD;AavhDD;ECvBE,mBAAA;EACA,oBAAA;CdijDD;AejjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfijDL;AejiDG;EACE,YAAA;CfmiDL;Ae5hDC;EACE,YAAA;Cf8hDH;Ae/hDC;EACE,oBAAA;CfiiDH;AeliDC;EACE,oBAAA;CfoiDH;AeriDC;EACE,WAAA;CfuiDH;AexiDC;EACE,oBAAA;Cf0iDH;Ae3iDC;EACE,oBAAA;Cf6iDH;Ae9iDC;EACE,WAAA;CfgjDH;AejjDC;EACE,oBAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,WAAA;CfyjDH;Ae1jDC;EACE,oBAAA;Cf4jDH;Ae7jDC;EACE,mBAAA;Cf+jDH;AejjDC;EACE,YAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,oBAAA;CfyjDH;Ae1jDC;EACE,WAAA;Cf4jDH;Ae7jDC;EACE,oBAAA;Cf+jDH;AehkDC;EACE,oBAAA;CfkkDH;AenkDC;EACE,WAAA;CfqkDH;AetkDC;EACE,oBAAA;CfwkDH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBsrED;AgBzrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,8BAAA;ChBsrEP;AgBpsED;EAoBI,uBAAA;EACA,iCAAA;ChBmrEH;AgBxsED;;;;;;EA8BQ,cAAA;ChBkrEP;AgBhtED;EAoCI,8BAAA;ChB+qEH;AgBntED;EAyCI,0BAAA;ChB6qEH;AgBtqED;;;;;;EAOQ,aAAA;ChBuqEP;AgB5pED;EACE,0BAAA;ChB8pED;AgB/pED;;;;;;EAQQ,0BAAA;ChB+pEP;AgBvqED;;EAeM,yBAAA;ChB4pEL;AgBlpED;EAEI,0BAAA;ChBmpEH;AgB1oED;EAEI,0BAAA;ChB2oEH;AgBloED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBooED;AgB/nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBkoEL;AiB9wEC;;;;;;;;;;;;EAOI,0BAAA;CjBqxEL;AiB/wEC;;;;;EAMI,0BAAA;CjBgxEL;AiBnyEC;;;;;;;;;;;;EAOI,0BAAA;CjB0yEL;AiBpyEC;;;;;EAMI,0BAAA;CjBqyEL;AiBxzEC;;;;;;;;;;;;EAOI,0BAAA;CjB+zEL;AiBzzEC;;;;;EAMI,0BAAA;CjB0zEL;AiB70EC;;;;;;;;;;;;EAOI,0BAAA;CjBo1EL;AiB90EC;;;;;EAMI,0BAAA;CjB+0EL;AiBl2EC;;;;;;;;;;;;EAOI,0BAAA;CjBy2EL;AiBn2EC;;;;;EAMI,0BAAA;CjBo2EL;AgBltED;EACE,iBAAA;EACA,kBAAA;ChBotED;AgBvpED;EAAA;IA1DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,0BAAA;GhBqtED;EgB/pEH;IAlDM,iBAAA;GhBotEH;EgBlqEH;;;;;;IAzCY,oBAAA;GhBmtET;EgB1qEH;IAjCM,UAAA;GhB8sEH;EgB7qEH;;;;;;IAxBY,eAAA;GhB6sET;EgBrrEH;;;;;;IApBY,gBAAA;GhBitET;EgB7rEH;;;;IAPY,iBAAA;GhB0sET;CACF;AkBp6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBm6ED;AkBh6ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBk6ED;AkB/5ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBi6ED;AkBt5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL63ET;AkBt5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBw5ED;AkBr5ED;EACE,eAAA;ClBu5ED;AkBn5ED;EACE,eAAA;EACA,YAAA;ClBq5ED;AkBj5ED;;EAEE,aAAA;ClBm5ED;AkB/4ED;;;EZvEE,qBAAA;EAEA,2CAAA;EACA,qBAAA;CN09ED;AkB/4ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClBi5ED;AkBv3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,0BAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CL0zET;AmBl8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CL27ET;AK15EC;EACE,eAAA;EACA,WAAA;CL45EH;AK15EC;EAA0B,eAAA;CL65E3B;AK55EC;EAAgC,eAAA;CL+5EjC;AkB/3EC;;;EAGE,0BAAA;EACA,WAAA;ClBi4EH;AkB93EC;;EAEE,oBAAA;ClBg4EH;AkB53EC;EACE,aAAA;ClB83EH;AkBl3ED;EACE,yBAAA;ClBo3ED;AkB50ED;EAtBI;;;;IACE,kBAAA;GlBw2EH;EkBr2EC;;;;;;;;IAEE,kBAAA;GlB62EH;EkB12EC;;;;;;;;IAEE,kBAAA;GlBk3EH;CACF;AkBx2ED;EACE,oBAAA;ClB02ED;AkBl2ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBo2ED;AkBz2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBq2EH;AkBl2ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBo2ED;AkBj2ED;;EAEE,iBAAA;ClBm2ED;AkB/1ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2ED;AkB/1ED;;EAEE,cAAA;EACA,kBAAA;ClBi2ED;AkBx1EC;;;;;;EAGE,oBAAA;ClB61EH;AkBv1EC;;;;EAEE,oBAAA;ClB21EH;AkBr1EC;;;;EAGI,oBAAA;ClBw1EL;AkB70ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClB60ED;AkB30EC;;EAEE,gBAAA;EACA,iBAAA;ClB60EH;AkBh0ED;EC7PE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBgkFD;AmB9jFC;EACE,aAAA;EACA,kBAAA;CnBgkFH;AmB7jFC;;EAEE,aAAA;CnB+jFH;AkB50ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClB60EH;AkBn1ED;EASI,aAAA;EACA,kBAAA;ClB60EH;AkBv1ED;;EAcI,aAAA;ClB60EH;AkB31ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClB60EH;AkBz0ED;ECzRE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBqmFD;AmBnmFC;EACE,aAAA;EACA,kBAAA;CnBqmFH;AmBlmFC;;EAEE,aAAA;CnBomFH;AkBr1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBs1EH;AkB51ED;EASI,aAAA;EACA,kBAAA;ClBs1EH;AkBh2ED;;EAcI,aAAA;ClBs1EH;AkBp2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBs1EH;AkB70ED;EAEE,mBAAA;ClB80ED;AkBh1ED;EAMI,sBAAA;ClB60EH;AkBz0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBv0ED;;;;;;;;;;ECpZI,eAAA;CnBuuFH;AkBn1ED;EChZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwrFT;AmBtuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6rFT;AkB71ED;ECtYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBsuFH;AkBl2ED;EChYI,eAAA;CnBquFH;AkBl2ED;;;;;;;;;;ECvZI,eAAA;CnBqwFH;AkB92ED;ECnZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLstFT;AmBpwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2tFT;AkBx3ED;ECzYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBowFH;AkB73ED;ECnYI,eAAA;CnBmwFH;AkB73ED;;;;;;;;;;EC1ZI,eAAA;CnBmyFH;AkBz4ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLovFT;AmBlyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CLyvFT;AkBn5ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBkyFH;AkBx5ED;ECtYI,eAAA;CnBiyFH;AkBp5EC;EACG,UAAA;ClBs5EJ;AkBp5EC;EACG,OAAA;ClBs5EJ;AkB54ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB84ED;AkB3zED;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB63EH;EkBj0EH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB23EH;EkBt0EH;IAhDM,sBAAA;GlBy3EH;EkBz0EH;IA5CM,sBAAA;IACA,uBAAA;GlBw3EH;EkB70EH;;;IAtCQ,YAAA;GlBw3EL;EkBl1EH;IAhCM,YAAA;GlBq3EH;EkBr1EH;IA5BM,iBAAA;IACA,uBAAA;GlBo3EH;EkBz1EH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBi3EH;EkBh2EH;;IAdQ,gBAAA;GlBk3EL;EkBp2EH;;IATM,mBAAA;IACA,eAAA;GlBi3EH;EkBz2EH;IAHM,OAAA;GlB+2EH;CACF;AkBr2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClBk2EH;AkB72ED;;EAiBI,iBAAA;ClBg2EH;AkBj3ED;EJhhBE,mBAAA;EACA,oBAAA;Cdo4FD;AkB90EC;EAAA;IAVI,kBAAA;IACA,iBAAA;IACA,iBAAA;GlB41EH;CACF;AkB53ED;EAwCI,YAAA;ClBu1EH;AkBz0EC;EAAA;IAJM,yBAAA;IACA,gBAAA;GlBi1EL;CACF;AkBv0EC;EAAA;IAJM,iBAAA;IACA,gBAAA;GlB+0EL;CACF;AoBl6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC6CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB4JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL6tFT;AoBr6FG;;;;;;EdrBF,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNi8FD;AoBz6FC;;;EAGE,eAAA;EACA,sBAAA;CpB26FH;AoBx6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLg5FT;AoBx6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CLy5FT;AoBx6FG;;EAEE,qBAAA;CpB06FL;AoBj6FD;EC3DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrB+9FD;AqB79FC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBq+FT;AqBl+FC;;;EAGE,uBAAA;CrBo+FH;AqB/9FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB6+FT;AoB/9FD;ECTI,eAAA;EACA,0BAAA;CrB2+FH;AoBh+FD;EC9DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBiiGD;AqB/hGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuiGT;AqBpiGC;;;EAGE,uBAAA;CrBsiGH;AqBjiGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB+iGT;AoB9hGD;ECZI,eAAA;EACA,0BAAA;CrB6iGH;AoB9hGD;EClEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBmmGD;AqBjmGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBymGT;AqBtmGC;;;EAGE,uBAAA;CrBwmGH;AqBnmGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBinGT;AoB5lGD;EChBI,eAAA;EACA,0BAAA;CrB+mGH;AoB5lGD;ECtEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBqqGD;AqBnqGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB2qGT;AqBxqGC;;;EAGE,uBAAA;CrB0qGH;AqBrqGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBmrGT;AoB1pGD;ECpBI,eAAA;EACA,0BAAA;CrBirGH;AoB1pGD;EC1EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBuuGD;AqBruGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB6uGT;AqB1uGC;;;EAGE,uBAAA;CrB4uGH;AqBvuGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBqvGT;AoBxtGD;ECxBI,eAAA;EACA,0BAAA;CrBmvGH;AoBxtGD;EC9EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrByyGD;AqBvyGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+yGT;AqB5yGC;;;EAGE,uBAAA;CrB8yGH;AqBzyGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBuzGT;AoBtxGD;EC5BI,eAAA;EACA,0BAAA;CrBqzGH;AoBjxGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpBmxGD;AoBjxGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLuzGT;AoBlxGC;;;;EAIE,0BAAA;CpBoxGH;AoBlxGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpBoxGH;AoBhxGG;;;;EAEE,eAAA;EACA,sBAAA;CpBoxGL;AoB3wGD;;ECrEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBo1GD;AoB9wGD;;ECzEE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrB21GD;AoBjxGD;;EC7EE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBk2GD;AoBhxGD;EACE,eAAA;EACA,YAAA;CpBkxGD;AoB9wGD;EACE,gBAAA;CpBgxGD;AoBzwGC;;;EACE,YAAA;CpB6wGH;AuBv6GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLsvGT;AuB16GC;EACE,WAAA;CvB46GH;AuBx6GD;EACE,cAAA;CvB06GD;AuBx6GC;EAAY,eAAA;CvB26Gb;AuB16GC;EAAY,mBAAA;CvB66Gb;AuB56GC;EAAY,yBAAA;CvB+6Gb;AuB56GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CLgwGT;AwB18GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxB48GD;AwBx8GD;;EAEE,mBAAA;CxB08GD;AwBt8GD;EACE,WAAA;CxBw8GD;AwBp8GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,0BAAA;EACA,0BAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBu8GD;AwBl8GC;EACE,SAAA;EACA,WAAA;CxBo8GH;AwB79GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBy/GD;AwBn+GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBm8GH;AwB77GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB+7GH;AwBz7GC;;;EAGE,eAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxB27GH;AwBl7GC;;;EAGE,eAAA;CxBo7GH;AwBh7GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxBk7GH;AwB76GD;EAGI,eAAA;CxB66GH;AwBh7GD;EAQI,WAAA;CxB26GH;AwBn6GD;EACE,WAAA;EACA,SAAA;CxBq6GD;AwB75GD;EACE,QAAA;EACA,YAAA;CxB+5GD;AwB35GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB65GD;AwBz5GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxB25GD;AwBv5GD;EACE,SAAA;EACA,WAAA;CxBy5GD;AwBj5GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxBi5GH;AwBx5GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxBi5GH;AwB53GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB+8GC;EwB54GD;IA1DA,QAAA;IACA,YAAA;GxBy8GC;CACF;A2BzlHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3B2lHD;A2B/lHD;;EAMI,mBAAA;EACA,YAAA;C3B6lHH;A2B3lHG;;;;;;;;EAIE,WAAA;C3BimHL;A2B3lHD;;;;EAKI,kBAAA;C3B4lHH;A2BvlHD;EACE,kBAAA;C3BylHD;A2B1lHD;;;EAOI,YAAA;C3BwlHH;A2B/lHD;;;EAYI,iBAAA;C3BwlHH;A2BplHD;EACE,iBAAA;C3BslHD;A2BllHD;EACE,eAAA;C3BolHD;A2BnlHC;EClDA,8BAAA;EACG,2BAAA;C5BwoHJ;A2BllHD;;EC/CE,6BAAA;EACG,0BAAA;C5BqoHJ;A2BjlHD;EACE,YAAA;C3BmlHD;A2BjlHD;EACE,iBAAA;C3BmlHD;A2BjlHD;;ECnEE,8BAAA;EACG,2BAAA;C5BwpHJ;A2BhlHD;ECjEE,6BAAA;EACG,0BAAA;C5BopHJ;A2B/kHD;;EAEE,WAAA;C3BilHD;A2BhkHD;EACE,kBAAA;EACA,mBAAA;C3BkkHD;A2BhkHD;EACE,mBAAA;EACA,oBAAA;C3BkkHD;A2B7jHD;EtB/CE,yDAAA;EACQ,iDAAA;CL+mHT;A2B7jHC;EtBnDA,yBAAA;EACQ,iBAAA;CLmnHT;A2B1jHD;EACE,eAAA;C3B4jHD;A2BzjHD;EACE,wBAAA;EACA,uBAAA;C3B2jHD;A2BxjHD;EACE,wBAAA;C3B0jHD;A2BnjHD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3BojHH;A2B3jHD;EAcM,YAAA;C3BgjHL;A2B9jHD;;;;EAsBI,iBAAA;EACA,eAAA;C3B8iHH;A2BziHC;EACE,iBAAA;C3B2iHH;A2BziHC;EACE,6BAAA;ECpKF,8BAAA;EACC,6BAAA;C5BgtHF;A2B1iHC;EACE,+BAAA;EChLF,2BAAA;EACC,0BAAA;C5B6tHF;A2B1iHD;EACE,iBAAA;C3B4iHD;A2B1iHD;;EC/KE,8BAAA;EACC,6BAAA;C5B6tHF;A2BziHD;EC7LE,2BAAA;EACC,0BAAA;C5ByuHF;A2BriHD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3BuiHD;A2B3iHD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3BwiHH;A2BjjHD;EAYI,YAAA;C3BwiHH;A2BpjHD;EAgBI,WAAA;C3BuiHH;A2BthHD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3BuhHL;A6BjwHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BmwHD;A6BhwHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7BkwHH;A6B3wHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7B0vHH;A6BjvHD;;;EV8BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwtHD;AmBttHC;;;EACE,aAAA;EACA,kBAAA;CnB0tHH;AmBvtHC;;;;;;EAEE,aAAA;CnB6tHH;A6BnwHD;;;EVyBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+uHD;AmB7uHC;;;EACE,aAAA;EACA,kBAAA;CnBivHH;AmB9uHC;;;;;;EAEE,aAAA;CnBovHH;A6BjxHD;;;EAGE,oBAAA;C7BmxHD;A6BjxHC;;;EACE,iBAAA;C7BqxHH;A6BjxHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7BmxHD;A6B9wHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;C7BgxHD;A6B7wHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6B7wHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6BnyHD;;EA0BI,cAAA;C7B6wHH;A6BxwHD;;;;;;;EDhGE,8BAAA;EACG,2BAAA;C5Bi3HJ;A6BzwHD;EACE,gBAAA;C7B2wHD;A6BzwHD;;;;;;;EDpGE,6BAAA;EACG,0BAAA;C5Bs3HJ;A6B1wHD;EACE,eAAA;C7B4wHD;A6BvwHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BuwHD;A6B5wHD;EAUI,mBAAA;C7BqwHH;A6B/wHD;EAYM,kBAAA;C7BswHL;A6BnwHG;;;EAGE,WAAA;C7BqwHL;A6BhwHC;;EAGI,mBAAA;C7BiwHL;A6B9vHC;;EAGI,WAAA;EACA,kBAAA;C7B+vHL;A8B15HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B45HD;A8B/5HD;EAOI,mBAAA;EACA,eAAA;C9B25HH;A8Bn6HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B25HL;A8B15HK;;EAEE,sBAAA;EACA,0BAAA;C9B45HP;A8Bv5HG;EACE,eAAA;C9By5HL;A8Bv5HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By5HP;A8Bl5HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo5HL;A8B77HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm8HD;A8Bn8HD;EA0DI,gBAAA;C9B44HH;A8Bn4HD;EACE,iCAAA;C9Bq4HD;A8Bt4HD;EAGI,YAAA;EAEA,oBAAA;C9Bq4HH;A8B14HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo4HL;A8Bn4HK;EACE,sCAAA;C9Bq4HP;A8B/3HK;;;EAGE,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,iCAAA;EACA,gBAAA;C9Bi4HP;A8B53HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6yHD;A8Bh4HC;EAwDE,YAAA;C9B20HH;A8Bn4HC;EA0DI,mBAAA;EACA,mBAAA;C9B40HL;A8Bv4HC;EAgEE,UAAA;EACA,WAAA;C9B00HH;A8B9zHD;EAAA;IAPM,oBAAA;IACA,UAAA;G9By0HH;E8Bn0HH;IAJQ,iBAAA;G9B00HL;CACF;A8Bp5HC;EAuFE,gBAAA;EACA,mBAAA;C9Bg0HH;A8Bx5HC;;;EA8FE,0BAAA;C9B+zHH;A8BjzHD;EAAA;IATM,iCAAA;IACA,2BAAA;G9B8zHH;E8BtzHH;;;IAHM,6BAAA;G9B8zHH;CACF;A8B/5HD;EAEI,YAAA;C9Bg6HH;A8Bl6HD;EAMM,mBAAA;C9B+5HL;A8Br6HD;EASM,iBAAA;C9B+5HL;A8B15HK;;;EAGE,eAAA;EACA,0BAAA;C9B45HP;A8Bp5HD;EAEI,YAAA;C9Bq5HH;A8Bv5HD;EAIM,gBAAA;EACA,eAAA;C9Bs5HL;A8B14HD;EACE,YAAA;C9B44HD;A8B74HD;EAII,YAAA;C9B44HH;A8Bh5HD;EAMM,mBAAA;EACA,mBAAA;C9B64HL;A8Bp5HD;EAYI,UAAA;EACA,WAAA;C9B24HH;A8B/3HD;EAAA;IAPM,oBAAA;IACA,UAAA;G9B04HH;E8Bp4HH;IAJQ,iBAAA;G9B24HL;CACF;A8Bn4HD;EACE,iBAAA;C9Bq4HD;A8Bt4HD;EAKI,gBAAA;EACA,mBAAA;C9Bo4HH;A8B14HD;;;EAYI,0BAAA;C9Bm4HH;A8Br3HD;EAAA;IATM,iCAAA;IACA,2BAAA;G9Bk4HH;E8B13HH;;;IAHM,6BAAA;G9Bk4HH;CACF;A8Bz3HD;EAEI,cAAA;C9B03HH;A8B53HD;EAKI,eAAA;C9B03HH;A8Bj3HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8lIF;A+BxlID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0lID;A+BllID;EAAA;IAFI,mBAAA;G/BwlID;CACF;A+BzkID;EAAA;IAFI,YAAA;G/B+kID;CACF;A+BjkID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkkID;A+BhkIC;EACE,iBAAA;C/BkkIH;A+BtiID;EAAA;IAxBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkkID;E+BhkIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkkIH;E+B/jIC;IACE,oBAAA;G/BikIH;E+B5jIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8jIH;CACF;A+B1jID;;EAGI,kBAAA;C/B2jIH;A+BtjIC;EAAA;;IAFI,kBAAA;G/B6jIH;CACF;A+BpjID;;;;EAII,oBAAA;EACA,mBAAA;C/BsjIH;A+BhjIC;EAAA;;;;IAHI,gBAAA;IACA,eAAA;G/B0jIH;CACF;A+B9iID;EACE,cAAA;EACA,sBAAA;C/BgjID;A+B3iID;EAAA;IAFI,iBAAA;G/BijID;CACF;A+B7iID;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+iID;A+BziID;EAAA;;IAFI,iBAAA;G/BgjID;CACF;A+B9iID;EACE,OAAA;EACA,sBAAA;C/BgjID;A+B9iID;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BgjID;A+B1iID;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4iID;A+B1iIC;;EAEE,sBAAA;C/B4iIH;A+BrjID;EAaI,eAAA;C/B2iIH;A+BliID;EALI;;IAEE,mBAAA;G/B0iIH;CACF;A+BhiID;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/BmiID;A+B/hIC;EACE,WAAA;C/BiiIH;A+B/iID;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B+hIH;A+BrjID;EAyBI,gBAAA;C/B+hIH;A+BzhID;EAAA;IAFI,cAAA;G/B+hID;CACF;A+BthID;EACE,oBAAA;C/BwhID;A+BzhID;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/BwhIH;A+B5/HC;EAAA;IAtBI,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/BshIH;E+BtgID;;IAbM,2BAAA;G/BuhIL;E+B1gID;IAVM,kBAAA;G/BuhIL;E+BthIK;;IAEE,uBAAA;G/BwhIP;CACF;A+BtgID;EAAA;IAXI,YAAA;IACA,UAAA;G/BqhID;E+B3gIH;IAPM,YAAA;G/BqhIH;E+B9gIH;IALQ,kBAAA;IACA,qBAAA;G/BshIL;CACF;A+B3gID;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4yID;AkB5xHD;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB81HH;EkBlyHH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB41HH;EkBvyHH;IAhDM,sBAAA;GlB01HH;EkB1yHH;IA5CM,sBAAA;IACA,uBAAA;GlBy1HH;EkB9yHH;;;IAtCQ,YAAA;GlBy1HL;EkBnzHH;IAhCM,YAAA;GlBs1HH;EkBtzHH;IA5BM,iBAAA;IACA,uBAAA;GlBq1HH;EkB1zHH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBk1HH;EkBj0HH;;IAdQ,gBAAA;GlBm1HL;EkBr0HH;;IATM,mBAAA;IACA,eAAA;GlBk1HH;EkB10HH;IAHM,OAAA;GlBg1HH;CACF;A+BpjIC;EAAA;IANI,mBAAA;G/B8jIH;E+B5jIG;IACE,iBAAA;G/B8jIL;CACF;A+B7iID;EAAA;IARI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmzIP;CACF;A+BnjID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B03IF;A+BnjID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By3IF;A+B/iID;EChVE,gBAAA;EACA,mBAAA;ChCk4ID;A+BhjIC;ECnVA,iBAAA;EACA,oBAAA;ChCs4ID;A+BjjIC;ECtVA,iBAAA;EACA,oBAAA;ChC04ID;A+B3iID;EChWE,iBAAA;EACA,oBAAA;ChC84ID;A+BviID;EAAA;IAJI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+iID;CACF;A+BlhID;EAhBE;IExWA,uBAAA;GjC84IC;E+BriID;IE5WA,wBAAA;IF8WE,oBAAA;G/BuiID;E+BziID;IAKI,gBAAA;G/BuiIH;CACF;A+B9hID;EACE,0BAAA;EACA,sBAAA;C/BgiID;A+BliID;EAKI,eAAA;C/BgiIH;A+B/hIG;;EAEE,eAAA;EACA,8BAAA;C/BiiIL;A+B1iID;EAcI,eAAA;C/B+hIH;A+B7iID;EAmBM,eAAA;C/B6hIL;A+B3hIK;;EAEE,eAAA;EACA,8BAAA;C/B6hIP;A+BzhIK;;;EAGE,eAAA;EACA,0BAAA;C/B2hIP;A+BvhIK;;;EAGE,eAAA;EACA,8BAAA;C/ByhIP;A+BjkID;EA8CI,sBAAA;C/BshIH;A+BrhIG;;EAEE,0BAAA;C/BuhIL;A+BxkID;EAoDM,0BAAA;C/BuhIL;A+B3kID;;EA0DI,sBAAA;C/BqhIH;A+B9gIK;;;EAGE,0BAAA;EACA,eAAA;C/BghIP;A+B/+HC;EAAA;IAzBQ,eAAA;G/B4gIP;E+B3gIO;;IAEE,eAAA;IACA,8BAAA;G/B6gIT;E+BzgIO;;;IAGE,eAAA;IACA,0BAAA;G/B2gIT;E+BvgIO;;;IAGE,eAAA;IACA,8BAAA;G/BygIT;CACF;A+B3mID;EA8GI,eAAA;C/BggIH;A+B//HG;EACE,eAAA;C/BigIL;A+BjnID;EAqHI,eAAA;C/B+/HH;A+B9/HG;;EAEE,eAAA;C/BggIL;A+B5/HK;;;;EAEE,eAAA;C/BggIP;A+Bx/HD;EACE,0BAAA;EACA,sBAAA;C/B0/HD;A+B5/HD;EAKI,eAAA;C/B0/HH;A+Bz/HG;;EAEE,eAAA;EACA,8BAAA;C/B2/HL;A+BpgID;EAcI,eAAA;C/By/HH;A+BvgID;EAmBM,eAAA;C/Bu/HL;A+Br/HK;;EAEE,eAAA;EACA,8BAAA;C/Bu/HP;A+Bn/HK;;;EAGE,eAAA;EACA,0BAAA;C/Bq/HP;A+Bj/HK;;;EAGE,eAAA;EACA,8BAAA;C/Bm/HP;A+B3hID;EA+CI,sBAAA;C/B++HH;A+B9+HG;;EAEE,0BAAA;C/Bg/HL;A+BliID;EAqDM,0BAAA;C/Bg/HL;A+BriID;;EA2DI,sBAAA;C/B8+HH;A+Bx+HK;;;EAGE,0BAAA;EACA,eAAA;C/B0+HP;A+Bn8HC;EAAA;IA/BQ,sBAAA;G/Bs+HP;E+Bv8HD;IA5BQ,0BAAA;G/Bs+HP;E+B18HD;IAzBQ,eAAA;G/Bs+HP;E+Br+HO;;IAEE,eAAA;IACA,8BAAA;G/Bu+HT;E+Bn+HO;;;IAGE,eAAA;IACA,0BAAA;G/Bq+HT;E+Bj+HO;;;IAGE,eAAA;IACA,8BAAA;G/Bm+HT;CACF;A+B3kID;EA+GI,eAAA;C/B+9HH;A+B99HG;EACE,eAAA;C/Bg+HL;A+BjlID;EAsHI,eAAA;C/B89HH;A+B79HG;;EAEE,eAAA;C/B+9HL;A+B39HK;;;;EAEE,eAAA;C/B+9HP;AkCzmJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2mJD;AkChnJD;EAQI,sBAAA;ClC2mJH;AkCnnJD;EAWM,kBAAA;EACA,eAAA;EACA,eAAA;ClC2mJL;AkCxnJD;EAkBI,eAAA;ClCymJH;AmC7nJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+nJD;AmCnoJD;EAOI,gBAAA;CnC+nJH;AmCtoJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,kBAAA;CnCgoJL;AmC9nJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2oJJ;AmC7nJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwpJJ;AmCxnJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CnC4nJL;AmCtnJG;;;;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2nJL;AmClrJD;;;;;;EAkEM,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,oBAAA;CnCwnJL;AmC/mJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8rJL;AoC5rJG;;ERKF,+BAAA;EACG,4BAAA;C5B2rJJ;AoC3rJG;;ERTF,gCAAA;EACG,6BAAA;C5BwsJJ;AmC1nJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8sJL;AoC5sJG;;ERKF,+BAAA;EACG,4BAAA;C5B2sJJ;AoC3sJG;;ERTF,gCAAA;EACG,6BAAA;C5BwtJJ;AqC3tJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6tJD;AqCjuJD;EAOI,gBAAA;CrC6tJH;AqCpuJD;;EAUM,sBAAA;EACA,kBAAA;EACA,0BAAA;EACA,0BAAA;EACA,oBAAA;CrC8tJL;AqC5uJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6tJL;AqCjvJD;;EA2BM,aAAA;CrC0tJL;AqCrvJD;;EAkCM,YAAA;CrCutJL;AqCzvJD;;;;EA2CM,eAAA;EACA,0BAAA;EACA,oBAAA;CrCotJL;AsClwJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCowJD;AsChwJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CtCkwJL;AsC7vJC;EACE,cAAA;CtC+vJH;AsC3vJC;EACE,mBAAA;EACA,UAAA;CtC6vJH;AsCtvJD;ECtCE,0BAAA;CvC+xJD;AuC5xJG;;EAEE,0BAAA;CvC8xJL;AsCzvJD;EC1CE,0BAAA;CvCsyJD;AuCnyJG;;EAEE,0BAAA;CvCqyJL;AsC5vJD;EC9CE,0BAAA;CvC6yJD;AuC1yJG;;EAEE,0BAAA;CvC4yJL;AsC/vJD;EClDE,0BAAA;CvCozJD;AuCjzJG;;EAEE,0BAAA;CvCmzJL;AsClwJD;ECtDE,0BAAA;CvC2zJD;AuCxzJG;;EAEE,0BAAA;CvC0zJL;AsCrwJD;EC1DE,0BAAA;CvCk0JD;AuC/zJG;;EAEE,0BAAA;CvCi0JL;AwCn0JD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCq0JD;AwCl0JC;EACE,cAAA;CxCo0JH;AwCh0JC;EACE,mBAAA;EACA,UAAA;CxCk0JH;AwC/zJC;;EAEE,OAAA;EACA,iBAAA;CxCi0JH;AwC5zJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CxC8zJL;AwCzzJC;;EAEE,eAAA;EACA,0BAAA;CxC2zJH;AwCxzJC;EACE,aAAA;CxC0zJH;AwCvzJC;EACE,kBAAA;CxCyzJH;AwCtzJC;EACE,iBAAA;CxCwzJH;AyCl3JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo3JD;AyCz3JD;;EASI,eAAA;CzCo3JH;AyC73JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm3JH;AyCl4JD;EAmBI,0BAAA;CzCk3JH;AyC/2JC;;EAEE,mBAAA;CzCi3JH;AyCz4JD;EA4BI,gBAAA;CzCg3JH;AyC91JD;EAAA;IAdI,kBAAA;IACA,qBAAA;GzCg3JD;EyC92JC;;IAEE,mBAAA;IACA,oBAAA;GzCg3JH;EyCx2JH;;IAHM,gBAAA;GzC+2JH;CACF;A0C15JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL4uJT;A0Ct6JD;;EAaI,kBAAA;EACA,mBAAA;C1C65JH;A0Cz5JC;;;EAGE,sBAAA;C1C25JH;A0Ch7JD;EA0BI,aAAA;EACA,eAAA;C1Cy5JH;A2Cl7JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Co7JD;A2Cx7JD;EAQI,cAAA;EAEA,eAAA;C3Ck7JH;A2C57JD;EAeI,kBAAA;C3Cg7JH;A2C/7JD;;EAqBI,iBAAA;C3C86JH;A2Cn8JD;EAyBI,gBAAA;C3C66JH;A2Cr6JD;;EAEE,oBAAA;C3Cu6JD;A2Cz6JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cu6JH;A2C/5JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cy9JD;A2Cp6JD;EClDI,0BAAA;C5Cy9JH;A2Cv6JD;EC/CI,eAAA;C5Cy9JH;A2Ct6JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Co+JD;A2C36JD;ECtDI,0BAAA;C5Co+JH;A2C96JD;ECnDI,eAAA;C5Co+JH;A2C76JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C++JD;A2Cl7JD;EC1DI,0BAAA;C5C++JH;A2Cr7JD;ECvDI,eAAA;C5C++JH;A2Cp7JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C0/JD;A2Cz7JD;EC9DI,0BAAA;C5C0/JH;A2C57JD;EC3DI,eAAA;C5C0/JH;A6C5/JD;EACE;IAAQ,4BAAA;G7C+/JP;E6C9/JD;IAAQ,yBAAA;G7CigKP;CACF;A6C9/JD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6CtgKD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6C5/JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CLy9JT;A6C3/JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL62JT;A6Cx/JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C4/JD;A6Cr/JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLqiKT;A6Cl/JD;EErEE,0BAAA;C/C0jKD;A+CvjKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0gKH;A6Ct/JD;EEzEE,0BAAA;C/CkkKD;A+C/jKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkhKH;A6C1/JD;EE7EE,0BAAA;C/C0kKD;A+CvkKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0hKH;A6C9/JD;EEjFE,0BAAA;C/CklKD;A+C/kKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkiKH;AgD1lKD;EAEE,iBAAA;ChD2lKD;AgDzlKC;EACE,cAAA;ChD2lKH;AgDvlKD;;EAEE,QAAA;EACA,iBAAA;ChDylKD;AgDtlKD;EACE,eAAA;ChDwlKD;AgDrlKD;EACE,eAAA;ChDulKD;AgDplKC;EACE,gBAAA;ChDslKH;AgDllKD;;EAEE,mBAAA;ChDolKD;AgDjlKD;;EAEE,oBAAA;ChDmlKD;AgDhlKD;;;EAGE,oBAAA;EACA,oBAAA;ChDklKD;AgD/kKD;EACE,uBAAA;ChDilKD;AgD9kKD;EACE,uBAAA;ChDglKD;AgD5kKD;EACE,cAAA;EACA,mBAAA;ChD8kKD;AgDxkKD;EACE,gBAAA;EACA,iBAAA;ChD0kKD;AiDjoKD;EAEE,oBAAA;EACA,gBAAA;CjDkoKD;AiD1nKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,0BAAA;EACA,0BAAA;CjD2nKD;AiDxnKC;ErB3BA,6BAAA;EACC,4BAAA;C5BspKF;AiDznKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BmpKF;AiDlnKD;;EAEE,eAAA;CjDonKD;AiDtnKD;;EAKI,eAAA;CjDqnKH;AiDjnKC;;;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CjDqnKH;AiDjnKD;EACE,YAAA;EACA,iBAAA;CjDmnKD;AiD9mKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDgnKH;AiDrnKC;;;EASI,eAAA;CjDinKL;AiD1nKC;;;EAYI,eAAA;CjDmnKL;AiD9mKC;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CjDgnKH;AiDtnKC;;;;;;;;;EAYI,eAAA;CjDqnKL;AiDjoKC;;;EAeI,eAAA;CjDunKL;AkDztKC;EACE,eAAA;EACA,0BAAA;ClD2tKH;AkDztKG;;EAEE,eAAA;ClD2tKL;AkD7tKG;;EAKI,eAAA;ClD4tKP;AkDztKK;;;;EAEE,eAAA;EACA,0BAAA;ClD6tKP;AkD3tKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDguKP;AkDtvKC;EACE,eAAA;EACA,0BAAA;ClDwvKH;AkDtvKG;;EAEE,eAAA;ClDwvKL;AkD1vKG;;EAKI,eAAA;ClDyvKP;AkDtvKK;;;;EAEE,eAAA;EACA,0BAAA;ClD0vKP;AkDxvKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD6vKP;AkDnxKC;EACE,eAAA;EACA,0BAAA;ClDqxKH;AkDnxKG;;EAEE,eAAA;ClDqxKL;AkDvxKG;;EAKI,eAAA;ClDsxKP;AkDnxKK;;;;EAEE,eAAA;EACA,0BAAA;ClDuxKP;AkDrxKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD0xKP;AkDhzKC;EACE,eAAA;EACA,0BAAA;ClDkzKH;AkDhzKG;;EAEE,eAAA;ClDkzKL;AkDpzKG;;EAKI,eAAA;ClDmzKP;AkDhzKK;;;;EAEE,eAAA;EACA,0BAAA;ClDozKP;AkDlzKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDuzKP;AiDttKD;EACE,cAAA;EACA,mBAAA;CjDwtKD;AiDttKD;EACE,iBAAA;EACA,iBAAA;CjDwtKD;AmDl1KD;EACE,oBAAA;EACA,0BAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL2xKT;AmDj1KD;EACE,cAAA;CnDm1KD;AmD90KD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5Bq2KF;AmDp1KD;EAMI,eAAA;CnDi1KH;AmD50KD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnD80KD;AmDl1KD;;;;;EAWI,eAAA;CnD80KH;AmDz0KD;EACE,mBAAA;EACA,0BAAA;EACA,8BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bo3KF;AmDn0KD;;EAGI,iBAAA;CnDo0KH;AmDv0KD;;EAMM,oBAAA;EACA,iBAAA;CnDq0KL;AmDj0KG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B24KF;AmD/zKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5By4KF;AmDx1KD;EvB1DE,2BAAA;EACC,0BAAA;C5Bq5KF;AmD3zKD;EAEI,oBAAA;CnD4zKH;AmDzzKD;EACE,oBAAA;CnD2zKD;AmDnzKD;;;EAII,iBAAA;CnDozKH;AmDxzKD;;;EAOM,mBAAA;EACA,oBAAA;CnDszKL;AmD9zKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B26KF;AmDn0KD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDszKP;AmD10KD;;;;;;;;EAwBU,4BAAA;CnD4zKT;AmDp1KD;;;;;;;;EA4BU,6BAAA;CnDk0KT;AmD91KD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bm8KF;AmDn2KD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDg0KP;AmD12KD;;;;;;;;EA8CU,+BAAA;CnDs0KT;AmDp3KD;;;;;;;;EAkDU,gCAAA;CnD40KT;AmD93KD;;;;EA2DI,8BAAA;CnDy0KH;AmDp4KD;;EA+DI,cAAA;CnDy0KH;AmDx4KD;;EAmEI,UAAA;CnDy0KH;AmD54KD;;;;;;;;;;;;EA0EU,eAAA;CnDg1KT;AmD15KD;;;;;;;;;;;;EA8EU,gBAAA;CnD01KT;AmDx6KD;;;;;;;;EAuFU,iBAAA;CnD21KT;AmDl7KD;;;;;;;;EAgGU,iBAAA;CnD41KT;AmD57KD;EAsGI,UAAA;EACA,iBAAA;CnDy1KH;AmD/0KD;EACE,oBAAA;CnDi1KD;AmDl1KD;EAKI,iBAAA;EACA,mBAAA;CnDg1KH;AmDt1KD;EASM,gBAAA;CnDg1KL;AmDz1KD;EAcI,iBAAA;CnD80KH;AmD51KD;;EAkBM,8BAAA;CnD80KL;AmDh2KD;EAuBI,cAAA;CnD40KH;AmDn2KD;EAyBM,iCAAA;CnD60KL;AmDt0KD;EC1PE,sBAAA;CpDmkLD;AoDjkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDmkLH;AoDtkLC;EAMI,0BAAA;CpDmkLL;AoDzkLC;EASI,eAAA;EACA,0BAAA;CpDmkLL;AoDhkLC;EAEI,6BAAA;CpDikLL;AmDr1KD;EC7PE,sBAAA;CpDqlLD;AoDnlLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDqlLH;AoDxlLC;EAMI,0BAAA;CpDqlLL;AoD3lLC;EASI,eAAA;EACA,0BAAA;CpDqlLL;AoDllLC;EAEI,6BAAA;CpDmlLL;AmDp2KD;EChQE,sBAAA;CpDumLD;AoDrmLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDumLH;AoD1mLC;EAMI,0BAAA;CpDumLL;AoD7mLC;EASI,eAAA;EACA,0BAAA;CpDumLL;AoDpmLC;EAEI,6BAAA;CpDqmLL;AmDn3KD;ECnQE,sBAAA;CpDynLD;AoDvnLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDynLH;AoD5nLC;EAMI,0BAAA;CpDynLL;AoD/nLC;EASI,eAAA;EACA,0BAAA;CpDynLL;AoDtnLC;EAEI,6BAAA;CpDunLL;AmDl4KD;ECtQE,sBAAA;CpD2oLD;AoDzoLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2oLH;AoD9oLC;EAMI,0BAAA;CpD2oLL;AoDjpLC;EASI,eAAA;EACA,0BAAA;CpD2oLL;AoDxoLC;EAEI,6BAAA;CpDyoLL;AmDj5KD;ECzQE,sBAAA;CpD6pLD;AoD3pLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6pLH;AoDhqLC;EAMI,0BAAA;CpD6pLL;AoDnqLC;EASI,eAAA;EACA,0BAAA;CpD6pLL;AoD1pLC;EAEI,6BAAA;CpD2pLL;AqD3qLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD6qLD;AqDlrLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD6qLH;AqDxqLD;EACE,uBAAA;CrD0qLD;AqDtqLD;EACE,oBAAA;CrDwqLD;AsDnsLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CL8oLT;AsD7sLD;EASI,mBAAA;EACA,kCAAA;CtDusLH;AsDlsLD;EACE,cAAA;EACA,mBAAA;CtDosLD;AsDlsLD;EACE,aAAA;EACA,mBAAA;CtDosLD;AuD1tLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,6BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBmuLD;AuD3tLC;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB2uLD;AuDvtLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvDytLH;AwD9uLD;EACE,iBAAA;CxDgvLD;AwD5uLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD2uLD;AwDxuLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL2jLT;AwD9uLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLsoLT;AwDlvLD;EACE,mBAAA;EACA,iBAAA;CxDovLD;AwDhvLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDkvLD;AwD9uLD;EACE,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDgvLD;AwD5uLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,0BAAA;CxD8uLD;AwD5uLC;ElCrEA,WAAA;EAGA,yBAAA;CtBkzLD;AwD/uLC;ElCtEA,aAAA;EAGA,0BAAA;CtBszLD;AwD9uLD;EACE,cAAA;EACA,iCAAA;EACA,0BAAA;CxDgvLD;AwD7uLD;EACE,iBAAA;CxD+uLD;AwD3uLD;EACE,UAAA;EACA,wBAAA;CxD6uLD;AwDxuLD;EACE,mBAAA;EACA,cAAA;CxD0uLD;AwDtuLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDwuLD;AwD3uLD;EAQI,iBAAA;EACA,iBAAA;CxDsuLH;AwD/uLD;EAaI,kBAAA;CxDquLH;AwDlvLD;EAiBI,eAAA;CxDouLH;AwD/tLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDiuLD;AwD/sLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD8tLD;EwD5tLD;InDvEA,kDAAA;IACQ,0CAAA;GLsyLP;EwD3tLD;IAAY,aAAA;GxD8tLX;CACF;AwDztLD;EAFE;IAAY,aAAA;GxD+tLX;CACF;AyD92LD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBq4LD;AyD13LC;EnCdA,aAAA;EAGA,0BAAA;CtBy4LD;AyD73LC;EAAW,iBAAA;EAAmB,eAAA;CzDi4L/B;AyDh4LC;EAAW,iBAAA;EAAmB,eAAA;CzDo4L/B;AyDn4LC;EAAW,gBAAA;EAAmB,eAAA;CzDu4L/B;AyDt4LC;EAAW,kBAAA;EAAmB,eAAA;CzD04L/B;AyDt4LD;EACE,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,mBAAA;CzDw4LD;AyDp4LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDs4LD;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,4BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,2BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;A2Dj+LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,0BAAA;EACA,qCAAA;UAAA,6BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLi8LT;A2D5+LC;EAAY,kBAAA;C3D++Lb;A2D9+LC;EAAY,kBAAA;C3Di/Lb;A2Dh/LC;EAAY,iBAAA;C3Dm/Lb;A2Dl/LC;EAAY,mBAAA;C3Dq/Lb;A2Dl/LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Do/LD;A2Dj/LD;EACE,kBAAA;C3Dm/LD;A2D3+LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D6+LH;A2D1+LD;EACE,mBAAA;C3D4+LD;A2D1+LD;EACE,mBAAA;EACA,YAAA;C3D4+LD;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;C3D2+LL;A2Dx+LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,4BAAA;C3D2+LL;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;C3D2+LL;A2Dv+LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3Dy+LH;A2Dx+LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,2BAAA;EACA,cAAA;C3D0+LL;A4DnmMD;EACE,mBAAA;C5DqmMD;A4DlmMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DomMD;A4DvmMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLw7LT;A4D9mMD;;EAcM,eAAA;C5DomML;A4D1kMC;EAAA;IvDiKA,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL69LP;E4DxmMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D2mML;E4DzmMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D4mML;E4D1mMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D6mML;CACF;A4DnpMD;;;EA6CI,eAAA;C5D2mMH;A4DxpMD;EAiDI,QAAA;C5D0mMH;A4D3pMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5DymMH;A4DjqMD;EA4DI,WAAA;C5DwmMH;A4DpqMD;EA+DI,YAAA;C5DwmMH;A4DvqMD;;EAmEI,QAAA;C5DwmMH;A4D3qMD;EAuEI,YAAA;C5DumMH;A4D9qMD;EA0EI,WAAA;C5DumMH;A4D/lMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DkmMD;A4D7lMC;EdlGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CksMH;A4DjmMC;EACE,WAAA;EACA,SAAA;EdvGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C2sMH;A4DnmMC;;EAEE,WAAA;EACA,eAAA;EACA,sBAAA;EtCtHF,aAAA;EAGA,0BAAA;CtB0tMD;A4DpoMD;;;;EAsCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DomMH;A4D9oMD;;EA8CI,UAAA;EACA,mBAAA;C5DomMH;A4DnpMD;;EAmDI,WAAA;EACA,oBAAA;C5DomMH;A4DxpMD;;EAwDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DomMH;A4D/lMG;EACE,iBAAA;C5DimML;A4D7lMG;EACE,iBAAA;C5D+lML;A4DrlMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DulMD;A4DhmMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D6kMH;A4D5mMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,0BAAA;C5D6kMH;A4DtkMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DwkMD;A4DvkMC;EACE,kBAAA;C5DykMH;A4DhiMD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DkkMH;E4D1kMD;;IAYI,mBAAA;G5DkkMH;E4D9kMD;;IAgBI,oBAAA;G5DkkMH;E4D7jMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5D+jMD;E4D3jMD;IACE,aAAA;G5D6jMD;CACF;A6D3zMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7Dy1MH;A6Dv1MC;;;;;;;;;;;;;;;EACE,YAAA;C7Du2MH;AiC/2MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D03MD;AiCj3MD;EACE,wBAAA;CjCm3MD;AiCj3MD;EACE,uBAAA;CjCm3MD;AiC32MD;EACE,yBAAA;CjC62MD;AiC32MD;EACE,0BAAA;CjC62MD;AiC32MD;EACE,mBAAA;CjC62MD;AiC32MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/Du4MD;AiCz2MD;EACE,yBAAA;CjC22MD;AiCp2MD;EACE,gBAAA;CjCs2MD;AgEv4MD;EACE,oBAAA;ChEy4MD;AgEn4MD;;;;ECdE,yBAAA;CjEu5MD;AgEl4MD;;;;;;;;;;;;EAYE,yBAAA;ChEo4MD;AgE73MD;EAAA;IChDE,0BAAA;GjEi7MC;EiEh7MD;IAAU,0BAAA;GjEm7MT;EiEl7MD;IAAU,8BAAA;GjEq7MT;EiEp7MD;;IACU,+BAAA;GjEu7MT;CACF;AgEv4MD;EAAA;IAFI,0BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,2BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,iCAAA;GhE64MD;CACF;AgEt4MD;EAAA;ICrEE,0BAAA;GjE+8MC;EiE98MD;IAAU,0BAAA;GjEi9MT;EiEh9MD;IAAU,8BAAA;GjEm9MT;EiEl9MD;;IACU,+BAAA;GjEq9MT;CACF;AgEh5MD;EAAA;IAFI,0BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,2BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,iCAAA;GhEs5MD;CACF;AgE/4MD;EAAA;IC1FE,0BAAA;GjE6+MC;EiE5+MD;IAAU,0BAAA;GjE++MT;EiE9+MD;IAAU,8BAAA;GjEi/MT;EiEh/MD;;IACU,+BAAA;GjEm/MT;CACF;AgEz5MD;EAAA;IAFI,0BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,2BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,iCAAA;GhE+5MD;CACF;AgEx5MD;EAAA;IC/GE,0BAAA;GjE2gNC;EiE1gND;IAAU,0BAAA;GjE6gNT;EiE5gND;IAAU,8BAAA;GjE+gNT;EiE9gND;;IACU,+BAAA;GjEihNT;CACF;AgEl6MD;EAAA;IAFI,0BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,2BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,iCAAA;GhEw6MD;CACF;AgEj6MD;EAAA;IC5HE,yBAAA;GjEiiNC;CACF;AgEj6MD;EAAA;ICjIE,yBAAA;GjEsiNC;CACF;AgEj6MD;EAAA;ICtIE,yBAAA;GjE2iNC;CACF;AgEj6MD;EAAA;IC3IE,yBAAA;GjEgjNC;CACF;AgE95MD;ECnJE,yBAAA;CjEojND;AgE35MD;EAAA;ICjKE,0BAAA;GjEgkNC;EiE/jND;IAAU,0BAAA;GjEkkNT;EiEjkND;IAAU,8BAAA;GjEokNT;EiEnkND;;IACU,+BAAA;GjEskNT;CACF;AgEz6MD;EACE,yBAAA;ChE26MD;AgEt6MD;EAAA;IAFI,0BAAA;GhE46MD;CACF;AgE16MD;EACE,yBAAA;ChE46MD;AgEv6MD;EAAA;IAFI,2BAAA;GhE66MD;CACF;AgE36MD;EACE,yBAAA;ChE66MD;AgEx6MD;EAAA;IAFI,iCAAA;GhE86MD;CACF;AgEv6MD;EAAA;ICpLE,yBAAA;GjE+lNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\2a\";\n}\n.glyphicon-plus:before {\n content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #ffffff;\n background-color: #333333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .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 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .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 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .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 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #dddddd;\n}\n.table .table {\n background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #dddddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #ffffff;\n background-image: none;\n border: 1px solid #cccccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 14.333333px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333333;\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default .badge {\n color: #ffffff;\n background-color: #333333;\n}\n.btn-primary {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #ffffff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #ffffff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.btn-success {\n color: #ffffff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #ffffff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #ffffff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #ffffff;\n}\n.btn-info {\n color: #ffffff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #ffffff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #ffffff;\n}\n.btn-warning {\n color: #ffffff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #ffffff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #ffffff;\n}\n.btn-danger {\n color: #ffffff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #ffffff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #ffffff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #ffffff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-bottom-left-radius: 4px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #ffffff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777777;\n}\n.navbar-default .navbar-link:hover {\n color: #333333;\n}\n.navbar-default .btn-link {\n color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #cccccc;\n}\n.navbar-inverse {\n background-color: #222222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #ffffff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #ffffff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #cccccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 3;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #ffffff;\n border-color: #dddddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #ffffff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #ffffff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #ffffff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #ffffff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #ffffff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #dddddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000000;\n text-shadow: 0 1px 0 #ffffff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #ffffff;\n border: 1px solid #999999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n min-height: 16.42857143px;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #ffffff;\n text-align: center;\n background-color: #000000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #ffffff;\n background-clip: padding-box;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #ffffff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #ffffff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #ffffff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #ffffff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -15px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -15px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n//
    Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n }\n\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"],\n input[type=\"time\"],\n input[type=\"datetime-local\"],\n input[type=\"month\"] {\n &.form-control {\n line-height: @input-height-base;\n }\n\n &.input-sm,\n .input-group-sm & {\n line-height: @input-height-small;\n }\n\n &.input-lg,\n .input-group-lg & {\n line-height: @input-height-large;\n }\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because
    "],tbody:t,tfoot:t,tr:[2,"","
    "],td:c=[3,"","
    "],th:c,option:d=[1,""],optgroup:d},f=E.a.W<=8,E.a.ta=function(e,t){var n;if(lna){if(lna.parseHTML)n=lna.parseHTML(e,t)||[];else if((n=lna.clean([e],t))&&n[0]){for(var i=n[0];i.parentNode&&11!==i.parentNode.nodeType;)i=i.parentNode;i.parentNode&&i.parentNode.removeChild(i)}}else{(n=t)||(n=jna),i=n.parentWindow||n.defaultView||ina;var r,a=E.a.Cb(e).toLowerCase(),o=n.createElement("div");for(a=(r=(a=a.match(/^(?:\x3c!--.*?--\x3e\s*?)*?<([a-z]+)[\s>]/))&&h[a[1]]||l)[0],r="ignored
    "+r[1]+e+r[2]+"
    ","function"==typeof i.innerShiv?o.appendChild(i.innerShiv(r)):(f&&n.body.appendChild(o),o.innerHTML=r,f&&o.parentNode.removeChild(o));a--;)o=o.lastChild;n=E.a.la(o.lastChild.childNodes)}return n},E.a.Ld=function(e,t){var n=E.a.ta(e,t);return n.length&&n[0].parentElement||E.a.Xb(n)},E.a.dc=function(e,t){if(E.a.Sb(e),null!==(t=E.a.c(t))&&t!==hna)if("string"!=typeof t&&(t=t.toString()),lna)lna(e).html(t);else for(var n=E.a.ta(t,e.ownerDocument),i=0;i]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,he=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g,{wd:function(e,t,n){t.isTemplateRewritten(e,n)||t.rewriteTemplate(e,function(e){return E.ic.Kd(e,t)},n)},Kd:function(e,a){return e.replace(de,function(e,t,n,i,r){return ge(r,t,n,a)}).replace(he,function(e,t){return ge(t,"\x3c!-- ko --\x3e","#comment",a)})},ld:function(i,r){return E.aa.Wb(function(e,t){var n=e.nextSibling;n&&n.nodeName.toLowerCase()===r&&E.eb(n,i,t)})}}),E.b("__tr_ambtns",E.ic.ld),function(){E.B={},E.B.D=function(e){if(this.D=e){var t=E.a.P(e);this.Db="script"===t?1:"textarea"===t?2:"template"==t&&e.content&&11===e.content.nodeType?3:4}},E.B.D.prototype.text=function(){var e=1===this.Db?"text":2===this.Db?"value":"innerHTML";if(0==arguments.length)return this.D[e];var t=arguments[0];"innerHTML"==e?E.a.dc(this.D,t):this.D[e]=t};var t=E.a.g.Z()+"_";E.B.D.prototype.data=function(e){if(1===arguments.length)return E.a.g.get(this.D,t+e);E.a.g.set(this.D,t+e,arguments[1])};var i=E.a.g.Z();E.B.D.prototype.nodes=function(){var e=this.D;if(0==arguments.length){var t=E.a.g.get(e,i)||{},n=t.jb||(3===this.Db?e.content:4===this.Db?e:hna);return n&&!t.hd||(t=this.text())&&(n=E.a.Ld(t,e.ownerDocument),this.text(""),E.a.g.set(e,i,{jb:n,hd:!0})),n}E.a.g.set(e,i,{jb:arguments[0]})},E.B.ia=function(e){this.D=e},E.B.ia.prototype=new E.B.D,E.B.ia.prototype.constructor=E.B.ia,E.B.ia.prototype.text=function(){if(0==arguments.length){var e=E.a.g.get(this.D,i)||{};return e.jc===hna&&e.jb&&(e.jc=e.jb.innerHTML),e.jc}E.a.g.set(this.D,i,{jc:arguments[0]})},E.b("templateSources",E.B),E.b("templateSources.domElement",E.B.D),E.b("templateSources.anonymousTemplate",E.B.ia)}(),function(){function i(e,t,n){var i;for(t=E.h.nextSibling(t);e&&(i=e)!==t;)n(i,e=E.h.nextSibling(i))}function h(e,t){if(e.length){var r=e[0],a=e[e.length-1],n=r.parentNode,o=E.ga.instance,s=o.preprocessNode;if(s){if(i(r,a,function(e,t){var n=e.previousSibling,i=s.call(o,e);i&&(e===r&&(r=i[0]||t),e===a&&(a=i[i.length-1]||n))}),e.length=0,!r)return;r===a?e.push(r):(e.push(r,a),E.a.Ua(e,n))}i(r,a,function(e){1!==e.nodeType&&8!==e.nodeType||E.uc(t,e)}),i(r,a,function(e){1!==e.nodeType&&8!==e.nodeType||E.aa.bd(e,[t])}),E.a.Ua(e,n)}}function l(e){return e.nodeType?e:0"+t+"<\/script>")},0").attr("id",e.containerId).addClass(e.positionClass)).appendTo(g(e.target)),w}(e)),w}function i(e,t,n){var i=!(!n||!n.force)&&n.force;return!(!e||!i&&0!==g(":focus",e).length||(e[t.hideMethod]({duration:t.hideDuration,easing:t.hideEasing,complete:function(){_(e)}}),0))}function y(e){t&&t(e)}function r(t){var r=b(),e=t.iconClass||r.iconClass;if(void 0!==t.optionsOverride&&(r=g.extend(r,t.optionsOverride),e=t.optionsOverride.iconClass||e),!function(e,t){if(e.preventDuplicates){if(t.message===D)return!0;D=t.message}return!1}(r,t)){x++,w=v(r,!0);var a=null,o=g("
    "),n=g("
    "),i=g("
    "),s=g("
    "),l=g(r.closeHtml),u={intervalId:null,hideEta:null,maxHideTime:null},c={toastId:x,state:"visible",startTime:new Date,options:r,map:t};return t.iconClass&&o.addClass(r.toastClass).addClass(e),function(){if(t.title){var e=t.title;r.escapeHtml&&(e=d(t.title)),n.append(e).addClass(r.titleClass),o.append(n)}}(),function(){if(t.message){var e=t.message;r.escapeHtml&&(e=d(t.message)),i.append(e).addClass(r.messageClass),o.append(i)}}(),r.closeButton&&(l.addClass(r.closeClass).attr("role","button"),o.prepend(l)),r.progressBar&&(s.addClass(r.progressClass),o.prepend(s)),r.rtl&&o.addClass("rtl"),r.newestOnTop?w.prepend(o):w.append(o),function(){var e="";switch(t.iconClass){case"toast-success":case"toast-info":e="polite";break;default:e="assertive"}o.attr("aria-live",e)}(),o.hide(),o[r.showMethod]({duration:r.showDuration,easing:r.showEasing,complete:r.onShown}),0/g,">")}function h(e){var t=e&&!1!==r.closeMethod?r.closeMethod:r.hideMethod,n=e&&!1!==r.closeDuration?r.closeDuration:r.hideDuration,i=e&&!1!==r.closeEasing?r.closeEasing:r.hideEasing;if(!g(":focus",o).length||e)return clearTimeout(u.intervalId),o[t]({duration:n,easing:i,complete:function(){_(o),clearTimeout(a),r.onHidden&&"hidden"!==c.state&&r.onHidden(),c.state="hidden",c.endTime=new Date,y(c)}})}function f(){(0×',closeClass:"toast-close-button",newestOnTop:!0,preventDuplicates:!1,progressBar:!1,progressClass:"toast-progress",rtl:!1},e.options)}function _(e){w=w||v(),e.is(":visible")||(e.remove(),e=null,0===w.children().length&&(w.remove(),D=void 0))}var w,t,D,x,a,o,s,l,e}),function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,r;function y(){return e.apply(null,arguments)}function s(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function l(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function u(e){return void 0===e}function c(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function d(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function h(e,t){var n,i=[];for(n=0;n>>0,i=0;iDe(e)?(a=e+1,s-De(e)):(a=e,s),{year:a,dayOfYear:o}}function Ve(e,t,n){var i,r,a=Re(e.year(),t,n),o=Math.floor((e.dayOfYear()-a-1)/7)+1;return o<1?i=o+Ue(r=e.year()-1,t,n):o>Ue(e.year(),t,n)?(i=o-Ue(e.year(),t,n),r=e.year()+1):(r=e.year(),i=o),{week:i,year:r}}function Ue(e,t,n){var i=Re(e,t,n),r=Re(e+1,t,n);return(De(e)-i+r)/7}function We(e,t){return e.slice(t,7).concat(e.slice(0,t))}U("w",["ww",2],"wo","week"),U("W",["WW",2],"Wo","isoWeek"),N("week","w"),N("isoWeek","W"),L("week",5),L("isoWeek",5),le("w",Q),le("ww",Q,$),le("W",Q),le("WW",Q,$),he(["w","ww","W","WW"],function(e,t,n,i){t[i.substr(0,1)]=C(e)}),U("d",0,"do","day"),U("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),U("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),U("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),U("e",0,0,"weekday"),U("E",0,0,"isoWeekday"),N("day","d"),N("weekday","e"),N("isoWeekday","E"),L("day",11),L("weekday",11),L("isoWeekday",11),le("d",Q),le("e",Q),le("E",Q),le("dd",function(e,t){return t.weekdaysMinRegex(e)}),le("ddd",function(e,t){return t.weekdaysShortRegex(e)}),le("dddd",function(e,t){return t.weekdaysRegex(e)}),he(["dd","ddd","dddd"],function(e,t,n,i){var r=n._locale.weekdaysParse(e,i,n._strict);null!=r?t.d=r:_(n).invalidWeekday=e}),he(["d","e","E"],function(e,t,n,i){t[i]=C(e)});var qe="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Be="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),$e="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),ze=oe,Ge=oe,Ke=oe;function Qe(){function e(e,t){return t.length-e.length}var t,n,i,r,a,o=[],s=[],l=[],u=[];for(t=0;t<7;t++)n=p([2e3,1]).day(t),i=this.weekdaysMin(n,""),r=this.weekdaysShort(n,""),a=this.weekdays(n,""),o.push(i),s.push(r),l.push(a),u.push(i),u.push(r),u.push(a);for(o.sort(e),s.sort(e),l.sort(e),u.sort(e),t=0;t<7;t++)s[t]=ue(s[t]),l[t]=ue(l[t]),u[t]=ue(u[t]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function Je(){return this.hours()%12||12}function Ze(e,t){U(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Xe(e,t){return t._meridiemParse}U("H",["HH",2],0,"hour"),U("h",["hh",2],0,Je),U("k",["kk",2],0,function(){return this.hours()||24}),U("hmm",0,0,function(){return""+Je.apply(this)+H(this.minutes(),2)}),U("hmmss",0,0,function(){return""+Je.apply(this)+H(this.minutes(),2)+H(this.seconds(),2)}),U("Hmm",0,0,function(){return""+this.hours()+H(this.minutes(),2)}),U("Hmmss",0,0,function(){return""+this.hours()+H(this.minutes(),2)+H(this.seconds(),2)}),Ze("a",!0),Ze("A",!1),N("hour","h"),L("hour",13),le("a",Xe),le("A",Xe),le("H",Q),le("h",Q),le("k",Q),le("HH",Q,$),le("hh",Q,$),le("kk",Q,$),le("hmm",J),le("hmmss",Z),le("Hmm",J),le("Hmmss",Z),de(["H","HH"],ge),de(["k","kk"],function(e,t,n){var i=C(e);t[ge]=24===i?0:i}),de(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),de(["h","hh"],function(e,t,n){t[ge]=C(e),_(n).bigHour=!0}),de("hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i)),_(n).bigHour=!0}),de("hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r)),_(n).bigHour=!0}),de("Hmm",function(e,t,n){var i=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i))}),de("Hmmss",function(e,t,n){var i=e.length-4,r=e.length-2;t[ge]=C(e.substr(0,i)),t[ve]=C(e.substr(i,2)),t[ye]=C(e.substr(r))});var et,tt=Te("Hours",!0),nt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Oe,monthsShort:Ne,week:{dow:0,doy:6},weekdays:qe,weekdaysMin:$e,weekdaysShort:Be,meridiemParse:/[ap]\.?m?\.?/i},it={},rt={};function at(e){return e?e.toLowerCase().replace("_","-"):e}function ot(e){var t=null;if(!it[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=et._abbr,require("./locale/"+e),st(t)}catch(e){}return it[e]}function st(e,t){var n;return e&&((n=u(t)?ut(e):lt(e,t))?et=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),et._abbr}function lt(e,t){if(null===t)return delete it[e],null;var n,i=nt;if(t.abbr=e,null!=it[e])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=it[e]._config;else if(null!=t.parentLocale)if(null!=it[t.parentLocale])i=it[t.parentLocale]._config;else{if(null==(n=ot(t.parentLocale)))return rt[t.parentLocale]||(rt[t.parentLocale]=[]),rt[t.parentLocale].push({name:e,config:t}),null;i=n._config}return it[e]=new A(M(i,t)),rt[e]&&rt[e].forEach(function(e){lt(e.name,e.config)}),st(e),it[e]}function ut(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return et;if(!s(e)){if(t=ot(e))return t;e=[e]}return function(e){for(var t,n,i,r,a=0;a=t&&o(r,n,!0)>=t-1)break;t--}a++}return et}(e)}function ct(e){var t,n=e._a;return n&&-2===_(e).overflow&&(t=n[pe]<0||11Me(n[fe],n[pe])?me:n[ge]<0||24Ue(n,a,o)?_(e)._overflowWeeks=!0:null!=l?_(e)._overflowWeekday=!0:(s=Ye(n,i,r,a,o),e._a[fe]=s.year,e._dayOfYear=s.dayOfYear)}(e),null!=e._dayOfYear&&(a=dt(e._a[fe],i[fe]),(e._dayOfYear>De(a)||0===e._dayOfYear)&&(_(e)._overflowDayOfYear=!0),n=Fe(a,0,e._dayOfYear),e._a[pe]=n.getUTCMonth(),e._a[me]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=o[t]=i[t];for(;t<7;t++)e._a[t]=o[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[ve]&&0===e._a[ye]&&0===e._a[be]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Fe:function(e,t,n,i,r,a,o){var s;return e<100&&0<=e?(s=new Date(e+400,t,n,i,r,a,o),isFinite(s.getFullYear())&&s.setFullYear(e)):s=new Date(e,t,n,i,r,a,o),s}).apply(null,o),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(_(e).weekdayMismatch=!0)}}var ft=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,pt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mt=/Z|[+-]\d\d(?::?\d\d)?/,gt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],vt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],yt=/^\/?Date\((\-?\d+)/i;function bt(e){var t,n,i,r,a,o,s=e._i,l=ft.exec(s)||pt.exec(s);if(l){for(_(e).iso=!0,t=0,n=gt.length;tn.valueOf():n.valueOf()this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},dn.isLocal=function(){return!!this.isValid()&&!this._isUTC},dn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},dn.isUtc=Rt,dn.isUTC=Rt,dn.zoneAbbr=function(){return this._isUTC?"UTC":""},dn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},dn.dates=n("dates accessor is deprecated. Use date instead.",an),dn.months=n("months accessor is deprecated. Use month instead",je),dn.years=n("years accessor is deprecated. Use year instead",ke),dn.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),dn.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!u(this._isDSTShifted))return this._isDSTShifted;var e={};if(v(e,this),(e=Ct(e))._a){var t=e._isUTC?p(e._a):Tt(e._a);this._isDSTShifted=this.isValid()&&0 div").hide().filter(".datepicker-"+h[this.currentViewMode].CLASS_NAME).show())},y.prototype._isInDisabledDates=function(e){return!0===this._options.disabledDates[e.format("YYYY-MM-DD")]},y.prototype._isInEnabledDates=function(e){return!0===this._options.enabledDates[e.format("YYYY-MM-DD")]},y.prototype._isInDisabledHours=function(e){return!0===this._options.disabledHours[e.format("H")]},y.prototype._isInEnabledHours=function(e){return!0===this._options.enabledHours[e.format("H")]},y.prototype._isValid=function(e,t){if(!e.isValid())return!1;if(this._options.disabledDates&&"d"===t&&this._isInDisabledDates(e))return!1;if(this._options.enabledDates&&"d"===t&&!this._isInEnabledDates(e))return!1;if(this._options.minDate&&e.isBefore(this._options.minDate,t))return!1;if(this._options.maxDate&&e.isAfter(this._options.maxDate,t))return!1;if(this._options.daysOfWeekDisabled&&"d"===t&&-1!==this._options.daysOfWeekDisabled.indexOf(e.day()))return!1;if(this._options.disabledHours&&("h"===t||"m"===t||"s"===t)&&this._isInDisabledHours(e))return!1;if(this._options.enabledHours&&("h"===t||"m"===t||"s"===t)&&!this._isInEnabledHours(e))return!1;if(this._options.disabledTimeIntervals&&("h"===t||"m"===t||"s"===t)){var n=!1;if(o.each(this._options.disabledTimeIntervals,function(){if(e.isBetween(this[0],this[1]))return!(n=!0)}),n)return!1}return!0},y.prototype._parseInputDate=function(e){return void 0===this._options.parseInputDate?n.isMoment(e)||(e=this.getMoment(e)):e=this._options.parseInputDate(e),e},y.prototype._keydown=function(e){var t=null,n=void 0,i=void 0,r=void 0,a=void 0,o=[],s={},l=e.which;for(n in m[l]="p",m)m.hasOwnProperty(n)&&"p"===m[n]&&(o.push(n),parseInt(n,10)!==l&&(s[n]=!0));for(n in this._options.keyBinds)if(this._options.keyBinds.hasOwnProperty(n)&&"function"==typeof this._options.keyBinds[n]&&(r=n.split(" ")).length===o.length&&f[l]===r[r.length-1]){for(a=!0,i=r.length-2;0<=i;i--)if(!(f[r[i]]in s)){a=!1;break}if(a){t=this._options.keyBinds[n];break}}t&&t.call(this)&&(e.stopPropagation(),e.preventDefault())},y.prototype._keyup=function(e){m[e.which]="r",g[e.which]&&(g[e.which]=!1,e.stopPropagation(),e.preventDefault())},y.prototype._indexGivenDates=function(e){var t={},n=this;return o.each(e,function(){var e=n._parseInputDate(this);e.isValid()&&(t[e.format("YYYY-MM-DD")]=!0)}),!!Object.keys(t).length&&t},y.prototype._indexGivenHours=function(e){var t={};return o.each(e,function(){t[this]=!0}),!!Object.keys(t).length&&t},y.prototype._initFormatting=function(){var e=this._options.format||"L LT",t=this;this.actualFormat=e.replace(/(\[[^\[]*])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(e){return t._dates[0].localeData().longDateFormat(e)||e}),this.parseFormats=this._options.extraFormats?this._options.extraFormats.slice():[],this.parseFormats.indexOf(e)<0&&this.parseFormats.indexOf(this.actualFormat)<0&&this.parseFormats.push(this.actualFormat),this.use24Hours=this.actualFormat.toLowerCase().indexOf("a")<1&&this.actualFormat.replace(/\[.*?]/g,"").indexOf("h")<1,this._isEnabled("y")&&(this.MinViewModeNumber=2),this._isEnabled("M")&&(this.MinViewModeNumber=1),this._isEnabled("d")&&(this.MinViewModeNumber=0),this.currentViewMode=Math.max(this.MinViewModeNumber,this.currentViewMode),this.unset||this._setValue(this._dates[0],0)},y.prototype._getLastPickedDate=function(){return this._dates[this._getLastPickedDateIndex()]},y.prototype._getLastPickedDateIndex=function(){return this._dates.length-1},y.prototype.getMoment=function(e){var t=void 0;return t=null==e?n():this._hasTimeZone()?n.tz(e,this.parseFormats,this._options.locale,this._options.useStrict,this._options.timeZone):n(e,this.parseFormats,this._options.locale,this._options.useStrict),this._hasTimeZone()&&t.tz(this._options.timeZone),t},y.prototype.toggle=function(){return this.widget?this.hide():this.show()},y.prototype.ignoreReadonly=function(e){if(0===arguments.length)return this._options.ignoreReadonly;if("boolean"!=typeof e)throw new TypeError("ignoreReadonly () expects a boolean parameter");this._options.ignoreReadonly=e},y.prototype.options=function(e){if(0===arguments.length)return o.extend(!0,{},this._options);if(!(e instanceof Object))throw new TypeError("options() this.options parameter should be an object");o.extend(!0,this._options,e);var n=this;o.each(this._options,function(e,t){void 0!==n[e]&&n[e](t)})},y.prototype.date=function(e,t){if(t=t||0,0===arguments.length)return this.unset?null:this._options.allowMultidate?this._dates.join(this._options.multidateSeparator):this._dates[t].clone();if(!(null===e||"string"==typeof e||n.isMoment(e)||e instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");this._setValue(null===e?null:this._parseInputDate(e),t)},y.prototype.format=function(e){if(0===arguments.length)return this._options.format;if("string"!=typeof e&&("boolean"!=typeof e||!1!==e))throw new TypeError("format() expects a string or boolean:false parameter "+e);this._options.format=e,this.actualFormat&&this._initFormatting()},y.prototype.timeZone=function(e){if(0===arguments.length)return this._options.timeZone;if("string"!=typeof e)throw new TypeError("newZone() expects a string parameter");this._options.timeZone=e},y.prototype.dayViewHeaderFormat=function(e){if(0===arguments.length)return this._options.dayViewHeaderFormat;if("string"!=typeof e)throw new TypeError("dayViewHeaderFormat() expects a string parameter");this._options.dayViewHeaderFormat=e},y.prototype.extraFormats=function(e){if(0===arguments.length)return this._options.extraFormats;if(!1!==e&&!(e instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");this._options.extraFormats=e,this.parseFormats&&this._initFormatting()},y.prototype.disabledDates=function(e){if(0===arguments.length)return this._options.disabledDates?o.extend({},this._options.disabledDates):this._options.disabledDates;if(!e)return this._options.disabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("disabledDates() expects an array parameter");this._options.disabledDates=this._indexGivenDates(e),this._options.enabledDates=!1,this._update()},y.prototype.enabledDates=function(e){if(0===arguments.length)return this._options.enabledDates?o.extend({},this._options.enabledDates):this._options.enabledDates;if(!e)return this._options.enabledDates=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("enabledDates() expects an array parameter");this._options.enabledDates=this._indexGivenDates(e),this._options.disabledDates=!1,this._update()},y.prototype.daysOfWeekDisabled=function(e){if(0===arguments.length)return this._options.daysOfWeekDisabled.splice(0);if("boolean"==typeof e&&!e)return this._options.daysOfWeekDisabled=!1,this._update(),!0;if(!(e instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");if(this._options.daysOfWeekDisabled=e.reduce(function(e,t){return 6<(t=parseInt(t,10))||t<0||isNaN(t)||-1===e.indexOf(t)&&e.push(t),e},[]).sort(),this._options.useCurrent&&!this._options.keepInvalid)for(var t=0;t").append(S("
    ").addClass("prev").attr("data-action","previous").append(S("").addClass(this._options.icons.previous))).append(S("").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",this._options.calendarWeeks?"6":"5")).append(S("").addClass("next").attr("data-action","next").append(S("").addClass(this._options.icons.next)))),t=S("
    ").attr("colspan",this._options.calendarWeeks?"8":"7")));return[S("
    ").addClass("datepicker-days").append(S("").addClass("table table-sm").append(e).append(S(""))),S("
    ").addClass("datepicker-months").append(S("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),S("
    ").addClass("datepicker-years").append(S("
    ").addClass("table-condensed").append(e.clone()).append(t.clone())),S("
    ").addClass("datepicker-decades").append(S("
    ").addClass("table-condensed").append(e.clone()).append(t.clone()))]},E.prototype._getTimePickerMainTemplate=function(){var e=S(""),t=S(""),n=S("");return this._isEnabled("h")&&(e.append(S("
    ").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementHour}).addClass("btn").attr("data-action","incrementHours").append(S("").addClass(this._options.icons.up)))),t.append(S("").append(S("").addClass("timepicker-hour").attr({"data-time-component":"hours",title:this._options.tooltips.pickHour}).attr("data-action","showHours"))),n.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementHour}).addClass("btn").attr("data-action","decrementHours").append(S("").addClass(this._options.icons.down))))),this._isEnabled("m")&&(this._isEnabled("h")&&(e.append(S("").addClass("separator")),t.append(S("").addClass("separator").html(":")),n.append(S("").addClass("separator"))),e.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementMinute}).addClass("btn").attr("data-action","incrementMinutes").append(S("").addClass(this._options.icons.up)))),t.append(S("").append(S("").addClass("timepicker-minute").attr({"data-time-component":"minutes",title:this._options.tooltips.pickMinute}).attr("data-action","showMinutes"))),n.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementMinute}).addClass("btn").attr("data-action","decrementMinutes").append(S("").addClass(this._options.icons.down))))),this._isEnabled("s")&&(this._isEnabled("m")&&(e.append(S("").addClass("separator")),t.append(S("").addClass("separator").html(":")),n.append(S("").addClass("separator"))),e.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.incrementSecond}).addClass("btn").attr("data-action","incrementSeconds").append(S("").addClass(this._options.icons.up)))),t.append(S("").append(S("").addClass("timepicker-second").attr({"data-time-component":"seconds",title:this._options.tooltips.pickSecond}).attr("data-action","showSeconds"))),n.append(S("").append(S("").attr({href:"#",tabindex:"-1",title:this._options.tooltips.decrementSecond}).addClass("btn").attr("data-action","decrementSeconds").append(S("").addClass(this._options.icons.down))))),this.use24Hours||(e.append(S("").addClass("separator")),t.append(S("").append(S("").addClass("separator"))),S("
    ").addClass("timepicker-picker").append(S("").addClass("table-condensed").append([e,t,n]))},E.prototype._getTimePickerTemplate=function(){var e=S("
    ").addClass("timepicker-hours").append(S("
    ").addClass("table-condensed")),t=S("
    ").addClass("timepicker-minutes").append(S("
    ").addClass("table-condensed")),n=S("
    ").addClass("timepicker-seconds").append(S("
    ").addClass("table-condensed")),i=[this._getTimePickerMainTemplate()];return this._isEnabled("h")&&i.push(e),this._isEnabled("m")&&i.push(t),this._isEnabled("s")&&i.push(n),i},E.prototype._getToolbar=function(){var e=[];if(this._options.buttons.showToday&&e.push(S("
    ").append(S("").attr({href:"#",tabindex:"-1","data-action":"today",title:this._options.tooltips.today}).append(S("").addClass(this._options.icons.today)))),!this._options.sideBySide&&this._hasDate()&&this._hasTime()){var t=void 0,n=void 0;n="times"===this._options.viewMode?(t=this._options.tooltips.selectDate,this._options.icons.date):(t=this._options.tooltips.selectTime,this._options.icons.time),e.push(S("").append(S("").attr({href:"#",tabindex:"-1","data-action":"togglePicker",title:t}).append(S("").addClass(n))))}return this._options.buttons.showClear&&e.push(S("").append(S("").attr({href:"#",tabindex:"-1","data-action":"clear",title:this._options.tooltips.clear}).append(S("").addClass(this._options.icons.clear)))),this._options.buttons.showClose&&e.push(S("").append(S("").attr({href:"#",tabindex:"-1","data-action":"close",title:this._options.tooltips.close}).append(S("").addClass(this._options.icons.close)))),0===e.length?"":S("").addClass("table-condensed").append(S("").append(S("").append(e)))},E.prototype._getTemplate=function(){var e=S("
    ").addClass("bootstrap-datetimepicker-widget dropdown-menu"),t=S("
    ").addClass("datepicker").append(this._getDatePickerTemplate()),n=S("
    ").addClass("timepicker").append(this._getTimePickerTemplate()),i=S("
      ").addClass("list-unstyled"),r=S("
    • ").addClass("picker-switch"+(this._options.collapse?" accordion-toggle":"")).append(this._getToolbar());return this._options.inline&&e.removeClass("dropdown-menu"),this.use24Hours&&e.addClass("usetwentyfour"),this._isEnabled("s")&&!this.use24Hours&&e.addClass("wider"),this._options.sideBySide&&this._hasDate()&&this._hasTime()?(e.addClass("timepicker-sbs"),"top"===this._options.toolbarPlacement&&e.append(r),e.append(S("
      ").addClass("row").append(t.addClass("col-md-6")).append(n.addClass("col-md-6"))),"bottom"!==this._options.toolbarPlacement&&"default"!==this._options.toolbarPlacement||e.append(r),e):("top"===this._options.toolbarPlacement&&i.append(r),this._hasDate()&&i.append(S("
    • ").addClass(this._options.collapse&&this._hasTime()?"collapse":"").addClass(this._options.collapse&&this._hasTime()&&"times"===this._options.viewMode?"":"show").append(t)),"default"===this._options.toolbarPlacement&&i.append(r),this._hasTime()&&i.append(S("
    • ").addClass(this._options.collapse&&this._hasDate()?"collapse":"").addClass(this._options.collapse&&this._hasDate()&&"times"===this._options.viewMode?"show":"").append(n)),"bottom"===this._options.toolbarPlacement&&i.append(r),e.append(i))},E.prototype._place=function(e){var t=e&&e.data&&e.data.picker||this,n=t._options.widgetPositioning.vertical,i=t._options.widgetPositioning.horizontal,r=void 0,a=(t.component&&t.component.length?t.component:t._element).position(),o=(t.component&&t.component.length?t.component:t._element).offset();if(t._options.widgetParent)r=t._options.widgetParent.append(t.widget);else if(t._element.is("input"))r=t._element.after(t.widget).parent();else{if(t._options.inline)return void(r=t._element.append(t.widget));r=t._element,t._element.children().first().after(t.widget)}if("auto"===n&&(n=o.top+1.5*t.widget.height()>=S(window).height()+S(window).scrollTop()&&t.widget.height()+t._element.outerHeight()S(window).width()?"right":"left"),"top"===n?t.widget.addClass("top").removeClass("bottom"):t.widget.addClass("bottom").removeClass("top"),"right"===i?t.widget.addClass("float-right"):t.widget.removeClass("float-right"),"relative"!==r.css("position")&&(r=r.parents().filter(function(){return"relative"===S(this).css("position")}).first()),0===r.length)throw new Error("datetimepicker component should be placed within a relative positioned container");t.widget.css({top:"top"===n?"auto":a.top+t._element.outerHeight()+"px",bottom:"top"===n?r.outerHeight()-(r===t._element?0:a.top)+"px":"auto",left:"left"===i?(r===t._element?0:a.left)+"px":"auto",right:"left"===i?"auto":r.outerWidth()-t._element.outerWidth()-(r===t._element?0:a.left)+"px"})},E.prototype._fillDow=function(){var e=S("
    "),t=this._viewDate.clone().startOf("w").startOf("d");for(!0===this._options.calendarWeeks&&e.append(S(""),this._options.calendarWeeks&&r.append('"),n.push(r)),a="",i.isBefore(this._viewDate,"M")&&(a+=" old"),i.isAfter(this._viewDate,"M")&&(a+=" new"),this._options.allowMultidate){var s=this._datesFormatted.indexOf(i.format("YYYY-MM-DD"));-1!==s&&i.isSame(this._datesFormatted[s],"d")&&!this.unset&&(a+=" active")}else i.isSame(this._getLastPickedDate(),"d")&&!this.unset&&(a+=" active");this._isValid(i,"d")||(a+=" disabled"),i.isSame(this.getMoment(),"d")&&(a+=" today"),0!==i.day()&&6!==i.day()||(a+=" weekend"),r.append('"),i.add(1,"d")}e.find("tbody").empty().append(n),this._updateMonths(),this._updateYears(),this._updateDecades()}},E.prototype._fillHours=function(){var e=this.widget.find(".timepicker-hours table"),t=this._viewDate.clone().startOf("d"),n=[],i=S("");for(11"),n.push(i)),i.append('"),t.add(1,"h");e.empty().append(n)},E.prototype._fillMinutes=function(){for(var e=this.widget.find(".timepicker-minutes table"),t=this._viewDate.clone().startOf("h"),n=[],i=1===this._options.stepping?5:this._options.stepping,r=S("");this._viewDate.isSame(t,"h");)t.minute()%(4*i)==0&&(r=S(""),n.push(r)),r.append('"),t.add(i,"m");e.empty().append(n)},E.prototype._fillSeconds=function(){for(var e=this.widget.find(".timepicker-seconds table"),t=this._viewDate.clone().startOf("m"),n=[],i=S("");this._viewDate.isSame(t,"m");)t.second()%20==0&&(i=S(""),n.push(i)),i.append('"),t.add(5,"s");e.empty().append(n)},E.prototype._fillTime=function(){var e=void 0,t=void 0,n=this.widget.find(".timepicker span[data-time-component]");this.use24Hours||(e=this.widget.find(".timepicker [data-action=togglePeriod]"),t=this._getLastPickedDate().clone().add(12<=this._getLastPickedDate().hours()?-12:12,"h"),e.text(this._getLastPickedDate().format("A")),this._isValid(t,"h")?e.removeClass("disabled"):e.addClass("disabled")),n.filter("[data-time-component=hours]").text(this._getLastPickedDate().format(this.use24Hours?"HH":"hh")),n.filter("[data-time-component=minutes]").text(this._getLastPickedDate().format("mm")),n.filter("[data-time-component=seconds]").text(this._getLastPickedDate().format("ss")),this._fillHours(),this._fillMinutes(),this._fillSeconds()},E.prototype._doAction=function(e,t){var n=this._getLastPickedDate();if(S(e.currentTarget).is(".disabled"))return!1;switch(t=t||S(e.currentTarget).data("action")){case"next":var i=T.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.add(T.DatePickerModes[this.currentViewMode].NAV_STEP,i),this._fillDate(),this._viewUpdate(i);break;case"previous":var r=T.DatePickerModes[this.currentViewMode].NAV_FUNCTION;this._viewDate.subtract(T.DatePickerModes[this.currentViewMode].NAV_STEP,r),this._fillDate(),this._viewUpdate(r);break;case"pickerSwitch":this._showMode(1);break;case"selectMonth":var a=S(e.target).closest("tbody").find("span").index(S(e.target));this._viewDate.month(a),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()).month(this._viewDate.month()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("M");break;case"selectYear":var o=parseInt(S(e.target).text(),10)||0;this._viewDate.year(o),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDecade":var s=parseInt(S(e.target).data("selection"),10)||0;this._viewDate.year(s),this.currentViewMode===this.MinViewModeNumber?(this._setValue(n.clone().year(this._viewDate.year()),this._getLastPickedDateIndex()),this._options.inline||this.hide()):(this._showMode(-1),this._fillDate()),this._viewUpdate("YYYY");break;case"selectDay":var l=this._viewDate.clone();S(e.target).is(".old")&&l.subtract(1,"M"),S(e.target).is(".new")&&l.add(1,"M");var u=l.date(parseInt(S(e.target).text(),10)),c=0;this._options.allowMultidate?-1!==(c=this._datesFormatted.indexOf(u.format("YYYY-MM-DD")))?this._setValue(null,c):this._setValue(u,this._getLastPickedDateIndex()+1):this._setValue(u,this._getLastPickedDateIndex()),this._hasTime()||this._options.keepOpen||this._options.inline||this._options.allowMultidate||this.hide();break;case"incrementHours":var d=n.clone().add(1,"h");this._isValid(d,"h")&&this._setValue(d,this._getLastPickedDateIndex());break;case"incrementMinutes":var h=n.clone().add(this._options.stepping,"m");this._isValid(h,"m")&&this._setValue(h,this._getLastPickedDateIndex());break;case"incrementSeconds":var f=n.clone().add(1,"s");this._isValid(f,"s")&&this._setValue(f,this._getLastPickedDateIndex());break;case"decrementHours":var p=n.clone().subtract(1,"h");this._isValid(p,"h")&&this._setValue(p,this._getLastPickedDateIndex());break;case"decrementMinutes":var m=n.clone().subtract(this._options.stepping,"m");this._isValid(m,"m")&&this._setValue(m,this._getLastPickedDateIndex());break;case"decrementSeconds":var g=n.clone().subtract(1,"s");this._isValid(g,"s")&&this._setValue(g,this._getLastPickedDateIndex());break;case"togglePeriod":this._setValue(n.clone().add(12<=n.hours()?-12:12,"h"),this._getLastPickedDateIndex());break;case"togglePicker":var v=S(e.target),y=v.closest("a"),b=v.closest("ul"),_=b.find(".show"),w=b.find(".collapse:not(.show)"),D=v.is("span")?v:v.find("span"),x=void 0;if(_&&_.length){if((x=_.data("collapse"))&&x.transitioning)return!0;_.collapse?(_.collapse("hide"),w.collapse("show")):(_.removeClass("show"),w.addClass("show")),D.toggleClass(this._options.icons.time+" "+this._options.icons.date),D.hasClass(this._options.icons.date)?y.attr("title",this._options.tooltips.selectDate):y.attr("title",this._options.tooltips.selectTime)}break;case"showPicker":this.widget.find(".timepicker > div:not(.timepicker-picker)").hide(),this.widget.find(".timepicker .timepicker-picker").show();break;case"showHours":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-hours").show();break;case"showMinutes":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-minutes").show();break;case"showSeconds":this.widget.find(".timepicker .timepicker-picker").hide(),this.widget.find(".timepicker .timepicker-seconds").show();break;case"selectHour":var C=parseInt(S(e.target).text(),10);this.use24Hours||(12<=n.hours()?12!==C&&(C+=12):12===C&&(C=0)),this._setValue(n.clone().hours(C),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("m")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectMinute":this._setValue(n.clone().minutes(parseInt(S(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._isEnabled("s")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"selectSecond":this._setValue(n.clone().seconds(parseInt(S(e.target).text(),10)),this._getLastPickedDateIndex()),this._isEnabled("a")||this._options.keepOpen||this._options.inline?this._doAction(e,"showPicker"):this.hide();break;case"clear":this.clear();break;case"close":this.hide();break;case"today":var k=this.getMoment();this._isValid(k,"d")&&this._setValue(k,this._getLastPickedDateIndex())}return!1},E.prototype.hide=function(){var t=!1;this.widget&&(this.widget.find(".collapse").each(function(){var e=S(this).data("collapse");return!e||!e.transitioning||!(t=!0)}),t||(this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this.widget.hide(),S(window).off("resize",this._place()),this.widget.off("click","[data-action]"),this.widget.off("mousedown",!1),this.widget.remove(),this.widget=!1,this._notifyEvent({type:T.Event.HIDE,date:this._getLastPickedDate().clone()}),void 0!==this.input&&this.input.blur(),this._viewDate=this._getLastPickedDate().clone()))},E.prototype.show=function(){var e=void 0,t={year:function(e){return e.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(e){return e.date(1).hours(0).seconds(0).minutes(0)},day:function(e){return e.hours(0).seconds(0).minutes(0)},hour:function(e){return e.seconds(0).minutes(0)},minute:function(e){return e.seconds(0)}};if(void 0!==this.input){if(this.input.prop("disabled")||!this._options.ignoreReadonly&&this.input.prop("readonly")||this.widget)return;void 0!==this.input.val()&&0!==this.input.val().trim().length?this._setValue(this._parseInputDate(this.input.val().trim()),0):this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0))}else this.unset&&this._options.useCurrent&&(e=this.getMoment(),"string"==typeof this._options.useCurrent&&(e=t[this._options.useCurrent](e)),this._setValue(e,0));this.widget=this._getTemplate(),this._fillDow(),this._fillMonths(),this.widget.find(".timepicker-hours").hide(),this.widget.find(".timepicker-minutes").hide(),this.widget.find(".timepicker-seconds").hide(),this._update(),this._showMode(),S(window).on("resize",{picker:this},this._place),this.widget.on("click","[data-action]",S.proxy(this._doAction,this)),this.widget.on("mousedown",!1),this.component&&this.component.hasClass("btn")&&this.component.toggleClass("active"),this._place(),this.widget.show(),void 0!==this.input&&this._options.focusOnShow&&!this.input.is(":focus")&&this.input.focus(),this._notifyEvent({type:T.Event.SHOW})},E.prototype.destroy=function(){this.hide(),this._element.removeData(T.DATA_KEY),this._element.removeData("date")},E.prototype.disable=function(){this.hide(),this.component&&this.component.hasClass("btn")&&this.component.addClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!0)},E.prototype.enable=function(){this.component&&this.component.hasClass("btn")&&this.component.removeClass("disabled"),void 0!==this.input&&this.input.prop("disabled",!1)},E.prototype.toolbarPlacement=function(e){if(0===arguments.length)return this._options.toolbarPlacement;if("string"!=typeof e)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===x.indexOf(e))throw new TypeError("toolbarPlacement() parameter must be one of ("+x.join(", ")+") value");this._options.toolbarPlacement=e,this.widget&&(this.hide(),this.show())},E.prototype.widgetPositioning=function(e){if(0===arguments.length)return S.extend({},this._options.widgetPositioning);if("[object Object]"!=={}.toString.call(e))throw new TypeError("widgetPositioning() expects an object variable");if(e.horizontal){if("string"!=typeof e.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(e.horizontal=e.horizontal.toLowerCase(),-1===D.indexOf(e.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+D.join(", ")+")");this._options.widgetPositioning.horizontal=e.horizontal}if(e.vertical){if("string"!=typeof e.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(e.vertical=e.vertical.toLowerCase(),-1===w.indexOf(e.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+w.join(", ")+")");this._options.widgetPositioning.vertical=e.vertical}this._update()},E.prototype.widgetParent=function(e){if(0===arguments.length)return this._options.widgetParent;if("string"==typeof e&&(e=S(e)),null!==e&&"string"!=typeof e&&!(e instanceof S))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");this._options.widgetParent=e,this.widget&&(this.hide(),this.show())},E._jQueryHandleThis=function(e,t,n){var i=S(e).data(T.DATA_KEY);if("object"===(void 0===t?"undefined":r(t))&&S.extend({},T.Default,t),i||(i=new E(S(e),t),S(e).data(T.DATA_KEY,i)),"string"==typeof t){if(void 0===i[t])throw new Error('No method named "'+t+'"');return void 0===n?i[t]():i[t](n)}},E._jQueryInterface=function(e,t){return 1===this.length?E._jQueryHandleThis(this[0],e,t):this.each(function(){E._jQueryHandleThis(this,e,t)})},C=E,S(document).on(T.Event.CLICK_DATA_API,T.Selector.DATA_TOGGLE,function(){var e=k(S(this));0!==e.length&&C._jQueryInterface.call(e,"toggle")}).on(T.Event.CHANGE,"."+T.ClassName.INPUT,function(e){var t=k(S(this));0!==t.length&&C._jQueryInterface.call(t,"_change",e)}).on(T.Event.BLUR,"."+T.ClassName.INPUT,function(e){var t=k(S(this)),n=t.data(T.DATA_KEY);0!==t.length&&(n._options.debug||window.debug||C._jQueryInterface.call(t,"hide",e))}).on(T.Event.KEYDOWN,"."+T.ClassName.INPUT,function(e){var t=k(S(this));0!==t.length&&C._jQueryInterface.call(t,"_keydown",e)}).on(T.Event.KEYUP,"."+T.ClassName.INPUT,function(e){var t=k(S(this));0!==t.length&&C._jQueryInterface.call(t,"_keyup",e)}).on(T.Event.FOCUS,"."+T.ClassName.INPUT,function(e){var t=k(S(this)),n=t.data(T.DATA_KEY);0!==t.length&&n._options.allowInputToggle&&C._jQueryInterface.call(t,"show",e)}),S.fn[T.NAME]=C._jQueryInterface,S.fn[T.NAME].Constructor=C,S.fn[T.NAME].noConflict=function(){return S.fn[T.NAME]=_,C._jQueryInterface};function k(e){var t=e.data("target"),n=void 0;return t||(t=e.attr("href")||"",t=/^#[a-z]/i.test(t)?t:null),0===(n=S(t)).length||n.data(T.DATA_KEY)||S.extend({},n.data(),S(this).data()),n}function E(e,t){a(this,E);var n=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}(this,b.call(this,e,t));return n._init(),n}}(),function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function(E,M){function A(){return new Date(Date.UTC.apply(Date,arguments))}function O(){var e=new Date;return A(e.getFullYear(),e.getMonth(),e.getDate())}function a(e,t){return e.getUTCFullYear()===t.getUTCFullYear()&&e.getUTCMonth()===t.getUTCMonth()&&e.getUTCDate()===t.getUTCDate()}function e(e,t){return function(){return t!==M&&E.fn.datepicker.deprecated(t),this[e].apply(this,arguments)}}function w(e,t){E.data(e,"datepicker",this),this._events=[],this._secondaryEvents=[],this._process_options(t),this.dates=new n,this.viewDate=this.o.defaultViewDate,this.focusDate=null,this.element=E(e),this.isInput=this.element.is("input"),this.inputField=this.isInput?this.element:this.element.find("input"),this.component=!!this.element.hasClass("date")&&this.element.find(".add-on, .input-group-addon, .input-group-append, .input-group-prepend, .btn"),this.component&&0===this.component.length&&(this.component=!1),this.isInline=!this.component&&this.element.is("div"),this.picker=E(I.template),this._check_template(this.o.templates.leftArrow)&&this.picker.find(".prev").html(this.o.templates.leftArrow),this._check_template(this.o.templates.rightArrow)&&this.picker.find(".next").html(this.o.templates.rightArrow),this._buildEvents(),this._attachEvents(),this.isInline?this.picker.addClass("datepicker-inline").appendTo(this.element):this.picker.addClass("datepicker-dropdown dropdown-menu"),this.o.rtl&&this.picker.addClass("datepicker-rtl"),this.o.calendarWeeks&&this.picker.find(".datepicker-days .datepicker-switch, thead .datepicker-title, tfoot .today, tfoot .clear").attr("colspan",function(e,t){return Number(t)+1}),this._process_options({startDate:this._o.startDate,endDate:this._o.endDate,daysOfWeekDisabled:this.o.daysOfWeekDisabled,daysOfWeekHighlighted:this.o.daysOfWeekHighlighted,datesDisabled:this.o.datesDisabled}),this._allow_update=!1,this.setViewMode(this.o.startView),this._allow_update=!0,this.fillDow(),this.fillMonths(),this.update(),this.isInline&&this.show()}var t,n=(t={get:function(e){return this.slice(e)[0]},contains:function(e){for(var t=e&&e.valueOf(),n=0,i=this.length;n]/g)||[]).length<=0||0this.o.endDate?this.viewDate=new Date(this.o.endDate):this.viewDate=this.o.defaultViewDate),t?(this.setValue(),this.element.change()):this.dates.length&&String(e)!==String(this.dates)&&t&&(this._trigger("changeDate"),this.element.change()),!this.dates.length&&e.length&&(this._trigger("clearDate"),this.element.change()),this.fill(),this},fillDow:function(){if(this.o.showWeekDays){var e=this.o.weekStart,t="";for(this.o.calendarWeeks&&(t+='');e";t+="",this.picker.find(".datepicker-days thead").append(t)}},fillMonths:function(){for(var e=this._utc_to_local(this.viewDate),t="",n=0;n<12;n++)t+=''+N[this.o.language].monthsShort[n]+"";this.picker.find(".datepicker-months td").html(t)},setRange:function(e){e&&e.length?this.range=E.map(e,function(e){return e.valueOf()}):delete this.range,this.fill()},getClassNames:function(e){var t=[],n=this.viewDate.getUTCFullYear(),i=this.viewDate.getUTCMonth(),r=O();return e.getUTCFullYear()n||e.getUTCFullYear()===n&&e.getUTCMonth()>i)&&t.push("new"),this.focusDate&&e.valueOf()===this.focusDate.valueOf()&&t.push("focused"),this.o.todayHighlight&&a(e,r)&&t.push("today"),-1!==this.dates.contains(e)&&t.push("active"),this.dateWithinRange(e)||t.push("disabled"),this.dateIsDisabled(e)&&t.push("disabled","disabled-date"),-1!==E.inArray(e.getUTCDay(),this.o.daysOfWeekHighlighted)&&t.push("highlighted"),this.range&&(e>this.range[0]&&e"+v+"";h.find(".datepicker-switch").text(f+"-"+p),h.find("td").html(c)},fill:function(){var e,t,n=new Date(this.viewDate),r=n.getUTCFullYear(),i=n.getUTCMonth(),a=this.o.startDate!==-1/0?this.o.startDate.getUTCFullYear():-1/0,o=this.o.startDate!==-1/0?this.o.startDate.getUTCMonth():-1/0,s=this.o.endDate!==1/0?this.o.endDate.getUTCFullYear():1/0,l=this.o.endDate!==1/0?this.o.endDate.getUTCMonth():1/0,u=N[this.o.language].today||N.en.today||"",c=N[this.o.language].clear||N.en.clear||"",d=N[this.o.language].titleFormat||N.en.titleFormat,h=O(),f=(!0===this.o.todayBtn||"linked"===this.o.todayBtn)&&h>=this.o.startDate&&h<=this.o.endDate&&!this.weekOfDateIsDisabled(h);if(!isNaN(r)&&!isNaN(i)){this.picker.find(".datepicker-days .datepicker-switch").text(I.formatDate(n,d,this.o.language)),this.picker.find("tfoot .today").text(u).css("display",f?"table-cell":"none"),this.picker.find("tfoot .clear").text(c).css("display",!0===this.o.clearBtn?"table-cell":"none"),this.picker.find("thead .datepicker-title").text(this.o.title).css("display","string"==typeof this.o.title&&""!==this.o.title?"table-cell":"none"),this.updateNavArrows(),this.fillMonths();var p=A(r,i,0),m=p.getUTCDate();p.setUTCDate(m-(p.getUTCDay()-this.o.weekStart+7)%7);var g=new Date(p);p.getUTCFullYear()<100&&g.setUTCFullYear(p.getUTCFullYear()),g.setUTCDate(g.getUTCDate()+42),g=g.valueOf();for(var v,y,b=[];p.valueOf()"),this.o.calendarWeeks)){var _=new Date(+p+(this.o.weekStart-v-7)%7*864e5),w=new Date(Number(_)+(11-_.getUTCDay())%7*864e5),D=new Date(Number(D=A(w.getUTCFullYear(),0,1))+(11-D.getUTCDay())%7*864e5),x=(w-D)/864e5/7+1;b.push('")}(y=this.getClassNames(p)).push("day");var C=p.getUTCDate();this.o.beforeShowDay!==E.noop&&((t=this.o.beforeShowDay(this._utc_to_local(p)))===M?t={}:"boolean"==typeof t?t={enabled:t}:"string"==typeof t&&(t={classes:t}),!1===t.enabled&&y.push("disabled"),t.classes&&(y=y.concat(t.classes.split(/\s+/))),t.tooltip&&(e=t.tooltip),t.content&&(C=t.content)),y=E.isFunction(E.uniqueSort)?E.uniqueSort(y):E.unique(y),b.push('"),e=null,v===this.o.weekEnd&&b.push(""),p.setUTCDate(p.getUTCDate()+1)}this.picker.find(".datepicker-days tbody").html(b.join(""));var k=N[this.o.language].monthsTitle||N.en.monthsTitle||"Months",T=this.picker.find(".datepicker-months").find(".datepicker-switch").text(this.o.maxViewMode<2?k:r).end().find("tbody span").removeClass("active");if(E.each(this.dates,function(e,t){t.getUTCFullYear()===r&&T.eq(t.getUTCMonth()).addClass("active")}),(rs;break;case 0:e=i<=a&&r<=o,t=s<=i&&l<=r}this.picker.find(".prev").toggleClass("disabled",e),this.picker.find(".next").toggleClass("disabled",t)}},click:function(e){var t,n,i;e.preventDefault(),e.stopPropagation(),(t=E(e.target)).hasClass("datepicker-switch")&&this.viewMode!==this.o.maxViewMode&&this.setViewMode(this.viewMode+1),t.hasClass("today")&&!t.hasClass("day")&&(this.setViewMode(0),this._setDate(O(),"linked"===this.o.todayBtn?null:"view")),t.hasClass("clear")&&this.clearDates(),t.hasClass("disabled")||(t.hasClass("month")||t.hasClass("year")||t.hasClass("decade")||t.hasClass("century"))&&(this.viewDate.setUTCDate(1),1===this.viewMode?(i=t.parent().find("span").index(t),n=this.viewDate.getUTCFullYear(),this.viewDate.setUTCMonth(i)):(i=0,n=Number(t.text()),this.viewDate.setUTCFullYear(n)),this._trigger(I.viewModes[this.viewMode-1].e,this.viewDate),this.viewMode===this.o.minViewMode?this._setDate(A(n,i,1)):(this.setViewMode(this.viewMode-1),this.fill())),this.picker.is(":visible")&&this._focused_from&&this._focused_from.focus(),delete this._focused_from},dayCellClick:function(e){var t=E(e.currentTarget).data("date"),n=new Date(t);this.o.updateViewDate&&(n.getUTCFullYear()!==this.viewDate.getUTCFullYear()&&this._trigger("changeYear",this.viewDate),n.getUTCMonth()!==this.viewDate.getUTCMonth()&&this._trigger("changeMonth",this.viewDate)),this._setDate(n)},navArrowsClick:function(e){var t=E(e.currentTarget).hasClass("prev")?-1:1;0!==this.viewMode&&(t*=12*I.viewModes[this.viewMode].navStep),this.viewDate=this.moveMonth(this.viewDate,t),this._trigger(I.viewModes[this.viewMode].e,this.viewDate),this.fill()},_toggle_multidate:function(e){var t=this.dates.contains(e);if(e||this.dates.clear(),-1!==t?(!0===this.o.multidate||1this.o.multidate;)this.dates.remove(0)},_setDate:function(e,t){t&&"date"!==t||this._toggle_multidate(e&&new Date(e)),(!t&&this.o.updateViewDate||"view"===t)&&(this.viewDate=e&&new Date(e)),this.fill(),this.setValue(),t&&"view"===t||this._trigger("changeDate"),this.inputField.trigger("change"),!this.o.autoclose||t&&"date"!==t||this.hide()},moveDay:function(e,t){var n=new Date(e);return n.setUTCDate(e.getUTCDate()+t),n},moveWeek:function(e,t){return this.moveDay(e,7*t)},moveMonth:function(e,t){if(!function(e){return e&&!isNaN(e.getTime())}(e))return this.o.defaultViewDate;if(!t)return e;var n,i,r=new Date(e.valueOf()),a=r.getUTCDate(),o=r.getUTCMonth(),s=Math.abs(t);if(t=0=this.o.startDate&&e<=this.o.endDate},keydown:function(e){if(this.picker.is(":visible")){var t,n,i=!1,r=this.focusDate||this.viewDate;switch(e.keyCode){case 27:this.focusDate?(this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill()):this.hide(),e.preventDefault(),e.stopPropagation();break;case 37:case 38:case 39:case 40:if(!this.o.keyboardNavigation||7===this.o.daysOfWeekDisabled.length)break;t=37===e.keyCode||38===e.keyCode?-1:1,0===this.viewMode?e.ctrlKey?(n=this.moveAvailableDate(r,t,"moveYear"))&&this._trigger("changeYear",this.viewDate):e.shiftKey?(n=this.moveAvailableDate(r,t,"moveMonth"))&&this._trigger("changeMonth",this.viewDate):37===e.keyCode||39===e.keyCode?n=this.moveAvailableDate(r,t,"moveDay"):this.weekOfDateIsDisabled(r)||(n=this.moveAvailableDate(r,t,"moveWeek")):1===this.viewMode?(38!==e.keyCode&&40!==e.keyCode||(t*=4),n=this.moveAvailableDate(r,t,"moveMonth")):2===this.viewMode&&(38!==e.keyCode&&40!==e.keyCode||(t*=4),n=this.moveAvailableDate(r,t,"moveYear")),n&&(this.focusDate=this.viewDate=n,this.setValue(),this.fill(),e.preventDefault());break;case 13:if(!this.o.forceParse)break;r=this.focusDate||this.dates.get(-1)||this.viewDate,this.o.keyboardNavigation&&(this._toggle_multidate(r),i=!0),this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.setValue(),this.fill(),this.picker.is(":visible")&&(e.preventDefault(),e.stopPropagation(),this.o.autoclose&&this.hide());break;case 9:this.focusDate=null,this.viewDate=this.dates.get(-1)||this.viewDate,this.fill(),this.hide()}i&&(this.dates.length?this._trigger("changeDate"):this._trigger("clearDate"),this.inputField.trigger("change"))}else 40!==e.keyCode&&27!==e.keyCode||(this.show(),e.stopPropagation())},setViewMode:function(e){this.viewMode=e,this.picker.children("div").hide().filter(".datepicker-"+I.viewModes[this.viewMode].clsName).show(),this.updateNavArrows(),this._trigger("changeViewMode",new Date(this.viewDate))}};function u(e,t){E.data(e,"datepicker",this),this.element=E(e),this.inputs=E.map(t.inputs,function(e){return e.jquery?e[0]:e}),delete t.inputs,this.keepEmptyValues=t.keepEmptyValues,delete t.keepEmptyValues,r.call(E(this.inputs),t).on("changeDate",E.proxy(this.dateUpdated,this)),this.pickers=E.map(this.inputs,function(e){return E.data(e,"datepicker")}),this.updateDates()}u.prototype={updateDates:function(){this.dates=E.map(this.pickers,function(e){return e.getUTCDate()}),this.updateRanges()},updateRanges:function(){var n=E.map(this.dates,function(e){return e.valueOf()});E.each(this.pickers,function(e,t){t.setRange(n)})},clearDates:function(){E.each(this.pickers,function(e,t){t.clearDates()})},dateUpdated:function(e){if(!this.updating){this.updating=!0;var n=E.data(e.target,"datepicker");if(n!==M){var i=n.getUTCDate(),r=this.keepEmptyValues,t=E.inArray(e.target,this.inputs),a=t-1,o=t+1,s=this.inputs.length;if(-1!==t){if(E.each(this.pickers,function(e,t){t.getUTCDate()||t!==n&&r||t.setUTCDate(i)}),ithis.dates[o])for(;othis.dates[o];)this.pickers[o++].setUTCDate(i);this.updateDates(),delete this.updating}}}},destroy:function(){E.map(this.pickers,function(e){e.destroy()}),E(this.inputs).off("changeDate",this.dateUpdated),delete this.element.data().datepicker},remove:e("destroy","Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead")};var i=E.fn.datepicker,r=function(o){var s,l=Array.apply(null,arguments);if(l.shift(),this.each(function(){var e=E(this),t=e.data("datepicker"),n="object"==typeof o&&o;if(!t){var i=function(e,t){function n(e,t){return t.toLowerCase()}var i=E(e).data(),r={},a=new RegExp("^"+t.toLowerCase()+"([A-Z])");for(var o in t=new RegExp("^"+t.toLowerCase()),i)t.test(o)&&(r[o.replace(a,n)]=i[o]);return r}(this,"date"),r=function(e){var n={};if(N[e]||(e=e.split("-")[0],N[e])){var i=N[e];return E.each(d,function(e,t){t in i&&(n[t]=i[t])}),n}}(E.extend({},c,i,n).language),a=E.extend({},c,r,i,n);t=e.hasClass("input-daterange")||a.inputs?(E.extend(a,{inputs:a.inputs||e.find("input").toArray()}),new u(this,a)):new w(this,a),e.data("datepicker",t)}"string"==typeof o&&"function"==typeof t[o]&&(s=t[o].apply(t,l))}),s===M||s instanceof w||s instanceof u)return this;if(1(new Date).getFullYear()+t&&(e-=100),e}(t,i):t)},m:function(e,t){if(isNaN(e))return e;for(t-=1;t<0;)t+=12;for(t%=12,e.setUTCMonth(t);e.getUTCMonth()!==t;)e.setUTCDate(e.getUTCDate()-1);return e},d:function(e,t){return e.setUTCDate(t)}};g.yy=g.yyyy,g.M=g.MM=g.mm=g.m,g.dd=g.d,e=O();var v=t.parts.slice();if(a.length!==v.length&&(v=E(v).filter(function(e,t){return-1!==E.inArray(t,m)}).toArray()),a.length===v.length){var y,b,_;for(l=0,y=v.length;l",contTemplate:'',footTemplate:''};I.template='
    ").addClass("cw").text("#"));t.isBefore(this._viewDate.clone().endOf("w"));)e.append(S("").addClass("dow").text(t.format("dd"))),t.add(1,"d");this.widget.find(".datepicker-days thead").append(e)},E.prototype._fillMonths=function(){for(var e=[],t=this._viewDate.clone().startOf("y").startOf("d");t.isSame(this._viewDate,"y");)e.push(S("").attr("data-action","selectMonth").addClass("month").text(t.format("MMM"))),t.add(1,"M");this.widget.find(".datepicker-months td").empty().append(e)},E.prototype._updateMonths=function(){var e=this.widget.find(".datepicker-months"),t=e.find("th"),n=e.find("tbody").find("span"),i=this;t.eq(0).find("span").attr("title",this._options.tooltips.prevYear),t.eq(1).attr("title",this._options.tooltips.selectYear),t.eq(2).find("span").attr("title",this._options.tooltips.nextYear),e.find(".disabled").removeClass("disabled"),this._isValid(this._viewDate.clone().subtract(1,"y"),"y")||t.eq(0).addClass("disabled"),t.eq(1).text(this._viewDate.year()),this._isValid(this._viewDate.clone().add(1,"y"),"y")||t.eq(2).addClass("disabled"),n.removeClass("active"),this._getLastPickedDate().isSame(this._viewDate,"y")&&!this.unset&&n.eq(this._getLastPickedDate().month()).addClass("active"),n.each(function(e){i._isValid(i._viewDate.clone().month(e),"M")||S(this).addClass("disabled")})},E.prototype._getStartEndYear=function(e,t){var n=e/10,i=Math.floor(t/e)*e;return[i,i+9*n,Math.floor(t/n)*n]},E.prototype._updateYears=function(){var e=this.widget.find(".datepicker-years"),t=e.find("th"),n=this._getStartEndYear(10,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),a="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevDecade),t.eq(1).attr("title",this._options.tooltips.selectDecade),t.eq(2).find("span").attr("title",this._options.tooltips.nextDecade),e.find(".disabled").removeClass("disabled"),this._options.minDate&&this._options.minDate.isAfter(i,"y")&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),a+=''+(i.year()-1)+"";!i.isAfter(r,"y");)a+=''+i.year()+"",i.add(1,"y");a+=''+i.year()+"",e.find("td").html(a)},E.prototype._updateDecades=function(){var e=this.widget.find(".datepicker-decades"),t=e.find("th"),n=this._getStartEndYear(100,this._viewDate.year()),i=this._viewDate.clone().year(n[0]),r=this._viewDate.clone().year(n[1]),a=!1,o=!1,s=void 0,l="";for(t.eq(0).find("span").attr("title",this._options.tooltips.prevCentury),t.eq(2).find("span").attr("title",this._options.tooltips.nextCentury),e.find(".disabled").removeClass("disabled"),(0===i.year()||this._options.minDate&&this._options.minDate.isAfter(i,"y"))&&t.eq(0).addClass("disabled"),t.eq(1).text(i.year()+"-"+r.year()),this._options.maxDate&&this._options.maxDate.isBefore(r,"y")&&t.eq(2).addClass("disabled"),i.year()-10<0?l+=" ":l+=''+(i.year()-10)+"";!i.isAfter(r,"y");)s=i.year()+11,a=this._options.minDate&&this._options.minDate.isAfter(i,"y")&&this._options.minDate.year()<=s,o=this._options.maxDate&&this._options.maxDate.isAfter(i,"y")&&this._options.maxDate.year()<=s,l+=''+i.year()+"",i.add(10,"y");l+=''+i.year()+"",e.find("td").html(l)},E.prototype._fillDate=function(){var e=this.widget.find(".datepicker-days"),t=e.find("th"),n=[],i=void 0,r=void 0,a=void 0,o=void 0;if(this._hasDate()){for(t.eq(0).find("span").attr("title",this._options.tooltips.prevMonth),t.eq(1).attr("title",this._options.tooltips.selectMonth),t.eq(2).find("span").attr("title",this._options.tooltips.nextMonth),e.find(".disabled").removeClass("disabled"),t.eq(1).text(this._viewDate.format(this._options.dayViewHeaderFormat)),this._isValid(this._viewDate.clone().subtract(1,"M"),"M")||t.eq(0).addClass("disabled"),this._isValid(this._viewDate.clone().add(1,"M"),"M")||t.eq(2).addClass("disabled"),i=this._viewDate.clone().startOf("M").startOf("w").startOf("d"),o=0;o<42;o++){if(0===i.weekday()&&(r=S("
    '+i.week()+"'+i.date()+"
    '+t.format(this.use24Hours?"HH":"hh")+"
    '+t.format("mm")+"
    '+t.format("ss")+"
     
    '+x+"'+C+"
    '+c.templates.leftArrow+''+c.templates.rightArrow+"
    '+I.headTemplate+""+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+'
    '+I.headTemplate+I.contTemplate+I.footTemplate+"
    ",E.fn.datepicker.DPGlobal=I,E.fn.datepicker.noConflict=function(){return E.fn.datepicker=i,this},E.fn.datepicker.version="1.9.0",E.fn.datepicker.deprecated=function(e){var t=window.console;t&&t.warn&&t.warn("DEPRECATED: "+e)},E(document).on("focus.datepicker.data-api click.datepicker.data-api",'[data-provide="datepicker"]',function(e){var t=E(this);t.data("datepicker")||(e.preventDefault(),r.call(t,"show"))}),E(function(){r.call(E('[data-provide="datepicker-inline"]'))})}),jQuery.fn.datepicker.dates.fa={days:["یک‌شنبه","دوشنبه","سه‌شنبه","چهارشنبه","پنج‌شنبه","جمعه","شنبه","یک‌شنبه"],daysShort:["یک","دو","سه","چهار","پنج","جمعه","شنبه","یک"],daysMin:["ی","د","س","چ","پ","ج","ش","ی"],months:["ژانویه","فوریه","مارس","آوریل","مه","ژوئن","ژوئیه","اوت","سپتامبر","اکتبر","نوامبر","دسامبر"],monthsShort:["ژان","فور","مار","آور","مه","ژون","ژوی","اوت","سپت","اکت","نوا","دسا"],today:"امروز",clear:"پاک کن",weekStart:1,format:"yyyy/mm/dd"},jQuery.fn.datepicker.dates.fr={days:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],daysShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],daysMin:["d","l","ma","me","j","v","s"],months:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthsShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],today:"Aujourd'hui",monthsTitle:"Mois",clear:"Effacer",weekStart:1,format:"dd/mm/yyyy"},jQuery.fn.datepicker.dates.ru={days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вск","Пнд","Втр","Срд","Чтв","Птн","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",format:"dd.mm.yyyy",weekStart:1,monthsTitle:"Месяцы"},jQuery.fn.datepicker.dates.sv={days:["söndag","måndag","tisdag","onsdag","torsdag","fredag","lördag"],daysShort:["sön","mån","tis","ons","tor","fre","lör"],daysMin:["sö","må","ti","on","to","fr","lö"],months:["januari","februari","mars","april","maj","juni","juli","augusti","september","oktober","november","december"],monthsShort:["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"],today:"Idag",format:"yyyy-mm-dd",weekStart:1,clear:"Rensa"},jQuery.fn.datepicker.dates["zh-CN"]={days:["星期日","星期一","星期二","星期三","星期四","星期五","星期六"],daysShort:["周日","周一","周二","周三","周四","周五","周六"],daysMin:["日","一","二","三","四","五","六"],months:["一月","二月","三月","四月","五月","六月","七月","八月","九月","十月","十一月","十二月"],monthsShort:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],today:"今天",monthsTitle:"选择月份",clear:"清除",format:"yyyy-mm-dd",titleFormat:"yyyy年mm月",weekStart:1};var Menu={init:function(){$(function(){Menu.itemClick()})},itemClick:function(){$(".menu-button").click(function(e){e.preventDefault(),$(".menu-item").is(":visible")?$(".menu-item").css("display",""):$(".menu-item").show()})}};Menu.init(),ko.bindingHandlers.modal={init:function(e,t){$(e).modal({show:!1});var n=t();ko.isObservable(n)&&$(e).on("hidden.bs.modal",function(){n(!1)})},update:function(e,t){var n=t();ko.utils.unwrapObservable(n)?$(e).modal("show"):$(e).modal("hide")}},ko.components.register("picker",{viewModel:function(n){var i=this;this.textTerm=ko.observable("").extend({rateLimit:500}),this.minSearchText=ko.observable(n.minSearchText||2),this.multipleSelect=ko.observable(n.multipleSelect||!1),this.searchInputPlaceholder=ko.observable(n.searchInputPlaceholder||"Enter "+this.minSearchText()+" or more characters"),this.selectedItemsTitle=ko.observable(n.selectedItemsTitle||"Selected: "),this.searchResultTitle=ko.observable(n.searchResultTitle||"Search result: "),this.suggestedItemsTitle=ko.observable(n.suggestedItemsTitle||"Suggested items: "),this.noItemSelectedTitle=ko.observable(n.noItemSelectedTitle||"No item/s selected"),this.showAllItemsTitle=ko.observable(n.showAllItemsTitle||"more"),this.allowSuggestedItems=ko.observable(n.allowSuggestedItems&&n.url||!1),this.topSuggestedItems=ko.observable(n.topSuggestedItems||5),this.allowItemAlreadySelectedNotification=ko.observable(n.allowItemAlreadySelectedNotification||!0),this.itemAlreadySelectedTitle=ko.observable(n.itemAlreadySelectedTitle||"item already selected"),this.searchResult=ko.observableArray([]),this.selectedResult=ko.observableArray(n.selectedItems||[]),this.suggestedResult=ko.observableArray([]),this.loading=ko.observable(!1),this.isVisibleEditDialog=ko.observable(!1),this.editedItem=ko.observable(""),this.editedItemOriginal=ko.observable("");var e=ko.toJSON(this.selectedResult);!0===this.multipleSelect()?0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(e):0===this.selectedResult().length?$("#"+n.hiddenId).val(""):$("#"+n.hiddenId).val(this.selectedResult()[0]),this.textTerm.subscribe(function(t){""===t.trim()&&i.searchResult([]),""!==t.trim()&&t.trim().length>=i.minSearchText()&&(n.url?(i.loading(!0),$.get(n.url+"="+t,function(e){-1===e.indexOf(t)&&e.push(t),i.searchResult(e),i.loading(!1)})):i.searchResult([t]))}),this.notify=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.info(e+" "+this.itemAlreadySelectedTitle())},this.notifyError=function(e){toastr.options.closeButton=!0,toastr.options.preventDuplicates=!0,toastr.error(e)},this.add=function(e){e=e.replace(/'/g,"").replace(/"/g,""),-1

    Loading..

    \x3c!-- ko foreach: suggestedResult --\x3e\x3c!-- /ko --\x3e
    '}),ko.applyBindings(),Holder.addTheme("thumb",{bg:"#55595c",fg:"#eceeef",text:"Thumbnail"});var FormMvc={allowValidateHiddenField:function(e){e.data("validator").settings.ignore=""},disableEnter:function(e){e.on("keyup keypress",function(e){if(13===(e.keyCode||e.which))return e.preventDefault(),!1})}};$(function(){$(".single-select").removeAttr("multiple"),$('[data-toggle="tooltip"]').tooltip()});var JSONTree=function(){var t={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"},e=0,n=0;this.create=function(e,t){return n+=1,p(s(e,0,!1),{class:"jstValue"})};function a(e){return e.replace(/[&<>'"]/g,function(e){return t[e]})}function o(){return n+"_"+e++}var s=function(e,t,n){if(null===e)return d(n?t:0);switch(typeof e){case"boolean":return c(e,n?t:0);case"number":return u(e,n?t:0);case"string":return l(e,n?t:0);default:return e instanceof Array?r(e,t,n):i(e,t,n)}},i=function(t,n,e){var i=o(),r=Object.keys(t).map(function(e){return h(e,t[e],n+1,!0)}).join(f()),a=[g("{",e?n:0,i),p(r,{id:i}),v("}",n)].join("\n");return p(a,{})},r=function(e,t,n){var i=o(),r=e.map(function(e){return s(e,t+1,!0)}).join(f());return[g("[",n?t:0,i),p(r,{id:i}),v("]",t)].join("\n")},l=function(e,t){var n=a(JSON.stringify(e));return p(y(n,t),{class:"jstStr"})},u=function(e,t){return p(y(e,t),{class:"jstNum"})},c=function(e,t){return p(y(e,t),{class:"jstBool"})},d=function(e){return p(y("null",e),{class:"jstNull"})},h=function(e,t,n){var i=y(a(JSON.stringify(e))+": ",n),r=p(s(t,n,!1),{});return p(i+r,{class:"jstProperty"})},f=function(){return p(",\n",{class:"jstComma"})},p=function(e,t){return m("span",t,e)},m=function(e,t,n){return"<"+e+Object.keys(t).map(function(e){return" "+e+'="'+t[e]+'"'}).join("")+">"+n+""},g=function(e,t,n){return p(y(e,t),{class:"jstBracket"})+p("",{class:"jstFold",onclick:"JSONTree.toggle('"+n+"')"})};this.toggle=function(e){var t=document.getElementById(e),n=t.parentNode,i=t.previousElementSibling;""===t.className?(t.className="jstHiddenBlock",n.className="jstFolded",i.className="jstExpand"):(t.className="",n.className="",i.className="jstFold")};var v=function(e,t){return p(y(e,t),{})},y=function(e,t){return Array(2*t+1).join(" ")+e};return this}();$(function(){$(".local-datetime").each(function(){var e=$(this),t=parseInt(e.attr("data-utc"),10)||0;if(t){var n=moment.utc(t).local().format("DD MMM YYYY HH:mm");e.text(n)}}),$('[data-toggle="tooltip"]').tooltip()});var errorLog={eventHandlers:function(){$(".error-log-delete-button").click(function(){return $(".error-log-form").validate(),$(".error-log-form").validate().form()?$("#deleteLogsModal").modal("show"):$(this).submit(),!1}),$(".row-error-detail>td").each(function(){var t,n=$(this).data("error-json");try{t=JSONTree.create(JSON.parse(n))}catch(e){t=JSONTree.create(n)}$(this).html(t)}),$(".btn-error-detail").click(function(e){e.preventDefault();var t=$(this).data("error-id");return $(".row-error-detail[data-error-id="+t+"]").is(":visible")?$(".row-error-detail[data-error-id="+t+"]").addClass("d-none"):$(".row-error-detail[data-error-id="+t+"]").removeClass("d-none"),!1})},init:function(){$(function(){errorLog.eventHandlers()})}};errorLog.init();var auditLog={createJsonTree:function(t){var n;try{n=JSONTree.create(JSON.parse(t))}catch(e){n=JSONTree.create(t)}return n},initJsonTrees:function(){$(".json-tree").each(function(){var e=$(this).data("json-tree"),t=auditLog.createJsonTree(e);$(this).html(t)})},eventHandlers:function(){$(".audit-subject-button").click(function(){var e=$(this).data("subject-identifier"),t=$(this).data("subject-name"),n=$(this).data("subject-type"),i=$(this).data("subject-additional-data");$(".modal-title").html(t+" - "+e+" - ("+n+")"),$(".audit-modal-value").html(auditLog.createJsonTree(i)),$(".audit-modal").modal("show")}),$(".audit-action-button").click(function(){var e=$(this).data("action"),t=$(this).data("action-title");$(".modal-title").html(t),$(".audit-modal-value").html(auditLog.createJsonTree(e)),$(".audit-modal").modal("show")}),$(".audit-log-delete-button").click(function(){return $(".audit-log-form").validate(),$(".audit-log-form").validate().form()?$("#deleteLogsModal").modal("show"):$(this).submit(),!1})},init:function(){$(function(){auditLog.eventHandlers(),auditLog.initJsonTrees()})}};auditLog.init(),$(function(){var e={guid:function(){return"ss-s-s-s-sss".replace(/s/g,e.s4)},s4:function(){return Math.floor(65536*(1+Math.random())).toString(16).substring(1)},eventHandlers:function(){$("#generate-guid-button").click(function(){$("#secret-input").val(e.guid())}),$(".secret-value-button").click(function(){var e=$(this).data("secret-value");$(".modal-secret-value").html(e),$(".secret-modal").modal("show")})},init:function(){e.eventHandlers()}};e.init()}),$(function(){var t={getCookie:function(e){for(var t=e+"=",n=document.cookie.split(";"),i=0;i .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - 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 #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\2a"; -} -.glyphicon-plus:before { - content: "\2b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .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 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .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 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .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 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 14.333333px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled, -.btn-default[disabled], -fieldset[disabled] .btn-default, -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus, -.btn-default.disabled:active, -.btn-default[disabled]:active, -fieldset[disabled] .btn-default:active, -.btn-default.disabled.active, -.btn-default[disabled].active, -fieldset[disabled] .btn-default.active { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled, -.btn-primary[disabled], -fieldset[disabled] .btn-primary, -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus, -.btn-primary.disabled:active, -.btn-primary[disabled]:active, -fieldset[disabled] .btn-primary:active, -.btn-primary.disabled.active, -.btn-primary[disabled].active, -fieldset[disabled] .btn-primary.active { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled, -.btn-success[disabled], -fieldset[disabled] .btn-success, -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus, -.btn-success.disabled:active, -.btn-success[disabled]:active, -fieldset[disabled] .btn-success:active, -.btn-success.disabled.active, -.btn-success[disabled].active, -fieldset[disabled] .btn-success.active { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled, -.btn-info[disabled], -fieldset[disabled] .btn-info, -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus, -.btn-info.disabled:active, -.btn-info[disabled]:active, -fieldset[disabled] .btn-info:active, -.btn-info.disabled.active, -.btn-info[disabled].active, -fieldset[disabled] .btn-info.active { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled, -.btn-warning[disabled], -fieldset[disabled] .btn-warning, -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus, -.btn-warning.disabled:active, -.btn-warning[disabled]:active, -fieldset[disabled] .btn-warning:active, -.btn-warning.disabled.active, -.btn-warning[disabled].active, -fieldset[disabled] .btn-warning.active { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled, -.btn-danger[disabled], -fieldset[disabled] .btn-danger, -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus, -.btn-danger.disabled:active, -.btn-danger[disabled]:active, -fieldset[disabled] .btn-danger:active, -.btn-danger.disabled.active, -.btn-danger[disabled].active, -fieldset[disabled] .btn-danger.active { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 3; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 2; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.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-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - min-height: 16.42857143px; - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -15px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -15px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -15px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/src/Monolith/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map b/src/Monolith/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map deleted file mode 100644 index 9f60ed2b1..000000000 --- a/src/Monolith/ClassifiedAds.IdentityServer/wwwroot/lib/bootstrap/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,eAAA;CH8O9C;AG7OmC;EAAW,eAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EErDA,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNqkCD;AIxgCD;EACE,UAAA;CJ0gCD;AIpgCD;EACE,uBAAA;CJsgCD;AIlgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CPglCD;AItgCD;EACE,mBAAA;CJwgCD;AIlgCD;EACE,aAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CPgmCD;AIlgCD;EACE,mBAAA;CJogCD;AI9/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJggCD;AIx/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJ0/BD;AIl/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJo/BH;AIz+BD;EACE,gBAAA;CJ2+BD;AQloCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR8oCD;AQnpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRoqCH;AQhqCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRqqCD;AQzqCD;;;;;;;;;;;;EAQI,eAAA;CR+qCH;AQ5qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRirCD;AQrrCD;;;;;;;;;;;;EAQI,eAAA;CR2rCH;AQvrCD;;EAAU,gBAAA;CR2rCT;AQ1rCD;;EAAU,gBAAA;CR8rCT;AQ7rCD;;EAAU,gBAAA;CRisCT;AQhsCD;;EAAU,gBAAA;CRosCT;AQnsCD;;EAAU,gBAAA;CRusCT;AQtsCD;;EAAU,gBAAA;CR0sCT;AQpsCD;EACE,iBAAA;CRssCD;AQnsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRqsCD;AQhsCD;EAAA;IAFI,gBAAA;GRssCD;CACF;AQ9rCD;;EAEE,eAAA;CRgsCD;AQ7rCD;;EAEE,0BAAA;EACA,cAAA;CR+rCD;AQ3rCD;EAAuB,iBAAA;CR8rCtB;AQ7rCD;EAAuB,kBAAA;CRgsCtB;AQ/rCD;EAAuB,mBAAA;CRksCtB;AQjsCD;EAAuB,oBAAA;CRosCtB;AQnsCD;EAAuB,oBAAA;CRssCtB;AQnsCD;EAAuB,0BAAA;CRssCtB;AQrsCD;EAAuB,0BAAA;CRwsCtB;AQvsCD;EAAuB,2BAAA;CR0sCtB;AQvsCD;EACE,eAAA;CRysCD;AQvsCD;ECrGE,eAAA;CT+yCD;AS9yCC;;EAEE,eAAA;CTgzCH;AQ3sCD;ECxGE,eAAA;CTszCD;ASrzCC;;EAEE,eAAA;CTuzCH;AQ/sCD;EC3GE,eAAA;CT6zCD;AS5zCC;;EAEE,eAAA;CT8zCH;AQntCD;EC9GE,eAAA;CTo0CD;ASn0CC;;EAEE,eAAA;CTq0CH;AQvtCD;ECjHE,eAAA;CT20CD;AS10CC;;EAEE,eAAA;CT40CH;AQvtCD;EAGE,YAAA;EE3HA,0BAAA;CVm1CD;AUl1CC;;EAEE,0BAAA;CVo1CH;AQztCD;EE9HE,0BAAA;CV01CD;AUz1CC;;EAEE,0BAAA;CV21CH;AQ7tCD;EEjIE,0BAAA;CVi2CD;AUh2CC;;EAEE,0BAAA;CVk2CH;AQjuCD;EEpIE,0BAAA;CVw2CD;AUv2CC;;EAEE,0BAAA;CVy2CH;AQruCD;EEvIE,0BAAA;CV+2CD;AU92CC;;EAEE,0BAAA;CVg3CH;AQpuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRsuCD;AQ9tCD;;EAEE,cAAA;EACA,oBAAA;CRguCD;AQnuCD;;;;EAMI,iBAAA;CRmuCH;AQ5tCD;EACE,gBAAA;EACA,iBAAA;CR8tCD;AQ1tCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR6tCD;AQ/tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR6tCH;AQxtCD;EACE,cAAA;EACA,oBAAA;CR0tCD;AQxtCD;;EAEE,wBAAA;CR0tCD;AQxtCD;EACE,kBAAA;CR0tCD;AQxtCD;EACE,eAAA;CR0tCD;AQjsCD;EAAA;IAVM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXs6CC;EQ3sCH;IAHM,mBAAA;GRitCH;CACF;AQxsCD;;EAGE,aAAA;EACA,kCAAA;CRysCD;AQvsCD;EACE,eAAA;EA9IqB,0BAAA;CRw1CtB;AQrsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRusCD;AQlsCG;;;EACE,iBAAA;CRssCL;AQhtCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRksCH;AQhsCG;;;EACE,uBAAA;CRosCL;AQ5rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR8rCD;AQxrCG;;;;;;EAAW,YAAA;CRgsCd;AQ/rCG;;;;;;EACE,uBAAA;CRssCL;AQhsCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRksCD;AYx+CD;;;;EAIE,+DAAA;CZ0+CD;AYt+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZw+CD;AYp+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZs+CD;AY5+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZs+CH;AYj+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;CZm+CD;AY9+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZk+CH;AY79CD;EACE,kBAAA;EACA,mBAAA;CZ+9CD;AazhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd+hDD;AazhDC;EAAA;IAFE,aAAA;Gb+hDD;CACF;Aa3hDC;EAAA;IAFE,aAAA;GbiiDD;CACF;Aa7hDD;EAAA;IAFI,cAAA;GbmiDD;CACF;Aa1hDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdojDD;AavhDD;ECvBE,mBAAA;EACA,oBAAA;CdijDD;AejjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfijDL;AejiDG;EACE,YAAA;CfmiDL;Ae5hDC;EACE,YAAA;Cf8hDH;Ae/hDC;EACE,oBAAA;CfiiDH;AeliDC;EACE,oBAAA;CfoiDH;AeriDC;EACE,WAAA;CfuiDH;AexiDC;EACE,oBAAA;Cf0iDH;Ae3iDC;EACE,oBAAA;Cf6iDH;Ae9iDC;EACE,WAAA;CfgjDH;AejjDC;EACE,oBAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,WAAA;CfyjDH;Ae1jDC;EACE,oBAAA;Cf4jDH;Ae7jDC;EACE,mBAAA;Cf+jDH;AejjDC;EACE,YAAA;CfmjDH;AepjDC;EACE,oBAAA;CfsjDH;AevjDC;EACE,oBAAA;CfyjDH;Ae1jDC;EACE,WAAA;Cf4jDH;Ae7jDC;EACE,oBAAA;Cf+jDH;AehkDC;EACE,oBAAA;CfkkDH;AenkDC;EACE,WAAA;CfqkDH;AetkDC;EACE,oBAAA;CfwkDH;AezkDC;EACE,oBAAA;Cf2kDH;Ae5kDC;EACE,WAAA;Cf8kDH;Ae/kDC;EACE,oBAAA;CfilDH;AellDC;EACE,mBAAA;CfolDH;AehlDC;EACE,YAAA;CfklDH;AelmDC;EACE,WAAA;CfomDH;AermDC;EACE,mBAAA;CfumDH;AexmDC;EACE,mBAAA;Cf0mDH;Ae3mDC;EACE,UAAA;Cf6mDH;Ae9mDC;EACE,mBAAA;CfgnDH;AejnDC;EACE,mBAAA;CfmnDH;AepnDC;EACE,UAAA;CfsnDH;AevnDC;EACE,mBAAA;CfynDH;Ae1nDC;EACE,mBAAA;Cf4nDH;Ae7nDC;EACE,UAAA;Cf+nDH;AehoDC;EACE,mBAAA;CfkoDH;AenoDC;EACE,kBAAA;CfqoDH;AejoDC;EACE,WAAA;CfmoDH;AernDC;EACE,kBAAA;CfunDH;AexnDC;EACE,0BAAA;Cf0nDH;Ae3nDC;EACE,0BAAA;Cf6nDH;Ae9nDC;EACE,iBAAA;CfgoDH;AejoDC;EACE,0BAAA;CfmoDH;AepoDC;EACE,0BAAA;CfsoDH;AevoDC;EACE,iBAAA;CfyoDH;Ae1oDC;EACE,0BAAA;Cf4oDH;Ae7oDC;EACE,0BAAA;Cf+oDH;AehpDC;EACE,iBAAA;CfkpDH;AenpDC;EACE,0BAAA;CfqpDH;AetpDC;EACE,yBAAA;CfwpDH;AezpDC;EACE,gBAAA;Cf2pDH;Aa3pDD;EElCI;IACE,YAAA;GfgsDH;EezrDD;IACE,YAAA;Gf2rDD;Ee5rDD;IACE,oBAAA;Gf8rDD;Ee/rDD;IACE,oBAAA;GfisDD;EelsDD;IACE,WAAA;GfosDD;EersDD;IACE,oBAAA;GfusDD;EexsDD;IACE,oBAAA;Gf0sDD;Ee3sDD;IACE,WAAA;Gf6sDD;Ee9sDD;IACE,oBAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,WAAA;GfstDD;EevtDD;IACE,oBAAA;GfytDD;Ee1tDD;IACE,mBAAA;Gf4tDD;Ee9sDD;IACE,YAAA;GfgtDD;EejtDD;IACE,oBAAA;GfmtDD;EeptDD;IACE,oBAAA;GfstDD;EevtDD;IACE,WAAA;GfytDD;Ee1tDD;IACE,oBAAA;Gf4tDD;Ee7tDD;IACE,oBAAA;Gf+tDD;EehuDD;IACE,WAAA;GfkuDD;EenuDD;IACE,oBAAA;GfquDD;EetuDD;IACE,oBAAA;GfwuDD;EezuDD;IACE,WAAA;Gf2uDD;Ee5uDD;IACE,oBAAA;Gf8uDD;Ee/uDD;IACE,mBAAA;GfivDD;Ee7uDD;IACE,YAAA;Gf+uDD;Ee/vDD;IACE,WAAA;GfiwDD;EelwDD;IACE,mBAAA;GfowDD;EerwDD;IACE,mBAAA;GfuwDD;EexwDD;IACE,UAAA;Gf0wDD;Ee3wDD;IACE,mBAAA;Gf6wDD;Ee9wDD;IACE,mBAAA;GfgxDD;EejxDD;IACE,UAAA;GfmxDD;EepxDD;IACE,mBAAA;GfsxDD;EevxDD;IACE,mBAAA;GfyxDD;Ee1xDD;IACE,UAAA;Gf4xDD;Ee7xDD;IACE,mBAAA;Gf+xDD;EehyDD;IACE,kBAAA;GfkyDD;Ee9xDD;IACE,WAAA;GfgyDD;EelxDD;IACE,kBAAA;GfoxDD;EerxDD;IACE,0BAAA;GfuxDD;EexxDD;IACE,0BAAA;Gf0xDD;Ee3xDD;IACE,iBAAA;Gf6xDD;Ee9xDD;IACE,0BAAA;GfgyDD;EejyDD;IACE,0BAAA;GfmyDD;EepyDD;IACE,iBAAA;GfsyDD;EevyDD;IACE,0BAAA;GfyyDD;Ee1yDD;IACE,0BAAA;Gf4yDD;Ee7yDD;IACE,iBAAA;Gf+yDD;EehzDD;IACE,0BAAA;GfkzDD;EenzDD;IACE,yBAAA;GfqzDD;EetzDD;IACE,gBAAA;GfwzDD;CACF;AahzDD;EE3CI;IACE,YAAA;Gf81DH;Eev1DD;IACE,YAAA;Gfy1DD;Ee11DD;IACE,oBAAA;Gf41DD;Ee71DD;IACE,oBAAA;Gf+1DD;Eeh2DD;IACE,WAAA;Gfk2DD;Een2DD;IACE,oBAAA;Gfq2DD;Eet2DD;IACE,oBAAA;Gfw2DD;Eez2DD;IACE,WAAA;Gf22DD;Ee52DD;IACE,oBAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,WAAA;Gfo3DD;Eer3DD;IACE,oBAAA;Gfu3DD;Eex3DD;IACE,mBAAA;Gf03DD;Ee52DD;IACE,YAAA;Gf82DD;Ee/2DD;IACE,oBAAA;Gfi3DD;Eel3DD;IACE,oBAAA;Gfo3DD;Eer3DD;IACE,WAAA;Gfu3DD;Eex3DD;IACE,oBAAA;Gf03DD;Ee33DD;IACE,oBAAA;Gf63DD;Ee93DD;IACE,WAAA;Gfg4DD;Eej4DD;IACE,oBAAA;Gfm4DD;Eep4DD;IACE,oBAAA;Gfs4DD;Eev4DD;IACE,WAAA;Gfy4DD;Ee14DD;IACE,oBAAA;Gf44DD;Ee74DD;IACE,mBAAA;Gf+4DD;Ee34DD;IACE,YAAA;Gf64DD;Ee75DD;IACE,WAAA;Gf+5DD;Eeh6DD;IACE,mBAAA;Gfk6DD;Een6DD;IACE,mBAAA;Gfq6DD;Eet6DD;IACE,UAAA;Gfw6DD;Eez6DD;IACE,mBAAA;Gf26DD;Ee56DD;IACE,mBAAA;Gf86DD;Ee/6DD;IACE,UAAA;Gfi7DD;Eel7DD;IACE,mBAAA;Gfo7DD;Eer7DD;IACE,mBAAA;Gfu7DD;Eex7DD;IACE,UAAA;Gf07DD;Ee37DD;IACE,mBAAA;Gf67DD;Ee97DD;IACE,kBAAA;Gfg8DD;Ee57DD;IACE,WAAA;Gf87DD;Eeh7DD;IACE,kBAAA;Gfk7DD;Een7DD;IACE,0BAAA;Gfq7DD;Eet7DD;IACE,0BAAA;Gfw7DD;Eez7DD;IACE,iBAAA;Gf27DD;Ee57DD;IACE,0BAAA;Gf87DD;Ee/7DD;IACE,0BAAA;Gfi8DD;Eel8DD;IACE,iBAAA;Gfo8DD;Eer8DD;IACE,0BAAA;Gfu8DD;Eex8DD;IACE,0BAAA;Gf08DD;Ee38DD;IACE,iBAAA;Gf68DD;Ee98DD;IACE,0BAAA;Gfg9DD;Eej9DD;IACE,yBAAA;Gfm9DD;Eep9DD;IACE,gBAAA;Gfs9DD;CACF;Aa38DD;EE9CI;IACE,YAAA;Gf4/DH;Eer/DD;IACE,YAAA;Gfu/DD;Eex/DD;IACE,oBAAA;Gf0/DD;Ee3/DD;IACE,oBAAA;Gf6/DD;Ee9/DD;IACE,WAAA;GfggED;EejgED;IACE,oBAAA;GfmgED;EepgED;IACE,oBAAA;GfsgED;EevgED;IACE,WAAA;GfygED;Ee1gED;IACE,oBAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,WAAA;GfkhED;EenhED;IACE,oBAAA;GfqhED;EethED;IACE,mBAAA;GfwhED;Ee1gED;IACE,YAAA;Gf4gED;Ee7gED;IACE,oBAAA;Gf+gED;EehhED;IACE,oBAAA;GfkhED;EenhED;IACE,WAAA;GfqhED;EethED;IACE,oBAAA;GfwhED;EezhED;IACE,oBAAA;Gf2hED;Ee5hED;IACE,WAAA;Gf8hED;Ee/hED;IACE,oBAAA;GfiiED;EeliED;IACE,oBAAA;GfoiED;EeriED;IACE,WAAA;GfuiED;EexiED;IACE,oBAAA;Gf0iED;Ee3iED;IACE,mBAAA;Gf6iED;EeziED;IACE,YAAA;Gf2iED;Ee3jED;IACE,WAAA;Gf6jED;Ee9jED;IACE,mBAAA;GfgkED;EejkED;IACE,mBAAA;GfmkED;EepkED;IACE,UAAA;GfskED;EevkED;IACE,mBAAA;GfykED;Ee1kED;IACE,mBAAA;Gf4kED;Ee7kED;IACE,UAAA;Gf+kED;EehlED;IACE,mBAAA;GfklED;EenlED;IACE,mBAAA;GfqlED;EetlED;IACE,UAAA;GfwlED;EezlED;IACE,mBAAA;Gf2lED;Ee5lED;IACE,kBAAA;Gf8lED;Ee1lED;IACE,WAAA;Gf4lED;Ee9kED;IACE,kBAAA;GfglED;EejlED;IACE,0BAAA;GfmlED;EeplED;IACE,0BAAA;GfslED;EevlED;IACE,iBAAA;GfylED;Ee1lED;IACE,0BAAA;Gf4lED;Ee7lED;IACE,0BAAA;Gf+lED;EehmED;IACE,iBAAA;GfkmED;EenmED;IACE,0BAAA;GfqmED;EetmED;IACE,0BAAA;GfwmED;EezmED;IACE,iBAAA;Gf2mED;Ee5mED;IACE,0BAAA;Gf8mED;Ee/mED;IACE,yBAAA;GfinED;EelnED;IACE,gBAAA;GfonED;CACF;AgBxrED;EACE,8BAAA;ChB0rED;AgBxrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChB0rED;AgBxrED;EACE,iBAAA;ChB0rED;AgBprED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBsrED;AgBzrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,8BAAA;ChBsrEP;AgBpsED;EAoBI,uBAAA;EACA,iCAAA;ChBmrEH;AgBxsED;;;;;;EA8BQ,cAAA;ChBkrEP;AgBhtED;EAoCI,8BAAA;ChB+qEH;AgBntED;EAyCI,0BAAA;ChB6qEH;AgBtqED;;;;;;EAOQ,aAAA;ChBuqEP;AgB5pED;EACE,0BAAA;ChB8pED;AgB/pED;;;;;;EAQQ,0BAAA;ChB+pEP;AgBvqED;;EAeM,yBAAA;ChB4pEL;AgBlpED;EAEI,0BAAA;ChBmpEH;AgB1oED;EAEI,0BAAA;ChB2oEH;AgBloED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBooED;AgB/nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBkoEL;AiB9wEC;;;;;;;;;;;;EAOI,0BAAA;CjBqxEL;AiB/wEC;;;;;EAMI,0BAAA;CjBgxEL;AiBnyEC;;;;;;;;;;;;EAOI,0BAAA;CjB0yEL;AiBpyEC;;;;;EAMI,0BAAA;CjBqyEL;AiBxzEC;;;;;;;;;;;;EAOI,0BAAA;CjB+zEL;AiBzzEC;;;;;EAMI,0BAAA;CjB0zEL;AiB70EC;;;;;;;;;;;;EAOI,0BAAA;CjBo1EL;AiB90EC;;;;;EAMI,0BAAA;CjB+0EL;AiBl2EC;;;;;;;;;;;;EAOI,0BAAA;CjBy2EL;AiBn2EC;;;;;EAMI,0BAAA;CjBo2EL;AgBltED;EACE,iBAAA;EACA,kBAAA;ChBotED;AgBvpED;EAAA;IA1DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,0BAAA;GhBqtED;EgB/pEH;IAlDM,iBAAA;GhBotEH;EgBlqEH;;;;;;IAzCY,oBAAA;GhBmtET;EgB1qEH;IAjCM,UAAA;GhB8sEH;EgB7qEH;;;;;;IAxBY,eAAA;GhB6sET;EgBrrEH;;;;;;IApBY,gBAAA;GhBitET;EgB7rEH;;;;IAPY,iBAAA;GhB0sET;CACF;AkBp6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBm6ED;AkBh6ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBk6ED;AkB/5ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBi6ED;AkBt5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL63ET;AkBt5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBw5ED;AkBr5ED;EACE,eAAA;ClBu5ED;AkBn5ED;EACE,eAAA;EACA,YAAA;ClBq5ED;AkBj5ED;;EAEE,aAAA;ClBm5ED;AkB/4ED;;;EZvEE,qBAAA;EAEA,2CAAA;EACA,qBAAA;CN09ED;AkB/4ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClBi5ED;AkBv3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,0BAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CL0zET;AmBl8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CL27ET;AK15EC;EACE,eAAA;EACA,WAAA;CL45EH;AK15EC;EAA0B,eAAA;CL65E3B;AK55EC;EAAgC,eAAA;CL+5EjC;AkB/3EC;;;EAGE,0BAAA;EACA,WAAA;ClBi4EH;AkB93EC;;EAEE,oBAAA;ClBg4EH;AkB53EC;EACE,aAAA;ClB83EH;AkBl3ED;EACE,yBAAA;ClBo3ED;AkB50ED;EAtBI;;;;IACE,kBAAA;GlBw2EH;EkBr2EC;;;;;;;;IAEE,kBAAA;GlB62EH;EkB12EC;;;;;;;;IAEE,kBAAA;GlBk3EH;CACF;AkBx2ED;EACE,oBAAA;ClB02ED;AkBl2ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBo2ED;AkBz2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBq2EH;AkBl2ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBo2ED;AkBj2ED;;EAEE,iBAAA;ClBm2ED;AkB/1ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2ED;AkB/1ED;;EAEE,cAAA;EACA,kBAAA;ClBi2ED;AkBx1EC;;;;;;EAGE,oBAAA;ClB61EH;AkBv1EC;;;;EAEE,oBAAA;ClB21EH;AkBr1EC;;;;EAGI,oBAAA;ClBw1EL;AkB70ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClB60ED;AkB30EC;;EAEE,gBAAA;EACA,iBAAA;ClB60EH;AkBh0ED;EC7PE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBgkFD;AmB9jFC;EACE,aAAA;EACA,kBAAA;CnBgkFH;AmB7jFC;;EAEE,aAAA;CnB+jFH;AkB50ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClB60EH;AkBn1ED;EASI,aAAA;EACA,kBAAA;ClB60EH;AkBv1ED;;EAcI,aAAA;ClB60EH;AkB31ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClB60EH;AkBz0ED;ECzRE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBqmFD;AmBnmFC;EACE,aAAA;EACA,kBAAA;CnBqmFH;AmBlmFC;;EAEE,aAAA;CnBomFH;AkBr1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBs1EH;AkB51ED;EASI,aAAA;EACA,kBAAA;ClBs1EH;AkBh2ED;;EAcI,aAAA;ClBs1EH;AkBp2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBs1EH;AkB70ED;EAEE,mBAAA;ClB80ED;AkBh1ED;EAMI,sBAAA;ClB60EH;AkBz0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBz0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClB20ED;AkBv0ED;;;;;;;;;;ECpZI,eAAA;CnBuuFH;AkBn1ED;EChZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwrFT;AmBtuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6rFT;AkB71ED;ECtYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBsuFH;AkBl2ED;EChYI,eAAA;CnBquFH;AkBl2ED;;;;;;;;;;ECvZI,eAAA;CnBqwFH;AkB92ED;ECnZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLstFT;AmBpwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2tFT;AkBx3ED;ECzYI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBowFH;AkB73ED;ECnYI,eAAA;CnBmwFH;AkB73ED;;;;;;;;;;EC1ZI,eAAA;CnBmyFH;AkBz4ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLovFT;AmBlyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CLyvFT;AkBn5ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBkyFH;AkBx5ED;ECtYI,eAAA;CnBiyFH;AkBp5EC;EACG,UAAA;ClBs5EJ;AkBp5EC;EACG,OAAA;ClBs5EJ;AkB54ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB84ED;AkB3zED;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB63EH;EkBj0EH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB23EH;EkBt0EH;IAhDM,sBAAA;GlBy3EH;EkBz0EH;IA5CM,sBAAA;IACA,uBAAA;GlBw3EH;EkB70EH;;;IAtCQ,YAAA;GlBw3EL;EkBl1EH;IAhCM,YAAA;GlBq3EH;EkBr1EH;IA5BM,iBAAA;IACA,uBAAA;GlBo3EH;EkBz1EH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBi3EH;EkBh2EH;;IAdQ,gBAAA;GlBk3EL;EkBp2EH;;IATM,mBAAA;IACA,eAAA;GlBi3EH;EkBz2EH;IAHM,OAAA;GlB+2EH;CACF;AkBr2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClBk2EH;AkB72ED;;EAiBI,iBAAA;ClBg2EH;AkBj3ED;EJhhBE,mBAAA;EACA,oBAAA;Cdo4FD;AkB90EC;EAAA;IAVI,kBAAA;IACA,iBAAA;IACA,iBAAA;GlB41EH;CACF;AkB53ED;EAwCI,YAAA;ClBu1EH;AkBz0EC;EAAA;IAJM,yBAAA;IACA,gBAAA;GlBi1EL;CACF;AkBv0EC;EAAA;IAJM,iBAAA;IACA,gBAAA;GlB+0EL;CACF;AoBl6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC6CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB4JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL6tFT;AoBr6FG;;;;;;EdrBF,qBAAA;EAEA,2CAAA;EACA,qBAAA;CNi8FD;AoBz6FC;;;EAGE,eAAA;EACA,sBAAA;CpB26FH;AoBx6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLg5FT;AoBx6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CLy5FT;AoBx6FG;;EAEE,qBAAA;CpB06FL;AoBj6FD;EC3DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrB+9FD;AqB79FC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+9FP;AqB79FG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBq+FT;AqBl+FC;;;EAGE,uBAAA;CrBo+FH;AqB/9FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB6+FT;AoB/9FD;ECTI,eAAA;EACA,0BAAA;CrB2+FH;AoBh+FD;EC9DE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBiiGD;AqB/hGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBiiGP;AqB/hGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuiGT;AqBpiGC;;;EAGE,uBAAA;CrBsiGH;AqBjiGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrB+iGT;AoB9hGD;ECZI,eAAA;EACA,0BAAA;CrB6iGH;AoB9hGD;EClEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBmmGD;AqBjmGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBmmGP;AqBjmGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBymGT;AqBtmGC;;;EAGE,uBAAA;CrBwmGH;AqBnmGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBinGT;AoB5lGD;EChBI,eAAA;EACA,0BAAA;CrB+mGH;AoB5lGD;ECtEE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBqqGD;AqBnqGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBqqGP;AqBnqGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB2qGT;AqBxqGC;;;EAGE,uBAAA;CrB0qGH;AqBrqGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBmrGT;AoB1pGD;ECpBI,eAAA;EACA,0BAAA;CrBirGH;AoB1pGD;EC1EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrBuuGD;AqBruGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrBuuGP;AqBruGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB6uGT;AqB1uGC;;;EAGE,uBAAA;CrB4uGH;AqBvuGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBqvGT;AoBxtGD;ECxBI,eAAA;EACA,0BAAA;CrBmvGH;AoBxtGD;EC9EE,eAAA;EACA,0BAAA;EACA,sBAAA;CrByyGD;AqBvyGC;;EAEE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;EACE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGC;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrByyGP;AqBvyGG;;;;;;;;;EAGE,eAAA;EACA,0BAAA;EACI,sBAAA;CrB+yGT;AqB5yGC;;;EAGE,uBAAA;CrB8yGH;AqBzyGG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACI,sBAAA;CrBuzGT;AoBtxGD;EC5BI,eAAA;EACA,0BAAA;CrBqzGH;AoBjxGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpBmxGD;AoBjxGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLuzGT;AoBlxGC;;;;EAIE,0BAAA;CpBoxGH;AoBlxGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpBoxGH;AoBhxGG;;;;EAEE,eAAA;EACA,sBAAA;CpBoxGL;AoB3wGD;;ECrEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBo1GD;AoB9wGD;;ECzEE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrB21GD;AoBjxGD;;EC7EE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBk2GD;AoBhxGD;EACE,eAAA;EACA,YAAA;CpBkxGD;AoB9wGD;EACE,gBAAA;CpBgxGD;AoBzwGC;;;EACE,YAAA;CpB6wGH;AuBv6GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLsvGT;AuB16GC;EACE,WAAA;CvB46GH;AuBx6GD;EACE,cAAA;CvB06GD;AuBx6GC;EAAY,eAAA;CvB26Gb;AuB16GC;EAAY,mBAAA;CvB66Gb;AuB56GC;EAAY,yBAAA;CvB+6Gb;AuB56GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CLgwGT;AwB18GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxB48GD;AwBx8GD;;EAEE,mBAAA;CxB08GD;AwBt8GD;EACE,WAAA;CxBw8GD;AwBp8GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,0BAAA;EACA,0BAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBu8GD;AwBl8GC;EACE,SAAA;EACA,WAAA;CxBo8GH;AwB79GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBy/GD;AwBn+GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBm8GH;AwB77GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB+7GH;AwBz7GC;;;EAGE,eAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxB27GH;AwBl7GC;;;EAGE,eAAA;CxBo7GH;AwBh7GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxBk7GH;AwB76GD;EAGI,eAAA;CxB66GH;AwBh7GD;EAQI,WAAA;CxB26GH;AwBn6GD;EACE,WAAA;EACA,SAAA;CxBq6GD;AwB75GD;EACE,QAAA;EACA,YAAA;CxB+5GD;AwB35GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB65GD;AwBz5GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxB25GD;AwBv5GD;EACE,SAAA;EACA,WAAA;CxBy5GD;AwBj5GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxBi5GH;AwBx5GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxBi5GH;AwB53GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB+8GC;EwB54GD;IA1DA,QAAA;IACA,YAAA;GxBy8GC;CACF;A2BzlHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3B2lHD;A2B/lHD;;EAMI,mBAAA;EACA,YAAA;C3B6lHH;A2B3lHG;;;;;;;;EAIE,WAAA;C3BimHL;A2B3lHD;;;;EAKI,kBAAA;C3B4lHH;A2BvlHD;EACE,kBAAA;C3BylHD;A2B1lHD;;;EAOI,YAAA;C3BwlHH;A2B/lHD;;;EAYI,iBAAA;C3BwlHH;A2BplHD;EACE,iBAAA;C3BslHD;A2BllHD;EACE,eAAA;C3BolHD;A2BnlHC;EClDA,8BAAA;EACG,2BAAA;C5BwoHJ;A2BllHD;;EC/CE,6BAAA;EACG,0BAAA;C5BqoHJ;A2BjlHD;EACE,YAAA;C3BmlHD;A2BjlHD;EACE,iBAAA;C3BmlHD;A2BjlHD;;ECnEE,8BAAA;EACG,2BAAA;C5BwpHJ;A2BhlHD;ECjEE,6BAAA;EACG,0BAAA;C5BopHJ;A2B/kHD;;EAEE,WAAA;C3BilHD;A2BhkHD;EACE,kBAAA;EACA,mBAAA;C3BkkHD;A2BhkHD;EACE,mBAAA;EACA,oBAAA;C3BkkHD;A2B7jHD;EtB/CE,yDAAA;EACQ,iDAAA;CL+mHT;A2B7jHC;EtBnDA,yBAAA;EACQ,iBAAA;CLmnHT;A2B1jHD;EACE,eAAA;C3B4jHD;A2BzjHD;EACE,wBAAA;EACA,uBAAA;C3B2jHD;A2BxjHD;EACE,wBAAA;C3B0jHD;A2BnjHD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3BojHH;A2B3jHD;EAcM,YAAA;C3BgjHL;A2B9jHD;;;;EAsBI,iBAAA;EACA,eAAA;C3B8iHH;A2BziHC;EACE,iBAAA;C3B2iHH;A2BziHC;EACE,6BAAA;ECpKF,8BAAA;EACC,6BAAA;C5BgtHF;A2B1iHC;EACE,+BAAA;EChLF,2BAAA;EACC,0BAAA;C5B6tHF;A2B1iHD;EACE,iBAAA;C3B4iHD;A2B1iHD;;EC/KE,8BAAA;EACC,6BAAA;C5B6tHF;A2BziHD;EC7LE,2BAAA;EACC,0BAAA;C5ByuHF;A2BriHD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3BuiHD;A2B3iHD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3BwiHH;A2BjjHD;EAYI,YAAA;C3BwiHH;A2BpjHD;EAgBI,WAAA;C3BuiHH;A2BthHD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3BuhHL;A6BjwHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BmwHD;A6BhwHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7BkwHH;A6B3wHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7B0vHH;A6BjvHD;;;EV8BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwtHD;AmBttHC;;;EACE,aAAA;EACA,kBAAA;CnB0tHH;AmBvtHC;;;;;;EAEE,aAAA;CnB6tHH;A6BnwHD;;;EVyBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+uHD;AmB7uHC;;;EACE,aAAA;EACA,kBAAA;CnBivHH;AmB9uHC;;;;;;EAEE,aAAA;CnBovHH;A6BjxHD;;;EAGE,oBAAA;C7BmxHD;A6BjxHC;;;EACE,iBAAA;C7BqxHH;A6BjxHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7BmxHD;A6B9wHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;C7BgxHD;A6B7wHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6B7wHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B+wHH;A6BnyHD;;EA0BI,cAAA;C7B6wHH;A6BxwHD;;;;;;;EDhGE,8BAAA;EACG,2BAAA;C5Bi3HJ;A6BzwHD;EACE,gBAAA;C7B2wHD;A6BzwHD;;;;;;;EDpGE,6BAAA;EACG,0BAAA;C5Bs3HJ;A6B1wHD;EACE,eAAA;C7B4wHD;A6BvwHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BuwHD;A6B5wHD;EAUI,mBAAA;C7BqwHH;A6B/wHD;EAYM,kBAAA;C7BswHL;A6BnwHG;;;EAGE,WAAA;C7BqwHL;A6BhwHC;;EAGI,mBAAA;C7BiwHL;A6B9vHC;;EAGI,WAAA;EACA,kBAAA;C7B+vHL;A8B15HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B45HD;A8B/5HD;EAOI,mBAAA;EACA,eAAA;C9B25HH;A8Bn6HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B25HL;A8B15HK;;EAEE,sBAAA;EACA,0BAAA;C9B45HP;A8Bv5HG;EACE,eAAA;C9By5HL;A8Bv5HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By5HP;A8Bl5HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo5HL;A8B77HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm8HD;A8Bn8HD;EA0DI,gBAAA;C9B44HH;A8Bn4HD;EACE,iCAAA;C9Bq4HD;A8Bt4HD;EAGI,YAAA;EAEA,oBAAA;C9Bq4HH;A8B14HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo4HL;A8Bn4HK;EACE,sCAAA;C9Bq4HP;A8B/3HK;;;EAGE,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,iCAAA;EACA,gBAAA;C9Bi4HP;A8B53HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6yHD;A8Bh4HC;EAwDE,YAAA;C9B20HH;A8Bn4HC;EA0DI,mBAAA;EACA,mBAAA;C9B40HL;A8Bv4HC;EAgEE,UAAA;EACA,WAAA;C9B00HH;A8B9zHD;EAAA;IAPM,oBAAA;IACA,UAAA;G9By0HH;E8Bn0HH;IAJQ,iBAAA;G9B00HL;CACF;A8Bp5HC;EAuFE,gBAAA;EACA,mBAAA;C9Bg0HH;A8Bx5HC;;;EA8FE,0BAAA;C9B+zHH;A8BjzHD;EAAA;IATM,iCAAA;IACA,2BAAA;G9B8zHH;E8BtzHH;;;IAHM,6BAAA;G9B8zHH;CACF;A8B/5HD;EAEI,YAAA;C9Bg6HH;A8Bl6HD;EAMM,mBAAA;C9B+5HL;A8Br6HD;EASM,iBAAA;C9B+5HL;A8B15HK;;;EAGE,eAAA;EACA,0BAAA;C9B45HP;A8Bp5HD;EAEI,YAAA;C9Bq5HH;A8Bv5HD;EAIM,gBAAA;EACA,eAAA;C9Bs5HL;A8B14HD;EACE,YAAA;C9B44HD;A8B74HD;EAII,YAAA;C9B44HH;A8Bh5HD;EAMM,mBAAA;EACA,mBAAA;C9B64HL;A8Bp5HD;EAYI,UAAA;EACA,WAAA;C9B24HH;A8B/3HD;EAAA;IAPM,oBAAA;IACA,UAAA;G9B04HH;E8Bp4HH;IAJQ,iBAAA;G9B24HL;CACF;A8Bn4HD;EACE,iBAAA;C9Bq4HD;A8Bt4HD;EAKI,gBAAA;EACA,mBAAA;C9Bo4HH;A8B14HD;;;EAYI,0BAAA;C9Bm4HH;A8Br3HD;EAAA;IATM,iCAAA;IACA,2BAAA;G9Bk4HH;E8B13HH;;;IAHM,6BAAA;G9Bk4HH;CACF;A8Bz3HD;EAEI,cAAA;C9B03HH;A8B53HD;EAKI,eAAA;C9B03HH;A8Bj3HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8lIF;A+BxlID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0lID;A+BllID;EAAA;IAFI,mBAAA;G/BwlID;CACF;A+BzkID;EAAA;IAFI,YAAA;G/B+kID;CACF;A+BjkID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkkID;A+BhkIC;EACE,iBAAA;C/BkkIH;A+BtiID;EAAA;IAxBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkkID;E+BhkIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkkIH;E+B/jIC;IACE,oBAAA;G/BikIH;E+B5jIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8jIH;CACF;A+B1jID;;EAGI,kBAAA;C/B2jIH;A+BtjIC;EAAA;;IAFI,kBAAA;G/B6jIH;CACF;A+BpjID;;;;EAII,oBAAA;EACA,mBAAA;C/BsjIH;A+BhjIC;EAAA;;;;IAHI,gBAAA;IACA,eAAA;G/B0jIH;CACF;A+B9iID;EACE,cAAA;EACA,sBAAA;C/BgjID;A+B3iID;EAAA;IAFI,iBAAA;G/BijID;CACF;A+B7iID;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+iID;A+BziID;EAAA;;IAFI,iBAAA;G/BgjID;CACF;A+B9iID;EACE,OAAA;EACA,sBAAA;C/BgjID;A+B9iID;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BgjID;A+B1iID;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4iID;A+B1iIC;;EAEE,sBAAA;C/B4iIH;A+BrjID;EAaI,eAAA;C/B2iIH;A+BliID;EALI;;IAEE,mBAAA;G/B0iIH;CACF;A+BhiID;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/BmiID;A+B/hIC;EACE,WAAA;C/BiiIH;A+B/iID;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B+hIH;A+BrjID;EAyBI,gBAAA;C/B+hIH;A+BzhID;EAAA;IAFI,cAAA;G/B+hID;CACF;A+BthID;EACE,oBAAA;C/BwhID;A+BzhID;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/BwhIH;A+B5/HC;EAAA;IAtBI,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/BshIH;E+BtgID;;IAbM,2BAAA;G/BuhIL;E+B1gID;IAVM,kBAAA;G/BuhIL;E+BthIK;;IAEE,uBAAA;G/BwhIP;CACF;A+BtgID;EAAA;IAXI,YAAA;IACA,UAAA;G/BqhID;E+B3gIH;IAPM,YAAA;G/BqhIH;E+B9gIH;IALQ,kBAAA;IACA,qBAAA;G/BshIL;CACF;A+B3gID;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4yID;AkB5xHD;EAAA;IA9DM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlB81HH;EkBlyHH;IAvDM,sBAAA;IACA,YAAA;IACA,uBAAA;GlB41HH;EkBvyHH;IAhDM,sBAAA;GlB01HH;EkB1yHH;IA5CM,sBAAA;IACA,uBAAA;GlBy1HH;EkB9yHH;;;IAtCQ,YAAA;GlBy1HL;EkBnzHH;IAhCM,YAAA;GlBs1HH;EkBtzHH;IA5BM,iBAAA;IACA,uBAAA;GlBq1HH;EkB1zHH;;IApBM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlBk1HH;EkBj0HH;;IAdQ,gBAAA;GlBm1HL;EkBr0HH;;IATM,mBAAA;IACA,eAAA;GlBk1HH;EkB10HH;IAHM,OAAA;GlBg1HH;CACF;A+BpjIC;EAAA;IANI,mBAAA;G/B8jIH;E+B5jIG;IACE,iBAAA;G/B8jIL;CACF;A+B7iID;EAAA;IARI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmzIP;CACF;A+BnjID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B03IF;A+BnjID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By3IF;A+B/iID;EChVE,gBAAA;EACA,mBAAA;ChCk4ID;A+BhjIC;ECnVA,iBAAA;EACA,oBAAA;ChCs4ID;A+BjjIC;ECtVA,iBAAA;EACA,oBAAA;ChC04ID;A+B3iID;EChWE,iBAAA;EACA,oBAAA;ChC84ID;A+BviID;EAAA;IAJI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+iID;CACF;A+BlhID;EAhBE;IExWA,uBAAA;GjC84IC;E+BriID;IE5WA,wBAAA;IF8WE,oBAAA;G/BuiID;E+BziID;IAKI,gBAAA;G/BuiIH;CACF;A+B9hID;EACE,0BAAA;EACA,sBAAA;C/BgiID;A+BliID;EAKI,eAAA;C/BgiIH;A+B/hIG;;EAEE,eAAA;EACA,8BAAA;C/BiiIL;A+B1iID;EAcI,eAAA;C/B+hIH;A+B7iID;EAmBM,eAAA;C/B6hIL;A+B3hIK;;EAEE,eAAA;EACA,8BAAA;C/B6hIP;A+BzhIK;;;EAGE,eAAA;EACA,0BAAA;C/B2hIP;A+BvhIK;;;EAGE,eAAA;EACA,8BAAA;C/ByhIP;A+BjkID;EA8CI,sBAAA;C/BshIH;A+BrhIG;;EAEE,0BAAA;C/BuhIL;A+BxkID;EAoDM,0BAAA;C/BuhIL;A+B3kID;;EA0DI,sBAAA;C/BqhIH;A+B9gIK;;;EAGE,0BAAA;EACA,eAAA;C/BghIP;A+B/+HC;EAAA;IAzBQ,eAAA;G/B4gIP;E+B3gIO;;IAEE,eAAA;IACA,8BAAA;G/B6gIT;E+BzgIO;;;IAGE,eAAA;IACA,0BAAA;G/B2gIT;E+BvgIO;;;IAGE,eAAA;IACA,8BAAA;G/BygIT;CACF;A+B3mID;EA8GI,eAAA;C/BggIH;A+B//HG;EACE,eAAA;C/BigIL;A+BjnID;EAqHI,eAAA;C/B+/HH;A+B9/HG;;EAEE,eAAA;C/BggIL;A+B5/HK;;;;EAEE,eAAA;C/BggIP;A+Bx/HD;EACE,0BAAA;EACA,sBAAA;C/B0/HD;A+B5/HD;EAKI,eAAA;C/B0/HH;A+Bz/HG;;EAEE,eAAA;EACA,8BAAA;C/B2/HL;A+BpgID;EAcI,eAAA;C/By/HH;A+BvgID;EAmBM,eAAA;C/Bu/HL;A+Br/HK;;EAEE,eAAA;EACA,8BAAA;C/Bu/HP;A+Bn/HK;;;EAGE,eAAA;EACA,0BAAA;C/Bq/HP;A+Bj/HK;;;EAGE,eAAA;EACA,8BAAA;C/Bm/HP;A+B3hID;EA+CI,sBAAA;C/B++HH;A+B9+HG;;EAEE,0BAAA;C/Bg/HL;A+BliID;EAqDM,0BAAA;C/Bg/HL;A+BriID;;EA2DI,sBAAA;C/B8+HH;A+Bx+HK;;;EAGE,0BAAA;EACA,eAAA;C/B0+HP;A+Bn8HC;EAAA;IA/BQ,sBAAA;G/Bs+HP;E+Bv8HD;IA5BQ,0BAAA;G/Bs+HP;E+B18HD;IAzBQ,eAAA;G/Bs+HP;E+Br+HO;;IAEE,eAAA;IACA,8BAAA;G/Bu+HT;E+Bn+HO;;;IAGE,eAAA;IACA,0BAAA;G/Bq+HT;E+Bj+HO;;;IAGE,eAAA;IACA,8BAAA;G/Bm+HT;CACF;A+B3kID;EA+GI,eAAA;C/B+9HH;A+B99HG;EACE,eAAA;C/Bg+HL;A+BjlID;EAsHI,eAAA;C/B89HH;A+B79HG;;EAEE,eAAA;C/B+9HL;A+B39HK;;;;EAEE,eAAA;C/B+9HP;AkCzmJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2mJD;AkChnJD;EAQI,sBAAA;ClC2mJH;AkCnnJD;EAWM,kBAAA;EACA,eAAA;EACA,eAAA;ClC2mJL;AkCxnJD;EAkBI,eAAA;ClCymJH;AmC7nJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+nJD;AmCnoJD;EAOI,gBAAA;CnC+nJH;AmCtoJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,0BAAA;EACA,kBAAA;CnCgoJL;AmC9nJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2oJJ;AmC7nJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwpJJ;AmCxnJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CnC4nJL;AmCtnJG;;;;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2nJL;AmClrJD;;;;;;EAkEM,eAAA;EACA,0BAAA;EACA,sBAAA;EACA,oBAAA;CnCwnJL;AmC/mJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8rJL;AoC5rJG;;ERKF,+BAAA;EACG,4BAAA;C5B2rJJ;AoC3rJG;;ERTF,gCAAA;EACG,6BAAA;C5BwsJJ;AmC1nJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8sJL;AoC5sJG;;ERKF,+BAAA;EACG,4BAAA;C5B2sJJ;AoC3sJG;;ERTF,gCAAA;EACG,6BAAA;C5BwtJJ;AqC3tJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6tJD;AqCjuJD;EAOI,gBAAA;CrC6tJH;AqCpuJD;;EAUM,sBAAA;EACA,kBAAA;EACA,0BAAA;EACA,0BAAA;EACA,oBAAA;CrC8tJL;AqC5uJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6tJL;AqCjvJD;;EA2BM,aAAA;CrC0tJL;AqCrvJD;;EAkCM,YAAA;CrCutJL;AqCzvJD;;;;EA2CM,eAAA;EACA,0BAAA;EACA,oBAAA;CrCotJL;AsClwJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCowJD;AsChwJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CtCkwJL;AsC7vJC;EACE,cAAA;CtC+vJH;AsC3vJC;EACE,mBAAA;EACA,UAAA;CtC6vJH;AsCtvJD;ECtCE,0BAAA;CvC+xJD;AuC5xJG;;EAEE,0BAAA;CvC8xJL;AsCzvJD;EC1CE,0BAAA;CvCsyJD;AuCnyJG;;EAEE,0BAAA;CvCqyJL;AsC5vJD;EC9CE,0BAAA;CvC6yJD;AuC1yJG;;EAEE,0BAAA;CvC4yJL;AsC/vJD;EClDE,0BAAA;CvCozJD;AuCjzJG;;EAEE,0BAAA;CvCmzJL;AsClwJD;ECtDE,0BAAA;CvC2zJD;AuCxzJG;;EAEE,0BAAA;CvC0zJL;AsCrwJD;EC1DE,0BAAA;CvCk0JD;AuC/zJG;;EAEE,0BAAA;CvCi0JL;AwCn0JD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCq0JD;AwCl0JC;EACE,cAAA;CxCo0JH;AwCh0JC;EACE,mBAAA;EACA,UAAA;CxCk0JH;AwC/zJC;;EAEE,OAAA;EACA,iBAAA;CxCi0JH;AwC5zJG;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;CxC8zJL;AwCzzJC;;EAEE,eAAA;EACA,0BAAA;CxC2zJH;AwCxzJC;EACE,aAAA;CxC0zJH;AwCvzJC;EACE,kBAAA;CxCyzJH;AwCtzJC;EACE,iBAAA;CxCwzJH;AyCl3JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo3JD;AyCz3JD;;EASI,eAAA;CzCo3JH;AyC73JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm3JH;AyCl4JD;EAmBI,0BAAA;CzCk3JH;AyC/2JC;;EAEE,mBAAA;CzCi3JH;AyCz4JD;EA4BI,gBAAA;CzCg3JH;AyC91JD;EAAA;IAdI,kBAAA;IACA,qBAAA;GzCg3JD;EyC92JC;;IAEE,mBAAA;IACA,oBAAA;GzCg3JH;EyCx2JH;;IAHM,gBAAA;GzC+2JH;CACF;A0C15JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL4uJT;A0Ct6JD;;EAaI,kBAAA;EACA,mBAAA;C1C65JH;A0Cz5JC;;;EAGE,sBAAA;C1C25JH;A0Ch7JD;EA0BI,aAAA;EACA,eAAA;C1Cy5JH;A2Cl7JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Co7JD;A2Cx7JD;EAQI,cAAA;EAEA,eAAA;C3Ck7JH;A2C57JD;EAeI,kBAAA;C3Cg7JH;A2C/7JD;;EAqBI,iBAAA;C3C86JH;A2Cn8JD;EAyBI,gBAAA;C3C66JH;A2Cr6JD;;EAEE,oBAAA;C3Cu6JD;A2Cz6JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cu6JH;A2C/5JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cy9JD;A2Cp6JD;EClDI,0BAAA;C5Cy9JH;A2Cv6JD;EC/CI,eAAA;C5Cy9JH;A2Ct6JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Co+JD;A2C36JD;ECtDI,0BAAA;C5Co+JH;A2C96JD;ECnDI,eAAA;C5Co+JH;A2C76JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C++JD;A2Cl7JD;EC1DI,0BAAA;C5C++JH;A2Cr7JD;ECvDI,eAAA;C5C++JH;A2Cp7JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C0/JD;A2Cz7JD;EC9DI,0BAAA;C5C0/JH;A2C57JD;EC3DI,eAAA;C5C0/JH;A6C5/JD;EACE;IAAQ,4BAAA;G7C+/JP;E6C9/JD;IAAQ,yBAAA;G7CigKP;CACF;A6C9/JD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6CtgKD;EACE;IAAQ,4BAAA;G7CigKP;E6ChgKD;IAAQ,yBAAA;G7CmgKP;CACF;A6C5/JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CLy9JT;A6C3/JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL62JT;A6Cx/JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C4/JD;A6Cr/JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLqiKT;A6Cl/JD;EErEE,0BAAA;C/C0jKD;A+CvjKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0gKH;A6Ct/JD;EEzEE,0BAAA;C/CkkKD;A+C/jKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkhKH;A6C1/JD;EE7EE,0BAAA;C/C0kKD;A+CvkKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C0hKH;A6C9/JD;EEjFE,0BAAA;C/CklKD;A+C/kKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9CkiKH;AgD1lKD;EAEE,iBAAA;ChD2lKD;AgDzlKC;EACE,cAAA;ChD2lKH;AgDvlKD;;EAEE,QAAA;EACA,iBAAA;ChDylKD;AgDtlKD;EACE,eAAA;ChDwlKD;AgDrlKD;EACE,eAAA;ChDulKD;AgDplKC;EACE,gBAAA;ChDslKH;AgDllKD;;EAEE,mBAAA;ChDolKD;AgDjlKD;;EAEE,oBAAA;ChDmlKD;AgDhlKD;;;EAGE,oBAAA;EACA,oBAAA;ChDklKD;AgD/kKD;EACE,uBAAA;ChDilKD;AgD9kKD;EACE,uBAAA;ChDglKD;AgD5kKD;EACE,cAAA;EACA,mBAAA;ChD8kKD;AgDxkKD;EACE,gBAAA;EACA,iBAAA;ChD0kKD;AiDjoKD;EAEE,oBAAA;EACA,gBAAA;CjDkoKD;AiD1nKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,0BAAA;EACA,0BAAA;CjD2nKD;AiDxnKC;ErB3BA,6BAAA;EACC,4BAAA;C5BspKF;AiDznKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BmpKF;AiDlnKD;;EAEE,eAAA;CjDonKD;AiDtnKD;;EAKI,eAAA;CjDqnKH;AiDjnKC;;;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CjDqnKH;AiDjnKD;EACE,YAAA;EACA,iBAAA;CjDmnKD;AiD9mKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDgnKH;AiDrnKC;;;EASI,eAAA;CjDinKL;AiD1nKC;;;EAYI,eAAA;CjDmnKL;AiD9mKC;;;EAGE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,sBAAA;CjDgnKH;AiDtnKC;;;;;;;;;EAYI,eAAA;CjDqnKL;AiDjoKC;;;EAeI,eAAA;CjDunKL;AkDztKC;EACE,eAAA;EACA,0BAAA;ClD2tKH;AkDztKG;;EAEE,eAAA;ClD2tKL;AkD7tKG;;EAKI,eAAA;ClD4tKP;AkDztKK;;;;EAEE,eAAA;EACA,0BAAA;ClD6tKP;AkD3tKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDguKP;AkDtvKC;EACE,eAAA;EACA,0BAAA;ClDwvKH;AkDtvKG;;EAEE,eAAA;ClDwvKL;AkD1vKG;;EAKI,eAAA;ClDyvKP;AkDtvKK;;;;EAEE,eAAA;EACA,0BAAA;ClD0vKP;AkDxvKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD6vKP;AkDnxKC;EACE,eAAA;EACA,0BAAA;ClDqxKH;AkDnxKG;;EAEE,eAAA;ClDqxKL;AkDvxKG;;EAKI,eAAA;ClDsxKP;AkDnxKK;;;;EAEE,eAAA;EACA,0BAAA;ClDuxKP;AkDrxKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD0xKP;AkDhzKC;EACE,eAAA;EACA,0BAAA;ClDkzKH;AkDhzKG;;EAEE,eAAA;ClDkzKL;AkDpzKG;;EAKI,eAAA;ClDmzKP;AkDhzKK;;;;EAEE,eAAA;EACA,0BAAA;ClDozKP;AkDlzKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDuzKP;AiDttKD;EACE,cAAA;EACA,mBAAA;CjDwtKD;AiDttKD;EACE,iBAAA;EACA,iBAAA;CjDwtKD;AmDl1KD;EACE,oBAAA;EACA,0BAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL2xKT;AmDj1KD;EACE,cAAA;CnDm1KD;AmD90KD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5Bq2KF;AmDp1KD;EAMI,eAAA;CnDi1KH;AmD50KD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnD80KD;AmDl1KD;;;;;EAWI,eAAA;CnD80KH;AmDz0KD;EACE,mBAAA;EACA,0BAAA;EACA,8BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bo3KF;AmDn0KD;;EAGI,iBAAA;CnDo0KH;AmDv0KD;;EAMM,oBAAA;EACA,iBAAA;CnDq0KL;AmDj0KG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B24KF;AmD/zKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5By4KF;AmDx1KD;EvB1DE,2BAAA;EACC,0BAAA;C5Bq5KF;AmD3zKD;EAEI,oBAAA;CnD4zKH;AmDzzKD;EACE,oBAAA;CnD2zKD;AmDnzKD;;;EAII,iBAAA;CnDozKH;AmDxzKD;;;EAOM,mBAAA;EACA,oBAAA;CnDszKL;AmD9zKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B26KF;AmDn0KD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDszKP;AmD10KD;;;;;;;;EAwBU,4BAAA;CnD4zKT;AmDp1KD;;;;;;;;EA4BU,6BAAA;CnDk0KT;AmD91KD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bm8KF;AmDn2KD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDg0KP;AmD12KD;;;;;;;;EA8CU,+BAAA;CnDs0KT;AmDp3KD;;;;;;;;EAkDU,gCAAA;CnD40KT;AmD93KD;;;;EA2DI,8BAAA;CnDy0KH;AmDp4KD;;EA+DI,cAAA;CnDy0KH;AmDx4KD;;EAmEI,UAAA;CnDy0KH;AmD54KD;;;;;;;;;;;;EA0EU,eAAA;CnDg1KT;AmD15KD;;;;;;;;;;;;EA8EU,gBAAA;CnD01KT;AmDx6KD;;;;;;;;EAuFU,iBAAA;CnD21KT;AmDl7KD;;;;;;;;EAgGU,iBAAA;CnD41KT;AmD57KD;EAsGI,UAAA;EACA,iBAAA;CnDy1KH;AmD/0KD;EACE,oBAAA;CnDi1KD;AmDl1KD;EAKI,iBAAA;EACA,mBAAA;CnDg1KH;AmDt1KD;EASM,gBAAA;CnDg1KL;AmDz1KD;EAcI,iBAAA;CnD80KH;AmD51KD;;EAkBM,8BAAA;CnD80KL;AmDh2KD;EAuBI,cAAA;CnD40KH;AmDn2KD;EAyBM,iCAAA;CnD60KL;AmDt0KD;EC1PE,sBAAA;CpDmkLD;AoDjkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDmkLH;AoDtkLC;EAMI,0BAAA;CpDmkLL;AoDzkLC;EASI,eAAA;EACA,0BAAA;CpDmkLL;AoDhkLC;EAEI,6BAAA;CpDikLL;AmDr1KD;EC7PE,sBAAA;CpDqlLD;AoDnlLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDqlLH;AoDxlLC;EAMI,0BAAA;CpDqlLL;AoD3lLC;EASI,eAAA;EACA,0BAAA;CpDqlLL;AoDllLC;EAEI,6BAAA;CpDmlLL;AmDp2KD;EChQE,sBAAA;CpDumLD;AoDrmLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDumLH;AoD1mLC;EAMI,0BAAA;CpDumLL;AoD7mLC;EASI,eAAA;EACA,0BAAA;CpDumLL;AoDpmLC;EAEI,6BAAA;CpDqmLL;AmDn3KD;ECnQE,sBAAA;CpDynLD;AoDvnLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDynLH;AoD5nLC;EAMI,0BAAA;CpDynLL;AoD/nLC;EASI,eAAA;EACA,0BAAA;CpDynLL;AoDtnLC;EAEI,6BAAA;CpDunLL;AmDl4KD;ECtQE,sBAAA;CpD2oLD;AoDzoLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2oLH;AoD9oLC;EAMI,0BAAA;CpD2oLL;AoDjpLC;EASI,eAAA;EACA,0BAAA;CpD2oLL;AoDxoLC;EAEI,6BAAA;CpDyoLL;AmDj5KD;ECzQE,sBAAA;CpD6pLD;AoD3pLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6pLH;AoDhqLC;EAMI,0BAAA;CpD6pLL;AoDnqLC;EASI,eAAA;EACA,0BAAA;CpD6pLL;AoD1pLC;EAEI,6BAAA;CpD2pLL;AqD3qLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD6qLD;AqDlrLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD6qLH;AqDxqLD;EACE,uBAAA;CrD0qLD;AqDtqLD;EACE,oBAAA;CrDwqLD;AsDnsLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CL8oLT;AsD7sLD;EASI,mBAAA;EACA,kCAAA;CtDusLH;AsDlsLD;EACE,cAAA;EACA,mBAAA;CtDosLD;AsDlsLD;EACE,aAAA;EACA,mBAAA;CtDosLD;AuD1tLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,6BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBmuLD;AuD3tLC;;EAEE,eAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB2uLD;AuDvtLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvDytLH;AwD9uLD;EACE,iBAAA;CxDgvLD;AwD5uLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD2uLD;AwDxuLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL2jLT;AwD9uLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLsoLT;AwDlvLD;EACE,mBAAA;EACA,iBAAA;CxDovLD;AwDhvLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDkvLD;AwD9uLD;EACE,mBAAA;EACA,0BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDgvLD;AwD5uLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,0BAAA;CxD8uLD;AwD5uLC;ElCrEA,WAAA;EAGA,yBAAA;CtBkzLD;AwD/uLC;ElCtEA,aAAA;EAGA,0BAAA;CtBszLD;AwD9uLD;EACE,cAAA;EACA,iCAAA;EACA,0BAAA;CxDgvLD;AwD7uLD;EACE,iBAAA;CxD+uLD;AwD3uLD;EACE,UAAA;EACA,wBAAA;CxD6uLD;AwDxuLD;EACE,mBAAA;EACA,cAAA;CxD0uLD;AwDtuLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDwuLD;AwD3uLD;EAQI,iBAAA;EACA,iBAAA;CxDsuLH;AwD/uLD;EAaI,kBAAA;CxDquLH;AwDlvLD;EAiBI,eAAA;CxDouLH;AwD/tLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDiuLD;AwD/sLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD8tLD;EwD5tLD;InDvEA,kDAAA;IACQ,0CAAA;GLsyLP;EwD3tLD;IAAY,aAAA;GxD8tLX;CACF;AwDztLD;EAFE;IAAY,aAAA;GxD+tLX;CACF;AyD92LD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBq4LD;AyD13LC;EnCdA,aAAA;EAGA,0BAAA;CtBy4LD;AyD73LC;EAAW,iBAAA;EAAmB,eAAA;CzDi4L/B;AyDh4LC;EAAW,iBAAA;EAAmB,eAAA;CzDo4L/B;AyDn4LC;EAAW,gBAAA;EAAmB,eAAA;CzDu4L/B;AyDt4LC;EAAW,kBAAA;EAAmB,eAAA;CzD04L/B;AyDt4LD;EACE,iBAAA;EACA,iBAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,mBAAA;CzDw4LD;AyDp4LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDs4LD;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,0BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,4BAAA;CzDo4LH;AyDl4LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,2BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;AyDl4LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,6BAAA;CzDo4LH;A2Dj+LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,0BAAA;EACA,qCAAA;UAAA,6BAAA;EACA,0BAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLi8LT;A2D5+LC;EAAY,kBAAA;C3D++Lb;A2D9+LC;EAAY,kBAAA;C3Di/Lb;A2Dh/LC;EAAY,iBAAA;C3Dm/Lb;A2Dl/LC;EAAY,mBAAA;C3Dq/Lb;A2Dl/LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Do/LD;A2Dj/LD;EACE,kBAAA;C3Dm/LD;A2D3+LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D6+LH;A2D1+LD;EACE,mBAAA;C3D4+LD;A2D1+LD;EACE,mBAAA;EACA,YAAA;C3D4+LD;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;C3D2+LL;A2Dx+LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,4BAAA;C3D2+LL;A2Dx+LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D0+LH;A2Dz+LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;C3D2+LL;A2Dv+LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3Dy+LH;A2Dx+LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,2BAAA;EACA,cAAA;C3D0+LL;A4DnmMD;EACE,mBAAA;C5DqmMD;A4DlmMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DomMD;A4DvmMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLw7LT;A4D9mMD;;EAcM,eAAA;C5DomML;A4D1kMC;EAAA;IvDiKA,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL69LP;E4DxmMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D2mML;E4DzmMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D4mML;E4D1mMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D6mML;CACF;A4DnpMD;;;EA6CI,eAAA;C5D2mMH;A4DxpMD;EAiDI,QAAA;C5D0mMH;A4D3pMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5DymMH;A4DjqMD;EA4DI,WAAA;C5DwmMH;A4DpqMD;EA+DI,YAAA;C5DwmMH;A4DvqMD;;EAmEI,QAAA;C5DwmMH;A4D3qMD;EAuEI,YAAA;C5DumMH;A4D9qMD;EA0EI,WAAA;C5DumMH;A4D/lMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DkmMD;A4D7lMC;EdlGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CksMH;A4DjmMC;EACE,WAAA;EACA,SAAA;EdvGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C2sMH;A4DnmMC;;EAEE,WAAA;EACA,eAAA;EACA,sBAAA;EtCtHF,aAAA;EAGA,0BAAA;CtB0tMD;A4DpoMD;;;;EAsCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DomMH;A4D9oMD;;EA8CI,UAAA;EACA,mBAAA;C5DomMH;A4DnpMD;;EAmDI,WAAA;EACA,oBAAA;C5DomMH;A4DxpMD;;EAwDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DomMH;A4D/lMG;EACE,iBAAA;C5DimML;A4D7lMG;EACE,iBAAA;C5D+lML;A4DrlMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DulMD;A4DhmMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D6kMH;A4D5mMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,0BAAA;C5D6kMH;A4DtkMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,eAAA;EACA,mBAAA;EACA,0CAAA;C5DwkMD;A4DvkMC;EACE,kBAAA;C5DykMH;A4DhiMD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DkkMH;E4D1kMD;;IAYI,mBAAA;G5DkkMH;E4D9kMD;;IAgBI,oBAAA;G5DkkMH;E4D7jMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5D+jMD;E4D3jMD;IACE,aAAA;G5D6jMD;CACF;A6D3zMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7Dy1MH;A6Dv1MC;;;;;;;;;;;;;;;EACE,YAAA;C7Du2MH;AiC/2MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D03MD;AiCj3MD;EACE,wBAAA;CjCm3MD;AiCj3MD;EACE,uBAAA;CjCm3MD;AiC32MD;EACE,yBAAA;CjC62MD;AiC32MD;EACE,0BAAA;CjC62MD;AiC32MD;EACE,mBAAA;CjC62MD;AiC32MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/Du4MD;AiCz2MD;EACE,yBAAA;CjC22MD;AiCp2MD;EACE,gBAAA;CjCs2MD;AgEv4MD;EACE,oBAAA;ChEy4MD;AgEn4MD;;;;ECdE,yBAAA;CjEu5MD;AgEl4MD;;;;;;;;;;;;EAYE,yBAAA;ChEo4MD;AgE73MD;EAAA;IChDE,0BAAA;GjEi7MC;EiEh7MD;IAAU,0BAAA;GjEm7MT;EiEl7MD;IAAU,8BAAA;GjEq7MT;EiEp7MD;;IACU,+BAAA;GjEu7MT;CACF;AgEv4MD;EAAA;IAFI,0BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,2BAAA;GhE64MD;CACF;AgEv4MD;EAAA;IAFI,iCAAA;GhE64MD;CACF;AgEt4MD;EAAA;ICrEE,0BAAA;GjE+8MC;EiE98MD;IAAU,0BAAA;GjEi9MT;EiEh9MD;IAAU,8BAAA;GjEm9MT;EiEl9MD;;IACU,+BAAA;GjEq9MT;CACF;AgEh5MD;EAAA;IAFI,0BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,2BAAA;GhEs5MD;CACF;AgEh5MD;EAAA;IAFI,iCAAA;GhEs5MD;CACF;AgE/4MD;EAAA;IC1FE,0BAAA;GjE6+MC;EiE5+MD;IAAU,0BAAA;GjE++MT;EiE9+MD;IAAU,8BAAA;GjEi/MT;EiEh/MD;;IACU,+BAAA;GjEm/MT;CACF;AgEz5MD;EAAA;IAFI,0BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,2BAAA;GhE+5MD;CACF;AgEz5MD;EAAA;IAFI,iCAAA;GhE+5MD;CACF;AgEx5MD;EAAA;IC/GE,0BAAA;GjE2gNC;EiE1gND;IAAU,0BAAA;GjE6gNT;EiE5gND;IAAU,8BAAA;GjE+gNT;EiE9gND;;IACU,+BAAA;GjEihNT;CACF;AgEl6MD;EAAA;IAFI,0BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,2BAAA;GhEw6MD;CACF;AgEl6MD;EAAA;IAFI,iCAAA;GhEw6MD;CACF;AgEj6MD;EAAA;IC5HE,yBAAA;GjEiiNC;CACF;AgEj6MD;EAAA;ICjIE,yBAAA;GjEsiNC;CACF;AgEj6MD;EAAA;ICtIE,yBAAA;GjE2iNC;CACF;AgEj6MD;EAAA;IC3IE,yBAAA;GjEgjNC;CACF;AgE95MD;ECnJE,yBAAA;CjEojND;AgE35MD;EAAA;ICjKE,0BAAA;GjEgkNC;EiE/jND;IAAU,0BAAA;GjEkkNT;EiEjkND;IAAU,8BAAA;GjEokNT;EiEnkND;;IACU,+BAAA;GjEskNT;CACF;AgEz6MD;EACE,yBAAA;ChE26MD;AgEt6MD;EAAA;IAFI,0BAAA;GhE46MD;CACF;AgE16MD;EACE,yBAAA;ChE46MD;AgEv6MD;EAAA;IAFI,2BAAA;GhE66MD;CACF;AgE36MD;EACE,yBAAA;ChE66MD;AgEx6MD;EAAA;IAFI,iCAAA;GhE86MD;CACF;AgEv6MD;EAAA;ICpLE,yBAAA;GjE+lNC;CACF","file":"bootstrap.css","sourcesContent":["/*!\n * Bootstrap v3.3.5 (http://getbootstrap.com)\n * Copyright 2011-2015 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\nhtml {\n font-family: sans-serif;\n -ms-text-size-adjust: 100%;\n -webkit-text-size-adjust: 100%;\n}\nbody {\n margin: 0;\n}\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block;\n vertical-align: baseline;\n}\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n[hidden],\ntemplate {\n display: none;\n}\na {\n background-color: transparent;\n}\na:active,\na:hover {\n outline: 0;\n}\nabbr[title] {\n border-bottom: 1px dotted;\n}\nb,\nstrong {\n font-weight: bold;\n}\ndfn {\n font-style: italic;\n}\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\nmark {\n background: #ff0;\n color: #000;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup {\n top: -0.5em;\n}\nsub {\n bottom: -0.25em;\n}\nimg {\n border: 0;\n}\nsvg:not(:root) {\n overflow: hidden;\n}\nfigure {\n margin: 1em 40px;\n}\nhr {\n box-sizing: content-box;\n height: 0;\n}\npre {\n overflow: auto;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit;\n font: inherit;\n margin: 0;\n}\nbutton {\n overflow: visible;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\nhtml input[type=\"button\"],\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button;\n cursor: pointer;\n}\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\ninput {\n line-height: normal;\n}\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box;\n padding: 0;\n}\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: textfield;\n box-sizing: content-box;\n}\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\nlegend {\n border: 0;\n padding: 0;\n}\ntextarea {\n overflow: auto;\n}\noptgroup {\n font-weight: bold;\n}\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\ntd,\nth {\n padding: 0;\n}\n/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important;\n box-shadow: none !important;\n text-shadow: none !important;\n }\n a,\n a:visited {\n text-decoration: underline;\n }\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n img {\n max-width: 100% !important;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n .navbar {\n display: none;\n }\n .btn > .caret,\n .dropup > .btn > .caret {\n border-top-color: #000 !important;\n }\n .label {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #ddd !important;\n }\n}\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('../fonts/glyphicons-halflings-regular.eot');\n src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\n}\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.glyphicon-asterisk:before {\n content: \"\\2a\";\n}\n.glyphicon-plus:before {\n content: \"\\2b\";\n}\n.glyphicon-euro:before,\n.glyphicon-eur:before {\n content: \"\\20ac\";\n}\n.glyphicon-minus:before {\n content: \"\\2212\";\n}\n.glyphicon-cloud:before {\n content: \"\\2601\";\n}\n.glyphicon-envelope:before {\n content: \"\\2709\";\n}\n.glyphicon-pencil:before {\n content: \"\\270f\";\n}\n.glyphicon-glass:before {\n content: \"\\e001\";\n}\n.glyphicon-music:before {\n content: \"\\e002\";\n}\n.glyphicon-search:before {\n content: \"\\e003\";\n}\n.glyphicon-heart:before {\n content: \"\\e005\";\n}\n.glyphicon-star:before {\n content: \"\\e006\";\n}\n.glyphicon-star-empty:before {\n content: \"\\e007\";\n}\n.glyphicon-user:before {\n content: \"\\e008\";\n}\n.glyphicon-film:before {\n content: \"\\e009\";\n}\n.glyphicon-th-large:before {\n content: \"\\e010\";\n}\n.glyphicon-th:before {\n content: \"\\e011\";\n}\n.glyphicon-th-list:before {\n content: \"\\e012\";\n}\n.glyphicon-ok:before {\n content: \"\\e013\";\n}\n.glyphicon-remove:before {\n content: \"\\e014\";\n}\n.glyphicon-zoom-in:before {\n content: \"\\e015\";\n}\n.glyphicon-zoom-out:before {\n content: \"\\e016\";\n}\n.glyphicon-off:before {\n content: \"\\e017\";\n}\n.glyphicon-signal:before {\n content: \"\\e018\";\n}\n.glyphicon-cog:before {\n content: \"\\e019\";\n}\n.glyphicon-trash:before {\n content: \"\\e020\";\n}\n.glyphicon-home:before {\n content: \"\\e021\";\n}\n.glyphicon-file:before {\n content: \"\\e022\";\n}\n.glyphicon-time:before {\n content: \"\\e023\";\n}\n.glyphicon-road:before {\n content: \"\\e024\";\n}\n.glyphicon-download-alt:before {\n content: \"\\e025\";\n}\n.glyphicon-download:before {\n content: \"\\e026\";\n}\n.glyphicon-upload:before {\n content: \"\\e027\";\n}\n.glyphicon-inbox:before {\n content: \"\\e028\";\n}\n.glyphicon-play-circle:before {\n content: \"\\e029\";\n}\n.glyphicon-repeat:before {\n content: \"\\e030\";\n}\n.glyphicon-refresh:before {\n content: \"\\e031\";\n}\n.glyphicon-list-alt:before {\n content: \"\\e032\";\n}\n.glyphicon-lock:before {\n content: \"\\e033\";\n}\n.glyphicon-flag:before {\n content: \"\\e034\";\n}\n.glyphicon-headphones:before {\n content: \"\\e035\";\n}\n.glyphicon-volume-off:before {\n content: \"\\e036\";\n}\n.glyphicon-volume-down:before {\n content: \"\\e037\";\n}\n.glyphicon-volume-up:before {\n content: \"\\e038\";\n}\n.glyphicon-qrcode:before {\n content: \"\\e039\";\n}\n.glyphicon-barcode:before {\n content: \"\\e040\";\n}\n.glyphicon-tag:before {\n content: \"\\e041\";\n}\n.glyphicon-tags:before {\n content: \"\\e042\";\n}\n.glyphicon-book:before {\n content: \"\\e043\";\n}\n.glyphicon-bookmark:before {\n content: \"\\e044\";\n}\n.glyphicon-print:before {\n content: \"\\e045\";\n}\n.glyphicon-camera:before {\n content: \"\\e046\";\n}\n.glyphicon-font:before {\n content: \"\\e047\";\n}\n.glyphicon-bold:before {\n content: \"\\e048\";\n}\n.glyphicon-italic:before {\n content: \"\\e049\";\n}\n.glyphicon-text-height:before {\n content: \"\\e050\";\n}\n.glyphicon-text-width:before {\n content: \"\\e051\";\n}\n.glyphicon-align-left:before {\n content: \"\\e052\";\n}\n.glyphicon-align-center:before {\n content: \"\\e053\";\n}\n.glyphicon-align-right:before {\n content: \"\\e054\";\n}\n.glyphicon-align-justify:before {\n content: \"\\e055\";\n}\n.glyphicon-list:before {\n content: \"\\e056\";\n}\n.glyphicon-indent-left:before {\n content: \"\\e057\";\n}\n.glyphicon-indent-right:before {\n content: \"\\e058\";\n}\n.glyphicon-facetime-video:before {\n content: \"\\e059\";\n}\n.glyphicon-picture:before {\n content: \"\\e060\";\n}\n.glyphicon-map-marker:before {\n content: \"\\e062\";\n}\n.glyphicon-adjust:before {\n content: \"\\e063\";\n}\n.glyphicon-tint:before {\n content: \"\\e064\";\n}\n.glyphicon-edit:before {\n content: \"\\e065\";\n}\n.glyphicon-share:before {\n content: \"\\e066\";\n}\n.glyphicon-check:before {\n content: \"\\e067\";\n}\n.glyphicon-move:before {\n content: \"\\e068\";\n}\n.glyphicon-step-backward:before {\n content: \"\\e069\";\n}\n.glyphicon-fast-backward:before {\n content: \"\\e070\";\n}\n.glyphicon-backward:before {\n content: \"\\e071\";\n}\n.glyphicon-play:before {\n content: \"\\e072\";\n}\n.glyphicon-pause:before {\n content: \"\\e073\";\n}\n.glyphicon-stop:before {\n content: \"\\e074\";\n}\n.glyphicon-forward:before {\n content: \"\\e075\";\n}\n.glyphicon-fast-forward:before {\n content: \"\\e076\";\n}\n.glyphicon-step-forward:before {\n content: \"\\e077\";\n}\n.glyphicon-eject:before {\n content: \"\\e078\";\n}\n.glyphicon-chevron-left:before {\n content: \"\\e079\";\n}\n.glyphicon-chevron-right:before {\n content: \"\\e080\";\n}\n.glyphicon-plus-sign:before {\n content: \"\\e081\";\n}\n.glyphicon-minus-sign:before {\n content: \"\\e082\";\n}\n.glyphicon-remove-sign:before {\n content: \"\\e083\";\n}\n.glyphicon-ok-sign:before {\n content: \"\\e084\";\n}\n.glyphicon-question-sign:before {\n content: \"\\e085\";\n}\n.glyphicon-info-sign:before {\n content: \"\\e086\";\n}\n.glyphicon-screenshot:before {\n content: \"\\e087\";\n}\n.glyphicon-remove-circle:before {\n content: \"\\e088\";\n}\n.glyphicon-ok-circle:before {\n content: \"\\e089\";\n}\n.glyphicon-ban-circle:before {\n content: \"\\e090\";\n}\n.glyphicon-arrow-left:before {\n content: \"\\e091\";\n}\n.glyphicon-arrow-right:before {\n content: \"\\e092\";\n}\n.glyphicon-arrow-up:before {\n content: \"\\e093\";\n}\n.glyphicon-arrow-down:before {\n content: \"\\e094\";\n}\n.glyphicon-share-alt:before {\n content: \"\\e095\";\n}\n.glyphicon-resize-full:before {\n content: \"\\e096\";\n}\n.glyphicon-resize-small:before {\n content: \"\\e097\";\n}\n.glyphicon-exclamation-sign:before {\n content: \"\\e101\";\n}\n.glyphicon-gift:before {\n content: \"\\e102\";\n}\n.glyphicon-leaf:before {\n content: \"\\e103\";\n}\n.glyphicon-fire:before {\n content: \"\\e104\";\n}\n.glyphicon-eye-open:before {\n content: \"\\e105\";\n}\n.glyphicon-eye-close:before {\n content: \"\\e106\";\n}\n.glyphicon-warning-sign:before {\n content: \"\\e107\";\n}\n.glyphicon-plane:before {\n content: \"\\e108\";\n}\n.glyphicon-calendar:before {\n content: \"\\e109\";\n}\n.glyphicon-random:before {\n content: \"\\e110\";\n}\n.glyphicon-comment:before {\n content: \"\\e111\";\n}\n.glyphicon-magnet:before {\n content: \"\\e112\";\n}\n.glyphicon-chevron-up:before {\n content: \"\\e113\";\n}\n.glyphicon-chevron-down:before {\n content: \"\\e114\";\n}\n.glyphicon-retweet:before {\n content: \"\\e115\";\n}\n.glyphicon-shopping-cart:before {\n content: \"\\e116\";\n}\n.glyphicon-folder-close:before {\n content: \"\\e117\";\n}\n.glyphicon-folder-open:before {\n content: \"\\e118\";\n}\n.glyphicon-resize-vertical:before {\n content: \"\\e119\";\n}\n.glyphicon-resize-horizontal:before {\n content: \"\\e120\";\n}\n.glyphicon-hdd:before {\n content: \"\\e121\";\n}\n.glyphicon-bullhorn:before {\n content: \"\\e122\";\n}\n.glyphicon-bell:before {\n content: \"\\e123\";\n}\n.glyphicon-certificate:before {\n content: \"\\e124\";\n}\n.glyphicon-thumbs-up:before {\n content: \"\\e125\";\n}\n.glyphicon-thumbs-down:before {\n content: \"\\e126\";\n}\n.glyphicon-hand-right:before {\n content: \"\\e127\";\n}\n.glyphicon-hand-left:before {\n content: \"\\e128\";\n}\n.glyphicon-hand-up:before {\n content: \"\\e129\";\n}\n.glyphicon-hand-down:before {\n content: \"\\e130\";\n}\n.glyphicon-circle-arrow-right:before {\n content: \"\\e131\";\n}\n.glyphicon-circle-arrow-left:before {\n content: \"\\e132\";\n}\n.glyphicon-circle-arrow-up:before {\n content: \"\\e133\";\n}\n.glyphicon-circle-arrow-down:before {\n content: \"\\e134\";\n}\n.glyphicon-globe:before {\n content: \"\\e135\";\n}\n.glyphicon-wrench:before {\n content: \"\\e136\";\n}\n.glyphicon-tasks:before {\n content: \"\\e137\";\n}\n.glyphicon-filter:before {\n content: \"\\e138\";\n}\n.glyphicon-briefcase:before {\n content: \"\\e139\";\n}\n.glyphicon-fullscreen:before {\n content: \"\\e140\";\n}\n.glyphicon-dashboard:before {\n content: \"\\e141\";\n}\n.glyphicon-paperclip:before {\n content: \"\\e142\";\n}\n.glyphicon-heart-empty:before {\n content: \"\\e143\";\n}\n.glyphicon-link:before {\n content: \"\\e144\";\n}\n.glyphicon-phone:before {\n content: \"\\e145\";\n}\n.glyphicon-pushpin:before {\n content: \"\\e146\";\n}\n.glyphicon-usd:before {\n content: \"\\e148\";\n}\n.glyphicon-gbp:before {\n content: \"\\e149\";\n}\n.glyphicon-sort:before {\n content: \"\\e150\";\n}\n.glyphicon-sort-by-alphabet:before {\n content: \"\\e151\";\n}\n.glyphicon-sort-by-alphabet-alt:before {\n content: \"\\e152\";\n}\n.glyphicon-sort-by-order:before {\n content: \"\\e153\";\n}\n.glyphicon-sort-by-order-alt:before {\n content: \"\\e154\";\n}\n.glyphicon-sort-by-attributes:before {\n content: \"\\e155\";\n}\n.glyphicon-sort-by-attributes-alt:before {\n content: \"\\e156\";\n}\n.glyphicon-unchecked:before {\n content: \"\\e157\";\n}\n.glyphicon-expand:before {\n content: \"\\e158\";\n}\n.glyphicon-collapse-down:before {\n content: \"\\e159\";\n}\n.glyphicon-collapse-up:before {\n content: \"\\e160\";\n}\n.glyphicon-log-in:before {\n content: \"\\e161\";\n}\n.glyphicon-flash:before {\n content: \"\\e162\";\n}\n.glyphicon-log-out:before {\n content: \"\\e163\";\n}\n.glyphicon-new-window:before {\n content: \"\\e164\";\n}\n.glyphicon-record:before {\n content: \"\\e165\";\n}\n.glyphicon-save:before {\n content: \"\\e166\";\n}\n.glyphicon-open:before {\n content: \"\\e167\";\n}\n.glyphicon-saved:before {\n content: \"\\e168\";\n}\n.glyphicon-import:before {\n content: \"\\e169\";\n}\n.glyphicon-export:before {\n content: \"\\e170\";\n}\n.glyphicon-send:before {\n content: \"\\e171\";\n}\n.glyphicon-floppy-disk:before {\n content: \"\\e172\";\n}\n.glyphicon-floppy-saved:before {\n content: \"\\e173\";\n}\n.glyphicon-floppy-remove:before {\n content: \"\\e174\";\n}\n.glyphicon-floppy-save:before {\n content: \"\\e175\";\n}\n.glyphicon-floppy-open:before {\n content: \"\\e176\";\n}\n.glyphicon-credit-card:before {\n content: \"\\e177\";\n}\n.glyphicon-transfer:before {\n content: \"\\e178\";\n}\n.glyphicon-cutlery:before {\n content: \"\\e179\";\n}\n.glyphicon-header:before {\n content: \"\\e180\";\n}\n.glyphicon-compressed:before {\n content: \"\\e181\";\n}\n.glyphicon-earphone:before {\n content: \"\\e182\";\n}\n.glyphicon-phone-alt:before {\n content: \"\\e183\";\n}\n.glyphicon-tower:before {\n content: \"\\e184\";\n}\n.glyphicon-stats:before {\n content: \"\\e185\";\n}\n.glyphicon-sd-video:before {\n content: \"\\e186\";\n}\n.glyphicon-hd-video:before {\n content: \"\\e187\";\n}\n.glyphicon-subtitles:before {\n content: \"\\e188\";\n}\n.glyphicon-sound-stereo:before {\n content: \"\\e189\";\n}\n.glyphicon-sound-dolby:before {\n content: \"\\e190\";\n}\n.glyphicon-sound-5-1:before {\n content: \"\\e191\";\n}\n.glyphicon-sound-6-1:before {\n content: \"\\e192\";\n}\n.glyphicon-sound-7-1:before {\n content: \"\\e193\";\n}\n.glyphicon-copyright-mark:before {\n content: \"\\e194\";\n}\n.glyphicon-registration-mark:before {\n content: \"\\e195\";\n}\n.glyphicon-cloud-download:before {\n content: \"\\e197\";\n}\n.glyphicon-cloud-upload:before {\n content: \"\\e198\";\n}\n.glyphicon-tree-conifer:before {\n content: \"\\e199\";\n}\n.glyphicon-tree-deciduous:before {\n content: \"\\e200\";\n}\n.glyphicon-cd:before {\n content: \"\\e201\";\n}\n.glyphicon-save-file:before {\n content: \"\\e202\";\n}\n.glyphicon-open-file:before {\n content: \"\\e203\";\n}\n.glyphicon-level-up:before {\n content: \"\\e204\";\n}\n.glyphicon-copy:before {\n content: \"\\e205\";\n}\n.glyphicon-paste:before {\n content: \"\\e206\";\n}\n.glyphicon-alert:before {\n content: \"\\e209\";\n}\n.glyphicon-equalizer:before {\n content: \"\\e210\";\n}\n.glyphicon-king:before {\n content: \"\\e211\";\n}\n.glyphicon-queen:before {\n content: \"\\e212\";\n}\n.glyphicon-pawn:before {\n content: \"\\e213\";\n}\n.glyphicon-bishop:before {\n content: \"\\e214\";\n}\n.glyphicon-knight:before {\n content: \"\\e215\";\n}\n.glyphicon-baby-formula:before {\n content: \"\\e216\";\n}\n.glyphicon-tent:before {\n content: \"\\26fa\";\n}\n.glyphicon-blackboard:before {\n content: \"\\e218\";\n}\n.glyphicon-bed:before {\n content: \"\\e219\";\n}\n.glyphicon-apple:before {\n content: \"\\f8ff\";\n}\n.glyphicon-erase:before {\n content: \"\\e221\";\n}\n.glyphicon-hourglass:before {\n content: \"\\231b\";\n}\n.glyphicon-lamp:before {\n content: \"\\e223\";\n}\n.glyphicon-duplicate:before {\n content: \"\\e224\";\n}\n.glyphicon-piggy-bank:before {\n content: \"\\e225\";\n}\n.glyphicon-scissors:before {\n content: \"\\e226\";\n}\n.glyphicon-bitcoin:before {\n content: \"\\e227\";\n}\n.glyphicon-btc:before {\n content: \"\\e227\";\n}\n.glyphicon-xbt:before {\n content: \"\\e227\";\n}\n.glyphicon-yen:before {\n content: \"\\00a5\";\n}\n.glyphicon-jpy:before {\n content: \"\\00a5\";\n}\n.glyphicon-ruble:before {\n content: \"\\20bd\";\n}\n.glyphicon-rub:before {\n content: \"\\20bd\";\n}\n.glyphicon-scale:before {\n content: \"\\e230\";\n}\n.glyphicon-ice-lolly:before {\n content: \"\\e231\";\n}\n.glyphicon-ice-lolly-tasted:before {\n content: \"\\e232\";\n}\n.glyphicon-education:before {\n content: \"\\e233\";\n}\n.glyphicon-option-horizontal:before {\n content: \"\\e234\";\n}\n.glyphicon-option-vertical:before {\n content: \"\\e235\";\n}\n.glyphicon-menu-hamburger:before {\n content: \"\\e236\";\n}\n.glyphicon-modal-window:before {\n content: \"\\e237\";\n}\n.glyphicon-oil:before {\n content: \"\\e238\";\n}\n.glyphicon-grain:before {\n content: \"\\e239\";\n}\n.glyphicon-sunglasses:before {\n content: \"\\e240\";\n}\n.glyphicon-text-size:before {\n content: \"\\e241\";\n}\n.glyphicon-text-color:before {\n content: \"\\e242\";\n}\n.glyphicon-text-background:before {\n content: \"\\e243\";\n}\n.glyphicon-object-align-top:before {\n content: \"\\e244\";\n}\n.glyphicon-object-align-bottom:before {\n content: \"\\e245\";\n}\n.glyphicon-object-align-horizontal:before {\n content: \"\\e246\";\n}\n.glyphicon-object-align-left:before {\n content: \"\\e247\";\n}\n.glyphicon-object-align-vertical:before {\n content: \"\\e248\";\n}\n.glyphicon-object-align-right:before {\n content: \"\\e249\";\n}\n.glyphicon-triangle-right:before {\n content: \"\\e250\";\n}\n.glyphicon-triangle-left:before {\n content: \"\\e251\";\n}\n.glyphicon-triangle-bottom:before {\n content: \"\\e252\";\n}\n.glyphicon-triangle-top:before {\n content: \"\\e253\";\n}\n.glyphicon-console:before {\n content: \"\\e254\";\n}\n.glyphicon-superscript:before {\n content: \"\\e255\";\n}\n.glyphicon-subscript:before {\n content: \"\\e256\";\n}\n.glyphicon-menu-left:before {\n content: \"\\e257\";\n}\n.glyphicon-menu-right:before {\n content: \"\\e258\";\n}\n.glyphicon-menu-down:before {\n content: \"\\e259\";\n}\n.glyphicon-menu-up:before {\n content: \"\\e260\";\n}\n* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n*:before,\n*:after {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\nbody {\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 14px;\n line-height: 1.42857143;\n color: #333333;\n background-color: #ffffff;\n}\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\na {\n color: #337ab7;\n text-decoration: none;\n}\na:hover,\na:focus {\n color: #23527c;\n text-decoration: underline;\n}\na:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\nfigure {\n margin: 0;\n}\nimg {\n vertical-align: middle;\n}\n.img-responsive,\n.thumbnail > img,\n.thumbnail a > img,\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.img-rounded {\n border-radius: 6px;\n}\n.img-thumbnail {\n padding: 4px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: all 0.2s ease-in-out;\n -o-transition: all 0.2s ease-in-out;\n transition: all 0.2s ease-in-out;\n display: inline-block;\n max-width: 100%;\n height: auto;\n}\n.img-circle {\n border-radius: 50%;\n}\nhr {\n margin-top: 20px;\n margin-bottom: 20px;\n border: 0;\n border-top: 1px solid #eeeeee;\n}\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n border: 0;\n}\n.sr-only-focusable:active,\n.sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n}\n[role=\"button\"] {\n cursor: pointer;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n font-family: inherit;\n font-weight: 500;\n line-height: 1.1;\n color: inherit;\n}\nh1 small,\nh2 small,\nh3 small,\nh4 small,\nh5 small,\nh6 small,\n.h1 small,\n.h2 small,\n.h3 small,\n.h4 small,\n.h5 small,\n.h6 small,\nh1 .small,\nh2 .small,\nh3 .small,\nh4 .small,\nh5 .small,\nh6 .small,\n.h1 .small,\n.h2 .small,\n.h3 .small,\n.h4 .small,\n.h5 .small,\n.h6 .small {\n font-weight: normal;\n line-height: 1;\n color: #777777;\n}\nh1,\n.h1,\nh2,\n.h2,\nh3,\n.h3 {\n margin-top: 20px;\n margin-bottom: 10px;\n}\nh1 small,\n.h1 small,\nh2 small,\n.h2 small,\nh3 small,\n.h3 small,\nh1 .small,\n.h1 .small,\nh2 .small,\n.h2 .small,\nh3 .small,\n.h3 .small {\n font-size: 65%;\n}\nh4,\n.h4,\nh5,\n.h5,\nh6,\n.h6 {\n margin-top: 10px;\n margin-bottom: 10px;\n}\nh4 small,\n.h4 small,\nh5 small,\n.h5 small,\nh6 small,\n.h6 small,\nh4 .small,\n.h4 .small,\nh5 .small,\n.h5 .small,\nh6 .small,\n.h6 .small {\n font-size: 75%;\n}\nh1,\n.h1 {\n font-size: 36px;\n}\nh2,\n.h2 {\n font-size: 30px;\n}\nh3,\n.h3 {\n font-size: 24px;\n}\nh4,\n.h4 {\n font-size: 18px;\n}\nh5,\n.h5 {\n font-size: 14px;\n}\nh6,\n.h6 {\n font-size: 12px;\n}\np {\n margin: 0 0 10px;\n}\n.lead {\n margin-bottom: 20px;\n font-size: 16px;\n font-weight: 300;\n line-height: 1.4;\n}\n@media (min-width: 768px) {\n .lead {\n font-size: 21px;\n }\n}\nsmall,\n.small {\n font-size: 85%;\n}\nmark,\n.mark {\n background-color: #fcf8e3;\n padding: .2em;\n}\n.text-left {\n text-align: left;\n}\n.text-right {\n text-align: right;\n}\n.text-center {\n text-align: center;\n}\n.text-justify {\n text-align: justify;\n}\n.text-nowrap {\n white-space: nowrap;\n}\n.text-lowercase {\n text-transform: lowercase;\n}\n.text-uppercase {\n text-transform: uppercase;\n}\n.text-capitalize {\n text-transform: capitalize;\n}\n.text-muted {\n color: #777777;\n}\n.text-primary {\n color: #337ab7;\n}\na.text-primary:hover,\na.text-primary:focus {\n color: #286090;\n}\n.text-success {\n color: #3c763d;\n}\na.text-success:hover,\na.text-success:focus {\n color: #2b542c;\n}\n.text-info {\n color: #31708f;\n}\na.text-info:hover,\na.text-info:focus {\n color: #245269;\n}\n.text-warning {\n color: #8a6d3b;\n}\na.text-warning:hover,\na.text-warning:focus {\n color: #66512c;\n}\n.text-danger {\n color: #a94442;\n}\na.text-danger:hover,\na.text-danger:focus {\n color: #843534;\n}\n.bg-primary {\n color: #fff;\n background-color: #337ab7;\n}\na.bg-primary:hover,\na.bg-primary:focus {\n background-color: #286090;\n}\n.bg-success {\n background-color: #dff0d8;\n}\na.bg-success:hover,\na.bg-success:focus {\n background-color: #c1e2b3;\n}\n.bg-info {\n background-color: #d9edf7;\n}\na.bg-info:hover,\na.bg-info:focus {\n background-color: #afd9ee;\n}\n.bg-warning {\n background-color: #fcf8e3;\n}\na.bg-warning:hover,\na.bg-warning:focus {\n background-color: #f7ecb5;\n}\n.bg-danger {\n background-color: #f2dede;\n}\na.bg-danger:hover,\na.bg-danger:focus {\n background-color: #e4b9b9;\n}\n.page-header {\n padding-bottom: 9px;\n margin: 40px 0 20px;\n border-bottom: 1px solid #eeeeee;\n}\nul,\nol {\n margin-top: 0;\n margin-bottom: 10px;\n}\nul ul,\nol ul,\nul ol,\nol ol {\n margin-bottom: 0;\n}\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n.list-inline {\n padding-left: 0;\n list-style: none;\n margin-left: -5px;\n}\n.list-inline > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n}\ndl {\n margin-top: 0;\n margin-bottom: 20px;\n}\ndt,\ndd {\n line-height: 1.42857143;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0;\n}\n@media (min-width: 768px) {\n .dl-horizontal dt {\n float: left;\n width: 160px;\n clear: left;\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .dl-horizontal dd {\n margin-left: 180px;\n }\n}\nabbr[title],\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted #777777;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\nblockquote {\n padding: 10px 20px;\n margin: 0 0 20px;\n font-size: 17.5px;\n border-left: 5px solid #eeeeee;\n}\nblockquote p:last-child,\nblockquote ul:last-child,\nblockquote ol:last-child {\n margin-bottom: 0;\n}\nblockquote footer,\nblockquote small,\nblockquote .small {\n display: block;\n font-size: 80%;\n line-height: 1.42857143;\n color: #777777;\n}\nblockquote footer:before,\nblockquote small:before,\nblockquote .small:before {\n content: '\\2014 \\00A0';\n}\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid #eeeeee;\n border-left: 0;\n text-align: right;\n}\n.blockquote-reverse footer:before,\nblockquote.pull-right footer:before,\n.blockquote-reverse small:before,\nblockquote.pull-right small:before,\n.blockquote-reverse .small:before,\nblockquote.pull-right .small:before {\n content: '';\n}\n.blockquote-reverse footer:after,\nblockquote.pull-right footer:after,\n.blockquote-reverse small:after,\nblockquote.pull-right small:after,\n.blockquote-reverse .small:after,\nblockquote.pull-right .small:after {\n content: '\\00A0 \\2014';\n}\naddress {\n margin-bottom: 20px;\n font-style: normal;\n line-height: 1.42857143;\n}\ncode,\nkbd,\npre,\nsamp {\n font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n}\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: #c7254e;\n background-color: #f9f2f4;\n border-radius: 4px;\n}\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: #ffffff;\n background-color: #333333;\n border-radius: 3px;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n}\npre {\n display: block;\n padding: 9.5px;\n margin: 0 0 10px;\n font-size: 13px;\n line-height: 1.42857143;\n word-break: break-all;\n word-wrap: break-word;\n color: #333333;\n background-color: #f5f5f5;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\npre code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n}\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n.container {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n@media (min-width: 768px) {\n .container {\n width: 750px;\n }\n}\n@media (min-width: 992px) {\n .container {\n width: 970px;\n }\n}\n@media (min-width: 1200px) {\n .container {\n width: 1170px;\n }\n}\n.container-fluid {\n margin-right: auto;\n margin-left: auto;\n padding-left: 15px;\n padding-right: 15px;\n}\n.row {\n margin-left: -15px;\n margin-right: -15px;\n}\n.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\n position: relative;\n min-height: 1px;\n padding-left: 15px;\n padding-right: 15px;\n}\n.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\n float: left;\n}\n.col-xs-12 {\n width: 100%;\n}\n.col-xs-11 {\n width: 91.66666667%;\n}\n.col-xs-10 {\n width: 83.33333333%;\n}\n.col-xs-9 {\n width: 75%;\n}\n.col-xs-8 {\n width: 66.66666667%;\n}\n.col-xs-7 {\n width: 58.33333333%;\n}\n.col-xs-6 {\n width: 50%;\n}\n.col-xs-5 {\n width: 41.66666667%;\n}\n.col-xs-4 {\n width: 33.33333333%;\n}\n.col-xs-3 {\n width: 25%;\n}\n.col-xs-2 {\n width: 16.66666667%;\n}\n.col-xs-1 {\n width: 8.33333333%;\n}\n.col-xs-pull-12 {\n right: 100%;\n}\n.col-xs-pull-11 {\n right: 91.66666667%;\n}\n.col-xs-pull-10 {\n right: 83.33333333%;\n}\n.col-xs-pull-9 {\n right: 75%;\n}\n.col-xs-pull-8 {\n right: 66.66666667%;\n}\n.col-xs-pull-7 {\n right: 58.33333333%;\n}\n.col-xs-pull-6 {\n right: 50%;\n}\n.col-xs-pull-5 {\n right: 41.66666667%;\n}\n.col-xs-pull-4 {\n right: 33.33333333%;\n}\n.col-xs-pull-3 {\n right: 25%;\n}\n.col-xs-pull-2 {\n right: 16.66666667%;\n}\n.col-xs-pull-1 {\n right: 8.33333333%;\n}\n.col-xs-pull-0 {\n right: auto;\n}\n.col-xs-push-12 {\n left: 100%;\n}\n.col-xs-push-11 {\n left: 91.66666667%;\n}\n.col-xs-push-10 {\n left: 83.33333333%;\n}\n.col-xs-push-9 {\n left: 75%;\n}\n.col-xs-push-8 {\n left: 66.66666667%;\n}\n.col-xs-push-7 {\n left: 58.33333333%;\n}\n.col-xs-push-6 {\n left: 50%;\n}\n.col-xs-push-5 {\n left: 41.66666667%;\n}\n.col-xs-push-4 {\n left: 33.33333333%;\n}\n.col-xs-push-3 {\n left: 25%;\n}\n.col-xs-push-2 {\n left: 16.66666667%;\n}\n.col-xs-push-1 {\n left: 8.33333333%;\n}\n.col-xs-push-0 {\n left: auto;\n}\n.col-xs-offset-12 {\n margin-left: 100%;\n}\n.col-xs-offset-11 {\n margin-left: 91.66666667%;\n}\n.col-xs-offset-10 {\n margin-left: 83.33333333%;\n}\n.col-xs-offset-9 {\n margin-left: 75%;\n}\n.col-xs-offset-8 {\n margin-left: 66.66666667%;\n}\n.col-xs-offset-7 {\n margin-left: 58.33333333%;\n}\n.col-xs-offset-6 {\n margin-left: 50%;\n}\n.col-xs-offset-5 {\n margin-left: 41.66666667%;\n}\n.col-xs-offset-4 {\n margin-left: 33.33333333%;\n}\n.col-xs-offset-3 {\n margin-left: 25%;\n}\n.col-xs-offset-2 {\n margin-left: 16.66666667%;\n}\n.col-xs-offset-1 {\n margin-left: 8.33333333%;\n}\n.col-xs-offset-0 {\n margin-left: 0%;\n}\n@media (min-width: 768px) {\n .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 {\n float: left;\n }\n .col-sm-12 {\n width: 100%;\n }\n .col-sm-11 {\n width: 91.66666667%;\n }\n .col-sm-10 {\n width: 83.33333333%;\n }\n .col-sm-9 {\n width: 75%;\n }\n .col-sm-8 {\n width: 66.66666667%;\n }\n .col-sm-7 {\n width: 58.33333333%;\n }\n .col-sm-6 {\n width: 50%;\n }\n .col-sm-5 {\n width: 41.66666667%;\n }\n .col-sm-4 {\n width: 33.33333333%;\n }\n .col-sm-3 {\n width: 25%;\n }\n .col-sm-2 {\n width: 16.66666667%;\n }\n .col-sm-1 {\n width: 8.33333333%;\n }\n .col-sm-pull-12 {\n right: 100%;\n }\n .col-sm-pull-11 {\n right: 91.66666667%;\n }\n .col-sm-pull-10 {\n right: 83.33333333%;\n }\n .col-sm-pull-9 {\n right: 75%;\n }\n .col-sm-pull-8 {\n right: 66.66666667%;\n }\n .col-sm-pull-7 {\n right: 58.33333333%;\n }\n .col-sm-pull-6 {\n right: 50%;\n }\n .col-sm-pull-5 {\n right: 41.66666667%;\n }\n .col-sm-pull-4 {\n right: 33.33333333%;\n }\n .col-sm-pull-3 {\n right: 25%;\n }\n .col-sm-pull-2 {\n right: 16.66666667%;\n }\n .col-sm-pull-1 {\n right: 8.33333333%;\n }\n .col-sm-pull-0 {\n right: auto;\n }\n .col-sm-push-12 {\n left: 100%;\n }\n .col-sm-push-11 {\n left: 91.66666667%;\n }\n .col-sm-push-10 {\n left: 83.33333333%;\n }\n .col-sm-push-9 {\n left: 75%;\n }\n .col-sm-push-8 {\n left: 66.66666667%;\n }\n .col-sm-push-7 {\n left: 58.33333333%;\n }\n .col-sm-push-6 {\n left: 50%;\n }\n .col-sm-push-5 {\n left: 41.66666667%;\n }\n .col-sm-push-4 {\n left: 33.33333333%;\n }\n .col-sm-push-3 {\n left: 25%;\n }\n .col-sm-push-2 {\n left: 16.66666667%;\n }\n .col-sm-push-1 {\n left: 8.33333333%;\n }\n .col-sm-push-0 {\n left: auto;\n }\n .col-sm-offset-12 {\n margin-left: 100%;\n }\n .col-sm-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-sm-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-sm-offset-9 {\n margin-left: 75%;\n }\n .col-sm-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-sm-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-sm-offset-6 {\n margin-left: 50%;\n }\n .col-sm-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-sm-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-sm-offset-3 {\n margin-left: 25%;\n }\n .col-sm-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-sm-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-sm-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 992px) {\n .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 {\n float: left;\n }\n .col-md-12 {\n width: 100%;\n }\n .col-md-11 {\n width: 91.66666667%;\n }\n .col-md-10 {\n width: 83.33333333%;\n }\n .col-md-9 {\n width: 75%;\n }\n .col-md-8 {\n width: 66.66666667%;\n }\n .col-md-7 {\n width: 58.33333333%;\n }\n .col-md-6 {\n width: 50%;\n }\n .col-md-5 {\n width: 41.66666667%;\n }\n .col-md-4 {\n width: 33.33333333%;\n }\n .col-md-3 {\n width: 25%;\n }\n .col-md-2 {\n width: 16.66666667%;\n }\n .col-md-1 {\n width: 8.33333333%;\n }\n .col-md-pull-12 {\n right: 100%;\n }\n .col-md-pull-11 {\n right: 91.66666667%;\n }\n .col-md-pull-10 {\n right: 83.33333333%;\n }\n .col-md-pull-9 {\n right: 75%;\n }\n .col-md-pull-8 {\n right: 66.66666667%;\n }\n .col-md-pull-7 {\n right: 58.33333333%;\n }\n .col-md-pull-6 {\n right: 50%;\n }\n .col-md-pull-5 {\n right: 41.66666667%;\n }\n .col-md-pull-4 {\n right: 33.33333333%;\n }\n .col-md-pull-3 {\n right: 25%;\n }\n .col-md-pull-2 {\n right: 16.66666667%;\n }\n .col-md-pull-1 {\n right: 8.33333333%;\n }\n .col-md-pull-0 {\n right: auto;\n }\n .col-md-push-12 {\n left: 100%;\n }\n .col-md-push-11 {\n left: 91.66666667%;\n }\n .col-md-push-10 {\n left: 83.33333333%;\n }\n .col-md-push-9 {\n left: 75%;\n }\n .col-md-push-8 {\n left: 66.66666667%;\n }\n .col-md-push-7 {\n left: 58.33333333%;\n }\n .col-md-push-6 {\n left: 50%;\n }\n .col-md-push-5 {\n left: 41.66666667%;\n }\n .col-md-push-4 {\n left: 33.33333333%;\n }\n .col-md-push-3 {\n left: 25%;\n }\n .col-md-push-2 {\n left: 16.66666667%;\n }\n .col-md-push-1 {\n left: 8.33333333%;\n }\n .col-md-push-0 {\n left: auto;\n }\n .col-md-offset-12 {\n margin-left: 100%;\n }\n .col-md-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-md-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-md-offset-9 {\n margin-left: 75%;\n }\n .col-md-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-md-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-md-offset-6 {\n margin-left: 50%;\n }\n .col-md-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-md-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-md-offset-3 {\n margin-left: 25%;\n }\n .col-md-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-md-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-md-offset-0 {\n margin-left: 0%;\n }\n}\n@media (min-width: 1200px) {\n .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 {\n float: left;\n }\n .col-lg-12 {\n width: 100%;\n }\n .col-lg-11 {\n width: 91.66666667%;\n }\n .col-lg-10 {\n width: 83.33333333%;\n }\n .col-lg-9 {\n width: 75%;\n }\n .col-lg-8 {\n width: 66.66666667%;\n }\n .col-lg-7 {\n width: 58.33333333%;\n }\n .col-lg-6 {\n width: 50%;\n }\n .col-lg-5 {\n width: 41.66666667%;\n }\n .col-lg-4 {\n width: 33.33333333%;\n }\n .col-lg-3 {\n width: 25%;\n }\n .col-lg-2 {\n width: 16.66666667%;\n }\n .col-lg-1 {\n width: 8.33333333%;\n }\n .col-lg-pull-12 {\n right: 100%;\n }\n .col-lg-pull-11 {\n right: 91.66666667%;\n }\n .col-lg-pull-10 {\n right: 83.33333333%;\n }\n .col-lg-pull-9 {\n right: 75%;\n }\n .col-lg-pull-8 {\n right: 66.66666667%;\n }\n .col-lg-pull-7 {\n right: 58.33333333%;\n }\n .col-lg-pull-6 {\n right: 50%;\n }\n .col-lg-pull-5 {\n right: 41.66666667%;\n }\n .col-lg-pull-4 {\n right: 33.33333333%;\n }\n .col-lg-pull-3 {\n right: 25%;\n }\n .col-lg-pull-2 {\n right: 16.66666667%;\n }\n .col-lg-pull-1 {\n right: 8.33333333%;\n }\n .col-lg-pull-0 {\n right: auto;\n }\n .col-lg-push-12 {\n left: 100%;\n }\n .col-lg-push-11 {\n left: 91.66666667%;\n }\n .col-lg-push-10 {\n left: 83.33333333%;\n }\n .col-lg-push-9 {\n left: 75%;\n }\n .col-lg-push-8 {\n left: 66.66666667%;\n }\n .col-lg-push-7 {\n left: 58.33333333%;\n }\n .col-lg-push-6 {\n left: 50%;\n }\n .col-lg-push-5 {\n left: 41.66666667%;\n }\n .col-lg-push-4 {\n left: 33.33333333%;\n }\n .col-lg-push-3 {\n left: 25%;\n }\n .col-lg-push-2 {\n left: 16.66666667%;\n }\n .col-lg-push-1 {\n left: 8.33333333%;\n }\n .col-lg-push-0 {\n left: auto;\n }\n .col-lg-offset-12 {\n margin-left: 100%;\n }\n .col-lg-offset-11 {\n margin-left: 91.66666667%;\n }\n .col-lg-offset-10 {\n margin-left: 83.33333333%;\n }\n .col-lg-offset-9 {\n margin-left: 75%;\n }\n .col-lg-offset-8 {\n margin-left: 66.66666667%;\n }\n .col-lg-offset-7 {\n margin-left: 58.33333333%;\n }\n .col-lg-offset-6 {\n margin-left: 50%;\n }\n .col-lg-offset-5 {\n margin-left: 41.66666667%;\n }\n .col-lg-offset-4 {\n margin-left: 33.33333333%;\n }\n .col-lg-offset-3 {\n margin-left: 25%;\n }\n .col-lg-offset-2 {\n margin-left: 16.66666667%;\n }\n .col-lg-offset-1 {\n margin-left: 8.33333333%;\n }\n .col-lg-offset-0 {\n margin-left: 0%;\n }\n}\ntable {\n background-color: transparent;\n}\ncaption {\n padding-top: 8px;\n padding-bottom: 8px;\n color: #777777;\n text-align: left;\n}\nth {\n text-align: left;\n}\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n padding: 8px;\n line-height: 1.42857143;\n vertical-align: top;\n border-top: 1px solid #dddddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #dddddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n border-top: 0;\n}\n.table > tbody + tbody {\n border-top: 2px solid #dddddd;\n}\n.table .table {\n background-color: #ffffff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n padding: 5px;\n}\n.table-bordered {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > tbody > tr > th,\n.table-bordered > tfoot > tr > th,\n.table-bordered > thead > tr > td,\n.table-bordered > tbody > tr > td,\n.table-bordered > tfoot > tr > td {\n border: 1px solid #dddddd;\n}\n.table-bordered > thead > tr > th,\n.table-bordered > thead > tr > td {\n border-bottom-width: 2px;\n}\n.table-striped > tbody > tr:nth-of-type(odd) {\n background-color: #f9f9f9;\n}\n.table-hover > tbody > tr:hover {\n background-color: #f5f5f5;\n}\ntable col[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-column;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n position: static;\n float: none;\n display: table-cell;\n}\n.table > thead > tr > td.active,\n.table > tbody > tr > td.active,\n.table > tfoot > tr > td.active,\n.table > thead > tr > th.active,\n.table > tbody > tr > th.active,\n.table > tfoot > tr > th.active,\n.table > thead > tr.active > td,\n.table > tbody > tr.active > td,\n.table > tfoot > tr.active > td,\n.table > thead > tr.active > th,\n.table > tbody > tr.active > th,\n.table > tfoot > tr.active > th {\n background-color: #f5f5f5;\n}\n.table-hover > tbody > tr > td.active:hover,\n.table-hover > tbody > tr > th.active:hover,\n.table-hover > tbody > tr.active:hover > td,\n.table-hover > tbody > tr:hover > .active,\n.table-hover > tbody > tr.active:hover > th {\n background-color: #e8e8e8;\n}\n.table > thead > tr > td.success,\n.table > tbody > tr > td.success,\n.table > tfoot > tr > td.success,\n.table > thead > tr > th.success,\n.table > tbody > tr > th.success,\n.table > tfoot > tr > th.success,\n.table > thead > tr.success > td,\n.table > tbody > tr.success > td,\n.table > tfoot > tr.success > td,\n.table > thead > tr.success > th,\n.table > tbody > tr.success > th,\n.table > tfoot > tr.success > th {\n background-color: #dff0d8;\n}\n.table-hover > tbody > tr > td.success:hover,\n.table-hover > tbody > tr > th.success:hover,\n.table-hover > tbody > tr.success:hover > td,\n.table-hover > tbody > tr:hover > .success,\n.table-hover > tbody > tr.success:hover > th {\n background-color: #d0e9c6;\n}\n.table > thead > tr > td.info,\n.table > tbody > tr > td.info,\n.table > tfoot > tr > td.info,\n.table > thead > tr > th.info,\n.table > tbody > tr > th.info,\n.table > tfoot > tr > th.info,\n.table > thead > tr.info > td,\n.table > tbody > tr.info > td,\n.table > tfoot > tr.info > td,\n.table > thead > tr.info > th,\n.table > tbody > tr.info > th,\n.table > tfoot > tr.info > th {\n background-color: #d9edf7;\n}\n.table-hover > tbody > tr > td.info:hover,\n.table-hover > tbody > tr > th.info:hover,\n.table-hover > tbody > tr.info:hover > td,\n.table-hover > tbody > tr:hover > .info,\n.table-hover > tbody > tr.info:hover > th {\n background-color: #c4e3f3;\n}\n.table > thead > tr > td.warning,\n.table > tbody > tr > td.warning,\n.table > tfoot > tr > td.warning,\n.table > thead > tr > th.warning,\n.table > tbody > tr > th.warning,\n.table > tfoot > tr > th.warning,\n.table > thead > tr.warning > td,\n.table > tbody > tr.warning > td,\n.table > tfoot > tr.warning > td,\n.table > thead > tr.warning > th,\n.table > tbody > tr.warning > th,\n.table > tfoot > tr.warning > th {\n background-color: #fcf8e3;\n}\n.table-hover > tbody > tr > td.warning:hover,\n.table-hover > tbody > tr > th.warning:hover,\n.table-hover > tbody > tr.warning:hover > td,\n.table-hover > tbody > tr:hover > .warning,\n.table-hover > tbody > tr.warning:hover > th {\n background-color: #faf2cc;\n}\n.table > thead > tr > td.danger,\n.table > tbody > tr > td.danger,\n.table > tfoot > tr > td.danger,\n.table > thead > tr > th.danger,\n.table > tbody > tr > th.danger,\n.table > tfoot > tr > th.danger,\n.table > thead > tr.danger > td,\n.table > tbody > tr.danger > td,\n.table > tfoot > tr.danger > td,\n.table > thead > tr.danger > th,\n.table > tbody > tr.danger > th,\n.table > tfoot > tr.danger > th {\n background-color: #f2dede;\n}\n.table-hover > tbody > tr > td.danger:hover,\n.table-hover > tbody > tr > th.danger:hover,\n.table-hover > tbody > tr.danger:hover > td,\n.table-hover > tbody > tr:hover > .danger,\n.table-hover > tbody > tr.danger:hover > th {\n background-color: #ebcccc;\n}\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%;\n}\n@media screen and (max-width: 767px) {\n .table-responsive {\n width: 100%;\n margin-bottom: 15px;\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid #dddddd;\n }\n .table-responsive > .table {\n margin-bottom: 0;\n }\n .table-responsive > .table > thead > tr > th,\n .table-responsive > .table > tbody > tr > th,\n .table-responsive > .table > tfoot > tr > th,\n .table-responsive > .table > thead > tr > td,\n .table-responsive > .table > tbody > tr > td,\n .table-responsive > .table > tfoot > tr > td {\n white-space: nowrap;\n }\n .table-responsive > .table-bordered {\n border: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:first-child,\n .table-responsive > .table-bordered > tbody > tr > th:first-child,\n .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n .table-responsive > .table-bordered > thead > tr > td:first-child,\n .table-responsive > .table-bordered > tbody > tr > td:first-child,\n .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n }\n .table-responsive > .table-bordered > thead > tr > th:last-child,\n .table-responsive > .table-bordered > tbody > tr > th:last-child,\n .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n .table-responsive > .table-bordered > thead > tr > td:last-child,\n .table-responsive > .table-bordered > tbody > tr > td:last-child,\n .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n }\n .table-responsive > .table-bordered > tbody > tr:last-child > th,\n .table-responsive > .table-bordered > tfoot > tr:last-child > th,\n .table-responsive > .table-bordered > tbody > tr:last-child > td,\n .table-responsive > .table-bordered > tfoot > tr:last-child > td {\n border-bottom: 0;\n }\n}\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n min-width: 0;\n}\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: 20px;\n font-size: 21px;\n line-height: inherit;\n color: #333333;\n border: 0;\n border-bottom: 1px solid #e5e5e5;\n}\nlabel {\n display: inline-block;\n max-width: 100%;\n margin-bottom: 5px;\n font-weight: bold;\n}\ninput[type=\"search\"] {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9;\n line-height: normal;\n}\ninput[type=\"file\"] {\n display: block;\n}\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\nselect[multiple],\nselect[size] {\n height: auto;\n}\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\noutput {\n display: block;\n padding-top: 7px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n}\n.form-control {\n display: block;\n width: 100%;\n height: 34px;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n color: #555555;\n background-color: #ffffff;\n background-image: none;\n border: 1px solid #cccccc;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\n}\n.form-control:focus {\n border-color: #66afe9;\n outline: 0;\n -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\n}\n.form-control::-moz-placeholder {\n color: #999999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999999;\n}\n.form-control[disabled],\n.form-control[readonly],\nfieldset[disabled] .form-control {\n background-color: #eeeeee;\n opacity: 1;\n}\n.form-control[disabled],\nfieldset[disabled] .form-control {\n cursor: not-allowed;\n}\ntextarea.form-control {\n height: auto;\n}\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"].form-control,\n input[type=\"time\"].form-control,\n input[type=\"datetime-local\"].form-control,\n input[type=\"month\"].form-control {\n line-height: 34px;\n }\n input[type=\"date\"].input-sm,\n input[type=\"time\"].input-sm,\n input[type=\"datetime-local\"].input-sm,\n input[type=\"month\"].input-sm,\n .input-group-sm input[type=\"date\"],\n .input-group-sm input[type=\"time\"],\n .input-group-sm input[type=\"datetime-local\"],\n .input-group-sm input[type=\"month\"] {\n line-height: 30px;\n }\n input[type=\"date\"].input-lg,\n input[type=\"time\"].input-lg,\n input[type=\"datetime-local\"].input-lg,\n input[type=\"month\"].input-lg,\n .input-group-lg input[type=\"date\"],\n .input-group-lg input[type=\"time\"],\n .input-group-lg input[type=\"datetime-local\"],\n .input-group-lg input[type=\"month\"] {\n line-height: 46px;\n }\n}\n.form-group {\n margin-bottom: 15px;\n}\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.radio label,\n.checkbox label {\n min-height: 20px;\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px;\n}\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px;\n}\ninput[type=\"radio\"][disabled],\ninput[type=\"checkbox\"][disabled],\ninput[type=\"radio\"].disabled,\ninput[type=\"checkbox\"].disabled,\nfieldset[disabled] input[type=\"radio\"],\nfieldset[disabled] input[type=\"checkbox\"] {\n cursor: not-allowed;\n}\n.radio-inline.disabled,\n.checkbox-inline.disabled,\nfieldset[disabled] .radio-inline,\nfieldset[disabled] .checkbox-inline {\n cursor: not-allowed;\n}\n.radio.disabled label,\n.checkbox.disabled label,\nfieldset[disabled] .radio label,\nfieldset[disabled] .checkbox label {\n cursor: not-allowed;\n}\n.form-control-static {\n padding-top: 7px;\n padding-bottom: 7px;\n margin-bottom: 0;\n min-height: 34px;\n}\n.form-control-static.input-lg,\n.form-control-static.input-sm {\n padding-left: 0;\n padding-right: 0;\n}\n.input-sm {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-sm {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-sm,\nselect[multiple].input-sm {\n height: auto;\n}\n.form-group-sm .form-control {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.form-group-sm select.form-control {\n height: 30px;\n line-height: 30px;\n}\n.form-group-sm textarea.form-control,\n.form-group-sm select[multiple].form-control {\n height: auto;\n}\n.form-group-sm .form-control-static {\n height: 30px;\n min-height: 32px;\n padding: 6px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.input-lg {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-lg {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-lg,\nselect[multiple].input-lg {\n height: auto;\n}\n.form-group-lg .form-control {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.form-group-lg select.form-control {\n height: 46px;\n line-height: 46px;\n}\n.form-group-lg textarea.form-control,\n.form-group-lg select[multiple].form-control {\n height: auto;\n}\n.form-group-lg .form-control-static {\n height: 46px;\n min-height: 38px;\n padding: 11px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.has-feedback {\n position: relative;\n}\n.has-feedback .form-control {\n padding-right: 42.5px;\n}\n.form-control-feedback {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n display: block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n pointer-events: none;\n}\n.input-lg + .form-control-feedback,\n.input-group-lg + .form-control-feedback,\n.form-group-lg .form-control + .form-control-feedback {\n width: 46px;\n height: 46px;\n line-height: 46px;\n}\n.input-sm + .form-control-feedback,\n.input-group-sm + .form-control-feedback,\n.form-group-sm .form-control + .form-control-feedback {\n width: 30px;\n height: 30px;\n line-height: 30px;\n}\n.has-success .help-block,\n.has-success .control-label,\n.has-success .radio,\n.has-success .checkbox,\n.has-success .radio-inline,\n.has-success .checkbox-inline,\n.has-success.radio label,\n.has-success.checkbox label,\n.has-success.radio-inline label,\n.has-success.checkbox-inline label {\n color: #3c763d;\n}\n.has-success .form-control {\n border-color: #3c763d;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-success .form-control:focus {\n border-color: #2b542c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;\n}\n.has-success .input-group-addon {\n color: #3c763d;\n border-color: #3c763d;\n background-color: #dff0d8;\n}\n.has-success .form-control-feedback {\n color: #3c763d;\n}\n.has-warning .help-block,\n.has-warning .control-label,\n.has-warning .radio,\n.has-warning .checkbox,\n.has-warning .radio-inline,\n.has-warning .checkbox-inline,\n.has-warning.radio label,\n.has-warning.checkbox label,\n.has-warning.radio-inline label,\n.has-warning.checkbox-inline label {\n color: #8a6d3b;\n}\n.has-warning .form-control {\n border-color: #8a6d3b;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-warning .form-control:focus {\n border-color: #66512c;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;\n}\n.has-warning .input-group-addon {\n color: #8a6d3b;\n border-color: #8a6d3b;\n background-color: #fcf8e3;\n}\n.has-warning .form-control-feedback {\n color: #8a6d3b;\n}\n.has-error .help-block,\n.has-error .control-label,\n.has-error .radio,\n.has-error .checkbox,\n.has-error .radio-inline,\n.has-error .checkbox-inline,\n.has-error.radio label,\n.has-error.checkbox label,\n.has-error.radio-inline label,\n.has-error.checkbox-inline label {\n color: #a94442;\n}\n.has-error .form-control {\n border-color: #a94442;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.has-error .form-control:focus {\n border-color: #843534;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;\n}\n.has-error .input-group-addon {\n color: #a94442;\n border-color: #a94442;\n background-color: #f2dede;\n}\n.has-error .form-control-feedback {\n color: #a94442;\n}\n.has-feedback label ~ .form-control-feedback {\n top: 25px;\n}\n.has-feedback label.sr-only ~ .form-control-feedback {\n top: 0;\n}\n.help-block {\n display: block;\n margin-top: 5px;\n margin-bottom: 10px;\n color: #737373;\n}\n@media (min-width: 768px) {\n .form-inline .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-static {\n display: inline-block;\n }\n .form-inline .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .form-inline .input-group .input-group-addon,\n .form-inline .input-group .input-group-btn,\n .form-inline .input-group .form-control {\n width: auto;\n }\n .form-inline .input-group > .form-control {\n width: 100%;\n }\n .form-inline .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio,\n .form-inline .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .form-inline .radio label,\n .form-inline .checkbox label {\n padding-left: 0;\n }\n .form-inline .radio input[type=\"radio\"],\n .form-inline .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .form-inline .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox,\n.form-horizontal .radio-inline,\n.form-horizontal .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: 7px;\n}\n.form-horizontal .radio,\n.form-horizontal .checkbox {\n min-height: 27px;\n}\n.form-horizontal .form-group {\n margin-left: -15px;\n margin-right: -15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .control-label {\n text-align: right;\n margin-bottom: 0;\n padding-top: 7px;\n }\n}\n.form-horizontal .has-feedback .form-control-feedback {\n right: 15px;\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-lg .control-label {\n padding-top: 14.333333px;\n font-size: 18px;\n }\n}\n@media (min-width: 768px) {\n .form-horizontal .form-group-sm .control-label {\n padding-top: 6px;\n font-size: 12px;\n }\n}\n.btn {\n display: inline-block;\n margin-bottom: 0;\n font-weight: normal;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none;\n border: 1px solid transparent;\n white-space: nowrap;\n padding: 6px 12px;\n font-size: 14px;\n line-height: 1.42857143;\n border-radius: 4px;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.btn:focus,\n.btn:active:focus,\n.btn.active:focus,\n.btn.focus,\n.btn:active.focus,\n.btn.active.focus {\n outline: thin dotted;\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333333;\n text-decoration: none;\n}\n.btn:active,\n.btn.active {\n outline: 0;\n background-image: none;\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn.disabled,\n.btn[disabled],\nfieldset[disabled] .btn {\n cursor: not-allowed;\n opacity: 0.65;\n filter: alpha(opacity=65);\n -webkit-box-shadow: none;\n box-shadow: none;\n}\na.btn.disabled,\nfieldset[disabled] a.btn {\n pointer-events: none;\n}\n.btn-default {\n color: #333333;\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n color: #333333;\n background-color: #e6e6e6;\n border-color: #adadad;\n}\n.btn-default:active:hover,\n.btn-default.active:hover,\n.open > .dropdown-toggle.btn-default:hover,\n.btn-default:active:focus,\n.btn-default.active:focus,\n.open > .dropdown-toggle.btn-default:focus,\n.btn-default:active.focus,\n.btn-default.active.focus,\n.open > .dropdown-toggle.btn-default.focus {\n color: #333333;\n background-color: #d4d4d4;\n border-color: #8c8c8c;\n}\n.btn-default:active,\n.btn-default.active,\n.open > .dropdown-toggle.btn-default {\n background-image: none;\n}\n.btn-default.disabled,\n.btn-default[disabled],\nfieldset[disabled] .btn-default,\n.btn-default.disabled:hover,\n.btn-default[disabled]:hover,\nfieldset[disabled] .btn-default:hover,\n.btn-default.disabled:focus,\n.btn-default[disabled]:focus,\nfieldset[disabled] .btn-default:focus,\n.btn-default.disabled.focus,\n.btn-default[disabled].focus,\nfieldset[disabled] .btn-default.focus,\n.btn-default.disabled:active,\n.btn-default[disabled]:active,\nfieldset[disabled] .btn-default:active,\n.btn-default.disabled.active,\n.btn-default[disabled].active,\nfieldset[disabled] .btn-default.active {\n background-color: #ffffff;\n border-color: #cccccc;\n}\n.btn-default .badge {\n color: #ffffff;\n background-color: #333333;\n}\n.btn-primary {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #ffffff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n color: #ffffff;\n background-color: #286090;\n border-color: #204d74;\n}\n.btn-primary:active:hover,\n.btn-primary.active:hover,\n.open > .dropdown-toggle.btn-primary:hover,\n.btn-primary:active:focus,\n.btn-primary.active:focus,\n.open > .dropdown-toggle.btn-primary:focus,\n.btn-primary:active.focus,\n.btn-primary.active.focus,\n.open > .dropdown-toggle.btn-primary.focus {\n color: #ffffff;\n background-color: #204d74;\n border-color: #122b40;\n}\n.btn-primary:active,\n.btn-primary.active,\n.open > .dropdown-toggle.btn-primary {\n background-image: none;\n}\n.btn-primary.disabled,\n.btn-primary[disabled],\nfieldset[disabled] .btn-primary,\n.btn-primary.disabled:hover,\n.btn-primary[disabled]:hover,\nfieldset[disabled] .btn-primary:hover,\n.btn-primary.disabled:focus,\n.btn-primary[disabled]:focus,\nfieldset[disabled] .btn-primary:focus,\n.btn-primary.disabled.focus,\n.btn-primary[disabled].focus,\nfieldset[disabled] .btn-primary.focus,\n.btn-primary.disabled:active,\n.btn-primary[disabled]:active,\nfieldset[disabled] .btn-primary:active,\n.btn-primary.disabled.active,\n.btn-primary[disabled].active,\nfieldset[disabled] .btn-primary.active {\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.btn-success {\n color: #ffffff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #ffffff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n color: #ffffff;\n background-color: #449d44;\n border-color: #398439;\n}\n.btn-success:active:hover,\n.btn-success.active:hover,\n.open > .dropdown-toggle.btn-success:hover,\n.btn-success:active:focus,\n.btn-success.active:focus,\n.open > .dropdown-toggle.btn-success:focus,\n.btn-success:active.focus,\n.btn-success.active.focus,\n.open > .dropdown-toggle.btn-success.focus {\n color: #ffffff;\n background-color: #398439;\n border-color: #255625;\n}\n.btn-success:active,\n.btn-success.active,\n.open > .dropdown-toggle.btn-success {\n background-image: none;\n}\n.btn-success.disabled,\n.btn-success[disabled],\nfieldset[disabled] .btn-success,\n.btn-success.disabled:hover,\n.btn-success[disabled]:hover,\nfieldset[disabled] .btn-success:hover,\n.btn-success.disabled:focus,\n.btn-success[disabled]:focus,\nfieldset[disabled] .btn-success:focus,\n.btn-success.disabled.focus,\n.btn-success[disabled].focus,\nfieldset[disabled] .btn-success.focus,\n.btn-success.disabled:active,\n.btn-success[disabled]:active,\nfieldset[disabled] .btn-success:active,\n.btn-success.disabled.active,\n.btn-success[disabled].active,\nfieldset[disabled] .btn-success.active {\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #ffffff;\n}\n.btn-info {\n color: #ffffff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n color: #ffffff;\n background-color: #31b0d5;\n border-color: #269abc;\n}\n.btn-info:active:hover,\n.btn-info.active:hover,\n.open > .dropdown-toggle.btn-info:hover,\n.btn-info:active:focus,\n.btn-info.active:focus,\n.open > .dropdown-toggle.btn-info:focus,\n.btn-info:active.focus,\n.btn-info.active.focus,\n.open > .dropdown-toggle.btn-info.focus {\n color: #ffffff;\n background-color: #269abc;\n border-color: #1b6d85;\n}\n.btn-info:active,\n.btn-info.active,\n.open > .dropdown-toggle.btn-info {\n background-image: none;\n}\n.btn-info.disabled,\n.btn-info[disabled],\nfieldset[disabled] .btn-info,\n.btn-info.disabled:hover,\n.btn-info[disabled]:hover,\nfieldset[disabled] .btn-info:hover,\n.btn-info.disabled:focus,\n.btn-info[disabled]:focus,\nfieldset[disabled] .btn-info:focus,\n.btn-info.disabled.focus,\n.btn-info[disabled].focus,\nfieldset[disabled] .btn-info.focus,\n.btn-info.disabled:active,\n.btn-info[disabled]:active,\nfieldset[disabled] .btn-info:active,\n.btn-info.disabled.active,\n.btn-info[disabled].active,\nfieldset[disabled] .btn-info.active {\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #ffffff;\n}\n.btn-warning {\n color: #ffffff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n color: #ffffff;\n background-color: #ec971f;\n border-color: #d58512;\n}\n.btn-warning:active:hover,\n.btn-warning.active:hover,\n.open > .dropdown-toggle.btn-warning:hover,\n.btn-warning:active:focus,\n.btn-warning.active:focus,\n.open > .dropdown-toggle.btn-warning:focus,\n.btn-warning:active.focus,\n.btn-warning.active.focus,\n.open > .dropdown-toggle.btn-warning.focus {\n color: #ffffff;\n background-color: #d58512;\n border-color: #985f0d;\n}\n.btn-warning:active,\n.btn-warning.active,\n.open > .dropdown-toggle.btn-warning {\n background-image: none;\n}\n.btn-warning.disabled,\n.btn-warning[disabled],\nfieldset[disabled] .btn-warning,\n.btn-warning.disabled:hover,\n.btn-warning[disabled]:hover,\nfieldset[disabled] .btn-warning:hover,\n.btn-warning.disabled:focus,\n.btn-warning[disabled]:focus,\nfieldset[disabled] .btn-warning:focus,\n.btn-warning.disabled.focus,\n.btn-warning[disabled].focus,\nfieldset[disabled] .btn-warning.focus,\n.btn-warning.disabled:active,\n.btn-warning[disabled]:active,\nfieldset[disabled] .btn-warning:active,\n.btn-warning.disabled.active,\n.btn-warning[disabled].active,\nfieldset[disabled] .btn-warning.active {\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #ffffff;\n}\n.btn-danger {\n color: #ffffff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n color: #ffffff;\n background-color: #c9302c;\n border-color: #ac2925;\n}\n.btn-danger:active:hover,\n.btn-danger.active:hover,\n.open > .dropdown-toggle.btn-danger:hover,\n.btn-danger:active:focus,\n.btn-danger.active:focus,\n.open > .dropdown-toggle.btn-danger:focus,\n.btn-danger:active.focus,\n.btn-danger.active.focus,\n.open > .dropdown-toggle.btn-danger.focus {\n color: #ffffff;\n background-color: #ac2925;\n border-color: #761c19;\n}\n.btn-danger:active,\n.btn-danger.active,\n.open > .dropdown-toggle.btn-danger {\n background-image: none;\n}\n.btn-danger.disabled,\n.btn-danger[disabled],\nfieldset[disabled] .btn-danger,\n.btn-danger.disabled:hover,\n.btn-danger[disabled]:hover,\nfieldset[disabled] .btn-danger:hover,\n.btn-danger.disabled:focus,\n.btn-danger[disabled]:focus,\nfieldset[disabled] .btn-danger:focus,\n.btn-danger.disabled.focus,\n.btn-danger[disabled].focus,\nfieldset[disabled] .btn-danger.focus,\n.btn-danger.disabled:active,\n.btn-danger[disabled]:active,\nfieldset[disabled] .btn-danger:active,\n.btn-danger.disabled.active,\n.btn-danger[disabled].active,\nfieldset[disabled] .btn-danger.active {\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #ffffff;\n}\n.btn-link {\n color: #337ab7;\n font-weight: normal;\n border-radius: 0;\n}\n.btn-link,\n.btn-link:active,\n.btn-link.active,\n.btn-link[disabled],\nfieldset[disabled] .btn-link {\n background-color: transparent;\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-link,\n.btn-link:hover,\n.btn-link:focus,\n.btn-link:active {\n border-color: transparent;\n}\n.btn-link:hover,\n.btn-link:focus {\n color: #23527c;\n text-decoration: underline;\n background-color: transparent;\n}\n.btn-link[disabled]:hover,\nfieldset[disabled] .btn-link:hover,\n.btn-link[disabled]:focus,\nfieldset[disabled] .btn-link:focus {\n color: #777777;\n text-decoration: none;\n}\n.btn-lg,\n.btn-group-lg > .btn {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\n.btn-sm,\n.btn-group-sm > .btn {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-xs,\n.btn-group-xs > .btn {\n padding: 1px 5px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\n.btn-block {\n display: block;\n width: 100%;\n}\n.btn-block + .btn-block {\n margin-top: 5px;\n}\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n.fade {\n opacity: 0;\n -webkit-transition: opacity 0.15s linear;\n -o-transition: opacity 0.15s linear;\n transition: opacity 0.15s linear;\n}\n.fade.in {\n opacity: 1;\n}\n.collapse {\n display: none;\n}\n.collapse.in {\n display: block;\n}\ntr.collapse.in {\n display: table-row;\n}\ntbody.collapse.in {\n display: table-row-group;\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n -webkit-transition-property: height, visibility;\n transition-property: height, visibility;\n -webkit-transition-duration: 0.35s;\n transition-duration: 0.35s;\n -webkit-transition-timing-function: ease;\n transition-timing-function: ease;\n}\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: 4px dashed;\n border-top: 4px solid \\9;\n border-right: 4px solid transparent;\n border-left: 4px solid transparent;\n}\n.dropup,\n.dropdown {\n position: relative;\n}\n.dropdown-toggle:focus {\n outline: 0;\n}\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0;\n list-style: none;\n font-size: 14px;\n text-align: left;\n background-color: #ffffff;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 4px;\n -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\n background-clip: padding-box;\n}\n.dropdown-menu.pull-right {\n right: 0;\n left: auto;\n}\n.dropdown-menu .divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.dropdown-menu > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: 1.42857143;\n color: #333333;\n white-space: nowrap;\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n text-decoration: none;\n color: #262626;\n background-color: #f5f5f5;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n color: #ffffff;\n text-decoration: none;\n outline: 0;\n background-color: #337ab7;\n}\n.dropdown-menu > .disabled > a,\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n color: #777777;\n}\n.dropdown-menu > .disabled > a:hover,\n.dropdown-menu > .disabled > a:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none;\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n cursor: not-allowed;\n}\n.open > .dropdown-menu {\n display: block;\n}\n.open > a {\n outline: 0;\n}\n.dropdown-menu-right {\n left: auto;\n right: 0;\n}\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: 12px;\n line-height: 1.42857143;\n color: #777777;\n white-space: nowrap;\n}\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: 990;\n}\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n.dropup .caret,\n.navbar-fixed-bottom .dropdown .caret {\n border-top: 0;\n border-bottom: 4px dashed;\n border-bottom: 4px solid \\9;\n content: \"\";\n}\n.dropup .dropdown-menu,\n.navbar-fixed-bottom .dropdown .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n}\n@media (min-width: 768px) {\n .navbar-right .dropdown-menu {\n left: auto;\n right: 0;\n }\n .navbar-right .dropdown-menu-left {\n left: 0;\n right: auto;\n }\n}\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n float: left;\n}\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group-vertical > .btn:focus,\n.btn-group > .btn:active,\n.btn-group-vertical > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn.active {\n z-index: 2;\n}\n.btn-group .btn + .btn,\n.btn-group .btn + .btn-group,\n.btn-group .btn-group + .btn,\n.btn-group .btn-group + .btn-group {\n margin-left: -1px;\n}\n.btn-toolbar {\n margin-left: -5px;\n}\n.btn-toolbar .btn,\n.btn-toolbar .btn-group,\n.btn-toolbar .input-group {\n float: left;\n}\n.btn-toolbar > .btn,\n.btn-toolbar > .btn-group,\n.btn-toolbar > .input-group {\n margin-left: 5px;\n}\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n.btn-group > .btn:first-child {\n margin-left: 0;\n}\n.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n.btn-group.open .dropdown-toggle {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-group.open .dropdown-toggle.btn-link {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn .caret {\n margin-left: 0;\n}\n.btn-lg .caret {\n border-width: 5px 5px 0;\n border-bottom-width: 0;\n}\n.dropup .btn-lg .caret {\n border-width: 0 5px 5px;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group,\n.btn-group-vertical > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n.btn-group-vertical > .btn-group > .btn {\n float: none;\n}\n.btn-group-vertical > .btn + .btn,\n.btn-group-vertical > .btn + .btn-group,\n.btn-group-vertical > .btn-group + .btn,\n.btn-group-vertical > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n}\n.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.btn-group-vertical > .btn:first-child:not(:last-child) {\n border-top-right-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn:last-child:not(:first-child) {\n border-bottom-left-radius: 4px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\n.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n}\n.btn-group-justified > .btn,\n.btn-group-justified > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n}\n.btn-group-justified > .btn-group .btn {\n width: 100%;\n}\n.btn-group-justified > .btn-group .dropdown-menu {\n left: auto;\n}\n[data-toggle=\"buttons\"] > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn input[type=\"checkbox\"],\n[data-toggle=\"buttons\"] > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.input-group {\n position: relative;\n display: table;\n border-collapse: separate;\n}\n.input-group[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n}\n.input-group .form-control {\n position: relative;\n z-index: 2;\n float: left;\n width: 100%;\n margin-bottom: 0;\n}\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n border-radius: 6px;\n}\nselect.input-group-lg > .form-control,\nselect.input-group-lg > .input-group-addon,\nselect.input-group-lg > .input-group-btn > .btn {\n height: 46px;\n line-height: 46px;\n}\ntextarea.input-group-lg > .form-control,\ntextarea.input-group-lg > .input-group-addon,\ntextarea.input-group-lg > .input-group-btn > .btn,\nselect[multiple].input-group-lg > .form-control,\nselect[multiple].input-group-lg > .input-group-addon,\nselect[multiple].input-group-lg > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n border-radius: 3px;\n}\nselect.input-group-sm > .form-control,\nselect.input-group-sm > .input-group-addon,\nselect.input-group-sm > .input-group-btn > .btn {\n height: 30px;\n line-height: 30px;\n}\ntextarea.input-group-sm > .form-control,\ntextarea.input-group-sm > .input-group-addon,\ntextarea.input-group-sm > .input-group-btn > .btn,\nselect[multiple].input-group-sm > .form-control,\nselect[multiple].input-group-sm > .input-group-addon,\nselect[multiple].input-group-sm > .input-group-btn > .btn {\n height: auto;\n}\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n}\n.input-group-addon:not(:first-child):not(:last-child),\n.input-group-btn:not(:first-child):not(:last-child),\n.input-group .form-control:not(:first-child):not(:last-child) {\n border-radius: 0;\n}\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle;\n}\n.input-group-addon {\n padding: 6px 12px;\n font-size: 14px;\n font-weight: normal;\n line-height: 1;\n color: #555555;\n text-align: center;\n background-color: #eeeeee;\n border: 1px solid #cccccc;\n border-radius: 4px;\n}\n.input-group-addon.input-sm {\n padding: 5px 10px;\n font-size: 12px;\n border-radius: 3px;\n}\n.input-group-addon.input-lg {\n padding: 10px 16px;\n font-size: 18px;\n border-radius: 6px;\n}\n.input-group-addon input[type=\"radio\"],\n.input-group-addon input[type=\"checkbox\"] {\n margin-top: 0;\n}\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-top-right-radius: 0;\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n border-bottom-left-radius: 0;\n border-top-left-radius: 0;\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n.input-group-btn {\n position: relative;\n font-size: 0;\n white-space: nowrap;\n}\n.input-group-btn > .btn {\n position: relative;\n}\n.input-group-btn > .btn + .btn {\n margin-left: -1px;\n}\n.input-group-btn > .btn:hover,\n.input-group-btn > .btn:focus,\n.input-group-btn > .btn:active {\n z-index: 2;\n}\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group {\n margin-right: -1px;\n}\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group {\n z-index: 2;\n margin-left: -1px;\n}\n.nav {\n margin-bottom: 0;\n padding-left: 0;\n list-style: none;\n}\n.nav > li {\n position: relative;\n display: block;\n}\n.nav > li > a {\n position: relative;\n display: block;\n padding: 10px 15px;\n}\n.nav > li > a:hover,\n.nav > li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.nav > li.disabled > a {\n color: #777777;\n}\n.nav > li.disabled > a:hover,\n.nav > li.disabled > a:focus {\n color: #777777;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n}\n.nav .open > a,\n.nav .open > a:hover,\n.nav .open > a:focus {\n background-color: #eeeeee;\n border-color: #337ab7;\n}\n.nav .nav-divider {\n height: 1px;\n margin: 9px 0;\n overflow: hidden;\n background-color: #e5e5e5;\n}\n.nav > li > a > img {\n max-width: none;\n}\n.nav-tabs {\n border-bottom: 1px solid #dddddd;\n}\n.nav-tabs > li {\n float: left;\n margin-bottom: -1px;\n}\n.nav-tabs > li > a {\n margin-right: 2px;\n line-height: 1.42857143;\n border: 1px solid transparent;\n border-radius: 4px 4px 0 0;\n}\n.nav-tabs > li > a:hover {\n border-color: #eeeeee #eeeeee #dddddd;\n}\n.nav-tabs > li.active > a,\n.nav-tabs > li.active > a:hover,\n.nav-tabs > li.active > a:focus {\n color: #555555;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-bottom-color: transparent;\n cursor: default;\n}\n.nav-tabs.nav-justified {\n width: 100%;\n border-bottom: 0;\n}\n.nav-tabs.nav-justified > li {\n float: none;\n}\n.nav-tabs.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-tabs.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-tabs.nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs.nav-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs.nav-justified > .active > a,\n.nav-tabs.nav-justified > .active > a:hover,\n.nav-tabs.nav-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs.nav-justified > .active > a,\n .nav-tabs.nav-justified > .active > a:hover,\n .nav-tabs.nav-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.nav-pills > li {\n float: left;\n}\n.nav-pills > li > a {\n border-radius: 4px;\n}\n.nav-pills > li + li {\n margin-left: 2px;\n}\n.nav-pills > li.active > a,\n.nav-pills > li.active > a:hover,\n.nav-pills > li.active > a:focus {\n color: #ffffff;\n background-color: #337ab7;\n}\n.nav-stacked > li {\n float: none;\n}\n.nav-stacked > li + li {\n margin-top: 2px;\n margin-left: 0;\n}\n.nav-justified {\n width: 100%;\n}\n.nav-justified > li {\n float: none;\n}\n.nav-justified > li > a {\n text-align: center;\n margin-bottom: 5px;\n}\n.nav-justified > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n}\n@media (min-width: 768px) {\n .nav-justified > li {\n display: table-cell;\n width: 1%;\n }\n .nav-justified > li > a {\n margin-bottom: 0;\n }\n}\n.nav-tabs-justified {\n border-bottom: 0;\n}\n.nav-tabs-justified > li > a {\n margin-right: 0;\n border-radius: 4px;\n}\n.nav-tabs-justified > .active > a,\n.nav-tabs-justified > .active > a:hover,\n.nav-tabs-justified > .active > a:focus {\n border: 1px solid #dddddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #dddddd;\n border-radius: 4px 4px 0 0;\n }\n .nav-tabs-justified > .active > a,\n .nav-tabs-justified > .active > a:hover,\n .nav-tabs-justified > .active > a:focus {\n border-bottom-color: #ffffff;\n }\n}\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar {\n position: relative;\n min-height: 50px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n}\n@media (min-width: 768px) {\n .navbar {\n border-radius: 4px;\n }\n}\n@media (min-width: 768px) {\n .navbar-header {\n float: left;\n }\n}\n.navbar-collapse {\n overflow-x: visible;\n padding-right: 15px;\n padding-left: 15px;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\n -webkit-overflow-scrolling: touch;\n}\n.navbar-collapse.in {\n overflow-y: auto;\n}\n@media (min-width: 768px) {\n .navbar-collapse {\n width: auto;\n border-top: 0;\n box-shadow: none;\n }\n .navbar-collapse.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0;\n overflow: visible !important;\n }\n .navbar-collapse.in {\n overflow-y: visible;\n }\n .navbar-fixed-top .navbar-collapse,\n .navbar-static-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n padding-left: 0;\n padding-right: 0;\n }\n}\n.navbar-fixed-top .navbar-collapse,\n.navbar-fixed-bottom .navbar-collapse {\n max-height: 340px;\n}\n@media (max-device-width: 480px) and (orientation: landscape) {\n .navbar-fixed-top .navbar-collapse,\n .navbar-fixed-bottom .navbar-collapse {\n max-height: 200px;\n }\n}\n.container > .navbar-header,\n.container-fluid > .navbar-header,\n.container > .navbar-collapse,\n.container-fluid > .navbar-collapse {\n margin-right: -15px;\n margin-left: -15px;\n}\n@media (min-width: 768px) {\n .container > .navbar-header,\n .container-fluid > .navbar-header,\n .container > .navbar-collapse,\n .container-fluid > .navbar-collapse {\n margin-right: 0;\n margin-left: 0;\n }\n}\n.navbar-static-top {\n z-index: 1000;\n border-width: 0 0 1px;\n}\n@media (min-width: 768px) {\n .navbar-static-top {\n border-radius: 0;\n }\n}\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n@media (min-width: 768px) {\n .navbar-fixed-top,\n .navbar-fixed-bottom {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0;\n border-width: 1px 0 0;\n}\n.navbar-brand {\n float: left;\n padding: 15px 15px;\n font-size: 18px;\n line-height: 20px;\n height: 50px;\n}\n.navbar-brand:hover,\n.navbar-brand:focus {\n text-decoration: none;\n}\n.navbar-brand > img {\n display: block;\n}\n@media (min-width: 768px) {\n .navbar > .container .navbar-brand,\n .navbar > .container-fluid .navbar-brand {\n margin-left: -15px;\n }\n}\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: 15px;\n padding: 9px 10px;\n margin-top: 8px;\n margin-bottom: 8px;\n background-color: transparent;\n background-image: none;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.navbar-toggle:focus {\n outline: 0;\n}\n.navbar-toggle .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n}\n.navbar-toggle .icon-bar + .icon-bar {\n margin-top: 4px;\n}\n@media (min-width: 768px) {\n .navbar-toggle {\n display: none;\n }\n}\n.navbar-nav {\n margin: 7.5px -15px;\n}\n.navbar-nav > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: 20px;\n}\n@media (max-width: 767px) {\n .navbar-nav .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n }\n .navbar-nav .open .dropdown-menu > li > a,\n .navbar-nav .open .dropdown-menu .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n .navbar-nav .open .dropdown-menu > li > a {\n line-height: 20px;\n }\n .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-nav .open .dropdown-menu > li > a:focus {\n background-image: none;\n }\n}\n@media (min-width: 768px) {\n .navbar-nav {\n float: left;\n margin: 0;\n }\n .navbar-nav > li {\n float: left;\n }\n .navbar-nav > li > a {\n padding-top: 15px;\n padding-bottom: 15px;\n }\n}\n.navbar-form {\n margin-left: -15px;\n margin-right: -15px;\n padding: 10px 15px;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\n margin-top: 8px;\n margin-bottom: 8px;\n}\n@media (min-width: 768px) {\n .navbar-form .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .navbar-form .form-control-static {\n display: inline-block;\n }\n .navbar-form .input-group {\n display: inline-table;\n vertical-align: middle;\n }\n .navbar-form .input-group .input-group-addon,\n .navbar-form .input-group .input-group-btn,\n .navbar-form .input-group .form-control {\n width: auto;\n }\n .navbar-form .input-group > .form-control {\n width: 100%;\n }\n .navbar-form .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio,\n .navbar-form .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n vertical-align: middle;\n }\n .navbar-form .radio label,\n .navbar-form .checkbox label {\n padding-left: 0;\n }\n .navbar-form .radio input[type=\"radio\"],\n .navbar-form .checkbox input[type=\"checkbox\"] {\n position: relative;\n margin-left: 0;\n }\n .navbar-form .has-feedback .form-control-feedback {\n top: 0;\n }\n}\n@media (max-width: 767px) {\n .navbar-form .form-group {\n margin-bottom: 5px;\n }\n .navbar-form .form-group:last-child {\n margin-bottom: 0;\n }\n}\n@media (min-width: 768px) {\n .navbar-form {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n -webkit-box-shadow: none;\n box-shadow: none;\n }\n}\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.navbar-btn {\n margin-top: 8px;\n margin-bottom: 8px;\n}\n.navbar-btn.btn-sm {\n margin-top: 10px;\n margin-bottom: 10px;\n}\n.navbar-btn.btn-xs {\n margin-top: 14px;\n margin-bottom: 14px;\n}\n.navbar-text {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n@media (min-width: 768px) {\n .navbar-text {\n float: left;\n margin-left: 15px;\n margin-right: 15px;\n }\n}\n@media (min-width: 768px) {\n .navbar-left {\n float: left !important;\n }\n .navbar-right {\n float: right !important;\n margin-right: -15px;\n }\n .navbar-right ~ .navbar-right {\n margin-right: 0;\n }\n}\n.navbar-default {\n background-color: #f8f8f8;\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-brand {\n color: #777777;\n}\n.navbar-default .navbar-brand:hover,\n.navbar-default .navbar-brand:focus {\n color: #5e5e5e;\n background-color: transparent;\n}\n.navbar-default .navbar-text {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333333;\n background-color: transparent;\n}\n.navbar-default .navbar-nav > .active > a,\n.navbar-default .navbar-nav > .active > a:hover,\n.navbar-default .navbar-nav > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .disabled > a,\n.navbar-default .navbar-nav > .disabled > a:hover,\n.navbar-default .navbar-nav > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #dddddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #dddddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888888;\n}\n.navbar-default .navbar-collapse,\n.navbar-default .navbar-form {\n border-color: #e7e7e7;\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .open > a:hover,\n.navbar-default .navbar-nav > .open > a:focus {\n background-color: #e7e7e7;\n color: #555555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777777;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #333333;\n background-color: transparent;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #555555;\n background-color: #e7e7e7;\n }\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #cccccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777777;\n}\n.navbar-default .navbar-link:hover {\n color: #333333;\n}\n.navbar-default .btn-link {\n color: #777777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333333;\n}\n.navbar-default .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-default .btn-link:hover,\n.navbar-default .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-default .btn-link:focus {\n color: #cccccc;\n}\n.navbar-inverse {\n background-color: #222222;\n border-color: #080808;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-text {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-nav > li > a:hover,\n.navbar-inverse .navbar-nav > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n}\n.navbar-inverse .navbar-nav > .active > a,\n.navbar-inverse .navbar-nav > .active > a:hover,\n.navbar-inverse .navbar-nav > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n}\n.navbar-inverse .navbar-nav > .disabled > a,\n.navbar-inverse .navbar-nav > .disabled > a:hover,\n.navbar-inverse .navbar-nav > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #ffffff;\n}\n.navbar-inverse .navbar-collapse,\n.navbar-inverse .navbar-form {\n border-color: #101010;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .open > a:hover,\n.navbar-inverse .navbar-nav > .open > a:focus {\n background-color: #080808;\n color: #ffffff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\n color: #9d9d9d;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\n color: #ffffff;\n background-color: transparent;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #ffffff;\n background-color: #080808;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\n .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\n color: #444444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #ffffff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #ffffff;\n}\n.navbar-inverse .btn-link[disabled]:hover,\nfieldset[disabled] .navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link[disabled]:focus,\nfieldset[disabled] .navbar-inverse .btn-link:focus {\n color: #444444;\n}\n.breadcrumb {\n padding: 8px 15px;\n margin-bottom: 20px;\n list-style: none;\n background-color: #f5f5f5;\n border-radius: 4px;\n}\n.breadcrumb > li {\n display: inline-block;\n}\n.breadcrumb > li + li:before {\n content: \"/\\00a0\";\n padding: 0 5px;\n color: #cccccc;\n}\n.breadcrumb > .active {\n color: #777777;\n}\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: 20px 0;\n border-radius: 4px;\n}\n.pagination > li {\n display: inline;\n}\n.pagination > li > a,\n.pagination > li > span {\n position: relative;\n float: left;\n padding: 6px 12px;\n line-height: 1.42857143;\n text-decoration: none;\n color: #337ab7;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n margin-left: -1px;\n}\n.pagination > li:first-child > a,\n.pagination > li:first-child > span {\n margin-left: 0;\n border-bottom-left-radius: 4px;\n border-top-left-radius: 4px;\n}\n.pagination > li:last-child > a,\n.pagination > li:last-child > span {\n border-bottom-right-radius: 4px;\n border-top-right-radius: 4px;\n}\n.pagination > li > a:hover,\n.pagination > li > span:hover,\n.pagination > li > a:focus,\n.pagination > li > span:focus {\n z-index: 3;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #dddddd;\n}\n.pagination > .active > a,\n.pagination > .active > span,\n.pagination > .active > a:hover,\n.pagination > .active > span:hover,\n.pagination > .active > a:focus,\n.pagination > .active > span:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n cursor: default;\n}\n.pagination > .disabled > span,\n.pagination > .disabled > span:hover,\n.pagination > .disabled > span:focus,\n.pagination > .disabled > a,\n.pagination > .disabled > a:hover,\n.pagination > .disabled > a:focus {\n color: #777777;\n background-color: #ffffff;\n border-color: #dddddd;\n cursor: not-allowed;\n}\n.pagination-lg > li > a,\n.pagination-lg > li > span {\n padding: 10px 16px;\n font-size: 18px;\n line-height: 1.3333333;\n}\n.pagination-lg > li:first-child > a,\n.pagination-lg > li:first-child > span {\n border-bottom-left-radius: 6px;\n border-top-left-radius: 6px;\n}\n.pagination-lg > li:last-child > a,\n.pagination-lg > li:last-child > span {\n border-bottom-right-radius: 6px;\n border-top-right-radius: 6px;\n}\n.pagination-sm > li > a,\n.pagination-sm > li > span {\n padding: 5px 10px;\n font-size: 12px;\n line-height: 1.5;\n}\n.pagination-sm > li:first-child > a,\n.pagination-sm > li:first-child > span {\n border-bottom-left-radius: 3px;\n border-top-left-radius: 3px;\n}\n.pagination-sm > li:last-child > a,\n.pagination-sm > li:last-child > span {\n border-bottom-right-radius: 3px;\n border-top-right-radius: 3px;\n}\n.pager {\n padding-left: 0;\n margin: 20px 0;\n list-style: none;\n text-align: center;\n}\n.pager li {\n display: inline;\n}\n.pager li > a,\n.pager li > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 15px;\n}\n.pager li > a:hover,\n.pager li > a:focus {\n text-decoration: none;\n background-color: #eeeeee;\n}\n.pager .next > a,\n.pager .next > span {\n float: right;\n}\n.pager .previous > a,\n.pager .previous > span {\n float: left;\n}\n.pager .disabled > a,\n.pager .disabled > a:hover,\n.pager .disabled > a:focus,\n.pager .disabled > span {\n color: #777777;\n background-color: #ffffff;\n cursor: not-allowed;\n}\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: #ffffff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n}\na.label:hover,\na.label:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.label:empty {\n display: none;\n}\n.btn .label {\n position: relative;\n top: -1px;\n}\n.label-default {\n background-color: #777777;\n}\n.label-default[href]:hover,\n.label-default[href]:focus {\n background-color: #5e5e5e;\n}\n.label-primary {\n background-color: #337ab7;\n}\n.label-primary[href]:hover,\n.label-primary[href]:focus {\n background-color: #286090;\n}\n.label-success {\n background-color: #5cb85c;\n}\n.label-success[href]:hover,\n.label-success[href]:focus {\n background-color: #449d44;\n}\n.label-info {\n background-color: #5bc0de;\n}\n.label-info[href]:hover,\n.label-info[href]:focus {\n background-color: #31b0d5;\n}\n.label-warning {\n background-color: #f0ad4e;\n}\n.label-warning[href]:hover,\n.label-warning[href]:focus {\n background-color: #ec971f;\n}\n.label-danger {\n background-color: #d9534f;\n}\n.label-danger[href]:hover,\n.label-danger[href]:focus {\n background-color: #c9302c;\n}\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: 12px;\n font-weight: bold;\n color: #ffffff;\n line-height: 1;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: #777777;\n border-radius: 10px;\n}\n.badge:empty {\n display: none;\n}\n.btn .badge {\n position: relative;\n top: -1px;\n}\n.btn-xs .badge,\n.btn-group-xs > .btn .badge {\n top: 0;\n padding: 1px 5px;\n}\na.badge:hover,\na.badge:focus {\n color: #ffffff;\n text-decoration: none;\n cursor: pointer;\n}\n.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.list-group-item > .badge {\n float: right;\n}\n.list-group-item > .badge + .badge {\n margin-right: 5px;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n.jumbotron {\n padding-top: 30px;\n padding-bottom: 30px;\n margin-bottom: 30px;\n color: inherit;\n background-color: #eeeeee;\n}\n.jumbotron h1,\n.jumbotron .h1 {\n color: inherit;\n}\n.jumbotron p {\n margin-bottom: 15px;\n font-size: 21px;\n font-weight: 200;\n}\n.jumbotron > hr {\n border-top-color: #d5d5d5;\n}\n.container .jumbotron,\n.container-fluid .jumbotron {\n border-radius: 6px;\n}\n.jumbotron .container {\n max-width: 100%;\n}\n@media screen and (min-width: 768px) {\n .jumbotron {\n padding-top: 48px;\n padding-bottom: 48px;\n }\n .container .jumbotron,\n .container-fluid .jumbotron {\n padding-left: 60px;\n padding-right: 60px;\n }\n .jumbotron h1,\n .jumbotron .h1 {\n font-size: 63px;\n }\n}\n.thumbnail {\n display: block;\n padding: 4px;\n margin-bottom: 20px;\n line-height: 1.42857143;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n border-radius: 4px;\n -webkit-transition: border 0.2s ease-in-out;\n -o-transition: border 0.2s ease-in-out;\n transition: border 0.2s ease-in-out;\n}\n.thumbnail > img,\n.thumbnail a > img {\n margin-left: auto;\n margin-right: auto;\n}\na.thumbnail:hover,\na.thumbnail:focus,\na.thumbnail.active {\n border-color: #337ab7;\n}\n.thumbnail .caption {\n padding: 9px;\n color: #333333;\n}\n.alert {\n padding: 15px;\n margin-bottom: 20px;\n border: 1px solid transparent;\n border-radius: 4px;\n}\n.alert h4 {\n margin-top: 0;\n color: inherit;\n}\n.alert .alert-link {\n font-weight: bold;\n}\n.alert > p,\n.alert > ul {\n margin-bottom: 0;\n}\n.alert > p + p {\n margin-top: 5px;\n}\n.alert-dismissable,\n.alert-dismissible {\n padding-right: 35px;\n}\n.alert-dismissable .close,\n.alert-dismissible .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n}\n.alert-success {\n background-color: #dff0d8;\n border-color: #d6e9c6;\n color: #3c763d;\n}\n.alert-success hr {\n border-top-color: #c9e2b3;\n}\n.alert-success .alert-link {\n color: #2b542c;\n}\n.alert-info {\n background-color: #d9edf7;\n border-color: #bce8f1;\n color: #31708f;\n}\n.alert-info hr {\n border-top-color: #a6e1ec;\n}\n.alert-info .alert-link {\n color: #245269;\n}\n.alert-warning {\n background-color: #fcf8e3;\n border-color: #faebcc;\n color: #8a6d3b;\n}\n.alert-warning hr {\n border-top-color: #f7e1b5;\n}\n.alert-warning .alert-link {\n color: #66512c;\n}\n.alert-danger {\n background-color: #f2dede;\n border-color: #ebccd1;\n color: #a94442;\n}\n.alert-danger hr {\n border-top-color: #e4b9c0;\n}\n.alert-danger .alert-link {\n color: #843534;\n}\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n@keyframes progress-bar-stripes {\n from {\n background-position: 40px 0;\n }\n to {\n background-position: 0 0;\n }\n}\n.progress {\n overflow: hidden;\n height: 20px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\n}\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: 12px;\n line-height: 20px;\n color: #ffffff;\n text-align: center;\n background-color: #337ab7;\n -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\n -webkit-transition: width 0.6s ease;\n -o-transition: width 0.6s ease;\n transition: width 0.6s ease;\n}\n.progress-striped .progress-bar,\n.progress-bar-striped {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n background-size: 40px 40px;\n}\n.progress.active .progress-bar,\n.progress-bar.active {\n -webkit-animation: progress-bar-stripes 2s linear infinite;\n -o-animation: progress-bar-stripes 2s linear infinite;\n animation: progress-bar-stripes 2s linear infinite;\n}\n.progress-bar-success {\n background-color: #5cb85c;\n}\n.progress-striped .progress-bar-success {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.progress-bar-info {\n background-color: #5bc0de;\n}\n.progress-striped .progress-bar-info {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.progress-bar-warning {\n background-color: #f0ad4e;\n}\n.progress-striped .progress-bar-warning {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.progress-bar-danger {\n background-color: #d9534f;\n}\n.progress-striped .progress-bar-danger {\n background-image: -webkit-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);\n background-image: -o-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);\n 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);\n}\n.media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n.media-body {\n width: 10000px;\n}\n.media-object {\n display: block;\n}\n.media-object.img-thumbnail {\n max-width: none;\n}\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n.media-middle {\n vertical-align: middle;\n}\n.media-bottom {\n vertical-align: bottom;\n}\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n.list-group {\n margin-bottom: 20px;\n padding-left: 0;\n}\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n margin-bottom: -1px;\n background-color: #ffffff;\n border: 1px solid #dddddd;\n}\n.list-group-item:first-child {\n border-top-right-radius: 4px;\n border-top-left-radius: 4px;\n}\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\n}\na.list-group-item,\nbutton.list-group-item {\n color: #555555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333333;\n}\na.list-group-item:hover,\nbutton.list-group-item:hover,\na.list-group-item:focus,\nbutton.list-group-item:focus {\n text-decoration: none;\n color: #555555;\n background-color: #f5f5f5;\n}\nbutton.list-group-item {\n width: 100%;\n text-align: left;\n}\n.list-group-item.disabled,\n.list-group-item.disabled:hover,\n.list-group-item.disabled:focus {\n background-color: #eeeeee;\n color: #777777;\n cursor: not-allowed;\n}\n.list-group-item.disabled .list-group-item-heading,\n.list-group-item.disabled:hover .list-group-item-heading,\n.list-group-item.disabled:focus .list-group-item-heading {\n color: inherit;\n}\n.list-group-item.disabled .list-group-item-text,\n.list-group-item.disabled:hover .list-group-item-text,\n.list-group-item.disabled:focus .list-group-item-text {\n color: #777777;\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n z-index: 2;\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.list-group-item.active .list-group-item-heading,\n.list-group-item.active:hover .list-group-item-heading,\n.list-group-item.active:focus .list-group-item-heading,\n.list-group-item.active .list-group-item-heading > small,\n.list-group-item.active:hover .list-group-item-heading > small,\n.list-group-item.active:focus .list-group-item-heading > small,\n.list-group-item.active .list-group-item-heading > .small,\n.list-group-item.active:hover .list-group-item-heading > .small,\n.list-group-item.active:focus .list-group-item-heading > .small {\n color: inherit;\n}\n.list-group-item.active .list-group-item-text,\n.list-group-item.active:hover .list-group-item-text,\n.list-group-item.active:focus .list-group-item-text {\n color: #c7ddef;\n}\n.list-group-item-success {\n color: #3c763d;\n background-color: #dff0d8;\n}\na.list-group-item-success,\nbutton.list-group-item-success {\n color: #3c763d;\n}\na.list-group-item-success .list-group-item-heading,\nbutton.list-group-item-success .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-success:hover,\nbutton.list-group-item-success:hover,\na.list-group-item-success:focus,\nbutton.list-group-item-success:focus {\n color: #3c763d;\n background-color: #d0e9c6;\n}\na.list-group-item-success.active,\nbutton.list-group-item-success.active,\na.list-group-item-success.active:hover,\nbutton.list-group-item-success.active:hover,\na.list-group-item-success.active:focus,\nbutton.list-group-item-success.active:focus {\n color: #fff;\n background-color: #3c763d;\n border-color: #3c763d;\n}\n.list-group-item-info {\n color: #31708f;\n background-color: #d9edf7;\n}\na.list-group-item-info,\nbutton.list-group-item-info {\n color: #31708f;\n}\na.list-group-item-info .list-group-item-heading,\nbutton.list-group-item-info .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-info:hover,\nbutton.list-group-item-info:hover,\na.list-group-item-info:focus,\nbutton.list-group-item-info:focus {\n color: #31708f;\n background-color: #c4e3f3;\n}\na.list-group-item-info.active,\nbutton.list-group-item-info.active,\na.list-group-item-info.active:hover,\nbutton.list-group-item-info.active:hover,\na.list-group-item-info.active:focus,\nbutton.list-group-item-info.active:focus {\n color: #fff;\n background-color: #31708f;\n border-color: #31708f;\n}\n.list-group-item-warning {\n color: #8a6d3b;\n background-color: #fcf8e3;\n}\na.list-group-item-warning,\nbutton.list-group-item-warning {\n color: #8a6d3b;\n}\na.list-group-item-warning .list-group-item-heading,\nbutton.list-group-item-warning .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-warning:hover,\nbutton.list-group-item-warning:hover,\na.list-group-item-warning:focus,\nbutton.list-group-item-warning:focus {\n color: #8a6d3b;\n background-color: #faf2cc;\n}\na.list-group-item-warning.active,\nbutton.list-group-item-warning.active,\na.list-group-item-warning.active:hover,\nbutton.list-group-item-warning.active:hover,\na.list-group-item-warning.active:focus,\nbutton.list-group-item-warning.active:focus {\n color: #fff;\n background-color: #8a6d3b;\n border-color: #8a6d3b;\n}\n.list-group-item-danger {\n color: #a94442;\n background-color: #f2dede;\n}\na.list-group-item-danger,\nbutton.list-group-item-danger {\n color: #a94442;\n}\na.list-group-item-danger .list-group-item-heading,\nbutton.list-group-item-danger .list-group-item-heading {\n color: inherit;\n}\na.list-group-item-danger:hover,\nbutton.list-group-item-danger:hover,\na.list-group-item-danger:focus,\nbutton.list-group-item-danger:focus {\n color: #a94442;\n background-color: #ebcccc;\n}\na.list-group-item-danger.active,\nbutton.list-group-item-danger.active,\na.list-group-item-danger.active:hover,\nbutton.list-group-item-danger.active:hover,\na.list-group-item-danger.active:focus,\nbutton.list-group-item-danger.active:focus {\n color: #fff;\n background-color: #a94442;\n border-color: #a94442;\n}\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n.panel {\n margin-bottom: 20px;\n background-color: #ffffff;\n border: 1px solid transparent;\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.panel-body {\n padding: 15px;\n}\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel-heading > .dropdown .dropdown-toggle {\n color: inherit;\n}\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: 16px;\n color: inherit;\n}\n.panel-title > a,\n.panel-title > small,\n.panel-title > .small,\n.panel-title > small > a,\n.panel-title > .small > a {\n color: inherit;\n}\n.panel-footer {\n padding: 10px 15px;\n background-color: #f5f5f5;\n border-top: 1px solid #dddddd;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .list-group,\n.panel > .panel-collapse > .list-group {\n margin-bottom: 0;\n}\n.panel > .list-group .list-group-item,\n.panel > .panel-collapse > .list-group .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n}\n.panel > .list-group:first-child .list-group-item:first-child,\n.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {\n border-top: 0;\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .list-group:last-child .list-group-item:last-child,\n.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {\n border-bottom: 0;\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child {\n border-top-right-radius: 0;\n border-top-left-radius: 0;\n}\n.panel-heading + .list-group .list-group-item:first-child {\n border-top-width: 0;\n}\n.list-group + .panel-footer {\n border-top-width: 0;\n}\n.panel > .table,\n.panel > .table-responsive > .table,\n.panel > .panel-collapse > .table {\n margin-bottom: 0;\n}\n.panel > .table caption,\n.panel > .table-responsive > .table caption,\n.panel > .panel-collapse > .table caption {\n padding-left: 15px;\n padding-right: 15px;\n}\n.panel > .table:first-child,\n.panel > .table-responsive:first-child > .table:first-child {\n border-top-right-radius: 3px;\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {\n border-top-left-radius: 3px;\n border-top-right-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\n border-top-left-radius: 3px;\n}\n.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\n.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\n.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\n.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\n border-top-right-radius: 3px;\n}\n.panel > .table:last-child,\n.panel > .table-responsive:last-child > .table:last-child {\n border-bottom-right-radius: 3px;\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {\n border-bottom-left-radius: 3px;\n border-bottom-right-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\n border-bottom-left-radius: 3px;\n}\n.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\n.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\n.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\n.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\n border-bottom-right-radius: 3px;\n}\n.panel > .panel-body + .table,\n.panel > .panel-body + .table-responsive,\n.panel > .table + .panel-body,\n.panel > .table-responsive + .panel-body {\n border-top: 1px solid #dddddd;\n}\n.panel > .table > tbody:first-child > tr:first-child th,\n.panel > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n}\n.panel > .table-bordered,\n.panel > .table-responsive > .table-bordered {\n border: 0;\n}\n.panel > .table-bordered > thead > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\n.panel > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\n.panel > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\n.panel > .table-bordered > thead > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\n.panel > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\n.panel > .table-bordered > tfoot > tr > td:first-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\n border-left: 0;\n}\n.panel > .table-bordered > thead > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\n.panel > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\n.panel > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\n.panel > .table-bordered > thead > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\n.panel > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\n.panel > .table-bordered > tfoot > tr > td:last-child,\n.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\n border-right: 0;\n}\n.panel > .table-bordered > thead > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\n.panel > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\n.panel > .table-bordered > thead > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\n.panel > .table-bordered > tbody > tr:first-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\n border-bottom: 0;\n}\n.panel > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\n.panel > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\n.panel > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\n.panel > .table-bordered > tfoot > tr:last-child > th,\n.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\n border-bottom: 0;\n}\n.panel > .table-responsive {\n border: 0;\n margin-bottom: 0;\n}\n.panel-group {\n margin-bottom: 20px;\n}\n.panel-group .panel {\n margin-bottom: 0;\n border-radius: 4px;\n}\n.panel-group .panel + .panel {\n margin-top: 5px;\n}\n.panel-group .panel-heading {\n border-bottom: 0;\n}\n.panel-group .panel-heading + .panel-collapse > .panel-body,\n.panel-group .panel-heading + .panel-collapse > .list-group {\n border-top: 1px solid #dddddd;\n}\n.panel-group .panel-footer {\n border-top: 0;\n}\n.panel-group .panel-footer + .panel-collapse .panel-body {\n border-bottom: 1px solid #dddddd;\n}\n.panel-default {\n border-color: #dddddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #dddddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #dddddd;\n}\n.panel-default > .panel-heading .badge {\n color: #f5f5f5;\n background-color: #333333;\n}\n.panel-default > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #dddddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #ffffff;\n background-color: #337ab7;\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #337ab7;\n}\n.panel-primary > .panel-heading .badge {\n color: #337ab7;\n background-color: #ffffff;\n}\n.panel-primary > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #337ab7;\n}\n.panel-success {\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading {\n color: #3c763d;\n background-color: #dff0d8;\n border-color: #d6e9c6;\n}\n.panel-success > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #d6e9c6;\n}\n.panel-success > .panel-heading .badge {\n color: #dff0d8;\n background-color: #3c763d;\n}\n.panel-success > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #d6e9c6;\n}\n.panel-info {\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading {\n color: #31708f;\n background-color: #d9edf7;\n border-color: #bce8f1;\n}\n.panel-info > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #bce8f1;\n}\n.panel-info > .panel-heading .badge {\n color: #d9edf7;\n background-color: #31708f;\n}\n.panel-info > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #bce8f1;\n}\n.panel-warning {\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading {\n color: #8a6d3b;\n background-color: #fcf8e3;\n border-color: #faebcc;\n}\n.panel-warning > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #faebcc;\n}\n.panel-warning > .panel-heading .badge {\n color: #fcf8e3;\n background-color: #8a6d3b;\n}\n.panel-warning > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #faebcc;\n}\n.panel-danger {\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading {\n color: #a94442;\n background-color: #f2dede;\n border-color: #ebccd1;\n}\n.panel-danger > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ebccd1;\n}\n.panel-danger > .panel-heading .badge {\n color: #f2dede;\n background-color: #a94442;\n}\n.panel-danger > .panel-footer + .panel-collapse > .panel-body {\n border-bottom-color: #ebccd1;\n}\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n}\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n}\n.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n}\n.embed-responsive-4by3 {\n padding-bottom: 75%;\n}\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: #f5f5f5;\n border: 1px solid #e3e3e3;\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\n}\n.well blockquote {\n border-color: #ddd;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.well-lg {\n padding: 24px;\n border-radius: 6px;\n}\n.well-sm {\n padding: 9px;\n border-radius: 3px;\n}\n.close {\n float: right;\n font-size: 21px;\n font-weight: bold;\n line-height: 1;\n color: #000000;\n text-shadow: 0 1px 0 #ffffff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000000;\n text-decoration: none;\n cursor: pointer;\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\nbutton.close {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n}\n.modal-open {\n overflow: hidden;\n}\n.modal {\n display: none;\n overflow: hidden;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1050;\n -webkit-overflow-scrolling: touch;\n outline: 0;\n}\n.modal.fade .modal-dialog {\n -webkit-transform: translate(0, -25%);\n -ms-transform: translate(0, -25%);\n -o-transform: translate(0, -25%);\n transform: translate(0, -25%);\n -webkit-transition: -webkit-transform 0.3s ease-out;\n -moz-transition: -moz-transform 0.3s ease-out;\n -o-transition: -o-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n}\n.modal.in .modal-dialog {\n -webkit-transform: translate(0, 0);\n -ms-transform: translate(0, 0);\n -o-transform: translate(0, 0);\n transform: translate(0, 0);\n}\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n.modal-content {\n position: relative;\n background-color: #ffffff;\n border: 1px solid #999999;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\n background-clip: padding-box;\n outline: 0;\n}\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1040;\n background-color: #000000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.modal-backdrop.in {\n opacity: 0.5;\n filter: alpha(opacity=50);\n}\n.modal-header {\n padding: 15px;\n border-bottom: 1px solid #e5e5e5;\n min-height: 16.42857143px;\n}\n.modal-header .close {\n margin-top: -2px;\n}\n.modal-title {\n margin: 0;\n line-height: 1.42857143;\n}\n.modal-body {\n position: relative;\n padding: 15px;\n}\n.modal-footer {\n padding: 15px;\n text-align: right;\n border-top: 1px solid #e5e5e5;\n}\n.modal-footer .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0;\n}\n.modal-footer .btn-group .btn + .btn {\n margin-left: -1px;\n}\n.modal-footer .btn-block + .btn-block {\n margin-left: 0;\n}\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n@media (min-width: 768px) {\n .modal-dialog {\n width: 600px;\n margin: 30px auto;\n }\n .modal-content {\n -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\n }\n .modal-sm {\n width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg {\n width: 900px;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 12px;\n opacity: 0;\n filter: alpha(opacity=0);\n}\n.tooltip.in {\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.tooltip.top {\n margin-top: -3px;\n padding: 5px 0;\n}\n.tooltip.right {\n margin-left: 3px;\n padding: 0 5px;\n}\n.tooltip.bottom {\n margin-top: 3px;\n padding: 5px 0;\n}\n.tooltip.left {\n margin-left: -3px;\n padding: 0 5px;\n}\n.tooltip-inner {\n max-width: 200px;\n padding: 3px 8px;\n color: #ffffff;\n text-align: center;\n background-color: #000000;\n border-radius: 4px;\n}\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-left .tooltip-arrow {\n bottom: 0;\n right: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.top-right .tooltip-arrow {\n bottom: 0;\n left: 5px;\n margin-bottom: -5px;\n border-width: 5px 5px 0;\n border-top-color: #000000;\n}\n.tooltip.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -5px;\n border-width: 5px 5px 5px 0;\n border-right-color: #000000;\n}\n.tooltip.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -5px;\n border-width: 5px 0 5px 5px;\n border-left-color: #000000;\n}\n.tooltip.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-left .tooltip-arrow {\n top: 0;\n right: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.tooltip.bottom-right .tooltip-arrow {\n top: 0;\n left: 5px;\n margin-top: -5px;\n border-width: 0 5px 5px;\n border-bottom-color: #000000;\n}\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: none;\n max-width: 276px;\n padding: 1px;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-style: normal;\n font-weight: normal;\n letter-spacing: normal;\n line-break: auto;\n line-height: 1.42857143;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n white-space: normal;\n word-break: normal;\n word-spacing: normal;\n word-wrap: normal;\n font-size: 14px;\n background-color: #ffffff;\n background-clip: padding-box;\n border: 1px solid #cccccc;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 6px;\n -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\n}\n.popover.top {\n margin-top: -10px;\n}\n.popover.right {\n margin-left: 10px;\n}\n.popover.bottom {\n margin-top: 10px;\n}\n.popover.left {\n margin-left: -10px;\n}\n.popover-title {\n margin: 0;\n padding: 8px 14px;\n font-size: 14px;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-radius: 5px 5px 0 0;\n}\n.popover-content {\n padding: 9px 14px;\n}\n.popover > .arrow,\n.popover > .arrow:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.popover > .arrow {\n border-width: 11px;\n}\n.popover > .arrow:after {\n border-width: 10px;\n content: \"\";\n}\n.popover.top > .arrow {\n left: 50%;\n margin-left: -11px;\n border-bottom-width: 0;\n border-top-color: #999999;\n border-top-color: rgba(0, 0, 0, 0.25);\n bottom: -11px;\n}\n.popover.top > .arrow:after {\n content: \" \";\n bottom: 1px;\n margin-left: -10px;\n border-bottom-width: 0;\n border-top-color: #ffffff;\n}\n.popover.right > .arrow {\n top: 50%;\n left: -11px;\n margin-top: -11px;\n border-left-width: 0;\n border-right-color: #999999;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.popover.right > .arrow:after {\n content: \" \";\n left: 1px;\n bottom: -10px;\n border-left-width: 0;\n border-right-color: #ffffff;\n}\n.popover.bottom > .arrow {\n left: 50%;\n margin-left: -11px;\n border-top-width: 0;\n border-bottom-color: #999999;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n top: -11px;\n}\n.popover.bottom > .arrow:after {\n content: \" \";\n top: 1px;\n margin-left: -10px;\n border-top-width: 0;\n border-bottom-color: #ffffff;\n}\n.popover.left > .arrow {\n top: 50%;\n right: -11px;\n margin-top: -11px;\n border-right-width: 0;\n border-left-color: #999999;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.popover.left > .arrow:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: #ffffff;\n bottom: -10px;\n}\n.carousel {\n position: relative;\n}\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n}\n.carousel-inner > .item {\n display: none;\n position: relative;\n -webkit-transition: 0.6s ease-in-out left;\n -o-transition: 0.6s ease-in-out left;\n transition: 0.6s ease-in-out left;\n}\n.carousel-inner > .item > img,\n.carousel-inner > .item > a > img {\n line-height: 1;\n}\n@media all and (transform-3d), (-webkit-transform-3d) {\n .carousel-inner > .item {\n -webkit-transition: -webkit-transform 0.6s ease-in-out;\n -moz-transition: -moz-transform 0.6s ease-in-out;\n -o-transition: -o-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n -webkit-backface-visibility: hidden;\n -moz-backface-visibility: hidden;\n backface-visibility: hidden;\n -webkit-perspective: 1000px;\n -moz-perspective: 1000px;\n perspective: 1000px;\n }\n .carousel-inner > .item.next,\n .carousel-inner > .item.active.right {\n -webkit-transform: translate3d(100%, 0, 0);\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.prev,\n .carousel-inner > .item.active.left {\n -webkit-transform: translate3d(-100%, 0, 0);\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n .carousel-inner > .item.next.left,\n .carousel-inner > .item.prev.right,\n .carousel-inner > .item.active {\n -webkit-transform: translate3d(0, 0, 0);\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n}\n.carousel-inner > .active,\n.carousel-inner > .next,\n.carousel-inner > .prev {\n display: block;\n}\n.carousel-inner > .active {\n left: 0;\n}\n.carousel-inner > .next,\n.carousel-inner > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n}\n.carousel-inner > .next {\n left: 100%;\n}\n.carousel-inner > .prev {\n left: -100%;\n}\n.carousel-inner > .next.left,\n.carousel-inner > .prev.right {\n left: 0;\n}\n.carousel-inner > .active.left {\n left: -100%;\n}\n.carousel-inner > .active.right {\n left: 100%;\n}\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: 15%;\n opacity: 0.5;\n filter: alpha(opacity=50);\n font-size: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-control.left {\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\n}\n.carousel-control.right {\n left: auto;\n right: 0;\n background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\n}\n.carousel-control:hover,\n.carousel-control:focus {\n outline: 0;\n color: #ffffff;\n text-decoration: none;\n opacity: 0.9;\n filter: alpha(opacity=90);\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-left,\n.carousel-control .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n margin-top: -10px;\n z-index: 5;\n display: inline-block;\n}\n.carousel-control .icon-prev,\n.carousel-control .glyphicon-chevron-left {\n left: 50%;\n margin-left: -10px;\n}\n.carousel-control .icon-next,\n.carousel-control .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n}\n.carousel-control .icon-prev,\n.carousel-control .icon-next {\n width: 20px;\n height: 20px;\n line-height: 1;\n font-family: serif;\n}\n.carousel-control .icon-prev:before {\n content: '\\2039';\n}\n.carousel-control .icon-next:before {\n content: '\\203a';\n}\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n}\n.carousel-indicators li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid #ffffff;\n border-radius: 10px;\n cursor: pointer;\n background-color: #000 \\9;\n background-color: rgba(0, 0, 0, 0);\n}\n.carousel-indicators .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: #ffffff;\n}\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #ffffff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n}\n.carousel-caption .btn {\n text-shadow: none;\n}\n@media screen and (min-width: 768px) {\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-prev,\n .carousel-control .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -15px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -15px;\n }\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n .carousel-indicators {\n bottom: 20px;\n }\n}\n.clearfix:before,\n.clearfix:after,\n.dl-horizontal dd:before,\n.dl-horizontal dd:after,\n.container:before,\n.container:after,\n.container-fluid:before,\n.container-fluid:after,\n.row:before,\n.row:after,\n.form-horizontal .form-group:before,\n.form-horizontal .form-group:after,\n.btn-toolbar:before,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:before,\n.btn-group-vertical > .btn-group:after,\n.nav:before,\n.nav:after,\n.navbar:before,\n.navbar:after,\n.navbar-header:before,\n.navbar-header:after,\n.navbar-collapse:before,\n.navbar-collapse:after,\n.pager:before,\n.pager:after,\n.panel-body:before,\n.panel-body:after,\n.modal-footer:before,\n.modal-footer:after {\n content: \" \";\n display: table;\n}\n.clearfix:after,\n.dl-horizontal dd:after,\n.container:after,\n.container-fluid:after,\n.row:after,\n.form-horizontal .form-group:after,\n.btn-toolbar:after,\n.btn-group-vertical > .btn-group:after,\n.nav:after,\n.navbar:after,\n.navbar-header:after,\n.navbar-collapse:after,\n.pager:after,\n.panel-body:after,\n.modal-footer:after {\n clear: both;\n}\n.center-block {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n.hidden {\n display: none !important;\n}\n.affix {\n position: fixed;\n}\n@-ms-viewport {\n width: device-width;\n}\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n display: none !important;\n}\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n@media (max-width: 767px) {\n .visible-xs {\n display: block !important;\n }\n table.visible-xs {\n display: table !important;\n }\n tr.visible-xs {\n display: table-row !important;\n }\n th.visible-xs,\n td.visible-xs {\n display: table-cell !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-block {\n display: block !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline {\n display: inline !important;\n }\n}\n@media (max-width: 767px) {\n .visible-xs-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm {\n display: block !important;\n }\n table.visible-sm {\n display: table !important;\n }\n tr.visible-sm {\n display: table-row !important;\n }\n th.visible-sm,\n td.visible-sm {\n display: table-cell !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-block {\n display: block !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline {\n display: inline !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .visible-sm-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md {\n display: block !important;\n }\n table.visible-md {\n display: table !important;\n }\n tr.visible-md {\n display: table-row !important;\n }\n th.visible-md,\n td.visible-md {\n display: table-cell !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-block {\n display: block !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline {\n display: inline !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .visible-md-inline-block {\n display: inline-block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg {\n display: block !important;\n }\n table.visible-lg {\n display: table !important;\n }\n tr.visible-lg {\n display: table-row !important;\n }\n th.visible-lg,\n td.visible-lg {\n display: table-cell !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-block {\n display: block !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline {\n display: inline !important;\n }\n}\n@media (min-width: 1200px) {\n .visible-lg-inline-block {\n display: inline-block !important;\n }\n}\n@media (max-width: 767px) {\n .hidden-xs {\n display: none !important;\n }\n}\n@media (min-width: 768px) and (max-width: 991px) {\n .hidden-sm {\n display: none !important;\n }\n}\n@media (min-width: 992px) and (max-width: 1199px) {\n .hidden-md {\n display: none !important;\n }\n}\n@media (min-width: 1200px) {\n .hidden-lg {\n display: none !important;\n }\n}\n.visible-print {\n display: none !important;\n}\n@media print {\n .visible-print {\n display: block !important;\n }\n table.visible-print {\n display: table !important;\n }\n tr.visible-print {\n display: table-row !important;\n }\n th.visible-print,\n td.visible-print {\n display: table-cell !important;\n }\n}\n.visible-print-block {\n display: none !important;\n}\n@media print {\n .visible-print-block {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n}\n@media print {\n .visible-print-inline {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n}\n@media print {\n .visible-print-inline-block {\n display: inline-block !important;\n }\n}\n@media print {\n .hidden-print {\n display: none !important;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */\n\n//\n// 1. Set default font family to sans-serif.\n// 2. Prevent iOS and IE text size adjust after device orientation change,\n// without disabling user zoom.\n//\n\nhtml {\n font-family: sans-serif; // 1\n -ms-text-size-adjust: 100%; // 2\n -webkit-text-size-adjust: 100%; // 2\n}\n\n//\n// Remove default margin.\n//\n\nbody {\n margin: 0;\n}\n\n// HTML5 display definitions\n// ==========================================================================\n\n//\n// Correct `block` display not defined for any HTML5 element in IE 8/9.\n// Correct `block` display not defined for `details` or `summary` in IE 10/11\n// and Firefox.\n// Correct `block` display not defined for `main` in IE 11.\n//\n\narticle,\naside,\ndetails,\nfigcaption,\nfigure,\nfooter,\nheader,\nhgroup,\nmain,\nmenu,\nnav,\nsection,\nsummary {\n display: block;\n}\n\n//\n// 1. Correct `inline-block` display not defined in IE 8/9.\n// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.\n//\n\naudio,\ncanvas,\nprogress,\nvideo {\n display: inline-block; // 1\n vertical-align: baseline; // 2\n}\n\n//\n// Prevent modern browsers from displaying `audio` without controls.\n// Remove excess height in iOS 5 devices.\n//\n\naudio:not([controls]) {\n display: none;\n height: 0;\n}\n\n//\n// Address `[hidden]` styling not present in IE 8/9/10.\n// Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.\n//\n\n[hidden],\ntemplate {\n display: none;\n}\n\n// Links\n// ==========================================================================\n\n//\n// Remove the gray background color from active links in IE 10.\n//\n\na {\n background-color: transparent;\n}\n\n//\n// Improve readability of focused elements when they are also in an\n// active/hover state.\n//\n\na:active,\na:hover {\n outline: 0;\n}\n\n// Text-level semantics\n// ==========================================================================\n\n//\n// Address styling not present in IE 8/9/10/11, Safari, and Chrome.\n//\n\nabbr[title] {\n border-bottom: 1px dotted;\n}\n\n//\n// Address style set to `bolder` in Firefox 4+, Safari, and Chrome.\n//\n\nb,\nstrong {\n font-weight: bold;\n}\n\n//\n// Address styling not present in Safari and Chrome.\n//\n\ndfn {\n font-style: italic;\n}\n\n//\n// Address variable `h1` font-size and margin within `section` and `article`\n// contexts in Firefox 4+, Safari, and Chrome.\n//\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n//\n// Address styling not present in IE 8/9.\n//\n\nmark {\n background: #ff0;\n color: #000;\n}\n\n//\n// Address inconsistent and variable font size in all browsers.\n//\n\nsmall {\n font-size: 80%;\n}\n\n//\n// Prevent `sub` and `sup` affecting `line-height` in all browsers.\n//\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsup {\n top: -0.5em;\n}\n\nsub {\n bottom: -0.25em;\n}\n\n// Embedded content\n// ==========================================================================\n\n//\n// Remove border when inside `a` element in IE 8/9/10.\n//\n\nimg {\n border: 0;\n}\n\n//\n// Correct overflow not hidden in IE 9/10/11.\n//\n\nsvg:not(:root) {\n overflow: hidden;\n}\n\n// Grouping content\n// ==========================================================================\n\n//\n// Address margin not present in IE 8/9 and Safari.\n//\n\nfigure {\n margin: 1em 40px;\n}\n\n//\n// Address differences between Firefox and other browsers.\n//\n\nhr {\n box-sizing: content-box;\n height: 0;\n}\n\n//\n// Contain overflow in all browsers.\n//\n\npre {\n overflow: auto;\n}\n\n//\n// Address odd `em`-unit font size rendering in all browsers.\n//\n\ncode,\nkbd,\npre,\nsamp {\n font-family: monospace, monospace;\n font-size: 1em;\n}\n\n// Forms\n// ==========================================================================\n\n//\n// Known limitation: by default, Chrome and Safari on OS X allow very limited\n// styling of `select`, unless a `border` property is set.\n//\n\n//\n// 1. Correct color not being inherited.\n// Known issue: affects color of disabled elements.\n// 2. Correct font properties not being inherited.\n// 3. Address margins set differently in Firefox 4+, Safari, and Chrome.\n//\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n color: inherit; // 1\n font: inherit; // 2\n margin: 0; // 3\n}\n\n//\n// Address `overflow` set to `hidden` in IE 8/9/10/11.\n//\n\nbutton {\n overflow: visible;\n}\n\n//\n// Address inconsistent `text-transform` inheritance for `button` and `select`.\n// All other form control elements do not inherit `text-transform` values.\n// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.\n// Correct `select` style inheritance in Firefox.\n//\n\nbutton,\nselect {\n text-transform: none;\n}\n\n//\n// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`\n// and `video` controls.\n// 2. Correct inability to style clickable `input` types in iOS.\n// 3. Improve usability and consistency of cursor style between image-type\n// `input` and others.\n//\n\nbutton,\nhtml input[type=\"button\"], // 1\ninput[type=\"reset\"],\ninput[type=\"submit\"] {\n -webkit-appearance: button; // 2\n cursor: pointer; // 3\n}\n\n//\n// Re-set default cursor for disabled elements.\n//\n\nbutton[disabled],\nhtml input[disabled] {\n cursor: default;\n}\n\n//\n// Remove inner padding and border in Firefox 4+.\n//\n\nbutton::-moz-focus-inner,\ninput::-moz-focus-inner {\n border: 0;\n padding: 0;\n}\n\n//\n// Address Firefox 4+ setting `line-height` on `input` using `!important` in\n// the UA stylesheet.\n//\n\ninput {\n line-height: normal;\n}\n\n//\n// It's recommended that you don't attempt to style these elements.\n// Firefox's implementation doesn't respect box-sizing, padding, or width.\n//\n// 1. Address box sizing set to `content-box` in IE 8/9/10.\n// 2. Remove excess padding in IE 8/9/10.\n//\n\ninput[type=\"checkbox\"],\ninput[type=\"radio\"] {\n box-sizing: border-box; // 1\n padding: 0; // 2\n}\n\n//\n// Fix the cursor style for Chrome's increment/decrement buttons. For certain\n// `font-size` values of the `input`, it causes the cursor style of the\n// decrement button to change from `default` to `text`.\n//\n\ninput[type=\"number\"]::-webkit-inner-spin-button,\ninput[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n//\n// 1. Address `appearance` set to `searchfield` in Safari and Chrome.\n// 2. Address `box-sizing` set to `border-box` in Safari and Chrome.\n//\n\ninput[type=\"search\"] {\n -webkit-appearance: textfield; // 1\n box-sizing: content-box; //2\n}\n\n//\n// Remove inner padding and search cancel button in Safari and Chrome on OS X.\n// Safari (but not Chrome) clips the cancel button when the search input has\n// padding (and `textfield` appearance).\n//\n\ninput[type=\"search\"]::-webkit-search-cancel-button,\ninput[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// Define consistent border, margin, and padding.\n//\n\nfieldset {\n border: 1px solid #c0c0c0;\n margin: 0 2px;\n padding: 0.35em 0.625em 0.75em;\n}\n\n//\n// 1. Correct `color` not being inherited in IE 8/9/10/11.\n// 2. Remove padding so people aren't caught out if they zero out fieldsets.\n//\n\nlegend {\n border: 0; // 1\n padding: 0; // 2\n}\n\n//\n// Remove default vertical scrollbar in IE 8/9/10/11.\n//\n\ntextarea {\n overflow: auto;\n}\n\n//\n// Don't inherit the `font-weight` (applied by a rule above).\n// NOTE: the default cannot safely be changed in Chrome and Safari on OS X.\n//\n\noptgroup {\n font-weight: bold;\n}\n\n// Tables\n// ==========================================================================\n\n//\n// Remove most spacing between table cells.\n//\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\ntd,\nth {\n padding: 0;\n}\n","/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */\n\n// ==========================================================================\n// Print styles.\n// Inlined to avoid the additional HTTP request: h5bp.com/r\n// ==========================================================================\n\n@media print {\n *,\n *:before,\n *:after {\n background: transparent !important;\n color: #000 !important; // Black prints faster: h5bp.com/s\n box-shadow: none !important;\n text-shadow: none !important;\n }\n\n a,\n a:visited {\n text-decoration: underline;\n }\n\n a[href]:after {\n content: \" (\" attr(href) \")\";\n }\n\n abbr[title]:after {\n content: \" (\" attr(title) \")\";\n }\n\n // Don't show links that are fragment identifiers,\n // or use the `javascript:` pseudo protocol\n a[href^=\"#\"]:after,\n a[href^=\"javascript:\"]:after {\n content: \"\";\n }\n\n pre,\n blockquote {\n border: 1px solid #999;\n page-break-inside: avoid;\n }\n\n thead {\n display: table-header-group; // h5bp.com/t\n }\n\n tr,\n img {\n page-break-inside: avoid;\n }\n\n img {\n max-width: 100% !important;\n }\n\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n\n h2,\n h3 {\n page-break-after: avoid;\n }\n\n // Bootstrap specific changes start\n\n // Bootstrap components\n .navbar {\n display: none;\n }\n .btn,\n .dropup > .btn {\n > .caret {\n border-top-color: #000 !important;\n }\n }\n .label {\n border: 1px solid #000;\n }\n\n .table {\n border-collapse: collapse !important;\n\n td,\n th {\n background-color: #fff !important;\n }\n }\n .table-bordered {\n th,\n td {\n border: 1px solid #ddd !important;\n }\n }\n\n // Bootstrap specific changes end\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n//
    Star\n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: url('@{icon-font-path}@{icon-font-name}.eot');\n src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'),\n url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'),\n url('@{icon-font-path}@{icon-font-name}.woff') format('woff'),\n url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'),\n url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg');\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro,\n.glyphicon-eur { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n.glyphicon-cd { &:before { content: \"\\e201\"; } }\n.glyphicon-save-file { &:before { content: \"\\e202\"; } }\n.glyphicon-open-file { &:before { content: \"\\e203\"; } }\n.glyphicon-level-up { &:before { content: \"\\e204\"; } }\n.glyphicon-copy { &:before { content: \"\\e205\"; } }\n.glyphicon-paste { &:before { content: \"\\e206\"; } }\n// The following 2 Glyphicons are omitted for the time being because\n// they currently use Unicode codepoints that are outside the\n// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle\n// non-BMP codepoints in CSS string escapes, and thus can't display these two icons.\n// Notably, the bug affects some older versions of the Android Browser.\n// More info: https://github.com/twbs/bootstrap/issues/10106\n// .glyphicon-door { &:before { content: \"\\1f6aa\"; } }\n// .glyphicon-key { &:before { content: \"\\1f511\"; } }\n.glyphicon-alert { &:before { content: \"\\e209\"; } }\n.glyphicon-equalizer { &:before { content: \"\\e210\"; } }\n.glyphicon-king { &:before { content: \"\\e211\"; } }\n.glyphicon-queen { &:before { content: \"\\e212\"; } }\n.glyphicon-pawn { &:before { content: \"\\e213\"; } }\n.glyphicon-bishop { &:before { content: \"\\e214\"; } }\n.glyphicon-knight { &:before { content: \"\\e215\"; } }\n.glyphicon-baby-formula { &:before { content: \"\\e216\"; } }\n.glyphicon-tent { &:before { content: \"\\26fa\"; } }\n.glyphicon-blackboard { &:before { content: \"\\e218\"; } }\n.glyphicon-bed { &:before { content: \"\\e219\"; } }\n.glyphicon-apple { &:before { content: \"\\f8ff\"; } }\n.glyphicon-erase { &:before { content: \"\\e221\"; } }\n.glyphicon-hourglass { &:before { content: \"\\231b\"; } }\n.glyphicon-lamp { &:before { content: \"\\e223\"; } }\n.glyphicon-duplicate { &:before { content: \"\\e224\"; } }\n.glyphicon-piggy-bank { &:before { content: \"\\e225\"; } }\n.glyphicon-scissors { &:before { content: \"\\e226\"; } }\n.glyphicon-bitcoin { &:before { content: \"\\e227\"; } }\n.glyphicon-btc { &:before { content: \"\\e227\"; } }\n.glyphicon-xbt { &:before { content: \"\\e227\"; } }\n.glyphicon-yen { &:before { content: \"\\00a5\"; } }\n.glyphicon-jpy { &:before { content: \"\\00a5\"; } }\n.glyphicon-ruble { &:before { content: \"\\20bd\"; } }\n.glyphicon-rub { &:before { content: \"\\20bd\"; } }\n.glyphicon-scale { &:before { content: \"\\e230\"; } }\n.glyphicon-ice-lolly { &:before { content: \"\\e231\"; } }\n.glyphicon-ice-lolly-tasted { &:before { content: \"\\e232\"; } }\n.glyphicon-education { &:before { content: \"\\e233\"; } }\n.glyphicon-option-horizontal { &:before { content: \"\\e234\"; } }\n.glyphicon-option-vertical { &:before { content: \"\\e235\"; } }\n.glyphicon-menu-hamburger { &:before { content: \"\\e236\"; } }\n.glyphicon-modal-window { &:before { content: \"\\e237\"; } }\n.glyphicon-oil { &:before { content: \"\\e238\"; } }\n.glyphicon-grain { &:before { content: \"\\e239\"; } }\n.glyphicon-sunglasses { &:before { content: \"\\e240\"; } }\n.glyphicon-text-size { &:before { content: \"\\e241\"; } }\n.glyphicon-text-color { &:before { content: \"\\e242\"; } }\n.glyphicon-text-background { &:before { content: \"\\e243\"; } }\n.glyphicon-object-align-top { &:before { content: \"\\e244\"; } }\n.glyphicon-object-align-bottom { &:before { content: \"\\e245\"; } }\n.glyphicon-object-align-horizontal{ &:before { content: \"\\e246\"; } }\n.glyphicon-object-align-left { &:before { content: \"\\e247\"; } }\n.glyphicon-object-align-vertical { &:before { content: \"\\e248\"; } }\n.glyphicon-object-align-right { &:before { content: \"\\e249\"; } }\n.glyphicon-triangle-right { &:before { content: \"\\e250\"; } }\n.glyphicon-triangle-left { &:before { content: \"\\e251\"; } }\n.glyphicon-triangle-bottom { &:before { content: \"\\e252\"; } }\n.glyphicon-triangle-top { &:before { content: \"\\e253\"; } }\n.glyphicon-console { &:before { content: \"\\e254\"; } }\n.glyphicon-superscript { &:before { content: \"\\e255\"; } }\n.glyphicon-subscript { &:before { content: \"\\e256\"; } }\n.glyphicon-menu-left { &:before { content: \"\\e257\"; } }\n.glyphicon-menu-right { &:before { content: \"\\e258\"; } }\n.glyphicon-menu-down { &:before { content: \"\\e259\"; } }\n.glyphicon-menu-up { &:before { content: \"\\e260\"; } }\n","//\n// Scaffolding\n// --------------------------------------------------\n\n\n// Reset the box-sizing\n//\n// Heads up! This reset may cause conflicts with some third-party widgets.\n// For recommendations on resolving such conflicts, see\n// http://getbootstrap.com/getting-started/#third-box-sizing\n* {\n .box-sizing(border-box);\n}\n*:before,\n*:after {\n .box-sizing(border-box);\n}\n\n\n// Body reset\n\nhtml {\n font-size: 10px;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\n\nbody {\n font-family: @font-family-base;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @text-color;\n background-color: @body-bg;\n}\n\n// Reset fonts for relevant elements\ninput,\nbutton,\nselect,\ntextarea {\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\n\n// Links\n\na {\n color: @link-color;\n text-decoration: none;\n\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n }\n\n &:focus {\n .tab-focus();\n }\n}\n\n\n// Figures\n//\n// We reset this here because previously Normalize had no `figure` margins. This\n// ensures we don't break anyone's use of the element.\n\nfigure {\n margin: 0;\n}\n\n\n// Images\n\nimg {\n vertical-align: middle;\n}\n\n// Responsive images (ensure images don't scale beyond their parents)\n.img-responsive {\n .img-responsive();\n}\n\n// Rounded corners\n.img-rounded {\n border-radius: @border-radius-large;\n}\n\n// Image thumbnails\n//\n// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`.\n.img-thumbnail {\n padding: @thumbnail-padding;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n // Keep them at most 100% wide\n .img-responsive(inline-block);\n}\n\n// Perfect circle\n.img-circle {\n border-radius: 50%; // set radius in percents\n}\n\n\n// Horizontal rules\n\nhr {\n margin-top: @line-height-computed;\n margin-bottom: @line-height-computed;\n border: 0;\n border-top: 1px solid @hr-border;\n}\n\n\n// Only display content to screen readers\n//\n// See: http://a11yproject.com/posts/how-to-hide-content/\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0,0,0,0);\n border: 0;\n}\n\n// Use in conjunction with .sr-only to only display content when it's focused.\n// Useful for \"Skip to main content\" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1\n// Credit: HTML5 Boilerplate\n\n.sr-only-focusable {\n &:active,\n &:focus {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n overflow: visible;\n clip: auto;\n }\n}\n\n\n// iOS \"clickable elements\" fix for role=\"button\"\n//\n// Fixes \"clickability\" issue (and more generally, the firing of events such as focus as well)\n// for traditionally non-focusable elements with role=\"button\"\n// see https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile\n\n[role=\"button\"] {\n cursor: pointer;\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n","// WebKit-style focus\n\n.tab-focus() {\n // Default\n outline: thin dotted;\n // WebKit\n outline: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n.img-responsive(@display: block) {\n display: @display;\n max-width: 100%; // Part 1: Set a maximum relative to the parent\n height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size. Note that the\n// spelling of `min--moz-device-pixel-ratio` is intentional.\n.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {\n background-image: url(\"@{file-1x}\");\n\n @media\n only screen and (-webkit-min-device-pixel-ratio: 2),\n only screen and ( min--moz-device-pixel-ratio: 2),\n only screen and ( -o-min-device-pixel-ratio: 2/1),\n only screen and ( min-device-pixel-ratio: 2),\n only screen and ( min-resolution: 192dpi),\n only screen and ( min-resolution: 2dppx) {\n background-image: url(\"@{file-2x}\");\n background-size: @width-1x @height-1x;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 300;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: (12px small font / 14px base font) * 100% = about 85%\nsmall,\n.small {\n font-size: floor((100% * @font-size-small / @font-size-base));\n}\n\nmark,\n.mark {\n background-color: @state-warning-bg;\n padding: .2em;\n}\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n.text-nowrap { white-space: nowrap; }\n\n// Transformation\n.text-lowercase { text-transform: lowercase; }\n.text-uppercase { text-transform: uppercase; }\n.text-capitalize { text-transform: capitalize; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// -------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n.dl-horizontal {\n dd {\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n\n @media (min-width: @grid-float-breakpoint) {\n dt {\n float: left;\n width: (@dl-horizontal-offset - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @dl-horizontal-offset;\n }\n }\n}\n\n\n// Misc\n// -------------------------\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n .text-uppercase();\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","// Typography\n\n.text-emphasis-variant(@color) {\n color: @color;\n a&:hover,\n a&:focus {\n color: darken(@color, 10%);\n }\n}\n","// Contextual backgrounds\n\n.bg-variant(@color) {\n background-color: @color;\n a&:hover,\n a&:focus {\n background-color: darken(@color, 10%);\n }\n}\n","// Text overflow\n// Requires inline-block or block for proper styling\n\n.text-overflow() {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n\n kbd {\n padding: 0;\n font-size: 100%;\n font-weight: bold;\n box-shadow: none;\n }\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","// Grid system\n//\n// Generate semantic grid columns with these mixins.\n\n// Centered container element\n.container-fixed(@gutter: @grid-gutter-width) {\n margin-right: auto;\n margin-left: auto;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n &:extend(.clearfix all);\n}\n\n// Creates a wrapper for a series of columns\n.make-row(@gutter: @grid-gutter-width) {\n margin-left: ceil((@gutter / -2));\n margin-right: floor((@gutter / -2));\n &:extend(.clearfix all);\n}\n\n// Generate the extra small columns\n.make-xs-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n float: left;\n width: percentage((@columns / @grid-columns));\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n}\n.make-xs-column-offset(@columns) {\n margin-left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-push(@columns) {\n left: percentage((@columns / @grid-columns));\n}\n.make-xs-column-pull(@columns) {\n right: percentage((@columns / @grid-columns));\n}\n\n// Generate the small columns\n.make-sm-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-sm-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-offset(@columns) {\n @media (min-width: @screen-sm-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-push(@columns) {\n @media (min-width: @screen-sm-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-sm-column-pull(@columns) {\n @media (min-width: @screen-sm-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the medium columns\n.make-md-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-md-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-offset(@columns) {\n @media (min-width: @screen-md-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-push(@columns) {\n @media (min-width: @screen-md-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-md-column-pull(@columns) {\n @media (min-width: @screen-md-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n\n// Generate the large columns\n.make-lg-column(@columns; @gutter: @grid-gutter-width) {\n position: relative;\n min-height: 1px;\n padding-left: (@gutter / 2);\n padding-right: (@gutter / 2);\n\n @media (min-width: @screen-lg-min) {\n float: left;\n width: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-offset(@columns) {\n @media (min-width: @screen-lg-min) {\n margin-left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-push(@columns) {\n @media (min-width: @screen-lg-min) {\n left: percentage((@columns / @grid-columns));\n }\n}\n.make-lg-column-pull(@columns) {\n @media (min-width: @screen-lg-min) {\n right: percentage((@columns / @grid-columns));\n }\n}\n","// Framework grid generation\n//\n// Used only by Bootstrap to generate the correct number of grid classes given\n// any value of `@grid-columns`.\n\n.make-grid-columns() {\n // Common styles for all sizes of grid columns, widths 1-12\n .col(@index) { // initial\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general; \"=<\" isn't a typo\n @item: ~\".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n position: relative;\n // Prevent columns from collapsing when empty\n min-height: 1px;\n // Inner gutter via padding\n padding-left: ceil((@grid-gutter-width / 2));\n padding-right: floor((@grid-gutter-width / 2));\n }\n }\n .col(1); // kickstart it\n}\n\n.float-grid-columns(@class) {\n .col(@index) { // initial\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), @item);\n }\n .col(@index, @list) when (@index =< @grid-columns) { // general\n @item: ~\".col-@{class}-@{index}\";\n .col((@index + 1), ~\"@{list}, @{item}\");\n }\n .col(@index, @list) when (@index > @grid-columns) { // terminal\n @{list} {\n float: left;\n }\n }\n .col(1); // kickstart it\n}\n\n.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {\n .col-@{class}-@{index} {\n width: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {\n .col-@{class}-push-@{index} {\n left: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {\n .col-@{class}-push-0 {\n left: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {\n .col-@{class}-pull-@{index} {\n right: percentage((@index / @grid-columns));\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {\n .col-@{class}-pull-0 {\n right: auto;\n }\n}\n.calc-grid-column(@index, @class, @type) when (@type = offset) {\n .col-@{class}-offset-@{index} {\n margin-left: percentage((@index / @grid-columns));\n }\n}\n\n// Basic looping in LESS\n.loop-grid-columns(@index, @class, @type) when (@index >= 0) {\n .calc-grid-column(@index, @class, @type);\n // next iteration\n .loop-grid-columns((@index - 1), @class, @type);\n}\n\n// Create grid for specific class\n.make-grid(@class) {\n .float-grid-columns(@class);\n .loop-grid-columns(@grid-columns, @class, width);\n .loop-grid-columns(@grid-columns, @class, pull);\n .loop-grid-columns(@grid-columns, @class, push);\n .loop-grid-columns(@grid-columns, @class, offset);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n background-color: @table-bg;\n}\ncaption {\n padding-top: @table-cell-padding;\n padding-bottom: @table-cell-padding;\n color: @text-muted;\n text-align: left;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n max-width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-of-type(odd) {\n background-color: @table-bg-accent;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n background-color: @table-bg-hover;\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n.table-responsive {\n overflow-x: auto;\n min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837)\n\n @media screen and (max-width: @screen-xs-max) {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","// Tables\n\n.table-row-variant(@state; @background) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table > thead > tr,\n .table > tbody > tr,\n .table > tfoot > tr {\n > td.@{state},\n > th.@{state},\n &.@{state} > td,\n &.@{state} > th {\n background-color: @background;\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover > tbody > tr {\n > td.@{state}:hover,\n > th.@{state}:hover,\n &.@{state}:hover > td,\n &:hover > .@{state},\n &.@{state}:hover > th {\n background-color: darken(@background, 5%);\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141)\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; // IE8-9\n line-height: normal;\n}\n\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius; // Note: This has no effect on s in CSS.\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655\n }\n\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS temporal inputs\n//\n// In Mobile Safari, setting `display: block` on temporal inputs causes the\n// text within the input to become vertically misaligned. As a workaround, we\n// set a pixel line-height that matches the given height of the input, but only\n// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848\n//\n// Note that as of 8.3, iOS doesn't support `datetime` or `week`.\n\n@media screen and (-webkit-min-device-pixel-ratio: 0) {\n input[type=\"date\"],\n input[type=\"time\"],\n input[type=\"datetime-local\"],\n input[type=\"month\"] {\n &.form-control {\n line-height: @input-height-base;\n }\n\n &.input-sm,\n .input-group-sm & {\n line-height: @input-height-small;\n }\n\n &.input-lg,\n .input-group-lg & {\n line-height: @input-height-large;\n }\n }\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: @form-group-margin-bottom;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n position: relative;\n display: block;\n margin-top: 10px;\n margin-bottom: 10px;\n\n label {\n min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text\n padding-left: 20px;\n margin-bottom: 0;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n position: absolute;\n margin-left: -20px;\n margin-top: 4px \\9;\n}\n\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n position: relative;\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n// Some special care is needed because