diff --git a/CodeSmith/CSharp/ApiGenerate.cst b/CodeSmith/CSharp/ApiGenerate.cst new file mode 100644 index 0000000000000000000000000000000000000000..9d201f29cef79c89e77c818dadfe5f92f9a0c1f4 --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate.cst @@ -0,0 +1,226 @@ +<%-- +Author: yubaolee +Description: 用于生成OpenAuth.WebApi接口相关代码,包括controller/app/实体/dbcontext +--%> +<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="Normal" %> + +<%@ Assembly Name="SchemaExplorer" %> +<%@ Assembly Name="CodeSmith.CustomProperties" %> + +<%@ Assembly Name="Mono.Cecil" Path="..\Common" %> +<%@ Assembly Name="ICSharpCode.NRefactory" Path="..\Common" %> +<%@ Assembly Name="ICSharpCode.NRefactory.CSharp" Path="..\Common" %> + +<%@ Assembly Src="Internal\Model.cs" %> +<%@ Assembly Src="Internal\Extensions.cs" %> +<%@ Assembly Src="Internal\Generator.cs" %> +<%@ Assembly Src="Internal\Parser.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.IO" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> +<%@ Import Namespace="System.Diagnostics" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="WholeDb" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否直接生成选定数据库中的所有表" %> + +<%@ Property Name="HeaderModel" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为启用头表模式,即类似‘入库订单’界面" %> + +<%@ Property Name="SourceDatabase" + Type="SchemaExplorer.DatabaseSchema" + Category="1.Database" + Description="The source database." %> + +<%@ Property Name="SourceTables" +Type="SchemaExplorer.TableSchemaCollection" +Category="1.Database" Description="可以选择一个或多个表(使用Ctrl键)" %> + +<%@ Property Name="directory" + Type="System.String" + Default=".\" + Optional="True" + Description="代码生成路径" + Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> + +<%@ Property Name="ContextNamespace" + Type="System.String" + Category="2.Class" + Default="OpenAuth.Repository" + OnChanged="OnContextNamespaceChanged" + Description="DbContext默认命名空间,尽量不要更改"%> +<%@ Property Name="EntityNamespace" + Type="System.String" + Default="OpenAuth.Repository.Domain" + Category="2.Class" + Description="实体默认命名空间,尽量不要更改"%> + + +<%@ Register Name="EntityGeneratedClass" + Template="Internal\Entity.Generated.cst" + MergeProperties="False" %> + +<%@ Register Name="ContextGeneratedClass" + Template="Internal\Context.Generated.cst" + MergeProperties="True" %> + +<%@ Register Name="ApplicationGenerateClass" + Template="ApiGenerate\Application.cst" + MergeProperties="False" %> +<%@ Register Name="RequestGenerateClass" + Template="ApiGenerate\Request.cst" + MergeProperties="False" %> +<%@ Register Name="ModifyReqGenerateClass" + Template="ApiGenerate\ModifyReq.cst" + MergeProperties="False" %> +<%@ Register Name="ControllerGenerateClass" + Template="ApiGenerate\Controller.cst" + MergeProperties="False" %> + +开始创建OpenAuth.Core WebApi相关代码 ... +<% Generate(); %> + + \ No newline at end of file diff --git a/CodeSmith/CSharp/ApiGenerate/Application.cst b/CodeSmith/CSharp/ApiGenerate/Application.cst new file mode 100644 index 0000000000000000000000000000000000000000..28689ccbd24029ff011a31e8d34606ebf51fd02c --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/Application.cst @@ -0,0 +1,185 @@ +<%-- +Name: Database Table Properties +Author: yubaolee +Description: Create a list of properties from a database table +--%> +<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="True" Description="应用层" %> +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> +<%@ Property Name="Table" + Type="SchemaExplorer.TableSchema" %> + +<%@ Property Name="HeaderModel" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为启用头表模式,即类似‘入库订单’界面" %> + +using System; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class <%=Table.Name%>App : BaseApp<<%=Table.Name%>> + { + private RevelanceManagerApp _revelanceApp; + + /// + /// 加载列表 + /// + public TableData Load(Query<%=Table.Name%>ListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + var properties = loginContext.GetProperties("<%=Table.Name%>"); + + if (properties == null || properties.Count == 0) + { + throw new Exception("当前登录用户没有访问该模块字段的权限,请联系管理员配置"); + } + + + var result = new TableData(); + var objs = UnitWork.Find<<%=Table.Name%>>(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key)); + } + + + var propertyStr = string.Join(',', properties.Select(u => u.Key)); + result.columnHeaders = properties; + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).Select($"new ({propertyStr})"); + result.count = objs.Count(); + return result; + } + + <% + if(Table.Name.Contains("Tbl") && (!Table.Name.Contains("Dtbl")) && this.HeaderModel){ + var dtblName = Table.Name.Replace("Tbl","Dtbl"); //明细表的表名 + %> + + public void Add(AddOrUpdate<%=Table.Name%>Req req) + { + var obj = req.MapTo<<%=Table.Name%>>(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + UnitWork.Add(obj); + if (req.<%=dtblName%>Reqs != null && req.<%=dtblName%>Reqs.Any()) + { + foreach (var detail in req.<%=dtblName%>Reqs) + { + detail.ForeignKeyId = obj.Id; //todo:调整自己的明细表外键 + _<%=dtblName%>App.AddNoSave(detail); + } + } + + UnitWork.Save(); + } + + public void Update(AddOrUpdate<%=Table.Name%>Req obj) + { + var user = _auth.GetCurrentUser().User; + + if (obj.<%=dtblName%>Reqs != null && obj.<%=dtblName%>Reqs.Any()) + { + //id为空的添加 + foreach (var detail in obj.<%=dtblName%>Reqs.Where(u =>string.IsNullOrEmpty(u.Id))) + { + detail.ForeignKeyId = obj.Id; //todo:调整自己的明细表外键 + _<%=dtblName%>App.AddNoSave(detail); + } + + //id比数据库少的,删除 + var containids = obj.<%=dtblName%>Reqs.Select(u => u.Id) + .Where(u =>!string.IsNullOrEmpty(u)).ToList(); + if (containids.Any()) + { + UnitWork.Delete<<%=dtblName%>>(u =>(!containids.Contains(u.Id)) && u.ForeignKeyId == obj.Id); //todo:调整自己的明细表外键 + } + + + //更新id相同的 + foreach (var detail in obj.<%=dtblName%>Reqs.Where(u =>!string.IsNullOrEmpty(u.Id))) + { + _<%=dtblName%>App.Update(detail); + } + } + + UnitWork.Update<<%=Table.Name%>>(u => u.Id == obj.Id, u => new <%=Table.Name%> + { + <% foreach(ColumnSchema p in Table.Columns) { + if(p.IsPrimaryKeyMember) continue; + %> + <%= p.Name%> = obj.<%= p.Name%>, + <% } %> + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + + UnitWork.Save(); + + } + + + <% + }else{ %> + public void Add(AddOrUpdate<%=Table.Name%>Req req) + { + var obj = req.MapTo<<%=Table.Name%>>(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + Repository.Add(obj); + } + + public void Update(AddOrUpdate<%=Table.Name%>Req obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update<<%=Table.Name%>>(u => u.Id == obj.Id, u => new <%=Table.Name%> + { + <% foreach(ColumnSchema p in Table.Columns) { + if(p.IsPrimaryKeyMember) continue; + %> + <%= p.Name%> = obj.<%= p.Name%>, + <% } %> + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + + } + + <% + } + %> + + public <%=Table.Name%>App(IUnitWork unitWork, IRepository<<%=Table.Name%>> repository, + RevelanceManagerApp app, IAuth auth) : base(unitWork, repository,auth) + { + _revelanceApp = app; + } + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/ApiGenerate/Controller.cst b/CodeSmith/CSharp/ApiGenerate/Controller.cst new file mode 100644 index 0000000000000000000000000000000000000000..fdfb966f1f76b0e50c580350e7951bdc5c26a3dd --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/Controller.cst @@ -0,0 +1,122 @@ +<%-- +Name: Database Table Properties +Author: yubaolee +Description: Create a list of properties from a database table +--%> +<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="True" Description="控制器" %> +<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> +<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> +<%@ Assembly Name="SchemaExplorer" %> +<%@ Import Namespace="SchemaExplorer" %> + +using System; +using Infrastructure; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.WebApi.Controllers +{ + /// + /// <%=ModuleName%>操作 + /// + [Route("api/[controller]/[action]")] + [ApiController] + public class <%=ModuleName%>sController : ControllerBase + { + private readonly <%=ModuleName%>App _app; + + //获取详情 + [HttpGet] + public Response<<%=ModuleName%>> Get(string id) + { + var result = new Response<<%=ModuleName%>>(); + try + { + result.Result = _app.Get(id); + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + //添加 + [HttpPost] + public Response Add(AddOrUpdate<%=ModuleName%>Req obj) + { + var result = new Response(); + try + { + _app.Add(obj); + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + //修改 + [HttpPost] + public Response Update(AddOrUpdate<%=ModuleName%>Req obj) + { + var result = new Response(); + try + { + _app.Update(obj); + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + /// + /// 加载列表 + /// + [HttpGet] + public TableData Load([FromQuery]Query<%=ModuleName%>ListReq request) + { + return _app.Load(request); + } + + /// + /// 批量删除 + /// + [HttpPost] + public Response Delete([FromBody]string[] ids) + { + var result = new Response(); + try + { + _app.Delete(ids); + + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.InnerException?.Message ?? ex.Message; + } + + return result; + } + + public <%=ModuleName%>sController(<%=ModuleName%>App app) + { + _app = app; + } + } +} diff --git a/CodeSmith/CSharp/ApiGenerate/ModifyReq.cst b/CodeSmith/CSharp/ApiGenerate/ModifyReq.cst new file mode 100644 index 0000000000000000000000000000000000000000..867bfd0cd3c42d07c06b7fb29e737c3fbd267482 --- /dev/null +++ b/CodeSmith/CSharp/ApiGenerate/ModifyReq.cst @@ -0,0 +1,66 @@ +<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> + +<%@ Assembly Src="../Internal/Model.cs" %> +<%@ Assembly Src="../Internal/Extensions.cs" %> + +<%@ Import Namespace="System.Collections.Generic" %> +<%@ Import Namespace="System.Linq" %> +<%@ Import Namespace="System.Text" %> +<%@ Import Namespace="System.Text.RegularExpressions" %> + +<%@ Import Namespace="SchemaMapper" %> + +<%@ Property Name="Table" + Type="SchemaExplorer.TableSchema" %> + +<%@ Property Name="HeaderModel" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为启用头表模式,即类似‘入库订单’界面" %> + +<%@ Property Name="EntityNamespace" + Type="System.String" %> +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// <%= Table.Description %> + /// + [Table("<%= Table.Name%>")] + public partial class AddOrUpdate<%= Table.Name %>Req + { + + <% foreach(ColumnSchema p in Table.Columns) { + + %> + /// + /// <%=p.Description %> + /// + public <%= p.SystemType.ToNullableType(p.AllowDBNull == true) %> <%= p.Name%> { get; set; } + <% } %> + + //todo:添加自己的请求字段 + <% + if(Table.Name.Contains("Tbl") && (!Table.Name.Contains("Dtbl")) && this.HeaderModel){ + var dtblName = Table.Name.Replace("Tbl","Dtbl"); //明细表的表名 + %> + public ListReq> <%=dtblName%>Reqs { get; set; } + <% } %> + } +} \ No newline at end of file diff --git a/CodeSmith/CSharp/Web/Request.cst b/CodeSmith/CSharp/ApiGenerate/Request.cst similarity index 93% rename from CodeSmith/CSharp/Web/Request.cst rename to CodeSmith/CSharp/ApiGenerate/Request.cst index 734ad9ad6585c35b2fc8795920237c4225cc04e5..c7b9cf2f20d9218bdf9965f25f2b7d6cc355540d 100644 --- a/CodeSmith/CSharp/Web/Request.cst +++ b/CodeSmith/CSharp/ApiGenerate/Request.cst @@ -12,6 +12,6 @@ namespace OpenAuth.App.Request { public class Query<%=ModuleName%>ListReq : PageReq { - public string orgId { get; set; } + //todo:添加自己的请求字段 } } \ No newline at end of file diff --git a/CodeSmith/CSharp/Entity.cst b/CodeSmith/CSharp/Entity.cst deleted file mode 100644 index 90952b98f534b1617177f02436aa3772585ab47a..0000000000000000000000000000000000000000 --- a/CodeSmith/CSharp/Entity.cst +++ /dev/null @@ -1,309 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="None" %> - -<%@ Assembly Name="SchemaExplorer" %> -<%@ Assembly Name="CodeSmith.CustomProperties" %> - -<%@ Assembly Name="Mono.Cecil" Path="..\Common" %> -<%@ Assembly Name="ICSharpCode.NRefactory" Path="..\Common" %> -<%@ Assembly Name="ICSharpCode.NRefactory.CSharp" Path="..\Common" %> - -<%@ Assembly Src="Internal\Model.cs" %> -<%@ Assembly Src="Internal\Extensions.cs" %> -<%@ Assembly Src="Internal\Generator.cs" %> -<%@ Assembly Src="Internal\Parser.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.IO" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> - -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="SourceDatabase" - Type="SchemaExplorer.DatabaseSchema" - Category="1.Database" - OnChanged="OnSourceDatabaseChanged" - Description="The source database." %> - -<%@ Property Name="ContextNamespace" - Type="System.String" - Category="2.Class" - OnChanged="OnContextNamespaceChanged" - Description="The namespace to use for the data context class files."%> -<%@ Property Name="EntityNamespace" - Type="System.String" - Category="2.Class" - Description="The namespace to use for the entity class files."%> -<%@ Property Name="MappingNamespace" - Type="System.String" - Category="2.Class" - Description="The namespace to use for the mapping class files."%> - -<%@ Property Name="ContextDirectory" - Category="3.Output" - Type="System.String" - Default=".\" - Optional="True" - Description="The folder to save the generated context files." - Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> -<%@ Property Name="EntityDirectory" - Category="3.Output" - Type="System.String" - Default=".\Entities" - Optional="True" - Description="The folder to save the generated entity files." - Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> -<%@ Property Name="MappingDirectory" - Category="3.Output" - Type="System.String" - Default=".\Mapping" - Optional="True" - Description="The folder to save the generated mapping files." - Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> - -<%@ Property Name="TableNaming" - Type="SchemaMapper.TableNaming" - Category="4.Hints" - Default="Singular" - Description="Provide generator a hint as to how the tables are named." %> -<%@ Property Name="EntityNaming" - Type="SchemaMapper.EntityNaming" - Category="4.Hints" - Default="Singular" - Description="Tell generator how the entities are to be named." %> -<%@ Property Name="RelationshipNaming" - Type="SchemaMapper.RelationshipNaming" - Category="4.Hints" - Default="Plural" - Description="Tell generator how the relationships are to be named." %> -<%@ Property Name="ContextNaming" - Type="SchemaMapper.ContextNaming" - Category="4.Hints" - Default="Plural" - Description="Tell generator how the context properties are to be named." %> - -<%@ Property Name="IgnoreList" - Type="CodeSmith.CustomProperties.StringCollection" - Category="5.Customization" - Default="sysdiagrams$" - Optional="True" - Description="List of regular expressions to ignore tables, views and commands when generating mapping." %> -<%@ Property Name="InclusionMode" - Type="Boolean" - Category="5.Customization" - Default="False" - Optional="True" - Description="Change the IgnoreList to be a list of table to include instead of ignore." %> -<%@ Property Name="CleanExpressions" - Type="CodeSmith.CustomProperties.StringCollection" - Category="5.Customization" - Default="^(sp|tbl|udf|vw)_" - Optional="True" - Description="List of regular expressions to clean table, view and column names." %> -<%@ Property Name="InterfaceMode" - Type="Boolean" - Category="5.Customization" - Default="False" - Optional="True" - Description="Use interfaces for DbContext." %> - - - -<%@ Register Name="ContextGeneratedClass" - Template="Internal\Context.Generated.cst" - MergeProperties="False" %> - -<%@ Register Name="EntityGeneratedClass" - Template="Internal\Entity.Generated.cst" - MergeProperties="False" %> - -<%@ Register Name="MappingGeneratedClass" - Template="Internal\Mapping.Generated.cst" - MergeProperties="False" %> - -Generating Entities ... -<% Generate(); %> - - \ No newline at end of file diff --git a/CodeSmith/CSharp/Internal/Context.Generated.cst b/CodeSmith/CSharp/Internal/Context.Generated.cst index a314e2faf3be47b106442eb3c623fd57e3b2f53a..1a61f6e39cc0e13a11551d911e2a718f03fd58a2 100644 --- a/CodeSmith/CSharp/Internal/Context.Generated.cst +++ b/CodeSmith/CSharp/Internal/Context.Generated.cst @@ -1,7 +1,7 @@ <%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> -<%@ Assembly Src="Model.cs" %> -<%@ Assembly Src="Extensions.cs" %> +<%@ Assembly Src="../Internal/Model.cs" %> +<%@ Assembly Src="../Internal/Extensions.cs" %> <%@ Import Namespace="System.Collections.Generic" %> <%@ Import Namespace="System.Linq" %> @@ -10,87 +10,72 @@ <%@ Import Namespace="SchemaMapper" %> -<%@ Property Name="EntityContext" Type="SchemaMapper.EntityContext" %> +<%@ Property Name="WholeDb" +Type="System.Boolean" +Category="1.Database" +Default="true" +Description="是否为整个数据库" %> + +<%@ Property Name="SourceDatabase" + Type="SchemaExplorer.DatabaseSchema" + Category="1.Database" + Description="The source database." %> + +<%@ Property Name="SourceTables" +Type="SchemaExplorer.TableSchemaCollection" +Category="1.Database" Description="选择部分表" %> <%@ Property Name="ContextNamespace" Type="System.String" %> <%@ Property Name="EntityNamespace" Type="System.String" %> -<%@ Property Name="MappingNamespace" Type="System.String" %> -<%@ Property Name="InterfaceMode" Type="Boolean" Default="False" Optional="True" %> + -//------------------------------------------------------------------------------ -// -// This code was generated by a CodeSmith Template. -// -// DO NOT MODIFY contents of this file. Changes to this -// file will be lost if the code is regenerated. -// -//------------------------------------------------------------------------------ -using System; -using System.Data.Entity; -using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; using <%= EntityNamespace %>; -using <%= MappingNamespace %>; namespace <%= ContextNamespace %> { -<% if (InterfaceMode) { %> - public interface IDbContext : IDisposable - { - System.Data.Entity.Database Database { get; } - System.Data.Entity.Infrastructure.DbChangeTracker ChangeTracker { get; } - System.Data.Entity.Infrastructure.DbContextConfiguration Configuration { get; } - - System.Data.Entity.Infrastructure.DbEntityEntry Entry(object entity); - System.Data.Entity.Infrastructure.DbEntityEntry Entry(TEntity entity) where TEntity : class; - - IEnumerable GetValidationErrors(); - - System.Data.Entity.DbSet Set(Type entityType); - System.Data.Entity.IDbSet Set() where TEntity : class; - - int SaveChanges(); - } - - public partial interface I<%= EntityContext.ClassName.ToSafeName() %> : IDbContext - { -<% foreach(var p in EntityContext.Entities) { %> - System.Data.Entity.IDbSet<<%= EntityNamespace %>.<%= p.ClassName.ToSafeName() %>> <%= p.ContextName.ToSafeName() %> { get; set; } -<% } // foreach %> - } - -<% } // if interface %> - public partial class <%= EntityContext.ClassName.ToSafeName() %>: DbContext<%= InterfaceMode ? ", I" + EntityContext.ClassName.ToSafeName() : string.Empty %> - { - static <%= EntityContext.ClassName.ToSafeName() %>() - { - Database.SetInitializer< <%= EntityContext.ClassName.ToSafeName() %>>(null); + <% + string dbContextName; + if(WholeDb){ + dbContextName = SourceDatabase.Name.ToSafeName(); } - public <%= EntityContext.ClassName.ToSafeName() %>() - :base("Name=<%= EntityContext.ClassName.ToSafeName() %>") - { } - - public <%= EntityContext.ClassName.ToSafeName() %>(string nameOrConnectionString) - : base(nameOrConnectionString) - { } - -<% foreach(var p in EntityContext.Entities) { %> - public System.Data.Entity.<%= InterfaceMode ? "I" : "" %>DbSet<<%= p.ClassName.ToSafeName() %>> <%= p.ContextName.ToSafeName() %> { get; set; } -<% } // foreach %> - - protected override void OnModelCreating(DbModelBuilder modelBuilder) + else{ + dbContextName = SourceTables.First().Database.Name.ToSafeName(); + } + dbContextName = StringUtil.ToPascalCase(dbContextName); + Response.WriteLine(" public partial class "+ dbContextName +"Context: DbContext"); + + %> + { + + protected override void OnModelCreating(ModelBuilder modelBuilder) { -<% foreach(var p in EntityContext.Entities) { %> - modelBuilder.Configurations.Add(new <%= p.MappingName.ToSafeName() %>()); -<% } // foreach %> - - // InitializeMapping(modelBuilder); + //当主键为联合主键时,需要把这里的内容拷贝到对应的位置 + <% + TableSchemaCollection tables; + if(WholeDb){ + tables = SourceDatabase.Tables; + } + else{ + tables = SourceTables; + } + + foreach(TableSchema table in tables) + { + if(table.PrimaryKeys.Count <=1) continue; + var keys = string.Join(",", table.Columns.Where(u=>u.IsPrimaryKeyMember==true) + .Select(u =>"c."+u.Name)); + Response.WriteLine(" modelBuilder.Entity<"+table.Name+">()"); + Response.WriteLine(" .HasKey(c => new { "+keys+" });"); + } + %> } -<% if (InterfaceMode) { %> - System.Data.Entity.IDbSet IDbContext.Set() + <% + foreach(TableSchema table in tables) { - return base.Set(); + Response.WriteLine(" public virtual DbSet<"+table.Name+"> "+StringUtil.ToPascalCase(StringUtil.ToPlural(table.Name))+" { get; set; }"); } -<% } // if interface %> + %> } } \ No newline at end of file diff --git a/CodeSmith/CSharp/Internal/Entity.Generated.cst b/CodeSmith/CSharp/Internal/Entity.Generated.cst index 632d50d91e29a36be54718dc75025f6c9789428d..ba085a781bf7a984f70bbc06e6eab8b769e84ec6 100644 --- a/CodeSmith/CSharp/Internal/Entity.Generated.cst +++ b/CodeSmith/CSharp/Internal/Entity.Generated.cst @@ -1,7 +1,7 @@ <%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> -<%@ Assembly Src="Model.cs" %> -<%@ Assembly Src="Extensions.cs" %> +<%@ Assembly Src="../Internal/Model.cs" %> +<%@ Assembly Src="../Internal/Extensions.cs" %> <%@ Import Namespace="System.Collections.Generic" %> <%@ Import Namespace="System.Linq" %> @@ -10,8 +10,8 @@ <%@ Import Namespace="SchemaMapper" %> -<%@ Property Name="Entity" - Type="SchemaMapper.Entity" %> +<%@ Property Name="Table" + Type="SchemaExplorer.TableSchema" %> <%@ Property Name="EntityNamespace" Type="System.String" %> @@ -26,47 +26,50 @@ //------------------------------------------------------------------------------ using System; using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; using System.Text; +using OpenAuth.Repository.Core; namespace <%= EntityNamespace %> { /// - /// <%= Entity.Description %> + /// <%= Table.Description %> /// - public partial class <%= Entity.ClassName.ToSafeName() %> : Entity + [Table("<%= Table.Name%>")] + public partial class <%= Table.Name %> : Entity { - public <%= Entity.ClassName.ToSafeName() %>() + public <%= Table.Name %>() { -<% foreach(var p in Entity.Properties) { - if(p.IsPrimaryKey ==true) continue; - string type = p.SystemType.ToNullableType(p.IsNullable == true); - if(type =="int" || type=="decimal") - Response.WriteLine(" this."+p.PropertyName.ToSafeName()+"= 0;"); - else if(type =="string") - Response.WriteLine(" this."+p.PropertyName.ToSafeName()+"= string.Empty;"); - else if(type.ToLower().Contains("datetime")) - Response.WriteLine(" this."+p.PropertyName.ToSafeName()+"= DateTime.Now;"); - } // foreach %> -<% foreach(var r in Entity.Relationships.Where(e => e.ThisCardinality == Cardinality.Many)) { %> - <%= r.ThisPropertyName.ToSafeName() %> = new List<<%= r.OtherEntity.ToSafeName() %>>(); -<% } // foreach %> + <% foreach(ColumnSchema p in Table.Columns) { + if(p.IsPrimaryKeyMember) continue; + string type = p.SystemType.ToNullableType(p.AllowDBNull == true); + if(type =="int" || type=="decimal") + Response.WriteLine(" this."+p.Name+"= 0;"); + else if(type =="string") + Response.WriteLine(" this."+p.Name+"= string.Empty;"); + else if(type.ToLower().Contains("datetime")) + Response.WriteLine(" this."+p.Name+"= DateTime.Now;"); + } // foreach %> } -<% foreach(var p in Entity.Properties) { - if(p.IsPrimaryKey ==true) continue; - %> + + <% + foreach(ColumnSchema p in Table.Columns) { + if(p.IsPrimaryKeyMember) continue; + %> /// - /// <%=p.Description %> - /// - public <%= p.SystemType.ToNullableType(p.IsNullable == true) %> <%= p.PropertyName.ToSafeName() %> { get; set; } -<% } // foreach %> - -<% foreach(var r in Entity.Relationships) { %> -<% if(r.ThisCardinality == Cardinality.Many) { %> - public virtual ICollection<<%= r.OtherEntity.ToSafeName() %>> <%= r.ThisPropertyName.ToSafeName() %> { get; set; } -<% } else { %> - public virtual <%= r.OtherEntity.ToSafeName() %> <%= r.ThisPropertyName.ToSafeName() %> { get; set; } -<% } %> -<% } // foreach %> + /// <%=p.Description %> + /// + [Description("<%=p.Description%>")] + <%if(p.Name.LastIndexOf("Id") != -1){%> + [Browsable(false)] + <%}%> + <%if(p.DataType == DbType.Byte){%> + public bool <%= p.Name%> { get; set; } + <%}else{%> + public <%= p.SystemType.ToNullableType(p.AllowDBNull == true) %> <%= p.Name%> { get; set; } + <%}%> + <% } // foreach %> } } \ No newline at end of file diff --git a/CodeSmith/CSharp/Internal/Generator.cs b/CodeSmith/CSharp/Internal/Generator.cs index c3ecd1c6e56b80693a214222e3e878deda50282f..a5e660e030975435f008efd476a590291ba7ab13 100644 --- a/CodeSmith/CSharp/Internal/Generator.cs +++ b/CodeSmith/CSharp/Internal/Generator.cs @@ -205,6 +205,27 @@ namespace SchemaMapper get { return _settings; } } + //按表信息创建DbContext + public EntityContext Generate(TableSchema tableSchema) + { + // only DeepLoad when in ignore mode + tableSchema.DeepLoad = !Settings.InclusionMode; + + var entityContext = new EntityContext(); + entityContext.DatabaseName = tableSchema.Database.Name; + + string dataContextName = StringUtil.ToPascalCase(tableSchema.Database.Name) + "Context"; + dataContextName = _namer.UniqueClassName(dataContextName); + + entityContext.ClassName = dataContextName; + + GetEntity(entityContext, tableSchema); + + + return entityContext; + } + + //按数据库连接信息创建DbContext public EntityContext Generate(DatabaseSchema databaseSchema) { // only DeepLoad when in ignore mode @@ -240,8 +261,8 @@ namespace SchemaMapper return entityContext; } - - private Entity GetEntity(EntityContext entityContext, TableSchema tableSchema, bool processRelationships = true, bool processMethods = true) + //根据DbContext和tableSchema获取实体 + public Entity GetEntity(EntityContext entityContext, TableSchema tableSchema, bool processRelationships = true, bool processMethods = true) { string key = tableSchema.FullName; @@ -363,7 +384,7 @@ namespace SchemaMapper if (Settings.IsIgnored(tableKey.ForeignKeyTable.FullName) || Settings.IsIgnored(tableKey.PrimaryKeyTable.FullName)) { - Debug.WriteLine("Skipping relationship '{0}' because table '{1}' or '{2}' is ignored.", + Debug.WriteLine("Skipping relationship '{0}' because table '{1}' or '{2}' is ignored.", tableKey.FullName, tableKey.ForeignKeyTable.FullName, tableKey.PrimaryKeyTable.FullName); continue; diff --git a/CodeSmith/CSharp/Internal/Mapping.Generated.cst b/CodeSmith/CSharp/Internal/Mapping.Generated.cst deleted file mode 100644 index 8d59b7fdd5a524cf011400680f1d6c977562eca8..0000000000000000000000000000000000000000 --- a/CodeSmith/CSharp/Internal/Mapping.Generated.cst +++ /dev/null @@ -1,294 +0,0 @@ -<%@ Template Language="C#" TargetLanguage="C#" Debug="True" Encoding="UTF-8" %> - -<%@ Assembly Src="Model.cs" %> -<%@ Assembly Src="Extensions.cs" %> - -<%@ Import Namespace="System.Collections.Generic" %> -<%@ Import Namespace="System.Linq" %> -<%@ Import Namespace="System.Text" %> -<%@ Import Namespace="System.Text.RegularExpressions" %> - -<%@ Import Namespace="SchemaMapper" %> - -<%@ Property Name="Entity" Type="SchemaMapper.Entity" %> -<%@ Property Name="ContextNamespace" Type="System.String" %> -<%@ Property Name="EntityNamespace" Type="System.String" %> -<%@ Property Name="MappingNamespace" Type="System.String" %> -//------------------------------------------------------------------------------ -// -// This code was generated by a CodeSmith Template. -// -// DO NOT MODIFY contents of this file. Changes to this -// file will be lost if the code is regenerated. -// -//------------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace <%= MappingNamespace %> -{ - public partial class <%= Entity.MappingName.ToSafeName() %> - : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<<%= EntityNamespace %>.<%= Entity.ClassName.ToSafeName() %>> - { - public <%= Entity.MappingName.ToSafeName() %>() - { - // table -<% if (string.IsNullOrEmpty(Entity.TableSchema)) { %> - ToTable("<%= Entity.TableName %>"); -<% } else { %> - ToTable("<%= Entity.TableName %>", "<%= Entity.TableSchema %>"); -<% } %> - - // keys -<% if (Entity.Properties.PrimaryKeys.Count() > 0) { %> - HasKey(t => <%= KeyExpression(Entity.Properties.PrimaryKeys, "t") %>); -<% } %> - - // Properties -<% -foreach(var p in Entity.Properties) -{ - Response.Write(PropertyExpression(p)); -} -%> - - // Relationships -<% -foreach(var r in Entity.Relationships.Where(e => e.IsMapped)) -{ - if (r.IsManyToMany) - Response.Write(ManyToManyExpression(r)); - else - Response.Write(RelationshipExpression(r)); -} -%> - } - } -} - - \ No newline at end of file diff --git a/CodeSmith/CSharp/Web/Controller.cst b/CodeSmith/CSharp/Web/Controller.cst index 3858e30dcbd84885a6e4fdda6eedf689d26345c1..307d1fe01ac1638d340351eba626f678d08b913e 100644 --- a/CodeSmith/CSharp/Web/Controller.cst +++ b/CodeSmith/CSharp/Web/Controller.cst @@ -10,34 +10,39 @@ Description: Create a list of properties from a database table <%@ Import Namespace="SchemaExplorer" %> using System; -using System.Web.Http; -using System.Web.Mvc; +using System.Collections.Generic; +using System.Linq; using Infrastructure; +using Microsoft.AspNetCore.Mvc; using OpenAuth.App; +using OpenAuth.App.Interface; using OpenAuth.App.Request; -using OpenAuth.App.Response; -using OpenAuth.Mvc.Models; +using OpenAuth.Repository.Domain; namespace OpenAuth.Mvc.Controllers { public class <%=ModuleName%>sController : BaseController { - public <%=ModuleName%>App App { get; set; } + private readonly <%=ModuleName%>App _app; - // - [Authenticate] + public <%=ModuleName%>sController(<%=ModuleName%>App app, IAuth auth) : base(auth) + { + _app = app; + } + + //主页 public ActionResult Index() { return View(); } //添加或修改 - [System.Web.Mvc.HttpPost] + [HttpPost] public string Add(<%=ModuleName%> obj) { try { - App.Add(obj); + _app.Add(obj); } catch (Exception ex) @@ -49,12 +54,12 @@ namespace OpenAuth.Mvc.Controllers } //添加或修改 - [System.Web.Mvc.HttpPost] + [HttpPost] public string Update(<%=ModuleName%> obj) { try { - App.Update(obj); + _app.Update(obj); } catch (Exception ex) @@ -68,17 +73,17 @@ namespace OpenAuth.Mvc.Controllers /// /// 加载列表 /// - public string Load([FromUri]Query<%=ModuleName%>ListReq request) + public string Load([FromQuery]Query<%=ModuleName%>ListReq request) { - return JsonHelper.Instance.Serialize(App.Load(request)); + return JsonHelper.Instance.Serialize(_app.Load(request)); } - [System.Web.Mvc.HttpPost] + [HttpPost] public string Delete(string[] ids) { try { - App.Delete(ids); + _app.Delete(ids); } catch (Exception e) { diff --git a/CodeSmith/CSharp/Web/Index.cshtml.cst b/CodeSmith/CSharp/Web/Index.cshtml.cst index 4ff073c3c1c42a2313930bf8f69ba40af849c445..94c8e495e72c383b8a5c8d45a3d3e16facdca5a4 100644 --- a/CodeSmith/CSharp/Web/Index.cshtml.cst +++ b/CodeSmith/CSharp/Web/Index.cshtml.cst @@ -21,45 +21,34 @@ Description="连接的数据库" %> -
-
-
    -
-
-
- - - - - <% foreach (ColumnSchema column in this.SourceTable.Columns) {%> - - <% }%> - - - -
<%=Tools.GetDescription(column)%>
-
-
+
+ + +
- + +
- + diff --git a/CodeSmith/CSharp/Web/index.js.cst b/CodeSmith/CSharp/Web/index.js.cst index 89f5942b2d3c3b66eb962cef37f1cbdbdbedf2b5..85c54192687a6f320d6d87cc3e23c4c0e3722d9c 100644 --- a/CodeSmith/CSharp/Web/index.js.cst +++ b/CodeSmith/CSharp/Web/index.js.cst @@ -13,73 +13,63 @@ Author: yubaolee layui.config({ base: "/js/" -}).use(['form','vue', 'ztree', 'layer', 'jquery', 'table','droptree','openauth', 'utils'], function () { +}).use(['form', 'vue', 'ztree', 'layer', 'jquery', 'table', 'droptree', 'openauth', 'utils'], function () { var form = layui.form, layer = layui.layer, $ = layui.jquery; var table = layui.table; var openauth = layui.openauth; var toplayer = (top == undefined || top.layer === undefined) ? layer : top.layer; //顶层的LAYER - layui.droptree("/UserSession/GetOrgs", "#Organizations", "#OrganizationIds"); $("#menus").loadMenus("<%=ModuleName%>"); - + + + //加载表头 + $.getJSON('/<%=ModuleName%>s/Load', + { page: 1, limit: 1 }, + function (data) { + var columns = data.columnHeaders.map(function (e) { + return { + field: e.Key, + title: e.Description + }; + }); + columns.unshift({ + type: 'checkbox', + fixed: 'left' + }); + table.render({ + elem: '#mainList', + page: true, + url: '/<%=ModuleName%>s/Load', + cols: [columns] + , response: { + statusCode: 200 //规定成功的状态码,默认:0 + } + }); + }); + + //主列表加载,可反复调用进行刷新 - var config= {}; //table的参数,如搜索key,点击tree的id - var mainList = function (options) { + var config = {}; //table的参数,如搜索key,点击tree的id + var mainList = function(options) { if (options != undefined) { $.extend(config, options); } - table.reload('mainList', { - url: '/<%=ModuleName%>s/Load', - where: config - }); - } - //左边树状机构列表 - var ztree = function () { - var url = '/UserSession/GetOrgs'; - var zTreeObj; - var setting = { - view: { selectedMulti: false }, - data: { - key: { - name: 'Name', - title: 'Name' - }, - simpleData: { - enable: true, - idKey: 'Id', - pIdKey: 'ParentId', - rootPId: 'null' - } - }, - callback: { - onClick: function (event, treeId, treeNode) { - mainList({ orgId: treeNode.Id }); - } - } - }; - var load = function () { - $.getJSON(url, function (json) { - zTreeObj = $.fn.zTree.init($("#tree"), setting); - var newNode = { Name: "根节点", Id: null, ParentId: "" }; - json.push(newNode); - zTreeObj.addNodes(null, json); - mainList({ orgId: "" }); - zTreeObj.expandAll(true); + table.reload('mainList', + { + url: '/<%=ModuleName%>s/Load', + where: config + , response: { + statusCode: 200 //规定成功的状态码,默认:0 + } }); - }; - load(); - return { - reload: load - } - }(); - + }; + mainList(); + //添加(编辑)对话框 - var editDlg = function() { - var vm = new Vue({ - el: "#formEdit" - }); + var editDlg = function () { + var vm; var update = false; //是否为更新 var show = function (data) { var title = update ? "编辑信息" : "添加"; @@ -88,21 +78,46 @@ layui.config({ area: ["500px", "400px"], type: 1, content: $('#divEdit'), - success: function() { - vm.$set('$data', data); + success: function () { + if(vm == undefined){ + vm = new Vue({ + el: "#formEdit", + data(){ + return { + tmp:data //使用一个tmp封装一下,后面可以直接用vm.tmp赋值 + } + }, + watch:{ + tmp(val){ + this.$nextTick(function () { + form.render(); //刷新select等 + layui.droptree("/Applications/GetList", "#AppName", "#AppId", false); + + }) + } + }, + mounted(){ + form.render(); + layui.droptree("/Applications/GetList", "#AppName", "#AppId", false); + + } + }); + }else{ + vm.tmp = Object.assign({}, vm.tmp,data) + } }, end: mainList }); var url = "/<%=ModuleName%>s/Add"; if (update) { - url = "/<%=ModuleName%>s/Update"; + url = "/<%=ModuleName%>s/Update"; } //提交数据 form.on('submit(formSubmit)', - function(data) { + function (data) { $.post(url, data.field, - function(data) { + function (data) { layer.msg(data.Message); }, "json"); @@ -110,25 +125,25 @@ layui.config({ }); } return { - add: function() { //弹出添加 + add: function () { //弹出添加 update = false; show({ Id: '' }); }, - update: function(data) { //弹出编辑框 + update: function (data) { //弹出编辑框 update = true; show(data); } }; }(); - + //监听表格内部按钮 table.on('tool(list)', function (obj) { var data = obj.data; if (obj.event === 'detail') { //查看 layer.msg('ID:' + data.Id + ' 的查看操作'); - } + } }); @@ -144,20 +159,20 @@ layui.config({ , btnAdd: function () { //添加 editDlg.add(); } - , btnEdit: function () { //编辑 - var checkStatus = table.checkStatus('mainList') - , data = checkStatus.data; - if (data.length != 1) { - layer.msg("请选择编辑的行,且同时只能编辑一行"); - return; - } - editDlg.update(data[0]); - } + , btnEdit: function () { //编辑 + var checkStatus = table.checkStatus('mainList') + , data = checkStatus.data; + if (data.length != 1) { + layer.msg("请选择编辑的行,且同时只能编辑一行"); + return; + } + editDlg.update(data[0]); + } , search: function () { //搜索 mainList({ key: $('#key').val() }); } - , btnRefresh: function() { + , btnRefresh: function () { mainList(); } }; @@ -168,4 +183,4 @@ layui.config({ }); //监听页面主按钮操作 end -}) \ No newline at end of file +}) diff --git a/CodeSmith/CSharp/WebGenerate.cst b/CodeSmith/CSharp/WebGenerate.cst index 026d729078dbe6d31f4732171a6712ab71f62a0c..01b04d5a3fe3b2db956bbad5837c04287e2316fa 100644 --- a/CodeSmith/CSharp/WebGenerate.cst +++ b/CodeSmith/CSharp/WebGenerate.cst @@ -1,4 +1,8 @@ -<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="None" %> +<%-- +Author: yubaolee +Description: 用于生成OpenAuth.Core开源版前端Web界面,包括mvc controller/csthml/js +--%> +<%@ Template Language="C#" TargetLanguage="Text" Debug="True" OutputType="None" %> <%@ Assembly Name="SchemaExplorer" %> <%@ Assembly Name="CodeSmith.CustomProperties" %> @@ -36,12 +40,6 @@ OnChanged="OnSourceDatabaseChanged"%> Description="代码生成路径" Editor="System.Windows.Forms.Design.FolderNameEditor, System.Design, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %> -<%@ Register Name="ApplicationGenerateClass" - Template="Web\Application.cst" - MergeProperties="False" %> -<%@ Register Name="RequestGenerateClass" - Template="Web\Request.cst" - MergeProperties="False" %> <%@ Register Name="HtmlGenerateClass" Template="Web\Index.cshtml.cst" MergeProperties="False" %> @@ -61,8 +59,6 @@ Generating Entities ... string outputDirectory = Path.GetFullPath(directory); CreateControllerClass(); - CreateApplicationClass(); - CreateReqClass(); CreateHtmlClass(); CreateJSClass(); @@ -84,23 +80,7 @@ Generating Entities ... string rootDirectory = Path.GetFullPath(directory); - string generatedFile = Path.GetFullPath(directory) + "/controllers/"+ ModuleName + "sController.cs"; - - generatedClass.ModuleName = ModuleName; - - Response.WriteLine(generatedFile); - generatedClass.RenderToFile(generatedFile, generatedFile, true); - } - - //创建APP层,如UserManagerApp.cs - public void CreateApplicationClass() - { - ApplicationGenerateClass generatedClass = this.Create(); - this.CopyPropertiesTo(generatedClass); - - string rootDirectory = Path.GetFullPath(directory); - - string generatedFile = Path.GetFullPath(directory) + "/APP/"+ ModuleName + "App.cs"; + string generatedFile = Path.GetFullPath(directory) + "/Controllers/"+ ModuleName + "sController.cs"; generatedClass.ModuleName = ModuleName; @@ -108,22 +88,6 @@ Generating Entities ... generatedClass.RenderToFile(generatedFile, generatedFile, true); } - - //创建请求参数,如QueryUserListReq.cs - public void CreateReqClass() - { - RequestGenerateClass generatedClass = this.Create(); - this.CopyPropertiesTo(generatedClass); - - string rootDirectory = Path.GetFullPath(directory); - - string generatedFile = Path.GetFullPath(directory) + "/APP/Request/Query"+ ModuleName + "ListReq.cs"; - - generatedClass.ModuleName = ModuleName; - - Response.WriteLine(generatedFile); - generatedClass.RenderToFile(generatedFile, generatedFile, true); - } //创建视图,如views/Users/index.html public void CreateHtmlClass() @@ -131,7 +95,7 @@ Generating Entities ... HtmlGenerateClass generatedClass = this.Create(); this.CopyPropertiesTo(generatedClass); - string generatedFile = Path.GetFullPath(directory) + "/views/"+ModuleName+"s/index.cshtml"; + string generatedFile = Path.GetFullPath(directory) + "/Views/"+ModuleName+"s/index.cshtml"; generatedClass.ModuleName = ModuleName; generatedClass.SourceTable = SourceTable; @@ -158,7 +122,7 @@ Generating Entities ... //更换数据源时,改变ModuleName private void OnSourceDatabaseChanged(object sender, EventArgs e) { - if (SourceTable == null || ModuleName != null) + if (SourceTable == null) return; ModuleName = SourceTable.Name; } diff --git a/Infrastructure/AutoMapperExt.cs b/Infrastructure/AutoMapperExt.cs index b0d99bf6cadb41f78d4719d7096ce482196a2d64..13fc941620a398e0876334c617f296880487c2d5 100644 --- a/Infrastructure/AutoMapperExt.cs +++ b/Infrastructure/AutoMapperExt.cs @@ -12,10 +12,10 @@ // // *********************************************************************** +using System; using AutoMapper; using System.Collections; using System.Collections.Generic; -using System.Data; namespace Infrastructure { @@ -27,8 +27,10 @@ namespace Infrastructure public static T MapTo(this object obj) { if (obj == null) return default(T); - Mapper.CreateMap(obj.GetType(), typeof(T)); - return Mapper.Map(obj); + + var config = new MapperConfiguration(cfg=>cfg.CreateMap(obj.GetType(),typeof(T))); + var mapper = config.CreateMapper(); + return mapper.Map(obj); } /// @@ -36,13 +38,11 @@ namespace Infrastructure /// public static List MapToList(this IEnumerable source) { - foreach (var first in source) - { - var type = first.GetType(); - Mapper.CreateMap(type, typeof(TDestination)); - break; - } - return Mapper.Map>(source); + Type sourceType = source.GetType().GetGenericArguments()[0]; //获取枚举的成员类型 + var config = new MapperConfiguration(cfg => cfg.CreateMap(sourceType, typeof(TDestination))); + var mapper = config.CreateMapper(); + + return mapper.Map>(source); } /// @@ -50,9 +50,10 @@ namespace Infrastructure /// public static List MapToList(this IEnumerable source) { - //IEnumerable 类型需要创建元素的映射 - Mapper.CreateMap(); - return Mapper.Map>(source); + var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination))); + var mapper = config.CreateMapper(); + + return mapper.Map>(source); } /// @@ -63,18 +64,11 @@ namespace Infrastructure where TDestination : class { if (source == null) return destination; - Mapper.CreateMap(); - return Mapper.Map(source, destination); - } - /// - /// DataReader映射 - /// - public static IEnumerable DataReaderMapTo(this IDataReader reader) - { - Mapper.Reset(); - Mapper.CreateMap>(); - return Mapper.Map>(reader); + var config = new MapperConfiguration(cfg => cfg.CreateMap(typeof(TSource), typeof(TDestination))); + var mapper = config.CreateMapper(); + return mapper.Map(source); } + } } \ No newline at end of file diff --git a/Infrastructure/Cache/CacheContext.cs b/Infrastructure/Cache/CacheContext.cs index f063e6fecd7366a046fcb711ed0472241ec7dc80..a7670f7096e5a64c41158f3525b3e74793a95b17 100644 --- a/Infrastructure/Cache/CacheContext.cs +++ b/Infrastructure/Cache/CacheContext.cs @@ -10,17 +10,22 @@ // *********************************************************************** using System; -using System.Web; +using Microsoft.Extensions.Caching.Memory; namespace Infrastructure.Cache { public class CacheContext : ICacheContext { - private readonly System.Web.Caching.Cache _objCache = HttpRuntime.Cache; + private IMemoryCache _objCache; + + public CacheContext(IMemoryCache objCache) + { + _objCache = objCache; + } + public override T Get(string key) { - System.Web.Caching.Cache objCache = HttpRuntime.Cache; - return (T) objCache[key]; + return _objCache.Get(key); } public override bool Set(string key, T t, DateTime expire) @@ -30,8 +35,10 @@ namespace Infrastructure.Cache { Remove(key); } - - _objCache.Insert(key, t, null, expire, System.Web.Caching.Cache.NoSlidingExpiration); + + _objCache.Set(key, t, new MemoryCacheEntryOptions() + .SetAbsoluteExpiration(expire)); //绝对过期时间 + return true; } diff --git a/Infrastructure/Cache/CacheProvider.cs b/Infrastructure/Cache/CacheProvider.cs deleted file mode 100644 index 18c0255b754dd0aaf3d029f0394b2bc9142e9346..0000000000000000000000000000000000000000 --- a/Infrastructure/Cache/CacheProvider.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Globalization; - -namespace Infrastructure.Cache -{ - /// - /// 缓存工厂 - /// 李玉宝新增于2016-11-09 9:42:52 - /// - public abstract class CacheProvider : IDisposable - { - /// - /// 缓存组件 - /// - public ICacheContext CacheContext { get; private set; } - - /// - /// 动态设置缓存对象的新实例 - /// - /// 缓存实例对象 - public void SetCacheInstance(ICacheContext cacheContext) - { - //先释放现有的缓存组件 - if (CacheContext != null) - { - CacheContext = null; - } - - //初始化缓存组件新的实例 - CacheContext = cacheContext; - } - - public void SetCacheInstance(Type cacheContextType) - { - if (cacheContextType == null) - { - throw new ArgumentNullException("cacheContextType"); - } - - if (!typeof(ICacheContext).IsAssignableFrom(cacheContextType)) - { - throw new ArgumentException( - string.Format(CultureInfo.CurrentCulture, "该类型 {0} 必须继承自抽象类CacheContext", cacheContextType), - "cacheContextType"); - } - - try - { - CacheContext = Activator.CreateInstance(cacheContextType) as ICacheContext; - } - catch (Exception ex) - { - throw new InvalidOperationException( - String.Format( - CultureInfo.CurrentCulture, - "创建抽象类 CacheContext 的实例 {0} 失败", - cacheContextType), - ex); - } - } - - public void Dispose() - { - - } - } -} diff --git a/Infrastructure/Cache/EnyimMemcachedContext.cs b/Infrastructure/Cache/EnyimMemcachedContext.cs index 64640dcf58292ede9bb9e286f001746df6eedee0..e12fb2e2c78e7562687d6764ae282fd62c963dec 100644 --- a/Infrastructure/Cache/EnyimMemcachedContext.cs +++ b/Infrastructure/Cache/EnyimMemcachedContext.cs @@ -5,11 +5,13 @@ // // Last Modified By : yubaolee // Last Modified On : 06-21-2016 -// Contact : +// Contact : Add services.AddEnyimMemcached(...) +// and app.UseEnyimMemcached() in Startup. // File: EnyimMemcachedContext.cs // *********************************************************************** + using System; using Enyim.Caching; using Enyim.Caching.Memcached; @@ -18,7 +20,12 @@ namespace Infrastructure.Cache { public sealed class EnyimMemcachedContext : ICacheContext { - private static readonly MemcachedClient _memcachedClient = new MemcachedClient(); + private IMemcachedClient _memcachedClient; + + public EnyimMemcachedContext(IMemcachedClient client) + { + _memcachedClient = client; + } public override T Get(string key) { diff --git a/Infrastructure/Cache/ObjCacheProvider.cs b/Infrastructure/Cache/ObjCacheProvider.cs deleted file mode 100644 index 127158fc6d9dc26f4ae4e2c04659676d93b95498..0000000000000000000000000000000000000000 --- a/Infrastructure/Cache/ObjCacheProvider.cs +++ /dev/null @@ -1,49 +0,0 @@ -// *********************************************************************** -// Assembly : Helper -// Author : Administrator -// Created : 12-21-2016 -// -// Last Modified By : Administrator -// Last Modified On : 12-22-2016 -// Contact : -// File: ObjCacheProvider.cs -// *********************************************************************** - - -using System; - -namespace Infrastructure.Cache -{ - /// - /// 缓存工厂实现 - /// 这样做是方便换其他的缓存时(如memcachedContext)只换这一个地方即可 - /// - public class ObjCacheProvider : CacheProvider - { - public ObjCacheProvider() - { - SetCacheInstance(new CacheContext()); - } - - public bool Create(string key, T val, DateTime expire) - { - //设置缓存 - return CacheContext.Set(key, val, expire); - } - - /// - /// 根据失效时间获取缓存 - /// 李玉宝于2016-11-08 16:54:04 - /// - /// The key. - public T GetCache(string key) - { - return CacheContext.Get(key); - } - - public void Remove(string key) - { - CacheContext.Remove(key); - } - } -} \ No newline at end of file diff --git a/Infrastructure/CommonException.cs b/Infrastructure/CommonException.cs new file mode 100644 index 0000000000000000000000000000000000000000..32e0cc242649287d2dd55b7fc93436f6d5412ea7 --- /dev/null +++ b/Infrastructure/CommonException.cs @@ -0,0 +1,22 @@ + +using System; + +namespace Infrastructure +{ + public class CommonException : Exception + { + private int _code; + + public CommonException(string message, int code) + : base(message) + { + this._code = code; + } + + public int Code + { + get { return _code; } + } + + } +} diff --git a/Infrastructure/Const/HtmlElementType.cs b/Infrastructure/Const/HtmlElementType.cs new file mode 100644 index 0000000000000000000000000000000000000000..0389706c148421af320eb26933c18aed280c7207 --- /dev/null +++ b/Infrastructure/Const/HtmlElementType.cs @@ -0,0 +1,26 @@ +namespace Infrastructure.Const +{ + public struct HtmlElementType + { + public const string drop = "drop"; + public const string droplist = "droplist"; + public const string select = "select"; + public const string selectlist = "selectlist"; + public const string checkbox = "checkbox"; + public const string textarea = "textarea"; + public const string thanorequal = "thanorequal"; + public const string lessorequal = "lessorequal"; + + + public const string gt = "gt"; + public const string lt = "lt"; + public const string GT = ">"; + public const string LT = "<"; + public const string like = "like"; + + public const string ThanOrEqual = ">="; + public const string LessOrequal = "<="; + public const string Contains = "in"; + public const string Equal = "="; + } +} \ No newline at end of file diff --git a/Infrastructure/Const/LinqExpressionType.cs b/Infrastructure/Const/LinqExpressionType.cs new file mode 100644 index 0000000000000000000000000000000000000000..ecc32e2daedb66a1c585da917b431b84dbe8301a --- /dev/null +++ b/Infrastructure/Const/LinqExpressionType.cs @@ -0,0 +1,15 @@ +namespace Infrastructure.Const +{ + public enum LinqExpressionType + { + Equal=0,//= + NotEqual=1,//!= + GreaterThan,//> + LessThan,//< + ThanOrEqual,//>= + LessThanOrEqual,//<= + In, + Contains,//Contains + NotContains//NotContains + } +} diff --git a/Infrastructure/Const/QueryOrderBy.cs b/Infrastructure/Const/QueryOrderBy.cs new file mode 100644 index 0000000000000000000000000000000000000000..f40970777d480f512cead1be10a3acc99144b1fe --- /dev/null +++ b/Infrastructure/Const/QueryOrderBy.cs @@ -0,0 +1,8 @@ +namespace Infrastructure.Const +{ + public enum QueryOrderBy + { + Desc=1, + Asc=2 + } +} diff --git a/Infrastructure/Const/ResponseType.cs b/Infrastructure/Const/ResponseType.cs new file mode 100644 index 0000000000000000000000000000000000000000..1552f90c042b243c34f95de4e4d4157e61fe78e5 --- /dev/null +++ b/Infrastructure/Const/ResponseType.cs @@ -0,0 +1,27 @@ +namespace Infrastructure.Const +{ + public enum ResponseType + { + ServerError = 1, + LoginExpiration = 302, + ParametersLack = 303, + TokenExpiration, + PINError, + NoPermissions, + NoRolePermissions, + LoginError, + AccountLocked, + LoginSuccess, + SaveSuccess, + AuditSuccess, + OperSuccess, + RegisterSuccess, + ModifyPwdSuccess, + EidtSuccess, + DelSuccess, + NoKey, + NoKeyDel, + KeyError, + Other + } +} diff --git a/Infrastructure/Const/SqlDbTypeName.cs b/Infrastructure/Const/SqlDbTypeName.cs new file mode 100644 index 0000000000000000000000000000000000000000..2bc9bc723df311354d9d8e2ee23590f20e4877f7 --- /dev/null +++ b/Infrastructure/Const/SqlDbTypeName.cs @@ -0,0 +1,24 @@ +namespace Infrastructure.Const +{ + public struct SqlDbTypeName + { + public const string NVarChar = "nvarchar"; + public const string VarChar = "varchar"; + public const string NChar = "nchar"; + public const string Char = "char"; + public const string Text = "text"; + public const string Int = "int"; + public const string BigInt = "bigint"; + public const string DateTime = "datetime"; + public const string Date = "date"; + public const string SmallDateTime = "smalldatetime"; + public const string SmallDate = "smalldate"; + public const string Float = "float"; + public const string Decimal = "decimal"; + public const string Double = "double"; + public const string Bit = "bit"; + public const string Bool = "bool"; + public const string UniqueIdentifier = "uniqueidentifier"; + + } +} diff --git a/Infrastructure/CookieHelper.cs b/Infrastructure/CookieHelper.cs deleted file mode 100644 index 11ce0b82d8d3ee6c5e89cc759126ac999e2c40ab..0000000000000000000000000000000000000000 --- a/Infrastructure/CookieHelper.cs +++ /dev/null @@ -1,112 +0,0 @@ -// *********************************************************************** -// Assembly : Infrastructure -// Author : Administrator -// Created : 09-22-2015 -// -// *********************************************************************** -// -// Copyright (c) . All rights reserved. -// -// Cookie辅助 -// *********************************************************************** - -using System; -using System.Web; - -namespace Infrastructure -{ - /// - /// Cookie帮助类 - /// - public class CookieHelper - { - /// - /// 写cookie值 - /// - /// 名称 - /// 值 - public static void WriteCookie(string strName, string strValue) - { - HttpCookie cookie = HttpContext.Current.Request.Cookies[strName]; - if (cookie == null) - { - cookie = new HttpCookie(strName); - } - cookie.Value = strValue; - HttpContext.Current.Response.AppendCookie(cookie); - - } - /// - /// 写cookie值 - /// - /// 名称 - /// 值 - /// 过期时间(分钟) - public static void WriteCookie(string strName, string strValue, int expires) - { - HttpCookie cookie = HttpContext.Current.Request.Cookies[strName]; - if (cookie == null) - { - cookie = new HttpCookie(strName); - } - cookie.Value = strValue; - cookie.Expires = DateTime.Now.AddMinutes(expires); - HttpContext.Current.Response.AppendCookie(cookie); - - } - - /// - /// 读cookie值 - /// - /// 名称 - /// cookie值 - public static string GetCookie(string strName) - { - if (HttpContext.Current.Request.Cookies[strName] != null) - { - return HttpContext.Current.Request.Cookies[strName].Value.ToString(); - } - return ""; - } - - /// - /// Get cookie expiry date that was set in the cookie value - /// - /// - /// - public static DateTime GetExpirationDate(HttpCookie cookie) - { - if (String.IsNullOrEmpty(cookie.Value)) - { - return DateTime.MinValue; - } - string strDateTime = cookie.Value.Substring(cookie.Value.IndexOf("|") + 1); - return Convert.ToDateTime(strDateTime); - } - - /// - /// Set cookie value using the token and the expiry date - /// - /// - /// - /// - public static string BuildCookueValue(string value, int minutes) - { - return String.Format("{0}|{1}", value, DateTime.Now.AddMinutes(minutes).ToString()); - } - - /// - /// Reads cookie value from the cookie - /// - /// - /// - public static string GetCookieValue(HttpCookie cookie) - { - if (String.IsNullOrEmpty(cookie.Value)) - { - return cookie.Value; - } - return cookie.Value.Substring(0, cookie.Value.IndexOf("|")); - } - } -} diff --git a/Infrastructure/DynamicLinq.cs b/Infrastructure/DynamicLinq.cs index 1f3c16c949c1fc85f24fd9495a85124fb87df72d..8f334dcf8a28e0cc2c3833e17e814fa7a5f770b5 100644 --- a/Infrastructure/DynamicLinq.cs +++ b/Infrastructure/DynamicLinq.cs @@ -34,43 +34,51 @@ namespace Infrastructure { PropertyInfo property = typeof(T).GetProperty(filterObj.Key); - //组装左边 - Expression left = Expression.Property(param, property); + Expression left = null; //组装左边 //组装右边 Expression right = null; - if (property.PropertyType == typeof(int)) + if (property != null) { - right = Expression.Constant(int.Parse(filterObj.Value)); - } - else if (property.PropertyType == typeof(DateTime)) - { - right = Expression.Constant(DateTime.Parse(filterObj.Value)); - } - else if (property.PropertyType == typeof(string)) - { - right = Expression.Constant((filterObj.Value)); - } - else if (property.PropertyType == typeof(decimal)) - { - right = Expression.Constant(decimal.Parse(filterObj.Value)); - } - else if (property.PropertyType == typeof(Guid)) - { - right = Expression.Constant(Guid.Parse(filterObj.Value)); - } - else if (property.PropertyType == typeof(bool)) - { - right = Expression.Constant(filterObj.Value.Equals("1")); - } - else if (property.PropertyType == typeof(Guid?)) - { - left = Expression.Property(left, "Value"); - right = Expression.Constant(Guid.Parse(filterObj.Value)); + left = Expression.Property(param, property); + if (property.PropertyType == typeof(int)) + { + right = Expression.Constant(int.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(DateTime)) + { + right = Expression.Constant(DateTime.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(string)) + { + right = Expression.Constant(filterObj.Value); + } + else if (property.PropertyType == typeof(decimal)) + { + right = Expression.Constant(decimal.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(Guid)) + { + right = Expression.Constant(Guid.Parse(filterObj.Value)); + } + else if (property.PropertyType == typeof(bool)) + { + right = Expression.Constant(filterObj.Value.Equals("1")); + } + else if (property.PropertyType == typeof(Guid?)) + { + left = Expression.Property(left, "Value"); + right = Expression.Constant(Guid.Parse(filterObj.Value)); + } + else + { + throw new Exception("暂不能解析该Key的类型"); + } } - else + else //如果左边不是属性,直接是值的情况 { - throw new Exception("暂不能解析该Key的类型"); + left = Expression.Constant(filterObj.Key); + right = Expression.Constant(filterObj.Value); } //c.XXX=="XXX" @@ -95,20 +103,33 @@ namespace Infrastructure case "!=": filter = Expression.NotEqual(left, right); break; - - case "like": - filter = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), - Expression.Constant(filterObj.Value)); + case "contains": + filter = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] {typeof(string)}), + Expression.Constant(filterObj.Value)); + break; + case "in": + var lExp = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 + var methodInfo = typeof(List).GetMethod("Contains", + new Type[] {typeof(string)}); //Contains语句 + filter = Expression.Call(lExp, methodInfo, left); break; case "not in": var listExpression = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 - var method = typeof(List).GetMethod("Contains", new Type[] { typeof(string) }); //Contains语句 + var method = typeof(List).GetMethod("Contains", new Type[] {typeof(string)}); //Contains语句 filter = Expression.Not(Expression.Call(listExpression, method, left)); break; - case "in": - var lExp = Expression.Constant(filterObj.Value.Split(',').ToList()); //数组 - var methodInfo = typeof(List).GetMethod("Contains", new Type[] { typeof(string) }); //Contains语句 - filter = Expression.Call(lExp, methodInfo, left); + //交集,使用交集时左值必须时固定的值 + case "intersect": //交集 + if (property != null) + { + throw new Exception("交集模式下,表达式左边不能为变量,请调整数据规则,如:c=>\"A,B,C\" intersect \"B,D\""); + } + + var rightval = filterObj.Value.Split(',').ToList(); + var leftval = filterObj.Key.Split(',').ToList(); + var val = rightval.Intersect(leftval); + + filter = Expression.Constant(val.Count() > 0); break; } @@ -117,7 +138,7 @@ namespace Infrastructure public static Expression> GenerateTypeBody(this ParameterExpression param, Filter filterObj) { - return (Expression>)(param.GenerateBody(filterObj)); + return (Expression>) (param.GenerateBody(filterObj)); } /// @@ -131,7 +152,7 @@ namespace Infrastructure public static Expression> GenerateTypeLambda(this ParameterExpression param, Expression body) { - return (Expression>)(param.GenerateLambda(body)); + return (Expression>) (param.GenerateLambda(body)); } public static Expression AndAlso(this Expression expression, Expression expressionRight) @@ -149,32 +170,137 @@ namespace Infrastructure return Expression.And(expression, expressionRight); } - //系统已经有该函数的实现 - //public static IQueryable Where(this IQueryable query, Expression expression) - //{ - // Expression expr = Expression.Call(typeof(Queryable), "Where", new[] { typeof(T) }, - // Expression.Constant(query), expression); - // //生成动态查询 - // IQueryable result = query.Provider.CreateQuery(expr); - // return result; - //} - - public static IQueryable GenerateFilter(this IQueryable query, string filterjson) + public static IQueryable GenerateFilter(this IQueryable query, string parametername, string filterjson) { if (!string.IsNullOrEmpty(filterjson)) { - var filters = JsonHelper.Instance.Deserialize>(filterjson); - var param = CreateLambdaParam("c"); + var filterGroup = JsonHelper.Instance.Deserialize(filterjson); + query = GenerateFilter(query, parametername, filterGroup); + } + + return query; + } + + /// + /// 转换FilterGroup为Lambda表达式 + /// + /// + /// + /// + /// + /// + public static IQueryable GenerateFilter(this IQueryable query, string parametername, + FilterGroup filterGroup) + { + var param = CreateLambdaParam(parametername); + Expression result = ConvertGroup(filterGroup, param); + query = query.Where(param.GenerateTypeLambda(result)); + return query; + } + + /// + /// 转换filtergroup为表达式 + /// + /// + /// + /// + /// + public static Expression ConvertGroup(FilterGroup filterGroup, ParameterExpression param) + { + if (filterGroup == null) return null; + + if (filterGroup.Filters.Length == 1 &&(filterGroup.Children == null || !filterGroup.Children.Any())) //只有一个条件 + { + return param.GenerateBody(filterGroup.Filters[0]); + } + + Expression result = ConvertFilters(filterGroup.Filters, param, filterGroup.Operation); + Expression gresult = ConvertGroup(filterGroup.Children, param, filterGroup.Operation); + if (gresult == null) return result; + if (result == null) return gresult; + + if (filterGroup.Operation == "and") + { + return result.AndAlso(gresult); + } + else //or + { + return result.Or(gresult); + } + } - Expression result = Expression.Constant(true); - foreach (var filter in filters) + /// + /// 转换FilterGroup[]为表达式,不管FilterGroup里面的Filters + /// + /// + /// + /// + /// + /// + private static Expression ConvertGroup(FilterGroup[] groups, ParameterExpression param, string operation) + { + if (groups == null || !groups.Any()) return null; + + Expression result = ConvertGroup(groups[0], param); + + if (groups.Length == 1) return result; + + if (operation == "and") + { + foreach (var filter in groups.Skip(1)) { - result = result.AndAlso(param.GenerateBody(filter)); + result = result.AndAlso(ConvertGroup(filter, param)); + } + } + else + { + foreach (var filter in groups.Skip(1)) + { + result = result.Or(ConvertGroup(filter, param)); } + } + + return result; + } - query = query.Where(param.GenerateTypeLambda(result)); + /// + /// 转换Filter数组为表达式 + /// + /// + /// + /// + /// + /// + private static Expression ConvertFilters(Filter[] filters, ParameterExpression param, string operation) + { + if (filters == null || !filters.Any()) + { + return null; } - return query; + + Expression result = param.GenerateBody(filters[0]); + + if (filters.Length == 1) + { + return result; + } + + if (operation == "and") + { + foreach (var filter in filters.Skip(1)) + { + result = result.AndAlso(param.GenerateBody(filter)); + } + } + else + { + foreach (var filter in filters.Skip(1)) + { + result = result.Or(param.GenerateBody(filter)); + } + } + + return result; } } } \ No newline at end of file diff --git a/Infrastructure/DynamicQueryable.cs b/Infrastructure/DynamicQueryable.cs index 7b6da375fb3ece4cb32983b938a1cfca666d5549..5dc7afc39a14d83e9388cd3caf8d51e1dbb46332 100644 --- a/Infrastructure/DynamicQueryable.cs +++ b/Infrastructure/DynamicQueryable.cs @@ -277,7 +277,8 @@ namespace Infrastructure private ClassFactory() { AssemblyName name = new AssemblyName("DynamicClasses"); - AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + AssemblyBuilder assembly = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + // AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); #if ENABLE_LINQ_PARTIAL_TRUST new ReflectionPermission(PermissionState.Unrestricted).Assert(); #endif @@ -300,7 +301,10 @@ namespace Infrastructure Type type; if (!classes.TryGetValue(signature, out type)) { type = CreateDynamicClass(signature.properties); - classes.Add(signature, type); + //fixed by https://gitee.com/DUWENINK + if (!classes.ContainsKey(signature)){ + classes.Add(signature, type); + } } return type; } diff --git a/Infrastructure/Extensions/AutofacManager/AutofacContainerModule.cs b/Infrastructure/Extensions/AutofacManager/AutofacContainerModule.cs new file mode 100644 index 0000000000000000000000000000000000000000..b29053f65644900482ab325f0051d52e116d871c --- /dev/null +++ b/Infrastructure/Extensions/AutofacManager/AutofacContainerModule.cs @@ -0,0 +1,22 @@ +using System; + +namespace Infrastructure.Extensions.AutofacManager +{ + /// + /// 提供全局静态获取服务的能力。 + /// 例:AutofacContainerModule.GetService() + /// + public class AutofacContainerModule + { + static private IServiceProvider _provider; + public static void ConfigServiceProvider(IServiceProvider serviceProvider) + { + _provider = serviceProvider; + } + public static TService GetService() where TService:class + { + Type typeParameterType = typeof(TService); + return (TService)_provider.GetService(typeParameterType); + } + } +} diff --git a/Infrastructure/Extensions/AutofacManager/IDependency.cs b/Infrastructure/Extensions/AutofacManager/IDependency.cs new file mode 100644 index 0000000000000000000000000000000000000000..638e254fc22602d4a7c3a443956e3cf8e7961ed9 --- /dev/null +++ b/Infrastructure/Extensions/AutofacManager/IDependency.cs @@ -0,0 +1,9 @@ +namespace Infrastructure.Extensions.AutofacManager +{ + /// + /// 所有AutoFac注入的基类 + /// + public interface IDependency + { + } +} diff --git a/Infrastructure/Extensions/ConvertJsonExtension.cs b/Infrastructure/Extensions/ConvertJsonExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..ad7cff2ec4f587ac7ff83232112dfc073d0fa742 --- /dev/null +++ b/Infrastructure/Extensions/ConvertJsonExtension.cs @@ -0,0 +1,361 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Text; +using Newtonsoft.Json; + +namespace Infrastructure.Extensions +{ + public static class ConvertJsonExtension + { + #region 私有方法 + /// + /// 过滤特殊字符 + /// + /// 字符串 + /// json字符串 + private static string String2Json(String s) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.Length; i++) + { + char c = s.ToCharArray()[i]; + switch (c) + { + case '\"': + sb.Append("\\\""); break; + case '\\': + sb.Append("\\\\"); break; + case '/': + sb.Append("\\/"); break; + case '\b': + sb.Append("\\b"); break; + case '\f': + sb.Append("\\f"); break; + case '\n': + sb.Append("\\n"); break; + case '\r': + sb.Append("\\r"); break; + case '\t': + sb.Append("\\t"); break; + default: + sb.Append(c); break; + } + } + return sb.ToString(); + } + /// + /// 格式化字符型、日期型、布尔型 + /// + /// + /// + /// + private static string StringFormat(string str, Type type) + { + if (type == typeof(string)) + { + str = String2Json(str); + str = "\"" + str + "\""; + } + else if (type == typeof(DateTime)) + { + str = "\"" + str + "\""; + } + else if (type == typeof(bool)) + { + str = str.ToLower(); + } + else if (type != typeof(string) && string.IsNullOrEmpty(str)) + { + str = "\"" + str + "\""; + } + return str; + } + + #endregion + + #region list转换成JSON + /// + /// list转换为Json + /// + /// + /// + /// + public static string ListToJson(this IList list) + { + object obj = list[0]; + return ListToJson(list, obj.GetType().Name); + } + /// + /// list转换为json + /// + /// + /// + /// + /// + private static string ListToJson(this IList list, string JsonName) + { + if (list.Count == 0) + { + return ""; + } + StringBuilder Json = new StringBuilder(); + if (string.IsNullOrEmpty(JsonName)) + JsonName = list[0].GetType().Name; + Json.Append("{\"" + JsonName + "\":["); + + for (int i = 0; i < list.Count; i++) + { + T obj = Activator.CreateInstance(); + PropertyInfo[] pi = obj.GetType().GetProperties(); + Json.Append("{"); + for (int j = 0; j < pi.Length; j++) + { + Type type = pi[j].GetValue(list[i], null).GetType(); + Json.Append("\"" + pi[j].Name.ToString() + "\":" + StringFormat(pi[j].GetValue(list[i], null).ToString(), type)); + if (j < pi.Length - 1) + { + Json.Append(","); + } + } + Json.Append("}"); + if (i < list.Count - 1) + { + Json.Append(","); + } + } + Json.Append("]}"); + return Json.ToString(); + } + #endregion + + #region 对象转换为Json + /// + /// 对象转换为json + /// + /// json对象 + /// json字符串 + public static string ToJson(this object jsonObject) + { + string jsonString = "{"; + PropertyInfo[] propertyInfo = jsonObject.GetType().GetProperties(); + for (int i = 0; i < propertyInfo.Length; i++) + { + object objectValue = propertyInfo[i].GetGetMethod().Invoke(jsonObject, null); + string value = string.Empty; + if (objectValue is DateTime || objectValue is Guid || objectValue is TimeSpan) + { + value = "'" + objectValue.ToString() + "'"; + } + else if (objectValue is string) + { + value = "'" + ToJson(objectValue.ToString()) + "'"; + } + else if (objectValue is IEnumerable) + { + value = ToJson((IEnumerable)objectValue); + } + else + { + value = ToJson(objectValue.ToString()); + } + jsonString += "\"" + ToJson(propertyInfo[i].Name) + "\":" + value + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "}"; + } + + #endregion + + #region 对象集合转换为json + /// + /// 对象集合转换为json + /// + /// 对象集合 + /// json字符串 + public static string ToJson(this IEnumerable array) + { + string jsonString = "{"; + foreach (object item in array) + { + jsonString += ToJson(item) + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "]"; + } + #endregion + + #region 普通集合转换Json + /// + /// 普通集合转换Json + /// + /// 集合对象 + /// Json字符串 + public static string ToArrayString(this IEnumerable array) + { + string jsonString = "["; + foreach (object item in array) + { + jsonString = ToJson(item.ToString()) + ","; + } + jsonString.Remove(jsonString.Length - 1, jsonString.Length); + return jsonString + "]"; + } + #endregion + + #region DataSet转换为Json + /// + /// DataSet转换为Json + /// + /// DataSet对象 + /// Json字符串 + public static string ToJson(this DataSet dataSet) + { + string jsonString = "{"; + foreach (DataTable table in dataSet.Tables) + { + jsonString += "\"" + table.TableName + "\":" + ToJson(table) + ","; + } + jsonString = jsonString.TrimEnd(','); + return jsonString + "}"; + } + #endregion + + #region Datatable转换为Json + /// + /// Datatable转换为Json + /// + /// Datatable对象 + /// Json字符串 + public static string ToJson(this DataTable dt) + { + StringBuilder jsonString = new StringBuilder(); + jsonString.Append("["); + DataRowCollection drc = dt.Rows; + for (int i = 0; i < drc.Count; i++) + { + jsonString.Append("{"); + for (int j = 0; j < dt.Columns.Count; j++) + { + string strKey = dt.Columns[j].ColumnName; + string strValue = drc[i][j].ToString(); + Type type = dt.Columns[j].DataType; + jsonString.Append("\"" + strKey + "\":"); + strValue = StringFormat(strValue, type); + if (j < dt.Columns.Count - 1) + { + jsonString.Append(strValue + ","); + } + else + { + jsonString.Append(strValue); + } + } + jsonString.Append("},"); + } + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("]"); + return jsonString.ToString(); + } + /// + /// DataTable转换为Json + /// + public static string ToJson(this DataTable dt, string jsonName) + { + StringBuilder Json = new StringBuilder(); + if (string.IsNullOrEmpty(jsonName)) + jsonName = dt.TableName; + Json.Append("{\"" + jsonName + "\":["); + if (dt.Rows.Count > 0) + { + for (int i = 0; i < dt.Rows.Count; i++) + { + Json.Append("{"); + for (int j = 0; j < dt.Columns.Count; j++) + { + Type type = dt.Rows[i][j].GetType(); + Json.Append("\"" + dt.Columns[j].ColumnName.ToString() + "\":" + StringFormat(dt.Rows[i][j].ToString(), type)); + if (j < dt.Columns.Count - 1) + { + Json.Append(","); + } + } + Json.Append("}"); + if (i < dt.Rows.Count - 1) + { + Json.Append(","); + } + } + } + Json.Append("]}"); + return Json.ToString(); + } + + #endregion + + #region DataReader转换为Json + /// + /// DataReader转换为Json + /// + /// DataReader对象 + /// Json字符串 + public static string ReaderJson(this IDataReader dataReader) + { + StringBuilder jsonString = new StringBuilder(); + Dictionary ModelField = new Dictionary(); + for (int i = 0; i < dataReader.FieldCount; i++) + { + ModelField.Add(dataReader.GetName(i), dataReader.GetFieldType(i)); + } + jsonString.Append("["); + while (dataReader.Read()) + { + jsonString.Append("{"); + foreach (KeyValuePair keyVal in ModelField) + { + Type type = keyVal.Value; + string strKey = keyVal.Key; + string strValue = dataReader[strKey].ToString(); + jsonString.Append("\"" + strKey + "\":"); + strValue = StringFormat(strValue, type); + jsonString.Append(strValue + ","); + } + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("},"); + } + dataReader.Close(); + jsonString.Remove(jsonString.Length - 1, 1); + jsonString.Append("]"); + return jsonString.ToString(); + } + #endregion + + + public static T DeserializeObject(this string entityString) + { + if (string.IsNullOrEmpty(entityString)) + { + return default(T); + } + if (entityString == "{}") + { + entityString = "[]"; + } + return JsonConvert.DeserializeObject(entityString); + } + + public static string Serialize(this object obj, JsonSerializerSettings formatDate = null) + { + if (obj == null) return null; + formatDate = formatDate ?? new JsonSerializerSettings + { + DateFormatString = "yyyy-MM-dd HH:mm:ss" + }; + return JsonConvert.SerializeObject(obj, formatDate); + } + + } +} + diff --git a/Infrastructure/Extensions/DateTimeExtension.cs b/Infrastructure/Extensions/DateTimeExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..bf3914a8ad86d6f99be901e55410886825bb0132 --- /dev/null +++ b/Infrastructure/Extensions/DateTimeExtension.cs @@ -0,0 +1,34 @@ +using System; + +namespace Infrastructure.Extensions +{ + public static class DateTimeExtension + { + /// + /// 实现由C# 的时间到 Javascript 的时间的转换 + /// returns the number of milliseconds since Jan 1, 1970 (useful for converting C# dates to JS dates) + /// + /// + /// + public static double UnixTicks(this DateTime dt) + { + DateTime d1 = new DateTime(1970, 1, 1); + DateTime d2 = dt.AddHours(8).ToUniversalTime(); + TimeSpan ts = new TimeSpan(d2.Ticks - d1.Ticks); + return ts.TotalMilliseconds; + } + + /// + /// 将毫秒值转成 C# DateTime 类型 + /// + /// + /// + public static DateTime ConvertTime(this long time) + { + DateTime timeStamp = new DateTime(1970, 1, 1); //得到1970年的时间戳 + long t = (time + 8 * 60 * 60) * 10000000 + timeStamp.Ticks; + DateTime dt = new DateTime(t); + return dt; + } + } +} diff --git a/Infrastructure/Extensions/EntityProperties.cs b/Infrastructure/Extensions/EntityProperties.cs new file mode 100644 index 0000000000000000000000000000000000000000..72f7619ef160be488c271aee6c87b149adc8520c --- /dev/null +++ b/Infrastructure/Extensions/EntityProperties.cs @@ -0,0 +1,1219 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using Infrastructure.Const; +using Infrastructure.Utilities; + +namespace Infrastructure.Extensions +{ + public static class EntityProperties + { + + public static string GetExpressionPropertyFirst(this Expression> properties) + { + string[] arr = properties.GetExpressionProperty(); + if (arr.Length > 0) + return arr[0]; + return ""; + } + /// + /// 获取对象里指定成员名称 + /// + /// + /// 格式 Expression> exp = x => new { x.字段1, x.字段2 };或x=>x.Name + /// + public static string[] GetExpressionProperty(this Expression> properties) + { + if (properties == null) + return new string[] { }; + if (properties.Body is NewExpression) + return ((NewExpression)properties.Body).Members.Select(x => x.Name).ToArray(); + if (properties.Body is MemberExpression) + return new string[] { ((MemberExpression)properties.Body).Member.Name }; + if (properties.Body is UnaryExpression) + return new string[] { ((properties.Body as UnaryExpression).Operand as MemberExpression).Member.Name }; + throw new Exception("未实现的表达式"); + } + public static string ValidateHashInEntity(this Type typeinfo, Dictionary dic) + { + return typeinfo.ValidateDicInEntity(dic, false); + } + + public static void RemoveNotExistColumns(this Type typeinfo, List cols) + { + + } + + /// + /// 获取所有字段的名称 + /// + /// + /// + public static List GetAtrrNames(this Type typeinfo) + { + return typeinfo.GetProperties().Select(c => c.Name).ToList(); + } + public static void IsExistColumns(this Type typeinfo) + { + + } + public static Dictionary GetColumType(this PropertyInfo[] properties) + { + return properties.GetColumType(false); + } + public static Dictionary GetColumType(this PropertyInfo[] properties, bool containsKey) + { + Dictionary dictionary = new Dictionary(); + foreach (PropertyInfo property in properties) + { + if (!containsKey && property.IsKey()) + { + continue; + } + var keyVal = GetColumnType(property, true); + dictionary.Add(keyVal.Key, keyVal.Value); + } + return dictionary; + } + + private static readonly Dictionary entityMapDbColumnType = new Dictionary() { + {typeof(int),SqlDbTypeName.Int }, + {typeof(int?),SqlDbTypeName.Int }, + {typeof(long),SqlDbTypeName.BigInt }, + {typeof(long?),SqlDbTypeName.BigInt }, + {typeof(decimal),"decimal(18, 5)" }, + {typeof(decimal?),"decimal(18, 5)" }, + {typeof(double),"decimal(18, 5)" }, + {typeof(double?),"decimal(18, 5)" }, + {typeof(float),"decimal(18, 5)" }, + {typeof(float?),"decimal(18, 5)" }, + {typeof(Guid),"UniqueIdentifier" }, + {typeof(Guid?),"UniqueIdentifier" }, + {typeof(byte),"tinyint" }, + {typeof(byte?),"tinyint" }, + {typeof(string),"nvarchar" } + }; + /// + /// 返回属性的字段及数据库类型 + /// + /// + /// 是否包括后字段具体长度:nvarchar(100) + /// + public static KeyValuePair GetColumnType(this PropertyInfo property, bool lenght = false) + { + string colType = ""; + object objAtrr = property.GetTypeCustomAttributes(typeof(ColumnAttribute), out bool asType); + if (asType) + { + colType = ((ColumnAttribute)objAtrr).TypeName.ToLower(); + if (!string.IsNullOrEmpty(colType)) + { + //不需要具体长度直接返回 + if (!lenght) + { + return new KeyValuePair(property.Name, colType); + } + if (colType == "decimal" || colType == "double" || colType == "float") + { + objAtrr = property.GetTypeCustomAttributes(typeof(DisplayFormatAttribute), out asType); + colType += "(" + (asType ? ((DisplayFormatAttribute)objAtrr).DataFormatString : "18,5") + ")"; + + } + ///如果是string,根据 varchar或nvarchar判断最大长度 + if (property.PropertyType.ToString() == "System.String") + { + colType = colType.Split("(")[0]; + objAtrr = property.GetTypeCustomAttributes(typeof(MaxLengthAttribute), out asType); + if (asType) + { + int length = ((MaxLengthAttribute)objAtrr).Length; + colType += "(" + (length < 1 || length > (colType.StartsWith("n") ? 8000 : 4000) ? "max" : length.ToString()) + ")"; + } + else + { + colType += "(max)"; + } + } + return new KeyValuePair(property.Name, colType); + } + } + if (entityMapDbColumnType.TryGetValue(property.PropertyType, out string value)) + { + colType = value; + } + else + { + colType = SqlDbTypeName.NVarChar; + } + if (lenght && colType == SqlDbTypeName.NVarChar) + { + colType = "nvarchar(max)"; + } + return new KeyValuePair(property.Name, colType); + } + + /// + /// + /// + /// 将数组转换成sql语句 + /// 指定FieldType数据库字段类型 + /// + /// + public static string GetArraySql(this object[] array, FieldType fieldType) + { + if (array == null || array.Count() == 0) + { + return string.Empty; + } + string columnType = string.Empty; + List arrrayEntityList = array.Select(x => new ArrayEntity { column1 = x.ToString() }).ToList(); + return arrrayEntityList.GetEntitySql(false, null, null, null, fieldType); + } + /// + ///要执行的sql语句如:通过EntityToSqlTempName.Temp_Insert0.ToString()字符串占位,生成的的sql语句会把EntityToSqlTempName.Temp_Insert0.ToString()替换成生成的sql临时表数据 + /// string sql = " ;DELETE FROM " + typeEntity.Name + " where " + typeEntity.GetKeyName() + + /// " in (select * from " + EntityToSqlTempName.Temp_Insert0.ToString() + ")"; + /// + /// + /// + /// 指定生成的数组值的类型 + /// + /// + public static string GetArraySql(this object[] array, FieldType fieldType, string sql) + { + if (array == null || array.Count() == 0) + { + return string.Empty; + } + string columnType = string.Empty; + List arrrayEntityList = array.Select(x => new ArrayEntity { column1 = x.ToString() }).ToList(); + return arrrayEntityList.GetEntitySql(false, sql, null, null, fieldType); + } + public static string GetArraySql(this object[] array, string sql) + { + return array.GetArraySql(typeof(T).GetFieldType(), sql); + } + /// + /// 根据实体获取key的类型,用于update或del操作 + /// + /// + /// + public static FieldType GetFieldType(this Type typeEntity) + { + FieldType fieldType; + string columnType = typeEntity.GetProperties().Where(x => x.Name == typeEntity.GetKeyName()).ToList()[0].GetColumnType(false).Value; + switch (columnType) + { + case SqlDbTypeName.Int: fieldType = FieldType.Int; break; + case SqlDbTypeName.BigInt: fieldType = FieldType.BigInt; break; + case SqlDbTypeName.VarChar: fieldType = FieldType.VarChar; break; + case SqlDbTypeName.UniqueIdentifier: fieldType = FieldType.UniqueIdentifier; break; + default: fieldType = FieldType.NvarChar; break; + } + return fieldType; + } + public static string GetEntitySql(this IEnumerable entityList, + bool containsKey = false, + string sql = null, + Expression> ignoreFileds = null, + Expression> fixedColumns = null, + FieldType? fieldType = null + ) + { + + if (entityList == null || entityList.Count() == 0) return ""; + PropertyInfo[] propertyInfo = typeof(T).GetProperties().ToArray(); + if (propertyInfo.Count() == 0) + { + propertyInfo = entityList.ToArray()[0].GetType().GetGenericProperties().ToArray(); + } + propertyInfo = propertyInfo.GetGenericProperties().ToArray(); + + string[] arr = null; + if (fixedColumns != null) + { + arr = fixedColumns.GetExpressionToArray(); + PropertyInfo keyProperty = typeof(T).GetKeyProperty(); + propertyInfo = propertyInfo.Where(x => (containsKey && x.Name == keyProperty.Name) || arr.Contains(x.Name)).ToArray(); + } + if (ignoreFileds != null) + { + arr = ignoreFileds.GetExpressionToArray(); + propertyInfo = propertyInfo.Where(x => !arr.Contains(x.Name)).ToArray(); + } + + Dictionary dictProperties = propertyInfo.GetColumType(containsKey); + if (fieldType != null) + { + string realType = fieldType.ToString(); + if ((int)fieldType == 0 || (int)fieldType == 1) + { + realType += "(max)"; + } + dictProperties = new Dictionary { { dictProperties.Select(x => x.Key).ToList()[0], realType } }; + } + if (dictProperties.Keys.Count * entityList.Count() > 50 * 3000) + { + throw new Exception("写入数据太多,请分开写入。"); + } + + string cols = string.Join(",", dictProperties.Select(c => "[" + c.Key + "]" + " " + c.Value)); + StringBuilder declareTable = new StringBuilder(); + + string tempTablbe = "#" + EntityToSqlTempName.TempInsert.ToString(); + + declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")"); + declareTable.Append("\r\n"); + + //参数总数量 + int parCount = (dictProperties.Count) * (entityList.Count()); + int takeCount = 0; + int maxParsCount = 2050; + if (parCount > maxParsCount) + { + //如果参数总数量超过2100,设置每次分批循环写入表的大小 + takeCount = maxParsCount / dictProperties.Count; + } + + int count = 0; + StringBuilder stringLeft = new StringBuilder(); + StringBuilder stringCenter = new StringBuilder(); + StringBuilder stringRight = new StringBuilder(); + + int index = 0; + foreach (T entity in entityList) + { + //每1000行需要分批写入(数据库限制每批至多写入1000行数据) + if (index == 0 || index >= 1000 || takeCount - index == 0) + { + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); stringCenter.Clear(); stringRight.Clear(); + } + + stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;"); + stringCenter.Append("N'"); + + index = 0; count = 0; + } + stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " "); + index++; + foreach (PropertyInfo property in propertyInfo) + { + if (!containsKey && property.IsKey()) { continue; } + string par = "@v" + count; + stringLeft.Append(par + ","); + stringCenter.Append(par + " " + dictProperties[property.Name] + ","); + object val = property.GetValue(entity); + if (val == null) + { + stringRight.Append(par + "=NUll,"); + } + else + { + stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',"); + } + count++; + } + stringLeft.Remove(stringLeft.Length - 1, 1); + stringLeft.Append("),("); + } + + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); stringCenter.Clear(); stringRight.Clear(); + } + if (!string.IsNullOrEmpty(sql)) + { + sql = sql.Replace(EntityToSqlTempName.TempInsert.ToString(), tempTablbe); + declareTable.AppendLine(sql); + } + else + { + declareTable.AppendLine(" SELECT " + (string.Join(",", fixedColumns?.GetExpressionToArray() ?? new string[] { "*" })) + " FROM " + tempTablbe); + } + + + if (tempTablbe.Substring(0, 1) == "#") + { + declareTable.AppendLine("; drop table " + tempTablbe); + } + return declareTable.ToString(); + } + + + /// + ///此方法适用于数据量少,只有几列数据,不超过1W行,或几十列数据不超过1000行的情况下使用 + /// 大批量的数据考虑其他方式 + /// 將datatable生成sql語句,替換datatable作為參數傳入存儲過程 + /// + /// + /// + public static string GetDataTableSql(this DataTable table) + { + Dictionary dictCloumn = new Dictionary(); + for (int i = 0; i < table.Columns.Count; i++) + { + dictCloumn.Add(table.Columns[i].ColumnName, " nvarchar(max)"); + } + + + //参数总数量 + int parCount = (dictCloumn.Count) * (table.Rows.Count); + int takeCount = 0; + int maxParsCount = 2050; + if (parCount > maxParsCount) + { + //如果参数总数量超过2100,设置每次分批循环写入表的大小 + takeCount = maxParsCount / dictCloumn.Count; + } + + if (dictCloumn.Keys.Count * table.Rows.Count > 50 * 3000) + { + throw new Exception("写入数据太多,请分开写入。"); + } + + string cols = string.Join(",", dictCloumn.Select(c => "[" + c.Key + "]" + " " + c.Value)); + StringBuilder declareTable = new StringBuilder(); + + string tempTablbe = "#Temp_Insert0"; + declareTable.Append("CREATE TABLE " + tempTablbe + " (" + cols + ")"); + declareTable.Append("\r\n"); + int count = 0; + StringBuilder stringLeft = new StringBuilder(); + StringBuilder stringCenter = new StringBuilder(); + StringBuilder stringRight = new StringBuilder(); + + int index = 0; + + foreach (DataRow row in table.Rows) + { + //每1000行需要分批写入(数据库限制每批至多写入1000行数据) + if (index == 0 || index >= 1000 || takeCount - index == 0) + { + if (stringLeft.Length > 0) + { + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); stringCenter.Clear(); stringRight.Clear(); + } + // sbLeft.AppendLine(" INSERT INTO @toInsert0"); + stringLeft.AppendLine("exec sp_executesql N'SET NOCOUNT ON;"); + stringCenter.Append("N'"); + + index = 0; count = 0; + } + stringLeft.Append(index == 0 ? "; INSERT INTO " + tempTablbe + " values (" : " "); + index++; + foreach (KeyValuePair keyValue in dictCloumn) + { + string par = "@v" + count; + stringLeft.Append(par + ","); + stringCenter.Append(par + " " + keyValue.Value + ","); + object val = row[keyValue.Key]; + if (val == null) + { + stringRight.Append(par + "=NUll,"); + } + else + { + stringRight.Append(par + "='" + val.ToString().Replace("'", "''''") + "',"); + } + count++; + } + stringLeft.Remove(stringLeft.Length - 1, 1); + stringLeft.Append("),("); + } + + + + + if (stringLeft.Length > 0) + { + + declareTable.AppendLine( + stringLeft.Remove(stringLeft.Length - 2, 2).Append("',").ToString() + + stringCenter.Remove(stringCenter.Length - 1, 1).Append("',").ToString() + + stringRight.Remove(stringRight.Length - 1, 1).ToString()); + + stringLeft.Clear(); stringCenter.Clear(); stringRight.Clear(); + } + declareTable.AppendLine(" SELECT * FROM " + tempTablbe); + if (tempTablbe.Substring(0, 1) == "#") + { + declareTable.AppendLine("; drop table " + tempTablbe); + } + return declareTable.ToString(); + } + + + + public static string GetKeyName(this Type typeinfo) + { + return typeinfo.GetProperties().GetKeyName(); + } + public static string GetKeyType(this Type typeinfo) + { + string keyType = typeinfo.GetProperties().GetKeyName(true); + if (keyType == "varchar") + { + return "varchar(max)"; + } + else if (keyType != "nvarchar") + { + return keyType; + } + else + { + return "nvarchar(max)"; + } + } + public static string GetKeyName(this PropertyInfo[] properties) + { + return properties.GetKeyName(false); + } + /// + /// 获取key列名 + /// + /// + /// true获取key对应类型,false返回对象Key的名称 + /// + public static string GetKeyName(this PropertyInfo[] properties, bool keyType) + { + string keyName = string.Empty; + foreach (PropertyInfo propertyInfo in properties) + { + if (!propertyInfo.IsKey()) + continue; + if (!keyType) + return propertyInfo.Name; + var attributes = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), false); + //如果没有ColumnAttribute的需要单独再验证,下面只验证有属性的 + if (attributes.Length > 0) + return ((ColumnAttribute)attributes[0]).TypeName.ToLower(); + else + return GetColumType(new PropertyInfo[] { propertyInfo }, true)[propertyInfo.Name]; + } + return keyName; + } + + /// + /// 获取主键字段 + /// + /// + /// + public static PropertyInfo GetKeyProperty(this Type entity) + { + return entity.GetProperties().GetKeyProperty(); + } + public static PropertyInfo GetKeyProperty(this PropertyInfo[] properties) + { + return properties.Where(c => c.IsKey()).FirstOrDefault(); + } + public static bool IsKey(this PropertyInfo propertyInfo) + { + object[] keyAttributes = propertyInfo.GetCustomAttributes(typeof(KeyAttribute), false); + if (keyAttributes.Length > 0) + return true; + return false; + } + + + private static string[] _userEditFields { get; set; } + + + + + /// + /// 判断是否包含某个属性: + /// 如 [Editable(true)] + // public string MO { get; set; }包含Editable + /// + /// + /// + /// + public static bool ContainsCustomAttributes(this PropertyInfo propertyInfo, Type type) + { + propertyInfo.GetTypeCustomAttributes(type, out bool contains); + return contains; + } + + public static List ContainsCustomAttributes(this Type obj, Type containType) + { + List proList = new List(); + foreach (PropertyInfo pro in obj.GetProperties()) + { + if (pro.GetTypeCustomAttributes(containType) != null) + { + proList.Add(pro); + } + } + return proList; + } + + /// + /// 获取PropertyInfo指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this PropertyInfo propertyInfo, Type type, out bool asType) + { + object[] attributes = propertyInfo.GetCustomAttributes(type, false); + if (attributes.Length == 0) + { + asType = false; + return new string[0]; + } + asType = true; + return attributes[0]; + } + + /// + /// 验证集合的属性 + /// + /// + /// + /// + /// + public static WebResponseContent ValidationEntityList(this List entityList, Expression> expression = null) + { + WebResponseContent responseData = new WebResponseContent(); + foreach (T entity in entityList) + { + responseData = entity.ValidationEntity(expression); + if (!responseData.Status) + { + return responseData; + } + } + responseData.Status = true; + return responseData; + } + /// + /// 指定需要验证的字段 + /// + /// + /// + /// 对指定属性进行验证x=>{x.Name,x.Size} + /// + public static WebResponseContent ValidationEntity(this T entity, Expression> expression = null, Expression> validateProperties = null) + { + return ValidationEntity(entity, expression?.GetExpressionProperty(), validateProperties?.GetExpressionProperty()); + } + /// + /// specificProperties=null并且validateProperties=null,对所有属性验证,只验证其是否合法,不验证是否为空(除属性标识指定了不能为空外) + /// specificProperties!=null,对指定属性校验,并且都必须有值 + /// null并且validateProperties!=null,对指定属性校验,不判断是否有值 + /// + /// + /// + /// 验证指定的属性,并且非空判断 + /// 验证指定属性,只对字段合法性判断,不验证是否为空 + /// + public static WebResponseContent ValidationEntity(this T entity, string[] specificProperties, string[] validateProperties = null) + { + WebResponseContent responseData = new WebResponseContent(); + if (entity == null) return responseData.Error("对象不能为null"); + + PropertyInfo[] propertyArray = typeof(T).GetProperties(); + //若T为object取不到属性 + if (propertyArray.Length == 0) + { + propertyArray = entity.GetType().GetProperties(); + } + List compareProper = new List(); + + //只验证数据合法性,验证非空 + if (specificProperties != null && specificProperties.Length > 0) + { + compareProper.AddRange(propertyArray.Where(x => specificProperties.Contains(x.Name))); + } + + //只验证数据合法性,不验证非空 + if (validateProperties != null && validateProperties.Length > 0) + { + compareProper.AddRange(propertyArray.Where(x => validateProperties.Contains(x.Name))); + } + if (compareProper.Count() > 0) + { + propertyArray = compareProper.ToArray(); + } + foreach (PropertyInfo propertyInfo in propertyArray) + { + object value = propertyInfo.GetValue(entity); + //设置默认状态的值 + if (propertyInfo.Name == "Enable" || propertyInfo.Name == "AuditStatus") + { + if (value == null) + { + propertyInfo.SetValue(entity, 0); + continue; + } + } + //若存在specificProperties并且属性为数组specificProperties中的值,校验时就需要判断是否为空 + var reslut = propertyInfo.ValidationProperty(value, + specificProperties != null && specificProperties.Contains(propertyInfo.Name) ? true : false + ); + if (!reslut.Item1) + return responseData.Error(reslut.Item2); + } + return responseData.OK(); + } + + /// + /// 获取数据库类型,不带长度,如varchar(100),只返回的varchar + /// + /// + /// + public static string GetSqlDbType(this PropertyInfo propertyInfo) + { + string dbType = propertyInfo.GetTypeCustomValue(x => new { x.TypeName }); + + if (string.IsNullOrEmpty(dbType)) + { + return dbType; + } + dbType = dbType.ToLower(); + if (dbType.Contains(SqlDbTypeName.NVarChar)) + { + dbType = SqlDbTypeName.NVarChar; + } + else if (dbType.Contains(SqlDbTypeName.VarChar)) + { + dbType = SqlDbTypeName.VarChar; + } + else if (dbType.Contains(SqlDbTypeName.NChar)) + { + dbType = SqlDbTypeName.NChar; + } + else if (dbType.Contains(SqlDbTypeName.Char)) + { + dbType = SqlDbTypeName.Char; + } + + return dbType; + } + + /// + /// 验证数据库字段类型与值是否正确, + /// + /// propertyInfo为当字段,当前字段必须有ColumnAttribute属性, + /// 如字段:标识为数据库int类型[Column(TypeName="int")] public int Id { get; set; } + /// 如果是小数float或Decimal必须对propertyInfo字段加DisplayFormatAttribute属性 + /// + /// + /// IEnumerable<(bool, string, object)> bool成否校验成功,string校验失败信息,object,当前校验的值 + public static IEnumerable<(bool, string, object)> ValidationValueForDbType(this PropertyInfo propertyInfo, params object[] values) + { + string dbTypeName = propertyInfo.GetTypeCustomValue(c => c.TypeName); + foreach (object value in values) + { + yield return dbTypeName.ValidationVal(value, propertyInfo); + } + } + + public static bool ValidationRquiredValueForDbType(this PropertyInfo propertyInfo, object value, out string message) + { + if (value == null || value?.ToString()?.Trim() == "") + { + message = $"{propertyInfo.GetDisplayName()}不能为空"; + return false; + } + var result = propertyInfo.GetProperWithDbType().ValidationVal(value, propertyInfo); + message = result.Item2; + return result.Item1; + } + + private static readonly Dictionary ProperWithDbType = new Dictionary() { + { typeof(string),SqlDbTypeName.NVarChar }, + { typeof(DateTime),SqlDbTypeName.DateTime}, + {typeof(long),SqlDbTypeName.BigInt }, + {typeof(int),SqlDbTypeName.Int}, + { typeof(decimal),SqlDbTypeName.Decimal }, + { typeof(float),SqlDbTypeName.Float }, + { typeof(double),SqlDbTypeName.Double }, + { typeof(byte),SqlDbTypeName.Int },//类型待完 + { typeof(Guid),SqlDbTypeName.UniqueIdentifier} + }; + public static string GetProperWithDbType(this PropertyInfo propertyInfo) + { + bool result = ProperWithDbType.TryGetValue(propertyInfo.PropertyType, out string value); + if (result) + { + return value; + } + return SqlDbTypeName.NVarChar; + } + + /// + /// 验证数据库字段类型与值是否正确, + /// + /// 数据库字段类型(如varchar,nvarchar,decimal,不要带后面长度如:varchar(50)) + /// 值 + /// 要验证的类的属性,若不为null,则会判断字符串的长度是否正确 + /// (bool, string, object)bool成否校验成功,string校验失败信息,object,当前校验的值 + public static (bool, string, object) ValidationVal(this string dbType, object value, PropertyInfo propertyInfo = null) + { + if (string.IsNullOrEmpty(dbType)) + { + dbType = propertyInfo != null ? propertyInfo.GetProperWithDbType() : SqlDbTypeName.NVarChar; + } + dbType = dbType.ToLower(); + string val = value?.ToString(); + //验证长度 + string reslutMsg = string.Empty; + if (dbType == SqlDbTypeName.Int || dbType == SqlDbTypeName.BigInt) + { + if (!StringExtension.IsInt(value)) + reslutMsg = "只能为有效整数"; + } + else if (dbType == SqlDbTypeName.DateTime + || dbType == SqlDbTypeName.Date + || dbType == SqlDbTypeName.SmallDateTime + || dbType == SqlDbTypeName.SmallDate + ) + { + if (!StringExtension.IsDate(value)) + reslutMsg = "必须为日期格式"; + } + else if (dbType == SqlDbTypeName.Float || dbType == SqlDbTypeName.Decimal || dbType == SqlDbTypeName.Double) + { + string formatString = string.Empty; + if (propertyInfo != null) + formatString = propertyInfo.GetTypeCustomValue(x => x.DataFormatString); + //if (string.IsNullOrEmpty(formatString)) + // throw new Exception("请对字段" + propertyInfo?.Name + "添加DisplayFormat属性标识"); + + if (!StringExtension.IsNumber(val, formatString)) + { + string[] arr = (formatString ?? "10,0").Split(','); + reslutMsg = $"整数{arr[0]}最多位,小数最多{arr[1]}位"; + } + } + else if (dbType == SqlDbTypeName.UniqueIdentifier) + { + if (!StringExtension.IsGuid(val)) + { + reslutMsg = propertyInfo.Name + "Guid不正确"; + } + } + else if (propertyInfo != null + && (dbType == SqlDbTypeName.VarChar + || dbType == SqlDbTypeName.NVarChar + || dbType == SqlDbTypeName.NChar + || dbType == SqlDbTypeName.Char + || dbType == SqlDbTypeName.Text)) + { + + //默认nvarchar(max) 、text 长度不能超过20000 + if (val.Length > 20000) + { + reslutMsg = $"字符长度最多【20000】"; + } + else + { + int length = StringExtension.GetInt(propertyInfo.GetTypeCustomValue(x => new { x.Length })); + if (length == 0) { return (true, null, null); } + //判断双字节与单字段 + else if (length < 8000 && + ((dbType.Substring(0, 1) != "n" + && Encoding.UTF8.GetBytes(val.ToCharArray()).Length > length) + || val.Length > length) + ) + { + reslutMsg = $"最多只能【{length}】个字符。"; + } + } + } + if (!string.IsNullOrEmpty(reslutMsg) && propertyInfo != null) + { + reslutMsg = propertyInfo.GetDisplayName() + reslutMsg; + } + return (reslutMsg == "" ? true : false, reslutMsg, value); + } + + public static string GetDisplayName(this PropertyInfo property) + { + string displayName = property.GetTypeCustomValue(x => new { x.Name }); + if (string.IsNullOrEmpty(displayName)) + { + return property.Name; + } + return displayName; + } + + /// + /// 验证每个属性的值是否正确 + /// + /// + /// 属性的值 + /// 是否指定当前属性必须有值 + /// + public static (bool, string, object) ValidationProperty(this PropertyInfo propertyInfo, object objectVal, bool required) + { + if (propertyInfo.IsKey()) { return (true, null, objectVal); } + + string val = objectVal == null ? "" : objectVal.ToString().Trim(); + + string requiredMsg = string.Empty; + if (!required) + { + var reuireVal = propertyInfo.GetTypeCustomValues(x => new { x.AllowEmptyStrings, x.ErrorMessage }); + if (reuireVal != null && !Convert.ToBoolean(reuireVal["AllowEmptyStrings"])) + { + required = true; + requiredMsg = reuireVal["ErrorMessage"]; + } + } + //如果不要求为必填项并且值为空,直接返回 + if (!required && string.IsNullOrEmpty(val)) + return (true, null, objectVal); + + if ((required && val == string.Empty)) + { + if (requiredMsg != "") return (false, requiredMsg, objectVal); + string propertyName = propertyInfo.GetTypeCustomValue(x => new { x.Name }); + return (false, requiredMsg + (string.IsNullOrEmpty(propertyName) ? propertyInfo.Name : propertyName) + "不能为空", objectVal); + } + //列名 + string typeName = propertyInfo.GetSqlDbType(); + + //如果没有ColumnAttribute的需要单独再验证,下面只验证有属性的 + if (typeName == null) { return (true, null, objectVal); } + //验证长度 + return typeName.ValidationVal(val, propertyInfo); + } + /// + /// 获取属性的指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this MemberInfo member, Type type) + { + object[] obj = member.GetCustomAttributes(type, false); + if (obj.Length == 0) return null; + return obj[0]; + } + + /// + /// 获取类的指定属性 + /// + /// + /// + /// + public static object GetTypeCustomAttributes(this Type entity, Type type) + { + object[] obj = entity.GetCustomAttributes(type, false); + if (obj.Length == 0) return null; + return obj[0]; + } + /// + /// 获取类的多个指定属性的值 + /// + /// 当前类 + /// 指定的类 + /// 指定属性的值 格式 Expression> exp = x => new { x.字段1, x.字段2 }; + /// 返回的是字段+value + public static Dictionary GetTypeCustomValues(this MemberInfo member, Expression> expression) + { + var attr = member.GetTypeCustomAttributes(typeof(TEntity)); + if (attr == null) + { + return null; + } + + string[] propertyName = expression.GetExpressionProperty(); + Dictionary propertyKeyValues = new Dictionary(); + + foreach (PropertyInfo property in attr.GetType().GetProperties()) + { + if (propertyName.Contains(property.Name)) + { + propertyKeyValues[property.Name] = (property.GetValue(attr) ?? string.Empty).ToString(); + } + } + return propertyKeyValues; + } + + /// + /// 获取类的单个指定属性的值(只会返回第一个属性的值) + /// + /// 当前类 + /// 指定的类 + /// 指定属性的值 格式 Expression> exp = x => new { x.字段1, x.字段2 }; + /// + public static string GetTypeCustomValue(this MemberInfo member, Expression> expression) + { + var propertyKeyValues = member.GetTypeCustomValues(expression); + if (propertyKeyValues == null || propertyKeyValues.Count == 0) + { + return null; + } + return propertyKeyValues.First().Value ?? ""; + } + /// + /// 判断hash的列是否为对应的实体,并且值是否有效 + /// + /// + /// + /// 移除不存在字段 + /// + public static string ValidateDicInEntity(this Type typeinfo, Dictionary dic, bool removeNotContains, string[] ignoreFields = null) + { + return typeinfo.ValidateDicInEntity(dic, removeNotContains, true, ignoreFields); + } + + public static string ValidateDicInEntity(this Type type, List> dicList, bool removeNotContains, bool removerKey, string[] ignoreFields = null) + { + PropertyInfo[] propertyInfo = type.GetProperties(); + string reslutMsg = string.Empty; + foreach (Dictionary dic in dicList) + { + reslutMsg = type.ValidateDicInEntity(dic, propertyInfo, removeNotContains, removerKey, ignoreFields); + if (!string.IsNullOrEmpty(reslutMsg)) + return reslutMsg; + } + return reslutMsg; + } + public static string ValidateDicInEntity(this Type type, Dictionary dic, bool removeNotContains, bool removerKey, string[] ignoreFields = null) + { + return type.ValidateDicInEntity(dic, null, removeNotContains, removerKey, ignoreFields); + } + /// + /// 判断hash的列是否为对应的实体,并且值是否有效 + /// + /// + /// + /// 移除不存在字段 + /// 移除主键 + /// + private static string ValidateDicInEntity(this Type typeinfo, Dictionary dic, PropertyInfo[] propertyInfo, bool removeNotContains, bool removerKey, string[] ignoreFields = null) + { + if (dic == null || dic.Count == 0) { return "参数无效"; } + if (propertyInfo == null) + propertyInfo = typeinfo.GetProperties().Where(x => x.PropertyType.Name != "List`1").ToArray(); + + // 不存在的字段直接移除 + dic.Where(x => !propertyInfo.Any(p => p.Name == x.Key)).Select(s => s.Key).ToList().ForEach(f => + { + dic.Remove(f); + }); + string keyName = typeinfo.GetKeyName(); + //移除主键 + if (removerKey) + { + dic.Remove(keyName); + } + foreach (PropertyInfo property in propertyInfo) + { + //忽略与主键的字段不做验证 + if (property.Name == keyName || (ignoreFields != null && ignoreFields.Contains(property.Name))) + continue; + + //不在编辑中的列,是否也要必填 + if (!dic.ContainsKey(property.Name)) + { + //移除主键默认为新增数据,将不在编辑列中的有默认值的数据设置为默认值 + //如果为true默认为添加功能,添加操作所有不能为空的列也必须要提交 + if (property.GetCustomAttributes(typeof(RequiredAttribute)).Count() > 0 + && property.PropertyType != typeof(int) + && property.PropertyType != typeof(long) + && property.PropertyType != typeof(byte) + && property.PropertyType != typeof(decimal) + ) + { + return property.GetTypeCustomValue(x => x.Name) + "为必须提交项"; + } + continue; + } + bool isEdit = property.ContainsCustomAttributes(typeof(EditableAttribute)); + //不是编辑列的直接移除,并且不是主键 + //removerKey=true,不保留主键,直接移除 + //removerKey=false,保留主键,属性与主键不同的直接移除 + // if (!isEdit && (removerKey || (!removerKey && property.Name != keyName))) + if (!isEdit) + { + if (property.GetCustomAttributes(typeof(RequiredAttribute)).Count() > 0) + { + return property.GetTypeCustomValue(x => x.Name) + "没有配置好Model为编辑列"; + } + dic.Remove(property.Name); + continue; + } + ////移除忽略的不保存的数据 + //if (property.ContainsCustomAttributes(typeof(JsonIgnoreAttribute))) + //{ + // hash.Remove(property.Name); + // continue; + //} + //验证数据类型,不验证是否为空 + var result = property.ValidationProperty(dic[property.Name], false); + if (!result.Item1) + return result.Item2; + + //将所有空值设置为null + if (dic[property.Name] != null && dic[property.Name].ToString() == string.Empty) + dic[property.Name] = null; + } + return string.Empty; + } + + + private static object MapToInstance(this Type reslutType, object sourceEntity, PropertyInfo[] sourcePro, PropertyInfo[] reslutPro, string[] sourceFilterField, string[] reslutFilterField, string mapType = null) + { + mapType = mapType ?? GetMapType(reslutType); + if (sourcePro == null) + { + sourcePro = sourceEntity.GetType().GetProperties(); + } + if (reslutPro == null) + { + reslutPro = reslutType.GetProperties(); ; + } + + object newObj = Activator.CreateInstance(reslutType); + + if (mapType == "Dictionary") + { + if (sourceFilterField != null && sourceFilterField.Length > 0) + { + sourcePro = sourcePro.Where(x => sourceFilterField.Contains(x.Name)).ToArray(); + } + foreach (var property in sourcePro) + { + (newObj as System.Collections.IDictionary).Add(property.Name, property.GetValue(sourceEntity)); + } + return newObj; + } + + if (reslutFilterField != null && reslutFilterField.Count() > 0) + { + reslutPro.Where(x => reslutFilterField.Contains(x.Name)); + } + + foreach (var property in reslutPro) + { + PropertyInfo info = sourcePro.Where(x => x.Name == property.Name).FirstOrDefault(); + if (!(info != null && info.PropertyType == property.PropertyType)) + continue; + property.SetValue(newObj, info.GetValue(sourceEntity)); + } + return newObj; + } + private static string GetMapType(Type type) + { + return typeof(Dictionary<,>) == type ? "Dictionary" : "entity"; + } + + /// + /// 将数据源映射到新的数据中,目前只支持List映射到List或TSource映射到TResult + /// 目前只支持Dictionary或实体类型 + /// + /// + /// + /// + /// 只映射返回对象的指定字段,若为null则默认为全部字段 + /// 只映射数据源对象的指定字段,若为null则默认为全部字段 + /// + public static TResult MapToObject(this TSource source, Expression> resultExpression, + Expression> sourceExpression = null + ) where TResult : class + { + if (source == null) + return null; + string[] sourceFilterField = sourceExpression == null ? typeof(TSource).GetProperties().Select(x => x.Name).ToArray() : sourceExpression.GetExpressionProperty(); + string[] reslutFilterField = resultExpression?.GetExpressionProperty(); + if (!(source is System.Collections.IList)) + return MapToInstance(typeof(TResult), source, null, null, sourceFilterField, reslutFilterField) as TResult; + + Type sourceType = null; + Type resultType = null; + System.Collections.IList sourceList = source as System.Collections.IList; + sourceType = sourceList[0].GetType(); + resultType = (typeof(TResult)).GenericTypeArguments[0]; + + System.Collections.IList reslutList = Activator.CreateInstance(typeof(TResult)) as System.Collections.IList; + PropertyInfo[] sourcePro = sourceType.GetProperties(); + PropertyInfo[] resultPro = resultType.GetProperties(); + + string mapType = GetMapType(resultType); + for (int i = 0; i < sourceList.Count; i++) + { + var reslutobj = MapToInstance(resultType, sourceList[i], sourcePro, resultPro, sourceFilterField, reslutFilterField, mapType); + reslutList.Add(reslutobj); + } + return reslutList as TResult; + } + + /// + /// 将一个实体的赋到另一个实体上,应用场景: + /// 两个实体,a a1= new a();b b1= new b(); a1.P=b1.P; a1.Name=b1.Name; + /// + /// + /// + /// + /// + /// 指定对需要的字段赋值,格式x=>new {x.Name,x.P},返回的结果只会对Name与P赋值 + public static void MapValueToEntity(this TSource source, TResult result, Expression> expression = null) where TResult : class + { + if (source == null) + return; + string[] fields = expression?.GetExpressionToArray(); + PropertyInfo[] reslutPro = fields == null ? result.GetType().GetProperties() : result.GetType().GetProperties().Where(x => fields.Contains(x.Name)).ToArray(); + PropertyInfo[] sourcePro = source.GetType().GetProperties(); + foreach (var property in reslutPro) + { + PropertyInfo info = sourcePro.Where(x => x.Name == property.Name).FirstOrDefault(); + if (info != null && info.PropertyType == property.PropertyType) + { + property.SetValue(result, info.GetValue(source)); + } + } + } + + + + + + } + + public class ArrayEntity + { + public string column1 { get; set; } + } + + public enum FieldType + { + VarChar = 0, + NvarChar, + Int, + BigInt, + UniqueIdentifier + } + + public enum EntityToSqlTempName + { + TempInsert = 0 + } +} diff --git a/Infrastructure/Extensions/GenericExtension.cs b/Infrastructure/Extensions/GenericExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..8b9f20a0d6e2153a0779045e3cd156a8092c3281 --- /dev/null +++ b/Infrastructure/Extensions/GenericExtension.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Infrastructure.Extensions +{ + /// + /// 泛型扩展 + /// + public static class GenericExtension + { + public static bool Equal(this T x, T y) + { + return ((IComparable)(x)).CompareTo(y) == 0; + } + + #region ToDictionary + /// + /// 将实体指定的字段写入字典 + /// + /// + /// + /// + /// + + public static Dictionary ToDictionary(this T t, Expression> expression) where T : class + { + Dictionary dic = new Dictionary(); + string[] fields = expression.GetExpressionToArray(); + PropertyInfo[] properties = expression == null ? t.GetType().GetProperties() : t.GetType().GetProperties().Where(x => fields.Contains(x.Name)).ToArray(); + + foreach (var property in properties) + { + var value = property.GetValue(t, null); + dic.Add(property.Name, value != null ? value.ToString() : ""); + } + return dic; + } + + public static Dictionary ToDictionary(this TInterface t, Dictionary dic = null) where T : class, TInterface + { + if (dic == null) + dic = new Dictionary(); + var properties = typeof(T).GetProperties(); + foreach (var property in properties) + { + var value = property.GetValue(t, null); + if (value == null) continue; + dic.Add(property.Name, value != null ? value.ToString() : ""); + } + return dic; + } + + #endregion + + + public static DataTable ToDataTable(this IEnumerable source, Expression> columns = null, bool contianKey = true) + { + DataTable dtReturn = new DataTable(); + if (source == null) return dtReturn; + + PropertyInfo[] oProps = typeof(T).GetProperties() + .Where(x => x.PropertyType.Name != "List`1").ToArray(); + if (columns != null) + { + string[] columnArray = columns.GetExpressionToArray(); + oProps = oProps.Where(x => columnArray.Contains(x.Name)).ToArray(); + } + //移除自增主键 + PropertyInfo keyType = oProps.GetKeyProperty();// oProps.GetKeyProperty()?.PropertyType; + if (!contianKey && keyType != null && (keyType.PropertyType == typeof(int) || keyType.PropertyType == typeof(long))) + { + oProps = oProps.Where(x => x.Name != keyType.Name).ToArray(); + } + + foreach (var pi in oProps) + { + var colType = pi.PropertyType; + + if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) + { + colType = colType.GetGenericArguments()[0]; + } + + dtReturn.Columns.Add(new DataColumn(pi.Name, colType)); + } + foreach (var rec in source) + { + var dr = dtReturn.NewRow(); + foreach (var pi in oProps) + { + dr[pi.Name] = pi.GetValue(rec, null) == null + ? DBNull.Value + : pi.GetValue + (rec, null); + } + dtReturn.Rows.Add(dr); + } + return dtReturn; + } + } +} diff --git a/Infrastructure/Extensions/LambdaExtensions.cs b/Infrastructure/Extensions/LambdaExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..5de78e6a1d51121c7889364e0fac63de05c9a46e --- /dev/null +++ b/Infrastructure/Extensions/LambdaExtensions.cs @@ -0,0 +1,504 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + using Infrastructure.Const; + + namespace Infrastructure.Extensions +{ + public static class LambdaExtensions + { + /// + /// 分页查询 + /// + /// + /// + /// + /// + /// + public static IQueryable TakePage(this IQueryable queryable, int page, int size = 15) + { + return queryable.TakeOrderByPage(page, size); + } + /// + /// 分页查询 + /// + /// + /// + /// + /// + /// + /// + public static IQueryable TakeOrderByPage(this IQueryable queryable, int page, int size = 15, Expression>> orderBy = null) + { + if (page <= 0) + { + page = 1; + } + return Queryable.Take(Queryable.Skip(queryable.GetIQueryableOrderBy(orderBy.GetExpressionToDic()), (page - 1) * size), size); + } + + /// + /// 创建lambda表达式:p=>true + /// + /// + /// + public static Expression> True() + { + return p => true; + } + + /// + /// 创建lambda表达式:p=>false + /// + /// + /// + public static Expression> False() + { + + return p => false; + } + + public static ParameterExpression GetExpressionParameter(this Type type) + { + + return Expression.Parameter(type, "p"); + } + /// + /// 创建lambda表达式:p=>p.propertyName + /// + /// + /// + /// + /// + public static Expression> GetExpression(this string propertyName) + { + return propertyName.GetExpression(typeof(T).GetExpressionParameter()); + } + /// + /// 创建委托有返回值的表达式:p=>p.propertyName + /// + /// + /// + /// + /// + public static Func GetFun(this string propertyName) + { + return propertyName.GetExpression(typeof(T).GetExpressionParameter()).Compile(); + } + + /// + /// 创建lambda表达式:p=>false + /// 在已知TKey字段类型时,如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID + /// Expression> expression = x => x.CreateDate;指定了类型 + /// + /// + /// + public static Expression> GetExpression(this string propertyName, ParameterExpression parameter) + { + if (typeof(TKey).Name == "Object") + return Expression.Lambda>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter); + return Expression.Lambda>(Expression.Property(parameter, propertyName), parameter); + } + /// + /// 创建lambda表达式:p=>false + /// object不能确认字段类型(datetime,int,string),如动态排序OrderBy(x=>x.ID)会用到此功能,返回的就是x=>x.ID + /// Expression> expression = x => x.CreateDate;任意类型的字段 + /// + /// + /// + public static Expression> GetExpression(this string propertyName) + { + return propertyName.GetExpression(typeof(T).GetExpressionParameter()); + } + + public static Expression> GetExpression(this string propertyName, ParameterExpression parameter) + { + return Expression.Lambda>(Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter); + } + + + + /// + /// + /// + /// + /// 字段名 + /// 表达式的值 + /// 创建表达式的类型,如:p=>p.propertyName != propertyValue + /// p=>p.propertyName.Contains(propertyValue) + /// + public static Expression> CreateExpression(this string propertyName, object propertyValue, LinqExpressionType expressionType) + { + return propertyName.CreateExpression(propertyValue, null, expressionType); + } + + /// + /// + /// + /// + /// 字段名 + /// 表达式的值 + /// 创建表达式的类型,如:p=>p.propertyName != propertyValue + /// p=>p.propertyName.Contains(propertyValue) + /// + private static Expression> CreateExpression( + this string propertyName, + object propertyValue, + ParameterExpression parameter, + LinqExpressionType expressionType) + { + Type proType = typeof(T).GetProperty(propertyName).PropertyType; + //创建节点变量如p=>的节点p + // parameter ??= Expression.Parameter(typeof(T), "p");//创建参数p + parameter = parameter ?? Expression.Parameter(typeof(T), "p"); + + //创建节点的属性p=>p.name 属性name + MemberExpression memberProperty = Expression.PropertyOrField(parameter, propertyName); + if (expressionType == LinqExpressionType.In) + { + if (!(propertyValue is System.Collections.IList list) || list.Count == 0) throw new Exception("属性值类型不正确"); + + bool isStringValue = true; + List objList = new List(); + + if (proType.ToString() != "System.String") + { + isStringValue = false; + foreach (var value in list) + { + objList.Add(value.ToString().ChangeType(proType)); + } + list = objList; + } + + if (isStringValue) + { + //string 类型的字段,如果值带有'单引号,EF会默认变成''两个单引号 + MethodInfo method = typeof(System.Collections.IList).GetMethod("Contains"); + //创建集合常量并设置为常量的值 + ConstantExpression constantCollection = Expression.Constant(list); + //创建一个表示调用带参数的方法的:new string[]{"1","a"}.Contains("a"); + MethodCallExpression methodCall = Expression.Call(constantCollection, method, memberProperty); + return Expression.Lambda>(methodCall, parameter); + } + //非string字段,按上面方式处理报异常Null TypeMapping in Sql Tree + BinaryExpression body = null; + foreach (var value in list) + { + ConstantExpression constantExpression = Expression.Constant(value); + UnaryExpression unaryExpression = Expression.Convert(memberProperty, constantExpression.Type); + + body = body == null + ? Expression.Equal(unaryExpression, constantExpression) + : Expression.OrElse(body, Expression.Equal(unaryExpression, constantExpression)); + } + return Expression.Lambda>(body, parameter); + } + + // object value = propertyValue; + ConstantExpression constant = proType.ToString() == "System.String" + ? Expression.Constant(propertyValue) : Expression.Constant(propertyValue.ToString().ChangeType(proType)); + + UnaryExpression member = Expression.Convert(memberProperty, constant.Type); + Expression> expression; + switch (expressionType) + { + //p=>p.propertyName == propertyValue + case LinqExpressionType.Equal: + expression = Expression.Lambda>(Expression.Equal(member, constant), parameter); + break; + //p=>p.propertyName != propertyValue + case LinqExpressionType.NotEqual: + expression = Expression.Lambda>(Expression.NotEqual(member, constant), parameter); + break; + // p => p.propertyName > propertyValue + case LinqExpressionType.GreaterThan: + expression = Expression.Lambda>(Expression.GreaterThan(member, constant), parameter); + break; + // p => p.propertyName < propertyValue + case LinqExpressionType.LessThan: + expression = Expression.Lambda>(Expression.LessThan(member, constant), parameter); + break; + // p => p.propertyName >= propertyValue + case LinqExpressionType.ThanOrEqual: + expression = Expression.Lambda>(Expression.GreaterThanOrEqual(member, constant), parameter); + break; + // p => p.propertyName <= propertyValue + case LinqExpressionType.LessThanOrEqual: + expression = Expression.Lambda>(Expression.LessThanOrEqual(member, constant), parameter); + break; + // p => p.propertyName.Contains(propertyValue) + // p => !p.propertyName.Contains(propertyValue) + case LinqExpressionType.Contains: + case LinqExpressionType.NotContains: + MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); + constant = Expression.Constant(propertyValue, typeof(string)); + if (expressionType == LinqExpressionType.Contains) + { + expression = Expression.Lambda>(Expression.Call(member, method, constant), parameter); + } + else + { + expression = Expression.Lambda>(Expression.Not(Expression.Call(member, method, constant)), parameter); + } + break; + default: + // p => p.false + expression = False(); + break; + } + return expression; + } + + /// + /// 表达式转换成KeyValList(主要用于多字段排序,并且多个字段的排序规则不一样) + /// 如有多个字段进行排序,参数格式为 + /// Expression>> orderBy = x => new Dictionary() { + /// { x.ID, true }, + /// { x.DestWarehouseName, true } + /// }; + /// 返回的是new Dictionary(){{}}key为排序字段,bool为升降序 + /// + /// + /// + /// + public static IEnumerable> GetExpressionToPair(this Expression>> expression) + { + + foreach (var exp in ((ListInitExpression)expression.Body).Initializers) + { + yield return new KeyValuePair( + exp.Arguments[0] is MemberExpression ? + (exp.Arguments[0] as MemberExpression).Member.Name.ToString() + : ((exp.Arguments[0] as UnaryExpression).Operand as MemberExpression).Member.Name, + (QueryOrderBy)((exp.Arguments[1] as ConstantExpression).Value)); + } + } + + + /// + /// 表达式转换成KeyValList(主要用于多字段排序,并且多个字段的排序规则不一样) + /// 如有多个字段进行排序,参数格式为 + /// Expression>> orderBy = x => new Dictionary() { + /// { x.ID, QueryOrderBy.Desc }, + /// { x.DestWarehouseName, QueryOrderBy.Asc } + /// }; + /// 返回的是new Dictionary(){{}}key为排序字段,QueryOrderBy为排序方式 + /// + /// + /// + /// + public static Dictionary GetExpressionToDic(this Expression>> expression) + { + return expression.GetExpressionToPair().Reverse().ToList().ToDictionary(x => x.Key, x => x.Value); + } + /// + /// 解析多字段排序 + /// + /// + /// + /// string=排序的字段,bool=true降序/false升序 + /// + public static IQueryable GetIQueryableOrderBy(this IQueryable queryable, Dictionary orderBySelector) + { + string[] orderByKeys = orderBySelector.Select(x => x.Key).ToArray(); + if (orderByKeys == null || orderByKeys.Length == 0) return queryable; + + IOrderedQueryable queryableOrderBy = null; + // string orderByKey = orderByKeys[^1]; + string orderByKey = orderByKeys[orderByKeys.Length-1]; + queryableOrderBy = orderBySelector[orderByKey] == QueryOrderBy.Desc + ? queryableOrderBy = queryable.OrderByDescending(orderByKey.GetExpression()) + : queryable.OrderBy(orderByKey.GetExpression()); + + for (int i = orderByKeys.Length - 2; i >= 0; i--) + { + queryableOrderBy = orderBySelector[orderByKeys[i]] == QueryOrderBy.Desc + ? queryableOrderBy.ThenByDescending(orderByKeys[i].GetExpression()) + : queryableOrderBy.ThenBy(orderByKeys[i].GetExpression()); + } + return queryableOrderBy; + } + + /// + /// 获取对象表达式指定属性的值 + /// 如获取:Out_Scheduling对象的ID或基他字段 + /// + /// + /// 格式 Expression>sch=x=>new {x.v1,x.v2} or x=>x.v1 解析里面的值返回为数组 + /// + public static string[] GetExpressionToArray(this Expression> expression) + { + string[] propertyNames = null; + if (expression.Body is MemberExpression) + { + propertyNames = new string[] { ((MemberExpression)expression.Body).Member.Name }; + } + else + { + propertyNames = expression.GetExpressionProperty().Distinct().ToArray(); + } + return propertyNames; + } + + /// + /// 与下面and生成方式有所不同,如果直接用表达式1.2进行合并产会提示数据源不同的异常,只能使用下面的的and合并 + /// 此种合并是在使用的同一个数据源(变量),生成的sql语句同样有性能问题(本身可以索引扫描的,生成的sql语句的case when变成索引查找) + /// + /// 通过字段动态生成where and /or表达 + /// 如:有多个where条件,当条件成立时where 1=1 and/or 2=2,依次往后拼接 + /// + /// + /// + /// ExpressionParameters + /// 1、Field生成的字段 + /// 2、ExpressionType 表达式类型大于、小于、于大=、小于=、contains + /// 3、Value表达式的值 + /// + /// + public static Expression> And(List listExpress) + { + return listExpress.Compose(Expression.And); + } + /// + /// 同上面and用法相同 + /// + /// + /// + /// + public static Expression> Or(this List listExpress) + { + return listExpress.Compose(Expression.Or); + } + private static Expression> Compose(this List listExpress, Func merge) + { + ParameterExpression parameter = Expression.Parameter(typeof(T), "p"); + Expression> expression = null; + foreach (ExpressionParameters exp in listExpress) + { + if (expression == null) + { + expression = exp.Field.GetExpression(parameter); + } + else + { + expression = expression.Compose(exp.Field.GetExpression(parameter), merge); + } + } + return expression; + } + /// + /// https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/ + /// 表达式合并(合并生产的sql语句有性能问题) + /// 合并两个where条件,如:多个查询条件时,判断条件成立才where + /// + /// + /// + /// + /// + public static Expression> And(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.And); + } + public static Expression> Or(this Expression> first, Expression> second) + { + return first.Compose(second, Expression.Or); + } + public static Expression Compose(this Expression first, Expression second, Func merge) + { + // build parameter map (from parameters of second to parameters of first) + var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); + // replace parameters in the second lambda expression with parameters from the first + var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); + // apply composition of lambda expression bodies to parameters from the first expression + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + + public static IQueryable GetQueryableSelect(this IQueryable queryable) + { + Expression> expression = CreateMemberInitExpression(); + return queryable.Select(expression); + } + + /// + /// 动态创建表达式Expression> expression = CreateMemberInitExpression(); + ///结果为Expression> expression1 = x => new User() { Age = x.Age, Species = x.Species }; + ///参照文档https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.memberinitexpression?redirectedfrom=MSDN&view=netframework-4.8 + /// + /// + /// + /// + public static Expression> CreateMemberInitExpression(Type resultType = null) + { + resultType = resultType ?? typeof(Result); + ParameterExpression left = Expression.Parameter(typeof(Source), "p"); + NewExpression newExpression = Expression.New(resultType); + PropertyInfo[] propertyInfos = resultType.GetProperties(); + List memberBindings = new List(); + foreach (PropertyInfo propertyInfo in propertyInfos) + { + MemberExpression member = Expression.Property(left, propertyInfo.Name); + MemberBinding speciesMemberBinding = Expression.Bind(resultType.GetMember(propertyInfo.Name)[0], member); + memberBindings.Add(speciesMemberBinding); + } + MemberInitExpression memberInitExpression = Expression.MemberInit(newExpression, memberBindings); + Expression> expression = Expression.Lambda>(memberInitExpression, new ParameterExpression[] { left }); + return expression; + } + public static Expression> CreateMemberInitExpression(Type resultType) + { + return CreateMemberInitExpression(resultType); + } + /// + /// 属性判断待完 + /// + /// + /// + public static IEnumerable GetGenericProperties(this Type type) + { + return type.GetProperties().GetGenericProperties(); + } + /// + /// 属性判断待完 + /// + /// + /// + public static IEnumerable GetGenericProperties(this IEnumerable properties) + { + return properties.Where(x => !x.PropertyType.IsGenericType && x.PropertyType.GetInterface("IList") == null || x.PropertyType.GetInterface("IEnumerable", false) == null); + } + } + + public class ExpressionParameters + { + public string Field { get; set; } + public LinqExpressionType ExpressionType { get; set; } + public object Value { get; set; } + // public + } + public class ParameterRebinder : ExpressionVisitor + { + + private readonly Dictionary map; + public ParameterRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + + public static Expression ReplaceParameters(Dictionary map, Expression exp) + { + return new ParameterRebinder(map).Visit(exp); + } + protected override Expression VisitParameter(ParameterExpression p) + { + ParameterExpression replacement; + if (map.TryGetValue(p, out replacement)) + { + p = replacement; + } + return base.VisitParameter(p); + } + } +} diff --git a/Infrastructure/Extensions/ObjectExtension.cs b/Infrastructure/Extensions/ObjectExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..a61d56f850f4891e963c19a5e6a9fcf0ef3b739d --- /dev/null +++ b/Infrastructure/Extensions/ObjectExtension.cs @@ -0,0 +1,1221 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; +using System.Xml; +using System.Xml.Linq; + +namespace Infrastructure.Extensions +{ + + public static class ObjectExtension + { + + public static bool DicKeyIsNullOrEmpty(this Dictionary dic, string key) + { + if (dic == null) + return true; + if (!dic.ContainsKey(key)) return true; + object value = dic[key]; + if (value == null || value.ToString() == "") + { + return true; + } + return false; + } + public static Dictionary ReaderToDictionary(this IDataReader Reader) + { + List> rowList = Reader.ReaderToDictionaryList(); + return rowList.Count() > 0 ? rowList[0] : null; + } + /// + /// IDataReader转换成DictionaryList + /// + /// + /// + public static List> ReaderToDictionaryList(this IDataReader Reader) + { + List> rowList = new List>(); + try + { + while (Reader.Read()) + { + Dictionary row = new Dictionary(StringComparer.OrdinalIgnoreCase); + for (var fieldCount = 0; fieldCount < Reader.FieldCount; fieldCount++) + { + row.Add(Reader.GetName(fieldCount), Reader[fieldCount]); + } + rowList.Add(row); + } + } + catch (Exception ex) { throw ex; } + finally + { + Reader.Close(); + Reader.Dispose(); + } + return rowList; + } + + public static T DicToEntity(this Dictionary dic) + { + return new List>() { dic }.DicToList().ToList()[0]; + } + public static List DicToList(this List> dicList) + { + return dicList.DicToIEnumerable().ToList(); + } + public static object DicToList(this List> dicList, Type type) + { + return typeof(ObjectExtension).GetMethod("DicToList") + .MakeGenericMethod(new Type[] { type }) + .Invoke(typeof(ObjectExtension), new object[] { dicList }); + } + + public static IEnumerable DicToIEnumerable(this List> dicList) + { + foreach (Dictionary dic in dicList) + { + T model = Activator.CreateInstance(); + foreach (PropertyInfo property in model.GetType() + .GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance)) + { + if (!dic.TryGetValue(property.Name, out object value)) continue; + property.SetValue(model, value?.ToString().ChangeType(property.PropertyType), null); + } + yield return model; + } + } + /// + /// IDataReader转换成List + /// + /// + /// + /// + public static List ReaderToList(this IDataReader Reader) + { + List objectField = new List(Reader.FieldCount); + for (int i = 0; i < Reader.FieldCount; i++) + { + objectField.Add(Reader.GetName(i).ToLower()); + } + List objectList = new List(); + try + { + while (Reader.Read()) + { + T model = Activator.CreateInstance(); + foreach (PropertyInfo property in model.GetType() + .GetProperties(BindingFlags.GetProperty + | BindingFlags.Public + | BindingFlags.Instance)) + { + if (!objectField.Contains(property.Name.ToLower())) { continue; } + if (StringExtension.IsNullOrEmpty(Reader[property.Name])) { continue; } + property.SetValue(model, Reader[property.Name].ToString().ChangeType(property.PropertyType), null); + } + objectList.Add(model); + } + } + catch (Exception ex) + { + + throw ex; + } + finally + { + Reader.Close(); + Reader.Dispose(); + } + return objectList; + } + + + public static object ChangeType(this object convertibleValue, Type type) + { + if (null == convertibleValue) return null; + + try + { + if (type == typeof(Guid) || type == typeof(Guid?)) + { + string value = convertibleValue.ToString(); + if (value == "") return null; + return Guid.Parse(value); + } + + if (!type.IsGenericType) return Convert.ChangeType(convertibleValue, type); + if (type.ToString() == "System.Nullable`1[System.Boolean]" || type.ToString() == "System.Boolean") + { + if (convertibleValue.ToString() == "0") + return false; + return true; + } + Type genericTypeDefinition = type.GetGenericTypeDefinition(); + if (genericTypeDefinition == typeof(Nullable<>)) + { + return Convert.ChangeType(convertibleValue, Nullable.GetUnderlyingType(type)); + } + } + catch + { + return null; + } + return null; + } + /// + /// 将集合转换为数据集。 + /// + /// 转换的元素类型。 + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToDataSet(this IEnumerable list, bool generic = true) + { + return ListToDataSet(list, generic); + } + + /// + /// 将集合转换为数据集。 + /// + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToDataSet(this IEnumerable list, bool generic = true) + { + return ListToDataSet(list, generic); + } + + /// + /// 将集合转换为数据集。 + /// + /// 转换的元素类型。 + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToDataSet(this IEnumerable list, bool generic = true) + { + return ListToDataSet(list, typeof(T), generic); + } + + /// + /// 将实例转换为集合数据集。 + /// + /// 实例类型。 + /// 实例。 + /// 是否生成泛型数据集。 + /// 数据集。 + public static DataSet ToListSet(this T o, bool generic = true) + { + if (o is IEnumerable) + { + return ListToDataSet(o as IEnumerable, generic); + } + else + { + return ListToDataSet(new T[] { o }, generic); + } + } + + /// + /// 将可序列化实例转换为XmlDocument。 + /// + /// 实例类型。 + /// 实例。 + /// XmlDocument。 + public static XmlDocument ToXmlDocument(this T o) + { + XmlDocument xmlDocument = new XmlDocument + { + InnerXml = o.ToListSet().GetXml() + }; + return xmlDocument; + } + + /// + /// 将集合转换为数据集。 + /// + /// 集合。 + /// 转换的元素类型。 + /// 是否生成泛型数据集。 + /// 转换后的数据集。 + private static DataSet ListToDataSet(IEnumerable list, Type t, bool generic) + { + DataSet ds = new DataSet("Data"); + if (t == null) + { + if (list != null) + { + foreach (var i in list) + { + if (i == null) + { + continue; + } + t = i.GetType(); + break; + } + } + if (t == null) + { + return ds; + } + } + ds.Tables.Add(t.Name); + //如果集合中元素为DataSet扩展涉及到的基本类型时,进行特殊转换。 + if (t.IsValueType || t == typeof(string)) + { + ds.Tables[0].TableName = "Info"; + ds.Tables[0].Columns.Add(t.Name); + if (list != null) + { + foreach (var i in list) + { + DataRow addRow = ds.Tables[0].NewRow(); + addRow[t.Name] = i; + ds.Tables[0].Rows.Add(addRow); + } + } + return ds; + } + //处理模型的字段和属性。 + var fields = t.GetFields(); + var properties = t.GetProperties(); + foreach (var j in fields) + { + if (!ds.Tables[0].Columns.Contains(j.Name)) + { + if (generic) + { + ds.Tables[0].Columns.Add(j.Name, j.FieldType); + } + else + { + ds.Tables[0].Columns.Add(j.Name); + } + } + } + foreach (var j in properties) + { + if (!ds.Tables[0].Columns.Contains(j.Name)) + { + if (generic) + { + ds.Tables[0].Columns.Add(j.Name, j.PropertyType); + } + else + { + ds.Tables[0].Columns.Add(j.Name); + } + } + } + if (list == null) + { + return ds; + } + //读取list中元素的值。 + foreach (var i in list) + { + if (i == null) + { + continue; + } + DataRow addRow = ds.Tables[0].NewRow(); + foreach (var j in fields) + { + MemberExpression field = Expression.Field(Expression.Constant(i), j.Name); + LambdaExpression lambda = Expression.Lambda(field, new ParameterExpression[] { }); + Delegate func = lambda.Compile(); + object value = func.DynamicInvoke(); + addRow[j.Name] = value; + } + foreach (var j in properties) + { + MemberExpression property = Expression.Property(Expression.Constant(i), j); + LambdaExpression lambda = Expression.Lambda(property, new ParameterExpression[] { }); + Delegate func = lambda.Compile(); + object value = func.DynamicInvoke(); + addRow[j.Name] = value; + } + ds.Tables[0].Rows.Add(addRow); + } + return ds; + } + + /// + /// 将集合转换为数据集。 + /// + /// 转换的元素类型。 + /// 集合。 + /// 是否生成泛型数据集。 + /// 数据集。 + private static DataSet ListToDataSet(IEnumerable list, bool generic) + { + return ListToDataSet(list, typeof(T), generic); + } + + /// + /// 将集合转换为数据集。 + /// + /// 集合。 + /// 是否转换为字符串形式。 + /// 转换后的数据集。 + private static DataSet ListToDataSet(IEnumerable list, bool generic) + { + return ListToDataSet(list, null, generic); + } + + /// + /// 获取DataSet第一表,第一行,第一列的值。 + /// + /// DataSet数据集。 + /// 值。 + public static object GetData(this DataSet ds) + { + if ( + ds == null + || ds.Tables.Count == 0 + ) + { + return string.Empty; + } + else + { + return ds.Tables[0].GetData(); + } + } + + /// + /// 获取DataTable第一行,第一列的值。 + /// + /// DataTable数据集表。 + /// 值。 + public static object GetData(this DataTable dt) + { + if ( + dt.Columns.Count == 0 + || dt.Rows.Count == 0 + ) + { + return string.Empty; + } + else + { + return dt.Rows[0][0]; + } + } + + /// + /// 获取DataSet第一个匹配columnName的值。 + /// + /// 数据集。 + /// 列名。 + /// 值。 + public static object GetData(this DataSet ds, string columnName) + { + if ( + ds == null + || ds.Tables.Count == 0 + ) + { + return string.Empty; + } + foreach (DataTable dt in ds.Tables) + { + object o = dt.GetData(columnName); + if (!string.IsNullOrEmpty(o.ToString())) + { + return o; + } + } + return string.Empty; + } + + /// + /// 获取DataTable第一个匹配columnName的值。 + /// + /// 数据表。 + /// 列名。 + /// 值。 + public static object GetData(this DataTable dt, string columnName) + { + if (string.IsNullOrEmpty(columnName)) + { + return GetData(dt); + } + if ( + dt.Columns.Count == 0 + || dt.Columns.IndexOf(columnName) == -1 + || dt.Rows.Count == 0 + ) + { + return string.Empty; + } + return dt.Rows[0][columnName]; + } + + /// + /// 将object转换为string类型信息。 + /// + /// object。 + /// 默认值。 + /// string。 + public static string ToString(this object o, string t) + { + string info = string.Empty; + if (o == null) + { + info = t; + } + else + { + info = o.ToString(); + } + return info; + } + + /// + /// 将DateTime?转换为string类型信息。 + /// + /// DateTime?。 + /// 标准或自定义日期和时间格式的字符串。 + /// 默认值。 + /// string。 + public static string ToString(this DateTime? o, string format, string t) + { + string info = string.Empty; + if (o == null) + { + info = t; + } + else + { + info = o.Value.ToString(format); + } + return info; + } + + /// + /// 将TimeSpan?转换为string类型信息。 + /// + /// TimeSpan?。 + /// 标准或自定义时间格式的字符串。 + /// 默认值。 + /// string。 + public static string ToString(this TimeSpan? o, string format, string t) + { + string info = string.Empty; + if (o == null) + { + info = t; + } + else + { + info = o.Value.ToString(format); + } + return info; + } + + + + /// + /// 将object转换为byte类型信息。 + /// + /// object。 + /// 默认值。 + /// byte。 + public static byte ToByte(this object o, byte t = default(byte)) + { + if (!byte.TryParse(o.ToString(string.Empty), out byte info)) + { + info = t; + } + return info; + } + + public static byte[] ToBytes(this object obj) + { + if (obj == null) + return null; + var bf = new BinaryFormatter(); + using (var ms = new MemoryStream()) + { + bf.Serialize(ms, obj); + return ms.ToArray(); + } + } + + public static object ToObject(this byte[] source) + { + using (var memStream = new MemoryStream()) + { + var bf = new BinaryFormatter(); + memStream.Write(source, 0, source.Length); + memStream.Seek(0, SeekOrigin.Begin); + var obj = bf.Deserialize(memStream); + return obj; + } + } + + /// + /// 将object转换为char类型信息。 + /// + /// object。 + /// 默认值。 + /// char。 + public static char ToChar(this object o, char t = default(char)) + { + if (!char.TryParse(o.ToString(string.Empty), out char info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为int类型信息。 + /// + /// object。 + /// 默认值。 + /// int。 + public static int ToInt(this object o, int t = default(int)) + { + if (!int.TryParse(o.ToString(string.Empty), out int info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为double类型信息。 + /// + /// object。 + /// 默认值。 + /// double。 + public static double ToDouble(this object o, double t = default(double)) + { + double info; + if (!double.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为decimal类型信息。 + /// + /// object。 + /// 默认值。 + /// decimal。 + public static decimal ToDecimal(this object o, decimal t = default(decimal)) + { + decimal info; + if (!decimal.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为float类型信息。 + /// + /// object。 + /// 默认值。 + /// float。 + public static float ToFloat(this object o, float t = default(float)) + { + if (!float.TryParse(o.ToString(string.Empty), out float info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为long类型信息。 + /// + /// object。 + /// 默认值。 + /// long。 + public static long ToLong(this object o, long t = default(long)) + { + long info; + if (!long.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为bool类型信息。 + /// + /// object。 + /// 默认值。 + /// bool。 + public static bool ToBool(this object o, bool t = default(bool)) + { + bool info; + if (!bool.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为sbyte类型信息。 + /// + /// object。 + /// 默认值。 + /// sbyte。 + public static sbyte ToSbyte(this object o, sbyte t = default(sbyte)) + { + sbyte info; + if (!sbyte.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为short类型信息。 + /// + /// object。 + /// 默认值。 + /// short。 + public static short ToShort(this object o, short t = default(short)) + { + short info; + if (!short.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为ushort类型信息。 + /// + /// object。 + /// 默认值。 + /// ushort。 + public static ushort ToUShort(this object o, ushort t = default(ushort)) + { + ushort info; + if (!ushort.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为ulong类型信息。 + /// + /// object。 + /// 默认值。 + /// ulong。 + public static ulong ToULong(this object o, ulong t = default(ulong)) + { + ulong info; + if (!ulong.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为Enum[T]类型信息。 + /// + /// object。 + /// 默认值。 + /// Enum[T]。 + public static T ToEnum(this object o, T t = default(T)) + where T : struct + { + if (!System.Enum.TryParse(o.ToString(string.Empty), out T info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为DateTime类型信息。 + /// + /// object。 + /// 默认值。 + /// DateTime。 + public static DateTime ToDateTime(this object o, DateTime t = default(DateTime)) + { + if (t == default(DateTime)) + { + t = new DateTime(1753, 1, 1); + } + DateTime info; + if (!DateTime.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为TimeSpan类型信息。 + /// + /// object。 + /// 默认值。 + /// TimeSpan。 + public static TimeSpan ToTimeSpan(this object o, TimeSpan t = default(TimeSpan)) + { + if (t == default(TimeSpan)) + { + t = new TimeSpan(0, 0, 0); + } + TimeSpan info; + if (!TimeSpan.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + /// + /// 将object转换为Guid类型信息。 + /// + /// object。 + /// 默认值。 + /// Guid。 + public static Guid ToGuid(this object o, Guid t = default(Guid)) + { + Guid info; + if (!Guid.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + return info; + } + + private static Regex BoolRegex = new Regex("(?(true|false))", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取bool类型信息。 + /// + /// object。 + /// bool。 + public static bool GetBool(this string value) + { + bool.TryParse(value, out bool result); + return result; + } + + private static Regex IntRegex = new Regex("(?-?\\d+)", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + + + private static Regex DecimalRegex = new Regex("(?-?\\d+(\\.\\d+)?)", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取decimal类型信息。 + /// + /// object。 + /// decimal。 + public static decimal? GetDecimal(this object o) + { + decimal info; + if (!decimal.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return info; + } + + + + /// + /// 从object中获取正数信息。 + /// + /// object。 + /// decimal。 + public static decimal? GetPositiveNumber(this object o) + { + decimal info; + if (!decimal.TryParse(DecimalRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return Math.Abs(info); + } + + private static Regex DateTimeRegex = new Regex("(?(((\\d+)[/年-](0?[13578]|1[02])[/月-](3[01]|[12]\\d|0?\\d)[日]?)|((\\d+)[/年-](0?[469]|11)[/月-](30|[12]\\d|0?\\d)[日]?)|((\\d+)[/年-]0?2[/月-](2[0-8]|1\\d|0?\\d)[日]?))(\\s((2[0-3]|[0-1]\\d)):[0-5]\\d:[0-5]\\d)?)", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取DateTime?类型信息。 + /// + /// object。 + /// DateTime?。 + public static DateTime? GetDateTime1(this object o) + { + DateTime info; + if (!DateTime.TryParse(DateTimeRegex.Match(o.ToString(string.Empty)).Groups["info"].Value.Replace("年", "-").Replace("月", "-").Replace("/", "-").Replace("日", ""), out info)) + { + return null; + } + return info; + } + + private static Regex TimeSpanRegex = new Regex("(?-?(\\d+\\.(([0-1]\\d)|(2[0-3])):[0-5]\\d:[0-5]\\d)|((([0-1]\\d)|(2[0-3])):[0-5]\\d:[0-5]\\d)|(\\d+))", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取TimeSpan?类型信息。 + /// + /// object。 + /// TimeSpan?。 + public static TimeSpan? GetTimeSpan(this object o) + { + TimeSpan info; + if (!TimeSpan.TryParse(TimeSpanRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return info; + } + + private static Regex GuidRegex = new Regex("(?\\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\\}{0,1})", RegexOptions.IgnoreCase | RegexOptions.Singleline); + + /// + /// 从object中获取Guid?类型信息。 + /// + /// object。 + /// Guid?。 + public static Guid? GetGuid(this object o) + { + Guid info; + if (!Guid.TryParse(GuidRegex.Match(o.ToString(string.Empty)).Groups["info"].Value, out info)) + { + return null; + } + return info; + } + + /// + /// 将object转换为SqlServer中的DateTime?类型信息。 + /// + /// object。 + /// 默认值。 + /// DateTime?。 + public static DateTime? GetSqlDateTime(this object o, DateTime t = default(DateTime)) + { + DateTime info; + if (!DateTime.TryParse(o.ToString(string.Empty), out info)) + { + info = t; + } + if (info < new DateTime(1753, 1, 1) || info > new DateTime(9999, 12, 31)) + { + return null; + } + return info; + } + + /// + /// 读取XElement节点的文本内容。 + /// + /// XElement节点。 + /// 默认值。 + /// 文本内容。 + public static string Value(this XElement xElement, string t = default(string)) + { + if (xElement == null) + { + return t; + } + else + { + return xElement.Value; + } + } + + /// + /// 获取与指定键相关的值。 + /// + /// 键类型。 + /// 值类型。 + /// 表示键/值对象的泛型集合。 + /// 键。 + /// 默认值。 + /// 值。 + public static TValue GetValue(this IDictionary dictionary, TKey key, TValue t = default(TValue)) + { + TValue value = default(TValue); + if (dictionary == null || key == null) + { + return t; + } + if (!dictionary.TryGetValue(key, out value)) + { + value = t; + } + return value; + } + + /// + /// 获取与指定键相关或者第一个的值。 + /// + /// 键类型。 + /// 值类型。 + /// 表示键/值对象的泛型集合。 + /// 键。 + /// 默认值。 + /// 值。 + public static TValue GetFirstOrDefaultValue(this IDictionary dictionary, TKey key, TValue t = default(TValue)) + { + TValue value = default(TValue); + if (dictionary == null || key == null) + { + return t; + } + if (!dictionary.TryGetValue(key, out value)) + { + if (dictionary.Count() == 0) + { + value = t; + } + else + { + value = dictionary.FirstOrDefault().Value; + } + } + return value; + } + + /// + /// 获取具有指定 System.Xml.Linq.XName 的第一个(按文档顺序)子元素。 + /// + /// XContainer。 + /// 要匹配的 System.Xml.Linq.XName。 + /// 是否返回同名默认值。 + /// 与指定 System.Xml.Linq.XName 匹配的 System.Xml.Linq.XElement,或者为 null。 + public static XElement Element(this XContainer xContainer, XName xName, bool t) + { + XElement info; + if (xContainer == null) + { + info = null; + } + else + { + info = xContainer.Element(xName); + } + if (t && info == null) + { + info = new XElement(xName); + } + return info; + } + + /// + /// 按文档顺序返回此元素或文档的子元素集合。 + /// + /// XContainer。 + /// 是否返回非空默认值。 + /// System.Xml.Linq.XElement 的按文档顺序包含此System.Xml.Linq.XContainer 的子元素,或者非空默认值。 + public static IEnumerable Elements(this XContainer xContainer, bool t) + { + IEnumerable info; + if (xContainer == null) + { + info = null; + } + else + { + info = xContainer.Elements(); + } + if (t && info == null) + { + info = new List(); + } + return info; + } + + /// + /// 按文档顺序返回此元素或文档的经过筛选的子元素集合。集合中只包括具有匹配 System.Xml.Linq.XName 的元素。 + /// + /// XContainer。 + /// 要匹配的 System.Xml.Linq.XName。 + /// 是否返回非空默认值。 + /// System.Xml.Linq.XElement 的按文档顺序包含具有匹配System.Xml.Linq.XName 的 System.Xml.Linq.XContainer 的子级,或者非空默认值。 + public static IEnumerable Elements(this XContainer xContainer, XName xName, bool t) + { + IEnumerable info; + if (xContainer == null) + { + info = null; + } + else + { + info = xContainer.Elements(xName); + } + if (t && info == null) + { + info = new List(); + } + return info; + } + + /// + /// 删除html标签。 + /// + /// 输入的字符串。 + /// 没有html标签的字符串。 + public static string RemoveHTMLTags(this string html) + { + return Regex.Replace(Regex.Replace(Regex.Replace((html ?? string.Empty).Replace(" ", " ").Replace("\r\n", " ").Replace("\n", " ").Replace("\r", " ").Replace("\t", " "), "<\\/?[^>]+>", "\r\n"), "(\r\n)+", "\r\n"), "(\\s)+", " ").Trim(); + } + + /// + /// 字符串转换为文件名。 + /// + /// 字符串。 + /// 文件名。 + public static string ToFileName(this string s) + { + return Regex.Replace(s.ToString(string.Empty), @"[\\/:*?<>|]", "_").Replace("\t", " ").Replace("\r\n", " ").Replace("\"", " "); + } + + + /// + /// 获取默认非空字符串。 + /// + /// 首选默认非空字符串。 + /// 依次非空字符串可选项。 + /// 默认非空字符串。若无可选项则返回string.Empty。 + public static string DefaultStringIfEmpty(this string s, params string[] args) + { + if (string.IsNullOrEmpty(s)) + { + return string.Empty; + } + foreach (string i in args) + { + if (!string.IsNullOrEmpty(i) && !string.IsNullOrEmpty(i.Trim())) + { + return i; + } + } + + return (s ?? string.Empty); + } + + /// + /// 对 URL 字符串进行编码。 + /// + /// 要编码的文本。 + /// 匹配要编码的文本。 + /// 指定编码方案的 System.Text.Encoding 对象。 + /// 一个已编码的字符串。 + public static string ToUrlEncodeString(this string s, Regex regex = default(Regex), Encoding encoding = null) + { + if (encoding == null) + { + encoding = Encoding.UTF8; + } + if (regex == null) + { + return HttpUtility.UrlEncode(s, encoding); + } + List l = new List(); + foreach (char i in s) + { + string t = i.ToString(); + l.Add(regex.IsMatch(t) ? HttpUtility.UrlEncode(t, encoding) : t); + } + return string.Join(string.Empty, l); + } + + /// + /// 对 URL 字符串进行编码。 + /// + /// 要编码的文本。 + /// 匹配要编码的文本。 + /// 指定编码方案的 System.Text.Encoding 对象。 + /// 一个已编码的字符串。 + public static string ToUrlEncodeString(this string s, string regex, Encoding encoding = null) + { + return ToUrlEncodeString(s, new Regex(regex), encoding); + } + + /// + /// 将日期转换为UNIX时间戳字符串 + /// + /// + /// + public static string ToUnixTimeStamp(this DateTime date) + { + DateTime startTime = TimeZoneInfo.ConvertTimeToUtc(new DateTime(1970, 1, 1)); + string timeStamp = date.Subtract(startTime).Ticks.ToString(); + return timeStamp.Substring(0, timeStamp.Length - 7); + } + + private static readonly Regex MobileRegex = new Regex("^1[3|4|5|7|8][0-9]\\d{4,8}$"); + private static readonly Regex EmailRegex = new Regex("^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\\.[a-zA-Z0-9_-]{2,3}){1,2})$"); + + /// + /// 判断当前字符串是否是移动电话号码 + /// + /// + /// + public static bool IsMobile(this string mobile) + { + return MobileRegex.IsMatch(mobile); + } + + /// + /// 判断当前字符串是否为邮箱 + /// + /// + /// + public static bool IsEmail(this string email) + { + return EmailRegex.IsMatch(email); + } + + } + + + + /// + /// 标记。 + /// + public enum Flag + { + /// + /// 默认。 + /// + Default, + + /// + /// 真。 + /// + True, + + /// + /// 假。 + /// + False + } +} diff --git a/Infrastructure/Extensions/SecurityEncDecryptExtension.cs b/Infrastructure/Extensions/SecurityEncDecryptExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..4902b7ed36e76a57bf9dc2033abab9ed85929de9 --- /dev/null +++ b/Infrastructure/Extensions/SecurityEncDecryptExtension.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Infrastructure.Extensions +{ + public static class SecurityEncDecryptExtensions + { + + private static byte[] Keys = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }; + /// + /// DES加密字符串 + /// + /// 待加密的字符串 + /// 加密密钥,要求为16位 + /// 加密成功返回加密后的字符串,失败返回源串 + + public static string EncryptDES(this string encryptString, string encryptKey) + { + try + { + byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 16)); + byte[] rgbIV = Keys; + byte[] inputByteArray = Encoding.UTF8.GetBytes(encryptString); + + using (var DCSP = Aes.Create()) + { + using (MemoryStream mStream = new MemoryStream()) + { + using (CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write)) + { + cStream.Write(inputByteArray, 0, inputByteArray.Length); + cStream.FlushFinalBlock(); + return Convert.ToBase64String(mStream.ToArray()).Replace('+', '_').Replace('/', '~'); + } + } + } + } + catch (Exception ex) + { + throw new Exception("密码加密异常" + ex.Message); + } + + } + + /// + /// DES解密字符串 + /// + /// 待解密的字符串 + /// 解密密钥,要求为16位,和加密密钥相同 + /// 解密成功返回解密后的字符串,失败返源串 + + public static string DecryptDES(this string decryptString, string decryptKey) + { + byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey.Substring(0, 16)); + byte[] rgbIV = Keys; + byte[] inputByteArray = Convert.FromBase64String(decryptString.Replace('_', '+').Replace('~', '/')); + using (var DCSP = Aes.Create()) + { + using (MemoryStream mStream = new MemoryStream()) + { + using (CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write)) + { + byte[] inputByteArrays = new byte[inputByteArray.Length]; + cStream.Write(inputByteArray, 0, inputByteArray.Length); + cStream.FlushFinalBlock(); + return Encoding.UTF8.GetString(mStream.ToArray()); + } + } + } + + } + public static bool TryDecryptDES(this string decryptString, string decryptKey, out string result) + { + result = ""; + try + { + result = DecryptDES(decryptString, decryptKey); + return true; + } + catch + { + return false; + } + } + } +} diff --git a/Infrastructure/Extensions/ServerExtension.cs b/Infrastructure/Extensions/ServerExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..844fcf8b66fd73e170068a90fa2c213c620bb5a1 --- /dev/null +++ b/Infrastructure/Extensions/ServerExtension.cs @@ -0,0 +1,28 @@ +using Infrastructure.Extensions.AutofacManager; +using Infrastructure.Provider; + +namespace Infrastructure.Extensions +{ + public static class ServerExtension + { + /// + /// 返回的路径后面不带/,拼接时需要自己加上/ + /// + /// + /// + public static string MapPath(this string path) + { + return MapPath(path, false); + } + /// + /// + /// + /// + /// 获取wwwroot路径 + /// + public static string MapPath(this string path,bool rootPath) + { + return AutofacContainerModule.GetService().MapPath(path,rootPath); + } + } +} diff --git a/Infrastructure/Extensions/StringExtension.cs b/Infrastructure/Extensions/StringExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..c46ea0508a0e646cae9dbd5fc6383de44aae53a0 --- /dev/null +++ b/Infrastructure/Extensions/StringExtension.cs @@ -0,0 +1,610 @@ +using System; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using Infrastructure.Const; + +namespace Infrastructure.Extensions +{ + public static class StringExtension + { + public static bool _windows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + /// + /// 自动调整windows/linux下面文件目录斜杠的处理 + /// + /// + /// + public static string ReplacePath(this string path) + { + if (string.IsNullOrEmpty(path)) + return ""; + if (_windows) + return path.Replace("/", "\\"); + return path.Replace("\\", "/"); + + } + + /// + /// 把一个字符串转成驼峰规则的字符串 + /// + /// + /// + public static string ToCamelCase(this string str) + { + if(!string.IsNullOrEmpty(str) && str.Length > 1) + { + return Char.ToLowerInvariant(str[0]) + str.Substring(1); + } + return str; + } + + private static DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0); + + private static long longTime = 621355968000000000; + + private static int samllTime = 10000000; + /// + /// 获取时间戳 + /// + /// + /// + public static long GetTimeStamp(this DateTime dateTime) + { + return (dateTime.ToUniversalTime().Ticks - longTime) / samllTime; + } + /// + /// 时间戳转换成日期 + /// + /// + /// + public static DateTime GetTimeSpmpToDate(this object timeStamp) + { + if (timeStamp == null) return dateStart; + DateTime dateTime = new DateTime(longTime + Convert.ToInt64(timeStamp) * samllTime, DateTimeKind.Utc).ToLocalTime(); + return dateTime; + } + + + public static bool IsUrl(this string str) + { + if (string.IsNullOrEmpty(str)) + return false; + string Url = @"(http://)?([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?"; + return Regex.IsMatch(str, Url); + + } + /// + /// 判断是不是正确的手机号码 + /// + /// + /// + public static bool IsPhoneNo(this string input) + { + if (string.IsNullOrEmpty(input)) + return false; + if (input.Length != 11) + return false; + + if (new Regex(@"^1[3578][01379]\d{8}$").IsMatch(input) + || new Regex(@"^1[34578][01256]\d{8}").IsMatch(input) + || new Regex(@"^(1[012345678]\d{8}|1[345678][0123456789]\d{8})$").IsMatch(input) + ) + return true; + return false; + } + + public static string GetDBCondition(this string stringType) + { + string reslut = ""; + switch (stringType?.ToLower()) + { + case HtmlElementType.droplist: + case HtmlElementType.selectlist: + case HtmlElementType.textarea: + case HtmlElementType.checkbox: + reslut = HtmlElementType.Contains; + break; + case HtmlElementType.thanorequal: + reslut = HtmlElementType.ThanOrEqual; + break; + case HtmlElementType.lessorequal: + reslut = HtmlElementType.LessOrequal; + break; + case HtmlElementType.gt: + reslut = HtmlElementType.GT; + break; + case HtmlElementType.lt: + reslut = HtmlElementType.lt; + break; + case HtmlElementType.like: + reslut = HtmlElementType.like; + break; + default: + reslut = HtmlElementType.Equal; + break; + } + return reslut; + } + + public static LinqExpressionType GetLinqCondition(this string stringType) + { + LinqExpressionType linqExpression; + switch (stringType) + { + case HtmlElementType.Contains: + linqExpression = LinqExpressionType.In; + break; + case HtmlElementType.ThanOrEqual: + linqExpression = LinqExpressionType.ThanOrEqual; + break; + case HtmlElementType.LessOrequal: + linqExpression = LinqExpressionType.LessThanOrEqual; + break; + case HtmlElementType.GT: + linqExpression = LinqExpressionType.GreaterThan; + break; + case HtmlElementType.lt: + linqExpression = LinqExpressionType.LessThan; + break; + case HtmlElementType.like: + linqExpression = LinqExpressionType.Contains; + break; + default: + linqExpression = LinqExpressionType.Equal; + break; + } + return linqExpression; + } + + public static bool GetGuid(this string guid, out Guid outId) + { + Guid emptyId = Guid.Empty; + return Guid.TryParse(guid, out outId); + } + + public static bool IsGuid(this string guid) + { + Guid newId; + return GetGuid(guid, out newId); + } + + public static bool IsInt(this object obj) + { + if (obj == null) + return false; + bool reslut = Int32.TryParse(obj.ToString(), out int _number); + return reslut; + + } + public static bool IsDate(this object str) + { + return IsDate(str, out _); + } + public static bool IsDate(this object str, out DateTime dateTime) + { + dateTime = DateTime.Now; + if (str == null || str.ToString() == "") + { + return false; + } + return DateTime.TryParse(str.ToString(), out dateTime); + } + /// + /// 根据传入格式判断是否为小数 + /// + /// + /// 18,5 + /// + public static bool IsNumber(this string str, string formatString) + { + if (string.IsNullOrEmpty(str)) + return false; + int precision = 32; + int scale = 5; + try + { + if (string.IsNullOrEmpty(formatString)) + { + precision = 10; + scale = 2; + } + else + { + string[] numbers = formatString.Split(','); + precision = Convert.ToInt32(numbers[0]); + scale = numbers.Length == 0 ? 2 : Convert.ToInt32(numbers[1]); + } + } + catch { }; + return IsNumber(str, precision, scale); + } + /**/ + /// + /// 判断一个字符串是否为合法数字(指定整数位数和小数位数) + /// + /// 字符串 + /// 整数位数 + /// 小数位数 + /// + public static bool IsNumber(this string str, int precision, int scale) + { + if ((precision == 0) && (scale == 0)) + { + return false; + } + string pattern = @"(^\d{1," + precision + "}"; + if (scale > 0) + { + pattern += @"\.\d{0," + scale + "}$)|" + pattern; + } + pattern += "$)"; + return Regex.IsMatch(str, pattern); + } + + + public static bool IsNullOrEmpty(this object str) + { + if (str == null) + return true; + return str.ToString() == ""; + } + + + public static int GetInt(this object obj) + { + if (obj == null) + return 0; + int.TryParse(obj.ToString(), out int _number); + return _number; + + } + + /// + /// 获取 object 中的枚举值 + /// + /// + /// + /// + public static long GetLong(this object obj) + { + if (obj == null) + return 0; + + try + { + return Convert.ToInt64(Convert.ToDouble(obj)); + } + catch + { + return 0; + } + } + + /// + /// 获取 object 中的 float + /// + /// + /// + + public static float GetFloat(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return 0; + + try + { + return float.Parse(obj.ToString()); + } + catch + { + return 0; + } + } + + public static double GetDouble(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return 0; + + try + { + return Convert.ToDouble(obj); + } + catch + { + return 0; + } + } + /// + /// 获取 object 中的 decimal + /// + /// + /// + /// + public static decimal GetDecimal(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return 0; + + try + { + return Convert.ToDecimal(obj); + } + catch + { + return 0; + } + } + + /// + /// 获取 object 中的 decimal + /// + /// + /// + /// + public static dynamic GetDynamic(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return null; + + try + { + string str = obj.ToString(); + if (IsNumber(str, 25, 15)) return Convert.ToDecimal(obj); + else return str; + } + catch + { + return string.Empty; + } + } + + public static DateTime? GetDateTime(this object obj) + { + if (System.DBNull.Value.Equals(obj) || null == obj) + return null; + bool result = DateTime.TryParse(obj.ToString(), out DateTime dateTime); + if (!result) + return null; + return dateTime; + } + + + + public static object ParseTo(this string str, string type) + { + switch (type) + { + case "System.Boolean": + return ToBoolean(str); + case "System.SByte": + return ToSByte(str); + case "System.Byte": + return ToByte(str); + case "System.UInt16": + return ToUInt16(str); + case "System.Int16": + return ToInt16(str); + case "System.uInt32": + return ToUInt32(str); + case "System.Int32": + return ToInt32(str); + case "System.UInt64": + return ToUInt64(str); + case "System.Int64": + return ToInt64(str); + case "System.Single": + return ToSingle(str); + case "System.Double": + return ToDouble(str); + case "System.Decimal": + return ToDecimal(str); + case "System.DateTime": + return ToDateTime(str); + case "System.Guid": + return ToGuid(str); + } + throw new NotSupportedException(string.Format("The string of \"{0}\" can not be parsed to {1}", str, type)); + } + + public static sbyte? ToSByte(this string value) + { + sbyte value2; + if (sbyte.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static byte? ToByte(this string value) + { + byte value2; + if (byte.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static ushort? ToUInt16(this string value) + { + ushort value2; + if (ushort.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static short? ToInt16(this string value) + { + if (short.TryParse(value, out short value2)) + { + return value2; + } + return null; + } + + public static uint? ToUInt32(this string value) + { + uint value2; + if (uint.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static ulong? ToUInt64(this string value) + { + ulong value2; + if (ulong.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static long? ToInt64(this string value) + { + long value2; + if (long.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static float? ToSingle(this string value) + { + float value2; + if (float.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static double? ToDouble(this string value) + { + double value2; + if (double.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static decimal? ToDecimal(this string value) + { + decimal value2; + if (decimal.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static bool? ToBoolean(this string value) + { + bool value2; + if (bool.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + + + public static Guid? ToGuid(this string str) + { + Guid value; + if (Guid.TryParse(str, out value)) + { + return value; + } + return null; + } + + public static DateTime? ToDateTime(this string value) + { + DateTime value2; + if (DateTime.TryParse(value, out value2)) + { + return value2; + } + return null; + } + + public static int? ToInt32(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return null; + } + int value; + if (int.TryParse(input, out value)) + { + return value; + } + return null; + } + + /// + /// 替换空格字符 + /// + /// + /// 替换为该字符 + /// 替换后的字符串 + public static string ReplaceWhitespace(this string input, string replacement = "") + { + return string.IsNullOrEmpty(input) ? null : Regex.Replace(input, "\\s", replacement, RegexOptions.Compiled); + } + + private static char[] randomConstant ={ + '0','1','2','3','4','5','6','7','8','9', + 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z' + }; + /// + /// 生成指定长度的随机数 + /// + /// + /// + public static string GenerateRandomNumber(this int length) + { + System.Text.StringBuilder newRandom = new System.Text.StringBuilder(62); + Random rd = new Random(); + for (int i = 0; i < length; i++) + { + newRandom.Append(randomConstant[rd.Next(62)]); + } + return newRandom.ToString(); + } + public static string MaxSubstring(this string origin, int maxLength) + { + return origin.Length >= maxLength ? origin.Substring(0, maxLength) : origin; + } + + public static string ToMd5(this string origin) + { + if (string.IsNullOrWhiteSpace(origin)) + { + return string.Empty; + } + + var md5Algorithm = MD5.Create(); + var utf8Bytes = Encoding.UTF8.GetBytes(origin); + var md5Hash = md5Algorithm.ComputeHash(utf8Bytes); + var hexString = new StringBuilder(); + foreach (var hexByte in md5Hash) + { + hexString.Append(hexByte.ToString("x2")); + } + return hexString.ToString(); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Filter.cs b/Infrastructure/Filter.cs index 4fa6782b60c8758950bebba0501922e86b9135e7..584b40204c6d05c608f7b40c60667de9df79cdf7 100644 --- a/Infrastructure/Filter.cs +++ b/Infrastructure/Filter.cs @@ -1,9 +1,25 @@ namespace Infrastructure { + public class Filter { public string Key { get; set; } public string Value { get; set; } public string Contrast { get; set; } + + public string Text { get; set; } } + + public class FilterGroup + { + /// + /// or /and + /// + public string Operation { get; set; } + public Filter[] Filters { get; set; } + public FilterGroup[] Children { get; set; } + } + + + } \ No newline at end of file diff --git a/Infrastructure/CommonHelper.cs b/Infrastructure/Helpers/CommonHelper.cs similarity index 99% rename from Infrastructure/CommonHelper.cs rename to Infrastructure/Helpers/CommonHelper.cs index e59f6a272fd3c20fd8a1753ec77961deb492ad38..81752e60671273df47acb338feeeec8d34ac64a7 100644 --- a/Infrastructure/CommonHelper.cs +++ b/Infrastructure/Helpers/CommonHelper.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text; -namespace Infrastructure +namespace Infrastructure.Helpers { /// /// 常用公共类 diff --git a/Infrastructure/Helpers/DateTimeHelper.cs b/Infrastructure/Helpers/DateTimeHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..07a29ceb529a9e0f4a6121e7d801c6e2e2389691 --- /dev/null +++ b/Infrastructure/Helpers/DateTimeHelper.cs @@ -0,0 +1,42 @@ +using System; + +namespace Infrastructure.Helpers +{ + public class DateTimeHelper + { + public static string FriendlyDate(DateTime? date) + { + if (!date.HasValue) return string.Empty; + + string strDate = date.Value.ToString("yyyy-MM-dd"); + string vDate = string.Empty; + if(DateTime.Now.ToString("yyyy-MM-dd")==strDate) + { + vDate = "今天"; + } + else if (DateTime.Now.AddDays(1).ToString("yyyy-MM-dd") == strDate) + { + vDate = "明天"; + } + else if (DateTime.Now.AddDays(2).ToString("yyyy-MM-dd") == strDate) + { + vDate = "后天"; + } + else if (DateTime.Now.AddDays(-1).ToString("yyyy-MM-dd") == strDate) + { + vDate = "昨天"; + } + else if (DateTime.Now.AddDays(2).ToString("yyyy-MM-dd") == strDate) + { + vDate = "前天"; + } + else + { + vDate = strDate; + } + + return vDate; + } + } +} + diff --git a/Infrastructure/Helpers/FileHelper.cs b/Infrastructure/Helpers/FileHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..653abb7e5cb41e335d574bb4a8a67fbdaf759e4f --- /dev/null +++ b/Infrastructure/Helpers/FileHelper.cs @@ -0,0 +1,339 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Infrastructure.Extensions; + +namespace Infrastructure.Helpers +{ + public class FileHelper + { + private static object _filePathObj = new object(); + + /// + /// 通过迭代器读取平面文件行内容(必须是带有\r\n换行的文件,百万行以上的内容读取效率存在问题,适用于100M左右文件,行100W内,超出的会有卡顿) + /// + /// 文件全路径 + /// 分页页数 + /// 分页大小 + /// 是否最后一行向前读取,默认从前向后读取 + /// + public static IEnumerable ReadPageLine(string fullPath, int page, int pageSize, bool seekEnd = false) + { + if (page <= 0) + { + page = 1; + } + fullPath = StringExtension.ReplacePath(fullPath); + var lines = File.ReadLines(fullPath, Encoding.UTF8); + if (seekEnd) + { + int lineCount = lines.Count(); + int linPageCount = (int)Math.Ceiling(lineCount / (pageSize * 1.00)); + //超过总页数,不处理 + if (page > linPageCount) + { + page = 0; + pageSize = 0; + } + else if (page == linPageCount)//最后一页,取最后一页剩下所有的行 + { + pageSize = lineCount - (page - 1) * pageSize; + if (page == 1) + { + page = 0; + } + else + { + page = lines.Count() - page * pageSize; + } + } + else + { + page = lines.Count() - page * pageSize; + } + } + else + { + page = (page - 1) * pageSize; + } + lines = lines.Skip(page).Take(pageSize); + + var enumerator = lines.GetEnumerator(); + int count = 1; + while (enumerator.MoveNext() || count <= pageSize) + { + yield return enumerator.Current; + count++; + } + enumerator.Dispose(); + } + public static bool FileExists(string path) + { + return File.Exists(StringExtension.ReplacePath(path)); + } + + public static string GetCurrentDownLoadPath() + { + return ("Download\\").MapPath(); + } + + public static bool DirectoryExists(string path) + { + return Directory.Exists(StringExtension.ReplacePath(path)); + } + + + public static string Read_File(string fullpath, string filename, string suffix) + { + return ReadFile((fullpath + "\\" + filename + suffix).MapPath()); + } + public static string ReadFile(string fullName) + { + // Encoding code = Encoding.GetEncoding(); //Encoding.GetEncoding("gb2312"); + string temp = fullName.MapPath().ReplacePath(); + string str = ""; + if (!File.Exists(temp)) + { + return str; + } + StreamReader sr = null; + try + { + sr = new StreamReader(temp); + str = sr.ReadToEnd(); // 读取文件 + } + catch { } + sr?.Close(); + sr?.Dispose(); + return str; + } + + + + /// + /// 取后缀名 + /// + /// 文件名 + /// .gif|.html格式 + public static string GetPostfixStr(string filename) + { + int start = filename.LastIndexOf("."); + int length = filename.Length; + string postfix = filename.Substring(start, length - start); + return postfix; + } + + + /// + /// + /// + /// 路径 + /// 文件名 + /// 写入的内容 + /// 是否将内容添加到未尾,默认不添加 + public static void WriteFile(string path, string fileName, string content, bool appendToLast = false) + { + path = StringExtension.ReplacePath(path); + fileName = StringExtension.ReplacePath(fileName); + if (!Directory.Exists(path))//如果不存在就创建file文件夹 + Directory.CreateDirectory(path); + + using (FileStream stream = File.Open(Path.Combine(path, fileName), FileMode.OpenOrCreate, FileAccess.Write)) + { + byte[] by = Encoding.Default.GetBytes(content); + if (appendToLast) + { + stream.Position = stream.Length; + } + else + { + stream.SetLength(0); + } + stream.Write(by, 0, by.Length); + } + } + + + + /// + /// 追加文件 + /// + /// 文件路径 + /// 内容 + public static void FileAdd(string Path, string strings) + { + StreamWriter sw = File.AppendText(StringExtension.ReplacePath(Path)); + sw.Write(strings); + sw.Flush(); + sw.Close(); + sw.Dispose(); + } + + + /// + /// 拷贝文件 + /// + /// 原始文件 + /// 新文件路径 + public static void FileCoppy(string OrignFile, string NewFile) + { + File.Copy(StringExtension.ReplacePath(OrignFile), StringExtension.ReplacePath(NewFile), true); + } + + + /// + /// 删除文件 + /// + /// 路径 + public static void FileDel(string Path) + { + File.Delete(StringExtension.ReplacePath(Path)); + } + + /// + /// 移动文件 + /// + /// 原始路径 + /// 新路径 + public static void FileMove(string OrignFile, string NewFile) + { + File.Move(StringExtension.ReplacePath(OrignFile), StringExtension.ReplacePath(NewFile)); + } + + /// + /// 在当前目录下创建目录 + /// + /// 当前目录 + /// 新目录 + public static void FolderCreate(string OrignFolder, string NewFloder) + { + Directory.SetCurrentDirectory(StringExtension.ReplacePath(OrignFolder)); + Directory.CreateDirectory(StringExtension.ReplacePath(NewFloder)); + } + + /// + /// 创建文件夹 + /// + /// + public static void FolderCreate(string Path) + { + // 判断目标目录是否存在如果不存在则新建之 + if (!Directory.Exists(StringExtension.ReplacePath(Path))) + Directory.CreateDirectory(StringExtension.ReplacePath(Path)); + } + + + public static void FileCreate(string Path) + { + FileInfo CreateFile = new FileInfo(StringExtension.ReplacePath(Path)); //创建文件 + if (!CreateFile.Exists) + { + FileStream FS = CreateFile.Create(); + FS.Close(); + } + } + /// + /// 递归删除文件夹目录及文件 + /// + /// + /// + public static void DeleteFolder(string dir) + { + dir = StringExtension.ReplacePath(dir); + if (Directory.Exists(dir)) //如果存在这个文件夹删除之 + { + foreach (string d in Directory.GetFileSystemEntries(dir)) + { + if (File.Exists(d)) + File.Delete(d); //直接删除其中的文件 + else + DeleteFolder(d); //递归删除子文件夹 + } + Directory.Delete(dir, true); //删除已空文件夹 + } + } + + + /// + /// 指定文件夹下面的所有内容copy到目标文件夹下面 + /// + /// 原始路径 + /// 目标文件夹 + public static void CopyDir(string srcPath, string aimPath) + { + try + { + aimPath = StringExtension.ReplacePath(aimPath); + // 检查目标目录是否以目录分割字符结束如果不是则添加之 + if (aimPath[aimPath.Length - 1] != Path.DirectorySeparatorChar) + aimPath += Path.DirectorySeparatorChar; + // 判断目标目录是否存在如果不存在则新建之 + if (!Directory.Exists(aimPath)) + Directory.CreateDirectory(aimPath); + // 得到源目录的文件列表,该里面是包含文件以及目录路径的一个数组 + //如果你指向copy目标文件下面的文件而不包含目录请使用下面的方法 + //string[] fileList = Directory.GetFiles(srcPath); + string[] fileList = Directory.GetFileSystemEntries(StringExtension.ReplacePath(srcPath)); + //遍历所有的文件和目录 + foreach (string file in fileList) + { + //先当作目录处理如果存在这个目录就递归Copy该目录下面的文件 + + if (Directory.Exists(file)) + CopyDir(file, aimPath + Path.GetFileName(file)); + //否则直接Copy文件 + else + File.Copy(file, aimPath + Path.GetFileName(file), true); + } + } + catch (Exception ee) + { + throw new Exception(ee.ToString()); + } + } + + /// + /// 获取文件夹大小 + /// + /// 文件夹路径 + /// + public static long GetDirectoryLength(string dirPath) + { + dirPath = StringExtension.ReplacePath(dirPath); + if (!Directory.Exists(dirPath)) + return 0; + long len = 0; + DirectoryInfo di = new DirectoryInfo(dirPath); + foreach (FileInfo fi in di.GetFiles()) + { + len += fi.Length; + } + DirectoryInfo[] dis = di.GetDirectories(); + if (dis.Length > 0) + { + for (int i = 0; i < dis.Length; i++) + { + len += GetDirectoryLength(dis[i].FullName); + } + } + return len; + } + + /// + /// 获取指定文件详细属性 + /// + /// 文件详细路径 + /// + public static string GetFileAttibe(string filePath) + { + string str = ""; + filePath = StringExtension.ReplacePath(filePath); + System.IO.FileInfo objFI = new System.IO.FileInfo(filePath); + str += "详细路径:" + objFI.FullName + "
文件名称:" + objFI.Name + "
文件长度:" + objFI.Length.ToString() + "字节
创建时间" + objFI.CreationTime.ToString() + "
最后访问时间:" + objFI.LastAccessTime.ToString() + "
修改时间:" + objFI.LastWriteTime.ToString() + "
所在目录:" + objFI.DirectoryName + "
扩展名:" + objFI.Extension; + return str; + } + + } +} diff --git a/Infrastructure/GenericHelpers.cs b/Infrastructure/Helpers/GenericHelper.cs similarity index 98% rename from Infrastructure/GenericHelpers.cs rename to Infrastructure/Helpers/GenericHelper.cs index 284ff92f36c395c3205fad34bd2a67863fe1e61d..f3e3a0fca350c493acab396806a3b1cbb937ebda 100644 --- a/Infrastructure/GenericHelpers.cs +++ b/Infrastructure/Helpers/GenericHelper.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace Infrastructure +namespace Infrastructure.Helpers { /// /// List转成Tree @@ -43,11 +43,11 @@ namespace Infrastructure }; } } - /// - /// 把数组转为逗号连接的字符串 - /// - /// - /// + /// + /// 把数组转为逗号连接的字符串 + /// + /// + /// /// public static string ArrayToString(dynamic data, string Str) { diff --git a/Infrastructure/HttpHelper.cs b/Infrastructure/Helpers/HttpHelper.cs similarity index 99% rename from Infrastructure/HttpHelper.cs rename to Infrastructure/Helpers/HttpHelper.cs index 5bf4539b6325e14b6ee6895f2c288a0bf38c8305..e80ac54787e00ce3a257b1d1fbe929949e9a5c93 100644 --- a/Infrastructure/HttpHelper.cs +++ b/Infrastructure/Helpers/HttpHelper.cs @@ -5,7 +5,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Text; -namespace Infrastructure +namespace Infrastructure.Helpers { /// /// http请求类 diff --git a/Infrastructure/Helpers/ImgHelper.cs b/Infrastructure/Helpers/ImgHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..2c54c60b4dace661c8a119868740dcf56ef59fb3 --- /dev/null +++ b/Infrastructure/Helpers/ImgHelper.cs @@ -0,0 +1,77 @@ +// +// Copyright (c) 2019 openauth.me. All rights reserved. +// +// www.cnblogs.com/yubaolee +// 2019-03-07 +// 生成缩略图 + +namespace Infrastructure.Helpers +{ + public class ImgHelper + { + //MakeThumbnail(path, tpath, 120, 90, "H"); + public static void MakeThumbnail(string originalImagePath, + string thumbnailPath, + int width=120, int height=90, string mode="H") + { + //Image originalImage = Image.FromFile(originalImagePath); + //int towidth = width; + //int toheight = height; + //int x = 0; + //int y = 0; + //int ow = originalImage.Width; + //int oh = originalImage.Height; + //switch (mode) + //{ + // case "HW"://指定高宽缩放(可能变形) + // break; + // case "W"://指定宽,高按比例 + // toheight = originalImage.Height * width / originalImage.Width; + // break; + // case "H"://指定高,宽按比例 + // towidth = originalImage.Width * height / originalImage.Height; + // break; + // case "Cut"://指定高宽裁减(不变形) + // if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight) + // { + // oh = originalImage.Height; + // ow = originalImage.Height * towidth / toheight; + // y = 0; + // x = (originalImage.Width - ow) / 2; + // } + // else + // { + // ow = originalImage.Width; + // oh = originalImage.Width * height / towidth; + // x = 0; + // y = (originalImage.Height - oh) / 2; + // } + // break; + // default: + // break; + //} + //MediaTypeNames.Image bitmap = new System.Drawing.Bitmap(towidth, toheight); + //Graphics g = System.Drawing.Graphics.FromImage(bitmap); + //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; + //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + //g.Clear(Color.Transparent); + //g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight), + // new Rectangle(x, y, ow, oh), + // GraphicsUnit.Pixel); + //try + //{ + // bitmap.Save(thumbnailPath, System.Drawing.Imaging.ImageFormat.Png); + //} + //catch (System.Exception e) + //{ + // throw e; + //} + //finally + //{ + // originalImage.Dispose(); + // bitmap.Dispose(); + // g.Dispose(); + //} + } + } +} diff --git a/Infrastructure/Md5.cs b/Infrastructure/Helpers/Md5.cs similarity index 95% rename from Infrastructure/Md5.cs rename to Infrastructure/Helpers/Md5.cs index 2589bdca44b417e2674f525d1a7978642a89958c..05ae4df1a2e596b712bed47846b8f8e7818eee73 100644 --- a/Infrastructure/Md5.cs +++ b/Infrastructure/Helpers/Md5.cs @@ -2,7 +2,7 @@ using System.Security.Cryptography; using System.Text; -namespace Infrastructure +namespace Infrastructure.Helpers { public class Md5 { diff --git a/Infrastructure/ObjectHelper.cs b/Infrastructure/Helpers/ObjectHelper.cs similarity index 93% rename from Infrastructure/ObjectHelper.cs rename to Infrastructure/Helpers/ObjectHelper.cs index cb7f304f693c86d28f0b8075b522578de4c857c0..4e169ca1bcf9442886ba3a021bc00ffb46d9cff5 100644 --- a/Infrastructure/ObjectHelper.cs +++ b/Infrastructure/Helpers/ObjectHelper.cs @@ -14,14 +14,9 @@ // // *********************************************************************** -using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; -namespace Infrastructure +namespace Infrastructure.Helpers { public static class ObjectHelper { diff --git a/Infrastructure/Helpers/XmlHelper.cs b/Infrastructure/Helpers/XmlHelper.cs new file mode 100644 index 0000000000000000000000000000000000000000..b16c22c1becb67904ed26cea705519ce2df3d208 --- /dev/null +++ b/Infrastructure/Helpers/XmlHelper.cs @@ -0,0 +1,712 @@ +using System.Xml; + +/// 这个是用VS2010写的,如果用VS2005,请去掉System.Linq和System.Xml.Linq的引用 +/// 可以将此文件直接编译成dll,今后程序只需要引用该dll后开头添加using XmlLibrary;即可。 +namespace Infrastructure.Helpers +{ + /// + /// XMLHelper参数 + /// + public class XmlParameter + { + private string _name; + private string _innerText; + private string _namespaceOfPrefix; + private AttributeParameter[] _attributes; + + public XmlParameter() { } + public XmlParameter(string name, params AttributeParameter[] attParas) : this(name, null, null, attParas) { } + public XmlParameter(string name, string innerText, params AttributeParameter[] attParas) : this(name, innerText, null, attParas) { } + public XmlParameter(string name, string innerText, string namespaceOfPrefix, params AttributeParameter[] attParas) + { + this._name = name; + this._innerText = innerText; + this._namespaceOfPrefix = namespaceOfPrefix; + this._attributes = attParas; + } + /// + /// 节点名称 + /// + public string Name + { + get { return this._name; } + set { this._name = value; } + } + /// + /// 节点文本 + /// + public string InnerText + { + get { return this._innerText; } + set { this._innerText = value; } + } + /// + /// 节点前缀xmlns声明(命名空间URI) + /// + public string NamespaceOfPrefix + { + get { return this._namespaceOfPrefix; } + set { this._namespaceOfPrefix = value; } + } + /// + /// 节点属性集 + /// + public AttributeParameter[] Attributes + { + get { return this._attributes; } + set { this._attributes = value; } + } + } + + /// + /// 节点属性参数 + /// + public class AttributeParameter + { + private string _name; + private string _value; + + public AttributeParameter() { } + public AttributeParameter(string attributeName, string attributeValue) + { + this._name = attributeName; + this._value = attributeValue; + } + /// + /// 属性名称 + /// + public string Name + { + get { return this._name; } + set { this._name = value; } + } + /// + /// 属性值 + /// + public string Value + { + get { return this._value; } + set { this._value = value; } + } + } + + public class XMLHelper + { + private static string _xPath; + /// + /// xml文件路径 + /// + public static string XmlPath + { + get { return _xPath; } + set { _xPath = value; } + } + private static string _configName = "XmlPath"; + /// + /// 配置文件节点名称,请设置在AppSettings节点下 + /// + public static string ConfigName + { + get { return _configName; } + set { _configName = value; GetConfig(); } + } + /// + /// 从配置文件读取xml路径 + /// + static void GetConfig() + { + //if (string.IsNullOrEmpty(_xPath)) + //{ + // try + // { + // _xPath = ConfigurationManager.AppSettings[_configName]; + // } + // catch { } + //} + } + static XMLHelper() { GetConfig(); } + + #region private AppendChild + /// + /// 添加一个子节点 + /// + /// XmlDocument对象 + /// 父节点 + /// Xml参数 + private static void AppendChild(XmlDocument xDoc, XmlNode parentNode, params XmlParameter[] xlParameter) + { + foreach (XmlParameter xpar in xlParameter) + { + XmlNode newNode = xDoc.CreateNode(XmlNodeType.Element, xpar.Name, null); + string ns = string.IsNullOrEmpty(xpar.NamespaceOfPrefix) ? "" : newNode.GetNamespaceOfPrefix(xpar.NamespaceOfPrefix); + foreach (AttributeParameter attp in xpar.Attributes) + { + XmlNode attr = xDoc.CreateNode(XmlNodeType.Attribute, attp.Name, ns == "" ? null : ns); + attr.Value = attp.Value; + newNode.Attributes.SetNamedItem(attr); + } + newNode.InnerText = xpar.InnerText; + parentNode.AppendChild(newNode); + } + } + #endregion + + #region private AddEveryNode + private static void AddEveryNode(XmlDocument xDoc, XmlNode parentNode, params XmlParameter[] paras) + { + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) + { + if (xns.Name == parentNode.Name) + { + AppendChild(xDoc, xns, paras); + } + else + { + foreach (XmlNode xn in xns) + { + if (xn.Name == parentNode.Name) + { + AppendChild(xDoc, xn, paras); + } + } + } + } + } + #endregion + + #region private GetXmlDom + /// + /// 获得一个XmlDocument对象 + /// + /// + private static XmlDocument GetXmlDom() + { + XmlDocument xdoc = new XmlDocument(); + xdoc.Load(_xPath); + return xdoc; + } + #endregion + + #region CreateXmlFile + /// + /// 创建一个XML文档,成功创建后操作路径将直接指向该文件 + /// + /// 文件物理路径名 + /// 根结点名称 + /// 元素节点名称 + /// XML参数 + public static void CreateXmlFile(string fileName, string rootNode, string elementName, params XmlParameter[] xmlParameter) + { + XmlDocument xDoc = new XmlDocument(); + XmlNode xn = xDoc.CreateXmlDeclaration("1.0", "UTF-8", null); + xDoc.AppendChild(xn); + XmlNode root = xDoc.CreateElement(rootNode); + xDoc.AppendChild(root); + XmlNode ln = xDoc.CreateNode(XmlNodeType.Element, elementName, null); + AppendChild(xDoc, ln, xmlParameter); + root.AppendChild(ln); + try + { + xDoc.Save(fileName); + _xPath = fileName; + } + catch + { + throw; + } + } + /// + /// 创建一个XML文档,成功创建后操作路径将直接指向该文件 + /// + /// 文件物理路径名 + /// xml字符串 + public static void CreateXmlFile(string fileName, string xmlString) + { + XmlDocument xDoc = new XmlDocument(); + try + { + xDoc.LoadXml(xmlString); + xDoc.Save(fileName); + _xPath = fileName; + } + catch { throw; } + } + #endregion + + #region AddNewNode + /// + /// 添加新节点 + /// + /// 新节点的父节点对象 + /// Xml参数对象 + public static void AddNewNode(XmlNode parentNode, params XmlParameter[] xmlParameter) + { + XmlDocument xDoc = GetXmlDom(); + if (parentNode.Name == xDoc.DocumentElement.Name) + { + XmlNode newNode = xDoc.CreateNode(XmlNodeType.Element, xDoc.DocumentElement.ChildNodes[0].Name, null); + AppendChild(xDoc, newNode, xmlParameter); + xDoc.DocumentElement.AppendChild(newNode); + } + else + { + AddEveryNode(xDoc, parentNode, xmlParameter); + } + xDoc.Save(_xPath); + } + /// + /// 添加新节点 + /// + /// XmlDocument对象 + /// 新节点的父节点名称 + /// XML参数对象 + public static void AddNewNode(string parentName, params XmlParameter[] xmlParameter) + { + XmlDocument xDoc = GetXmlDom(); + XmlNode parentNode = GetNode(xDoc, parentName); + if (parentNode == null) return; + if (parentNode.Name == xDoc.DocumentElement.Name) + { + XmlNode newNode = xDoc.CreateNode(XmlNodeType.Element, xDoc.DocumentElement.ChildNodes[0].Name, null); + AppendChild(xDoc, newNode, xmlParameter); + xDoc.DocumentElement.AppendChild(newNode); + } + else + { + AddEveryNode(xDoc, parentNode, xmlParameter); + } + xDoc.Save(_xPath); + } + #endregion + + #region AddAttribute + /// + /// 添加节点属性 + /// + /// 节点对象 + /// 该节点的命名空间URI + /// 新属性名称 + /// 属性值 + public static void AddAttribute(XmlNode node, string namespaceOfPrefix, string attributeName, string attributeValue) + { + XmlDocument xDoc = GetXmlDom(); + string ns = string.IsNullOrEmpty(namespaceOfPrefix) ? "" : node.GetNamespaceOfPrefix(namespaceOfPrefix); + XmlNode xn = xDoc.CreateNode(XmlNodeType.Attribute, attributeName, ns == "" ? null : ns); + xn.Value = attributeValue; + node.Attributes.SetNamedItem(xn); + xDoc.Save(_xPath); + } + /// + /// 添加节点属性 + /// + /// 节点对象 + /// 该节点的命名空间URI + /// 节点属性参数 + public static void AddAttribute(XmlNode node, string namespaceOfPrefix, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + string ns = string.IsNullOrEmpty(namespaceOfPrefix) ? "" : node.GetNamespaceOfPrefix(namespaceOfPrefix); + foreach (AttributeParameter attp in attributeParameters) + { + XmlNode xn = xDoc.CreateNode(XmlNodeType.Attribute, attp.Name, ns == "" ? null : ns); + xn.Value = attp.Value; + node.Attributes.SetNamedItem(xn); + } + xDoc.Save(_xPath); + } + /// + /// 添加节点属性 + /// + /// 节点名称 + /// 该节点的命名空间URI + /// 新属性名称 + /// 属性值 + public static void AddAttribute(string nodeName, string namespaceOfPrefix, string attributeName, string attributeValue) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList xlst = xDoc.DocumentElement.ChildNodes; + for (int i = 0; i < xlst.Count; i++) + { + XmlNode node = GetNode(xlst[i], nodeName); + if (node == null) break; + AddAttribute(node, namespaceOfPrefix, attributeName, attributeValue); + } + xDoc.Save(_xPath); + } + /// + /// 添加节点属性 + /// + /// 节点名称 + /// 该节点的命名空间URI + /// 节点属性参数 + public static void AddAttribute(string nodeName, string namespaceOfPrefix, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList xlst = xDoc.DocumentElement.ChildNodes; + for (int i = 0; i < xlst.Count; i++) + { + XmlNode node = GetNode(xlst[i], nodeName); + if (node == null) break; + AddAttribute(node, namespaceOfPrefix, attributeParameters); + } + xDoc.Save(_xPath); + } + #endregion + + #region GetNode + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点名称 + /// + public static XmlNode GetNode(string nodeName) + { + XmlDocument xDoc = GetXmlDom(); + if (xDoc.DocumentElement.Name == nodeName) return (XmlNode)xDoc.DocumentElement; + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == nodeName) return xns; + else + { + XmlNode xn = GetNode(xns, nodeName); + if (xn != null) return xn; /// V1.0.0.3添加节点判断 + } + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点对象 + /// 节点名称 + /// + public static XmlNode GetNode(XmlNode node, string nodeName) + { + foreach (XmlNode xn in node) + { + if (xn.Name == nodeName) return xn; + else + { + XmlNode tmp = GetNode(xn, nodeName); + if (tmp != null) return tmp; + } + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点索引 + /// 节点名称 + public static XmlNode GetNode(int index, string nodeName) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + if (nlst.Count <= index) return null; + if (nlst[index].Name == nodeName) return (XmlNode)nlst[index]; + foreach (XmlNode xn in nlst[index]) + { + return GetNode(xn, nodeName); + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点对象 + /// 节点名称 + /// 节点内容 + public static XmlNode GetNode(XmlNode node, string nodeName, string innerText) + { + foreach (XmlNode xn in node) + { + if (xn.Name == nodeName && xn.InnerText == innerText) return xn; + else + { + XmlNode tmp = GetNode(xn, nodeName, innerText); + if (tmp != null) return tmp; + } + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点名称 + /// 节点内容 + public static XmlNode GetNode(string nodeName, string innerText) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == nodeName && xns.InnerText == innerText) return xns; + XmlNode tmp = GetNode(xns, nodeName, innerText); + if (tmp != null) return tmp; + } + return null; + } + /// + /// 获取指定节点名称的节点对象 + /// + /// XML参数 + public static XmlNode GetNode(XmlParameter xmlParameter) + { + return GetNode(xmlParameter.Name, xmlParameter.InnerText); + } + /// + /// 获取指定节点名称的节点对象 + /// + /// 节点对象 + /// XML参数 + public static XmlNode GetNode(XmlNode node, XmlParameter xmlParameter) + { + return GetNode(node, xmlParameter.Name, node.InnerText); + } + #endregion + + #region UpdateNode + private static void UpdateNode(XmlNode node, XmlParameter xmlParameter) + { + node.InnerText = xmlParameter.InnerText; + for (int i = 0; i < xmlParameter.Attributes.Length; i++) + { + for (int j = 0; j < node.Attributes.Count; j++) + { + if (node.Attributes[j].Name == xmlParameter.Attributes[i].Name) + { + node.Attributes[j].Value = xmlParameter.Attributes[i].Value; + } + } + } + } + private static void UpdateNode(XmlNode node, string innerText, AttributeParameter[] attributeParameters) + { + node.InnerText = innerText; + for (int i = 0; i < attributeParameters.Length; i++) + { + for (int j = 0; j < node.Attributes.Count; j++) + { + if (node.Attributes[j].Name == attributeParameters[i].Name) + { + node.Attributes[j].Value = attributeParameters[i].Value; + } + } + } + } + /// + /// 修改节点的内容 + /// + /// 节点索引 + /// XML参数对象 + public static void UpdateNode(int index, XmlParameter xmlParameter) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + if (nlst.Count <= index) return; + if (nlst[index].Name == xmlParameter.Name) + { + UpdateNode(nlst[index], xmlParameter); + } + else + { + foreach (XmlNode xn in nlst[index]) + { + XmlNode xnd = GetNode(xn, xmlParameter.Name); + if (xnd != null) + { + UpdateNode(xnd, xmlParameter); + } + } + } + xDoc.Save(_xPath); + } + /// + /// 修改节点的内容 + /// + /// 节点索引 + /// 节点名称 + /// 修改后的内容 + public static void UpdateNode(int index, string nodeName, string newInnerText) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + if (nlst.Count <= index) return; + if (nlst[index].Name == nodeName) + { + nlst[index].InnerText = newInnerText; + } + else + { + foreach (XmlNode xn in nlst[index]) + { + XmlNode xnd = GetNode(xn, nodeName); + if (xnd != null) + { + xnd.InnerText = newInnerText; + } + } + } + xDoc.Save(_xPath); + } + /// + /// 修改节点的内容 + /// + /// XmlParameter对象 + /// 修改后的内容 + /// 需要修改的属性 + public static void UpdateNode(XmlParameter xmlParameter, string innerText, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == xmlParameter.Name && xns.InnerText == xmlParameter.InnerText) + { + UpdateNode(xns, innerText, attributeParameters); + break; + } + XmlNode tmp = GetNode(xns, xmlParameter); + if (tmp != null) + { + UpdateNode(tmp, innerText, attributeParameters); + break; + } + } + xDoc.Save(_xPath); + } + #endregion + + #region DeleteNode + /// + /// 删除节点 + /// + /// 节点索引 + public static void DeleteNode(int index) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + nlst[index].ParentNode.RemoveChild(nlst[index]); + xDoc.Save(_xPath); + } + /// + /// 删除节点 + /// + /// 需要删除的节点对象 + public static void DeleteNode(params XmlNode[] nodeList) + { + XmlDocument xDoc = GetXmlDom(); + foreach (XmlNode xnl in nodeList) + { + foreach (XmlNode xn in xDoc.DocumentElement.ChildNodes) + { + if (xnl.Equals(xn)) + { + xn.ParentNode.RemoveChild(xn); + break; + } + } + } + xDoc.Save(_xPath); + } + /// + /// 删除节点 + /// + /// XmlDocument对象 + /// 节点名称 + /// 节点内容 + public static void DeleteNode(string nodeName, string nodeText) + { + XmlDocument xDoc = GetXmlDom(); + foreach (XmlNode xn in xDoc.DocumentElement.ChildNodes) + { + if (xn.Name == nodeName) + { + if (xn.InnerText == nodeText) + { + xn.ParentNode.RemoveChild(xn); + return; + } + } + else + { + XmlNode node = GetNode(xn, nodeName); + if (node != null && node.InnerText == nodeText) + { + node.ParentNode.ParentNode.RemoveChild(node.ParentNode); + return; + } + } + } + xDoc.Save(_xPath); + } + #endregion + + #region SetAttribute + /// + /// 修改属性值 + /// + /// 元素对象 + /// 属性参数 + private static void SetAttribute(XmlElement elem, params AttributeParameter[] attps) + { + foreach (AttributeParameter attp in attps) + { + elem.SetAttribute(attp.Name, attp.Value); + } + } + /// + /// 修改属性值 + /// + /// XML参数 + /// 属性参数 + public static void SetAttribute(XmlParameter xmlParameter, params AttributeParameter[] attributeParameters) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == xmlParameter.Name && xns.InnerText == xmlParameter.InnerText) + { + SetAttribute((XmlElement)xns, attributeParameters); + break; + } + XmlNode tmp = GetNode(xns, xmlParameter); + if (tmp != null) + { + SetAttribute((XmlElement)tmp, attributeParameters); + break; + } + } + xDoc.Save(_xPath); + } + /// + /// 修改属性值 + /// + /// XML参数 + /// 新属性值 + public static void SetAttribute(XmlParameter xmlParameter, string attributeName, string attributeValue) + { + XmlDocument xDoc = GetXmlDom(); + XmlNodeList nlst = xDoc.DocumentElement.ChildNodes; + foreach (XmlNode xns in nlst) // 遍历所有子节点 + { + if (xns.Name == xmlParameter.Name && xns.InnerText == xmlParameter.InnerText) + { + ((XmlElement)xns).SetAttribute(attributeName, attributeValue); + break; + } + XmlNode tmp = GetNode(xns, xmlParameter); + if (tmp != null) + { + ((XmlElement)tmp).SetAttribute(attributeName, attributeValue); + break; + } + } + xDoc.Save(_xPath); + } + #endregion + } +} diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index bb9c618b550187cbc3b5ebb496cd36b7457eea21..d62d5695e0bc9fbfec12ae406b64ec53f3fcbfc3 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -1,129 +1,29 @@ - - - + + - Debug - AnyCPU - {5FEAEC9A-4F1E-4EE7-B377-9DB1B0870DAC} - Library - Properties - Infrastructure - Infrastructure - v4.5 - 512 - ..\ - true + netcoreapp5.0 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 + + + bin\Debug\netcoreapp3.1\Infrastructure.xml - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll - True - - - ..\packages\AutoMapper.4.1.0\lib\net45\AutoMapper.dll - - - ..\packages\EnyimMemcached.2.12\lib\net35\Enyim.Caching.dll - True - - - ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll - - - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - True - - - False - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - True - - - - - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll - True - - - ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll - True - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + - + + PreserveNewest + - - - - - \ No newline at end of file + + diff --git a/Infrastructure/KeyDescription.cs b/Infrastructure/KeyDescription.cs new file mode 100644 index 0000000000000000000000000000000000000000..9eca9769274f9115b83f6d055dfccc1795036276 --- /dev/null +++ b/Infrastructure/KeyDescription.cs @@ -0,0 +1,24 @@ +namespace Infrastructure +{ + public class KeyDescription + { + /// + /// 键值 + /// + public string Key { get; set; } + /// + /// 键的描述 + /// + public string Description { get; set; } + + /// + /// 前端是否显示 + /// + public bool Browsable { get; set; } + + /// + /// 字段类型 + /// + public string Type { get; set; } + } +} diff --git a/Infrastructure/LogHelper.cs b/Infrastructure/LogHelper.cs deleted file mode 100644 index 2b3a582908b8d9d5fa9e0383cd6570124e7cf703..0000000000000000000000000000000000000000 --- a/Infrastructure/LogHelper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using log4net; - -[assembly: log4net.Config.XmlConfigurator(Watch = true)] -namespace Infrastructure -{ - public class LogHelper - { - public static readonly ILog _log = LogManager.GetLogger("log4net"); - - public static void Log(string message) - { - _log.Info(message); - } - - public static void Debug(string message) - { - _log.Debug(message); - } - - public static void Fatal(string message) - { - _log.Fatal(message); - } - - public static void Warn(string message) - { - _log.Warn(message); - } - } -} diff --git a/Infrastructure/Properties/AssemblyInfo.cs b/Infrastructure/Properties/AssemblyInfo.cs deleted file mode 100644 index f374835c64d54d5ecadb387d3e737cccb4c0179e..0000000000000000000000000000000000000000 --- a/Infrastructure/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// 有关程序集的常规信息通过以下 -// 特性集控制。更改这些特性值可修改 -// 与程序集关联的信息。 -[assembly: AssemblyTitle("Infrastructure")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Infrastructure")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 将 ComVisible 设置为 false 使此程序集中的类型 -// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, -// 则将该类型上的 ComVisible 特性设置为 true。 -[assembly: ComVisible(false)] - -// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID -[assembly: Guid("e953a7ce-9f0c-4aec-973c-de31e38e10e6")] - -// 程序集的版本信息由下面四个值组成: -// -// 主版本 -// 次版本 -// 生成号 -// 修订号 -// -// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, -// 方法是按如下所示使用“*”: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Infrastructure/Provider/PathProvider.cs b/Infrastructure/Provider/PathProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..87dbb5f39d5fe64731271b41dbfd452f6172f255 --- /dev/null +++ b/Infrastructure/Provider/PathProvider.cs @@ -0,0 +1,52 @@ +using System.IO; +using Infrastructure.Extensions; +using Infrastructure.Extensions.AutofacManager; +using Microsoft.Extensions.Hosting; + +namespace Infrastructure.Provider +{ + public interface IPathProvider : IDependency + { + string MapPath(string path); + string MapPath(string path, bool rootPath); + IHostEnvironment GetHostingEnvironment(); + } + + public class PathProvider : IPathProvider + { + private IHostEnvironment _hostingEnvironment; + + public PathProvider(IHostEnvironment environment) + { + _hostingEnvironment = environment; + } + public IHostEnvironment GetHostingEnvironment() + { + return _hostingEnvironment; + } + + /// + /// 获取服务器文件路径 + /// + /// + /// + public string MapPath(string path) + { + return MapPath(path, false); + } + /// + /// 获取wwwroot路径 + /// + /// + /// 是否获取wwwroot路径 + /// + public string MapPath(string path, bool rootPath) + { + if (rootPath) + { + return Path.Combine(_hostingEnvironment.ContentRootPath,"wwwroot").ReplacePath(); + } + return Path.Combine(_hostingEnvironment.ContentRootPath, path).ReplacePath(); + } + } +} diff --git a/Infrastructure/SessionHelper.cs b/Infrastructure/SessionHelper.cs deleted file mode 100644 index e8399b7ad542c449c479b14d7bfdcfbc1be3ddc5..0000000000000000000000000000000000000000 --- a/Infrastructure/SessionHelper.cs +++ /dev/null @@ -1,50 +0,0 @@ -// *********************************************************************** -// Assembly : Infrastructure -// Author : Administrator -// Created : 09-22-2015 -// -// Last Modified By : Administrator -// Last Modified On : 09-22-2015 -// *********************************************************************** -// -// Copyright (c) . All rights reserved. -// -// SESSION辅助类 -// *********************************************************************** - -using System; -using System.Web; - -namespace Infrastructure -{ - /// - /// Session 帮助类 - /// - public class SessionHelper - { - private static readonly string SessionUser = "SESSION_USER"; - public static void AddSessionUser(T user) - { - HttpContext rq = HttpContext.Current; - rq.Session[SessionUser] = user; - } - public static T GetSessionUser() - { - try - { - HttpContext rq = HttpContext.Current; - return (T)rq.Session[SessionUser]; - } - catch (Exception e) - { - throw new Exception(e.Message); - } - } - - public static void Clear() - { - HttpContext rq = HttpContext.Current; - rq.Session[SessionUser] = null; - } - } -} diff --git a/Infrastructure/StringExtensions.cs b/Infrastructure/StringExtensions.cs deleted file mode 100644 index 019cefddeb1741311416aaab59d2dc8a7ff92672..0000000000000000000000000000000000000000 --- a/Infrastructure/StringExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Security.Cryptography; -using System.Text; - -namespace Infrastructure -{ - public static class StringExtensions - { - public static string MaxSubstring(this string origin, int maxLength) - { - return origin.Length >= maxLength ? origin.Substring(0, maxLength) : origin; - } - - public static string ToMd5(this string origin) - { - if (string.IsNullOrWhiteSpace(origin)) - { - return string.Empty; - } - - var md5Algorithm = MD5.Create(); - var utf8Bytes = Encoding.UTF8.GetBytes(origin); - var md5Hash = md5Algorithm.ComputeHash(utf8Bytes); - var hexString = new StringBuilder(); - foreach (var hexByte in md5Hash) - { - hexString.Append(hexByte.ToString("x2")); - } - return hexString.ToString(); - } - } -} \ No newline at end of file diff --git a/Infrastructure/Test/TestAutoMapper.cs b/Infrastructure/Test/TestAutoMapper.cs new file mode 100644 index 0000000000000000000000000000000000000000..47824e933bacc625e61d3cbf174a522dffd73522 --- /dev/null +++ b/Infrastructure/Test/TestAutoMapper.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Infrastructure.Test +{ + class TestAutoMapper + { + [Test] + public void TestConvert() + { + var my = new MyClass + { + Name = "yubao" + }; + + var dest = my.MapTo(); + Console.WriteLine(JsonHelper.Instance.Serialize(dest)); + } + [Test] + public void TestConvertList() + { + var users = new List { + new MyClass {Name = "yubaolee1"} + , new MyClass{Name = "yubaolee2"} + + }; + + var dest = users.MapToList(); + Console.WriteLine(JsonHelper.Instance.Serialize(dest)); + + var dest2 = users.MapToList(); + Console.WriteLine(JsonHelper.Instance.Serialize(dest2)); + + } + } + + + class MyClass + { + public string Name { get; set; } + public string NickName { get; set; } + } + + class DestClass + { + public string Name { get; set; } + public int Age { get; set; } + } +} diff --git a/Infrastructure/Test/TestDynamicLinq.cs b/Infrastructure/Test/TestDynamicLinq.cs new file mode 100644 index 0000000000000000000000000000000000000000..ddaef10dd6c3d9f49b19c8ce328b1c36b77b37ee --- /dev/null +++ b/Infrastructure/Test/TestDynamicLinq.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq.Expressions; +using NUnit.Framework; + +namespace Infrastructure.Test +{ + public class TestDynamicLinq + { + [Test] + public void Convert() + { + FilterGroup sub = new FilterGroup + { + Operation = "or" + }; + sub.Filters = new[] + { + new Filter {Key = "name", Value = "name", Contrast = "=="}, + new Filter {Key = "c3", Value = "10,20,30", Contrast = "in"} + }; + + FilterGroup filterGroup = new FilterGroup + { + Operation = "and" + }; + filterGroup.Filters = new[] + { + new Filter {Key = "c1", Value = "name", Contrast = "contains"}, + new Filter {Key = "10,20,30", Value = "40", Contrast = "intersect"} + }; + + filterGroup.Children = new[] + { + sub + }; + + var expression = DynamicLinq.ConvertGroup(filterGroup, + Expression.Parameter(typeof(TestOjb), "c")); + + Console.WriteLine(expression.ToString()); + + } + } + + public class TestOjb{ + public string c1 { get; set; } + public string c2 { get; set; } + public string c3 { get; set; } + public string c4 { get; set; } + public string c5 { get; set; } + public string c6 { get; set; } + public string c7 { get; set; } + } +} \ No newline at end of file diff --git a/Infrastructure/Utilities/DynamicPropertyBag .cs b/Infrastructure/Utilities/DynamicPropertyBag .cs new file mode 100644 index 0000000000000000000000000000000000000000..2e55f19a10f47a9aa3e2b0e43ab65e6a34b2d5d2 --- /dev/null +++ b/Infrastructure/Utilities/DynamicPropertyBag .cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Dynamic; +using System.IO; + +namespace Infrastructure.Utilities +{ + /// + /// 动态属性Bag + /// + public class DynamicPropertyBag : DynamicObject + { + private Dictionary storage = new Dictionary(); + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + if (storage.ContainsKey(binder.Name)) + { + result = storage[binder.Name]; + return true; + } + result = null; + return false; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + string key = binder.Name; + if (storage.ContainsKey(key)) + storage[key] = value; + else + storage.Add(key, value); + return true; + } + + public override string ToString() + { + StringWriter message = new StringWriter(); + foreach (var item in storage) + message.WriteLine("{0}:\t{1}", item.Key, item.Value); + return message.ToString(); + } + } +} diff --git a/Infrastructure/Utilities/HttpContext.cs b/Infrastructure/Utilities/HttpContext.cs new file mode 100644 index 0000000000000000000000000000000000000000..4d49563c6fa9faed02f08b0daf10280a14fda23e --- /dev/null +++ b/Infrastructure/Utilities/HttpContext.cs @@ -0,0 +1,13 @@ +using Infrastructure.Extensions.AutofacManager; +using Microsoft.AspNetCore.Http; + +namespace Infrastructure.Utilities +{ + public static class HttpContext + { + private static IHttpContextAccessor _accessor=AutofacContainerModule.GetService(); + + public static Microsoft.AspNetCore.Http.HttpContext Current => _accessor.HttpContext; + + } +} diff --git a/Infrastructure/Utilities/HttpManager.cs b/Infrastructure/Utilities/HttpManager.cs new file mode 100644 index 0000000000000000000000000000000000000000..5ed3331f0ba1de3945c51f0ee9922d37649b2bd2 --- /dev/null +++ b/Infrastructure/Utilities/HttpManager.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Infrastructure.Utilities +{ + public class HttpManager + { + public static Task HttpPostAsync(string url, string postData = null, string contentType = null, int timeOut = 30, Dictionary headers = null) + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "POST"; + if (!string.IsNullOrEmpty(contentType)) + { + request.ContentType = contentType; + } + if (headers != null) + { + foreach (var header in headers) + request.Headers[header.Key] = header.Value; + } + + try + { + byte[] bytes = Encoding.UTF8.GetBytes(postData ?? ""); + using (Stream sendStream = request.GetRequestStream()) + { + sendStream.Write(bytes, 0, bytes.Length); + } + + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + Stream responseStream = response.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8); + return streamReader.ReadToEndAsync(); + } + } + catch (Exception ex) + { + return Task.FromResult(ex.Message); + } + + } + public static Task HttpGetAsync(string url, Dictionary headers = null) + { + try + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + if (headers != null) + { + foreach (var header in headers) + request.Headers[header.Key] = header.Value; + } + using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) + { + Stream responseStream = response.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding.UTF8); + return streamReader.ReadToEndAsync(); + } + } + catch (Exception ex) + { + return Task.FromResult(ex.Message); + } + } + } +} diff --git a/Infrastructure/Utilities/ProjectPath.cs b/Infrastructure/Utilities/ProjectPath.cs new file mode 100644 index 0000000000000000000000000000000000000000..4e209caa75a40fdeb82f35cbf77d57281e8c9185 --- /dev/null +++ b/Infrastructure/Utilities/ProjectPath.cs @@ -0,0 +1,64 @@ +using System.IO; +using System.Linq; +using Infrastructure.Extensions; + +namespace Infrastructure.Utilities +{ + public class ProjectPath + { + /// + /// 获取web父目录所在位置 + /// + /// + public static DirectoryInfo GetProjectDirectoryInfo() + { + return GetProjectDirectoryInfo(new DirectoryInfo("".MapPath()), 1); + } + /// + /// 获取指定结尾的项目名称 + /// + /// + /// + public static string GetLastIndexOfDirectoryName(string lastIndexOfName) + { + string projectName = GetProjectDirectoryInfo()?.GetDirectories() + .Where(c => c.Name.LastIndexOf(lastIndexOfName) != -1).Select(x => x.Name).FirstOrDefault(); + if (string.IsNullOrEmpty(projectName)) + { + projectName = new DirectoryInfo("".MapPath()).GetFiles().Where(x => x.Name.LastIndexOf(lastIndexOfName + ".dll") != -1).FirstOrDefault().Name; + if (!string.IsNullOrEmpty(projectName)) + { + projectName = projectName.Replace(".dll", ""); + } + } + return projectName; + } + /// + /// 获取项目所在路径 + /// + /// + /// + private static DirectoryInfo GetProjectDirectoryInfo(DirectoryInfo directoryInfo, int findCount) + { + if (directoryInfo == null) + { + return null; + } + if (directoryInfo.Exists + && directoryInfo.GetDirectories().Where(x => x.Name.LastIndexOf(".Mvc") != -1).FirstOrDefault() != null) + { + return directoryInfo; + } + if (findCount < 7) + { + findCount++; + DirectoryInfo dir = GetProjectDirectoryInfo(directoryInfo.Parent, findCount); + if (dir != null) + { + return dir; + } + } + return null; + } + } +} diff --git a/Infrastructure/Utilities/ResponseMsg.cs b/Infrastructure/Utilities/ResponseMsg.cs new file mode 100644 index 0000000000000000000000000000000000000000..4a7ed46a9eb3f64ed6198172930e90d4dbfd3fda --- /dev/null +++ b/Infrastructure/Utilities/ResponseMsg.cs @@ -0,0 +1,59 @@ +using Infrastructure.Const; + + namespace Infrastructure.Utilities +{ + public static class ResponseMsg + { + public static string GetMsg(this ResponseType responseType) + { + string msg; + switch (responseType) + { + case ResponseType.LoginExpiration: + msg = "登陆已过期,请重新登陆"; break; + case ResponseType.TokenExpiration: + msg = "Token已过期,请重新登陆"; break; + case ResponseType.AccountLocked: + msg = "帐号已被锁定"; break; + case ResponseType.LoginSuccess: + msg = "登陆成功"; break; + case ResponseType.ParametersLack: + msg = "参数不完整"; break; + case ResponseType.NoPermissions: + msg = "没有权限操作"; break; + case ResponseType.NoRolePermissions: + msg = "角色没有权限操作"; break; + case ResponseType.ServerError: + msg = "服务器好像出了点问题....."; break; + case ResponseType.LoginError: + msg = "用户名或密码错误"; break; + case ResponseType.SaveSuccess: + msg = "保存成功"; break; + case ResponseType.NoKey: + msg = "没有主键不能编辑"; break; + case ResponseType.NoKeyDel: + msg = "没有主键不能删除"; break; + case ResponseType.KeyError: + msg = "主键不正确或没有传入主键"; break; + case ResponseType.EidtSuccess: + msg = "编辑成功"; break; + case ResponseType.DelSuccess: + msg = "删除成功"; break; + case ResponseType.RegisterSuccess: + msg = "注册成功"; break; + case ResponseType.AuditSuccess: + msg = "审核成功"; break; + case ResponseType.ModifyPwdSuccess: + msg = "密码修改成功"; break; + case ResponseType.OperSuccess: + msg = "操作成功"; break; + case ResponseType.PINError: + msg = "验证码不正确"; break; + + default: msg = responseType.ToString(); break; + } + return msg; + } + + } +} diff --git a/Infrastructure/UriUtil.cs b/Infrastructure/Utilities/UriUtil.cs similarity index 82% rename from Infrastructure/UriUtil.cs rename to Infrastructure/Utilities/UriUtil.cs index 7c60e434df86483ee5ee3f8ebf490398a0d319f9..d95739606bab9f0adff4542022aefc320253f20d 100644 --- a/Infrastructure/UriUtil.cs +++ b/Infrastructure/Utilities/UriUtil.cs @@ -13,7 +13,7 @@ using System; using System.Collections.Specialized; using System.Web; -namespace Infrastructure +namespace Infrastructure.Utilities { /// /// URl帮助类 @@ -64,11 +64,11 @@ namespace Infrastructure /// eg: /Home/About ==>http://192.168.0.1/Home/About /// /// System.String. - public static string GetAbsolutePathForRelativePath(string relativePath) - { - HttpRequest Request = HttpContext.Current.Request; - string returnUrl = string.Format("{0}{1}",Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, string.Empty) , VirtualPathUtility.ToAbsolute(relativePath)); - return returnUrl; - } + //public static string GetAbsolutePathForRelativePath(string relativePath) + //{ + // HttpRequest Request = HttpContext.Current.Request; + // string returnUrl = string.Format("{0}{1}",Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, string.Empty) , VirtualPathUtility.ToAbsolute(relativePath)); + // return returnUrl; + //} } } \ No newline at end of file diff --git a/Infrastructure/Utilities/WebResponseContent.cs b/Infrastructure/Utilities/WebResponseContent.cs new file mode 100644 index 0000000000000000000000000000000000000000..644704aa84c7054f43104580603cb8f02f105c7e --- /dev/null +++ b/Infrastructure/Utilities/WebResponseContent.cs @@ -0,0 +1,81 @@ +using Infrastructure.Const; + +namespace Infrastructure.Utilities +{ + public class WebResponseContent : Response + { + public WebResponseContent() + { + Code = 200; + Message = "操作成功"; + } + public WebResponseContent(bool status) + { + this.Status = status; + } + public bool Status { get; set; } + public object Result { get; set; } + + public WebResponseContent OK() + { + this.Status = true; + return this; + } + + public static WebResponseContent Instance + { + get { return new WebResponseContent(); } + } + public WebResponseContent OK(string message = null,object data=null) + { + this.Status = true; + this.Message = message; + this.Result = data; + return this; + } + public WebResponseContent OK(ResponseType responseType) + { + return Set(responseType, true); + } + public WebResponseContent Error(string message = null) + { + this.Status = false; + this.Message = message; + return this; + } + public WebResponseContent Error(ResponseType responseType) + { + return Set(responseType, false); + } + public WebResponseContent Set(ResponseType responseType) + { + bool? b = null; + return this.Set(responseType, b); + } + public WebResponseContent Set(ResponseType responseType, bool? status) + { + return this.Set(responseType, null, status); + } + public WebResponseContent Set(ResponseType responseType, string msg) + { + bool? b = null; + return this.Set(responseType, msg, b); + } + public WebResponseContent Set(ResponseType responseType, string msg, bool? status) + { + if (status != null) + { + this.Status = (bool)status; + } + this.Code = (int)responseType; + if (!string.IsNullOrEmpty(msg)) + { + Message = msg; + return this; + } + Message = responseType.GetMsg(); + return this; + } + + } +} diff --git a/Infrastructure/log4net.config b/Infrastructure/log4net.config new file mode 100644 index 0000000000000000000000000000000000000000..63c299dffc866c1daea3f9bc86b43b56953b09df --- /dev/null +++ b/Infrastructure/log4net.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Infrastructure/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs b/Infrastructure/obj/Release/TemporaryGeneratedFile_036C0B5B-1481-4323-8D20-8F5ADCB23D92.cs deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/Infrastructure/packages.config b/Infrastructure/packages.config deleted file mode 100644 index a82dcecad8fab5971659b45dbdcc11bd32f30eca..0000000000000000000000000000000000000000 --- a/Infrastructure/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/LICENSE b/LICENSE index 712acfe8bb67052aa3472a48367a31f497a5cb2a..bc0390e98c98f2ad3ab7decdb82b4d57d7f850d4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - GNU LESSER GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. @@ -6,9 +6,9 @@ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. -(This is the first released version of the Lesser GPL. It also counts +[This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.) + the version number 2.1.] Preamble @@ -470,8 +470,8 @@ safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {description} - Copyright (C) 2018 李玉宝 + + Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -485,7 +485,8 @@ convey the exclusion of warranty; and each file should have at least the You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA Also add information on how to contact you by electronic and paper mail. @@ -494,9 +495,10 @@ school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. - {signature of Ty Coon}, 1 April 1990 + , 1 April 1990 Ty Coon, President of Vice That's all there is to it! diff --git a/OpenAuth.App/AppManager.cs b/OpenAuth.App/AppManager.cs index 8e272e9d4db621d658d5e7ea89bbb6a8d6429f04..8332c5e68224df0de75d550da42b445693f37300 100644 --- a/OpenAuth.App/AppManager.cs +++ b/OpenAuth.App/AppManager.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenAuth.App.Interface; using OpenAuth.App.Request; -using OpenAuth.App.Response; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { @@ -23,7 +24,7 @@ namespace OpenAuth.App public void Update(Application Application) { - Repository.Update(u =>u.Id,Application); + Repository.Update(Application); } @@ -34,5 +35,8 @@ namespace OpenAuth.App return applications.ToList(); } + public AppManager(IUnitWork unitWork, IRepository repository,IAuth auth) : base(unitWork, repository, auth) + { + } } } \ No newline at end of file diff --git a/OpenAuth.App/AppSetting.cs b/OpenAuth.App/AppSetting.cs new file mode 100644 index 0000000000000000000000000000000000000000..146f91f9a7c5c2439e738a27e1da6a6c9be76185 --- /dev/null +++ b/OpenAuth.App/AppSetting.cs @@ -0,0 +1,42 @@ +namespace OpenAuth.App +{ + /// + /// 配置项 + /// + public class AppSetting + { + + public AppSetting() + { + SSOPassport = "http://localhost:52789"; + Version = ""; + UploadPath = ""; + IdentityServerUrl = ""; + DbType = Define.DBTYPE_SQLSERVER; + } + /// + /// SSO地址 + /// + public string SSOPassport { get; set; } + + /// + /// 版本信息 + /// 如果为demo,则屏蔽Post请求 + /// + public string Version { get; set; } + + /// + /// 数据库类型 SqlServer、MySql + /// + public string DbType { get; set; } + + /// 附件上传路径 + public string UploadPath { get; set; } + + //identity授权的地址 + public string IdentityServerUrl { get; set; } + + //是否是Identity授权方式 + public bool IsIdentityAuth => !string.IsNullOrEmpty(IdentityServerUrl); + } +} diff --git a/OpenAuth.App/AuthContextFactory.cs b/OpenAuth.App/AuthContextFactory.cs new file mode 100644 index 0000000000000000000000000000000000000000..c897472b9dced0ab9058e8c085656060de334fce --- /dev/null +++ b/OpenAuth.App/AuthContextFactory.cs @@ -0,0 +1,59 @@ +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : 李玉宝 +// Created : 07-05-2018 +// +// Last Modified By : 李玉宝 +// Last Modified On : 07-05-2018 +// *********************************************************************** +// +// Copyright (c) http://www.openauth.me. All rights reserved. +// +// +// 用户权限策略工厂 +// +// *********************************************************************** + +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + +namespace OpenAuth.App +{ + /// + /// 加载用户所有可访问的资源/机构/模块 + /// 李玉宝新增于2016-07-19 10:53:30 + /// + public class AuthContextFactory + { + private SystemAuthStrategy _systemAuth; + private NormalAuthStrategy _normalAuthStrategy; + private readonly IUnitWork _unitWork; + + public AuthContextFactory(SystemAuthStrategy sysStrategy + , NormalAuthStrategy normalAuthStrategy + , IUnitWork unitWork) + { + _systemAuth = sysStrategy; + _normalAuthStrategy = normalAuthStrategy; + _unitWork = unitWork; + } + + public AuthStrategyContext GetAuthStrategyContext(string username) + { + if (string.IsNullOrEmpty(username)) return null; + + IAuthStrategy service = null; + if (username == Define.SYSTEM_USERNAME) + { + service= _systemAuth; + } + else + { + service = _normalAuthStrategy; + service.User = _unitWork.FindSingle(u => u.Account == username); + } + + return new AuthStrategyContext(service); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Response/UserWithAccessedCtrls.cs b/OpenAuth.App/AuthStrategies/AuthStrategyContext.cs similarity index 32% rename from OpenAuth.App/Response/UserWithAccessedCtrls.cs rename to OpenAuth.App/AuthStrategies/AuthStrategyContext.cs index 8096c779c7b1397c3e095c96dc60c08b835e3218..c96cd17ffb5e7d1d1adaa5dbdb42d8e6f87d376d 100644 --- a/OpenAuth.App/Response/UserWithAccessedCtrls.cs +++ b/OpenAuth.App/AuthStrategies/AuthStrategyContext.cs @@ -1,45 +1,73 @@ -// *********************************************************************** -// Assembly : OpenAuth.App -// Author : Yubao Li -// Created : 12-01-2015 -// -// Last Modified By : Yubao Li -// Last Modified On : 12-01-2015 -// *********************************************************************** -// -// Copyright (c) . All rights reserved. -// -// 用户及权限视图模型 -// *********************************************************************** - -using System.Collections.Generic; -using OpenAuth.Repository.Domain; - -namespace OpenAuth.App.Response -{ - /// - /// 视图模型 - /// 包括用户及用户可访问的机构/资源/模块 - /// 李玉宝修改于2016-07-19 10:57:31 - /// - public class UserWithAccessedCtrls - { - public User User { get; set; } - /// - /// 用户可以访问到的模块(包括所属角色与自己的所有模块) - /// - public List Modules { get; set; } - - //用户可以访问的资源 - public List Resources { get; set; } - - /// - /// 用户所属机构 - /// - public List Orgs { get; set; } - - - public List Roles { get; set; } - } - -} +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : Yubao Li +// Created : 12-01-2015 +// +// Last Modified By : Yubao Li +// Last Modified On : 12-01-2015 +// *********************************************************************** +// +// Copyright (c) . All rights reserved. +// +// +// 授权策略上下文,一个典型的策略模式 +// 根据用户账号的不同,采用不同的授权模式,以后可以扩展更多的授权方式 +// +// *********************************************************************** + +using System.Collections.Generic; +using Infrastructure; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App +{ + /// + /// 授权策略上下文,一个典型的策略模式 + /// + public class AuthStrategyContext + { + private readonly IAuthStrategy _strategy; + public AuthStrategyContext(IAuthStrategy strategy) + { + this._strategy = strategy; + } + + public User User + { + get { return _strategy.User; } + } + + public List Modules + { + get { return _strategy.Modules; } + } + + public List ModuleElements + { + get { return _strategy.ModuleElements; } + } + + public List Roles + { + get { return _strategy.Roles; } + } + + public List Resources + { + get { return _strategy.Resources; } + } + + public List Orgs + { + get { return _strategy.Orgs; } + } + + public List GetProperties(string moduleCode) + { + return _strategy.GetProperties(moduleCode); + } + + } + +} diff --git a/OpenAuth.App/AuthStrategies/NormalAuthStrategy.cs b/OpenAuth.App/AuthStrategies/NormalAuthStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..0b68efa5b102c1cc942cfb818b6dc54ba614a88b --- /dev/null +++ b/OpenAuth.App/AuthStrategies/NormalAuthStrategy.cs @@ -0,0 +1,142 @@ +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : 李玉宝 +// Created : 06-06-2018 +// +// Last Modified By : 李玉宝 +// Last Modified On : 07-04-2018 +// *********************************************************************** +// +// Copyright (c) http://www.openauth.me. All rights reserved. +// +// +// 普通用户授权策略 +// +// *********************************************************************** + + +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + +namespace OpenAuth.App +{ + /// + /// 普通用户授权策略 + /// + public class NormalAuthStrategy :BaseApp, IAuthStrategy + { + + protected User _user; + + private List _userRoleIds; //用户角色GUID + private DbExtension _dbExtension; + + public List Modules + { + get { + var moduleIds = UnitWork.Find( + u => + (u.Key == Define.ROLEMODULE && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); + + var modules = (from module in UnitWork.Find(u =>moduleIds.Contains(u.Id)) + select new ModuleView + { + SortNo = module.SortNo, + Name = module.Name, + Code = module.Code, + CascadeId = module.CascadeId, + Id = module.Id, + IconName = module.IconName, + Url = module.Url, + ParentId = module.ParentId, + ParentName = module.ParentName, + IsSys = module.IsSys, + Status = module.Status + }).ToList(); + + var usermoduleelements = ModuleElements; + + foreach (var module in modules) + { + module.Elements =usermoduleelements.Where(u => u.ModuleId == module.Id).ToList(); + } + + return modules; + } + } + + public List ModuleElements + { + get + { + var elementIds = UnitWork.Find( + u => + (u.Key == Define.ROLEELEMENT && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); + var usermoduleelements = UnitWork.Find(u => elementIds.Contains(u.Id)); + return usermoduleelements.ToList(); + } + } + + public List Roles + { + get { return UnitWork.Find(u => _userRoleIds.Contains(u.Id)).ToList(); } + } + + public List Resources + { + get + { + var resourceIds = UnitWork.Find( + u => + (u.Key == Define.ROLERESOURCE && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); + return UnitWork.Find(u => resourceIds.Contains(u.Id)).ToList(); + } + } + + public List Orgs + { + get + { + var orgids = UnitWork.Find( + u =>u.FirstId == _user.Id && u.Key == Define.USERORG).Select(u => u.SecondId); + return UnitWork.Find(u => orgids.Contains(u.Id)).ToList(); + } + } + + public User User + { + get { return _user; } + set + { + _user = value; + _userRoleIds = UnitWork.Find(u => u.FirstId == _user.Id && u.Key == Define.USERROLE).Select(u => u.SecondId).ToList(); + } + } + + /// + /// 获取用户可访问的字段列表 + /// + /// 模块的code + /// + public List GetProperties(string moduleCode) + { + var allprops = _dbExtension.GetProperties(moduleCode); + var props =UnitWork.Find(u => + u.Key == Define.ROLEDATAPROPERTY && _userRoleIds.Contains(u.FirstId) && u.SecondId == moduleCode) + .Select(u => u.ThirdId); + + return allprops.Where(u => props.Contains(u.Key)).ToList(); + } + + //用户角色 + + public NormalAuthStrategy(IUnitWork unitWork, IRepository repository, DbExtension dbExtension) : base(unitWork, repository,null) + { + _dbExtension = dbExtension; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/AuthStrategies/SystemAuthStrategy.cs b/OpenAuth.App/AuthStrategies/SystemAuthStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..b3800c6f60a7a273406a8d1c7942a385ba55150c --- /dev/null +++ b/OpenAuth.App/AuthStrategies/SystemAuthStrategy.cs @@ -0,0 +1,111 @@ +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : 李玉宝 +// Created : 06-06-2018 +// +// Last Modified By : 李玉宝 +// Last Modified On : 07-05-2018 +// *********************************************************************** +// +// Copyright (c) http://www.openauth.me. All rights reserved. +// +// +// 超级管理员授权策略 +// +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + +namespace OpenAuth.App +{ + /// + /// 领域服务 + /// 超级管理员权限 + /// 超级管理员使用guid.empty为ID,可以根据需要修改 + /// + public class SystemAuthStrategy : BaseApp, IAuthStrategy + { + protected User _user; + private DbExtension _dbExtension; + + public List Modules + { + get { + var modules = (from module in UnitWork.Find(null) + select new ModuleView + { + SortNo = module.SortNo, + Name = module.Name, + Id = module.Id, + CascadeId = module.CascadeId, + Code = module.Code, + IconName = module.IconName, + Url = module.Url, + ParentId = module.ParentId, + ParentName = module.ParentName, + IsSys = module.IsSys, + Status = module.Status + }).ToList(); + + foreach (var module in modules) + { + module.Elements = UnitWork.Find(u => u.ModuleId == module.Id).ToList(); + } + + return modules; + } + } + + public List Roles + { + get { return UnitWork.Find(null).ToList(); } + } + + public List ModuleElements + { + get { return UnitWork.Find(null).ToList(); } + } + + public List Resources + { + get { return UnitWork.Find(null).ToList(); } + } + + public List Orgs + { + get { return UnitWork.Find(null).ToList(); } + } + + public User User + { + get { return _user; } + set //禁止外部设置 + { + throw new Exception("超级管理员,禁止设置用户"); + } + } + + public List GetProperties(string moduleCode) + { + return _dbExtension.GetProperties(moduleCode); + } + + + public SystemAuthStrategy(IUnitWork unitWork, IRepository repository, DbExtension dbExtension) : base(unitWork, repository, null) + { + _dbExtension = dbExtension; + _user = new User + { + Account = Define.SYSTEM_USERNAME, + Name = "超级管理员", + Id = Guid.Empty.ToString() + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/AuthoriseService.cs b/OpenAuth.App/AuthoriseService.cs deleted file mode 100644 index 64918228bae94b545592d13ab4eb01fd94f044fe..0000000000000000000000000000000000000000 --- a/OpenAuth.App/AuthoriseService.cs +++ /dev/null @@ -1,131 +0,0 @@ -// *********************************************************************** -// Assembly : OpenAuth.Domain -// Author : yubaolee -// Created : 04-21-2016 -// -// Last Modified By : yubaolee -// Last Modified On : 04-21-2016 -// Contact : Microsoft -// File: AuthenService.cs -// *********************************************************************** - -using System; -using System.Collections.Generic; -using System.Linq; -using OpenAuth.Repository.Domain; - -namespace OpenAuth.App -{ - /// - /// 领域服务 - /// 用户授权服务 - /// - public class AuthoriseService :BaseApp - { - - protected User _user; - - private List _userRoleIds; //用户角色GUID - - public List Modules - { - get { return GetModulesQuery().ToList(); } - } - - public List Roles - { - get { return GetRolesQuery().ToList(); } - } - - public List ModuleElements - { - get { return GetModuleElementsQuery().ToList(); } - } - - public List Resources - { - get { return GetResourcesQuery().ToList(); } - } - - public List Orgs - { - get { return GetOrgsQuery().ToList(); } - } - - public User User - { - get { return _user; } - set - { - _user = value; - _userRoleIds = UnitWork.Find(u => u.FirstId == _user.Id && u.Key == Define.USERROLE).Select(u => u.SecondId).ToList(); - } - } - - public void Check(string userName, string password) - { - var _user = Repository.FindSingle(u => u.Account == userName); - if (_user == null) - { - throw new Exception("用户帐号不存在"); - } - _user.CheckPassword(password); - } - - /// - /// 用户可访问的机构 - /// - /// IQueryable<Org>. - public virtual IQueryable GetOrgsQuery() - { - var orgids = UnitWork.Find( - u => - (u.FirstId == _user.Id && u.Key == Define.USERORG) || - (u.Key == Define.ROLEORG && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); - return UnitWork.Find(u => orgids.Contains(u.Id)); - } - - /// - /// 获取用户可访问的资源 - /// - /// IQueryable<Resource>. - public virtual IQueryable GetResourcesQuery() - { - var resourceIds = UnitWork.Find( - u => - (u.FirstId == _user.Id && u.Key == Define.USERRESOURCE) || - (u.Key == Define.ROLERESOURCE && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); - return UnitWork.Find(u => resourceIds.Contains(u.Id)); - } - - /// - /// 模块菜单权限 - /// - public virtual IQueryable GetModuleElementsQuery() - { - var elementIds = UnitWork.Find( - u => - (u.FirstId == _user.Id && u.Key == Define.USERELEMENT) || - (u.Key == Define.ROLEELEMENT && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); - return UnitWork.Find(u => elementIds.Contains(u.Id)); - } - - /// - /// 得出最终用户拥有的模块 - /// - public virtual IQueryable GetModulesQuery() - { - var moduleIds = UnitWork.Find( - u => - (u.FirstId == _user.Id && u.Key == Define.USERMODULE) || - (u.Key == Define.ROLEMODULE && _userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId); - return UnitWork.Find(u => moduleIds.Contains(u.Id)).OrderBy(u => u.SortNo); - } - - //用户角色 - public virtual IQueryable GetRolesQuery() - { - return UnitWork.Find(u => _userRoleIds.Contains(u.Id)); - } - } -} \ No newline at end of file diff --git a/OpenAuth.App/AuthorizeApp.cs b/OpenAuth.App/AuthorizeApp.cs deleted file mode 100644 index 75a5f3dc794529f6462ac69c58a677743c0b1a79..0000000000000000000000000000000000000000 --- a/OpenAuth.App/AuthorizeApp.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Linq; -using Infrastructure; -using OpenAuth.App.Response; -using OpenAuth.Repository.Domain; -using OpenAuth.Repository.Interface; - -namespace OpenAuth.App -{ - /// - /// 加载用户所有可访问的资源/机构/模块 - /// 李玉宝新增于2016-07-19 10:53:30 - /// - public class AuthorizeApp - { - public SystemAuthService AuthService { get; set; } - public AuthoriseService AuthoriseService { get; set; } - - public IUnitWork _unitWork { get; set; } - public AuthoriseService Create(string loginuser) - { - if (loginuser == "System") - { - return AuthService; - } - else - { - AuthoriseService.User = _unitWork.FindSingle(u => u.Account == loginuser); - return AuthoriseService; - } - } - - public UserWithAccessedCtrls GetAccessedControls(string username) - { - var service = Create(username); - var user = new UserWithAccessedCtrls - { - User = service.User, - Orgs = service.Orgs, - Modules = service.Modules.OrderBy(u => u.SortNo).ToList().MapToList(), - Resources = service.Resources, - Roles = service.Roles - }; - - var ModuleElements=service.ModuleElements; - - foreach (var moduleView in user.Modules) - { - moduleView.Elements = - ModuleElements.Where(u => u.ModuleId == moduleView.Id).OrderBy(u => u.Sort).ToList(); - } - - return user; - } - } -} \ No newline at end of file diff --git a/OpenAuth.App/AutofacExt.cs b/OpenAuth.App/AutofacExt.cs index d8f1f141af81fe77b314a94cc136cfc6d14793c0..bf37e6adea157226e861839597335e642ec71a65 100644 --- a/OpenAuth.App/AutofacExt.cs +++ b/OpenAuth.App/AutofacExt.cs @@ -12,12 +12,21 @@ // IOC扩展 // *********************************************************************** +using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; -using System.Web.Http; -using System.Web.Mvc; +using System.Runtime.Loader; using Autofac; -using Autofac.Integration.Mvc; -using Autofac.Integration.WebApi; +using Autofac.Extensions.DependencyInjection; +using Autofac.Extras.Quartz; +using Infrastructure.Cache; +using Infrastructure.Extensions.AutofacManager; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyModel; +using OpenAuth.App.Interface; +using OpenAuth.App.SSO; using OpenAuth.Repository; using OpenAuth.Repository.Interface; using IContainer = Autofac.IContainer; @@ -27,54 +36,95 @@ namespace OpenAuth.App public static class AutofacExt { private static IContainer _container; - - public static void InitAutofac() + public static IContainer InitForTest(IServiceCollection services) { var builder = new ContainerBuilder(); - + //注册数据库基础操作和工作单元 - builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>)).PropertiesAutowired(); - builder.RegisterType(typeof(UnitWork)).As(typeof(IUnitWork)).PropertiesAutowired(); - - //注册app层 - builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).PropertiesAutowired(); + services.AddScoped(typeof(IRepository<>), typeof(BaseRepository<>)); + services.AddScoped(typeof(IUnitWork), typeof(UnitWork)); - // 注册controller,使用属性注入 - builder.RegisterControllers(Assembly.GetCallingAssembly()).PropertiesAutowired(); + //注入授权 + builder.RegisterType(typeof(LocalAuth)).As(typeof(IAuth)); - //注册所有的ApiControllers - builder.RegisterApiControllers(Assembly.GetCallingAssembly()).PropertiesAutowired(); - - builder.RegisterModelBinders(Assembly.GetCallingAssembly()); - builder.RegisterModelBinderProvider(); + //注册app层 + builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()); - // OPTIONAL: Register web abstractions like HttpContextBase. - //builder.RegisterModule(); + //防止单元测试时已经注入 + if (services.All(u => u.ServiceType != typeof(ICacheContext))) + { + services.AddScoped(typeof(ICacheContext), typeof(CacheContext)); + } - // OPTIONAL: Enable property injection in view pages. - builder.RegisterSource(new ViewRegistrationSource()); + if (services.All(u => u.ServiceType != typeof(IHttpContextAccessor))) + { + services.AddScoped(typeof(IHttpContextAccessor), typeof(HttpContextAccessor)); + } + + InitDependency(builder); + + builder.RegisterModule(new QuartzAutofacFactoryModule()); - // 注册所有的Attribute - builder.RegisterFilterProvider(); + builder.Populate(services); - // Set the dependency resolver to be Autofac. _container = builder.Build(); + return _container; - //Set the MVC DependencyResolver - DependencyResolver.SetResolver(new AutofacDependencyResolver(_container)); - - //Set the WebApi DependencyResolver - GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)_container); } + + + public static void InitAutofac(ContainerBuilder builder) + { + + //注册数据库基础操作和工作单元 + builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IRepository<>)); + builder.RegisterType(typeof(UnitWork)).As(typeof(IUnitWork)); + //注入授权 + builder.RegisterType(typeof(LocalAuth)).As(typeof(IAuth)); + + //注册app层 + builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()); + + builder.RegisterType(typeof(CacheContext)).As(typeof(ICacheContext)); + builder.RegisterType(typeof(HttpContextAccessor)).As(typeof(IHttpContextAccessor)); + + InitDependency(builder); + + builder.RegisterModule(new QuartzAutofacFactoryModule()); + } + /// - /// 从容器中获取对象 + /// 注入所有继承了IDependency接口 /// - /// - public static T GetFromFac() + /// + private static void InitDependency(ContainerBuilder builder) { - return _container.Resolve(); - // return (T)DependencyResolver.Current.GetService(typeof(T)); + Type baseType = typeof(IDependency); + var compilationLibrary = DependencyContext.Default + .CompileLibraries + .Where(x => !x.Serviceable + && x.Type == "project") + .ToList(); + var count1 = compilationLibrary.Count; + List assemblyList = new List(); + + foreach (var _compilation in compilationLibrary) + { + try + { + assemblyList.Add(AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(_compilation.Name))); + } + catch (Exception ex) + { + Console.WriteLine(_compilation.Name + ex.Message); + } + } + + builder.RegisterAssemblyTypes(assemblyList.ToArray()) + .Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract) + .AsSelf().AsImplementedInterfaces() + .InstancePerLifetimeScope(); } } } \ No newline at end of file diff --git a/OpenAuth.App/BaseApp.cs b/OpenAuth.App/BaseApp.cs index e944954d2b52d3aca73a1b15a3b5895c51318092..f0525be910d49c1deb7a49e73ecb65a1486d100f 100644 --- a/OpenAuth.App/BaseApp.cs +++ b/OpenAuth.App/BaseApp.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; using OpenAuth.Repository.Core; using OpenAuth.Repository.Domain; using OpenAuth.Repository.Interface; @@ -11,24 +13,69 @@ namespace OpenAuth.App /// 如用户管理:Class UserManagerApp:BaseApp /// /// - public class BaseApp where T:Entity + public class BaseApp where T : Entity { + /// + /// 用于普通的数据库操作 + /// + /// The repository. + protected IRepository Repository; + /// /// 用于事务操作 /// /// The unit work. - public IUnitWork UnitWork { get; set; } + protected IUnitWork UnitWork; + + protected IAuth _auth; + + public BaseApp(IUnitWork unitWork, IRepository repository, IAuth auth) + { + UnitWork = unitWork; + Repository = repository; + _auth = auth; + } + /// - /// 用于普通的数据库操作 + /// 获取当前登录用户的数据访问权限 /// - /// The repository. - public IRepository Repository { get; set; } + /// linq表达式参数的名称,如u=>u.name中的"u" + /// + protected IQueryable GetDataPrivilege(string parametername) + { + var loginUser = _auth.GetCurrentUser(); + if (loginUser.User.Account == Define.SYSTEM_USERNAME) return UnitWork.Find(null); //超级管理员特权 + + var moduleName = typeof(T).Name; + var rule = UnitWork.FindSingle(u => u.SourceCode == moduleName); + if (rule == null) return UnitWork.Find(null); //没有设置数据规则,那么视为该资源允许被任何主体查看 + if (rule.PrivilegeRules.Contains(Define.DATAPRIVILEGE_LOGINUSER) || + rule.PrivilegeRules.Contains(Define.DATAPRIVILEGE_LOGINROLE)|| + rule.PrivilegeRules.Contains(Define.DATAPRIVILEGE_LOGINORG)) + { + + //即把{loginUser} =='xxxxxxx'换为 loginUser.User.Id =='xxxxxxx',从而把当前登录的用户名与当时设计规则时选定的用户id对比 + rule.PrivilegeRules = rule.PrivilegeRules.Replace(Define.DATAPRIVILEGE_LOGINUSER, loginUser.User.Id); + + var roles = loginUser.Roles.Select(u => u.Id).ToList(); + roles.Sort(); //按字母排序,这样可以进行like操作 + rule.PrivilegeRules = rule.PrivilegeRules.Replace(Define.DATAPRIVILEGE_LOGINROLE, + string.Join(',',roles)); + + var orgs = loginUser.Orgs.Select(u => u.Id).ToList(); + orgs.Sort(); + rule.PrivilegeRules = rule.PrivilegeRules.Replace(Define.DATAPRIVILEGE_LOGINORG, + string.Join(',',orgs)); + } + return UnitWork.Find(null).GenerateFilter(parametername, + JsonHelper.Instance.Deserialize(rule.PrivilegeRules)); + } /// /// 按id批量删除 /// /// - public void Delete(string[] ids) + public virtual void Delete(string[] ids) { Repository.Delete(u => ids.Contains(u.Id)); } @@ -39,16 +86,15 @@ namespace OpenAuth.App } /// - /// 如果一个类有层级结构(树状),则修改该节点时,要修改该节点的所有子节点 - /// //修改对象的级联ID,生成类似XXX.XXX.X.XX + /// 计算实体更新的层级信息 /// /// U必须是一个继承TreeEntity的结构 /// - - public void ChangeModuleCascade(U entity) where U:TreeEntity + public void CaculateCascade(U entity) where U : TreeEntity { + if (entity.ParentId == "") entity.ParentId = null; string cascadeId; - int currentCascadeId = 1; //当前结点的级联节点最后一位 + int currentCascadeId = 1; //当前结点的级联节点最后一位 var sameLevels = UnitWork.Find(o => o.ParentId == entity.ParentId && o.Id != entity.Id); foreach (var obj in sameLevels) { @@ -78,4 +124,4 @@ namespace OpenAuth.App entity.CascadeId = cascadeId; } } -} +} \ No newline at end of file diff --git a/OpenAuth.App/BaseTreeApp.cs b/OpenAuth.App/BaseTreeApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..f6383c73e5f90cb781dfb5d2182ba97b51e71df7 --- /dev/null +++ b/OpenAuth.App/BaseTreeApp.cs @@ -0,0 +1,57 @@ +using System.Linq; +using OpenAuth.App.Interface; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Interface; + +namespace OpenAuth.App +{ + /// + /// 树状结构处理 + /// + /// + public class BaseTreeApp :BaseApp where T : TreeEntity + { + + + public BaseTreeApp(IUnitWork unitWork, IRepository repository, IAuth auth) + : base(unitWork, repository,auth) + { + } + + + /// + /// 更新树状结构实体 + /// + /// + /// + public void UpdateTreeObj(U obj) where U : TreeEntity + { + CaculateCascade(obj); + + //获取旧的的CascadeId + var cascadeId = Repository.FindSingle(o => o.Id == obj.Id).CascadeId; + //根据CascadeId查询子部门 + var objs = Repository.Find(u => u.CascadeId.Contains(cascadeId) && u.Id != obj.Id) + .OrderBy(u => u.CascadeId).ToList(); + + //更新操作 + UnitWork.Update(obj); + + //更新子模块的CascadeId + foreach (var a in objs) + { + a.CascadeId = a.CascadeId.Replace(cascadeId, obj.CascadeId); + if (a.ParentId == obj.Id) + { + a.ParentName = obj.Name; + } + + UnitWork.Update(a); + } + + UnitWork.Save(); + } + + + } +} \ No newline at end of file diff --git a/OpenAuth.App/BuilderTableApp.cs b/OpenAuth.App/BuilderTableApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..9f03c85a3276bfe9153581ce84f737a432527e7a --- /dev/null +++ b/OpenAuth.App/BuilderTableApp.cs @@ -0,0 +1,652 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using System.Text; +using Infrastructure; +using Infrastructure.Extensions; +using Infrastructure.Helpers; +using Infrastructure.Utilities; +using Microsoft.Extensions.DependencyModel; +using Microsoft.Extensions.Options; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Core; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class BuilderTableApp : BaseApp + { + private BuilderTableColumnApp _builderTableColumnApp; + private CategoryApp _categoryApp; + private DbExtension _dbExtension; + private string _webProject = null; + private string _apiNameSpace = null; + private string _startName = ""; + private IOptions _appConfiguration; + + public BuilderTableApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app, IAuth auth, DbExtension dbExtension, BuilderTableColumnApp builderTableColumnApp, + IOptions appConfiguration, CategoryApp categoryApp) : base(unitWork, repository, auth) + { + _dbExtension = dbExtension; + _builderTableColumnApp = builderTableColumnApp; + _appConfiguration = appConfiguration; + _categoryApp = categoryApp; + } + + private string StratName + { + get + { + if (_startName == "") + { + _startName = WebProject.Substring(0, _webProject.IndexOf('.')); + } + + return _startName; + } + } + + private string WebProject + { + get + { + if (_webProject != null) + return _webProject; + _webProject = ProjectPath.GetLastIndexOfDirectoryName(".WebApi") ?? + ProjectPath.GetLastIndexOfDirectoryName("Api") ?? + ProjectPath.GetLastIndexOfDirectoryName(".Mvc"); + if (_webProject == null) + { + throw new Exception("未获取到以.WebApi结尾的项目名称,无法创建页面"); + } + + return _webProject; + } + } + + /// + /// 加载列表 + /// + public TableResp Load(QueryBuilderTableListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + var result = new TableResp(); + var objs = UnitWork.Find(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key)); + } + + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).ToList(); + result.count = objs.Count(); + return result; + } + + public string Add(AddOrUpdateBuilderTableReq req) + { + if (string.IsNullOrEmpty(req.TableName)) + { + throw new Exception("英文表名不能为空"); + } + + if (string.IsNullOrEmpty(req.ModuleName)) + { + throw new Exception("模块名称不能为空"); + } + + if (string.IsNullOrEmpty(req.Namespace)) + { + throw new Exception("命名空间不能为空"); + } + + var columns = _dbExtension.GetDbTableStructure(req.TableName); + if (!columns.Any()) + { + throw new Exception($"未能找到{req.TableName}表结构定义"); + } + + var obj = req.MapTo(); + if (string.IsNullOrEmpty(obj.ClassName)) obj.ClassName = obj.TableName; + if (string.IsNullOrEmpty(obj.ModuleCode)) obj.ModuleCode = obj.TableName; + + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + UnitWork.Add(obj); + + foreach (var column in columns) + { + var builderColumn = new BuilderTableColumn + { + ColumnName = column.ColumnName, + Comment = column.Comment, + ColumnType = column.ColumnType, + EntityType = column.EntityType, + EntityName = column.ColumnName, + + IsKey = column.IsKey == 1, + IsRequired = column.IsNull != 1, + IsEdit = true, + IsInsert = true, + IsList = true, + MaxLength = column.MaxLength, + TableName = obj.TableName, + TableId = obj.Id, + + CreateUserId = user.Id, + CreateUserName = user.Name, + CreateTime = DateTime.Now + }; + UnitWork.Add(builderColumn); + } + + UnitWork.Save(); + return obj.Id; + } + + public void Update(AddOrUpdateBuilderTableReq obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new BuilderTable + { + TableName = obj.TableName, + Comment = obj.Comment, + DetailTableName = obj.DetailTableName, + DetailComment = obj.DetailComment, + ClassName = obj.ClassName, + Namespace = obj.Namespace, + ModuleCode = obj.ModuleCode, + ModuleName = obj.ModuleName, + Folder = obj.Folder, + Options = obj.Options, + TypeId = obj.TypeId, + TypeName = obj.TypeName, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + } + + /// + /// 删除头和字段明细 + /// + /// + public void DelTableAndcolumns(string[] ids) + { + UnitWork.Delete(u => ids.Contains(u.Id)); + UnitWork.Delete(u => ids.Contains(u.TableId)); + UnitWork.Save(); + } + + + /// + /// 生成实体Model + /// + /// + public void CreateEntity(CreateEntityReq req) + { + var sysTableInfo = Repository.FindSingle(u => u.Id == req.Id); + var tableColumns = _builderTableColumnApp.Find(req.Id); + if (sysTableInfo == null + || tableColumns == null + || tableColumns.Count == 0) + throw new Exception("未能找到正确的模版信息"); + + CheckExistsModule(sysTableInfo.ClassName); + + CreateEntityModel(tableColumns, sysTableInfo); + } + + + /// + /// 创建业务逻辑层 + /// + /// + public void CreateBusiness(CreateBusiReq req) + { + var sysTableInfo = Repository.FindSingle(u => u.Id == req.Id); + var tableColumns = _builderTableColumnApp.Find(req.Id); + if (sysTableInfo == null + || tableColumns == null + || tableColumns.Count == 0) + throw new Exception("未能找到正确的模版信息"); + + //生成应用层 + GenerateApp(sysTableInfo); + + //生成应用层的请求参数 + GenerateAppReq(sysTableInfo, tableColumns); + + //生成WebApI接口 + GenerateWebApi(sysTableInfo); + } + + /// + /// 创建应用层 + /// + /// + /// + private void GenerateApp(BuilderTable sysTableInfo) + { + string appRootPath = ProjectPath.GetProjectDirectoryInfo() + .GetDirectories().FirstOrDefault(x => x.Name.ToLower().EndsWith(".app"))?.FullName; + if (string.IsNullOrEmpty(appRootPath)) + { + throw new Exception("未找到openauth.app类库,请确认是否存在"); + } + + CheckExistsModule(sysTableInfo.ModuleCode); + + string domainContent = FileHelper.ReadFile(@"Template\\BuildApp.html") + .Replace("{TableName}", sysTableInfo.TableName) + .Replace("{ModuleCode}", sysTableInfo.ModuleCode) + .Replace("{ModuleName}", sysTableInfo.ModuleName) + .Replace("{ClassName}", sysTableInfo.ClassName) + .Replace("{StartName}", StratName); + FileHelper.WriteFile(appRootPath, sysTableInfo.ModuleCode + ".cs", domainContent); + } + + /// + /// 生成APP层的请求参数 + /// + /// + /// + private void GenerateAppReq(BuilderTable sysTableInfo, List tableColumns) + { + string appRootPath = ProjectPath.GetProjectDirectoryInfo() + .GetDirectories().FirstOrDefault(x => x.Name.ToLower().EndsWith(".app"))?.FullName; + if (string.IsNullOrEmpty(appRootPath)) + { + throw new Exception("未找到openauth.app类库,请确认是否存在"); + } + string domainContent; + domainContent = FileHelper.ReadFile(@"Template\\BuildQueryReq.html") + .Replace("{TableName}", sysTableInfo.TableName) + .Replace("{ModuleCode}", sysTableInfo.ModuleCode) + .Replace("{ModuleName}", sysTableInfo.ModuleName) + .Replace("{ClassName}", sysTableInfo.ClassName) + .Replace("{StartName}", StratName); + FileHelper.WriteFile(Path.Combine(appRootPath, "Request"), $"Query{sysTableInfo.ClassName}ListReq.cs", + domainContent); + + + domainContent = FileHelper.ReadFile(@"Template\\BuildUpdateReq.html"); + + StringBuilder attributeBuilder = new StringBuilder(); + var sysColumn = tableColumns.OrderByDescending(c => c.Sort).ToList(); + foreach (BuilderTableColumn column in sysColumn) + { + attributeBuilder.Append("/// "); + attributeBuilder.Append("\r\n"); + attributeBuilder.Append(" ///" + column.Comment + ""); + attributeBuilder.Append("\r\n"); + attributeBuilder.Append(" /// "); + attributeBuilder.Append("\r\n"); + + string entityType = column.EntityType; + if (!column.IsRequired && column.EntityType != "string") + { + entityType = entityType + "?"; + } + + attributeBuilder.Append(" public " + entityType + " " + column.EntityName + " { get; set; }"); + attributeBuilder.Append("\r\n\r\n "); + } + + domainContent = domainContent.Replace("{ClassName}", sysTableInfo.ClassName) + .Replace("{AttributeList}", attributeBuilder.ToString()); + + var tableAttr = new StringBuilder(); + tableAttr.Append("/// "); + tableAttr.Append("\r\n"); + tableAttr.Append(" ///" + sysTableInfo.Comment + ""); + tableAttr.Append("\r\n"); + tableAttr.Append(" /// "); + tableAttr.Append("\r\n"); + domainContent = domainContent.Replace("{AttributeManager}", tableAttr.ToString()); + + FileHelper.WriteFile(Path.Combine(appRootPath, "Request"), $"AddOrUpdate{sysTableInfo.ClassName}Req.cs", + domainContent); + } + + /// + /// 创建WebAPI接口 + /// + /// + /// + private void GenerateWebApi(BuilderTable sysTableInfo) + { + string domainContent; + string apiPath = ProjectPath.GetProjectDirectoryInfo() + .GetDirectories().FirstOrDefault(x => x.Name.ToLower().EndsWith(".webapi"))?.FullName; + if (string.IsNullOrEmpty(apiPath)) + { + throw new Exception("未找到webapi类库,请确认是存在weiapi类库命名以.webapi结尾"); + } + + var controllerName = sysTableInfo.ClassName + "sController"; + CheckExistsModule(controllerName); //单元测试下无效,因为没有执行webapi项目 + var controllerPath = apiPath + $"\\Controllers\\"; + domainContent = FileHelper.ReadFile(@"Template\\BuildControllerApi.html") + .Replace("{TableName}", sysTableInfo.TableName) + .Replace("{ModuleCode}", sysTableInfo.ModuleCode) + .Replace("{ModuleName}", sysTableInfo.ModuleName) + .Replace("{ClassName}", sysTableInfo.ClassName) + .Replace("{StartName}", StratName); + FileHelper.WriteFile(controllerPath, controllerName + ".cs", domainContent); + } + + /// + /// 创建实体 + /// + /// + /// + private void CreateEntityModel(List sysColumn, BuilderTable tableInfo) + { + string template = "BuildEntity.html"; + string domainContent = FileHelper.ReadFile("Template\\" + template); + + StringBuilder attributeBuilder = new StringBuilder(); + StringBuilder constructionBuilder = new StringBuilder(); //生成构造函数初始化值 + sysColumn = sysColumn.OrderByDescending(c => c.Sort).ToList(); + foreach (BuilderTableColumn column in sysColumn) + { + if (column.IsKey) continue; + + attributeBuilder.Append("/// "); + attributeBuilder.Append("\r\n"); + attributeBuilder.Append(" ///" + column.Comment + ""); + attributeBuilder.Append("\r\n"); + attributeBuilder.Append(" /// "); + attributeBuilder.Append("\r\n"); + + attributeBuilder.Append(" [Description(\""+ column.Comment +"\")]"); + attributeBuilder.Append("\r\n"); + + string entityType = column.EntityType; + if (!column.IsRequired && column.EntityType != "string") + { + entityType = entityType + "?"; + } + + attributeBuilder.Append(" public " + entityType + " " + column.EntityName + " { get; set; }"); + attributeBuilder.Append("\r\n\r\n "); + + constructionBuilder.Append(" this." + column.EntityName + + "=" + (GetDefault(column.EntityType)??"\"\"") + + ";\r\n"); + } + + //获取的是本地开发代码所在目录,不是发布后的目录 + string mapPath = + ProjectPath.GetProjectDirectoryInfo()?.FullName; //new DirectoryInfo(("~/").MapPath()).Parent.FullName; + if (string.IsNullOrEmpty(mapPath)) + { + throw new Exception("未找到生成的目录!"); + } + + domainContent = domainContent.Replace("{ClassName}", tableInfo.ClassName) + .Replace("{AttributeList}", attributeBuilder.ToString()) + .Replace("{Construction}", constructionBuilder.ToString()); + + + var tableAttr = new StringBuilder(); + + tableAttr.Append("/// "); + tableAttr.Append("\r\n"); + tableAttr.Append(" ///" + tableInfo.Comment + ""); + tableAttr.Append("\r\n"); + tableAttr.Append(" /// "); + tableAttr.Append("\r\n"); + tableAttr.Append(" [Table(\"" + tableInfo.TableName + "\")]"); + domainContent = domainContent.Replace("{AttributeManager}", tableAttr.ToString()); + + FileHelper.WriteFile( + mapPath + + $"\\OpenAuth.Repository\\Domain\\", tableInfo.ClassName + ".cs", + domainContent); + } + + private bool IsMysql() + { + return (_appConfiguration.Value.DbType == Define.DBTYPE_MYSQL); + } + + Dictionary PrimitiveTypes = new Dictionary() + { + {"int", typeof(int)} + ,{"long", typeof(long)} + ,{"string", typeof(string)} + ,{"bool", typeof(bool)} + ,{"byte", typeof(byte)} + ,{"char", typeof(char)} + ,{"decimal", typeof(decimal)} + ,{"double", typeof(double)} + ,{"DateTime", typeof(DateTime)} + }; + string? GetDefault(string type) + { + Type t = PrimitiveTypes[type]; + if (t == null) + { + return null; + } + + if (t.IsValueType) + { + if (type == "DateTime") + { + return "DateTime.Now;"; + } + return Activator.CreateInstance(t).ToString(); + } + + return null; + } + + + /// + /// 校验模块是否已经存在 + /// + /// + /// + /// + public void CheckExistsModule(string moduleCode) + { + //如果是第一次创建model,此处反射获取到的是已经缓存过的文件,必须重新运行项目否则新增的文件无法做判断文件是否创建,需要重新做反射实际文件,待修改... + var compilationLibrary = DependencyContext + .Default + .CompileLibraries + .Where(x => !x.Serviceable && x.Type == "project"); + foreach (var compilation in compilationLibrary) + { + var types = AssemblyLoadContext.Default + .LoadFromAssemblyName(new AssemblyName(compilation.Name)) + .GetTypes().Where(x => x.GetTypeInfo().BaseType != null + && x.BaseType == typeof(Entity)); + foreach (var entity in types) + { + if (entity.Name == moduleCode ) + throw new Exception($"实际表名【{moduleCode}】已创建实体,不能创建实体"); + + if (entity.Name != moduleCode) + { + var tableAttr = entity.GetCustomAttribute(); + if (tableAttr != null && tableAttr.Name == moduleCode) + { + throw new Exception( + $"实际表名【{moduleCode}】已被创建建实体,不能创建"); + } + } + } + } + } + + /// + /// 创建vue界面 + /// + /// + /// + public void CreateVue(CreateVueReq req) + { + if (string.IsNullOrEmpty(req.VueProjRootPath)) + { + throw new Exception("请提供vue项目的根目录,如:C:\\OpenAuth.Pro\\Client"); + } + var sysTableInfo = Repository.FindSingle(u => u.Id == req.Id); + var tableColumns = _builderTableColumnApp.Find(req.Id); + if (sysTableInfo == null + || tableColumns == null + || tableColumns.Count == 0) + throw new Exception("未能找到正确的模版信息"); + + var domainContent = FileHelper.ReadFile(@"Template\\BuildVue.html"); + + StringBuilder dialogStrBuilder = new StringBuilder(); //编辑对话框 + StringBuilder tempBuilder = new StringBuilder(); //临时类的默认值属性 + var syscolums = tableColumns.OrderByDescending(c => c.Sort).ToList(); + + string[] eidtTye = new string[] { "select", "selectList", "checkbox" }; + if (syscolums.Exists(x => eidtTye.Contains(x.EditType) && string.IsNullOrEmpty(x.DataSource))) + { + throw new Exception($"编辑类型为[{string.Join(',', eidtTye)}]时必须选择数据源"); + } + + foreach (BuilderTableColumn column in syscolums) + { + if (!column.IsEdit) continue; + tempBuilder.Append($" {column.ColumnName.ToCamelCase()}: "); + + dialogStrBuilder.Append($" =0\">\r\n"); + + if (column.EditType == "switch") + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"false, //{column.Comment} \r\n"); + } + else if (column.EditType == "date") + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"'', //{column.Comment} \r\n"); + } + else if (column.EditType == "datetime") + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"'', //{column.Comment} \r\n"); + } + else if (column.EditType == "decimal") //小数 + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"0, //{column.Comment} \r\n"); + } + else if (column.EditType =="number") //整数 + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"0, //{column.Comment} \r\n"); + } + else if (column.EditType =="textarea") + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"'', //{column.Comment} \r\n"); + } + else if (column.EditType =="select") + { + var categories = _categoryApp.LoadByTypeId(column.DataSource); + if (categories.IsNullOrEmpty()) + { + throw new Exception($"未能找到{column.DataSource}对应的值,请在分类管理里面添加"); + } + + dialogStrBuilder.Append($" \r\n"); + foreach (var category in categories) + { + dialogStrBuilder.Append($" \r\n"); + } + dialogStrBuilder.Append(" \r\n"); + tempBuilder.Append($"'', //{column.Comment} \r\n"); + } + else if (column.EditType =="checkbox") + { + var categories = _categoryApp.LoadByTypeId(column.DataSource); + if (categories.IsNullOrEmpty()) + { + throw new Exception($"未能找到{column.DataSource}对应的值,请在分类管理里面添加"); + } + + dialogStrBuilder.Append($" \r\n"); + foreach (var category in categories) + { + dialogStrBuilder.Append($" \r\n"); + } + dialogStrBuilder.Append(" \r\n"); + tempBuilder.Append($"[], //{column.Comment} \r\n"); + } + else + { + dialogStrBuilder.Append($" \r\n"); + tempBuilder.Append($"'', //{column.Comment} \r\n"); + } + + dialogStrBuilder.Append(" \r\n"); + dialogStrBuilder.Append("\r\n"); + } + + tempBuilder.Append(" nothing:'' //代码生成时的占位符,看不顺眼可以删除 \r\n"); + + domainContent = domainContent.Replace("{ClassName}", sysTableInfo.ClassName) + .Replace("{TableName}", sysTableInfo.ClassName.ToCamelCase()) + .Replace("{Temp}", tempBuilder.ToString()) + .Replace("{DialogFormItem}", dialogStrBuilder.ToString()); + + FileHelper.WriteFile(Path.Combine(req.VueProjRootPath, $"src/views/{sysTableInfo.ClassName.ToCamelCase()}s/"), + $"index.vue", + domainContent); + } + + /// + /// 创建vue接口 + /// + /// + /// + public void CreateVueApi(CreateVueReq req) + { + if (string.IsNullOrEmpty(req.VueProjRootPath)) + { + throw new Exception("请提供vue项目的根目录,如:C:\\OpenAuth.Pro\\Client"); + } + var sysTableInfo = Repository.FindSingle(u => u.Id == req.Id); + var tableColumns = _builderTableColumnApp.Find(req.Id); + if (sysTableInfo == null + || tableColumns == null + || tableColumns.Count == 0) + throw new Exception("未能找到正确的模版信息"); + + var domainContent = FileHelper.ReadFile(@"Template\\BuildVueApi.html"); + + domainContent = domainContent.Replace("{TableName}", sysTableInfo.ClassName.ToCamelCase()); + + FileHelper.WriteFile(Path.Combine(req.VueProjRootPath, $"src/api/"),$"{sysTableInfo.ClassName.ToCamelCase()}s.js", + domainContent); + } + } +} + diff --git a/OpenAuth.App/BuilderTableColumnApp.cs b/OpenAuth.App/BuilderTableColumnApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..679182dc666d2dd537f8070307841de07d4b563e --- /dev/null +++ b/OpenAuth.App/BuilderTableColumnApp.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class BuilderTableColumnApp : BaseApp + { + public BuilderTableColumnApp(IUnitWork unitWork, IRepository repository, + IAuth auth) : base(unitWork, repository,auth) + { + } + + /// + /// 加载列表 + /// + public TableResp Load(QueryBuilderTableColumnListReq request) + { + if (string.IsNullOrEmpty(request.BuilderTableId)) + { + throw new Exception($"缺少必要的参数BuilderTableId"); + } + var result = new TableResp(); + var objs = UnitWork.Find(u =>u.TableId == request.BuilderTableId); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.ColumnName.Contains(request.key)); + } + + result.data = objs.OrderBy(u => u.ColumnName) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).ToList(); + result.count = objs.Count(); + return result; + } + + public void Update(AddOrUpdateBuilderTableColumnReq obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new BuilderTableColumn + { + TableId = obj.TableId, + TableName = obj.TableName, + ColumnName = obj.ColumnName, + Comment = obj.Comment, + ColumnType = obj.ColumnType, + EntityType = obj.EntityType, + EntityName = obj.EntityName, + IsKey = obj.IsKey, + IsIncrement = obj.IsIncrement, + IsRequired = obj.IsRequired, + IsInsert = obj.IsInsert, + IsEdit = obj.IsEdit, + IsList = obj.IsList, + IsQuery = obj.IsQuery, + QueryType = obj.QueryType, + HtmlType = obj.HtmlType, + EditType = obj.EditType, + Sort = obj.Sort, + CreateTime = obj.CreateTime, + CreateUserId = obj.CreateUserId, + EditRow = obj.EditRow, + EditCol = obj.EditCol, + CreateUserName = obj.CreateUserName, + MaxLength = obj.MaxLength, + DataSource = obj.DataSource, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + + } + + + + + public List Find(string tableId) + { + return Repository.Find(u => u.TableId == tableId).ToList(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/CategoryApp.cs b/OpenAuth.App/CategoryApp.cs index fc889d657bcf26fd224a88173b53a65caf021dd5..4803fb1ea74673b072ea40cd8cd201efe8c9c4e4 100644 --- a/OpenAuth.App/CategoryApp.cs +++ b/OpenAuth.App/CategoryApp.cs @@ -1,75 +1,96 @@ using System; using System.Collections.Generic; using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.Response; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + namespace OpenAuth.App { - /// - /// 分类管理 - /// - public class CategoryApp:BaseApp + public class CategoryApp : BaseApp { - - public IEnumerable Get(string type) + /// + /// 加载列表 + /// + public TableData Load(QueryCategoryListReq request) { - return Repository.Find(u => u.TypeId == type); - } - - public void Add(Category category) - { - if (string.IsNullOrEmpty(category.Id)) + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) { - category.Id = Guid.NewGuid().ToString(); + throw new CommonException("登录已过期", Define.INVALID_TOKEN); } - Repository.Add(category); - } - - public void Update(Category category) - { - Repository.Update(u =>u.Id,category); - } + var properties = loginContext.GetProperties("Category"); - public TableData All(QueryCategoriesReq request) - { - var result = new TableData(); - var categories = UnitWork.Find(null) ; - if (!string.IsNullOrEmpty(request.key)) + if (properties == null || properties.Count == 0) { - categories = categories.Where(u => u.Name.Contains(request.key) || u.Id.Contains(request.key)); + throw new Exception("当前登录用户没有访问该模块字段的权限,请联系管理员配置"); } - + + var result = new TableData(); + var objs = UnitWork.Find(null); if (!string.IsNullOrEmpty(request.TypeId)) { - categories = categories.Where(u => u.TypeId == request.TypeId); + objs = objs.Where(u => u.TypeId == request.TypeId); + } + + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key) || u.Name.Contains(request.key)); } - var query = from category in categories - join ct in UnitWork.Find(null) on category.TypeId equals ct.Id - into tmp - from ct in tmp.DefaultIfEmpty() - select new - { - category.Name, - category.Id, - category.TypeId, - TypeName = ct.Name, - category.Description - }; - - result.data = query.OrderBy(u => u.TypeId) + var propertyStr = string.Join(',', properties.Select(u =>u.Key)); + result.columnHeaders = properties; + result.data = objs.OrderBy(u => u.DtCode) .Skip((request.page - 1) * request.limit) - .Take(request.limit).ToList(); - result.count = categories.Count(); + .Take(request.limit).Select($"new ({propertyStr})"); + result.count = objs.Count(); return result; } - public List AllTypes() + public void Add(AddOrUpdateCategoryReq req) + { + var obj = req.MapTo(); + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + Repository.Add(obj); + } + + public void Update(AddOrUpdateCategoryReq obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new Category + { + Enable = obj.Enable, + DtValue = obj.DtValue, + DtCode = obj.DtCode, + TypeId = obj.TypeId, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:要修改的字段赋值 + }); + + } + + /// + /// 加载一个分类类型里面的所有值,即字典的所有值 + /// + /// + /// + public List LoadByTypeId(string typeId) + { + return Repository.Find(u => u.TypeId == typeId).ToList(); + } + + public CategoryApp(IUnitWork unitWork, IRepository repository,IAuth auth) : base(unitWork, repository, auth) { - return UnitWork.Find(null).ToList(); } } } \ No newline at end of file diff --git a/OpenAuth.App/CategoryTypeApp.cs b/OpenAuth.App/CategoryTypeApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..69b3be71a5f6764aa0a90330f6a9edc99e8a92d9 --- /dev/null +++ b/OpenAuth.App/CategoryTypeApp.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class CategoryTypeApp : BaseApp + { + private RevelanceManagerApp _revelanceApp; + + /// + /// 加载列表 + /// + public TableData Load(QueryCategoryTypeListReq request) + { + var result = new TableData(); + var objs = UnitWork.Find(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key) || u.Name.Contains(request.key)); + } + + result.data = objs.OrderBy(u => u.Name) + .Skip((request.page - 1) * request.limit) + .Take(request.limit); + result.count = objs.Count(); + return result; + } + + public void Add(AddOrUpdateCategoryTypeReq req) + { + var obj = req.MapTo(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + Repository.Add(obj); + } + + public void Update(AddOrUpdateCategoryTypeReq obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new CategoryType + { + Name = obj.Name, + CreateTime = DateTime.Now + //todo:补充或调整自己需要的字段 + }); + + } + + public new void Delete(string[] ids) + { + UnitWork.Delete(u=>ids.Contains(u.Id)); + UnitWork.Delete(u=>ids.Contains(u.TypeId)); + UnitWork.Save(); + } + + public List AllTypes() + { + return UnitWork.Find(null).ToList(); + } + + public CategoryTypeApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app, IAuth auth) : base(unitWork, repository,auth) + { + _revelanceApp = app; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/DataPrivilegeRuleApp.cs b/OpenAuth.App/DataPrivilegeRuleApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..c762167f5d2148dad1cb8fe865741e8eb20f5349 --- /dev/null +++ b/OpenAuth.App/DataPrivilegeRuleApp.cs @@ -0,0 +1,96 @@ +using System; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class DataPrivilegeRuleApp : BaseApp + { + private RevelanceManagerApp _revelanceApp; + /// + /// 加载列表 + /// + public TableData Load(QueryDataPrivilegeRuleListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + var properties = loginContext.GetProperties("DataPrivilegeRule"); + + if (properties == null || properties.Count == 0) + { + throw new Exception("当前登录用户没有访问该模块字段的权限,请联系管理员配置"); + } + + + var result = new TableData(); + var objs = UnitWork.Find(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key) || u.SourceCode.Contains(request.key) || u.Description.Contains(request.key)); + } + + + var propertyStr = string.Join(',', properties.Select(u => u.Key)); + result.columnHeaders = properties; + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).Select($"new ({propertyStr})"); + result.count = objs.Count(); + return result; + } + + public void Add(AddOrUpdateDataPriviReq req) + { + if (Repository.IsExist(u => u.SourceCode == req.SourceCode)) + { + throw new Exception($"已经存在{req.SourceCode}的数据规则,如果想调整规制请直接修改"); + } + var obj = req.MapTo(); + obj.CreateUserId = _auth.GetCurrentUser().User.Id; + obj.CreateTime = DateTime.Now; + Repository.Add(obj); + } + + public void Update(AddOrUpdateDataPriviReq obj) + { + UnitWork.Update(u => u.Id == obj.Id, u => new DataPrivilegeRule + { + SortNo = obj.SortNo, + SourceCode = obj.SourceCode, + UpdateTime = DateTime.Now, + UpdateUserId = _auth.GetCurrentUser().User.Id, + Description = obj.Description, + PrivilegeRules = obj.PrivilegeRules, + Enable = obj.Enable + //todo:要修改的字段赋值 + }); + + } + + public DataPrivilegeRuleApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app, IAuth auth) : base(unitWork, repository, auth) + { + _revelanceApp = app; + } + + public DataPrivilegeRule GetByModuleName(string moduleName) + { + return Repository.FindSingle(u=>u.SourceCode == moduleName); + } + + public void Clear() + { + Repository.Delete(u =>true); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/DbExtension.cs b/OpenAuth.App/DbExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..e77289e47cad2d8bb2e3d648eed572a83e44f762 --- /dev/null +++ b/OpenAuth.App/DbExtension.cs @@ -0,0 +1,283 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Linq; +using Infrastructure; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.Extensions.Options; +using OpenAuth.Repository; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.QueryObj; + +namespace OpenAuth.App +{ + public class DbExtension + { + private OpenAuthDBContext _context; + + private IOptions _appConfiguration; + + public DbExtension(OpenAuthDBContext context, IOptions appConfiguration) + { + _context = context; + _appConfiguration = appConfiguration; + } + + /// + /// 获取数据库一个表的所有属性值及属性描述 + /// + /// 模块名称/表名 + /// + public List GetProperties(string moduleName) + { + var result = new List(); + const string domain = "openauth.repository.domain."; + var entity = _context.Model.GetEntityTypes() + .FirstOrDefault(u => u.Name.ToLower()==domain + moduleName.ToLower()); + if (entity == null) + { + throw new Exception($"未能找到{moduleName}对应的实体类"); + } + + foreach (var property in entity.ClrType.GetProperties()) + { + object[] objs = property.GetCustomAttributes(typeof(DescriptionAttribute), true); + object[] browsableObjs = property.GetCustomAttributes(typeof(BrowsableAttribute), true); + var description = objs.Length > 0 ? ((DescriptionAttribute) objs[0]).Description : property.Name; + if (string.IsNullOrEmpty(description)) description = property.Name; + //如果没有BrowsableAttribute或 [Browsable(true)]表示可见,其他均为不可见,需要前端配合显示 + bool browsable = browsableObjs == null || browsableObjs.Length == 0 || + ((BrowsableAttribute) browsableObjs[0]).Browsable; + var typeName = property.PropertyType.Name; + if (Nullable.GetUnderlyingType(property.PropertyType) != null) + { + typeName = Nullable.GetUnderlyingType(property.PropertyType).Name; + } + result.Add(new KeyDescription + { + Key = property.Name, + Description = description, + Browsable = browsable, + Type = typeName + }); + } + + return result; + } + + /// + /// 获取数据库DbContext中所有的实体名称。 + /// 注意!并不能获取数据库中的所有表 + /// + public List GetDbEntityNames() + { + var names = new List(); + var model = _context.Model; + + // Get all the entity types information contained in the DbContext class, ... + var entityTypes = model.GetEntityTypes(); + foreach (var entityType in entityTypes) + { + var tableNameAnnotation = entityType.GetAnnotation("Relational:TableName"); + names.Add(tableNameAnnotation.Value.ToString()); + } + + return names; + } + + /// + /// 获取数据库表结构信息 + /// + /// + /// + public IList GetDbTableStructure(string tableName) + { + if (_appConfiguration.Value.DbType == Define.DBTYPE_MYSQL) + { + return GetMySqlStructure(tableName); + } + else + { + return GetSqlServerStructure(tableName); + } + } + + /// + /// 获取Mysql表结构信息 + /// + /// + /// + private IList GetMySqlStructure(string tableName) + { + var sql = $@"SELECT DISTINCT + Column_Name AS ColumnName, + '{ tableName}' as tableName, + Column_Comment AS Comment, + data_type as ColumnType, + CASE + WHEN data_type IN( 'BIT', 'BOOL', 'bit', 'bool') THEN + 'bool' + WHEN data_type in('smallint','SMALLINT') THEN 'short' + WHEN data_type in('tinyint','TINYINT') THEN 'bool' + WHEN data_type IN('MEDIUMINT','mediumint', 'int','INT','year', 'Year') THEN + 'int' + WHEN data_type in ( 'BIGINT','bigint') THEN + 'bigint' + WHEN data_type IN('FLOAT', 'DOUBLE', 'DECIMAL','float', 'double', 'decimal') THEN + 'decimal' + WHEN data_type IN('CHAR', 'VARCHAR', 'TINY TEXT', 'TEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TINYBLOB', 'BLOB', 'MEDIUMBLOB', 'LONGBLOB', 'Time','char', 'varchar', 'tiny text', 'text', 'mediumtext', 'longtext', 'tinyblob', 'blob', 'mediumblob', 'longblob', 'time') THEN + 'string' + WHEN data_type IN('Date', 'DateTime', 'TimeStamp','date', 'datetime', 'timestamp') THEN + 'DateTime' ELSE 'string' + END AS EntityType, + case WHEN CHARACTER_MAXIMUM_LENGTH>8000 THEN 0 ELSE CHARACTER_MAXIMUM_LENGTH end AS Maxlength, + CASE + WHEN COLUMN_KEY <> '' THEN + 1 ELSE 0 + END AS IsKey, + CASE + WHEN Column_Name IN( 'CreateID', 'ModifyID', '' ) + OR COLUMN_KEY<> '' THEN + 0 ELSE 1 + END AS IsDisplay, + 1 AS IsColumnData, + 120 AS ColumnWidth, + 0 AS OrderNo, + CASE + WHEN IS_NULLABLE = 'NO' THEN + 0 ELSE 1 + END AS IsNull, + CASE + WHEN COLUMN_KEY <> '' THEN + 1 ELSE 0 + END AS IsReadDataset + FROM + information_schema.COLUMNS + WHERE + table_name = '{tableName}' {GetMysqlTableSchema()}"; + + var columns = _context.Query().FromSqlRaw(sql); + return columns.ToList(); + } + + /// + /// 获取mysql当前的数据库名称 + /// + /// + private string GetMysqlTableSchema() + { + try + { + string dbName = _context.Database.GetDbConnection().ConnectionString.Split("Database=")[1].Split(";")[0]?.Trim(); + if (!string.IsNullOrEmpty(dbName)) + { + dbName = $" and table_schema = '{dbName}' "; + } + return dbName; + } + catch (Exception ex) + { + Console.WriteLine($"获取mysql数据库名异常:{ex.Message}"); + return ""; + } + } + + + /// + /// 获取SqlServer表结构信息 + /// + /// + /// + private IList GetSqlServerStructure(string tableName) + { + var sql = $@" + SELECT TableName, + LTRIM(RTRIM(ColumnName)) AS ColumnName, + Comment, + CASE WHEN ColumnType = 'uniqueidentifier' THEN 'guid' + WHEN ColumnType IN('smallint', 'INT') THEN 'int' + WHEN ColumnType = 'BIGINT' THEN 'long' + WHEN ColumnType IN('CHAR', 'VARCHAR', 'NVARCHAR', + 'text', 'xml', 'varbinary', 'image') + THEN 'string' + WHEN ColumnType IN('tinyint') + THEN 'byte' + WHEN ColumnType IN('bit') + THEN 'bool' + + WHEN ColumnType IN('bit') THEN 'bool' + WHEN ColumnType IN('time', 'date', 'DATETIME', 'smallDATETIME') + THEN 'DateTime' + WHEN ColumnType IN('smallmoney', 'DECIMAL', 'numeric', + 'money') THEN 'decimal' + WHEN ColumnType = 'float' THEN 'float' + ELSE 'string' + END as EntityType, + ColumnType, + [Maxlength], + IsKey, + CASE WHEN ColumnName IN('CreateID', 'ModifyID', '') + OR IsKey = 1 THEN 0 + ELSE 1 + END AS IsDisplay , + 1 AS IsColumnData, + + CASE + WHEN ColumnName IN('Modifier', 'Creator') THEN 130 + WHEN[Maxlength] < 110 AND[Maxlength] > 60 THEN 120 + WHEN[Maxlength] < 200 AND[Maxlength] >= 110 THEN 180 + WHEN[Maxlength] > 200 THEN 220 + ELSE 90 + END AS ColumnWidth , + 0 AS OrderNo, + --CASE WHEN IsKey = 1 OR t.[IsNull]=0 THEN 0 + -- ELSE 1 END + t.[IsNull] AS + [IsNull], + CASE WHEN IsKey = 1 THEN 1 ELSE 0 END IsReadDataset, + CASE WHEN IsKey!=1 AND t.[IsNull] = 0 THEN 0 ELSE NULL END AS EditColNo + FROM (SELECT obj.name AS TableName , + col.name AS ColumnName , + CONVERT(NVARCHAR(100),ISNULL(ep.[value], '')) AS Comment, + t.name AS ColumnType , + CASE WHEN col.length<1 THEN 0 ELSE col.length END AS[Maxlength], + CASE WHEN EXISTS (SELECT 1 + FROM dbo.sysindexes si + INNER JOIN dbo.sysindexkeys sik ON si.id = sik.id + AND si.indid = sik.indid + INNER JOIN dbo.syscolumns sc ON sc.id = sik.id + AND sc.colid = sik.colid + INNER JOIN dbo.sysobjects so ON so.name = si.name + AND so.xtype = 'PK' + WHERE sc.id = col.id + AND sc.colid = col.colid) + THEN 1 + ELSE 0 + END AS IsKey , + CASE WHEN col.isnullable = 1 THEN 1 + ELSE 0 + END AS[IsNull], + col.colorder + FROM dbo.syscolumns col + LEFT JOIN dbo.systypes t ON col.xtype = t.xusertype + INNER JOIN dbo.sysobjects obj ON col.id = obj.id + + AND obj.xtype IN ( 'U','V') + -- AND obj.status >= 01 + LEFT JOIN dbo.syscomments comm ON col.cdefault = comm.id + LEFT JOIN sys.extended_properties ep ON col.id = ep.major_id + AND col.colid = ep.minor_id + AND ep.name = 'MS_Description' + LEFT JOIN sys.extended_properties epTwo ON obj.id = epTwo.major_id + AND epTwo.minor_id = 0 + AND epTwo.name = 'MS_Description' + WHERE obj.name = '{ tableName}') AS t + ORDER BY t.colorder"; + + var columns = _context.Query().FromSqlRaw(sql); + return columns.ToList(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Define.cs b/OpenAuth.App/Define.cs index 6f191c69bab775e79ae6865ba54212232f1c1d4d..5ecd4d96495faa1ffaf4b8f4409265b7a5181023 100644 --- a/OpenAuth.App/Define.cs +++ b/OpenAuth.App/Define.cs @@ -1,15 +1,32 @@ -namespace OpenAuth.App +using System; + +namespace OpenAuth.App { public static class Define { public static string USERROLE = "UserRole"; //用户角色关联KEY public const string ROLERESOURCE= "RoleResource"; //角色资源关联KEY - public const string USERRESOURCE = "UserResource"; //用户资源关联KEY public const string USERORG = "UserOrg"; //用户机构关联KEY - public const string ROLEORG = "RoleOrg"; //角色机构关联KEY - public const string USERELEMENT = "UserElement"; //用户菜单关联KEY public const string ROLEELEMENT = "RoleElement"; //角色菜单关联KEY - public const string USERMODULE = "UserModule"; //用户模块关联KEY public const string ROLEMODULE = "RoleModule"; //角色模块关联KEY + public const string ROLEDATAPROPERTY = "RoleDataProperty"; //角色数据字段权限 + + public const string DBTYPE_SQLSERVER = "SqlServer"; //sql server + public const string DBTYPE_MYSQL = "MySql"; //sql server + + + public const int INVALID_TOKEN = 50014; //token无效 + + public const string TOKEN_NAME = "X-Token"; + + + public const string SYSTEM_USERNAME = "System"; + public const string SYSTEM_USERPWD = "123456"; + + public const string DATAPRIVILEGE_LOGINUSER = "{loginUser}"; //数据权限配置中,当前登录用户的key + public const string DATAPRIVILEGE_LOGINROLE = "{loginRole}"; //数据权限配置中,当前登录用户角色的key + public const string DATAPRIVILEGE_LOGINORG = "{loginOrg}"; //数据权限配置中,当前登录用户部门的key + + public const string JOBMAPKEY = "OpenJob"; } } diff --git a/OpenAuth.App/FileApp.cs b/OpenAuth.App/FileApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..df27ea31a2039154831a5cd1b84c8a5ea5bfa76a --- /dev/null +++ b/OpenAuth.App/FileApp.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Infrastructure; +using Infrastructure.Helpers; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenAuth.App.Interface; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + +namespace OpenAuth.App +{ + /// + /// 文件 + /// + public class FileApp : BaseApp + { + private ILogger _logger; + private string _filePath; + private string _dbFilePath; //数据库中的文件路径 + private string _dbThumbnail; //数据库中的缩略图路径 + + public FileApp( IOptions setOptions, IUnitWork unitWork, IRepository repository, ILogger logger, IAuth auth) + :base(unitWork, repository, auth) + { + _logger = logger; + _filePath = setOptions.Value.UploadPath; + if (string.IsNullOrEmpty(_filePath)) + { + _filePath = AppContext.BaseDirectory; + } + } + + public List Add(IFormFileCollection files) + { + var result = new List(); + foreach (var file in files) + { + result.Add(Add(file)); + } + + return result; + } + + public UploadFile Add(IFormFile file) + { + if (file != null) + { + _logger.LogInformation("收到新文件: " + file.FileName); + _logger.LogInformation("收到新文件: " + file.Length); + } + else + { + _logger.LogWarning("收到新文件为空"); + } + if (file != null && file.Length > 0 && file.Length < 10485760) + { + using (var binaryReader = new BinaryReader(file.OpenReadStream())) + { + var fileName = Path.GetFileName(file.FileName); + var data = binaryReader.ReadBytes((int)file.Length); + UploadFile(fileName, data); + + var filedb = new UploadFile + { + FilePath = _dbFilePath, + Thumbnail = _dbThumbnail, + FileName = fileName, + FileSize = file.Length, + FileType = Path.GetExtension(fileName), + Extension = Path.GetExtension(fileName) + }; + Repository.Add(filedb); + return filedb; + } + } + else + { + throw new Exception("文件过大"); + } + } + + private void UploadFile(string fileName, byte[] fileBuffers) + { + string folder = DateTime.Now.ToString("yyyyMMdd"); + + //判断文件是否为空 + if (string.IsNullOrEmpty(fileName)) + { + throw new Exception("文件名不能为空"); + } + + //判断文件是否为空 + if (fileBuffers.Length < 1) + { + throw new Exception("文件不能为空"); + } + + var uploadPath = Path.Combine(_filePath , folder ); + _logger.LogInformation("文件写入:" + uploadPath); + if (!Directory.Exists(uploadPath)) + { + Directory.CreateDirectory(uploadPath); + } + + var ext = Path.GetExtension(fileName).ToLower(); + string newName = GenerateId.GenerateOrderNumber() + ext; + + using (var fs = new FileStream(Path.Combine(uploadPath , newName), FileMode.Create)) + { + fs.Write(fileBuffers, 0, fileBuffers.Length); + fs.Close(); + + //生成缩略图 + if (ext.Contains(".jpg") || ext.Contains(".jpeg") || ext.Contains(".png") || ext.Contains(".bmp") || ext.Contains(".gif")) + { + string thumbnailName = GenerateId.GenerateOrderNumber() + ext; + ImgHelper.MakeThumbnail(Path.Combine(uploadPath , newName), Path.Combine(uploadPath , thumbnailName)); + _dbThumbnail = Path.Combine(folder , thumbnailName); + } + + + _dbFilePath = Path.Combine(folder , newName); + } + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Flow/FlowLine.cs b/OpenAuth.App/Flow/FlowLine.cs index 129cb2be0b014615bc23826119db4aa738fb4daa..51633043ef17059caebacf2ebe36dc0140ef497c 100644 --- a/OpenAuth.App/Flow/FlowLine.cs +++ b/OpenAuth.App/Flow/FlowLine.cs @@ -1,4 +1,14 @@ -namespace OpenAuth.App.Flow +// +// Copyright (c) 2019 openauth.me. All rights reserved. +// +// www.cnblogs.com/yubaolee +// 2019-03-05 +// 流程中的连线 + +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +namespace OpenAuth.App.Flow { /// /// 流程连线 @@ -6,10 +16,70 @@ public class FlowLine { public string id { get; set; } + public string label { get; set; } public string type { get; set; } public string from { get; set; } public string to { get; set; } public string name { get; set; } public bool dash { get; set; } + + /// 分支条件 + public List Compares { get; set; } + + public bool Compare(JObject frmDataJson) + { + bool result = true; + foreach (var compare in Compares) + { + decimal value = decimal.Parse(compare.Value); //参考值 + decimal frmvalue = decimal.Parse(frmDataJson.GetValue(compare.FieldName.ToLower()).ToString()); //表单中填写的值 + + switch (compare.Operation) + { + case DataCompare.Equal: + result &= compare.Value == frmDataJson.GetValue(compare.FieldName).ToString(); + break; + case DataCompare.Larger: + result &= frmvalue > value; + break; + case DataCompare.Less: + result &= frmvalue < value; + break; + case DataCompare.LargerEqual: + result &= frmvalue <= value; + break; + case DataCompare.LessEqual: + result &= frmvalue <= value; + break; + } + } + + return result; + } + } + + /// + /// 分支条件 + /// + public class DataCompare + { + public const string Larger = ">"; + public const string Less = "<"; + public const string LargerEqual = ">="; + public const string LessEqual = "<="; + public const string NotEqual = "!="; + public const string Equal = "="; + + /// 操作类型比如大于/等于/小于 + public string Operation { get; set; } + + /// form种的字段名称 + public string FieldName { get; set; } + + /// 字段类型:"form":为表单中的字段,后期扩展系统表等. + public string FieldType { get; set; } + + /// 比较的值 + public string Value { get; set; } } } diff --git a/OpenAuth.App/Flow/FlowNode.cs b/OpenAuth.App/Flow/FlowNode.cs index 10f65e5ac495aee32a2d7c07bddd154877656163..703b871eaa464a615fcb22b937ad9f9dd19c8d80 100644 --- a/OpenAuth.App/Flow/FlowNode.cs +++ b/OpenAuth.App/Flow/FlowNode.cs @@ -17,6 +17,12 @@ public string type { get; set; } + public int left { get; set; } + public int top { get; set; } + + public int width { get; set; } + public int height { get; set; } + public bool alt { get; set; } /// /// 节点的附加数据项 @@ -29,19 +35,46 @@ { public const string SPECIAL_USER = "SPECIAL_USER"; //指定用户 public const string ALL_USER = "ALL_USER"; //所有用户 - public const string SPECIAL_ROLE = "SPECIAL_ROLE"; + public const string SPECIAL_ROLE = "SPECIAL_ROLE"; //指定角色 + /// /// 节点执行权限类型 /// public string NodeDesignate { get; set; } + public Nodedesignatedata NodeDesignateData { get; set; } public string NodeCode { get; set; } public string NodeName { get; set; } + /// + /// 流程执行时,三方回调的URL地址 + /// + public string ThirdPartyUrl { get; set; } /// /// 驳回节点0"前一步"1"第一步"2"某一步" 3"不处理" /// public string NodeRejectType { get; set; } + + public int? Taged { get; set; } + public string UserName { get; set; } + public string UserId { get; set; } + public string Description { get; set; } + public string TagedTime { get; set; } + + //节点会签方式, + //all/空:默认为全部通过 + //one :至少有一个通过 + public string NodeConfluenceType { get; set; } + + /// + /// 会签通过的个数 + /// + public int? ConfluenceOk { get; set; } + + /// + /// 会签拒绝的个数 + /// + public int? ConfluenceNo { get; set; } } /// @@ -54,12 +87,16 @@ public string[] orgs { get; set; } } - /// /// 节点执行结果标签 /// public class Tag { + /// + /// 1: 通过 + /// 2:不通过 + /// 3:驳回 + /// public int Taged { get; set; } public string UserId { get; set; } public string UserName { get; set; } @@ -67,4 +104,15 @@ public string TagedTime { get; set; } } -} + /// + /// 1: 通过 + /// 2:不通过 + /// 3:驳回 + /// + public enum TagState + { + Ok = 1, + No , + Reject + } +} \ No newline at end of file diff --git a/OpenAuth.App/Flow/FlowRuntime.cs b/OpenAuth.App/Flow/FlowRuntime.cs index 754ccc0fdbcb155ab41b8f9ae363eb9b2980a4f2..ce73ad8e54d4bf41c257ee1b05359adf4189af87 100644 --- a/OpenAuth.App/Flow/FlowRuntime.cs +++ b/OpenAuth.App/Flow/FlowRuntime.cs @@ -1,189 +1,170 @@ -using System; -using System.Collections.Generic; -using System.Linq; +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : 李玉宝 +// Created : 07-19-2018 +// +// Last Modified By : 李玉宝 +// Last Modified On : 07-19-2018 +// *********************************************************************** +// +// Copyright (c) http://www.openauth.me. All rights reserved. +// +// +// 一个正在运行中的流程实例 +// +// *********************************************************************** + using Infrastructure; using Newtonsoft.Json.Linq; using OpenAuth.Repository.Domain; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using Castle.Core.Internal; namespace OpenAuth.App.Flow { public class FlowRuntime { - private FlowRuntimeModel _runtimeModel = null; - /// /// 构造函数 /// - /// 当前节点 - /// 表单数据 - /// public FlowRuntime(FlowInstance instance) { - _runtimeModel = new FlowRuntimeModel(); dynamic schemeContentJson = instance.SchemeContent.ToJson();//获取工作流模板内容的json对象; - _runtimeModel.frmData = instance.FrmData; - _runtimeModel.schemeContentJson = schemeContentJson;//模板流程json对象 - _runtimeModel.nodes = GetNodes(schemeContentJson);//节点集合 - _runtimeModel.lines = GetFromLines(schemeContentJson);//线条集合 - _runtimeModel.currentNodeId = (instance.ActivityId == "" ? _runtimeModel.startNodeId : instance.ActivityId); - _runtimeModel.currentNodeType = GetNodeType(_runtimeModel.currentNodeId); + + InitLines(schemeContentJson); + InitNodes(schemeContentJson); + + currentNodeId = (instance.ActivityId == "" ? startNodeId : instance.ActivityId); + currentNodeType = GetNodeType(currentNodeId); + FrmData = instance.FrmData; + title = schemeContentJson.title; + initNum = schemeContentJson.initNum?? 0; + previousId = instance.PreviousId; + flowInstanceId = instance.Id; //会签开始节点和流程结束节点没有下一步 - if (_runtimeModel.currentNodeType == 0 || _runtimeModel.currentNodeType == 4) + if (currentNodeType == 0 || currentNodeType == 4) { - _runtimeModel.nextNodeId = "-1"; - _runtimeModel.nextNodeType = -1; + nextNodeId = "-1"; + nextNodeType = -1; } else { - _runtimeModel.nextNodeId = GetNextNodeId(_runtimeModel.frmData);//下一个节点 - _runtimeModel.nextNodeType = GetNodeType(_runtimeModel.nextNodeId); + nextNodeId = GetNextNodeId();//下一个节点 + nextNodeType = GetNodeType(nextNodeId); } - - _runtimeModel.previousId = instance.PreviousId; - _runtimeModel.flowInstanceId = instance.Id; - } #region 私有方法 + /// /// 获取工作流节点的字典列表:key节点id /// /// /// - private Dictionary GetNodes(dynamic schemeContentJson) + private void InitNodes(dynamic schemeContentJson) { - Dictionary nodes = new Dictionary(); + Nodes = new Dictionary(); foreach (JObject item in schemeContentJson.nodes) { var node = item.ToObject(); - if (!nodes.ContainsKey(node.id)) + if (!Nodes.ContainsKey(node.id)) { - nodes.Add(node.id, node); + Nodes.Add(node.id, node); } if (node.type == FlowNode.START) { - this._runtimeModel.startNodeId = node.id; + this.startNodeId = node.id; } } - return nodes; } - /// - /// 获取工作流节点及以节点为出发点的流程 - /// - /// - /// - private Dictionary> GetFromLines(dynamic schemeContentJson) + + private void InitLines(dynamic schemeContentJson) { - Dictionary> lines = new Dictionary>(); + Lines = new List(); + FromNodeLines = new Dictionary>(); + ToNodeLines = new Dictionary>(); foreach (JObject item in schemeContentJson.lines) { var line = item.ToObject(); - if (!lines.ContainsKey(line.from)) + Lines.Add(line); + + if (!FromNodeLines.ContainsKey(line.from)) { List d = new List { line }; - lines.Add(line.from, d); + FromNodeLines.Add(line.from, d); } else { - lines[line.from].Add(line); + FromNodeLines[line.from].Add(line); } - } - return lines; - } - /// - /// 获取工作流节点的入口流程列表 - /// - /// - /// - private Dictionary> GetToLines(dynamic schemeContentJson) - { - Dictionary> lines = new Dictionary>(); - foreach (JObject item in schemeContentJson.lines) - { - var line = item.ToObject(); - if (!lines.ContainsKey(line.to)) + + if (!ToNodeLines.ContainsKey(line.to)) { List d = new List { line }; - lines.Add(line.to, d); + ToNodeLines.Add(line.to, d); } else { - lines[line.to].Add(line); + ToNodeLines[line.to].Add(line); } } - return lines; } /// /// 获取下一个节点 /// - private string GetNextNodeId(string frmData, string nodeId=null) + private string GetNextNodeId(string nodeId = null) { - List LineList = null; - if (nodeId == null) + var lines = nodeId == null ? FromNodeLines[currentNodeId] : FromNodeLines[nodeId]; + if (lines.Count == 0) { - LineList = runtimeModel.lines[runtimeModel.currentNodeId]; - } - else - { - LineList = runtimeModel.lines[nodeId]; - } - if (LineList.Count == 1) //只有一条流程 - { - return LineList[0].to; + throw new Exception("无法寻找到下一个节点"); } - if (frmData != "") //有分支的情况 + if (FrmData == "") return lines[0].to; + + FrmData = FrmData.ToLower();//统一转小写 + var frmDataJson = FrmData.ToJObject();//获取数据内容 + + foreach (var l in lines) { - frmData = frmData.ToLower();//统一转小写 - var frmDataJson = frmData.ToJObject();//获取数据内容 - bool flag = false; - foreach (var item in LineList)//轮训该节点所有连接的线路 + if (!(l.Compares.IsNullOrEmpty()) &&l.Compare(frmDataJson)) { - return item.to; + return l.to; } } - return "-1";//表示寻找不到节点 - } - /// - /// 通过节点Id获取下一个节点Id - /// - /// - /// - public string GetNextNode(string nodeId) - { - string frmData = ""; + return lines[0].to; + } - // frmData = GetNodeFrmData(_getFrmData, nodeId); + #endregion 私有方法 - return GetNextNodeId(frmData, nodeId); - } - #endregion + #region 共有方法 - #region 工作流实例流转API - /// - /// 工作流实例运行信息 - /// - /// - public FlowRuntimeModel runtimeModel + //获取下一个节点 + public FlowNode GetNextNode(string nodeId = null) { - get { return _runtimeModel; } + return Nodes[GetNextNodeId(nodeId)]; } + /// /// 获取实例接下来运行的状态 /// /// -1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束 public int GetNextNodeType() { - if (_runtimeModel.nextNodeId != "-1") + if (nextNodeId != "-1") { - return GetNodeType(_runtimeModel.nextNodeId); - + return GetNodeType(nextNodeId); } return -1; } + /// /// 获取节点类型 0会签开始,1会签结束,2一般节点,开始节点,4流程运行结束 /// @@ -191,193 +172,304 @@ namespace OpenAuth.App.Flow /// public int GetNodeType(string nodeId) { - if (_runtimeModel.nodes[nodeId].type == FlowNode.FORK)//会签开始节点 - { - return 0; - } - else if (_runtimeModel.nodes[nodeId].type == FlowNode.JOIN)//会签结束节点 - { - return 1; - } - else if (_runtimeModel.nodes[nodeId].type == FlowNode.END)//结束节点 - { - return 4; - } - else if (_runtimeModel.nodes[nodeId].type == FlowNode.START)//开始节点 - { - return 3; - } - else + switch (Nodes[nodeId].type) { - return 2; + //会签开始节点 + case FlowNode.FORK: + return 0; + //会签结束节点 + case FlowNode.JOIN: + return 1; + //结束节点 + case FlowNode.END: + return 4; + //开始节点 + case FlowNode.START: + return 3; + + default: + return 2; } } + /// - /// 获取会签下面需要审核的ID列表 - /// - /// 会签开始节点 - /// - public List GetCountersigningNodeIdList(string forknodeId) - { - return _runtimeModel.lines[forknodeId].Select(item => item.to).ToList(); - } - - /// - /// 节点会签审核 + /// 节点会签审核 /// - /// + /// 会签时,currentNodeId是会签开始节点。这个表示当前正在处理的节点 /// /// -1不通过,1等待,其它通过 public string NodeConfluence(string nodeId, Tag tag) { - string res = "-1"; - string joinNodeId = GetNextNode(nodeId); //获取回签的合流节点 + var forkNode = Nodes[currentNodeId]; //会签开始节点 + FlowNode nextNode = GetNextNode(nodeId); //获取当前处理的下一个节点 - if (joinNodeId == "-1") + int forkNumber = FromNodeLines[currentNodeId].Count; //直接与会签节点连接的点,即会签分支数目 + string res =string.Empty; //记录会签的结果,默认正在会签 + if (forkNode.setInfo.NodeConfluenceType == "one") //有一个步骤通过即可 { - throw (new Exception("寻找不到会签下合流节点")); - } + if (tag.Taged == (int) TagState.Ok) + { + if (nextNode.type == FlowNode.JOIN) //下一个节点是会签结束,则该线路结束 + { + res = GetNextNodeId(nextNode.id); + } + } + else if(tag.Taged ==(int) TagState.No) + { + if (forkNode.setInfo.ConfluenceNo == null) + { + forkNode.setInfo.ConfluenceNo = 1; + } + else if (forkNode.setInfo.ConfluenceNo == (forkNumber - 1)) + { + res = TagState.No.ToString("D"); + } + else + { + bool isFirst = true; //是不是从会签开始到现在第一个 + var preNode = GetPreNode(nodeId); + while (preNode.id != forkNode.id) //反向一直到会签开始节点 + { + if (preNode.setInfo != null && preNode.setInfo.Taged == (int) TagState.No) + { + isFirst = false; + break; + } + } - Dictionary> toLines = GetToLines(_runtimeModel.schemeContentJson); - int allnum = toLines[joinNodeId].Count; //总会签数量 - - int i = 0; - foreach (var item in _runtimeModel.schemeContentJson.nodes) + if (isFirst) + { + forkNode.setInfo.ConfluenceNo++; + } + } + } + } + else //默认所有步骤通过 { - if (item.id != joinNodeId) + if (tag.Taged == (int) TagState.No) //只要有一个不同意,那么流程就结束 { - i++; - continue; + res = TagState.No.ToString("D"); } - - if (string.IsNullOrEmpty(item.setInfo.NodeConfluenceType)) //默认所有步骤通过 + else if(tag.Taged == (int)TagState.Ok) { - if (tag.Taged == 1) + if (nextNode.type == FlowNode.JOIN) //这种模式下只有坚持到【会签结束】节点之前才有意义,是否需要判定这条线所有的节点都通过,不然直接执行这个节点?? { - if (item.setInfo.ConfluenceOk == null) + if (forkNode.setInfo.ConfluenceOk == null) { - _runtimeModel.schemeContentJson.nodes[i].setInfo.ConfluenceOk = 1; - res = "1"; + forkNode.setInfo.ConfluenceOk = 1; } - else if (item.setInfo.ConfluenceOk == (allnum - 1)) //会签成功 + else if (forkNode.setInfo.ConfluenceOk == (forkNumber - 1)) //会签成功 { - res = GetNextNode(joinNodeId); - if (res == "-1") - { - throw (new Exception("会签成功寻找不到下一个节点")); - } + res = GetNextNodeId(nextNode.id); } else { - _runtimeModel.schemeContentJson.nodes[i].setInfo.ConfluenceOk++; - res = "1"; + forkNode.setInfo.ConfluenceOk++; } } } - //else if (item.setInfo.NodeConfluenceType == "1") //1一个步骤通过即可 - //{ - // if (tag.Taged == 1) - // { - // res = GetNextNodeId(nextNodeId); - // if (res == "-1") - // { - // throw (new Exception("会签成功寻找不到下一个节点")); - // } - // } - // else - // { - // if (item.setInfo.ConfluenceNo == null) - // { - // _runtimeModel.schemeContentJson.nodes[i].setInfo.ConfluenceNo = 1; - // res = "1"; - // } - // else if (item.setInfo.ConfluenceNo == (allnum - 1)) - // { - // res = "-1"; - // } - // else - // { - // _runtimeModel.schemeContentJson.nodes[i].setInfo.ConfluenceNo++; - // res = "1"; - // } - // } - //} } - - - if (res == "-1") + if (res == TagState.No.ToString("D")) { - tag.Taged = -1; - MakeTagNode(joinNodeId, tag); + tag.Taged = (int) TagState.No; + MakeTagNode(nextNode.id, tag); } - else if (res != "1") //这时res是会签结束节点后面的一个节点 + else if (!string.IsNullOrEmpty(res)) //会签结束,标记合流节点 { - tag.Taged = 1; - MakeTagNode(joinNodeId, tag); - _runtimeModel.nextNodeId = res; - _runtimeModel.nextNodeType = GetNodeType(res); + tag.Taged = (int) TagState.Ok; + MakeTagNode(nextNode.id, tag); + nextNodeId = res; + nextNodeType = GetNodeType(res); } else { - _runtimeModel.nextNodeId = joinNodeId; - _runtimeModel.nextNodeType = GetNodeType(joinNodeId); + nextNodeId = nextNode.id; + nextNodeType = GetNodeType(nextNode.id); } return res; } + + //获取上一个节点 + private FlowNode GetPreNode(string nodeId = null) + { + var lines = nodeId == null ? ToNodeLines[currentNodeId] : ToNodeLines[nodeId]; + if (lines.Count == 0) + { + throw new Exception("无法找到上一个点"); + } + return Nodes[lines[0].from]; + } /// - /// 驳回节点0"前一步"1"第一步"2"某一步" 3"不处理" + /// 驳回 /// + /// 驳回类型。null:使用节点配置的驳回类型/0:前一步/1:第一步/2:指定节点,使用NodeRejectStep /// - public string RejectNode() - { - return RejectNode(_runtimeModel.currentNodeId); - } - - public string RejectNode(string nodeId) + public string RejectNode(string rejectType) { - dynamic node = _runtimeModel.nodes[nodeId]; - if (node.setInfo != null) + dynamic node = Nodes[currentNodeId]; + if (node.setInfo != null && string.IsNullOrEmpty(rejectType)) { - if (node.setInfo.NodeRejectType == "0") - { - return _runtimeModel.previousId; - } - if (node.setInfo.NodeRejectType == "1") - { - return GetNextNode(_runtimeModel.startNodeId); - } - if (node.setInfo.NodeRejectType == "2") - { - return node.setInfo.NodeRejectStep; - } - return ""; + rejectType = node.setInfo.NodeRejectType; } - return _runtimeModel.previousId; + + if (rejectType == "0") + { + return previousId; + } + if (rejectType == "1") + { + return GetNextNodeId(startNodeId); + } + return previousId; } + /// /// 标记节点1通过,-1不通过,0驳回 /// /// public void MakeTagNode(string nodeId, Tag tag) { - int i = 0; - foreach (var item in _runtimeModel.schemeContentJson.nodes) + foreach (var item in Nodes) { - if (item.id == nodeId) + if (item.Key == nodeId) { - _runtimeModel.schemeContentJson.nodes[i].setInfo.Taged = tag.Taged; - _runtimeModel.schemeContentJson.nodes[i].setInfo.UserId = tag.UserId; - _runtimeModel.schemeContentJson.nodes[i].setInfo.UserName = tag.UserName; - _runtimeModel.schemeContentJson.nodes[i].setInfo.Description = tag.Description; - _runtimeModel.schemeContentJson.nodes[i].setInfo.TagedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm"); + if (item.Value.setInfo == null) + { + item.Value.setInfo = new Setinfo(); + } + item.Value.setInfo.Taged = tag.Taged; + item.Value.setInfo.UserId = tag.UserId; + item.Value.setInfo.UserName = tag.UserName; + item.Value.setInfo.Description = tag.Description; + item.Value.setInfo.TagedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm"); break; } - i++; } } - } - #endregion -} + public object ToSchemeObj() + { + return new + { + title = this.title, + initNum = this.initNum, + lines = Lines, + nodes = Nodes.Select(u => u.Value), + areas = new string[0] + }; + } + + /// + /// 通知三方系统,节点执行情况 + /// + public void NotifyThirdParty(HttpClient client, Tag tag) + { + if (currentNode.setInfo == null || string.IsNullOrEmpty(currentNode.setInfo.ThirdPartyUrl)) + { + return; + } + + var postData = new + { + flowInstanceId, + nodeName=currentNode.name, + nodeId = currentNodeId, + userId = tag.UserId, + userName = tag.UserName, + result=tag.Taged, //1:通过;2:不通过;3驳回 + description = tag.Description, + execTime = tag.TagedTime, + isFinish = currentNodeType == 4 + }; + + using (HttpContent httpContent = new StringContent(JsonHelper.Instance.Serialize(postData), Encoding.UTF8)) + { + httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + client.PostAsync(currentNode.setInfo.ThirdPartyUrl, httpContent); + } + } + + #endregion 共有方法 + + #region 属性 + + public string title { get; set; } + + public int initNum { get; set; } + + /// + /// 运行实例的Id + /// + public string flowInstanceId { get; set; } + + /// + /// 开始节点的ID + /// + public string startNodeId { get; set; } + + /// + /// 当前节点的ID + /// + public string currentNodeId { get; set; } + + /// + /// 当前节点类型 0会签开始,1会签结束,2一般节点,开始节点,4流程运行结束 + /// + public int currentNodeType { get; set; } + + /// + /// 当前节点的对象 + /// + public FlowNode currentNode => Nodes[currentNodeId]; + + /// + /// 下一个节点 + /// + public string nextNodeId { get; set; } + + /// + /// 下一个节点类型 -1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束 + /// + /// The type of the next node. + public int nextNodeType { get; set; } + + /// + /// 下一个节点对象 + /// + public FlowNode nextNode => Nodes[nextNodeId]; + + /// + /// 上一个节点 + /// + public string previousId { get; set; } + + /// + /// 实例节点集合 + /// + public Dictionary Nodes { get; set; } + + /// + /// 流程实例中所有的线段 + /// + public List Lines { get; set; } + + /// + /// 从节点发出的线段集合 + /// + public Dictionary> FromNodeLines { get; set; } + + /// + /// 到达节点的线段集合 + /// + public Dictionary> ToNodeLines { get; set; } + + /// + /// 表单数据 + /// + public string FrmData { get; set; } + + #endregion 属性 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Flow/FlowRuntimeModel.cs b/OpenAuth.App/Flow/FlowRuntimeModel.cs deleted file mode 100644 index ffb005c79149112979c93e9f818535078c2b9f0f..0000000000000000000000000000000000000000 --- a/OpenAuth.App/Flow/FlowRuntimeModel.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Generic; - -namespace OpenAuth.App.Flow -{ - public class FlowRuntimeModel - { - /// - /// 运行实例的Id - /// - public string flowInstanceId { get; set; } - /// - /// 开始节点的ID - /// - public string startNodeId { get; set; } - - /// - /// 当前节点的ID - /// - public string currentNodeId { get; set; } - /// - /// 当前节点类型 0会签开始,1会签结束,2一般节点,开始节点,4流程运行结束 - /// - public int currentNodeType { get; set; } - /// - /// 当前节点的对象 - /// - public FlowNode currentNode { get { return nodes[currentNodeId]; } } - /// - /// 下一个节点 - /// - public string nextNodeId { get; set; } - /// - /// 下一个节点类型 -1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束 - /// - /// The type of the next node. - public int nextNodeType { get; set; } - /// - /// 下一个节点对象 - /// - public FlowNode nextNode { get { return nodes[nextNodeId]; } } - - /// - /// 上一个节点 - /// - public string previousId { get; set; } - - /// - /// 实例节点集合 - /// - public Dictionary nodes { get; set; } - /// - /// 流转的线段集合 - /// - public Dictionary> lines { get; set; } - - /// - /// 模板json数据 - /// - public dynamic schemeContentJson { get; set; } - /// - /// 表单数据 - /// - public string frmData { get; set; } - - } - -} diff --git a/OpenAuth.App/FlowInstanceApp.cs b/OpenAuth.App/FlowInstanceApp.cs index 0e18a5f4d9ccf9f851d791749405d83662e2b68b..d010ff3f59b0f6f5b35fb62d460a779e4eec017c 100644 --- a/OpenAuth.App/FlowInstanceApp.cs +++ b/OpenAuth.App/FlowInstanceApp.cs @@ -1,13 +1,30 @@ -using System; -using System.Collections.Generic; -using System.Linq; +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : 李玉宝 +// Created : 07-19-2018 +// +// Last Modified By : 李玉宝 +// Last Modified On : 07-19-2018 +// *********************************************************************** +// +// Copyright (c) http://www.openauth.me. All rights reserved. +// +// +// *********************************************************************** + using Infrastructure; -using Newtonsoft.Json.Linq; using OpenAuth.App.Flow; +using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.Response; -using OpenAuth.App.SSO; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Net.Http; +using Infrastructure.Helpers; namespace OpenAuth.App { @@ -16,43 +33,80 @@ namespace OpenAuth.App /// public class FlowInstanceApp : BaseApp { - public RevelanceManagerApp RevelanceManagerApp { get; set; } + private RevelanceManagerApp _revelanceApp; + private FlowSchemeApp _flowSchemeApp; + private FormApp _formApp; + private IHttpClientFactory _httpClientFactory; + private IServiceProvider _serviceProvider; #region 流程处理API + /// /// 创建一个实例 /// /// - public bool CreateInstance(JObject obj) + public bool CreateInstance(AddFlowInstanceReq addFlowInstanceReq) { - var flowInstance = obj.ToObject(); + FlowScheme scheme = null; + if (!string.IsNullOrEmpty(addFlowInstanceReq.SchemeId)) + { + scheme = _flowSchemeApp.Get(addFlowInstanceReq.SchemeId); + } + + if ((scheme == null) && !string.IsNullOrEmpty(addFlowInstanceReq.SchemeCode)) + { + scheme = _flowSchemeApp.FindByCode(addFlowInstanceReq.SchemeCode); + } - //获取提交的表单数据 - var frmdata = new JObject(); - foreach (var property in obj.Properties().Where(U => U.Name.Contains("data_"))) + if (scheme == null) { - frmdata[property.Name] = property.Value; + throw new Exception("该流程模板已不存在,请重新设计流程"); } - flowInstance.FrmData = JsonHelper.Instance.Serialize(frmdata); + + addFlowInstanceReq.SchemeContent = scheme.SchemeContent; + + var form = _formApp.FindSingle(scheme.FrmId); + if (form == null) + { + throw new Exception("该流程模板对应的表单已不存在,请重新设计流程"); + } + + addFlowInstanceReq.FrmContentData = form.ContentData; + addFlowInstanceReq.FrmContentParse = form.ContentParse; + addFlowInstanceReq.FrmType = form.FrmType; + addFlowInstanceReq.FrmId = form.Id; + + var flowInstance = addFlowInstanceReq.MapTo(); //创建运行实例 var wfruntime = new FlowRuntime(flowInstance); - var user = AuthUtil.GetCurrentUser(); + var user = _auth.GetCurrentUser(); #region 根据运行实例改变当前节点状态 - flowInstance.ActivityId = wfruntime.runtimeModel.nextNodeId; - flowInstance.ActivityType = wfruntime.GetNextNodeType();//-1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束 - flowInstance.ActivityName = wfruntime.runtimeModel.nextNode.name; - flowInstance.PreviousId = wfruntime.runtimeModel.currentNodeId; + + flowInstance.ActivityId = wfruntime.nextNodeId; + flowInstance.ActivityType = wfruntime.GetNextNodeType(); + flowInstance.ActivityName = wfruntime.nextNode.name; + flowInstance.PreviousId = wfruntime.currentNodeId; flowInstance.CreateUserId = user.User.Id; flowInstance.CreateUserName = user.User.Account; - flowInstance.MakerList = (wfruntime.GetNextNodeType() != 4 ? GetMakerList(wfruntime) : "");//当前节点可执行的人信息 + flowInstance.MakerList = (wfruntime.GetNextNodeType() != 4 ? GetNextMakers(wfruntime) : ""); flowInstance.IsFinish = (wfruntime.GetNextNodeType() == 4 ? 1 : 0); UnitWork.Add(flowInstance); - #endregion + wfruntime.flowInstanceId = flowInstance.Id; + + if (flowInstance.FrmType == 1) + { + var t = Type.GetType("OpenAuth.App."+ flowInstance.DbName +"App"); + ICustomerForm icf = (ICustomerForm) _serviceProvider.GetService(t); + icf.Add(flowInstance.Id, flowInstance.FrmData); + } + + #endregion 根据运行实例改变当前节点状态 #region 流程操作记录 + FlowInstanceOperationHistory processOperationHistoryEntity = new FlowInstanceOperationHistory { InstanceId = flowInstance.Id, @@ -62,31 +116,14 @@ namespace OpenAuth.App Content = "【创建】" + user.User.Name + "创建了一个流程进程【" - + flowInstance.Code + "/" - + flowInstance.CustomName + "】" + + addFlowInstanceReq.Code + "/" + + addFlowInstanceReq.CustomName + "】" }; UnitWork.Add(processOperationHistoryEntity); - #endregion - - #region 流转记录 - FlowInstanceTransitionHistory processTransitionHistoryEntity = new FlowInstanceTransitionHistory - { - InstanceId = flowInstance.Id, - FromNodeId = wfruntime.runtimeModel.currentNodeId, - FromNodeName = wfruntime.runtimeModel.currentNode.name, - FromNodeType = wfruntime.runtimeModel.currentNodeType, - ToNodeId = wfruntime.runtimeModel.nextNodeId, - ToNodeName = wfruntime.runtimeModel.nextNode.name, - ToNodeType = wfruntime.runtimeModel.nextNodeType, - IsFinish = wfruntime.runtimeModel.nextNodeType == 4?1:0, - TransitionSate = 0, - CreateUserId = user.User.Id, - CreateUserName = user.User.Name - }; - #endregion + #endregion 流程操作记录 - UnitWork.Add(processTransitionHistoryEntity); + AddTransHistory(wfruntime); UnitWork.Save(); return true; } @@ -96,213 +133,165 @@ namespace OpenAuth.App /// /// /// - public bool NodeVerification(string instanceId, bool flag, string description = "") + public bool NodeVerification(string instanceId, Tag tag) { - var user = AuthUtil.GetCurrentUser().User; FlowInstance flowInstance = Get(instanceId); FlowInstanceOperationHistory flowInstanceOperationHistory = new FlowInstanceOperationHistory { InstanceId = instanceId, - CreateUserId = user.Id, - CreateUserName = user.Name, + CreateUserId = tag.UserId, + CreateUserName = tag.UserName, CreateDate = DateTime.Now };//操作记录 FlowRuntime wfruntime = new FlowRuntime(flowInstance); - var tag = new Tag - { - UserName = user.Name, - UserId = user.Id, - Description = description - }; #region 会签 + if (flowInstance.ActivityType == 0)//当前节点是会签节点 { - tag.Taged = 1; - wfruntime.MakeTagNode(wfruntime.runtimeModel.currentNodeId, tag);//标记会签节点状态 - - string verificationNodeId = ""; //寻找当前登陆用户可审核的节点Id - List nodelist = wfruntime.GetCountersigningNodeIdList(wfruntime.runtimeModel.currentNodeId); - foreach (string item in nodelist) - { - var makerList = GetMakerList(wfruntime.runtimeModel.nodes[item] - , wfruntime.runtimeModel.flowInstanceId); - if (makerList == "-1") continue; + //会签时的【当前节点】一直是会签开始节点 + //TODO: 标记会签节点的状态,这个地方感觉怪怪的 + wfruntime.MakeTagNode(wfruntime.currentNodeId, tag); - if (makerList.Split(',').Any(one => user.Id == one)) - { - verificationNodeId = item; - } + string canCheckId = ""; //寻找当前登录用户可审核的节点Id + foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[wfruntime.currentNodeId].Select(u => u.to)) + { + var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点 + canCheckId = GetOneForkLineCanCheckNodeId(fromForkStartNode, wfruntime, tag); + if(!string.IsNullOrEmpty(canCheckId)) break; } - if (verificationNodeId != "") + if (canCheckId == "") { - if (flag) - { - tag.Taged = 1; - flowInstanceOperationHistory.Content = "【" + wfruntime.runtimeModel.nodes[verificationNodeId].name + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】同意,备注:" + description; - } - else - { - tag.Taged = -1; - flowInstanceOperationHistory.Content = "【" + wfruntime.runtimeModel.nodes[verificationNodeId].name + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】不同意,备注:" + description; - } + throw (new Exception("审核异常,找不到审核节点")); + } - wfruntime.MakeTagNode(verificationNodeId, tag);//标记审核节点状态 - string confluenceres = wfruntime.NodeConfluence(verificationNodeId, tag); - switch (confluenceres) - { - case "-1"://不通过 - flowInstance.IsFinish = 3; - break; - case "1"://等待,当前节点还是会签开始节点,不跳转 - break; - default://通过 - flowInstance.PreviousId = flowInstance.ActivityId; - flowInstance.ActivityId = wfruntime.runtimeModel.nextNodeId; - flowInstance.ActivityType = wfruntime.runtimeModel.nextNodeType;//-1无法运行,0会签开始,1会签结束,2一般节点,4流程运行结束 - flowInstance.ActivityName = wfruntime.runtimeModel.nextNode.name; - flowInstance.IsFinish = (wfruntime.runtimeModel.nextNodeType == 4 ? 1 : 0); - flowInstance.MakerList = (wfruntime.runtimeModel.nextNodeType == 4 ? "" : GetMakerList(wfruntime));//当前节点可执行的人信息 - - #region 流转记录 - UnitWork.Add(new FlowInstanceTransitionHistory - { - InstanceId = flowInstance.Id, - CreateUserId = user.Id, - CreateUserName = user.Name, - FromNodeId = wfruntime.runtimeModel.currentNodeId, - FromNodeName = wfruntime.runtimeModel.currentNode.name, - FromNodeType = wfruntime.runtimeModel.currentNodeType, - ToNodeId = wfruntime.runtimeModel.nextNodeId, - ToNodeName = wfruntime.runtimeModel.nextNode.name, - ToNodeType = wfruntime.runtimeModel.nextNodeType, - IsFinish = wfruntime.runtimeModel.nextNodeType ==4?1:0, - TransitionSate = 0 - }); - #endregion - - break; - } + flowInstanceOperationHistory.Content = "【" + wfruntime.Nodes[canCheckId].name + + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + + "】" + (tag.Taged == 1 ? "同意" : "不同意") + ",备注:" + + tag.Description; + + wfruntime.MakeTagNode(canCheckId, tag); //标记审核节点状态 + string res = wfruntime.NodeConfluence(canCheckId, tag); + if (res == TagState.No.ToString("D")) + { + flowInstance.IsFinish = 3; + } + else if(!string.IsNullOrEmpty(res)) + { + flowInstance.PreviousId = flowInstance.ActivityId; + flowInstance.ActivityId = wfruntime.nextNodeId; + flowInstance.ActivityType = wfruntime.nextNodeType; + flowInstance.ActivityName = wfruntime.nextNode.name; + flowInstance.IsFinish = (wfruntime.nextNodeType == 4 ? 1 : 0); + flowInstance.MakerList = + (wfruntime.nextNodeType == 4 ? "" : GetNextMakers(wfruntime)); + + AddTransHistory(wfruntime); } else { - throw (new Exception("审核异常,找不到审核节点")); + //会签过程中,需要更新用户 + flowInstance.MakerList = GetForkNodeMakers(wfruntime, wfruntime.currentNodeId); + AddTransHistory(wfruntime); } + } - #endregion + #endregion 会签 #region 一般审核 - else//一般审核 + + else { - if (flag) + wfruntime.MakeTagNode(wfruntime.currentNodeId, tag); + if (tag.Taged == (int) TagState.Ok) { - tag.Taged = 1; - wfruntime.MakeTagNode(wfruntime.runtimeModel.currentNodeId, tag); flowInstance.PreviousId = flowInstance.ActivityId; - flowInstance.ActivityId = wfruntime.runtimeModel.nextNodeId; - flowInstance.ActivityType = wfruntime.runtimeModel.nextNodeType; - flowInstance.ActivityName = wfruntime.runtimeModel.nextNode.name; - flowInstance.MakerList = wfruntime.runtimeModel.nextNodeType == 4 ? "" : GetMakerList(wfruntime);//当前节点可执行的人信息 - flowInstance.IsFinish = (wfruntime.runtimeModel.nextNodeType == 4 ? 1 : 0); - #region 流转记录 - - UnitWork.Add(new FlowInstanceTransitionHistory - { - InstanceId = flowInstance.Id, - CreateUserId = user.Id, - CreateUserName = user.Name, - FromNodeId = wfruntime.runtimeModel.currentNodeId, - FromNodeName = wfruntime.runtimeModel.currentNode.name, - FromNodeType = wfruntime.runtimeModel.currentNodeType, - ToNodeId = wfruntime.runtimeModel.nextNodeId, - ToNodeName = wfruntime.runtimeModel.nextNode.name, - ToNodeType = wfruntime.runtimeModel.nextNodeType, - IsFinish = wfruntime.runtimeModel.nextNodeType == 4 ? 1 : 0, - TransitionSate = 0 - }); - #endregion - - flowInstanceOperationHistory.Content = "【" + wfruntime.runtimeModel.currentNode.name - + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】同意,备注:" + description; + flowInstance.ActivityId = wfruntime.nextNodeId; + flowInstance.ActivityType = wfruntime.nextNodeType; + flowInstance.ActivityName = wfruntime.nextNode.name; + flowInstance.MakerList = wfruntime.nextNodeType == 4 ? "" : GetNextMakers(wfruntime); + flowInstance.IsFinish = (wfruntime.nextNodeType == 4 ? 1 : 0); + AddTransHistory(wfruntime); } else { flowInstance.IsFinish = 3; //表示该节点不同意 - tag.Taged = -1; - wfruntime.MakeTagNode(wfruntime.runtimeModel.currentNodeId, tag); - - flowInstanceOperationHistory.Content = "【" - + wfruntime.runtimeModel.currentNode.name + "】【" - + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】不同意,备注:" - + description; } + flowInstanceOperationHistory.Content = "【" + wfruntime.currentNode.name + + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + + "】" + (tag.Taged == 1 ? "同意" : "不同意") + ",备注:" + + tag.Description; } - #endregion - flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.runtimeModel.schemeContentJson); + #endregion 一般审核 + + flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.ToSchemeObj()); UnitWork.Update(flowInstance); UnitWork.Add(flowInstanceOperationHistory); UnitWork.Save(); + + wfruntime.NotifyThirdParty(_httpClientFactory.CreateClient(), tag); return true; } + + //会签时,获取一条会签分支上面是否有用户可审核的节点 + private string GetOneForkLineCanCheckNodeId(FlowNode fromForkStartNode, FlowRuntime wfruntime, Tag tag) + { + string canCheckId=""; + var node = fromForkStartNode; + do //沿一条分支线路执行,直到遇到会签结束节点 + { + var makerList = GetNodeMarkers(node); + + if (node.setInfo.Taged == null && !string.IsNullOrEmpty(makerList) && makerList.Split(',').Any(one => tag.UserId == one)) + { + canCheckId = node.id; + break; + } + + node = wfruntime.GetNextNode(node.id); + } while (node.type != FlowNode.JOIN); + + return canCheckId; + } + /// /// 驳回 + /// 如果NodeRejectStep不为空,优先使用;否则按照NodeRejectType驳回 /// /// public bool NodeReject(VerificationReq reqest) { - var user = AuthUtil.GetCurrentUser().User; + var user = _auth.GetCurrentUser().User; FlowInstance flowInstance = Get(reqest.FlowInstanceId); - + FlowRuntime wfruntime = new FlowRuntime(flowInstance); string resnode = ""; - if (string.IsNullOrEmpty(reqest.NodeRejectStep)) - { - resnode = wfruntime.RejectNode(); - } - else - { - resnode = reqest.NodeRejectStep; - } + resnode = string.IsNullOrEmpty(reqest.NodeRejectStep) ? wfruntime.RejectNode(reqest.NodeRejectType) : reqest.NodeRejectStep; var tag = new Tag { Description = reqest.VerificationOpinion, - Taged = 0, + Taged = (int) TagState.Reject, UserId = user.Id, UserName = user.Name }; - wfruntime.MakeTagNode(wfruntime.runtimeModel.currentNodeId, tag); + wfruntime.MakeTagNode(wfruntime.currentNodeId, tag); flowInstance.IsFinish = 4;//4表示驳回(需要申请者重新提交表单) if (resnode != "") { flowInstance.PreviousId = flowInstance.ActivityId; flowInstance.ActivityId = resnode; flowInstance.ActivityType = wfruntime.GetNodeType(resnode); - flowInstance.ActivityName = wfruntime.runtimeModel.nodes[resnode].name; - flowInstance.MakerList = GetMakerList(wfruntime.runtimeModel.nodes[resnode], flowInstance.PreviousId);//当前节点可执行的人信息 - #region 流转记录 + flowInstance.ActivityName = wfruntime.Nodes[resnode].name; + flowInstance.MakerList = GetNodeMarkers(wfruntime.Nodes[resnode]);//当前节点可执行的人信息 - UnitWork.Add(new FlowInstanceTransitionHistory - { - InstanceId = flowInstance.Id, - CreateUserId = user.Id, - CreateUserName = user.Name, - FromNodeId = wfruntime.runtimeModel.currentNodeId, - FromNodeName = wfruntime.runtimeModel.currentNode.name, - FromNodeType = wfruntime.runtimeModel.currentNodeType, - ToNodeId = wfruntime.runtimeModel.nextNodeId, - ToNodeName = wfruntime.runtimeModel.nextNode.name, - ToNodeType = wfruntime.runtimeModel.nextNodeType, - IsFinish = wfruntime.runtimeModel.nextNodeType == 4 ? 1 : 0, - TransitionSate = 1 - }); - #endregion + AddTransHistory(wfruntime); } UnitWork.Update(flowInstance); @@ -310,60 +299,49 @@ namespace OpenAuth.App UnitWork.Add(new FlowInstanceOperationHistory { InstanceId = reqest.FlowInstanceId - ,CreateUserId = user.Id - ,CreateUserName = user.Name - ,CreateDate = DateTime.Now - ,Content = "【" - + wfruntime.runtimeModel.currentNode.name - + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】驳回,备注:" + , + CreateUserId = user.Id + , + CreateUserName = user.Name + , + CreateDate = DateTime.Now + , + Content = "【" + + wfruntime.currentNode.name + + "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "】驳回,备注:" + reqest.VerificationOpinion - - }); + }); UnitWork.Save(); + wfruntime.NotifyThirdParty(_httpClientFactory.CreateClient(), tag); + return true; } - #endregion + #endregion 流程处理API + + #region 获取各种节点的流程审核者 /// - /// 寻找该节点执行人 + /// 寻找下一步的执行人 + /// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能 /// - /// /// - private string GetMakerList(FlowRuntime wfruntime) + private string GetNextMakers(FlowRuntime wfruntime) { string makerList = ""; - if (wfruntime.runtimeModel.nextNodeId == "-1") + if (wfruntime.nextNodeId == "-1") { throw (new Exception("无法寻找到下一个节点")); } - if (wfruntime.runtimeModel.nextNodeType == 0)//如果是会签节点 + if (wfruntime.nextNodeType == 0)//如果是会签节点 { - List _nodelist = wfruntime.GetCountersigningNodeIdList(wfruntime.runtimeModel.nextNodeId); - string _makerList = ""; - foreach (string item in _nodelist) - { - _makerList = GetMakerList(wfruntime.runtimeModel.nodes[item], wfruntime.runtimeModel.flowInstanceId); - if (_makerList == "-1") - { - throw (new Exception("无法寻找到会签节点的审核者,请查看流程设计是否有问题!")); - } - if (_makerList == "1") - { - throw (new Exception("会签节点的审核者不能为所有人,请查看流程设计是否有问题!")); - } - if (makerList != "") - { - makerList += ","; - } - makerList += _makerList; - } + makerList = GetForkNodeMakers(wfruntime, wfruntime.nextNodeId); } else { - makerList = GetMakerList(wfruntime.runtimeModel.nextNode, wfruntime.runtimeModel.flowInstanceId); - if (makerList == "-1") + makerList = GetNodeMarkers(wfruntime.nextNode); + if (string.IsNullOrEmpty(makerList)) { throw (new Exception("无法寻找到节点的审核者,请查看流程设计是否有问题!")); } @@ -371,20 +349,76 @@ namespace OpenAuth.App return makerList; } + + /// + /// 获取会签开始节点的所有可执行者 + /// + /// 会签开始节点 + /// + private string GetForkNodeMakers(FlowRuntime wfruntime, string forkNodeId) + { + string makerList=""; + foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[forkNodeId].Select(u => u.to)) + { + var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点 + if (makerList != "") + { + makerList += ","; + } + + makerList += GetOneForkLineMakers(fromForkStartNode, wfruntime); + } + + return makerList; + } + + //获取会签一条线上的审核者,该审核者应该是已审核过的节点的下一个人 + private string GetOneForkLineMakers(FlowNode fromForkStartNode, FlowRuntime wfruntime) + { + string markers=""; + var node = fromForkStartNode; + do //沿一条分支线路执行,直到遇到第一个没有审核的节点 + { + if (node.setInfo != null && node.setInfo.Taged != null) + { + if (node.type != FlowNode.FORK && node.setInfo.Taged != (int) TagState.Ok) //如果节点是不同意或驳回,则不用再找了 + { + break; + } + node = wfruntime.GetNextNode(node.id); //下一个节点 + continue; + } + var marker = GetNodeMarkers(node); + if (marker == "") + { + throw (new Exception($"节点{node.name}没有审核者,请检查!")); + } + if (marker == "1") + { + throw (new Exception($"节点{node.name}是会签节点,不能用所有人,请检查!")); + } + + if (markers != "") + { + markers += ","; + } + markers += marker; + break; + } while (node.type != FlowNode.JOIN); + + return markers; + } + /// /// 寻找该节点执行人 /// /// /// - private string GetMakerList(FlowNode node, string processId) + private string GetNodeMarkers(FlowNode node) { string makerList = ""; - if (node.setInfo == null) - { - makerList = "-1"; - } - else + if (node.setInfo != null) { if (node.setInfo.NodeDesignate == Setinfo.ALL_USER)//所有成员 { @@ -393,25 +427,20 @@ namespace OpenAuth.App else if (node.setInfo.NodeDesignate == Setinfo.SPECIAL_USER)//指定成员 { makerList = GenericHelpers.ArrayToString(node.setInfo.NodeDesignateData.users, makerList); - - if (makerList == "") - { - makerList = "-1"; - } } else if (node.setInfo.NodeDesignate == Setinfo.SPECIAL_ROLE) //指定角色 { - var users = RevelanceManagerApp.Get(Define.USERROLE, false, node.setInfo.NodeDesignateData.roles); + var users = _revelanceApp.Get(Define.USERROLE, false, node.setInfo.NodeDesignateData.roles); makerList = GenericHelpers.ArrayToString(users, makerList); - - if (makerList == "") - { - makerList = "-1"; - } } } + else //如果没有设置节点信息,默认所有人都可以审核 + { + makerList = "1"; + } return makerList; } + #endregion /// /// 审核流程 @@ -419,18 +448,22 @@ namespace OpenAuth.App /// public void Verification(VerificationReq request) { - //驳回 - if (request.VerificationFinally == "3") + var user = _auth.GetCurrentUser().User; + var tag = new Tag { - NodeReject(request); - } - else if (request.VerificationFinally == "2")//表示不同意 + UserName = user.Name, + UserId = user.Id, + Description = request.VerificationOpinion, + Taged = Int32.Parse(request.VerificationFinally) + }; + bool isReject = TagState.Reject.Equals((TagState) tag.Taged); + if (isReject) //驳回 { - NodeVerification(request.FlowInstanceId, false, request.VerificationOpinion); + NodeReject(request); } - else if (request.VerificationFinally == "1")//表示同意 + else { - NodeVerification(request.FlowInstanceId, true, request.VerificationOpinion); + NodeVerification(request.FlowInstanceId, tag); } } @@ -441,27 +474,36 @@ namespace OpenAuth.App public TableData Load(QueryFlowInstanceListReq request) { - //todo:待办/已办/我的 var result = new TableData(); - var user = AuthUtil.GetCurrentUser(); + var user = _auth.GetCurrentUser(); if (request.type == "wait") //待办事项 { - result.count = UnitWork.Find(u => u.MakerList =="1" || u.MakerList.Contains(user.User.Id)).Count(); + Expression> waitExp = u => (u.MakerList == "1" || u.MakerList.Contains(user.User.Id)) && u.IsFinish == 0; + + // 加入搜索自定义标题 + if (!string.IsNullOrEmpty(request.key)) + { + waitExp = waitExp.And(t => t.CustomName.Contains(request.key)); + } - result.data = UnitWork.Find(request.page, request.limit, "CreateDate descending", - u => u.MakerList == "1" || u.MakerList.Contains(user.User.Id)).ToList(); + result.count = UnitWork.Find(waitExp).Count(); + result.data = UnitWork.Find(request.page, request.limit, "CreateDate descending", waitExp).ToList(); } else if (request.type == "disposed") //已办事项(即我参与过的流程) { var instances = UnitWork.Find(u => u.CreateUserId == user.User.Id) .Select(u => u.InstanceId).Distinct(); var query = from ti in instances - join ct in UnitWork.Find(null) on ti equals ct.Id - into tmp - from ct in tmp.DefaultIfEmpty() - select ct; + join ct in UnitWork.Find(null) on ti equals ct.Id + select ct; + + // 加入搜索自定义标题 + if (!string.IsNullOrEmpty(request.key)) + { + query = query.Where(t => t.CustomName.Contains(request.key)); + } result.data = query.OrderByDescending(u => u.CreateDate) .Skip((request.page - 1) * request.limit) @@ -470,13 +512,59 @@ namespace OpenAuth.App } else //我的流程 { - result.count = UnitWork.Find(u => u.CreateUserId == user.User.Id).Count(); - result.data = UnitWork.Find(request.page, request.limit, - "CreateDate descending", u => u.CreateUserId == user.User.Id).ToList(); + Expression> myFlowExp = u => u.CreateUserId == user.User.Id; + + // 加入搜索自定义标题 + if (!string.IsNullOrEmpty(request.key)) + { + myFlowExp = myFlowExp.And(t => t.CustomName.Contains(request.key)); + } + + result.count = UnitWork.Find(myFlowExp).Count(); + result.data = UnitWork.Find(request.page, request.limit, + "CreateDate descending", myFlowExp).ToList(); } return result; } - } -} + /// + /// 添加扭转记录 + /// + private void AddTransHistory(FlowRuntime wfruntime) + { + var tag = _auth.GetCurrentUser().User; + UnitWork.Add(new FlowInstanceTransitionHistory + { + InstanceId = wfruntime.flowInstanceId, + CreateUserId = tag.Id, + CreateUserName = tag.Name, + FromNodeId = wfruntime.currentNodeId, + FromNodeName = wfruntime.currentNode.name, + FromNodeType = wfruntime.currentNodeType, + ToNodeId = wfruntime.nextNodeId, + ToNodeName = wfruntime.nextNode.name, + ToNodeType = wfruntime.nextNodeType, + IsFinish = wfruntime.nextNodeType == 4 ? 1 : 0, + TransitionSate = 0 + }); + } + + public FlowInstanceApp(IUnitWork unitWork, IRepository repository + , RevelanceManagerApp app, FlowSchemeApp flowSchemeApp, FormApp formApp, IHttpClientFactory httpClientFactory,IAuth auth, IServiceProvider serviceProvider) + : base(unitWork, repository, auth) + { + _revelanceApp = app; + _flowSchemeApp = flowSchemeApp; + _formApp = formApp; + _httpClientFactory = httpClientFactory; + _serviceProvider = serviceProvider; + } + + public List QueryHistories(QueryFlowInstanceHistoryReq request) + { + return UnitWork.Find(u => u.InstanceId == request.FlowInstanceId) + .OrderByDescending(u => u.CreateDate).ToList(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/FlowSchemeApp.cs b/OpenAuth.App/FlowSchemeApp.cs index fdb6b611f12fd9ba387727c9803863951f3c7ca6..562409c0472ecade8414ef514aefbe667f42b9c9 100644 --- a/OpenAuth.App/FlowSchemeApp.cs +++ b/OpenAuth.App/FlowSchemeApp.cs @@ -1,37 +1,71 @@ -using System; +using System; +using System.Linq; +using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.Response; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { public class FlowSchemeApp :BaseApp { - public void Add(FlowScheme flowScheme) { + if (Repository.IsExist(u => u.SchemeName == flowScheme.SchemeName)) + { + throw new Exception("流程名称已经存在"); + } + + var user = _auth.GetCurrentUser().User; + flowScheme.CreateUserId = user.Id; + flowScheme.CreateUserName = user.Name; Repository.Add(flowScheme); } + public FlowScheme FindByCode(string code) + { + return Repository.FindSingle(u => u.SchemeCode == code); + } + public void Update(FlowScheme flowScheme) { + if (Repository.IsExist(u => u.SchemeName == flowScheme.SchemeName && u.Id != flowScheme.Id)) + { + throw new Exception("流程名称已经存在"); + } + UnitWork.Update(u => u.Id == flowScheme.Id, u => new FlowScheme { SchemeContent = flowScheme.SchemeContent, SchemeName = flowScheme.SchemeName, ModifyDate = DateTime.Now, FrmId = flowScheme.FrmId, - Disabled = flowScheme.Disabled + FrmType = flowScheme.FrmType, + Disabled = flowScheme.Disabled, + Description = flowScheme.Description, + OrgId = flowScheme.OrgId }); } public TableData Load(QueryFlowSchemeListReq request) { - return new TableData + var result = new TableData(); + var objs = GetDataPrivilege("u"); + if (!string.IsNullOrEmpty(request.key)) { - count = Repository.GetCount(null), - data = Repository.Find(request.page, request.limit, "CreateDate desc") - }; + objs = objs.Where(u => u.SchemeName.Contains(request.key) || u.Id.Contains(request.key)); + } + + result.data = objs.OrderByDescending(u => u.CreateDate) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).ToList(); + result.count = objs.Count(); + return result; + } + + public FlowSchemeApp(IUnitWork unitWork, IRepository repository,IAuth auth) : base(unitWork, repository, auth) + { } } } diff --git a/OpenAuth.App/FormApp.cs b/OpenAuth.App/FormApp.cs index 349f45fe17a5794ef6cf9d28b20c03ae7c85588a..57ae53e11fcafa8ffcab2f7ebbfde45899e44ab9 100644 --- a/OpenAuth.App/FormApp.cs +++ b/OpenAuth.App/FormApp.cs @@ -1,35 +1,48 @@ -using System; +using System; +using System.Linq; using Infrastructure; +using Microsoft.Extensions.Options; +using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.Response; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { public class FormApp : BaseApp { - public RevelanceManagerApp ReleManagerApp { get; set; } - + private IAuth _auth; + private IOptions _appConfiguration; /// /// 加载列表 /// public TableData Load(QueryFormListReq request) { - - return new TableData + var result = new TableData(); + var forms = GetDataPrivilege("u"); + if (!string.IsNullOrEmpty(request.key)) { - count = Repository.GetCount(null), - data = Repository.Find(request.page, request.limit, "CreateDate desc") - }; + forms = forms.Where(u => u.Name.Contains(request.key) || u.Id.Contains(request.key)); + } + + result.data = forms.OrderByDescending(u => u.CreateDate) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).ToList(); + result.count = forms.Count(); + return result; } public void Add(Form obj) { + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; UnitWork.Add(obj); if (!string.IsNullOrEmpty(obj.DbName)) { - UnitWork.ExecuteSql(FormUtil.GetSql(obj)); + UnitWork.ExecuteSql(FormUtil.GetSql(obj, _appConfiguration.Value.DbType)); } UnitWork.Save(); } @@ -42,15 +55,17 @@ namespace OpenAuth.App Content = obj.Content, ContentParse = obj.ContentParse, Name = obj.Name, + Disabled = obj.Disabled, DbName = obj.DbName, SortCode = obj.SortCode, Description = obj.Description, + OrgId = obj.OrgId, ModifyDate = DateTime.Now }); if (!string.IsNullOrEmpty(obj.DbName)) { - UnitWork.ExecuteSql(FormUtil.GetSql(obj)); + UnitWork.ExecuteSql(FormUtil.GetSql(obj, _appConfiguration.Value.DbType)); } } @@ -60,5 +75,11 @@ namespace OpenAuth.App return form.MapTo(); } + public FormApp(IUnitWork unitWork, IRepository repository, + IAuth auth, IOptions appConfiguration) : base(unitWork, repository, auth) + { + _auth = auth; + _appConfiguration = appConfiguration; + } } } \ No newline at end of file diff --git a/OpenAuth.App/FormUtil.cs b/OpenAuth.App/FormUtil.cs index bda43c6c7397be3af459651b339a031d7f5f2930..66ae5cc45c845b2171d1a91f134c636eff3b0634 100644 --- a/OpenAuth.App/FormUtil.cs +++ b/OpenAuth.App/FormUtil.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.Text; using Infrastructure; using Newtonsoft.Json.Linq; @@ -12,6 +10,10 @@ namespace OpenAuth.App public static string GetHtml(string contentData, string contentParse,string frmData, string action) { + if (string.IsNullOrEmpty(contentData)) + { + return string.Empty; + } JObject tableData = null;//表单数据 if (!string.IsNullOrEmpty(frmData)) { @@ -72,6 +74,10 @@ namespace OpenAuth.App /// The form. /// System.String. public static string GetHtml(FormResp form){ + if (form.FrmType != 0) //只有开原版动态表单才需要转换 + { + return string.Empty; + } return GetHtml(form.ContentData, form.ContentParse,null, ""); @@ -84,9 +90,15 @@ namespace OpenAuth.App /// The content parse. /// The FRM data. /// System.String. - public static string Preview(string contentdata, string contentParse, string frmData) + public static string Preview(FlowInstance flowInstance) { - return GetHtml(contentdata, contentParse, frmData, "view"); + if (flowInstance.FrmType != 0) //只有开原版动态表单才需要转换 + { + return string.Empty; + } + + return GetHtml(flowInstance.FrmContentData, flowInstance.FrmContentParse, + flowInstance.FrmData, "view"); } //text @@ -233,6 +245,7 @@ namespace OpenAuth.App } string content =item.GetValue("content").ToString(); + content = content.Replace("leipiNewField", name); if (value != null)//用户设置过值 { content = content.Replace("selected=\"selected\"", ""); //先去掉模板中的选中项 @@ -480,55 +493,97 @@ namespace OpenAuth.App /** * 功能: 创建表单数据表格(基于sql server) */ - public static string GetSql(Form form){ - // 获取字段并处理 - var jsonArray = JArray.Parse(form.ContentData); + public static string GetSql(Form form, string dbType){ + + if (dbType == Define.DBTYPE_SQLSERVER) //Sql Server + { + // 获取字段并处理 + var jsonArray = JArray.Parse(form.ContentData); - // 数据库名称 - string tableName= form.DbName ; - // 创建数据表 - StringBuilder sql =new StringBuilder("if exists ( select * from sysobjects where name = '" - +tableName+"' and type = 'U') drop table " - + tableName +";") ; + // 数据库名称 + string tableName= form.DbName ; + // 创建数据表 + StringBuilder sql =new StringBuilder("if exists ( select * from sysobjects where name = '" + +tableName+"' and type = 'U') drop table " + + tableName +";") ; - sql.Append("CREATE TABLE " - + tableName - + " ( [Id] varchar(50) COLLATE Chinese_PRC_CI_AS NOT NULL,"); //主键 + sql.Append("CREATE TABLE " + + tableName + + " ( [Id] varchar(50) COLLATE Chinese_PRC_CI_AS NOT NULL,"); //主键 - string sqlDefault = ""; + string sqlDefault = ""; - foreach (var json in jsonArray) - { - string name; - string type = json["leipiplugins"].ToString(); + foreach (var json in jsonArray) + { + string name; + string type = json["leipiplugins"].ToString(); - if ("checkboxs" == type) - name = json["parse_name"].ToString(); - else - name = json["name"].ToString(); + if ("checkboxs" == type) + name = json["parse_name"].ToString(); + else + name = json["name"].ToString(); - sql.Append("[" + name + "] " + field_type_sql(type));//字段拼接 + sql.Append("[" + name + "] " + field_type_sql(type));//字段拼接 - if ("checkboxs" == type) - sqlDefault += field_type_sql_default(tableName, name, "0"); - else - sqlDefault += field_type_sql_default(tableName, name, "''"); + if ("checkboxs" == type) + sqlDefault += field_type_sql_default(tableName, name, "0"); + else + sqlDefault += field_type_sql_default(tableName, name, "''"); + } + + sql.Append(");"); + + //设置主键 + sql.Append("ALTER TABLE "+tableName+" ADD CONSTRAINT [PK_"+form.DbName+"] PRIMARY KEY NONCLUSTERED ([Id])"); + sql.Append( + "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) "); + sql.Append("ON [PRIMARY];"); + + //主键默认值 + sql.Append("ALTER TABLE "+tableName+" ADD DEFAULT (newid()) FOR [Id];"); + + return sql+sqlDefault; } + else + { + // 获取字段并处理 + var jsonArray = JArray.Parse(form.ContentData); + + // 数据库名称 + string tableName= form.DbName ; + // 创建数据表 + StringBuilder sql =new StringBuilder("create table if not exists `" + + tableName + + "` ( Id varchar(50) not null primary key,") ; //主键 - sql.Append(");"); - //设置主键 - sql.Append("ALTER TABLE "+tableName+" ADD CONSTRAINT [PK_"+form.DbName+"] PRIMARY KEY NONCLUSTERED ([Id])"); - sql.Append( - "WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) "); - sql.Append("ON [PRIMARY];"); + string sqlDefault = ""; - //主键默认值 - sql.Append("ALTER TABLE "+tableName+" ADD DEFAULT (newid()) FOR [Id];"); + foreach (var json in jsonArray) + { + string name; + string type = json["leipiplugins"].ToString(); + + if ("checkboxs" == type) + name = json["parse_name"].ToString(); + else + name = json["name"].ToString(); + + sql.Append("`" + name + "` " + field_type_mysql(type));//字段拼接 - return sql+sqlDefault; +// +// if ("checkboxs" == type) +// sqlDefault += field_type_sql_default(tableName, name, "0"); +// else +// sqlDefault += field_type_sql_default(tableName, name, "''"); + } + + sql.Append(");"); + return sql.ToString(); + } + } //获取控件字段类型 的sql private static string field_type_sql(string leipiplugins) @@ -546,6 +601,23 @@ namespace OpenAuth.App return " varchar(255) NULL ,"; } } + + private static string field_type_mysql(string leipiplugins) + { + if ("textarea"==leipiplugins || "listctrl"==leipiplugins) + { + return " varchar(255) null ,"; + } + else if ("checkboxs"==leipiplugins) + { + return " tinyint not null ,"; + } + else + { + return " varchar(255) NULL ,"; + } + } + private static string field_type_sql_default(string tablename, string field, string defaultValue) { return "ALTER TABLE "+tablename+" ADD DEFAULT ("+defaultValue+") FOR ["+field+"];"; diff --git a/CodeSmith/CSharp/Web/Application.cst b/OpenAuth.App/FrmLeaveReqApp.cs similarity index 36% rename from CodeSmith/CSharp/Web/Application.cst rename to OpenAuth.App/FrmLeaveReqApp.cs index c36341096cce06418a1b4007f7d6ea5aca97e474..44ad8755dea77ce0e9c605956743a9de1a6f5805 100644 --- a/CodeSmith/CSharp/Web/Application.cst +++ b/OpenAuth.App/FrmLeaveReqApp.cs @@ -1,53 +1,54 @@ -<%-- -Name: Database Table Properties -Author: yubaolee -Description: Create a list of properties from a database table ---%> -<%@ CodeTemplate Language="C#" Encoding="utf-8" TargetLanguage="C#" Debug="False" Description="应用层" %> -<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %> -<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %> -<%@ Assembly Name="SchemaExplorer" %> -<%@ Import Namespace="SchemaExplorer" %> -using System; -using System.Collections.Generic; -using System.Linq; -using OpenAuth.App.Request; -using OpenAuth.App.Response; -using OpenAuth.App.SSO; -using OpenAuth.Repository.Domain; - - -namespace OpenAuth.App -{ - public class <%=ModuleName%>App : BaseApp<<%=ModuleName%>> - { - public RevelanceManagerApp ReleManagerApp { get; set; } - - /// - /// 加载列表 - /// - public TableData Load(Query<%=ModuleName%>ListReq request) - { - return new TableData - { - count = Repository.GetCount(null), - data = Repository.Find(request.page, request.limit, "Id desc") - }; - } - - public void Add(<%=ModuleName%> obj) - { - Repository.Add(obj); - } - - public void Update(<%=ModuleName%> obj) - { - UnitWork.Update<<%=ModuleName%>>(u => u.Id == obj.Id, u => new <%=ModuleName%> - { - //todo:要修改的字段赋值 - }); - - } - - } +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class FrmLeaveReqApp : BaseApp, ICustomerForm + { + private RevelanceManagerApp _revelanceApp; + + /// + /// 加载列表 + /// + public TableData Load(QueryFrmLeaveReqListReq request) + { + return new TableData + { + count = Repository.GetCount(null), + data = Repository.Find(request.page, request.limit, "Id desc") + }; + } + + public void Add(FrmLeaveReq obj) + { + Repository.Add(obj); + } + + public void Update(FrmLeaveReq obj) + { + UnitWork.Update(u => u.Id == obj.Id, u => new FrmLeaveReq + { + //todo:要修改的字段赋值 + }); + + } + + public FrmLeaveReqApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app,IAuth auth) : base(unitWork, repository, auth) + { + _revelanceApp = app; + } + + public void Add(string flowInstanceId, string frmData) + { + var req = JsonHelper.Instance.Deserialize(frmData); + req.FlowInstanceId = flowInstanceId; + Add(req); + } + } } \ No newline at end of file diff --git a/OpenAuth.App/HostedService/QuartzService.cs b/OpenAuth.App/HostedService/QuartzService.cs new file mode 100644 index 0000000000000000000000000000000000000000..36a2f036e7e58b9f5652e2d09a64c8b1b3fd827a --- /dev/null +++ b/OpenAuth.App/HostedService/QuartzService.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Quartz; + +namespace OpenAuth.App.HostedService +{ + public class QuartzService : IHostedService, IDisposable + { + private readonly ILogger _logger; + private IScheduler _scheduler; + + public QuartzService(ILogger logger, IScheduler scheduler) + { + _logger = logger; + _scheduler = scheduler; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("启动定时job,可以在这里配置读取数据库需要启动的任务,然后启动他们"); + _scheduler.Start(); + return Task.CompletedTask; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _scheduler.Shutdown(); + _logger.LogInformation("关闭定时job"); + return Task.CompletedTask; + } + + public void Dispose() + { + + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Interface/IAuth.cs b/OpenAuth.App/Interface/IAuth.cs new file mode 100644 index 0000000000000000000000000000000000000000..2698f95c037fd2a9604b39d37293a6bd16817b51 --- /dev/null +++ b/OpenAuth.App/Interface/IAuth.cs @@ -0,0 +1,36 @@ +/* + *单独提取这个接口,为了以下几点: + * 1、可以方便的实现webapi 和本地登录相互切换 + * 2、可以方便的使用mock进行单元测试 + */ + +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Interface +{ + public interface IAuth + { + /// + /// 检验token是否有效 + /// + /// token值 + /// + /// + bool CheckLogin(string token="", string otherInfo = ""); + AuthStrategyContext GetCurrentUser(); + string GetUserName(string otherInfo = ""); + /// + /// 登录接口 + /// + /// 登录的应用appkey + /// 用户名 + /// 密码 + /// + LoginResult Login(string appKey, string username, string pwd); + /// + /// 退出登录 + /// + /// + bool Logout(); + } +} diff --git a/OpenAuth.App/Interface/IAuthStrategy.cs b/OpenAuth.App/Interface/IAuthStrategy.cs new file mode 100644 index 0000000000000000000000000000000000000000..bca1b0989ff54e17ea75aa0fc8b0fe8012f94b14 --- /dev/null +++ b/OpenAuth.App/Interface/IAuthStrategy.cs @@ -0,0 +1,50 @@ +// *********************************************************************** +// Assembly : OpenAuth.App +// Author : 李玉宝 +// Created : 07-05-2018 +// +// Last Modified By : 李玉宝 +// Last Modified On : 07-05-2018 +// *********************************************************************** +// +// Copyright (c) http://www.openauth.me. All rights reserved. +// +// +// 授权策略接口 +// +// *********************************************************************** + + +using System.Collections.Generic; +using Infrastructure; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App +{ + public interface IAuthStrategy + { + List Modules { get; } + + List ModuleElements { get; } + + List Roles { get; } + + List Resources { get; } + + List Orgs { get; } + + User User + { + get;set; + } + + /// + /// 根据模块id获取可访问的模块字段 + /// + /// + /// + List GetProperties(string moduleCode); + + } +} \ No newline at end of file diff --git a/OpenAuth.App/Interface/ICustomerForm.cs b/OpenAuth.App/Interface/ICustomerForm.cs new file mode 100644 index 0000000000000000000000000000000000000000..1312a967a8399e46324572086936f6caba850dfc --- /dev/null +++ b/OpenAuth.App/Interface/ICustomerForm.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Interface +{ + public interface ICustomerForm + { + void Add(string flowInstanceId, string frmData); + } +} diff --git a/OpenAuth.App/Jobs/SysLogJob.cs b/OpenAuth.App/Jobs/SysLogJob.cs new file mode 100644 index 0000000000000000000000000000000000000000..b951e170389ee8e33c1728a1e62ea9ee64378451 --- /dev/null +++ b/OpenAuth.App/Jobs/SysLogJob.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using Quartz; + +namespace OpenAuth.App.Jobs +{ + public class SysLogJob : IJob + { + private SysLogApp _sysLogApp; + private OpenJobApp _openJobApp; + + public SysLogJob(SysLogApp sysLogApp, OpenJobApp openJobApp) + { + _sysLogApp = sysLogApp; + _openJobApp = openJobApp; + } + + public Task Execute(IJobExecutionContext context) + { + var jobId = context.MergedJobDataMap.GetString(Define.JOBMAPKEY); + //todo:这里可以加入自己的自动任务逻辑 + _openJobApp.RecordRun(jobId); + return Task.Delay(1); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/ModuleManagerApp.cs b/OpenAuth.App/ModuleManagerApp.cs index eec2904a687de9ebb597bc9d3645cb1aaf75af3a..cee93922c52ee9b36555f91b2a8dbd065a838030 100644 --- a/OpenAuth.App/ModuleManagerApp.cs +++ b/OpenAuth.App/ModuleManagerApp.cs @@ -1,52 +1,48 @@ using System.Collections.Generic; using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { - public class ModuleManagerApp :BaseApp + public class ModuleManagerApp :BaseTreeApp { - public RevelanceManagerApp RevelanceManagerApp { get; set; } + private RevelanceManagerApp _revelanceApp; public void Add(Module model) { - ChangeModuleCascade(model); + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + CaculateCascade(model); + Repository.Add(model); + //当前登录用户的所有角色自动分配模块 + loginContext.Roles.ForEach(u => + { + _revelanceApp.Assign(new AssignReq + { + type=Define.ROLEMODULE, + firstId = u.Id, + secIds = new[]{model.Id} + }); + }); + } - public void Update(Module model) + public void Update(Module obj) { - ChangeModuleCascade(model); - Repository.Update(u =>u.Id, model); + UpdateTreeObj(obj); } - #region 用户/角色分配模块 - /// - /// 加载特定用户的模块 - /// TODO:这里会加载用户及用户角色的所有模块,“为用户分配模块”功能会给人一种混乱的感觉,但可以接受 - /// - /// The user unique identifier. - public IEnumerable LoadForUser(string userId) - { - var roleIds = RevelanceManagerApp.Get(Define.USERROLE, true, userId); - var moduleIds = UnitWork.Find( - u => - (u.FirstId == userId && u.Key == Define.USERMODULE) || - (u.Key == Define.ROLEMODULE && roleIds.Contains(u.FirstId))).Select(u => u.SecondId); - return UnitWork.Find(u => moduleIds.Contains(u.Id)).OrderBy(u => u.SortNo); - } - /// - /// 根据某用户ID获取可访问某模块的菜单项 - /// - /// - /// - /// - public IEnumerable LoadMenusForUser(string moduleId, string userId) - { - var elementIds = RevelanceManagerApp.Get(Define.USERELEMENT, true, userId); - return UnitWork.Find(u => elementIds.Contains(u.Id) && u.ModuleId == moduleId); - } + #region 用户/角色分配模块 + /// /// 加载特定角色的模块 @@ -59,11 +55,22 @@ namespace OpenAuth.App return UnitWork.Find(u => moduleIds.Contains(u.Id)).OrderBy(u => u.SortNo); } + //获取角色可访问的模块字段 + public IEnumerable LoadPropertiesForRole(string roleId, string moduleCode) + { + return _revelanceApp.Get(Define.ROLEDATAPROPERTY, roleId, moduleCode); + } + public IEnumerable LoadMenusForRole(string moduleId, string roleId) { - var elementIds = RevelanceManagerApp.Get(Define.ROLEELEMENT, true, roleId); - return UnitWork.Find(u => elementIds.Contains(u.Id) && u.ModuleId == moduleId); + var elementIds = _revelanceApp.Get(Define.ROLEELEMENT, true, roleId); + var query = UnitWork.Find(u => elementIds.Contains(u.Id)); + if (!string.IsNullOrEmpty(moduleId)) + { + query = query.Where(u => u.ModuleId == moduleId); + } + return query; } #endregion 用户/角色分配模块 @@ -77,19 +84,42 @@ namespace OpenAuth.App public void DelMenu(string[] ids) { UnitWork.Delete(u => ids.Contains(u.Id)); + UnitWork.Save(); } public void AddMenu(ModuleElement model) { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } UnitWork.Add(model); + + //当前登录用户的所有角色自动分配菜单 + loginContext.Roles.ForEach(u => + { + _revelanceApp.Assign(new AssignReq + { + type=Define.ROLEELEMENT, + firstId = u.Id, + secIds = new[]{model.Id} + }); + }); UnitWork.Save(); } #endregion public void UpdateMenu(ModuleElement model) { - UnitWork.Update(u =>u.Id, model); + UnitWork.Update(model); UnitWork.Save(); } + + public ModuleManagerApp(IUnitWork unitWork, IRepository repository + ,RevelanceManagerApp app,IAuth auth) : base(unitWork, repository, auth) + { + _revelanceApp = app; + } } } \ No newline at end of file diff --git a/OpenAuth.App/OpenAuth.App.csproj b/OpenAuth.App/OpenAuth.App.csproj index 97ca2bb9b076509d379d1c324c49499136975f59..91ef110f0432fe115abcc34cd1a9082a67644186 100644 --- a/OpenAuth.App/OpenAuth.App.csproj +++ b/OpenAuth.App/OpenAuth.App.csproj @@ -1,179 +1,38 @@ - - - + + - Debug - AnyCPU - {0BBF2D65-FFFD-4272-B138-8EA4FB6FEC48} - Library - Properties - OpenAuth.App - OpenAuth.App - v4.5 - 512 - + netcoreapp5.0 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false + + + bin\Debug\netcoreapp3.1\OpenAuth.App.xml - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - - ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll - True - - - ..\packages\Autofac.Mvc5.3.3.4\lib\net45\Autofac.Integration.Mvc.dll - True - - - ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll - True - - - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - True - - - ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll - True - - - - - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - True - - - - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - True - - - ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll - True - - - ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll - True - - - ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll - True - - - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll - True - - - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll - True - - - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - {5FEAEC9A-4F1E-4EE7-B377-9DB1B0870DAC} - Infrastructure - - - {e8df8dea-e2cf-4bdb-8f4f-3f8205b0e03a} - OpenAuth.Repository - + + + + - + + + + + + + + + + + + + + - + + - - - \ No newline at end of file + + diff --git a/OpenAuth.App/OpenJobApp.cs b/OpenAuth.App/OpenJobApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..fa1182c453e71f3039482a79547e56414c221311 --- /dev/null +++ b/OpenAuth.App/OpenJobApp.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Infrastructure; +using Microsoft.Extensions.Logging; +using OpenAuth.App.Interface; +using OpenAuth.App.Jobs; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; +using Quartz; + + +namespace OpenAuth.App +{ + public class OpenJobApp : BaseApp + { + private SysLogApp _sysLogApp; + private IScheduler _scheduler; + private ILogger _logger; + + /// + /// 加载列表 + /// + public TableData Load(QueryOpenJobListReq request) + { + var result = new TableData(); + var objs = Repository.Find(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key)); + } + + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit); + result.count = objs.Count(); + return result; + } + + public void Add(AddOrUpdateOpenJobReq req) + { + var obj = req.MapTo(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + Repository.Add(obj); + } + + public void Update(AddOrUpdateOpenJobReq obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new OpenJob + { + JobName = obj.JobName, + JobType = obj.JobType, + JobCall = obj.JobCall, + JobCallParams = obj.JobCallParams, + Cron = obj.Cron, + Status = obj.Status, + Remark = obj.Remark, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + } + + #region 定时任务运行相关操作 + + /// + /// 返回系统的job接口 + /// + /// + public List QueryLocalHandlers() + { + var types = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces() + .Contains(typeof(IJob)))) + .ToArray(); + return types.Select(u => u.FullName).ToList(); + } + + public void ChangeJobStatus(ChangeJobStatusReq req) + { + var job = Repository.FindSingle(u => u.Id == req.Id); + if (job == null) + { + throw new Exception("任务不存在"); + } + + + + if (req.Status == 0) //停止 + { + TriggerKey triggerKey = new TriggerKey(job.Id); + // 停止触发器 + _scheduler.PauseTrigger(triggerKey); + // 移除触发器 + _scheduler.UnscheduleJob(triggerKey); + // 删除任务 + _scheduler.DeleteJob(new JobKey(job.Id)); + } + else //启动 + { + var jobBuilderType = typeof(JobBuilder); + var method = jobBuilderType.GetMethods().FirstOrDefault( + x => x.Name.Equals("Create", StringComparison.OrdinalIgnoreCase) && + x.IsGenericMethod && x.GetParameters().Length == 0) + ?.MakeGenericMethod(Type.GetType(job.JobCall)); + + var jobBuilder = (JobBuilder)method.Invoke(null, null); + + IJobDetail jobDetail = jobBuilder.WithIdentity(job.Id).Build(); + jobDetail.JobDataMap[Define.JOBMAPKEY] = job.Id; //传递job信息 + ITrigger trigger = TriggerBuilder.Create() + .WithCronSchedule(job.Cron) + .WithIdentity(job.Id) + .StartNow() + .Build(); + _scheduler.ScheduleJob(jobDetail, trigger); + } + + + var user = _auth.GetCurrentUser().User; + + job.Status = req.Status; + job.UpdateTime = DateTime.Now; + job.UpdateUserId = user.Id; + job.UpdateUserName = user.Name; + Repository.Update(job); + } + + /// + /// 记录任务运行结果 + /// + /// + public void RecordRun(string jobId) + { + var job = Repository.FindSingle(u =>u.Id == jobId); + if (job == null) + { + _sysLogApp.Add(new SysLog + { + TypeName = "定时任务", + TypeId = "AUTOJOB", + Content = $"未能找到定时任务:{jobId}" + }); + return; + } + + job.RunCount++; + job.LastRunTime = DateTime.Now; + Repository.Update(job); + + _sysLogApp.Add(new SysLog + { + CreateName = "Quartz", + CreateId = "Quartz", + TypeName = "定时任务", + TypeId = "AUTOJOB", + Content = $"运行了自动任务:{job.JobName}" + }); + _logger.LogInformation($"运行了自动任务:{job.JobName}"); + } + + #endregion + + + public OpenJobApp(IUnitWork unitWork, IRepository repository, + IAuth auth, SysLogApp sysLogApp, IScheduler scheduler, ILogger logger) : base(unitWork, repository, auth) + { + _sysLogApp = sysLogApp; + _scheduler = scheduler; + _logger = logger; + } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/OrgManagerApp.cs b/OpenAuth.App/OrgManagerApp.cs index 7b5562af42343e3ca8eacef34806e554ad08365c..f0a3cad2ea5426503941d8e29861c83bd709d160 100644 --- a/OpenAuth.App/OrgManagerApp.cs +++ b/OpenAuth.App/OrgManagerApp.cs @@ -1,13 +1,16 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using OpenAuth.App.Response; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { - public class OrgManagerApp : BaseApp + public class OrgManagerApp : BaseTreeApp { + private RevelanceManagerApp _revelanceApp; /// /// 添加部门 /// @@ -16,86 +19,72 @@ namespace OpenAuth.App /// 未能找到该组织的父节点信息 public string Add(Org org) { - ChangeModuleCascade(org); + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + CaculateCascade(org); Repository.Add(org); - + + //如果当前账号不是SYSTEM,则直接分配 + var loginUser = _auth.GetCurrentUser(); + if (loginUser.User.Account != Define.SYSTEM_USERNAME) + { + _revelanceApp.Assign(new AssignReq + { + type=Define.USERORG, + firstId = loginContext.User.Id, + secIds = new[]{org.Id} + }); + } + return org.Id; } public string Update(Org org) { - ChangeModuleCascade(org); - - //获取旧的的CascadeId - var cascadeId = Repository.FindSingle(o => o.Id == org.Id).CascadeId; - //根据CascadeId查询子部门 - var orgs = Repository.Find(u => u.CascadeId.Contains(cascadeId) && u.Id != org.Id) - .OrderBy(u => u.CascadeId).ToList(); - - //更新操作 - UnitWork.Update(org); - - //更新子部门的CascadeId - foreach (var a in orgs) - { - ChangeModuleCascade(a); - UnitWork.Update(a); - } - - UnitWork.Save(); + UpdateTreeObj(org); return org.Id; } - + /// /// 删除指定ID的部门及其所有子部门 /// - public void DelOrg(string[] ids) + public void DelOrgCascade(string[] ids) { - var delOrg = Repository.Find(u => ids.Contains(u.Id)).ToList(); - foreach (var org in delOrg) + var delOrgCascadeIds = UnitWork.Find(u => ids.Contains(u.Id)).Select(u => u.CascadeId).ToArray(); + var delOrgIds = new List(); + foreach (var cascadeId in delOrgCascadeIds) { - Repository.Delete(u => u.CascadeId.Contains(org.CascadeId)); + delOrgIds.AddRange(UnitWork.Find(u=>u.CascadeId.Contains(cascadeId)).Select(u =>u.Id).ToArray()); } + + UnitWork.Delete(u =>u.Key == Define.USERORG && delOrgIds.Contains(u.SecondId)); + UnitWork.Delete(u => delOrgIds.Contains(u.Id)); + UnitWork.Save(); + } - /// - /// 加载特定用户的角色 - /// TODO:这里会加载用户及用户角色的所有角色,“为用户分配角色”功能会给人一种混乱的感觉,但可以接受 + /// 加载特定用户的部门 /// /// The user unique identifier. public List LoadForUser(string userId) { - //用户角色 - var userRoleIds = - UnitWork.Find(u => u.FirstId == userId && u.Key == Define.USERROLE).Select(u => u.SecondId).ToList(); - - //用户角色与自己分配到的角色ID - var moduleIds = - UnitWork.Find( - u => - (u.FirstId == userId && u.Key == Define.USERORG) || - (u.Key == Define.ROLEORG && userRoleIds.Contains(u.FirstId))).Select(u => u.SecondId).ToList(); - - if (!moduleIds.Any()) return new List(); - return UnitWork.Find(u => moduleIds.Contains(u.Id)).ToList(); + var result = from userorg in UnitWork.Find(null) + join org in UnitWork.Find(null) on userorg.SecondId equals org.Id + where userorg.FirstId == userId && userorg.Key == Define.USERORG + select org; + return result.ToList(); } - /// - /// 加载特定角色的角色 - /// - /// The role unique identifier. - public List LoadForRole(string roleId) + public OrgManagerApp(IUnitWork unitWork, IRepository repository,IAuth auth, + RevelanceManagerApp revelanceApp) : base(unitWork, repository, auth) { - var moduleIds = - UnitWork.Find(u => u.FirstId == roleId && u.Key == Define.ROLEORG) - .Select(u => u.SecondId) - .ToList(); - if (!moduleIds.Any()) return new List(); - return UnitWork.Find(u => moduleIds.Contains(u.Id)).ToList(); + _revelanceApp = revelanceApp; } - } } \ No newline at end of file diff --git a/OpenAuth.App/Properties/AssemblyInfo.cs b/OpenAuth.App/Properties/AssemblyInfo.cs deleted file mode 100644 index 4066e7a5c6f32b79568a1d1d7398f49f2b34ecff..0000000000000000000000000000000000000000 --- a/OpenAuth.App/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// 有关程序集的常规信息通过以下 -// 特性集控制。更改这些特性值可修改 -// 与程序集关联的信息。 -[assembly: AssemblyTitle("OpenAuth.App")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("OpenAuth.App")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// 将 ComVisible 设置为 false 使此程序集中的类型 -// 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, -// 则将该类型上的 ComVisible 特性设置为 true。 -[assembly: ComVisible(false)] - -// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID -[assembly: Guid("83c7318e-df7b-4774-8299-7e2728e9b590")] - -// 程序集的版本信息由下面四个值组成: -// -// 主版本 -// 次版本 -// 生成号 -// 修订号 -// -// 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, -// 方法是按如下所示使用“*”: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenAuth.App/Request/AddFlowInstanceReq.cs b/OpenAuth.App/Request/AddFlowInstanceReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..489ad2ba7208ab7559e64b47097be199240b9e57 --- /dev/null +++ b/OpenAuth.App/Request/AddFlowInstanceReq.cs @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +using System.ComponentModel; + +namespace OpenAuth.App.Request +{ + /// + /// 创建工作流请求 + /// + public class AddFlowInstanceReq + { + + /// + /// 实例编号 + /// + [Description("实例编号")] + public string Code { get; set; } + /// + /// 自定义名称 + /// + [Description("自定义名称")] + public string CustomName { get; set; } + + + + /// + /// 流程模板内容 + /// + [Description("流程模板内容")] + public string SchemeContent { get; set; } + /// + /// 流程模板ID + /// + public string SchemeId { get; set; } + + /// + /// 流程模板自定义编号 + /// + public string SchemeCode { get; set; } + + + /// + /// 数据库名称 + /// + [Description("数据库名称")] + public string DbName { get; set; } + /// + /// 表单数据 + /// + [Description("表单数据")] + public string FrmData { get; set; } + /// + /// 表单类型 + /// + [Description("表单类型")] + public int FrmType { get; set; } + /// + /// 表单中的控件属性描述 + /// + [Description("表单中的控件属性描述")] + public string FrmContentData { get; set; } + /// + /// 表单控件位置模板 + /// + [Description("表单控件位置模板")] + public string FrmContentParse { get; set; } + /// + /// 表单ID + /// + [Description("表单ID")] + public string FrmId { get; set; } + + + /// + /// 所属部门 + /// + [Description("所属部门")] + public string OrgId { get; set; } + + /// + /// 创建用户主键 + /// + [Description("创建用户主键")] + public string CreateUserId { get; set; } + /// + /// 创建用户 + /// + [Description("创建用户")] + public string CreateUserName { get; set; } + + /// + /// 实例备注 + /// + [Description("实例备注")] + public string Description { get; set; } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateBuilderTableColumnReq.cs b/OpenAuth.App/Request/AddOrUpdateBuilderTableColumnReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..771444037b7bf0adb9057dcdabb5359f12806d59 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateBuilderTableColumnReq.cs @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 代码生成器的字段信息 + /// + [Table("BuilderTableColumn")] + public partial class AddOrUpdateBuilderTableColumnReq + { + + /// + /// 编号 + /// + public string Id { get; set; } + /// + /// 归属表编号 + /// + public string TableId { get; set; } + /// + /// 表名称 + /// + public string TableName { get; set; } + /// + /// 列名称 + /// + public string ColumnName { get; set; } + /// + /// 列描述 + /// + public string Comment { get; set; } + /// + /// 列类型 + /// + public string ColumnType { get; set; } + /// + /// 实体类型 + /// + public string EntityType { get; set; } + /// + /// 实体名称 + /// + public string EntityName { get; set; } + /// + /// 是否主键 + /// + public bool IsKey { get; set; } + /// + /// 是否自增 + /// + public bool IsIncrement { get; set; } + /// + /// 是否必填 + /// + public bool IsRequired { get; set; } + /// + /// 是否为插入字段 + /// + public bool IsInsert { get; set; } + /// + /// 是否编辑字段 + /// + public bool IsEdit { get; set; } + /// + /// 是否列表字段 + /// + public bool IsList { get; set; } + /// + /// 是否查询字段 + /// + public bool IsQuery { get; set; } + /// + /// 查询方式(等于、不等于、大于、小于、范围) + /// + public string QueryType { get; set; } + /// + /// 显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件) + /// + public string HtmlType { get; set; } + /// + /// 编辑类型(文本框、文本域、下拉框、复选框、单选框、日期控件) + /// + public string EditType { get; set; } + /// + /// 排序 + /// + public int Sort { get; set; } + /// + /// 创建时间 + /// + public System.DateTime CreateTime { get; set; } + /// + /// 创建人ID + /// + public string CreateUserId { get; set; } + /// + /// 修改时间 + /// + public System.DateTime? UpdateTime { get; set; } + /// + /// 修改人ID + /// + public string UpdateUserId { get; set; } + /// + /// 修改时的行位置 + /// + public int? EditRow { get; set; } + /// + /// 修改时的列位置 + /// + public int? EditCol { get; set; } + /// + /// 修改人姓名 + /// + public string UpdateUserName { get; set; } + /// + /// 创建人姓名 + /// + public string CreateUserName { get; set; } + /// + /// 最大长度 + /// + public int? MaxLength { get; set; } + + /// + /// 数据源(用于下拉框、复选框等取值) + /// + public string DataSource { get; set; } + + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateBuilderTableReq.cs b/OpenAuth.App/Request/AddOrUpdateBuilderTableReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..baa39c611e0668370d9f7bf75256665f840a9391 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateBuilderTableReq.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 代码生成器的表信息 + /// + public partial class AddOrUpdateBuilderTableReq + { + /// + /// Id为空则为添加 + /// + public string Id { get; set; } + + /// + /// 表英文全称 + /// + public string TableName { get; set; } + /// + /// 表描述、中文名称 + /// + public string Comment { get; set; } + /// + /// 子表英文全称 + /// + public string DetailTableName { get; set; } + /// + /// 子表描述、中文名称 + /// + public string DetailComment { get; set; } + /// + /// 实体类名称 + /// + public string ClassName { get; set; } + /// + /// 命名空间 + /// + public string Namespace { get; set; } + /// + /// 模块标识 + /// + public string ModuleCode { get; set; } + /// + /// 模块名称 + /// + public string ModuleName { get; set; } + /// + /// 代码相对文件夹路径 + /// + public string Folder { get; set; } + /// + /// 其它生成选项 + /// + public string Options { get; set; } + /// + /// 分类ID + /// + public string TypeId { get; set; } + /// + /// 分类名称 + /// + public string TypeName { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateCategoryReq.cs b/OpenAuth.App/Request/AddOrUpdateCategoryReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..f4e6025c2a24c99f77d71f3e5af450ea83eb07b5 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateCategoryReq.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +namespace OpenAuth.App.Request +{ + /// + /// 分类表,也可用作数据字典。表示一个全集,比如:男、女、未知。关联的分类类型表示按什么进行的分类,如:按照性别对人类对象集 + /// + public class AddOrUpdateCategoryReq + { + + public string Id { get; set; } + + /// + /// 分类名称或描述 + /// + public string Name { get; set; } + /// + /// 分类标识 + /// + public string DtCode { get; set; } + /// + /// 通常与分类标识一致,但万一有不一样的情况呢? + /// + public string DtValue { get; set; } + /// + /// + /// + public bool Enable { get; set; } + /// + /// 排序号 + /// + public int SortNo { get; set; } + /// + /// 详细说明,基本没啥用 + /// + public string Description { get; set; } + /// + /// 分类的ID + /// + public string TypeId { get; set; } + + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateCategoryTypeReq.cs b/OpenAuth.App/Request/AddOrUpdateCategoryTypeReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..a2748dd297fae37013f113081a28b7ca1e472bd8 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateCategoryTypeReq.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +using System.ComponentModel.DataAnnotations.Schema; + +namespace OpenAuth.App.Request +{ + /// + /// 分类类型 + /// + [Table("CategoryType")] + public partial class AddOrUpdateCategoryTypeReq + { + + /// + /// 分类表ID + /// + public string Id { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateDataPriviReq.cs b/OpenAuth.App/Request/AddOrUpdateDataPriviReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..93dffc105d89cbb5542bd608a2eec2f9266f0dd0 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateDataPriviReq.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +namespace OpenAuth.App.Request +{ + + public class AddOrUpdateDataPriviReq + { + public string Id { get; set; } + /// + /// 资源标识(模块编号) + /// + public string SourceCode { get; set; } + /// + /// 二级资源标识 + /// + public string SubSourceCode { get; set; } + /// + /// 权限描述 + /// + public string Description { get; set; } + /// + /// 排序号 + /// + public int SortNo { get; set; } + /// + /// 权限规则 + /// + public string PrivilegeRules { get; set; } + /// + /// 是否可用 + /// + public bool Enable { get; set; } + + } +} \ No newline at end of file diff --git a/OpenAuth.Repository/Domain/Stock.cs b/OpenAuth.App/Request/AddOrUpdateOpenJobReq.cs similarity index 35% rename from OpenAuth.Repository/Domain/Stock.cs rename to OpenAuth.App/Request/AddOrUpdateOpenJobReq.cs index 6b41e950c5adeb950e0a001bdf5957f07e4707f0..6ccbc4e8f8588b16e69a9d905261fc7f2ae2a398 100644 --- a/OpenAuth.Repository/Domain/Stock.cs +++ b/OpenAuth.App/Request/AddOrUpdateOpenJobReq.cs @@ -1,67 +1,59 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a CodeSmith Template. -// -// DO NOT MODIFY contents of this file. Changes to this -// file will be lost if the code is regenerated. -// Author:Yubao Li -// -//------------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenAuth.Repository.Domain -{ - /// - /// 出入库信息表 - /// - public partial class Stock : Entity - { - public Stock() - { - this.Name= string.Empty; - this.Number= 0; - this.Price= 0; - this.Status= 0; - this.Viewable= string.Empty; - this.User= string.Empty; - this.Time= DateTime.Now; - this.OrgId= string.Empty; - } - - /// - /// 产品名称 - /// - public string Name { get; set; } - /// - /// 产品数量 - /// - public int Number { get; set; } - /// - /// 产品单价 - /// - public decimal Price { get; set; } - /// - /// 出库/入库 - /// - public int Status { get; set; } - /// - /// 可见范围 - /// - public string Viewable { get; set; } - /// - /// 操作人 - /// - public string User { get; set; } - /// - /// 操作时间 - /// - public System.DateTime Time { get; set; } - /// - /// 组织ID - /// - public string OrgId { get; set; } - - } +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 定时任务 + /// + [Table("OpenJob")] + public partial class AddOrUpdateOpenJobReq + { + + /// + /// Id + /// + public string Id { get; set; } + /// + /// 任务名称 + /// + public string JobName { get; set; } + /// + /// 任务执行方式0:本地任务;1:外部接口任务 + /// + public int JobType { get; set; } + /// + /// 任务地址 + /// + public string JobCall { get; set; } + /// + /// 任务参数,JSON格式 + /// + public string JobCallParams { get; set; } + /// + /// CRON表达式 + /// + public string Cron { get; set; } + /// + /// 任务运行状态(0:停止,1:正在运行,2:暂停) + /// + public int Status { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + } } \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateResReq.cs b/OpenAuth.App/Request/AddOrUpdateResReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..fbf59daece96a50c3f44d72b5d225d1046c43cfb --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateResReq.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + + namespace OpenAuth.Repository.Domain +{ + /// + /// 资源表 + /// + public class AddOrUpdateResReq + { + public string Id { get; set; } + /// + /// 节点语义ID + /// + public string CascadeId { get; set; } + /// + /// 名称 + /// + public string Name { get; set; } + /// + /// 排序号 + /// + public int SortNo { get; set; } + /// + /// 描述 + /// + public string Description { get; set; } + /// + /// 父节点名称 + /// + public string ParentName { get; set; } + /// + /// 父节点流ID + /// + public string ParentId { get; set; } + /// + /// 资源所属应用ID + /// + public string AppId { get; set; } + /// + /// 所属应用名称 + /// + public string AppName { get; set; } + /// + /// 分类名称 + /// + public string TypeName { get; set; } + /// + /// 分类ID + /// + public string TypeId { get; set; } + /// + /// 是否可用 + /// + public bool Disable { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateWmsInboundOrderDtblReq.cs b/OpenAuth.App/Request/AddOrUpdateWmsInboundOrderDtblReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..b0d64ef1d174b96493d5d986d11b677b3665d7d0 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateWmsInboundOrderDtblReq.cs @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +using System.ComponentModel.DataAnnotations.Schema; + +namespace OpenAuth.App.Request +{ + /// + /// 入库通知单明细 + /// + [Table("WmsInboundOrderDtbl")] + public partial class AddOrUpdateWmsInboundOrderDtblReq + { + + /// + /// 入库通知单明细号 + /// + public string Id { get; set; } + /// + /// 入库通知单号 + /// + public string OrderId { get; set; } + /// + /// 含税单价 + /// + public decimal? Price { get; set; } + /// + /// 无税单价 + /// + public decimal? PriceNoTax { get; set; } + /// + /// 是否收货中(0:非收货中,1:收货中) + /// + public bool InStockStatus { get; set; } + /// + /// 到货状况(SYS_GOODSARRIVESTATUS) + /// + public int AsnStatus { get; set; } + /// + /// 商品编号 + /// + public string GoodsId { get; set; } + /// + /// 商品批号 + /// + public string GoodsBatch { get; set; } + /// + /// 品质(SYS_QUALITYFLAG) + /// + public string QualityFlg { get; set; } + /// + /// 通知数量 + /// + public decimal OrderNum { get; set; } + /// + /// 到货数量 + /// + public decimal InNum { get; set; } + /// + /// 剩余数量 + /// + public decimal LeaveNum { get; set; } + /// + /// 占用数量 + /// + public decimal HoldNum { get; set; } + /// + /// 生产日期 + /// + public string ProdDate { get; set; } + /// + /// 失效日期 + /// + public string ExpireDate { get; set; } + /// + /// 税率 + /// + public decimal? TaxRate { get; set; } + /// + /// 货主编号 + /// + public string OwnerId { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AddOrUpdateWmsInboundOrderTblReq.cs b/OpenAuth.App/Request/AddOrUpdateWmsInboundOrderTblReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..370b1f3c47e998fc323dabe393eea6a4e3071ab7 --- /dev/null +++ b/OpenAuth.App/Request/AddOrUpdateWmsInboundOrderTblReq.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ + +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace OpenAuth.App.Request +{ + /// + /// 入库通知单(入库订单) + /// + [Table("WmsInboundOrderTbl")] + public partial class AddOrUpdateWmsInboundOrderTblReq + { + + /// + /// 入库通知单号 + /// + public string Id { get; set; } + /// + /// 相关单据号 + /// + public string ExternalNo { get; set; } + /// + /// 相关单据类型 + /// + public string ExternalType { get; set; } + /// + /// 入库通知单状态(SYS_INSTCINFORMSTATUS) + /// + public int Status { get; set; } + /// + /// 入库类型(SYS_INSTCTYPE) + /// + public string OrderType { get; set; } + /// + /// 商品类别 + /// + public string GoodsType { get; set; } + /// + /// 采购单号 + /// + public string PurchaseNo { get; set; } + /// + /// 仓库编号 + /// + public string StockId { get; set; } + /// + /// 货主编号(固定值CQM) + /// + public string OwnerId { get; set; } + /// + /// 承运人编号 + /// + public string ShipperId { get; set; } + /// + /// 供应商编号 + /// + public string SupplierId { get; set; } + /// + /// 预定入库时间 + /// + public System.DateTime? ScheduledInboundTime { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + /// + /// 有效标志 + /// + public bool Enable { get; set; } + /// + /// 承运方式 + /// + public string TransferType { get; set; } + /// + /// 是否入保税库(0:否,1:是) + /// + public bool InBondedArea { get; set; } + /// + /// 销退箱数 + /// + public decimal ReturnBoxNum { get; set; } + + /// + /// 所属部门 + /// + public string OrgId { get; set; } + + public List WmsInboundOrderDtblReqs { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AssignDataReq.cs b/OpenAuth.App/Request/AssignDataReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..f19c83ffd0404641d6742a0d061818c3eb289185 --- /dev/null +++ b/OpenAuth.App/Request/AssignDataReq.cs @@ -0,0 +1,21 @@ +namespace OpenAuth.App.Request +{ + /// + /// 为角色分配数据字段权限 + /// + public class AssignDataReq + { + /// + /// 角色ID + /// + public string RoleId { get; set; } + /// + /// 模块的Code,比如Category/Resource + /// + public string ModuleCode { get; set; } + /// + /// 字段名称列表 + /// + public string[] Properties { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AssignOrgUsers.cs b/OpenAuth.App/Request/AssignOrgUsers.cs new file mode 100644 index 0000000000000000000000000000000000000000..cf0c5bcfa0acfd68d384067bea5ad4c1e1cef7b8 --- /dev/null +++ b/OpenAuth.App/Request/AssignOrgUsers.cs @@ -0,0 +1,17 @@ +namespace OpenAuth.App.Request +{ + /// + /// 部门分配用户 + /// + public class AssignOrgUsers + { + /// + /// 部门id + /// + public string OrgId { get; set; } + /// + /// 用户id列表 + /// + public string[] UserIds { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AssignReq.cs b/OpenAuth.App/Request/AssignReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..fff707d6f30622018d3aaa1fed9aa29849c93935 --- /dev/null +++ b/OpenAuth.App/Request/AssignReq.cs @@ -0,0 +1,18 @@ +namespace OpenAuth.App.Request +{ + /// + /// 比如给用户分配资源,那么firstId就是用户ID,secIds就是资源ID列表 + /// + public class AssignReq + { + /// + /// 分配的关键字,比如:UserRole + /// + public string type { get; set; } + /// + /// 比如给用户分配角色,那么firstId就是用户ID,secIds就是角色ID列表 + /// + public string firstId { get; set; } + public string[] secIds { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/AssignRoleUsers.cs b/OpenAuth.App/Request/AssignRoleUsers.cs new file mode 100644 index 0000000000000000000000000000000000000000..e1fa8f7d84fe4356e3483b8774444f4bd10ee5db --- /dev/null +++ b/OpenAuth.App/Request/AssignRoleUsers.cs @@ -0,0 +1,17 @@ +namespace OpenAuth.App.Request +{ + /// + /// 角色分配用户 + /// + public class AssignRoleUsers + { + /// + /// 角色id + /// + public string RoleId { get; set; } + /// + /// 用户id列表 + /// + public string[] UserIds { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/ChangeJobStatusReq.cs b/OpenAuth.App/Request/ChangeJobStatusReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..c63ac55641f5310e600932c83a2c2bb92d1aa13f --- /dev/null +++ b/OpenAuth.App/Request/ChangeJobStatusReq.cs @@ -0,0 +1,16 @@ +namespace OpenAuth.App.Request +{ + public class ChangeJobStatusReq + { + /// + /// 任务ID + /// + public string Id { get; set; } + + /// + /// 改变任务状态 + /// 0:停止;1:启动(任务变成正在运行) + /// + public int Status { get; set; } + } +} diff --git a/OpenAuth.App/Request/ChangePasswordReq.cs b/OpenAuth.App/Request/ChangePasswordReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..892a5e949160b0e6fde96feec6c9895a2316b08f --- /dev/null +++ b/OpenAuth.App/Request/ChangePasswordReq.cs @@ -0,0 +1,8 @@ +namespace OpenAuth.App.Request +{ + public class ChangePasswordReq + { + public string Account { get; set; } + public string Password { get; set; } + } +} diff --git a/OpenAuth.App/Request/ChangeProfileReq.cs b/OpenAuth.App/Request/ChangeProfileReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..065f8c1c79f6c5d90370eb64044c8eabb98cb0fe --- /dev/null +++ b/OpenAuth.App/Request/ChangeProfileReq.cs @@ -0,0 +1,16 @@ +namespace OpenAuth.App.Request +{ + public class ChangeProfileReq + { + public string Account { get; set; } + + /// + /// 用户姓名 + /// + public string Name { get; set; } + /// + /// 性别 + /// + public int Sex { get; set; } + } +} diff --git a/OpenAuth.Repository/Mapping/CategoryTypeMap.cs b/OpenAuth.App/Request/CreateBusiReq.cs similarity index 32% rename from OpenAuth.Repository/Mapping/CategoryTypeMap.cs rename to OpenAuth.App/Request/CreateBusiReq.cs index d5dc7e915d9ac17d410e925d2b0dd2cfd184e132..77eb2f53e4fe492ab434eb65cecb04818bbea516 100644 --- a/OpenAuth.Repository/Mapping/CategoryTypeMap.cs +++ b/OpenAuth.App/Request/CreateBusiReq.cs @@ -1,42 +1,29 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a CodeSmith Template. -// -// DO NOT MODIFY contents of this file. Changes to this -// file will be lost if the code is regenerated. -// -//------------------------------------------------------------------------------ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace OpenAuth.Repository.Mapping -{ - public partial class CategoryTypeMap - : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration - { - public CategoryTypeMap() - { - // table - ToTable("CategoryType", "dbo"); - - // keys - HasKey(t => t.Id); - - // Properties - Property(t => t.Id) - .HasColumnName("Id") - .HasMaxLength(50) - .IsRequired(); - Property(t => t.Name) - .HasColumnName("Name") - .HasMaxLength(255) - .IsRequired(); - Property(t => t.CreateTime) - .HasColumnName("CreateTime") - .IsRequired(); - - // Relationships - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 代码生成器的表信息 + /// + public class CreateBusiReq + { + /// + /// 代码生成模版Id + /// + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/CreateEntityReq.cs b/OpenAuth.App/Request/CreateEntityReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..0b1e6a73f05bab54c474b499501e1cf9d836251a --- /dev/null +++ b/OpenAuth.App/Request/CreateEntityReq.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 代码生成器的表信息 + /// + public class CreateEntityReq + { + /// + /// 代码生成模版Id + /// + public string Id { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/CreateVueReq.cs b/OpenAuth.App/Request/CreateVueReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..da86006bf3774fb25edd3fe53e186703f7cd8326 --- /dev/null +++ b/OpenAuth.App/Request/CreateVueReq.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a CodeSmith Template. +// +// DO NOT MODIFY contents of this file. Changes to this +// file will be lost if the code is regenerated. +// Author:Yubao Li +// +//------------------------------------------------------------------------------ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text; +using OpenAuth.Repository.Core; + +namespace OpenAuth.App.Request +{ + /// + /// 代码生成器的表信息 + /// + public class CreateVueReq + { + /// + /// 代码生成模版Id + /// + public string Id { get; set; } + /// + /// vue项目所在的根目录,如:D:\OpenAuth.Pro\Client + /// + public string VueProjRootPath { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryBuilderTableColumnListReq.cs b/OpenAuth.App/Request/QueryBuilderTableColumnListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..ec6c354189f9edabebdd9ec34aef58320288f764 --- /dev/null +++ b/OpenAuth.App/Request/QueryBuilderTableColumnListReq.cs @@ -0,0 +1,10 @@ +namespace OpenAuth.App.Request +{ + public class QueryBuilderTableColumnListReq : PageReq + { + /// + /// 头表ID + /// + public string BuilderTableId { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryBuilderTableListReq.cs b/OpenAuth.App/Request/QueryBuilderTableListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..3da703e3d116699d6845f91872004333edad016d --- /dev/null +++ b/OpenAuth.App/Request/QueryBuilderTableListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryBuilderTableListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryCategoriesReq.cs b/OpenAuth.App/Request/QueryCategoryListReq.cs similarity index 40% rename from OpenAuth.App/Request/QueryCategoriesReq.cs rename to OpenAuth.App/Request/QueryCategoryListReq.cs index e4804213decfd9409c8a120dd0e527cde7412a7f..538e63e999a6b8d7051282fdfcaaf44762e8daad 100644 --- a/OpenAuth.App/Request/QueryCategoriesReq.cs +++ b/OpenAuth.App/Request/QueryCategoryListReq.cs @@ -1,16 +1,11 @@ -namespace OpenAuth.App.Request -{ - public class QueryCategoriesReq : PageReq - { - /// - /// 是否审核,all:全部; true:已审核;false:未审核 - /// - public string Checked { get; set; } - - /// - /// TypeID - /// - public string TypeId { get; set; } - - } -} +namespace OpenAuth.App.Request +{ + public class QueryCategoryListReq : PageReq + { + + /// + /// TypeID + /// + public string TypeId { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryCategoryTypeListReq.cs b/OpenAuth.App/Request/QueryCategoryTypeListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..1f27d06f53e0b18be44b2c0fbee9f37be982b0f3 --- /dev/null +++ b/OpenAuth.App/Request/QueryCategoryTypeListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryCategoryTypeListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryDataPrivilegeRuleListReq.cs b/OpenAuth.App/Request/QueryDataPrivilegeRuleListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..da93b4729cffeab99072546dac69a6132e51d231 --- /dev/null +++ b/OpenAuth.App/Request/QueryDataPrivilegeRuleListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryDataPrivilegeRuleListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryFlowInstanceHistoryReq.cs b/OpenAuth.App/Request/QueryFlowInstanceHistoryReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..c207d02948d1e941c9f8d633cb02e11ea153537b --- /dev/null +++ b/OpenAuth.App/Request/QueryFlowInstanceHistoryReq.cs @@ -0,0 +1,11 @@ +namespace OpenAuth.App.Request +{ + public class QueryFlowInstanceHistoryReq : PageReq + { + /// + /// 流程实体名称 + /// + public string FlowInstanceId { get; set; } + + } +} diff --git a/OpenAuth.App/Request/QueryFrmLeaveReqListReq.cs b/OpenAuth.App/Request/QueryFrmLeaveReqListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..69647d2d4c508e4b8969dac685e97a3106cec1c4 --- /dev/null +++ b/OpenAuth.App/Request/QueryFrmLeaveReqListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryFrmLeaveReqListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryOpenJobListReq.cs b/OpenAuth.App/Request/QueryOpenJobListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..07530014d0b121ff2626c48c6ca87b7db5cdf529 --- /dev/null +++ b/OpenAuth.App/Request/QueryOpenJobListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryOpenJobListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryResourcesReq.cs b/OpenAuth.App/Request/QueryResourcesReq.cs index 11274fd6175f9a5b5ff854949c9991833eb6a501..f6c3a9baf945de89b697b1762248a6ff0272dccb 100644 --- a/OpenAuth.App/Request/QueryResourcesReq.cs +++ b/OpenAuth.App/Request/QueryResourcesReq.cs @@ -5,7 +5,7 @@ /// /// TypeID /// - public string TypeId { get; set; } + public string appId { get; set; } } } diff --git a/OpenAuth.App/Request/QueryRoleListReq.cs b/OpenAuth.App/Request/QueryRoleListReq.cs index 953b9f4f010a8d623b6a7fe77fcea1a1a3c1abc6..65bd9b54e0c8f1a0e5d2a924359f98ae0c3dddf8 100644 --- a/OpenAuth.App/Request/QueryRoleListReq.cs +++ b/OpenAuth.App/Request/QueryRoleListReq.cs @@ -2,6 +2,5 @@ { public class QueryRoleListReq : PageReq { - public string orgId { get; set; } } } diff --git a/OpenAuth.App/Request/QuerySysLogListReq.cs b/OpenAuth.App/Request/QuerySysLogListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..a3fa012fe0492d73ec517143da1aae0f405bab39 --- /dev/null +++ b/OpenAuth.App/Request/QuerySysLogListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QuerySysLogListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QuerySysMessageListReq.cs b/OpenAuth.App/Request/QuerySysMessageListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..e7ce4b4a0f3309aa8106a4603d342eeb9a57e52c --- /dev/null +++ b/OpenAuth.App/Request/QuerySysMessageListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QuerySysMessageListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryUserListByOrgReq.cs b/OpenAuth.App/Request/QueryUserListByOrgReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..bf26bfa355f88d43b6258caac1d0b8f007712494 --- /dev/null +++ b/OpenAuth.App/Request/QueryUserListByOrgReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryUserListByOrgReq : PageReq + { + public string orgId { get; set; } + } +} diff --git a/OpenAuth.App/Request/QueryUserListByRoleReq.cs b/OpenAuth.App/Request/QueryUserListByRoleReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..ca5c17c98bdaf0e3f48152081c367afbc487d77d --- /dev/null +++ b/OpenAuth.App/Request/QueryUserListByRoleReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryUserListByRoleReq : PageReq + { + public string roleId { get; set; } + } +} diff --git a/OpenAuth.App/Request/QueryWmsInboundOrderDtblListReq.cs b/OpenAuth.App/Request/QueryWmsInboundOrderDtblListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..b58d44a6bc78611f6ffa7dcf12565f024168063f --- /dev/null +++ b/OpenAuth.App/Request/QueryWmsInboundOrderDtblListReq.cs @@ -0,0 +1,11 @@ +namespace OpenAuth.App.Request +{ + public class QueryWmsInboundOrderDtblListReq : PageReq + { + //todo:添加自己的请求字段 + /// + /// 入库订单号 + /// + public string InboundOrderId { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/QueryWmsInboundOrderTblListReq.cs b/OpenAuth.App/Request/QueryWmsInboundOrderTblListReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..d7f636ea81fcb436a82515d8de9aeb659231d308 --- /dev/null +++ b/OpenAuth.App/Request/QueryWmsInboundOrderTblListReq.cs @@ -0,0 +1,7 @@ +namespace OpenAuth.App.Request +{ + public class QueryWmsInboundOrderTblListReq : PageReq + { + //todo:添加自己的请求字段 + } +} \ No newline at end of file diff --git a/OpenAuth.App/Request/UpdateUserReq.cs b/OpenAuth.App/Request/UpdateUserReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..9e9029ce66141d8ea8e232bbd0d93a0a2950f6f8 --- /dev/null +++ b/OpenAuth.App/Request/UpdateUserReq.cs @@ -0,0 +1,70 @@ +using Infrastructure; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Request +{ + /// + /// 添加或修改用户信息的请求 + /// + public class UpdateUserReq + { + /// + /// 用户ID + /// + /// + public string Id { get; set; } + + + /// + /// + /// + public string Account { get; set; } + + /// + /// + /// + public string Password { get; set; } + + + /// + /// 组织名称 + /// + /// + public string Name { get; set; } + + + /// + /// + /// + public int Sex { get; set; } + + + /// + /// 当前状态 + /// + /// + public int Status { get; set; } + + + /// + /// 所属组织Id,多个可用,分隔 + /// + /// The organizations. + public string OrganizationIds { get; set; } + + public static implicit operator UpdateUserReq(User user) + { + return user.MapTo(); + } + + public static implicit operator User(UpdateUserReq view) + { + return view.MapTo(); + } + + public UpdateUserReq() + { + OrganizationIds = string.Empty; + } + } +} diff --git a/OpenAuth.App/Request/VerificationReq.cs b/OpenAuth.App/Request/VerificationReq.cs index f8f56d997e2af69cd4c526c407b31522f5e11c80..5ef05f436c1c0ea2f11e9986e09c506ffbdfbae8 100644 --- a/OpenAuth.App/Request/VerificationReq.cs +++ b/OpenAuth.App/Request/VerificationReq.cs @@ -17,5 +17,10 @@ /// 驳回的步骤,即驳回到的节点ID /// public string NodeRejectStep { get; set; } + + /// + /// 驳回类型。null:使用节点配置的驳回类型/0:前一步/1:第一步/2:指定节点,使用NodeRejectStep + /// + public string NodeRejectType { get; set; } } } diff --git a/OpenAuth.App/ResourceApp.cs b/OpenAuth.App/ResourceApp.cs index 4108508e3e551cf7de5887b42cc44abe94d08360..464c148168ad076895b83e7dcbb2cc4d8643b512 100644 --- a/OpenAuth.App/ResourceApp.cs +++ b/OpenAuth.App/ResourceApp.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.Response; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { @@ -12,62 +15,79 @@ namespace OpenAuth.App /// public class ResourceApp:BaseApp { - public RevelanceManagerApp RevelanceManagerApp { get; set; } + private RevelanceManagerApp _revelanceApp; - public IEnumerable Get(string type) + public void Add(AddOrUpdateResReq resource) { - return Repository.Find(u => u.TypeId == type); + var obj = resource.MapTo(); + CaculateCascade(obj); + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + Repository.Add(obj); } - public void Add(Resource resource) + public void Update(AddOrUpdateResReq obj) { - if (string.IsNullOrEmpty(resource.Id)) + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new Category { - resource.Id = Guid.NewGuid().ToString(); - } - Repository.Add(resource); + TypeId = obj.TypeId, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:要修改的字段赋值 + }); } - public void Update(Resource resource) + public IEnumerable LoadForRole(string appId, string roleId) { - Repository.Update(u =>u.Id,resource); + var elementIds = _revelanceApp.Get(Define.ROLERESOURCE, true, roleId); + return UnitWork.Find(u => elementIds.Contains(u.Id) && (appId == null || appId =="" || u.AppId == appId)); } - - public IEnumerable LoadForUser(string appId, string userId) + + public TableData Load(QueryResourcesReq request) { - var elementIds = RevelanceManagerApp.Get(Define.USERRESOURCE, true, userId); - return UnitWork.Find(u => elementIds.Contains(u.Id) && (appId == "" || u.AppId == appId)); - } + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } - public IEnumerable LoadForRole(string appId, string userId) - { - var elementIds = RevelanceManagerApp.Get(Define.ROLERESOURCE, true, userId); - return UnitWork.Find(u => elementIds.Contains(u.Id) && (appId =="" || u.AppId == appId)); - } + var properties = loginContext.GetProperties("Resource"); + if (properties == null || properties.Count == 0) + { + throw new Exception("当前登录用户没有访问该模块字段的权限,请联系管理员配置"); + } - public TableData Load(QueryResourcesReq request) - { var result = new TableData(); - var resources = UnitWork.Find(null) ; + var resources = GetDataPrivilege("u"); if (!string.IsNullOrEmpty(request.key)) { resources = resources.Where(u => u.Name.Contains(request.key) || u.Id.Contains(request.key)); } - if (!string.IsNullOrEmpty(request.TypeId)) + if (!string.IsNullOrEmpty(request.appId)) { - resources = resources.Where(u => u.TypeId == request.TypeId); + resources = resources.Where(u => u.AppId == request.appId); } - + var propertyStr = string.Join(',', properties.Select(u => u.Key)); + result.columnHeaders = properties; result.data = resources.OrderBy(u => u.TypeId) .Skip((request.page - 1) * request.limit) - .Take(request.limit).ToList(); + .Take(request.limit).Select($"new ({propertyStr})"); result.count = resources.Count(); return result; } + public ResourceApp(IUnitWork unitWork, IRepository repository + ,RevelanceManagerApp app,IAuth auth) : base(unitWork, repository, auth) + { + _revelanceApp = app; + } } } \ No newline at end of file diff --git a/OpenAuth.App/Response/FlowVerificationResp.cs b/OpenAuth.App/Response/FlowVerificationResp.cs index b209fe4b118a75cc1ad37aa44efb2271d939bd04..c543368b29d10e9ae017118eca37ed5fbd5ec793 100644 --- a/OpenAuth.App/Response/FlowVerificationResp.cs +++ b/OpenAuth.App/Response/FlowVerificationResp.cs @@ -10,7 +10,7 @@ namespace OpenAuth.App.Response /// The FRM data HTML. public string FrmPreviewHtml { - get { return FormUtil.Preview(FrmContentData, FrmContentParse, FrmData); } + get { return FormUtil.Preview(this); } } } } diff --git a/OpenAuth.App/Response/FormResp.cs b/OpenAuth.App/Response/FormResp.cs index 394ce4a5ad119a2625b6eba118ef6cfd18e48102..113da963be6bc3f65813c0e93d19774a140f78e9 100644 --- a/OpenAuth.App/Response/FormResp.cs +++ b/OpenAuth.App/Response/FormResp.cs @@ -15,6 +15,11 @@ namespace OpenAuth.App.Response ///
public class FormResp { + /// + /// 表单名称 + /// + public string Id { get; set; } + /// /// 表单名称 /// @@ -23,6 +28,14 @@ namespace OpenAuth.App.Response /// 字段个数 ///
public int Fields { get; set; } + /// + /// 表单类型,0:默认动态表单;1:Web自定义表单 + /// + public int FrmType { get; set; } + /// + /// 系统页面标识,当表单类型为用Web自定义的表单时,需要标识加载哪个页面 + /// + public string WebId { get; set; } /// /// 表单中的字段数据 /// diff --git a/OpenAuth.App/Response/ModuleView.cs b/OpenAuth.App/Response/ModuleView.cs index 32ba63de07a4c16f1ae07079b58a4ed0acdf272b..a13b7f66cfac9f7bd9824254af879d81d66bc2fe 100644 --- a/OpenAuth.App/Response/ModuleView.cs +++ b/OpenAuth.App/Response/ModuleView.cs @@ -48,6 +48,12 @@ namespace OpenAuth.App.Response public string IconName { get; set; } + /// + /// 当前状态,0:正常,-1:隐藏,不在导航列表中显示 + /// + public int Status { get; set; } + + public bool Checked { get; set; } /// @@ -57,10 +63,12 @@ namespace OpenAuth.App.Response public string Code { get; set; } + public bool IsSys { get; set; } + /// /// 模块中的元素 /// - public List Elements = new List(); + public List Elements { get; set; } public static implicit operator ModuleView(Module module) { diff --git a/OpenAuth.App/Response/RoleView.cs b/OpenAuth.App/Response/RoleView.cs index 5ea3c5ce0f7f55ce485fd7ccf00d73ee88d29d1e..e82e6412fb8066a04e370c65fc2d2fe2dc2358d0 100644 --- a/OpenAuth.App/Response/RoleView.cs +++ b/OpenAuth.App/Response/RoleView.cs @@ -41,16 +41,6 @@ namespace OpenAuth.App.Response public int Type { get; set; } - /// - /// 所属组织名称,多个可用,分隔 - /// - public string Organizations { get; set; } - - /// - /// 所属组织ID,多个可用,分隔 - /// - public string OrganizationIds { get; set; } - /// ///是否属于某用户 /// diff --git a/OpenAuth.App/Response/TableData.cs b/OpenAuth.App/Response/TableData.cs index 3f88f7cb2d5c3def01c63bb095321dbf16cc81f5..2ebd7f4d8b813b6c06eb673858c873592470e7d1 100644 --- a/OpenAuth.App/Response/TableData.cs +++ b/OpenAuth.App/Response/TableData.cs @@ -12,6 +12,9 @@ // layui datatable数据返回 // *********************************************************************** +using System.Collections.Generic; +using Infrastructure; + namespace OpenAuth.App.Response { /// @@ -22,20 +25,32 @@ namespace OpenAuth.App.Response /// /// 状态码 /// - public int code; + public int code { get; set; } /// /// 操作消息 /// - public string msg; + public string msg { get; set; } /// /// 总记录条数 /// - public int count; + public int count { get; set; } + + /// + /// 返回的列表头信息 + /// + public List columnHeaders; /// /// 数据内容 /// - public dynamic data; + public dynamic data { get; set; } + + public TableData() + { + code = 200; + msg = "加载成功"; + columnHeaders = new List(); + } } } \ No newline at end of file diff --git a/OpenAuth.App/Response/TableResp.cs b/OpenAuth.App/Response/TableResp.cs new file mode 100644 index 0000000000000000000000000000000000000000..08cae99fa9d47d2321e4707236e84983e2307165 --- /dev/null +++ b/OpenAuth.App/Response/TableResp.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using Infrastructure; + +namespace OpenAuth.App.Response +{ + /// + /// 返回确定类型的表格数据,可以为swagger提供精准的注释 + /// + public class TableResp + { + /// + /// 状态码 + /// + public int code { get; set; } + /// + /// 操作消息 + /// + public string msg { get; set; } + + /// + /// 总记录条数 + /// + public int count { get; set; } + + /// + /// 数据内容 + /// + public List data { get; set; } + + /// + /// 返回的列表头信息 + /// + public List columnHeaders { get; set; } + + public TableResp() + { + code = 200; + msg = "加载成功"; + columnHeaders = new List(); + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/RevelanceManagerApp.cs b/OpenAuth.App/RevelanceManagerApp.cs index ebbcef8b78fc305d367bb20b962494e27d66fc27..4b909ec94752737739de491fc2ed61939a95a40e 100644 --- a/OpenAuth.App/RevelanceManagerApp.cs +++ b/OpenAuth.App/RevelanceManagerApp.cs @@ -1,26 +1,36 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { public class RevelanceManagerApp :BaseApp { + public RevelanceManagerApp(IUnitWork unitWork, IRepository repository,IAuth auth) : base(unitWork, repository, auth) + { + } /// /// 添加关联 /// 比如给用户分配资源,那么firstId就是用户ID,secIds就是资源ID列表 /// /// 关联的类型,如Define.USERRESOURCE - public void Assign(string type, string firstId, string[] secIds) + public void Assign(AssignReq request) { - Assign(type, secIds.ToLookup(u => firstId)); + Assign(request.type, request.secIds.ToLookup(u => request.firstId)); } + /// + /// 添加关联,需要人工删除以前的关联 + /// + /// + /// public void Assign(string key, ILookup idMaps) { - DeleteBy(key, idMaps); UnitWork.BatchAdd((from sameVals in idMaps from value in sameVals select new Relevance @@ -33,14 +43,30 @@ namespace OpenAuth.App UnitWork.Save(); } - + /// + /// 取消关联 + /// + /// 关联的类型,如Define.USERRESOURCE + /// The first identifier. + /// The sec ids. + public void UnAssign(AssignReq req) + { + if (req.secIds == null || req.secIds.Length == 0) + { + DeleteBy(req.type, req.firstId); + } + else + { + DeleteBy(req.type, req.secIds.ToLookup(u => req.firstId)); + } + } /// /// 删除关联 /// /// 关联标识 /// 关联的<firstId, secondId>数组 - public void DeleteBy(string key, ILookup idMaps) + private void DeleteBy(string key, ILookup idMaps) { foreach (var sameVals in idMaps) { @@ -51,41 +77,11 @@ namespace OpenAuth.App } } - /// - /// 取消关联 - /// - /// 关联的类型,如Define.USERRESOURCE - /// The first identifier. - /// The sec ids. - public void UnAssign(string type, string firstId, string[] secIds) - { - DeleteBy(type, secIds.ToLookup(u =>firstId)); - } - public void DeleteBy(string key, params string[] firstIds) { Repository.Delete(u => firstIds.Contains(u.FirstId) && u.Key == key); } - /// - /// 添加新的关联 - /// - /// 关联标识 - /// 关联的<firstId, secondId>数组 - public void AddRelevance(string key, ILookup idMaps) - { - DeleteBy(key, idMaps); - UnitWork.BatchAdd((from sameVals in idMaps - from value in sameVals - select new Relevance - { - Key = key, - FirstId = sameVals.Key, - SecondId = value, - OperateTime = DateTime.Now - }).ToArray()); - UnitWork.Save(); - } /// /// 根据关联表的一个键获取另外键的值 @@ -107,5 +103,109 @@ namespace OpenAuth.App && ids.Contains(u.SecondId)).Select(u => u.FirstId).ToList(); } } + + /// + /// 根据key ,firstId,secondId获取thirdId + /// + /// + /// + /// + /// + public List Get(string key, string firstId, string secondId) + { + return Repository.Find(u => u.Key == key && u.FirstId == firstId && u.SecondId == secondId) + .Select(u => u.ThirdId).ToList(); + } + + /// + /// 分配数据字段权限 + /// + /// + public void AssignData(AssignDataReq request) + { + var relevances = new List(); + foreach (var requestProperty in request.Properties) + { + relevances.Add(new Relevance + { + Key = Define.ROLEDATAPROPERTY, + FirstId = request.RoleId, + SecondId = request.ModuleCode, + ThirdId = requestProperty, + OperateTime = DateTime.Now + }); + } + UnitWork.BatchAdd(relevances.ToArray()); + UnitWork.Save(); + } + + /// + /// 取消数据字段分配 + /// + /// + public void UnAssignData(AssignDataReq request) + { + if (request.Properties == null || request.Properties.Length == 0) + { + if (string.IsNullOrEmpty(request.ModuleCode)) //模块为空,直接把角色的所有授权删除 + { + DeleteBy(Define.ROLEDATAPROPERTY, request.RoleId); + } + else //把角色的某一个模块权限全部删除 + { + DeleteBy(Define.ROLEDATAPROPERTY, new []{ request.ModuleCode }.ToLookup(u =>request.RoleId)); + } + } + else //按具体的id删除 + { + foreach (var property in request.Properties) + { + Repository.Delete(u => u.Key == Define.ROLEDATAPROPERTY + && u.FirstId == request.RoleId + && u.SecondId == request.ModuleCode + && u.ThirdId == property); + } + } + } + + /// + /// 为角色分配用户,需要统一提交,会删除以前该角色的所有用户 + /// + /// + public void AssignRoleUsers(AssignRoleUsers request) + { + //删除以前的所有用户 + UnitWork.Delete(u => u.SecondId == request.RoleId && u.Key == Define.USERROLE); + //批量分配用户角色 + UnitWork.BatchAdd((from firstId in request.UserIds + select new Relevance + { + Key = Define.USERROLE, + FirstId = firstId, + SecondId = request.RoleId, + OperateTime = DateTime.Now + }).ToArray()); + UnitWork.Save(); + } + + /// + /// 为部门分配用户,需要统一提交,会删除以前该部门的所有用户 + /// + /// + public void AssignOrgUsers(AssignOrgUsers request) + { + //删除以前的所有用户 + UnitWork.Delete(u => u.SecondId == request.OrgId && u.Key == Define.USERORG); + //批量分配用户角色 + UnitWork.BatchAdd((from firstId in request.UserIds + select new Relevance + { + Key = Define.USERORG, + FirstId = firstId, + SecondId = request.OrgId, + OperateTime = DateTime.Now + }).ToArray()); + UnitWork.Save(); + } } } \ No newline at end of file diff --git a/OpenAuth.App/RoleApp.cs b/OpenAuth.App/RoleApp.cs index e58ada0f2a86f9a593ac7eef90b751f26c562842..e7b23f186b549f4f28bd932e929738e8a71c0669 100644 --- a/OpenAuth.App/RoleApp.cs +++ b/OpenAuth.App/RoleApp.cs @@ -1,89 +1,59 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; -using OpenAuth.App.Request; +using OpenAuth.App.Interface; using OpenAuth.App.Response; -using OpenAuth.App.SSO; using OpenAuth.Repository.Domain; - +using OpenAuth.Repository.Interface; +using System.Linq; +using OpenAuth.App.Request; namespace OpenAuth.App { public class RoleApp : BaseApp { - public RevelanceManagerApp ReleManagerApp { get; set; } + private RevelanceManagerApp _revelanceApp; /// - /// 加载当前登录用户可访问的一个部门及子部门全部角色 + /// 加载当前登录用户可访问的全部角色 /// - public TableData Load(QueryRoleListReq request) + public List Load(QueryRoleListReq request) { - var loginUser = AuthUtil.GetCurrentUser(); - - string cascadeId = ".0."; - if (!string.IsNullOrEmpty(request.orgId)) - { - var org = loginUser.Orgs.SingleOrDefault(u => u.Id == request.orgId); - cascadeId = org.CascadeId; - } - - var ids = loginUser.Orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray(); - var roleIds = ReleManagerApp.Get(Define.ROLEORG, false, ids); - - var roles = UnitWork.Find(u => roleIds.Contains(u.Id)) - .OrderBy(u => u.Name) - .Skip((request.page - 1) * request.limit) - .Take(request.limit); - - var records = Repository.GetCount(u => roleIds.Contains(u.Id)); - - - var roleViews = new List(); - foreach (var role in roles.ToList()) + var loginUser = _auth.GetCurrentUser(); + var roles = loginUser.Roles; + if (!string.IsNullOrEmpty(request.key)) { - RoleView uv = role; - var orgs = LoadByRole(role.Id); - uv.Organizations = string.Join(",", orgs.Select(u => u.Name).ToList()); - uv.OrganizationIds = string.Join(",", orgs.Select(u => u.Id).ToList()); - roleViews.Add(uv); + roles = roles.Where(u => u.Name.Contains(request.key)).ToList(); } - return new TableData - { - count = records, - data = roleViews, - }; - } - - /// - /// 加载角色的所有机构 - /// - public IEnumerable LoadByRole(string roleId) - { - var result = from userorg in UnitWork.Find(null) - join org in UnitWork.Find(null) on userorg.SecondId equals org.Id - where userorg.FirstId == roleId && userorg.Key == Define.ROLEORG - select org; - return result; + return roles; } public void Add(RoleView obj) { - if (string.IsNullOrEmpty(obj.OrganizationIds)) - throw new Exception("请为角色分配机构"); + Role role = obj; role.CreateTime = DateTime.Now; Repository.Add(role); obj.Id = role.Id; //要把保存后的ID存入view + + //如果当前账号不是SYSTEM,则直接分配 + var loginUser = _auth.GetCurrentUser(); + if (loginUser.User.Account != Define.SYSTEM_USERNAME) + { + _revelanceApp.Assign(new AssignReq + { + type = Define.USERROLE, + firstId = loginUser.User.Id, + secIds = new[] {role.Id} + }); + } + - UpdateRele(obj); } public void Update(RoleView obj) { - if (string.IsNullOrEmpty(obj.OrganizationIds)) - throw new Exception("请为角色分配机构"); Role role = obj; UnitWork.Update(u => u.Id == obj.Id, u => new Role @@ -92,18 +62,13 @@ namespace OpenAuth.App Status = role.Status }); - UpdateRele(obj); } - /// - /// 更新相应的多对多关系 - /// - /// - private void UpdateRele(RoleView view) + + public RoleApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app,IAuth auth) : base(unitWork, repository, auth) { - string[] orgIds = view.OrganizationIds.Split(',').ToArray(); - ReleManagerApp.DeleteBy(Define.ROLEORG, view.Id); - ReleManagerApp.AddRelevance(Define.ROLEORG, orgIds.ToLookup(u => view.Id)); + _revelanceApp = app; } } } \ No newline at end of file diff --git a/OpenAuth.App/SSO/AppInfoService.cs b/OpenAuth.App/SSO/AppInfoService.cs index 8cb7ce9c298ec559ac6211396c1c697ff92de211..683987b8d1c98d7da5f532f1811178fa2365ccf9 100644 --- a/OpenAuth.App/SSO/AppInfoService.cs +++ b/OpenAuth.App/SSO/AppInfoService.cs @@ -1,10 +1,9 @@ using System; using System.Linq; -using Infrastructure.Cache; namespace OpenAuth.App.SSO { - public class AppInfoService : CacheProvider + public class AppInfoService { public AppInfo Get(string appKey) { @@ -21,7 +20,7 @@ namespace OpenAuth.App.SSO IsEnable = true, Remark = "基于DDDLite的权限管理系统", ReturnUrl = "http://localhost:56813", - Title = "OpenAuth.Net", + Title = "OpenAuth.Core", CreateTime = DateTime.Now, }, new AppInfo @@ -31,7 +30,7 @@ namespace OpenAuth.App.SSO IsEnable = true, Remark = "这只是个模拟的测试站点", ReturnUrl = "http://localhost:53050", - Title = "OpenAuth.Net测试站点", + Title = "OpenAuth.Core测试站点", CreateTime = DateTime.Now, } }; diff --git a/OpenAuth.App/SSO/AuthUtil.cs b/OpenAuth.App/SSO/AuthUtil.cs deleted file mode 100644 index 82657dd8cdd61e57ce7715f5cb6f09c968c0d9c7..0000000000000000000000000000000000000000 --- a/OpenAuth.App/SSO/AuthUtil.cs +++ /dev/null @@ -1,182 +0,0 @@ -// *********************************************************************** -// Assembly : OpenAuth.App -// Author : yubaolee -// Created : 07-08-2016 -// -// Last Modified By : yubaolee -// Last Modified On : 07-08-2016 -// Contact : Microsoft -// File: AuthUtil.cs -// *********************************************************************** - - -using System; -using System.Configuration; -using System.Web; -using Infrastructure; -using OpenAuth.App.Response; - -namespace OpenAuth.App.SSO -{ - /// - /// վ¼֤ - /// ¼ʱ - /// - /// var result = AuthUtil.Login(AppKey, username, password); - /// if (result.Success) - /// return Redirect("/home/index?Token=" + result.Token); - /// - /// - public class AuthUtil - { - static HttpHelper _helper = new HttpHelper(ConfigurationManager.AppSettings["SSOPassport"]); - - private static string GetToken() - { - string token = HttpContext.Current.Request.QueryString["Token"]; - if (!String.IsNullOrEmpty(token)) return token; - - var cookie = HttpContext.Current.Request.Cookies["Token"]; - return cookie == null ? String.Empty : cookie.Value; - } - - public static bool CheckLogin(string token, string remark = "") - { - if (String.IsNullOrEmpty(token) || String.IsNullOrEmpty(GetToken())) - return false; - - var requestUri = String.Format("/api/Check/GetStatus?token={0}&requestid={1}", token, remark); - - try - { - var value = _helper.Get(null, requestUri); - var result = JsonHelper.Instance.Deserialize>(value); - if (result.Code == 200) - { - return result.Result; - } - throw new Exception(result.Message); - } - catch (Exception ex) - { - throw ex; - } - } - - /// - /// û¼״̬ - /// ͨURLеTokenCookieеToken - /// - /// עϢ - public static bool CheckLogin(string remark="") - { - return CheckLogin(GetToken(), remark); - } - - /// - /// ȡǰ¼ûϢ - /// ͨURLеTokenCookieеToken - /// - /// The remark. - /// LoginUserVM. - public static UserWithAccessedCtrls GetCurrentUser(string remark = "") - { - - var requestUri = String.Format("/api/Check/GetUser?token={0}&requestid={1}", GetToken(), remark); - - try - { - var value = _helper.Get(null, requestUri); - var result = JsonHelper.Instance.Deserialize>(value); - if (result.Code == 200) - { - return result.Result; - } - throw new Exception(result.Message); - } - catch (Exception ex) - { - throw ex; - } - } - - - /// - /// ȡǰ¼û - /// ͨURLеTokenCookieеToken - /// - /// The remark. - /// System.String. - public static string GetUserName(string remark = "") - { - var requestUri = String.Format("/api/Check/GetUserName?token={0}&requestid={1}", GetToken(), remark); - - try - { - var value = _helper.Get(null, requestUri); - var result = JsonHelper.Instance.Deserialize>(value); - if (result.Code == 200) - { - return result.Result; - } - throw new Exception(result.Message); - } - catch (Exception ex) - { - throw ex; - } - } - - /// - /// ¼ӿ - /// - /// Ӧókey. - /// û - /// - /// System.String. - public static LoginResult Login(string appKey, string username, string pwd) - { - var requestUri = "/api/Check/Login"; - - try - { - var value = _helper.Post(new - { - AppKey = appKey, - Account = username, - Password = pwd - }, requestUri); - - var result = JsonHelper.Instance.Deserialize(value); - return result; - - } - catch (Exception ex) - { - return null; - } - } - - /// - /// ע - /// - public static bool Logout() - { - var token = GetToken(); - if (String.IsNullOrEmpty(token)) return true; - - var requestUri = String.Format("/api/Check/Logout?token={0}&requestid={1}", token, ""); - - try - { - var value = _helper.Post(null, requestUri); - - return true; - } - catch (Exception ex) - { - return false; - } - } - } -} \ No newline at end of file diff --git a/OpenAuth.App/SSO/LocalAuth.cs b/OpenAuth.App/SSO/LocalAuth.cs new file mode 100644 index 0000000000000000000000000000000000000000..4697dfc0005dd5d0f9cfc770fd9dcdca96318bbe --- /dev/null +++ b/OpenAuth.App/SSO/LocalAuth.cs @@ -0,0 +1,184 @@ +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using OpenAuth.App.Interface; +using System; +using Microsoft.Extensions.Options; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.SSO +{ + /// + /// 使用本地登录。这个注入IAuth时,只需要OpenAuth.Mvc一个项目即可,无需webapi的支持 + /// + public class LocalAuth : IAuth + { + private IHttpContextAccessor _httpContextAccessor; + private IOptions _appConfiguration; + private SysLogApp _logApp; + + private AuthContextFactory _app; + private LoginParse _loginParse; + private ICacheContext _cacheContext; + + public LocalAuth(IHttpContextAccessor httpContextAccessor + , AuthContextFactory app + , LoginParse loginParse + , ICacheContext cacheContext, IOptions appConfiguration, SysLogApp logApp) + { + _httpContextAccessor = httpContextAccessor; + _app = app; + _loginParse = loginParse; + _cacheContext = cacheContext; + _appConfiguration = appConfiguration; + _logApp = logApp; + } + + /// + /// 如果是Identity,则返回信息为用户账号 + /// + /// + private string GetToken() + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _httpContextAccessor.HttpContext.User.Identity.Name; + } + string token = _httpContextAccessor.HttpContext.Request.Query[Define.TOKEN_NAME]; + if (!String.IsNullOrEmpty(token)) return token; + + token = _httpContextAccessor.HttpContext.Request.Headers[Define.TOKEN_NAME]; + if (!String.IsNullOrEmpty(token)) return token; + + var cookie = _httpContextAccessor.HttpContext.Request.Cookies[Define.TOKEN_NAME]; + return cookie ?? String.Empty; + } + + public bool CheckLogin(string token = "", string otherInfo = "") + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return (!string.IsNullOrEmpty(_httpContextAccessor.HttpContext.User.Identity.Name)); + } + + if (string.IsNullOrEmpty(token)) + { + token = GetToken(); + } + + if (string.IsNullOrEmpty(token)) + { + return false; + } + + try + { + var result = _cacheContext.Get(token) != null; + return result; + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// 获取当前登录的用户信息 + /// 通过URL中的Token参数或Cookie中的Token + /// + /// The account. + /// LoginUserVM. + public AuthStrategyContext GetCurrentUser() + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _app.GetAuthStrategyContext(GetToken()); + } + AuthStrategyContext context = null; + var user = _cacheContext.Get(GetToken()); + if (user != null) + { + context = _app.GetAuthStrategyContext(user.Account); + } + return context; + } + + /// + /// 获取当前登录的用户名 + /// 通过URL中的Token参数或Cookie中的Token + /// + /// The account. + /// System.String. + public string GetUserName(string otherInfo = "") + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return _httpContextAccessor.HttpContext.User.Identity.Name; + } + + var user = _cacheContext.Get(GetToken()); + if (user != null) + { + return user.Account; + } + + return ""; + } + + /// + /// 登录接口 + /// + /// 应用程序key. + /// 用户名 + /// 密码 + /// System.String. + public LoginResult Login(string appKey, string username, string pwd) + { + if (_appConfiguration.Value.IsIdentityAuth) + { + return new LoginResult + { + Code = 500, + Message = "接口启动了OAuth认证,暂时不能使用该方式登录" + }; + } + + var result = _loginParse.Do(new PassportLoginRequest + { + AppKey = appKey, + Account = username, + Password = pwd + }); + + var log = new SysLog + { + Content = $"用户登录,结果:{result.Message}", + Result = result.Code == 200 ? 0 : 1, + CreateId = username, + CreateName = username, + TypeName = "登录日志" + }; + _logApp.Add(log); + + return result; + } + + /// + /// 注销,如果是Identity登录,需要在controller处理注销逻辑 + /// + public bool Logout() + { + var token = GetToken(); + if (String.IsNullOrEmpty(token)) return true; + + try + { + _cacheContext.Remove(token); + return true; + } + catch (Exception ex) + { + return false; + } + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/SSO/SSOAuthUtil.cs b/OpenAuth.App/SSO/LoginParse.cs similarity index 55% rename from OpenAuth.App/SSO/SSOAuthUtil.cs rename to OpenAuth.App/SSO/LoginParse.cs index 2e18cc07a75090df509adbfb6839e77aecb6322a..318e315fefc9aa394dcc7092dec6a9efeed27303 100644 --- a/OpenAuth.App/SSO/SSOAuthUtil.cs +++ b/OpenAuth.App/SSO/LoginParse.cs @@ -1,77 +1,92 @@ -using System; -using System.Web; -using System.Web.Mvc; -using Infrastructure.Cache; -using OpenAuth.Repository.Domain; - - -namespace OpenAuth.App.SSO -{ - public class SSOAuthUtil - { - public static LoginResult Parse(PassportLoginRequest model) - { - var result = new LoginResult(); - try - { - model.Trim(); - //获取应用信息 - var appInfo = new AppInfoService().Get(model.AppKey); - if (appInfo == null) - { - throw new Exception("应用不存在"); - } - //获取用户信息 - User userInfo = null; - if (model.Account == "System") - { - userInfo = new User - { - Id = Guid.Empty.ToString(), //TODO:可以根据需要调整 - Account = "System", - Name ="超级管理员", - Password = "123456" - }; - } - else - { - var usermanager = (UserManagerApp)DependencyResolver.Current.GetService(typeof(UserManagerApp)); - userInfo = usermanager.Get(model.Account); - } - - if (userInfo == null) - { - throw new Exception("用户不存在"); - } - if (userInfo.Password != model.Password) - { - throw new Exception("密码错误"); - } - - var currentSession = new UserAuthSession - { - Account = model.Account, - Name = userInfo.Name, - Token = Guid.NewGuid().ToString().GetHashCode().ToString("x"), - AppKey = model.AppKey, - CreateTime = DateTime.Now, - IpAddress = HttpContext.Current.Request.UserHostAddress - }; - - //创建Session - new ObjCacheProvider().Create(currentSession.Token, currentSession, DateTime.Now.AddDays(10)); - - result.Code = 200; - result.ReturnUrl = appInfo.ReturnUrl; - result.Token = currentSession.Token; - } - catch (Exception ex) - { - result.Code = 500; - result.Message = ex.Message; - } - - return result; - } - } +/* + * 登录解析 + * 处理登录逻辑,验证客户段提交的账号密码,保存登录信息 + */ +using System; +using Infrastructure.Cache; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App.SSO +{ + public class LoginParse + { + + //这个地方使用IRepository 而不使用UserManagerApp是防止循环依赖 + public IRepository _app; + private ICacheContext _cacheContext; + private AppInfoService _appInfoService; + + public LoginParse( AppInfoService infoService, ICacheContext cacheContext, IRepository userApp) + { + _appInfoService = infoService; + _cacheContext = cacheContext; + _app = userApp; + } + + public LoginResult Do(PassportLoginRequest model) + { + var result = new LoginResult(); + try + { + model.Trim(); + //获取应用信息 + var appInfo = _appInfoService.Get(model.AppKey); + if (appInfo == null) + { + throw new Exception("应用不存在"); + } + //获取用户信息 + User userInfo = null; + if (model.Account == Define.SYSTEM_USERNAME) + { + userInfo = new User + { + Id = Guid.Empty.ToString(), + Account = Define.SYSTEM_USERNAME, + Name ="超级管理员", + Password = Define.SYSTEM_USERPWD + }; + } + else + { + userInfo = _app.FindSingle(u =>u.Account == model.Account); + } + + if (userInfo == null) + { + throw new Exception("用户不存在"); + } + if (userInfo.Password != model.Password) + { + throw new Exception("密码错误"); + } + + var currentSession = new UserAuthSession + { + Account = model.Account, + Name = userInfo.Name, + Token = Guid.NewGuid().ToString().GetHashCode().ToString("x"), + AppKey = model.AppKey, + CreateTime = DateTime.Now + // , IpAddress = HttpContext.Current.Request.UserHostAddress + }; + + //创建Session + _cacheContext.Set(currentSession.Token, currentSession, DateTime.Now.AddDays(10)); + + result.Code = 200; + result.ReturnUrl = appInfo.ReturnUrl; + result.Token = currentSession.Token; + } + catch (Exception ex) + { + result.Code = 500; + result.Message = ex.Message; + } + + return result; + } + } } \ No newline at end of file diff --git a/OpenAuth.App/SSO/SSOAuthAttribute.cs b/OpenAuth.App/SSO/SSOAuthAttribute.cs deleted file mode 100644 index 2ada9add5cfb72685c7fe3530c09983184d0ba17..0000000000000000000000000000000000000000 --- a/OpenAuth.App/SSO/SSOAuthAttribute.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Web; -using System.Web.Mvc; - -namespace OpenAuth.App.SSO -{ - /// - /// 采用Attribute的方式验证登录 - /// 李玉宝新增于2016-11-09 10:08:10 - /// - public class SSOAuthAttribute : ActionFilterAttribute - { - public const string Token = "Token"; - - public override void OnActionExecuting(ActionExecutingContext filterContext) - { - var token = ""; - - //Token by QueryString - var request = filterContext.HttpContext.Request; - if (request.QueryString[Token] != null) - { - token = request.QueryString[Token]; - var cookie = new HttpCookie(Token, token) - { - Expires = DateTime.Now.AddDays(10) - }; - filterContext.HttpContext.Response.Cookies.Add(cookie); - } - else if (request.Cookies[Token] != null) //从Cookie读取Token - { - token = request.Cookies[Token].Value; - } - - if (string.IsNullOrEmpty(token)) - { - //直接登录 - filterContext.Result = LoginResult(""); - return; - } - else - { - //验证 - if (AuthUtil.CheckLogin(token, request.RawUrl) == false) - { - //会话丢失,跳转到登录页面 - filterContext.Result = LoginResult(""); - return; - } - } - - base.OnActionExecuting(filterContext); - } - - public virtual ActionResult LoginResult(string username) - { - return new RedirectResult("/Login/Index"); - } - } -} diff --git a/OpenAuth.App/SSO/SSOController.cs b/OpenAuth.App/SSO/SSOController.cs deleted file mode 100644 index 55dee05c7d1a2ef4a9eeb4778d6e8517519aff11..0000000000000000000000000000000000000000 --- a/OpenAuth.App/SSO/SSOController.cs +++ /dev/null @@ -1,62 +0,0 @@ -// *********************************************************************** -// Assembly : OpenAuth.Mvc -// Author : Administrator -// Created : 09-22-2015 -// -// Last Modified By : Administrator -// Last Modified On : 09-22-2015 -// *********************************************************************** -// -// Copyright (c) . All rights reserved. -// -// -// 基础控制器 -// 继承该控制器可以防止未登录查看 -// 继承该控制器后,如果想访问控制器中存在,但模块配置里面没有的Action(如:Home/Git),请使用AnonymousAttribute -// -// *********************************************************************** - -using System; -using System.Web; -using System.Web.Mvc; - -namespace OpenAuth.App.SSO -{ - public class SSOController : Controller - { - public const string Token = "Token"; - - protected override void OnActionExecuting(ActionExecutingContext filterContext) - { - var token = ""; - - //Token by QueryString - var request = filterContext.HttpContext.Request; - if (request.Cookies[Token] != null) //从Cookie读取Token - { - token = request.Cookies[Token].Value; - } - - if (string.IsNullOrEmpty(token)) - { - //直接登录 - filterContext.Result = LoginResult(""); - return; - } - //验证 - if (AuthUtil.CheckLogin(token, request.RawUrl) == false) - { - //会话丢失,跳转到登录页面 - filterContext.Result = LoginResult(""); - return; - } - - base.OnActionExecuting(filterContext); - } - - public virtual ActionResult LoginResult(string username) - { - return new RedirectResult("/Login/Index"); - } - } -} \ No newline at end of file diff --git a/OpenAuth.App/SSO/UserAuthSession.cs b/OpenAuth.App/SSO/UserAuthSession.cs index 0b96e482e03d93bdf4c4d29e2ad1523b28ef3357..050f6bc1e0256b8b4af9c771e9cf9433bb16fdf7 100644 --- a/OpenAuth.App/SSO/UserAuthSession.cs +++ b/OpenAuth.App/SSO/UserAuthSession.cs @@ -1,26 +1,26 @@ -using System; - -namespace OpenAuth.App.SSO -{ - [Serializable] - public class UserAuthSession - { - public string Token { get; set; } - - public string AppKey { get; set; } - +using System; + +namespace OpenAuth.App.SSO +{ + [Serializable] + public class UserAuthSession + { + public string Token { get; set; } + + public string AppKey { get; set; } + /// /// 用户账号 - /// - public string Account { get; set; } - + /// + public string Account { get; set; } + /// /// 用户名 - /// - public string Name { get; set; } - - public string IpAddress { get; set; } - - public DateTime CreateTime { get; set; } - } + /// + public string Name { get; set; } + + public string IpAddress { get; set; } + + public DateTime CreateTime { get; set; } + } } \ No newline at end of file diff --git a/OpenAuth.App/SysLogApp.cs b/OpenAuth.App/SysLogApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..ac9b175826e1cd8dfcf12209fdf74a6e267babcc --- /dev/null +++ b/OpenAuth.App/SysLogApp.cs @@ -0,0 +1,53 @@ +using System.Linq; +using System.Reflection; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class SysLogApp : BaseApp + { + + /// + /// 加载列表 + /// + public TableData Load(QuerySysLogListReq request) + { + var result = new TableData(); + var objs = UnitWork.Find(null); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Content.Contains(request.key) || u.Id.Contains(request.key)); + } + + result.data = objs.OrderByDescending(u => u.CreateTime) + .Skip((request.page - 1) * request.limit) + .Take(request.limit); + result.count = objs.Count(); + return result; + } + + public void Add(SysLog obj) + { + //程序类型取入口应用的名称,可以根据自己需要调整 + obj.Application = Assembly.GetEntryAssembly().FullName.Split(',')[0]; + Repository.Add(obj); + } + + public void Update(SysLog obj) + { + UnitWork.Update(u => u.Id == obj.Id, u => new SysLog + { + //todo:要修改的字段赋值 + }); + + } + + public SysLogApp(IUnitWork unitWork, IRepository repository) : base(unitWork, repository, null) + { + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/SysMessageApp.cs b/OpenAuth.App/SysMessageApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..e660ee404872a12fa24f7f03b386f4f1772532d3 --- /dev/null +++ b/OpenAuth.App/SysMessageApp.cs @@ -0,0 +1,62 @@ +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class SysMessageApp : BaseApp + { + private RevelanceManagerApp _revelanceApp; + + /// + /// 加载列表 + /// + public TableData Load(QuerySysMessageListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + var result = new TableData(); + var objs = UnitWork.Find(u =>u.ToId == loginContext.User.Id); + + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Title.Contains(request.key) || u.Id.Contains(request.key)); + } + + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit); + result.count = objs.Count(); + return result; + } + + public void Add(SysMessage obj) + { + Repository.Add(obj); + } + + public void Update(SysMessage obj) + { + UnitWork.Update(u => u.Id == obj.Id, u => new SysMessage + { + //todo:要修改的字段赋值 + }); + + } + + public SysMessageApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app,IAuth auth) : base(unitWork, repository, auth) + { + _revelanceApp = app; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/SystemAuthService.cs b/OpenAuth.App/SystemAuthService.cs deleted file mode 100644 index 3a712ee4fe20e3594ae4b396786264c3ae6b2411..0000000000000000000000000000000000000000 --- a/OpenAuth.App/SystemAuthService.cs +++ /dev/null @@ -1,63 +0,0 @@ -// *********************************************************************** -// Assembly : OpenAuth.Domain -// Author : yubaolee -// Created : 04-21-2016 -// -// Last Modified By : yubaolee -// Last Modified On : 04-21-2016 -// Contact : Microsoft -// File: AuthenService.cs -// *********************************************************************** - -using System; -using System.Linq; -using OpenAuth.Repository.Domain; - -namespace OpenAuth.App -{ - /// - /// 领域服务 - /// 超级管理员权限 - /// todo:超级管理员使用guid.empty为ID,可以根据需要修改 - /// - public class SystemAuthService : AuthoriseService - { - public SystemAuthService() - { - _user = new User - { - Account = "System", - Name = "超级管理员", - Id = Guid.Empty.ToString() - }; - } - - - - public override IQueryable GetOrgsQuery() - { - return UnitWork.Find(null); - } - - public override IQueryable GetResourcesQuery() - { - return UnitWork.Find(null); - } - - public override IQueryable GetModuleElementsQuery() - { - return UnitWork.Find(null); - } - - public override IQueryable GetModulesQuery() - { - return UnitWork.Find(null); - } - - public override IQueryable GetRolesQuery() - { - //用户角色 - return UnitWork.Find(null); - } - } -} \ No newline at end of file diff --git a/OpenAuth.App/Test/TestAccessObjs.cs b/OpenAuth.App/Test/TestAccessObjs.cs new file mode 100644 index 0000000000000000000000000000000000000000..f2c78fbb327744bc6d31040a01957e46efad9e88 --- /dev/null +++ b/OpenAuth.App/Test/TestAccessObjs.cs @@ -0,0 +1,60 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Test +{ + /// + /// 测试分配权限 + /// + class TestAccessObjs :TestBase + { + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")) + .Returns(new UserAuthSession { Account = "System" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]) + .Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + [Test] + public void AssignOrgUsers() + { + var app = _autofacServiceProvider.GetService(); + var userApp = _autofacServiceProvider.GetService(); + + app.AssignOrgUsers(new AssignOrgUsers + { + OrgId = "8e31553c-cab8-4eb3-90b5-5f8ff1d21801", + UserIds = new []{"96f63f9d-e8c8-4258-963e-3327ed7d6f56"} + }); + + var result = userApp.Load(new QueryUserListReq + { + orgId = "8e31553c-cab8-4eb3-90b5-5f8ff1d21801", + page = 1, + limit = 10 + }); + + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + + } + } +} diff --git a/OpenAuth.App/Test/TestBase.cs b/OpenAuth.App/Test/TestBase.cs new file mode 100644 index 0000000000000000000000000000000000000000..dc0548bda25d7a32009960e56213187ad74cb5f0 --- /dev/null +++ b/OpenAuth.App/Test/TestBase.cs @@ -0,0 +1,50 @@ +using Autofac.Extensions.DependencyInjection; +using Infrastructure.Cache; +using Infrastructure.Extensions.AutofacManager; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using OpenAuth.App.SSO; +using OpenAuth.Repository; + +namespace OpenAuth.App.Test +{ + public class TestBase + { + protected AutofacServiceProvider _autofacServiceProvider; + + [SetUp] + public void Init() + { + var serviceCollection = GetService(); + serviceCollection.AddMemoryCache(); + serviceCollection.AddOptions(); + + var optionMock = new Mock>(); + optionMock.Setup(x => x.Value).Returns(new AppSetting { DbType = Define.DBTYPE_MYSQL}); + serviceCollection.AddScoped(x => optionMock.Object); + + // 测试my sql + serviceCollection.AddDbContext(options => + options.UseMySql("server=127.0.0.1;user id=root;database=openauthdb;password=000000")); + +// serviceCollection.AddDbContext(options => +// options.UseSqlServer("Data Source=.;Initial Catalog=OpenAuthDB;User=sa;Password=000000;Integrated Security=True")); + + var container = AutofacExt.InitForTest(serviceCollection); + _autofacServiceProvider = new AutofacServiceProvider(container); + AutofacContainerModule.ConfigServiceProvider(_autofacServiceProvider); + } + + /// + /// 测试框架默认只注入了缓存Cache,配置Option; + /// 如果在测试的过程中需要模拟登录用户,cookie等信息,需要重写该方法,可以参考TestFlow的写法 + /// + public virtual ServiceCollection GetService() + { + return new ServiceCollection(); + } + } +} diff --git a/OpenAuth.App/Test/TestBuilder.cs b/OpenAuth.App/Test/TestBuilder.cs new file mode 100644 index 0000000000000000000000000000000000000000..0c8abe315c4393156326ff97111a2156566aed0d --- /dev/null +++ b/OpenAuth.App/Test/TestBuilder.cs @@ -0,0 +1,123 @@ +using System.IO; +using System.Net.Http; +using System.Reflection; +using Infrastructure.Cache; +using Infrastructure.Provider; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Moq; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; +using HttpContext = Infrastructure.Utilities.HttpContext; + +namespace OpenAuth.App.Test +{ + [TestFixture] + public class TestBuilder :TestBase + { + //测试流程需要模拟登录用户 + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "test3" }); + services.AddScoped(x => cachemock.Object); + + //模拟服务端httpContext + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + services.AddScoped(x => httpContextAccessorMock.Object); + + //模拟httpclientfactory + var mockHttpFac = new Mock(); + services.AddScoped(x => mockHttpFac.Object); + + //模拟路径 + var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + .Replace("\\OpenAuth.App\\bin\\Debug\\netcoreapp3.1",""); + var mockPathProvider = new Mock(); + mockPathProvider.Setup(x => x.MapPath("",false)).Returns(path); + services.AddScoped(x => mockHttpFac.Object); + + var host = new Mock(); + host.Setup(x => x.ContentRootPath).Returns(Path.Combine(path, "OpenAuth.WebApi")); + services.AddScoped(x => host.Object); + return services; + } + + + [Test] + public void add() + { + var app = _autofacServiceProvider.GetService(); + app.Add(new AddOrUpdateBuilderTableReq() + { + TableName = "Stock", + Comment = "这是一个普通的应用管理生成模版", + ClassName = "Stock", + Namespace = "OpenAuth.Repository.Domain", + ModuleCode = "StockApp", + ModuleName = "仓储管理" + }); + } + + [Test] + public void CreateEntity() + { + var app = _autofacServiceProvider.GetService(); + // var id = app.Add(new AddOrUpdateBuilderTableReq() + // { + // TableName = "application", + // Comment = "这是一个普通的应用管理生成模版", + // ClassName = "Application", + // Folder = "Application", + // ModuleCode = "Application", + // ModuleName = "模块管理" + // }); + + app.CreateEntity(new CreateEntityReq + { + Id = "98dcbefb-8460-421f-b513-71618228a631" + }); + } + + [Test] + public void CheckModule() + { + var app = _autofacServiceProvider.GetService(); + app.CheckExistsModule("Application"); + } + + [Test] + public void CreateBusiness() + { + var app = _autofacServiceProvider.GetService(); + + app.CreateBusiness(new CreateBusiReq + { + Id = "f4464610-8c5c-441f-8825-13d6073e4385" + }); + } + + [Test] + public void CreateVue() + { + var app = _autofacServiceProvider.GetService(); + + app.CreateVue(new CreateVueReq() + { + Id = "f4464610-8c5c-441f-8825-13d6073e4385", + VueProjRootPath = "D:\\OpenAuth.Pro\\Client" + }); + app.CreateVueApi(new CreateVueReq() + { + Id = "f4464610-8c5c-441f-8825-13d6073e4385", + VueProjRootPath = "D:\\OpenAuth.Pro\\Client" + }); + } + } +} diff --git a/OpenAuth.App/Test/TestCategory.cs b/OpenAuth.App/Test/TestCategory.cs new file mode 100644 index 0000000000000000000000000000000000000000..387ebaa625e000621a6a6c57f6df787f7dbfe701 --- /dev/null +++ b/OpenAuth.App/Test/TestCategory.cs @@ -0,0 +1,59 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + public class TestCategory :TestBase + { + //测试流程需要模拟登录用户 + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "test" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + + [Test] + public void TestLoadCategories() + { + var app = _autofacServiceProvider.GetService(); + var result = app.Load(new QueryCategoryListReq + { + limit = 10, + page = 1 + }); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + + + [Test] + public void TestAssign() + { + var app = _autofacServiceProvider.GetService(); + + app.AssignData(new AssignDataReq + { + ModuleCode = "Category", + RoleId = "09ee2ffa-7463-4938-ae0b-1cb4e80c7c13", //管理员 + Properties = new []{"Id", "Name" } + }); + } + } +} diff --git a/OpenAuth.App/Test/TestDataPrivilege.cs b/OpenAuth.App/Test/TestDataPrivilege.cs new file mode 100644 index 0000000000000000000000000000000000000000..3b9684576168339e0e0f86083769205160115ff3 --- /dev/null +++ b/OpenAuth.App/Test/TestDataPrivilege.cs @@ -0,0 +1,96 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + class TestDataPrivilege :TestBase + { + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "Systems" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + /// + /// 测试资源列表的权限控制 + /// + [Test] + public void LoadData() + { + var app = _autofacServiceProvider.GetService(); + var result = app.Load(new QueryResourcesReq()); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + + [Test] + public void TestAdd() + { + var auth = _autofacServiceProvider.GetService(); + var app = _autofacServiceProvider.GetService(); + //该测试解析为:针对资源列表,【管理员】可以看到所有,角色为【神】或【测试】的只能看到自己创建的 + var filterGroup = new FilterGroup + { + Operation = "or" + }; + filterGroup.Filters = new[] + { + new Filter //角色为【管理员】的,可以看到所有 + { + Key = "{loginRole}", + Contrast = "contains", + Value = "09ee2ffa-7463-4938-ae0b-1cb4e80c7c13" //管理员 + } + }; + filterGroup.Children = new[] + { + new FilterGroup //登录用户角色包含【测试】或包含【神】的,只能看到自己的 + { + Operation = "and", + Filters = new Filter[] + { + new Filter + { + Key = "CreateUserId", + Contrast = "==", + Value = "{loginUser}", + Text = "{当前登录用户}" + }, + new Filter + { + Key = "{loginRole}", + Contrast = "intersect", + Value = "0a7ebd0c-78d6-4fbc-8fbe-6fc25c3a932d,77e6d0c3-f9e1-4933-92c3-c1c6eef75593" //测试,神 + } + } + } + }; + + app.Clear(); + app.Add(new AddOrUpdateDataPriviReq + { + SourceCode = "Resource", + Description = "资源数据规则", + PrivilegeRules = JsonHelper.Instance.Serialize(filterGroup) + }); + Console.WriteLine(JsonHelper.Instance.Serialize(filterGroup)); + } + } +} diff --git a/OpenAuth.App/Test/TestDbExtension.cs b/OpenAuth.App/Test/TestDbExtension.cs new file mode 100644 index 0000000000000000000000000000000000000000..a33906a46d111edad266732f3137b9010106a4b0 --- /dev/null +++ b/OpenAuth.App/Test/TestDbExtension.cs @@ -0,0 +1,41 @@ +using System; +using Infrastructure; +using log4net; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; + +namespace OpenAuth.App.Test +{ + class TestDbExtension :TestBase + { + private ILog log = LogManager.GetLogger(typeof(TestDbExtension)); + + [Test] + public void TestGetProperties() + { + + var app = _autofacServiceProvider.GetService(); + + var result = app.GetProperties("Category"); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + + [Test] + public void GetDbEntityNames() + { + var app = _autofacServiceProvider.GetService(); + + var result = app.GetDbEntityNames(); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + + [Test] + public void TestGetTables() + { + var app = _autofacServiceProvider.GetService(); + + var result = app.GetDbTableStructure("application"); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + } +} diff --git a/OpenAuth.App/Test/TestFlow.cs b/OpenAuth.App/Test/TestFlow.cs new file mode 100644 index 0000000000000000000000000000000000000000..b2244e2484de1ae0c18f79f363c092a7e4f62c7e --- /dev/null +++ b/OpenAuth.App/Test/TestFlow.cs @@ -0,0 +1,48 @@ +using System.Net.Http; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + [TestFixture] + public class TestFlow :TestBase + { + //测试流程需要模拟登录用户 + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "test3" }); + services.AddScoped(x => cachemock.Object); + + //模拟服务端httpContext + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + services.AddScoped(x => httpContextAccessorMock.Object); + + //模拟httpclientfactory + var mockHttpFac = new Mock(); + services.AddScoped(x => mockHttpFac.Object); + + return services; + } + + + [Test] + public void Verificate() + { + var app = _autofacServiceProvider.GetService(); + app.Verification(new VerificationReq + { + FlowInstanceId = "76c72db4-d6c8-4734-856e-b6ffee08314a", + VerificationFinally = "1" + }); + } + } +} diff --git a/OpenAuth.App/Test/TestForm.cs b/OpenAuth.App/Test/TestForm.cs new file mode 100644 index 0000000000000000000000000000000000000000..b29cf89a99273e31991cfb4320f165a9953e946b --- /dev/null +++ b/OpenAuth.App/Test/TestForm.cs @@ -0,0 +1,45 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + [TestFixture] + public class TestForm :TestBase + { + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "admin" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + + [Test] + public void Load() + { + var app = _autofacServiceProvider.GetService(); + var result = app.Load(new QueryFormListReq + { + page = 1, + limit = 10 + }); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + } +} diff --git a/OpenAuth.App/Test/TestFrmLeaveReq.cs b/OpenAuth.App/Test/TestFrmLeaveReq.cs new file mode 100644 index 0000000000000000000000000000000000000000..9e28c25a978d9370ce81d943ba880d7346d63f36 --- /dev/null +++ b/OpenAuth.App/Test/TestFrmLeaveReq.cs @@ -0,0 +1,40 @@ +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + public class TestFrmLeaveReq : TestBase + { + //测试流程需要模拟登录用户 + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "test" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + + [Test] + public void Add() + { + var frm = + "{\"id\":\"\",\"userName\":\"111111111\",\"requestType\":\"\",\"startDate\":\"2019-03-05T16:00:00.000Z\",\"startTime\":\"2019-03-19T08:01:23.000Z\",\"endDate\":\"2019-03-27T16:00:00.000Z\",\"endTime\":\"2019-03-19T08:01:26.000Z\",\"requestComment\":\"2222222222222\",\"attachment\":\"\",\"createDate\":\"2019-03-19T08:01:26.000Z\",\"createUserId\":\"\",\"createUserName\":\"\",\"extendInfo\":\"\"}"; + var app = _autofacServiceProvider.GetService(); + app.Add("d8fa445f-edd9-4604-8d9e-b17ba921f9dd", + frm); + } + } +} diff --git a/OpenAuth.App/Test/TestJob.cs b/OpenAuth.App/Test/TestJob.cs new file mode 100644 index 0000000000000000000000000000000000000000..0f2b0183ec9f3851a4a5288499fdd7c77015f476 --- /dev/null +++ b/OpenAuth.App/Test/TestJob.cs @@ -0,0 +1,41 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using NUnit.Framework; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + [TestFixture] + public class TestJob :TestBase + { + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "admin" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + + [Test] + public void GetSysJobs() + { + var app = _autofacServiceProvider.GetService(); + var result = app.QueryLocalHandlers(); + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + } +} diff --git a/OpenAuth.App/Test/TestOrgApp.cs b/OpenAuth.App/Test/TestOrgApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..be6206de193fa429e4dc2244017f6cfcb44d4d5f --- /dev/null +++ b/OpenAuth.App/Test/TestOrgApp.cs @@ -0,0 +1,77 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using OpenAuth.App.SSO; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.App.Test +{ + class TestOrgApp :TestBase + { + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "test" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + [Test] + public void TestAdd() + { + var orgname = "user_" + DateTime.Now.ToString("yyyy_MM_dd HH:mm:ss"); + Console.WriteLine(orgname); + var app = _autofacServiceProvider.GetService(); + + var id = app.Add(new Org + { + Name = orgname, + ParentId = "" + }); + + var org = app.Get(id); + Console.WriteLine(JsonHelper.Instance.Serialize(org)); + } + + /// + /// 测试删除华东、中部片区 + /// + [Test] + public void TestDel() + { + var app = _autofacServiceProvider.GetService(); + app.DelOrgCascade(new []{"66386671-0494-4e83-8346-fbcf73283f7b","182dac38-64a0-414c-990c-7c9b7558a367"}); + } + + + [Test] + public void TestUpdate() + { + var orgname = "user_" + DateTime.Now.ToString("yyyy_MM_dd HH:mm:ss"); + Console.WriteLine(orgname); + var app = _autofacServiceProvider.GetService(); + + var id = app.Update(new Org + { + Id="543a9fcf-4770-4fd9-865f-030e562be238", + Name = orgname, + ParentId = "" + }); + + var org = app.Get(id); + Console.WriteLine(JsonHelper.Instance.Serialize(org)); + } + } +} diff --git a/OpenAuth.App/Test/TestUserApp.cs b/OpenAuth.App/Test/TestUserApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..9a242c235be22fb74e2bf868167ae7eb7f424194 --- /dev/null +++ b/OpenAuth.App/Test/TestUserApp.cs @@ -0,0 +1,84 @@ +using System; +using Infrastructure; +using Infrastructure.Cache; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using NUnit.Framework; +using OpenAuth.App.Request; +using OpenAuth.App.SSO; + +namespace OpenAuth.App.Test +{ + class TestUserApp :TestBase + { + public override ServiceCollection GetService() + { + var services = new ServiceCollection(); + + var cachemock = new Mock(); + cachemock.Setup(x => x.Get("tokentest")).Returns(new UserAuthSession { Account = "System" }); + services.AddScoped(x => cachemock.Object); + + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(x => x.HttpContext.Request.Query[Define.TOKEN_NAME]).Returns("tokentest"); + + services.AddScoped(x => httpContextAccessorMock.Object); + + return services; + } + + [Test] + public void TestAdd() + { + var account = "user_" + DateTime.Now.ToString("yyyy_MM_dd HH:mm:ss"); + Console.WriteLine(account); + var app = _autofacServiceProvider.GetService(); + + var newuser = new UpdateUserReq + { + Account = account, + Name = account, + OrganizationIds = "08f41bf6-4388-4b1e-bd3e-2ff538b44b1b,543a9fcf-4770-4fd9-865f-030e562be238", + }; + app.AddOrUpdate(newuser); + + app.AddOrUpdate(new UpdateUserReq + { + Id = newuser.Id, + Password = "newpassword", + Account = account, + Name = "新名字", + OrganizationIds = "08f41bf6-4388-4b1e-bd3e-2ff538b44b1b", + }); + } + + [Test] + public void TestLoad() + { + var app = _autofacServiceProvider.GetService(); + var result = app.Load(new QueryUserListReq() + { + page = 1, + limit = 10, + orgId = "08f41bf6-4388-4b1e-bd3e-2ff538b44b1b" + }); + + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + + [Test] + public void TestLoadByOrg() + { + var app = _autofacServiceProvider.GetService(); + var result = app.LoadByOrg(new QueryUserListByOrgReq + { + page = 1, + limit = 10, + orgId = "08f41bf6-4388-4b1e-bd3e-2ff538b44b1b" + }); + + Console.WriteLine(JsonHelper.Instance.Serialize(result)); + } + } +} diff --git a/OpenAuth.App/UserManagerApp.cs b/OpenAuth.App/UserManagerApp.cs index f7f14a9b136430129c97bd6546a73ff346e27fe4..270ac048f879ef08003342bb2a4c137222ef94de 100644 --- a/OpenAuth.App/UserManagerApp.cs +++ b/OpenAuth.App/UserManagerApp.cs @@ -1,110 +1,247 @@ using System; using System.Collections.Generic; using System.Linq; +using Castle.Core.Internal; +using OpenAuth.App.Interface; using OpenAuth.App.Request; using OpenAuth.App.Response; -using OpenAuth.App.SSO; using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; namespace OpenAuth.App { public class UserManagerApp : BaseApp { - public RevelanceManagerApp ReleManagerApp { get; set; } + private RevelanceManagerApp _revelanceApp; + private OrgManagerApp _orgManagerApp; + + public UserManagerApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app,IAuth auth, OrgManagerApp orgManagerApp) : base(unitWork, repository, auth) + { + _revelanceApp = app; + _orgManagerApp = orgManagerApp; + } - public User Get(string account) + public User GetByAccount(string account) { return Repository.FindSingle(u => u.Account == account); } /// /// 加载当前登录用户可访问的一个部门及子部门全部用户 + /// 如果请求的request.OrgId为空,则可以获取到已被删除机构的用户(即:没有分配任何机构的用户) /// public TableData Load(QueryUserListReq request) { - var loginUser = AuthUtil.GetCurrentUser(); + var loginUser = _auth.GetCurrentUser(); + - string cascadeId = ".0."; + IQueryable query = UnitWork.Find(null); + if (!string.IsNullOrEmpty(request.key)) + { + query = UnitWork.Find(u => u.Name.Contains(request.key) || u.Account.Contains(request.key)); + } + + var userOrgs = from user in query + join relevance in UnitWork.Find(u =>u.Key=="UserOrg") + on user.Id equals relevance.FirstId into temp + from r in temp.DefaultIfEmpty() + join org in UnitWork.Find(null) + on r.SecondId equals org.Id into orgtmp + from o in orgtmp.DefaultIfEmpty() + select new + { + user.Account, + user.Name, + user.Id, + user.Sex, + user.Status, + user.BizCode, + user.CreateId, + user.CreateTime, + user.TypeId, + user.TypeName, + r.Key, + r.SecondId, + OrgId = o.Id, + OrgName= o.Name + }; + + //如果请求的orgId不为空 if (!string.IsNullOrEmpty(request.orgId)) { var org = loginUser.Orgs.SingleOrDefault(u => u.Id == request.orgId); - cascadeId = org.CascadeId; + var cascadeId = org.CascadeId; + + var orgIds = loginUser.Orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray(); + + //只获取机构里面的用户 + userOrgs = userOrgs.Where(u => u.Key == Define.USERORG && orgIds.Contains(u.OrgId)); } - - var ids = loginUser.Orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray(); - var userIds = ReleManagerApp.Get(Define.USERORG, false, ids); - - var users = UnitWork.Find(u => userIds.Contains(u.Id)) - .OrderBy(u => u.Name) - .Skip((request.page - 1) * request.limit) - .Take(request.limit); - - var records = Repository.GetCount(u => userIds.Contains(u.Id)); - - - var userviews = new List(); - foreach (var user in users.ToList()) + else //todo:如果请求的orgId为空,即为跟节点,这时可以额外获取到机构已经被删除的用户,从而进行机构分配。可以根据自己需求进行调整 { - UserView uv = user; - var orgs = LoadByUser(user.Id); - uv.Organizations = string.Join(",", orgs.Select(u => u.Name).ToList()); - uv.OrganizationIds = string.Join(",", orgs.Select(u => u.Id).ToList()); - userviews.Add(uv); + var orgIds = loginUser.Orgs.Select(u => u.Id).ToArray(); + + //获取用户可以访问的机构的用户和没有任何机构关联的用户(机构被删除后,没有删除这里面的关联关系) + userOrgs = userOrgs.Where(u => (u.Key == Define.USERORG && orgIds.Contains(u.OrgId)) || (u.OrgId == null)); } + + + var userViews = userOrgs.ToList().GroupBy(b => b.Account).Select(u =>new UserView + { + Id = u.First().Id, + Account = u.Key, + Name = u.First().Name, + Sex = u.First().Sex, + Status = u.First().Status, + CreateTime = u.First().CreateTime, + CreateUser = u.First().CreateId, + OrganizationIds = string.Join(",", u.Select(x=>x.OrgId)) + ,Organizations = string.Join(",", u.Select(x=>x.OrgName)) + + }); + return new TableData { - count = records, - data = userviews, + count = userViews.Count(), + data = userViews.OrderBy(u => u.Name) + .Skip((request.page - 1) * request.limit) + .Take(request.limit), }; } - public void AddOrUpdate(UserView view) + public void AddOrUpdate(UpdateUserReq request) { - if (string.IsNullOrEmpty(view.OrganizationIds)) + if (string.IsNullOrEmpty(request.OrganizationIds)) throw new Exception("请为用户分配机构"); - User user = view; - if (string.IsNullOrEmpty(view.Id)) + User requser = request; + requser.CreateId = _auth.GetCurrentUser().User.Id; + if (string.IsNullOrEmpty(request.Id)) { - if (UnitWork.IsExist(u => u.Account == view.Account)) + if (UnitWork.IsExist(u => u.Account == request.Account)) { throw new Exception("用户账号已存在"); } - user.CreateTime = DateTime.Now; - user.Password = user.Account; //初始密码与账号相同 - Repository.Add(user); - view.Id = user.Id; //要把保存后的ID存入view + + if (string.IsNullOrEmpty(requser.Password)) + { + requser.Password = requser.Account; //如果客户端没提供密码,默认密码同账号 + } + + requser.CreateTime = DateTime.Now; + + UnitWork.Add(requser); + request.Id = requser.Id; //要把保存后的ID存入view } else { - UnitWork.Update(u => u.Id == view.Id, u => new User + UnitWork.Update(u => u.Id == request.Id, u => new User { - Account = user.Account, - BizCode = user.BizCode, - Name = user.Name, - Sex = user.Sex, - Status = user.Status + Account = requser.Account, + BizCode = requser.BizCode, + Name = requser.Name, + Sex = requser.Sex, + Status = requser.Status }); + if (!string.IsNullOrEmpty(requser.Password)) //密码为空的时候,不做修改 + { + UnitWork.Update(u => u.Id == request.Id, u => new User + { + Password = requser.Password + }); + } } - string[] orgIds = view.OrganizationIds.Split(',').ToArray(); - ReleManagerApp.DeleteBy(Define.USERORG, user.Id); - ReleManagerApp.AddRelevance(Define.USERORG, orgIds.ToLookup(u => user.Id)); + UnitWork.Save(); + string[] orgIds = request.OrganizationIds.Split(',').ToArray(); + + _revelanceApp.DeleteBy(Define.USERORG, requser.Id); + _revelanceApp.Assign(Define.USERORG, orgIds.ToLookup(u => requser.Id)); + } + + /// + /// 删除用户,包含用户与组织关系、用户与角色关系 + /// + /// + public override void Delete(string[] ids) + { + UnitWork.Delete(u =>(u.Key == Define.USERROLE || u.Key == Define.USERORG) + && ids.Contains(u.FirstId)); + UnitWork.Delete(u => ids.Contains(u.Id)); + UnitWork.Save(); } + /// - /// 加载用户的所有机构 + /// 修改密码 /// - public IEnumerable LoadByUser(string userId) + /// + public void ChangePassword(ChangePasswordReq request) { - var result = from userorg in UnitWork.Find(null) - join org in UnitWork.Find(null) on userorg.SecondId equals org.Id - where userorg.FirstId == userId && userorg.Key == Define.USERORG - select org; - return result; + Repository.Update(u => u.Account == request.Account, user => new User + { + Password = request.Password + }); } + + /// + /// 获取指定角色包含的用户列表 + /// + /// + /// + public TableData LoadByRole(QueryUserListByRoleReq request) + { + var users = from userRole in UnitWork.Find(u => + u.SecondId == request.roleId && u.Key == Define.USERROLE) + join user in UnitWork.Find(null) on userRole.FirstId equals user.Id into temp + from c in temp.Where(u =>u.Id != null) + select c; + return new TableData + { + count = users.Count(), + data = users.Skip((request.page - 1) * request.limit).Take(request.limit) + }; + } + + /// + /// 获取指定机构包含的用户列表 + /// + /// + /// + public TableData LoadByOrg(QueryUserListByOrgReq request) + { + var users = from userRole in UnitWork.Find(u => + u.SecondId == request.orgId && u.Key == Define.USERORG) + join user in UnitWork.Find(null) on userRole.FirstId equals user.Id into temp + from c in temp.Where(u =>u.Id != null) + select c; + return new TableData + { + count = users.Count(), + data = users.Skip((request.page - 1) * request.limit).Take(request.limit) + }; + } + + /// + /// 修改用户资料 + /// + /// + public void ChangeProfile(ChangeProfileReq request) + { + if (request.Account == Define.SYSTEM_USERNAME) + { + throw new Exception("不能修改超级管理员信息"); + } + + Repository.Update(u => u.Account == request.Account, user => new User + { + Name = request.Name, + Sex = request.Sex + }); + } } } \ No newline at end of file diff --git a/OpenAuth.App/WmsInboundOrderDtblApp.cs b/OpenAuth.App/WmsInboundOrderDtblApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..24e97a3ae481f89bb37e154c20e28e4835aa323c --- /dev/null +++ b/OpenAuth.App/WmsInboundOrderDtblApp.cs @@ -0,0 +1,108 @@ +using System; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class WmsInboundOrderDtblApp : BaseApp + { + private RevelanceManagerApp _revelanceApp; + private DbExtension _dbExtension; + + /// + /// 加载列表 + /// + public TableData Load(QueryWmsInboundOrderDtblListReq request) + { + + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + //todo:普通账号如何分配明细的字段????先写死😰 + + var properties = _dbExtension.GetProperties("WmsInboundOrderDtbl"); + + var result = new TableData(); + var objs = UnitWork.Find(null); + if (!string.IsNullOrEmpty(request.InboundOrderId)) + { + objs = objs.Where(u => u.OrderId == request.InboundOrderId); + } + + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.GoodsId.Contains(request.key)); + } + + var propertyStr = string.Join(',', properties.Select(u => u.Key)); + result.columnHeaders = properties; + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).Select($"new ({propertyStr})"); + result.count = objs.Count(); + return result; + } + + public void Add(AddOrUpdateWmsInboundOrderDtblReq req) + { + AddNoSave(req); + UnitWork.Save(); + } + + public void AddNoSave(AddOrUpdateWmsInboundOrderDtblReq req) + { + var obj = req.MapTo(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + UnitWork.Add(obj); + } + + public void Update(AddOrUpdateWmsInboundOrderDtblReq obj) + { + var user = _auth.GetCurrentUser().User; + UnitWork.Update(u => u.Id == obj.Id, u => new WmsInboundOrderDtbl + { + Price = obj.Price, + PriceNoTax = obj.PriceNoTax, + InStockStatus = obj.InStockStatus, + AsnStatus = obj.AsnStatus, + GoodsId = obj.GoodsId, + GoodsBatch = obj.GoodsBatch, + QualityFlg = obj.QualityFlg, + OrderNum = obj.OrderNum, + InNum = obj.InNum, + LeaveNum = obj.LeaveNum, + HoldNum = obj.HoldNum, + ProdDate = obj.ProdDate, + ExpireDate = obj.ExpireDate, + TaxRate = obj.TaxRate, + OwnerId = obj.OwnerId, + Remark = obj.Remark, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + + } + + public WmsInboundOrderDtblApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app, IAuth auth, DbExtension dbExtension) : base(unitWork, repository,auth) + { + _dbExtension = dbExtension; + _revelanceApp = app; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/WmsInboundOrderTblApp.cs b/OpenAuth.App/WmsInboundOrderTblApp.cs new file mode 100644 index 0000000000000000000000000000000000000000..9d010b70f46671ab271cd97d7d9ad96456d7d788 --- /dev/null +++ b/OpenAuth.App/WmsInboundOrderTblApp.cs @@ -0,0 +1,138 @@ +using System; +using System.Linq; +using Infrastructure; +using OpenAuth.App.Interface; +using OpenAuth.App.Request; +using OpenAuth.App.Response; +using OpenAuth.Repository.Domain; +using OpenAuth.Repository.Interface; + + +namespace OpenAuth.App +{ + public class WmsInboundOrderTblApp : BaseApp + { + private RevelanceManagerApp _revelanceApp; + private WmsInboundOrderDtblApp _wmsInboundOrderDtblApp; + + /// + /// 加载列表 + /// + public TableData Load(QueryWmsInboundOrderTblListReq request) + { + var loginContext = _auth.GetCurrentUser(); + if (loginContext == null) + { + throw new CommonException("登录已过期", Define.INVALID_TOKEN); + } + + var properties = loginContext.GetProperties("WmsInboundOrderTbl"); + + if (properties == null || properties.Count == 0) + { + throw new Exception("当前登录用户没有访问该模块字段的权限,请联系管理员配置"); + } + + + var result = new TableData(); + var objs = GetDataPrivilege("u"); + if (!string.IsNullOrEmpty(request.key)) + { + objs = objs.Where(u => u.Id.Contains(request.key)); + } + + var propertyStr = string.Join(',', properties.Select(u => u.Key)); + result.columnHeaders = properties; + result.data = objs.OrderBy(u => u.Id) + .Skip((request.page - 1) * request.limit) + .Take(request.limit).Select($"new ({propertyStr})"); + result.count = objs.Count(); + return result; + } + + public void Add(AddOrUpdateWmsInboundOrderTblReq req) + { + var obj = req.MapTo(); + //todo:补充或调整自己需要的字段 + obj.CreateTime = DateTime.Now; + var user = _auth.GetCurrentUser().User; + obj.CreateUserId = user.Id; + obj.CreateUserName = user.Name; + UnitWork.Add(obj); + if (req.WmsInboundOrderDtblReqs != null && req.WmsInboundOrderDtblReqs.Any()) + { + foreach (var detail in req.WmsInboundOrderDtblReqs) + { + detail.OrderId = obj.Id; + _wmsInboundOrderDtblApp.AddNoSave(detail); + } + } + + UnitWork.Save(); + } + + public void Update(AddOrUpdateWmsInboundOrderTblReq obj) + { + var user = _auth.GetCurrentUser().User; + + if (obj.WmsInboundOrderDtblReqs != null && obj.WmsInboundOrderDtblReqs.Any()) + { + //id为空的添加 + foreach (var detail in obj.WmsInboundOrderDtblReqs.Where(u =>string.IsNullOrEmpty(u.Id))) + { + detail.OrderId = obj.Id; + _wmsInboundOrderDtblApp.AddNoSave(detail); + } + + //id比数据库少的,删除 + var containids = obj.WmsInboundOrderDtblReqs.Select(u => u.Id) + .Where(u =>!string.IsNullOrEmpty(u)).ToList(); + if (containids.Any()) + { + UnitWork.Delete(u =>(!containids.Contains(u.Id)) && u.OrderId == obj.Id); + } + + + //更新id相同的 + foreach (var detail in obj.WmsInboundOrderDtblReqs.Where(u =>!string.IsNullOrEmpty(u.Id))) + { + _wmsInboundOrderDtblApp.Update(detail); + } + } + + UnitWork.Update(u => u.Id == obj.Id, u => new WmsInboundOrderTbl + { + ExternalNo = obj.ExternalNo, + ExternalType = obj.ExternalType, + Status = obj.Status, + OrderType = obj.OrderType, + GoodsType = obj.GoodsType, + PurchaseNo = obj.PurchaseNo, + StockId = obj.StockId, + OwnerId = obj.OwnerId, + ShipperId = obj.ShipperId, + SupplierId = obj.SupplierId, + ScheduledInboundTime = obj.ScheduledInboundTime, + Remark = obj.Remark, + Enable = obj.Enable, + TransferType = obj.TransferType, + InBondedArea = obj.InBondedArea, + ReturnBoxNum = obj.ReturnBoxNum, + UpdateTime = DateTime.Now, + UpdateUserId = user.Id, + UpdateUserName = user.Name + //todo:补充或调整自己需要的字段 + }); + + UnitWork.Save(); + + } + + public WmsInboundOrderTblApp(IUnitWork unitWork, IRepository repository, + RevelanceManagerApp app, IAuth auth, WmsInboundOrderDtblApp wmsInboundOrderDtblApp) : base(unitWork, repository,auth) + { + _revelanceApp = app; + _wmsInboundOrderDtblApp = wmsInboundOrderDtblApp; + } + } +} \ No newline at end of file diff --git a/OpenAuth.App/packages.config b/OpenAuth.App/packages.config deleted file mode 100644 index fc56dc1f84590b3ef9a00b07ed3b9c21a3dab126..0000000000000000000000000000000000000000 --- a/OpenAuth.App/packages.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/OpenAuth.Core.sln b/OpenAuth.Core.sln new file mode 100644 index 0000000000000000000000000000000000000000..55eba04ed739cce5bd4170b9c2708e57e0eba9e8 --- /dev/null +++ b/OpenAuth.Core.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28822.285 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAuth.WebApi", "OpenAuth.WebApi\OpenAuth.WebApi.csproj", "{A44BD472-84E3-4D70-9DC5-06210617D6EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{8F9EAD1A-9407-41BA-844A-02282B4646F3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAuth.Repository", "OpenAuth.Repository\OpenAuth.Repository.csproj", "{4BD1F522-FC5C-4DEB-ACE9-1242BCE235BD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAuth.App", "OpenAuth.App\OpenAuth.App.csproj", "{00D63D11-D95D-4960-9F49-B102344303A7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAuth.Mvc", "OpenAuth.Mvc\OpenAuth.Mvc.csproj", "{4BCDEC12-AAED-4AC1-81D7-4BD114CB679A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAuth.IdentityServer", "OpenAuth.Identity\OpenAuth.IdentityServer.csproj", "{83AE5403-EEA5-45C5-9B6B-DD2E8DD11786}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A44BD472-84E3-4D70-9DC5-06210617D6EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A44BD472-84E3-4D70-9DC5-06210617D6EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A44BD472-84E3-4D70-9DC5-06210617D6EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A44BD472-84E3-4D70-9DC5-06210617D6EE}.Release|Any CPU.Build.0 = Release|Any CPU + {8F9EAD1A-9407-41BA-844A-02282B4646F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F9EAD1A-9407-41BA-844A-02282B4646F3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F9EAD1A-9407-41BA-844A-02282B4646F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F9EAD1A-9407-41BA-844A-02282B4646F3}.Release|Any CPU.Build.0 = Release|Any CPU + {4BD1F522-FC5C-4DEB-ACE9-1242BCE235BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BD1F522-FC5C-4DEB-ACE9-1242BCE235BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BD1F522-FC5C-4DEB-ACE9-1242BCE235BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BD1F522-FC5C-4DEB-ACE9-1242BCE235BD}.Release|Any CPU.Build.0 = Release|Any CPU + {00D63D11-D95D-4960-9F49-B102344303A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00D63D11-D95D-4960-9F49-B102344303A7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00D63D11-D95D-4960-9F49-B102344303A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {00D63D11-D95D-4960-9F49-B102344303A7}.Release|Any CPU.Build.0 = Release|Any CPU + {4BCDEC12-AAED-4AC1-81D7-4BD114CB679A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BCDEC12-AAED-4AC1-81D7-4BD114CB679A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BCDEC12-AAED-4AC1-81D7-4BD114CB679A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BCDEC12-AAED-4AC1-81D7-4BD114CB679A}.Release|Any CPU.Build.0 = Release|Any CPU + {83AE5403-EEA5-45C5-9B6B-DD2E8DD11786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83AE5403-EEA5-45C5-9B6B-DD2E8DD11786}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83AE5403-EEA5-45C5-9B6B-DD2E8DD11786}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83AE5403-EEA5-45C5-9B6B-DD2E8DD11786}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {88FD47DB-4BD4-4B0F-9E12-49383F5AFFEB} + EndGlobalSection +EndGlobal diff --git a/OpenAuth.Identity/Config.cs b/OpenAuth.Identity/Config.cs new file mode 100644 index 0000000000000000000000000000000000000000..aff9734a351a8c632920109607c468b67286872b --- /dev/null +++ b/OpenAuth.Identity/Config.cs @@ -0,0 +1,102 @@ +// 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; +using System.Security.Claims; +using IdentityModel; +using IdentityServer4; +using IdentityServer4.Models; + +namespace OpenAuth.IdentityServer +{ + public static class Config + { + public static IEnumerable GetIdentityResources() + { + return new IdentityResource[] + { + new IdentityResources.OpenId(), + new IdentityResources.Profile(), + }; + } + /// + /// API信息 + /// + /// + public static IEnumerable GetApis() + { + return new[] + { + new ApiResource("openauthapi", "OpenAuth.WebApi") + { + UserClaims = { ClaimTypes.Name, JwtClaimTypes.Name } + } + }; + } + /// + /// 客户端信息 + /// + /// + public static IEnumerable GetClients(bool isProduction) + { + var host = "http://localhost"; + if (isProduction) + { + host = "http://demo.openauth.me"; //切换为自己的服务器信息 + } + return new[] + { + new Client + { + ClientId = "OpenAuth.WebApi",//客户端名称 + ClientName = "开源版webapi认证",//客户端描述 + AllowedGrantTypes = GrantTypes.Implicit,//Implicit 方式 + AllowAccessTokensViaBrowser = true,//是否通过浏览器为此客户端传输访问令牌 + RedirectUris = + { + $"{host}:52789/swagger/oauth2-redirect.html" + }, + AllowedScopes = { "openauthapi" } + }, + new Client + { + ClientId = "OpenAuth.Mvc", + ClientName = "开源版mvc认证", + AllowedGrantTypes = GrantTypes.Implicit, + + // 登录成功回调处理地址,处理回调返回的数据 + RedirectUris = { $"{host}:1802/signin-oidc" }, + + // where to redirect to after logout + PostLogoutRedirectUris = { $"{host}:1802/signout-callback-oidc" }, + + AllowedScopes = new List + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "openauthapi" + } + }, + new Client + { + ClientId = "OpenAuth.Pro",//企业版名称 + ClientName = "企业版js请求认证",//企业版描述 + AllowedGrantTypes = GrantTypes.Code, + RequirePkce = true, + RequireClientSecret = false, + + RedirectUris = { $"{host}:1803/#/oidc-callback" }, + PostLogoutRedirectUris = { $"{host}:1803" }, + AllowedCorsOrigins = { $"{host}:1803" }, + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, //请求用户的姓名,昵称等 + "openauthapi" + } + } + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/CustomProfileService.cs b/OpenAuth.Identity/CustomProfileService.cs new file mode 100644 index 0000000000000000000000000000000000000000..0c6eb477fa5c0790e8bf3c06bd191747cb9c014c --- /dev/null +++ b/OpenAuth.Identity/CustomProfileService.cs @@ -0,0 +1,100 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using IdentityModel; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Test; +using Microsoft.Extensions.Logging; +using OpenAuth.App; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.IdentityServer +{ + public class CustomProfileService : IProfileService + { + /// + /// The logger + /// + protected readonly ILogger Logger; + + protected UserManagerApp UserManager; + + /// + /// Initializes a new instance of the class. + /// + /// The users. + /// The logger. + public CustomProfileService( ILogger logger, UserManagerApp userManager) + { + Logger = logger; + UserManager = userManager; + } + + /// + /// 只要有关用户的身份信息单元被请求(例如在令牌创建期间或通过用户信息终点),就会调用此方法 + /// + /// The context. + /// + public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) + { + context.LogProfileRequest(Logger); + + //判断是否有请求Claim信息 + if (context.RequestedClaimTypes.Any()) + { + var user = GetUserById(context.Subject.GetSubjectId()); + if (user != null) + { + //调用此方法以后内部会进行过滤,只将用户请求的Claim加入到 context.IssuedClaims 集合中 这样我们的请求方便能正常获取到所需Claim + var claims = new[] + { + new Claim(ClaimTypes.Name, user.Account), //请求用户的账号,这个可以保证User.Identity.Name有值 + new Claim(JwtClaimTypes.Name, user.Name), //请求用户的姓名 + }; + //返回apiresource中定义的claims + context.AddRequestedClaims(claims); + } + } + + context.LogIssuedClaims(Logger); + + return Task.CompletedTask; + } + + /// + /// 验证用户是否有效 例如:token创建或者验证 + /// + /// The context. + /// + public virtual Task IsActiveAsync(IsActiveContext context) + { + Logger.LogDebug("IsActive called from: {caller}", context.Caller); + + var user = GetUserById(context.Subject.GetSubjectId()); + context.IsActive = user?.Status == 0; + return Task.CompletedTask; + } + + private User GetUserById(string id) + { + User user; + if (id == Define.SYSTEM_USERNAME) + { + user = new User + { + Account = Define.SYSTEM_USERNAME, + Id = Define.SYSTEM_USERNAME, + Name = Define.SYSTEM_USERNAME + }; + } + else + { + user = UserManager.Get(id); + } + + return user; + } + } +} diff --git a/OpenAuth.Identity/OpenAuth.IdentityServer.csproj b/OpenAuth.Identity/OpenAuth.IdentityServer.csproj new file mode 100644 index 0000000000000000000000000000000000000000..8e97fdb17f5233ef8c16e8860848ee2d82f25861 --- /dev/null +++ b/OpenAuth.Identity/OpenAuth.IdentityServer.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp5.0 + + + + + + + + + + + + + + diff --git a/OpenAuth.Identity/Program.cs b/OpenAuth.Identity/Program.cs new file mode 100644 index 0000000000000000000000000000000000000000..c00d1859204dec40bec20c207674f00297a9f5cd --- /dev/null +++ b/OpenAuth.Identity/Program.cs @@ -0,0 +1,43 @@ +// 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 Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Serilog; +using Serilog.Events; +using Serilog.Sinks.SystemConsole.Themes; + +namespace OpenAuth.IdentityServer +{ + public class Program + { + public static void Main(string[] args) + { + Console.Title = "IdentityServer4"; + + CreateWebHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateWebHostBuilder(string[] args) + { + return Host.CreateDefaultBuilder(args) + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //将默认ServiceProviderFactory指定为AutofacServiceProviderFactory + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseUrls("http://*:12796").UseStartup(); + }).UseSerilog((context, configuration) => + { + configuration + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .MinimumLevel.Override("System", LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate); + }); + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Properties/launchSettings.json b/OpenAuth.Identity/Properties/launchSettings.json new file mode 100644 index 0000000000000000000000000000000000000000..699c7ca42e81457cdd2bf7fb010729abbb51bde3 --- /dev/null +++ b/OpenAuth.Identity/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "IdentityServer": { + "commandName": "Project", + "launchBrowser": false, + "launchUrl": ".well-known/openid-configuration", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:12796" + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/AccountController.cs b/OpenAuth.Identity/Quickstart/Account/AccountController.cs new file mode 100644 index 0000000000000000000000000000000000000000..64e932690dbdf94945fbc8da533419d1b30c4f3b --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/AccountController.cs @@ -0,0 +1,383 @@ +// 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 IdentityModel; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenAuth.Repository.Domain; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + /// + /// 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 UserManagerApp _userManager; + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly IAuthenticationSchemeProvider _schemeProvider; + private readonly IEventService _events; + + public AccountController( + IIdentityServerInteractionService interaction, + IClientStore clientStore, + IAuthenticationSchemeProvider schemeProvider, + IEventService events, UserManagerApp userManager) + { + + _interaction = interaction; + _clientStore = clientStore; + _schemeProvider = schemeProvider; + _events = events; + _userManager = userManager; + } + + /// + /// 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 { provider = 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.GrantConsentAsync(context, ConsentResponse.Denied); + + // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null + if (await _clientStore.IsPkceClientAsync(context.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = 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) + { + User user; + if (model.Username == Define.SYSTEM_USERNAME && model.Password == Define.SYSTEM_USERPWD) + { + user = new User + { + Account = Define.SYSTEM_USERNAME, + Password = Define.SYSTEM_USERPWD, + Id = Define.SYSTEM_USERNAME + }; + } + else + { + user = _userManager.GetByAccount(model.Username); + } + + if (user != null &&(user.Password ==model.Password)) + { + if (user.Status != 0) //判断用户状态 + { + await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid user status")); + ModelState.AddModelError(string.Empty, "user.status must be 0"); + var err = await BuildLoginViewModelAsync(model); + return View(err); + } + + await _events.RaiseAsync(new UserLoginSuccessEvent(user.Account, user.Id, user.Account)); + + // 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 + await HttpContext.SignInAsync(user.Id, user.Account, props); + + if (context != null) + { + if (await _clientStore.IsPkceClientAsync(context.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = 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?.ClientId)); + ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); + } + + // something went wrong, show form with error + var vm = await BuildLoginViewModelAsync(model); + return View(vm); + } + + + /// + /// 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(); + + // 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 || + (x.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase)) + ) + .Select(x => new ExternalProvider + { + DisplayName = x.DisplayName, + AuthenticationScheme = x.Name + }).ToList(); + + var allowLocal = true; + if (context?.ClientId != null) + { + var client = await _clientStore.FindEnabledClientByIdAsync(context.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; + } + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/AccountOptions.cs b/OpenAuth.Identity/Quickstart/Account/AccountOptions.cs new file mode 100644 index 0000000000000000000000000000000000000000..a451ce0d72e13e28074aef12a5c211c05a819940 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/AccountOptions.cs @@ -0,0 +1,25 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + 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; //允许注销后跳转 + + // specify the Windows authentication scheme being used + public static readonly string WindowsAuthenticationSchemeName = Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme; + // if user uses windows auth, should we load the groups from windows + public static bool IncludeWindowsGroups = false; + + public static string InvalidCredentialsErrorMessage = "Invalid username or password"; + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/ExternalController.cs b/OpenAuth.Identity/Quickstart/Account/ExternalController.cs new file mode 100644 index 0000000000000000000000000000000000000000..e3cd696316ec98d8d8c58525a337757535c344e7 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/ExternalController.cs @@ -0,0 +1,251 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading.Tasks; +using IdentityModel; +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.Mvc; +using Microsoft.Extensions.Logging; + +namespace OpenAuth.IdentityServer.Quickstart.Account +{ + [SecurityHeaders] + [AllowAnonymous] + public class ExternalController : Controller + { + private readonly TestUserStore _users; + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly ILogger _logger; + private readonly IEventService _events; + + public ExternalController( + IIdentityServerInteractionService interaction, + IClientStore clientStore, + IEventService events, + ILogger logger, + TestUserStore users = null) + { + // if the TestUserStore is not in DI, then we'll just use the global users collection + // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) + _users = users ?? new TestUserStore(TestUsers.Users); + + _interaction = interaction; + _clientStore = clientStore; + _logger = logger; + _events = events; + } + + /// + /// initiate roundtrip to external authentication provider + /// + [HttpGet] + public async Task Challenge(string provider, 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"); + } + + if (AccountOptions.WindowsAuthenticationSchemeName == provider) + { + // windows authentication needs special handling + return await ProcessWindowsLoginAsync(returnUrl); + } + else + { + // start challenge and roundtrip the return URL and scheme + var props = new AuthenticationProperties + { + RedirectUri = Url.Action(nameof(Callback)), + Items = + { + { "returnUrl", returnUrl }, + { "scheme", provider }, + } + }; + + return Challenge(props, provider); + } + } + + /// + /// Post processing of external authentication + /// + [HttpGet] + public async Task Callback() + { + // read external identity from the temporary cookie + var result = await HttpContext.AuthenticateAsync(IdentityServer4.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 additonal claims or properties + // for the specific prtotocols 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(); + ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); + ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps); + ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps); + + // issue authentication cookie for user + await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray()); + + // delete temporary cookie used during external authentication + await HttpContext.SignOutAsync(IdentityServer4.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.SubjectId, user.Username, true, context?.ClientId)); + + if (context != null) + { + if (await _clientStore.IsPkceClientAsync(context.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl }); + } + } + + return Redirect(returnUrl); + } + + private async Task ProcessWindowsLoginAsync(string returnUrl) + { + // see if windows auth has already been requested and succeeded + var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName); + if (result?.Principal is WindowsPrincipal wp) + { + // we will issue the external cookie and then redirect the + // user back to the external callback, in essence, treating windows + // auth the same as any other external authentication mechanism + var props = new AuthenticationProperties() + { + RedirectUri = Url.Action("Callback"), + Items = + { + { "returnUrl", returnUrl }, + { "scheme", AccountOptions.WindowsAuthenticationSchemeName }, + } + }; + + var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName); + id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name)); + id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); + + // add the groups as claims -- be careful if the number of groups is too large + if (AccountOptions.IncludeWindowsGroups) + { + var wi = wp.Identity as WindowsIdentity; + var groups = wi.Groups.Translate(typeof(NTAccount)); + var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value)); + id.AddClaims(roles); + } + + await HttpContext.SignInAsync( + IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme, + new ClaimsPrincipal(id), + props); + return Redirect(props.RedirectUri); + } + else + { + // trigger windows auth + // since windows auth don't support the redirect uri, + // this URL is re-triggered when we call challenge + return Challenge(AccountOptions.WindowsAuthenticationSchemeName); + } + } + + private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) + { + var externalUser = result.Principal; + + // 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 userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? + externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? + throw new Exception("Unknown userid"); + + // 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(userIdClaim); + + var provider = result.Properties.Items["scheme"]; + var providerUserId = userIdClaim.Value; + + // find external user + var user = _users.FindByExternalProvider(provider, providerUserId); + + return (user, provider, providerUserId, claims); + } + + private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) + { + var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); + return user; + } + + private void ProcessLoginCallbackForOidc(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 id_token = externalResult.Properties.GetTokenValue("id_token"); + if (id_token != null) + { + localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); + } + } + + private void ProcessLoginCallbackForWsFed(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) + { + } + + private void ProcessLoginCallbackForSaml2p(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) + { + } + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/ExternalProvider.cs b/OpenAuth.Identity/Quickstart/Account/ExternalProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..9e8c9f78e51f15e5060430a2a91d463a765b8fc4 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/ExternalProvider.cs @@ -0,0 +1,12 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + public class ExternalProvider + { + public string DisplayName { get; set; } + public string AuthenticationScheme { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/LoggedOutViewModel.cs b/OpenAuth.Identity/Quickstart/Account/LoggedOutViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..3eae77287dff22d7a5ead07cb283fdac11c5ff20 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LoggedOutViewModel.cs @@ -0,0 +1,19 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + public class LoggedOutViewModel + { + public string PostLogoutRedirectUri { get; set; } + public string ClientName { get; set; } + public string SignOutIframeUrl { get; set; } + + public bool AutomaticRedirectAfterSignOut { get; set; } = false; + + public string LogoutId { get; set; } + public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; + public string ExternalAuthenticationScheme { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Account/LoginInputModel.cs b/OpenAuth.Identity/Quickstart/Account/LoginInputModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..58fd09c57670b854cc3962ee8c7aae520c2a0216 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LoginInputModel.cs @@ -0,0 +1,18 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + 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/OpenAuth.Identity/Quickstart/Account/LoginViewModel.cs b/OpenAuth.Identity/Quickstart/Account/LoginViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..86b273a0123a600505c04967178df882a877e3be --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LoginViewModel.cs @@ -0,0 +1,22 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + 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/OpenAuth.Identity/Quickstart/Account/LogoutInputModel.cs b/OpenAuth.Identity/Quickstart/Account/LogoutInputModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..06f745af2193ec0dac6eb7e183ae7b1f49be199b --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LogoutInputModel.cs @@ -0,0 +1,11 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + public class LogoutInputModel + { + public string LogoutId { get; set; } + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/LogoutViewModel.cs b/OpenAuth.Identity/Quickstart/Account/LogoutViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..056cdf9e404bd105ef9790b294017d0ce300ed7e --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/LogoutViewModel.cs @@ -0,0 +1,11 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + public class LogoutViewModel : LogoutInputModel + { + public bool ShowLogoutPrompt { get; set; } = true; + } +} diff --git a/OpenAuth.Identity/Quickstart/Account/RedirectViewModel.cs b/OpenAuth.Identity/Quickstart/Account/RedirectViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..6a6da8d6d0701abb9b9ad22adb9776e5596d7ff7 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Account/RedirectViewModel.cs @@ -0,0 +1,12 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Account +{ + public class RedirectViewModel + { + public string RedirectUrl { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs new file mode 100644 index 0000000000000000000000000000000000000000..95343f8df3162ebb9ff8ab2a3683fd40caf10ade --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs @@ -0,0 +1,266 @@ +// 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 IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using OpenAuth.IdentityServer.Quickstart.Account; + +namespace OpenAuth.IdentityServer.Quickstart.Consent +{ + /// + /// This controller processes the consent UI + /// + [SecurityHeaders] + [Authorize] + public class ConsentController : Controller + { + private readonly IIdentityServerInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly IResourceStore _resourceStore; + private readonly IEventService _events; + private readonly ILogger _logger; + + public ConsentController( + IIdentityServerInteractionService interaction, + IClientStore clientStore, + IResourceStore resourceStore, + IEventService events, + ILogger logger) + { + _interaction = interaction; + _clientStore = clientStore; + _resourceStore = resourceStore; + _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) + { + if (await _clientStore.IsPkceClientAsync(result.ClientId)) + { + // if the client is PKCE then we assume it's native, so this change in how to + // return the response is for better UX for the end user. + return View("Redirect", new RedirectViewModel { RedirectUrl = 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 = ConsentResponse.Denied; + + // emit event + await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); + } + // 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, + ScopesConsented = scopes.ToArray() + }; + + // emit event + await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, 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.ClientId = request.ClientId; + } + 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) + { + var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); + if (client != null) + { + var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); + if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) + { + return CreateConsentViewModel(model, returnUrl, request, client, resources); + } + else + { + _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); + } + } + else + { + _logger.LogError("Invalid client id: {0}", request.ClientId); + } + } + else + { + _logger.LogError("No consent request matching request: {0}", returnUrl); + } + + return null; + } + + private ConsentViewModel CreateConsentViewModel( + ConsentInputModel model, string returnUrl, + AuthorizationRequest request, + Client client, Resources resources) + { + var vm = new ConsentViewModel + { + RememberConsent = model?.RememberConsent ?? true, + ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), + + ReturnUrl = returnUrl, + + ClientName = client.ClientName ?? client.ClientId, + ClientUrl = client.ClientUri, + ClientLogoUrl = client.LogoUri, + AllowRememberConsent = client.AllowRememberConsent + }; + + vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) + { + vm.ResourceScopes = vm.ResourceScopes.Union(new ScopeViewModel[] { + GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) + }); + } + + return vm; + } + + private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) + { + return new ScopeViewModel + { + Name = identity.Name, + DisplayName = identity.DisplayName, + Description = identity.Description, + Emphasize = identity.Emphasize, + Required = identity.Required, + Checked = check || identity.Required + }; + } + + public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) + { + return new ScopeViewModel + { + Name = scope.Name, + DisplayName = scope.DisplayName, + Description = scope.Description, + Emphasize = scope.Emphasize, + Required = scope.Required, + Checked = check || scope.Required + }; + } + + private ScopeViewModel GetOfflineAccessScope(bool check) + { + return new ScopeViewModel + { + Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, + DisplayName = ConsentOptions.OfflineAccessDisplayName, + Description = ConsentOptions.OfflineAccessDescription, + Emphasize = true, + Checked = check + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..7aeb4c4450a5cb8cbf17422c121d1888d2d16d97 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs @@ -0,0 +1,16 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ConsentInputModel + { + public string Button { get; set; } + public IEnumerable ScopesConsented { get; set; } + public bool RememberConsent { get; set; } + public string ReturnUrl { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs new file mode 100644 index 0000000000000000000000000000000000000000..d4727259cbd06ca34e93e6051ff2b528e557dfac --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs @@ -0,0 +1,16 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent +{ + 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/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..77a904413e6c19fbd58cfb75c4ee21a7eaa57771 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs @@ -0,0 +1,19 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent +{ + 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 ResourceScopes { get; set; } + } +} diff --git a/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs b/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..e3b3f00195da236159c43f8f4e2639e38a2b2fe0 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs @@ -0,0 +1,19 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ProcessConsentResult + { + public bool IsRedirect => RedirectUri != null; + public string RedirectUri { get; set; } + public string ClientId { 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/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs b/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..a75451af7e4e931a2c7bd98f94e897405c61c0a2 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs @@ -0,0 +1,16 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent +{ + public class ScopeViewModel + { + public string Name { 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/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..c78b6a2ab1e144149933419f5e662f8e6a1849fe --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs @@ -0,0 +1,13 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent; + +namespace OpenAuth.IdentityServer.Quickstart.Device +{ + public class DeviceAuthorizationInputModel : ConsentInputModel + { + public string UserCode { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..a1b2bb1c0b74f917bb68576f2e9be3b298672ba9 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs @@ -0,0 +1,14 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Consent; + +namespace OpenAuth.IdentityServer.Quickstart.Device +{ + public class DeviceAuthorizationViewModel : ConsentViewModel + { + public string UserCode { get; set; } + public bool ConfirmUserCode { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceController.cs b/OpenAuth.Identity/Quickstart/Device/DeviceController.cs new file mode 100644 index 0000000000000000000000000000000000000000..15fe30c0907d6fd3c15f5ae80403ca7a67da1cf6 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Device/DeviceController.cs @@ -0,0 +1,243 @@ +// 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 IdentityServer4.Configuration; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Models; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using OpenAuth.IdentityServer.Quickstart.Consent; + +namespace OpenAuth.IdentityServer.Quickstart.Device +{ + [Authorize] + [SecurityHeaders] + public class DeviceController : Controller + { + private readonly IDeviceFlowInteractionService _interaction; + private readonly IClientStore _clientStore; + private readonly IResourceStore _resourceStore; + private readonly IEventService _events; + private readonly IOptions _options; + private readonly ILogger _logger; + + public DeviceController( + IDeviceFlowInteractionService interaction, + IClientStore clientStore, + IResourceStore resourceStore, + IEventService eventService, + IOptions options, + ILogger logger) + { + _interaction = interaction; + _clientStore = clientStore; + _resourceStore = resourceStore; + _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 = ConsentResponse.Denied; + + // emit event + await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); + } + // 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, + ScopesConsented = scopes.ToArray() + }; + + // emit event + await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, 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.ClientId = request.ClientId; + } + 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) + { + var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); + if (client != null) + { + var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); + if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) + { + return CreateConsentViewModel(userCode, model, client, resources); + } + else + { + _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); + } + } + else + { + _logger.LogError("Invalid client id: {0}", request.ClientId); + } + } + + return null; + } + + private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, Client client, Resources resources) + { + var vm = new DeviceAuthorizationViewModel + { + UserCode = userCode, + + RememberConsent = model?.RememberConsent ?? true, + ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), + + ClientName = client.ClientName ?? client.ClientId, + ClientUrl = client.ClientUri, + ClientLogoUrl = client.LogoUri, + AllowRememberConsent = client.AllowRememberConsent + }; + + vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); + if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) + { + vm.ResourceScopes = vm.ResourceScopes.Union(new[] + { + GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) + }); + } + + return vm; + } + + private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) + { + return new ScopeViewModel + { + Name = identity.Name, + DisplayName = identity.DisplayName, + Description = identity.Description, + Emphasize = identity.Emphasize, + Required = identity.Required, + Checked = check || identity.Required + }; + } + + public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) + { + return new ScopeViewModel + { + Name = scope.Name, + DisplayName = scope.DisplayName, + Description = scope.Description, + Emphasize = scope.Emphasize, + Required = scope.Required, + Checked = check || scope.Required + }; + } + private ScopeViewModel GetOfflineAccessScope(bool check) + { + return new ScopeViewModel + { + Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, + DisplayName = ConsentOptions.OfflineAccessDisplayName, + Description = ConsentOptions.OfflineAccessDescription, + Emphasize = true, + Checked = check + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs new file mode 100644 index 0000000000000000000000000000000000000000..e4a9c456a212345112547c9c9cf1377f2a2b6f12 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs @@ -0,0 +1,29 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Diagnostics +{ + [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/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..e24c242e5c791b67183e2968c34e5df3bb3318a1 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs @@ -0,0 +1,32 @@ +// 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; +using System.Text; +using IdentityModel; +using Microsoft.AspNetCore.Authentication; +using Newtonsoft.Json; + +namespace OpenAuth.IdentityServer.Quickstart.Diagnostics +{ + 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 = JsonConvert.DeserializeObject(value); + } + } + + public AuthenticateResult AuthenticateResult { get; } + public IEnumerable Clients { get; } = new List(); + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Extensions.cs b/OpenAuth.Identity/Quickstart/Extensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..560e4b9f113d6ce6aee04676e2ecefa3c1d27aa0 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Extensions.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; +using IdentityServer4.Stores; + +namespace OpenAuth.IdentityServer.Quickstart +{ + public static class Extensions + { + /// + /// Determines whether the client is configured to use PKCE. + /// + /// The store. + /// The client identifier. + /// + public static async Task IsPkceClientAsync(this IClientStore store, string client_id) + { + if (!string.IsNullOrWhiteSpace(client_id)) + { + var client = await store.FindEnabledClientByIdAsync(client_id); + return client?.RequirePkce == true; + } + + return false; + } + } +} diff --git a/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs b/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs new file mode 100644 index 0000000000000000000000000000000000000000..c1d2695470d04f5cd447a86683063f20a33f1102 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs @@ -0,0 +1,96 @@ +// 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; +using System.Linq; +using System.Threading.Tasks; +using IdentityServer4.Events; +using IdentityServer4.Extensions; +using IdentityServer4.Services; +using IdentityServer4.Stores; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace OpenAuth.IdentityServer.Quickstart.Grants +{ + /// + /// 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.GetAllUserConsentsAsync(); + + 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, + Created = grant.CreationTime, + Expires = grant.Expiration, + IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), + ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray() + }; + + list.Add(item); + } + } + + return new GrantsViewModel + { + Grants = list + }; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs b/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..4281aebfa11677770cc5ea216ef2e4191827986e --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs @@ -0,0 +1,26 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Grants +{ + 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 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/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs b/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs new file mode 100644 index 0000000000000000000000000000000000000000..cf06e2ccc36249cbdc3ed449ca82ae020c9086aa --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs @@ -0,0 +1,22 @@ +// 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 OpenAuth.IdentityServer.Quickstart.Home +{ + public class ErrorViewModel + { + public ErrorViewModel() + { + } + + public ErrorViewModel(string error) + { + Error = new ErrorMessage { Error = error }; + } + + public ErrorMessage Error { get; set; } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Home/HomeController.cs b/OpenAuth.Identity/Quickstart/Home/HomeController.cs new file mode 100644 index 0000000000000000000000000000000000000000..2f89b2a72642de1b24cd941fd44d3c8be4983700 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/Home/HomeController.cs @@ -0,0 +1,64 @@ +// 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.Threading.Tasks; +using IdentityServer4.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace OpenAuth.IdentityServer.Quickstart.Home +{ + [SecurityHeaders] + [AllowAnonymous] + public class HomeController : Controller + { + private readonly IIdentityServerInteractionService _interaction; + private readonly IHostEnvironment _environment; + private readonly ILogger _logger; + + public HomeController(IIdentityServerInteractionService interaction, IHostEnvironment environment, ILogger logger) + { + _interaction = interaction; + _environment = environment; + _logger = logger; + } + + public IActionResult Index() + { + if (_environment.IsDevelopment()) + { + // only show in development + return View(); + } + + _logger.LogInformation("Homepage is disabled in production. Returning 404."); + return NotFound(); + } + + /// + /// 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/OpenAuth.Identity/Quickstart/SecurityHeadersAttribute.cs b/OpenAuth.Identity/Quickstart/SecurityHeadersAttribute.cs new file mode 100644 index 0000000000000000000000000000000000000000..8eb2afa100ce142a5499a918ddc1b59c2ddda2fd --- /dev/null +++ b/OpenAuth.Identity/Quickstart/SecurityHeadersAttribute.cs @@ -0,0 +1,56 @@ +// 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 OpenAuth.IdentityServer.Quickstart +{ + 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/OpenAuth.Identity/Quickstart/TestUsers.cs b/OpenAuth.Identity/Quickstart/TestUsers.cs new file mode 100644 index 0000000000000000000000000000000000000000..d6569a71c7e7e9b28e0e0059a9ef73f02aca4c23 --- /dev/null +++ b/OpenAuth.Identity/Quickstart/TestUsers.cs @@ -0,0 +1,25 @@ +// 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; +using System.Security.Claims; +using IdentityModel; +using IdentityServer4.Test; + +namespace OpenAuth.IdentityServer.Quickstart +{ + public class TestUsers + { + public static List Users = new List + { + new TestUser{SubjectId = "System", Username = "System", Password = "123456", + Claims = + { + new Claim(JwtClaimTypes.Name, "System"), + new Claim(JwtClaimTypes.GivenName, "yubao"), + new Claim(JwtClaimTypes.FamilyName, "lee")} + } + }; + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/SameSiteCookiesServiceCollectionExtensions.cs b/OpenAuth.Identity/SameSiteCookiesServiceCollectionExtensions.cs new file mode 100644 index 0000000000000000000000000000000000000000..752f8c06205f43211a0950d7a7fbb3c07fb84e08 --- /dev/null +++ b/OpenAuth.Identity/SameSiteCookiesServiceCollectionExtensions.cs @@ -0,0 +1,136 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace OpenAuth.IdentityServer +{ + public static class SameSiteCookiesServiceCollectionExtensions + { + /// + /// -1 defines the unspecified value, which tells ASPNET Core to NOT + /// send the SameSite attribute. With ASPNET Core 3.1 the + /// enum will have a definition for + /// Unspecified. + /// + private const SameSiteMode Unspecified = (SameSiteMode) (-1); + + /// + /// Configures a cookie policy to properly set the SameSite attribute + /// for Browsers that handle unknown values as Strict. Ensure that you + /// add the + /// into the pipeline before sending any cookies! + /// + /// + /// Minimum ASPNET Core Version required for this code: + /// - 2.1.14 + /// - 2.2.8 + /// - 3.0.1 + /// - 3.1.0-preview1 + /// Starting with version 80 of Chrome (to be released in February 2020) + /// cookies with NO SameSite attribute are treated as SameSite=Lax. + /// In order to always get the cookies send they need to be set to + /// SameSite=None. But since the current standard only defines Lax and + /// Strict as valid values there are some browsers that treat invalid + /// values as SameSite=Strict. We therefore need to check the browser + /// and either send SameSite=None or prevent the sending of SameSite=None. + /// Relevant links: + /// - https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1 + /// - https://tools.ietf.org/html/draft-west-cookie-incrementalism-00 + /// - https://www.chromium.org/updates/same-site + /// - https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ + /// - https://bugs.webkit.org/show_bug.cgi?id=198181 + /// + /// The service collection to register into. + /// The modified . + public static IServiceCollection ConfigureNonBreakingSameSiteCookies(this IServiceCollection services) + { + services.Configure(options => + { + options.MinimumSameSitePolicy = Unspecified; + options.OnAppendCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + options.OnDeleteCookie = cookieContext => + CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); + }); + + return services; + } + + private static void CheckSameSite(HttpContext httpContext, CookieOptions options) + { + if (options.SameSite == SameSiteMode.None) + { + var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); + + // if (DisallowsSameSiteNone(userAgent)) + // { + options.SameSite = Unspecified; + // } + } + } + + /// + /// Checks if the UserAgent is known to interpret an unknown value as Strict. + /// For those the property should be + /// set to . + /// + /// + /// This code is taken from Microsoft: + /// https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ + /// + /// The user agent string to check. + /// Whether the specified user agent (browser) accepts SameSite=None or not. + private static bool DisallowsSameSiteNone(string userAgent) + { + // Cover all iOS based browsers here. This includes: + // - Safari on iOS 12 for iPhone, iPod Touch, iPad + // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad + // - Chrome on iOS 12 for iPhone, iPod Touch, iPad + // All of which are broken by SameSite=None, because they use the + // iOS networking stack. + // Notes from Thinktecture: + // Regarding https://caniuse.com/#search=samesite iOS versions lower + // than 12 are not supporting SameSite at all. Starting with version 13 + // unknown values are NOT treated as strict anymore. Therefore we only + // need to check version 12. + if (userAgent.Contains("CPU iPhone OS 12") + || userAgent.Contains("iPad; CPU OS 12")) + { + return true; + } + + // Cover Mac OS X based browsers that use the Mac OS networking stack. + // This includes: + // - Safari on Mac OS X. + // This does not include: + // - Chrome on Mac OS X + // because they do not use the Mac OS networking stack. + // Notes from Thinktecture: + // Regarding https://caniuse.com/#search=samesite MacOS X versions lower + // than 10.14 are not supporting SameSite at all. Starting with version + // 10.15 unknown values are NOT treated as strict anymore. Therefore we + // only need to check version 10.14. + if (userAgent.Contains("Safari") + && userAgent.Contains("Macintosh; Intel Mac OS X 10_14") + && userAgent.Contains("Version/")) + { + return true; + } + + // Cover Chrome 50-69, because some versions are broken by SameSite=None + // and none in this range require it. + // Note: this covers some pre-Chromium Edge versions, + // but pre-Chromium Edge does not require SameSite=None. + // Notes from Thinktecture: + // We can not validate this assumption, but we trust Microsofts + // evaluation. And overall not sending a SameSite value equals to the same + // behavior as SameSite=None for these old versions anyways. + if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Startup.cs b/OpenAuth.Identity/Startup.cs new file mode 100644 index 0000000000000000000000000000000000000000..a7bfa8c49feab17d7249e017857b283cc0deb10b --- /dev/null +++ b/OpenAuth.Identity/Startup.cs @@ -0,0 +1,119 @@ +// 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 Autofac; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using OpenAuth.App; +using OpenAuth.Repository; + +namespace OpenAuth.IdentityServer +{ + public class Startup + { + public IHostEnvironment Environment { get; } + public IConfiguration Configuration { get; } + public Startup(IConfiguration configuration, IHostEnvironment environment) + { + Configuration = configuration; + Environment = environment; + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews(); + + var builder = services.AddIdentityServer() + .AddInMemoryIdentityResources(Config.GetIdentityResources()) + .AddInMemoryApiResources(Config.GetApis()) + .AddInMemoryClients(Config.GetClients(Environment.IsProduction())) + .AddProfileService(); + + services.ConfigureNonBreakingSameSiteCookies(); + + services.AddCors(); +// todo:如果正式 环境请用下面的方式限制随意访问跨域 +// var origins = new [] +// { +// "http://localhost:1803", +// "http://localhost:52789" +// }; +// if (Environment.IsProduction()) +// { +// origins = new [] +// { +// "http://demo.openauth.me:1803", +// "http://demo.openauth.me:52789" +// }; +// } +// services.AddCors(option=>option.AddPolicy("cors", policy => +// policy.AllowAnyHeader().AllowAnyMethod().AllowCredentials().WithOrigins(origins))); + + //全部用测试环境,正式环境请参考https://www.cnblogs.com/guolianyu/p/9872661.html + //if (Environment.IsDevelopment()) + //{ + builder.AddDeveloperSigningCredential(); + //} + //else + //{ + // throw new Exception("need to configure key material"); + //} + + services.AddAuthentication(); + + //映射配置文件 + services.Configure(Configuration.GetSection("AppSetting")); + + //在startup里面只能通过这种方式获取到appsettings里面的值,不能用IOptions😰 + var dbType = ((ConfigurationSection) Configuration.GetSection("AppSetting:DbType")).Value; + if (dbType == Define.DBTYPE_SQLSERVER) + { + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("OpenAuthDBContext"))); + } + else //mysql + { + services.AddDbContext(options => + options.UseMySql(Configuration.GetConnectionString("OpenAuthDBContext"))); + } + + } + + public void ConfigureContainer(ContainerBuilder builder) + { + AutofacExt.InitAutofac(builder); + } + + public void Configure(IApplicationBuilder app, IHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + + app.UseCookiePolicy(); + + //todo:测试可以允许任意跨域,正式环境要加权限 + app.UseCors(builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader()); + + app.UseStaticFiles(); + app.UseRouting(); + + app.UseIdentityServer(); + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + }); + } + } +} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Account/AccessDenied.cshtml b/OpenAuth.Identity/Views/Account/AccessDenied.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..ff22987b0aedf42ff031945d9f073edda590a94a --- /dev/null +++ b/OpenAuth.Identity/Views/Account/AccessDenied.cshtml @@ -0,0 +1,8 @@ + +
+ + +

You do not have access to that resource.

+
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Account/LoggedOut.cshtml b/OpenAuth.Identity/Views/Account/LoggedOut.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..14db8f928f9f3c85f107fb62b2db0bb3082d0c07 --- /dev/null +++ b/OpenAuth.Identity/Views/Account/LoggedOut.cshtml @@ -0,0 +1,34 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.LoggedOutViewModel + +@{ + // set this so the layout rendering sees an anonymous user + ViewData["signed-out"] = true; +} + + + +@section scripts +{ + @if (Model.AutomaticRedirectAfterSignOut) + { + + } +} diff --git a/OpenAuth.Identity/Views/Account/Login.cshtml b/OpenAuth.Identity/Views/Account/Login.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..d906d90f28f8fa2f2f0c1f7d77240b26b7440370 --- /dev/null +++ b/OpenAuth.Identity/Views/Account/Login.cshtml @@ -0,0 +1,92 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.LoginViewModel + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Account/Logout.cshtml b/OpenAuth.Identity/Views/Account/Logout.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..648fecc434cafc6465a327986e4376d632722850 --- /dev/null +++ b/OpenAuth.Identity/Views/Account/Logout.cshtml @@ -0,0 +1,21 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.LogoutViewModel + +
+ + +
+
+

Would you like to logout of IdentityServer?

+
+ +
+
+ +
+
+
+
+
+
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Consent/Index.cshtml b/OpenAuth.Identity/Views/Consent/Index.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..e990320c2b8e9841da3a033915be0a2129c3f9c1 --- /dev/null +++ b/OpenAuth.Identity/Views/Consent/Index.cshtml @@ -0,0 +1,82 @@ +@model OpenAuth.IdentityServer.Quickstart.Consent.ConsentViewModel + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/Success.cshtml b/OpenAuth.Identity/Views/Device/Success.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..f41cdefb6a73888d3ffdab988837b868bbf5874f --- /dev/null +++ b/OpenAuth.Identity/Views/Device/Success.cshtml @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml b/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..cbe38dd7a72177361032f24b75b693f7f66162d9 --- /dev/null +++ b/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml @@ -0,0 +1,14 @@ +@model string + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml b/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..9861210cf61530b4468d15d04b14fd832566d35b --- /dev/null +++ b/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml @@ -0,0 +1,93 @@ +@model OpenAuth.IdentityServer.Quickstart.Device.DeviceAuthorizationViewModel + + \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Diagnostics/Index.cshtml b/OpenAuth.Identity/Views/Diagnostics/Index.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..c14c6d4f86921d00517e823b24a4ef002495c5a1 --- /dev/null +++ b/OpenAuth.Identity/Views/Diagnostics/Index.cshtml @@ -0,0 +1,32 @@ +@model OpenAuth.IdentityServer.Quickstart.Diagnostics.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

+
    + @foreach (var client in Model.Clients) + { +
  • @client
  • + } +
+} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Grants/Index.cshtml b/OpenAuth.Identity/Views/Grants/Index.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..d34f007eb68a3d5b73e8fa79d381123905fac3dc --- /dev/null +++ b/OpenAuth.Identity/Views/Grants/Index.cshtml @@ -0,0 +1,79 @@ +@model OpenAuth.IdentityServer.Quickstart.Grants.GrantsViewModel + +
+ + + @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
+
+ Created: @grant.Created.ToString("yyyy-MM-dd") +
+ @if (grant.Expires.HasValue) + { +
+ Expires: @grant.Expires.Value.ToString("yyyy-MM-dd") +
+ } + @if (grant.IdentityGrantNames.Any()) + { +
+
Identity Grants
+
    + @foreach (var name in grant.IdentityGrantNames) + { +
  • @name
  • + } +
+
+ } + @if (grant.ApiGrantNames.Any()) + { +
+
API Grants
+
    + @foreach (var name in grant.ApiGrantNames) + { +
  • @name
  • + } +
+
+ } +
+
+
+ + +
+
+
+ } + } +
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Home/Index.cshtml b/OpenAuth.Identity/Views/Home/Index.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..57205690c357f57122323ce573a5e3ad61191437 --- /dev/null +++ b/OpenAuth.Identity/Views/Home/Index.cshtml @@ -0,0 +1,39 @@ +@{ + var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString(); +} + +
+ + +
+
+

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

+
+
+

+ Click here to manage your stored grants. +

+
+
+
+
+

+ Here are links to the + source code repository, + and ready to use samples. +

+
+
+
diff --git a/OpenAuth.Identity/Views/Shared/Error.cshtml b/OpenAuth.Identity/Views/Shared/Error.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..407f0579dc2b07ac8fce2de6192bc8a710947103 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/Error.cshtml @@ -0,0 +1,40 @@ +@model OpenAuth.IdentityServer.Quickstart.Home.ErrorViewModel + +@{ + var error = Model?.Error?.Error; + var errorDescription = Model?.Error?.ErrorDescription; + var request_id = Model?.Error?.RequestId; +} + +
+ + +
+
+
+ Sorry, there was an error + + @if (error != null) + { + + + : @error + + + + if (errorDescription != null) + { +
@errorDescription
+ } + } +
+ + @if (request_id != null) + { +
Request Id: @request_id
+ } +
+
+
diff --git a/OpenAuth.Identity/Views/Shared/Redirect.cshtml b/OpenAuth.Identity/Views/Shared/Redirect.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..73a7040598280fcd02a2431ffbc78c3b2b8c2939 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/Redirect.cshtml @@ -0,0 +1,7 @@ +@model OpenAuth.IdentityServer.Quickstart.Account.RedirectViewModel + +

You are now being returned to the application.

+

Once complete, you may close this tab

+ + + diff --git a/OpenAuth.Identity/Views/Shared/_Layout.cshtml b/OpenAuth.Identity/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..e825983a2051efac0fae22f945bd0aaee630a298 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/_Layout.cshtml @@ -0,0 +1,72 @@ +@using IdentityServer4.Extensions +@{ + string name = null; + if (!true.Equals(ViewData["signed-out"])) + { + name = Context.User?.GetDisplayName(); + } +} + + + + + + + OpenAuth.Net认证中心- 最好用的.net权限工作流框架|.net core快速开发框架|.net core权限管理|.net core工作流 + + + + + + + + + + +
+ @RenderBody() +
+ + + + + @RenderSection("scripts", required: false) + + diff --git a/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml b/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..1a3a6fd9d26de966353e20094c846d03f40a9307 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml @@ -0,0 +1,34 @@ +@model OpenAuth.IdentityServer.Quickstart.Consent.ScopeViewModel + +
  • + + @if (Model.Required) + { + (required) + } + @if (Model.Description != null) + { + + } +
  • \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Shared/_ValidationSummary.cshtml b/OpenAuth.Identity/Views/Shared/_ValidationSummary.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..674d68d8734a13095694bfc8a8183e59cb6000f8 --- /dev/null +++ b/OpenAuth.Identity/Views/Shared/_ValidationSummary.cshtml @@ -0,0 +1,7 @@ +@if (ViewContext.ModelState.IsValid == false) +{ +
    + Error +
    +
    +} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/_ViewImports.cshtml b/OpenAuth.Identity/Views/_ViewImports.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..afa82bbf249b2c6f92af293f2a1075f7c0f48bdc --- /dev/null +++ b/OpenAuth.Identity/Views/_ViewImports.cshtml @@ -0,0 +1 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/OpenAuth.Identity/Views/_ViewStart.cshtml b/OpenAuth.Identity/Views/_ViewStart.cshtml new file mode 100644 index 0000000000000000000000000000000000000000..a5f10045db97461e9565c3273fdb9252687aa4f1 --- /dev/null +++ b/OpenAuth.Identity/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} diff --git a/OpenAuth.Identity/appsettings.Development.json b/OpenAuth.Identity/appsettings.Development.json new file mode 100644 index 0000000000000000000000000000000000000000..e203e9407e74a6b9662aab8fde5d73ae64665f18 --- /dev/null +++ b/OpenAuth.Identity/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/OpenAuth.Identity/appsettings.Production.json b/OpenAuth.Identity/appsettings.Production.json new file mode 100644 index 0000000000000000000000000000000000000000..4817d665068b95b1034d5dcea78e8f8db330a511 --- /dev/null +++ b/OpenAuth.Identity/appsettings.Production.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "OpenAuthDBContext": "server=127.0.0.1;user id=root;database=openauthdb;password=000000" //my sql + }, + "AppSetting": { + "DbType": "MySql" //数据库类型:SqlServer/MySql + } +} diff --git a/OpenAuth.Identity/appsettings.json b/OpenAuth.Identity/appsettings.json new file mode 100644 index 0000000000000000000000000000000000000000..9eceb8e54e0abfaf729a6c76f782a631b33527b9 --- /dev/null +++ b/OpenAuth.Identity/appsettings.json @@ -0,0 +1,15 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "OpenAuthDBContext": "Data Source=.;Initial Catalog=OpenAuthDB;User=sa;Password=000000" + //"OpenAuthDBContext": "server=127.0.0.1;user id=root;database=openauthdb;password=000000" //my sql + }, + "AppSetting": { + "DbType": "SqlServer" //数据库类型:SqlServer/MySql + } +} diff --git a/OpenAuth.Identity/tempkey.rsa b/OpenAuth.Identity/tempkey.rsa new file mode 100644 index 0000000000000000000000000000000000000000..7b7bf59731c54e56a44e8077473710622aa774ce --- /dev/null +++ b/OpenAuth.Identity/tempkey.rsa @@ -0,0 +1 @@ +{"KeyId":"a33ecf9fadd0a1cd0aa7fbe05ab80eed","Parameters":{"D":"KqUVQKufCYXPJPp6/wCKYMX0044mHb/N+ZKjCQh7F5GQOvGpSEINAiPBuCEroyORDdT4jMwfTJYb4OPH9hOddBxLBZuKCNVZVlv4kxauiKMnUkEneIwOJL5thw5seAR1qnGxKZMby/1NlpqriY7ahYC5zHdlRm387xOz1/AtrjKilh5wYnSZOayq3lvjY8MRjf9v/89jnFNqM3i34ruUk5wOZgXwe6cNAaOG4xYJJ79+2x0hhqzwaDrpTfPQmmogDKf511OrcO1A6QokLN/qjdAYMFt0HLI6DN9GDePYjB8zUoXigtjO8Th4zHyUW2O2rHpsqExBALw1OGPaIeD97Q==","DP":"N6eDU4ySwI7VEFPUTwuWnRPxWoJWkzuTy19alD/2+c7wlcaYiIi26qZLdwL3j95xH645WxlKOMWFkU8HedQSUtCpNCEcKu7ejS/Gir/65V0fs3SxF1JT56RGF8E4Ma+vsIRRzKBsVtIuNca3mgDpdI1gxmuCradee2Z4fhKe3bU=","DQ":"26nTIdNfj7Ps68BSE74LynW4JdsUQorHMTGXgJfTSVth/hBkBbjF98Tjta2iG1EMRAJHveJ29lCQzxVYhxsrd1obyuD3mTX+n+k0ciHYAdGzPQKIel/iP8OYrsDvTgioAuS0Jy7WowT9TK7zC/yb/MbaqvQls1KcjF2jjSstpes=","Exponent":"AQAB","InverseQ":"ge27mwfvTlhFmMs1lMRG+WpFPrNHQtZFNPFoajJ9qL12VRaT0vodqW4M9MRp6Vs+Xvy+3pNzWkJRVZQq0HTN0oXIYgpSdG8stbt3g8orbTLsS8NFKWHnDsiKK1O1Dd9AsKoQmQbfPhRxECdWVQsCgTsedU6f24sJBIoBluF25BE=","Modulus":"+BjnkJNAxS78/tUd6trgJXV1Ltvh0Yw7NTa2qHlbD6wvR7IaXAWqJffzucyXFaOafs63tKiIXh1Tfhg+lQXIxXoJohyrz7exLqOkn4OvLGu3fjRDCF/2M2riDyJIGDNlt09e0NnrwQfUo1rhqr5k3N9T85LeETWA5i0EVRV3eanEbEOlNHtVPDEFm3LEKFm/FWxkchKnrjFz0t/2trsjo3gLDkslXKFkC/F9wPPldh5wCabVmr5pcKZpCPLnSzrRS35gF/g0+Fd9pZoflC+c798OsKFcsDDTKmtoRwU4FiX8I/z5H1pF2VHMEHpvU4akRD92TfmkKr+VdK+yiIZjxQ==","P":"/MQyAzEKfLXoWrT3tnrwFJ/sYv0A2Azl5wF9R1PmfBPphHA74KgIAe//88Q5PyhKDWaGW2BtUewr+fqMvEHtdXYwjYqz9q/NZ3hiZl+wDFkT6lz4ufwDKEZa+bUiAen0b8Ruk9sdwXSBJwLBq+rqEBaE1vPrlU68dOS2dMow5kc=","Q":"+0Vq//ZIhAadpc8riyD10/n9CkTiGecVc7WGsk6vfeDac2PaQvcDUDs/LeP7qT2Q9+2kra6jA7hOrPCxyP6PLukNOdqcmrdU+z2UJGZVLCZJ7RJJSt4H1fP26cJ/x1egkubVwImKiapYSpXWYLx9K8wTixZQHRAdzsK3qr1sD5M="}} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/css/site.css b/OpenAuth.Identity/wwwroot/css/site.css new file mode 100644 index 0000000000000000000000000000000000000000..a0bbbeda6a520c04739820b8fc4c221b7337a402 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/css/site.css @@ -0,0 +1,82 @@ +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/OpenAuth.Identity/wwwroot/css/site.less b/OpenAuth.Identity/wwwroot/css/site.less new file mode 100644 index 0000000000000000000000000000000000000000..09f514107e262f2d7119f320bc1e65ed4e667f85 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/css/site.less @@ -0,0 +1,116 @@ +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/OpenAuth.Identity/wwwroot/css/site.min.css b/OpenAuth.Identity/wwwroot/css/site.min.css new file mode 100644 index 0000000000000000000000000000000000000000..84ae0c4005f0433ccf1cf240b95d2f4ed7b50b27 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/css/site.min.css @@ -0,0 +1 @@ +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/OpenAuth.Identity/wwwroot/favicon.ico b/OpenAuth.Identity/wwwroot/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ee470e42facff8210727fb7caca55407becc0135 Binary files /dev/null and b/OpenAuth.Identity/wwwroot/favicon.ico differ diff --git a/OpenAuth.Identity/wwwroot/icon.jpg b/OpenAuth.Identity/wwwroot/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e6525028aca7ed7a339915a3986c38034e31ab1c Binary files /dev/null and b/OpenAuth.Identity/wwwroot/icon.jpg differ diff --git a/OpenAuth.Identity/wwwroot/icon.png b/OpenAuth.Identity/wwwroot/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cd386d5170151f6fb166bf1eab2486b1ffb3a421 Binary files /dev/null and b/OpenAuth.Identity/wwwroot/icon.png differ diff --git a/OpenAuth.Identity/wwwroot/js/signin-redirect.js b/OpenAuth.Identity/wwwroot/js/signin-redirect.js new file mode 100644 index 0000000000000000000000000000000000000000..6ebc5691c339bac89245e0e20f9ed46f39b90690 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/js/signin-redirect.js @@ -0,0 +1 @@ +window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); diff --git a/OpenAuth.Identity/wwwroot/js/signout-redirect.js b/OpenAuth.Identity/wwwroot/js/signout-redirect.js new file mode 100644 index 0000000000000000000000000000000000000000..cdfc5e78cd401821903ac8c322d516a637858a8d --- /dev/null +++ b/OpenAuth.Identity/wwwroot/js/signout-redirect.js @@ -0,0 +1,6 @@ +window.addEventListener("load", function () { + var a = document.querySelector("a.PostLogoutRedirectUri"); + if (a) { + window.location = a.href; + } +}); diff --git a/OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-ie6.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.css similarity index 100% rename from OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-ie6.css rename to OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.css diff --git a/OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-ie6.min.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.min.css similarity index 100% rename from OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-ie6.min.css rename to OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-ie6.min.css diff --git a/OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-responsive.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.css similarity index 100% rename from OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-responsive.css rename to OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.css diff --git a/OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-responsive.min.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.min.css similarity index 100% rename from OpenAuth.Mvc/js/ueditor/formdesign/bootstrap/css/bootstrap-responsive.min.css rename to OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-responsive.min.css diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css new file mode 100644 index 0000000000000000000000000000000000000000..31d8882661324aedb8693f8bb7322c4af8374ae0 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css @@ -0,0 +1,587 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); +} +.btn-default:active, +.btn-primary:active, +.btn-success:active, +.btn-info:active, +.btn-warning:active, +.btn-danger:active, +.btn-default.active, +.btn-primary.active, +.btn-success.active, +.btn-info.active, +.btn-warning.active, +.btn-danger.active { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-default.disabled, +.btn-primary.disabled, +.btn-success.disabled, +.btn-info.disabled, +.btn-warning.disabled, +.btn-danger.disabled, +.btn-default[disabled], +.btn-primary[disabled], +.btn-success[disabled], +.btn-info[disabled], +.btn-warning[disabled], +.btn-danger[disabled], +fieldset[disabled] .btn-default, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-danger { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default .badge, +.btn-primary .badge, +.btn-success .badge, +.btn-info .badge, +.btn-warning .badge, +.btn-danger .badge { + text-shadow: none; +} +.btn:active, +.btn.active { + background-image: none; +} +.btn-default { + text-shadow: 0 1px 0 #fff; + background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); + background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #dbdbdb; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus { + background-color: #e0e0e0; + background-position: 0 -15px; +} +.btn-default:active, +.btn-default.active { + background-color: #e0e0e0; + border-color: #dbdbdb; +} +.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: #e0e0e0; + background-image: none; +} +.btn-primary { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); + background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #245580; +} +.btn-primary:hover, +.btn-primary:focus { + background-color: #265a88; + background-position: 0 -15px; +} +.btn-primary:active, +.btn-primary.active { + background-color: #265a88; + border-color: #245580; +} +.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: #265a88; + background-image: none; +} +.btn-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #3e8f3e; +} +.btn-success:hover, +.btn-success:focus { + background-color: #419641; + background-position: 0 -15px; +} +.btn-success:active, +.btn-success.active { + background-color: #419641; + border-color: #3e8f3e; +} +.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: #419641; + background-image: none; +} +.btn-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #28a4c9; +} +.btn-info:hover, +.btn-info:focus { + background-color: #2aabd2; + background-position: 0 -15px; +} +.btn-info:active, +.btn-info.active { + background-color: #2aabd2; + border-color: #28a4c9; +} +.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: #2aabd2; + background-image: none; +} +.btn-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #e38d13; +} +.btn-warning:hover, +.btn-warning:focus { + background-color: #eb9316; + background-position: 0 -15px; +} +.btn-warning:active, +.btn-warning.active { + background-color: #eb9316; + border-color: #e38d13; +} +.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: #eb9316; + background-image: none; +} +.btn-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-color: #b92c28; +} +.btn-danger:hover, +.btn-danger:focus { + background-color: #c12e2a; + background-position: 0 -15px; +} +.btn-danger:active, +.btn-danger.active { + background-color: #c12e2a; + border-color: #b92c28; +} +.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: #c12e2a; + background-image: none; +} +.thumbnail, +.img-thumbnail { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + background-color: #e8e8e8; + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + background-color: #2e6da4; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.navbar-default { + background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); + background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); + background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255, 255, 255, .25); +} +.navbar-inverse { + background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); + background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + background-repeat: repeat-x; + border-radius: 4px; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .active > a { + background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); + background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); + background-repeat: repeat-x; + -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); + box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); +} +.navbar-inverse .navbar-brand, +.navbar-inverse .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); +} +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} +@media (max-width: 767px) { + .navbar .navbar-nav .open .dropdown-menu > .active > a, + .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; + } +} +.alert { + text-shadow: 0 1px 0 rgba(255, 255, 255, .2); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); +} +.alert-success { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border-color: #b2dba1; +} +.alert-info { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); + background-repeat: repeat-x; + border-color: #9acfea; +} +.alert-warning { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); + background-repeat: repeat-x; + border-color: #f5e79e; +} +.alert-danger { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); + background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); + background-repeat: repeat-x; + border-color: #dca7a7; +} +.progress { + background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); + background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-success { + background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); + background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-info { + background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); + background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-warning { + background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); + background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); + background-repeat: repeat-x; +} +.progress-bar-danger { + background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); + background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); + background-repeat: repeat-x; +} +.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); +} +.list-group { + border-radius: 4px; + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); + box-shadow: 0 1px 2px rgba(0, 0, 0, .075); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 #286090; + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); + background-repeat: repeat-x; + border-color: #2b669a; +} +.list-group-item.active .badge, +.list-group-item.active:hover .badge, +.list-group-item.active:focus .badge { + text-shadow: none; +} +.panel { + -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); + box-shadow: 0 1px 2px rgba(0, 0, 0, .05); +} +.panel-default > .panel-heading { + background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); + background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); + background-repeat: repeat-x; +} +.panel-primary > .panel-heading { + background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); + background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); + background-repeat: repeat-x; +} +.panel-success > .panel-heading { + background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); + background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); + background-repeat: repeat-x; +} +.panel-info > .panel-heading { + background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); + background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); + background-repeat: repeat-x; +} +.panel-warning > .panel-heading { + background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); + background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); + background-repeat: repeat-x; +} +.panel-danger > .panel-heading { + background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); + background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); + background-repeat: repeat-x; +} +.well { + background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); + background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); + background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); + background-repeat: repeat-x; + border-color: #dcdcdc; + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); +} +/*# sourceMappingURL=bootstrap-theme.css.map */ diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css.map b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css.map new file mode 100644 index 0000000000000000000000000000000000000000..d876f60fb4fd2a1c421a4813ef080ac97b670465 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["bootstrap-theme.css","less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"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","file":"bootstrap-theme.css","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\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-default.disabled,\n.btn-primary.disabled,\n.btn-success.disabled,\n.btn-info.disabled,\n.btn-warning.disabled,\n.btn-danger.disabled,\n.btn-default[disabled],\n.btn-primary[disabled],\n.btn-success[disabled],\n.btn-info[disabled],\n.btn-warning[disabled],\n.btn-danger[disabled],\nfieldset[disabled] .btn-default,\nfieldset[disabled] .btn-primary,\nfieldset[disabled] .btn-success,\nfieldset[disabled] .btn-info,\nfieldset[disabled] .btn-warning,\nfieldset[disabled] .btn-danger {\n -webkit-box-shadow: none;\n box-shadow: none;\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\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: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\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: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\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: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\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: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\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: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\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: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\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}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\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 have been 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","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css new file mode 100644 index 0000000000000000000000000000000000000000..5e39401957d87d5d810f3d05ab05aca5febd7b5d --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger.disabled,.btn-danger[disabled],.btn-default.disabled,.btn-default[disabled],.btn-info.disabled,.btn-info[disabled],.btn-primary.disabled,.btn-primary[disabled],.btn-success.disabled,.btn-success[disabled],.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-danger,fieldset[disabled] .btn-default,fieldset[disabled] .btn-info,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-warning{-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.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)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} +/*# sourceMappingURL=bootstrap-theme.min.css.map */ \ No newline at end of file diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css.map b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css.map new file mode 100644 index 0000000000000000000000000000000000000000..94813e9006074c2ce411b02c4014be94b5bb6039 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap-theme.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n .box-shadow(none);\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &,\n &:hover,\n &:focus,\n &.focus,\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n border-radius: @navbar-border-radius;\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\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 have been 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","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} \ No newline at end of file diff --git a/OpenAuth.WebTest/Content/bootstrap.css b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css similarity index 52% rename from OpenAuth.WebTest/Content/bootstrap.css rename to OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css index 6d6e68281ddecfe6611fbb2c9f79306e69359cab..680e7687862ea56dbd8f7717b23eeacc6e0de78c 100644 --- a/OpenAuth.WebTest/Content/bootstrap.css +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css @@ -1,26 +1,17 @@ -/* NUGET: BEGIN LICENSE TEXT - * - * Microsoft grants you the right to use these script files for the sole - * purpose of either: (i) interacting through your browser with the Microsoft - * website or online service, subject to the applicable licensing or use - * terms; or (ii) using the files as included with a Microsoft product subject - * to that product's license terms. Microsoft reserves all other rights to the - * files not expressly granted by Microsoft, whether by implication, estoppel - * or otherwise. The notices and licenses below are for informational purposes only. - * - * NUGET: END LICENSE TEXT */ /*! - * Bootstrap v3.0.0 - * - * Copyright 2013 Twitter, Inc - * Licensed under the Apache License v2.0 - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Designed and built with all the love in the world by @mdo and @fat. + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ - -/*! normalize.css v2.1.0 | MIT License | git.io/normalize */ - +/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} article, aside, details, @@ -30,95 +21,55 @@ footer, header, hgroup, main, +menu, nav, section, summary { display: block; } - audio, canvas, +progress, video { display: inline-block; + vertical-align: baseline; } - audio:not([controls]) { display: none; height: 0; } - -[hidden] { +[hidden], +template { display: none; } - -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} - -body { - margin: 0; -} - -a:focus { - outline: thin dotted; +a { + background-color: transparent; } - a:active, a:hover { outline: 0; } - -h1 { - margin: 0.67em 0; - font-size: 2em; -} - abbr[title] { border-bottom: 1px dotted; } - b, strong { font-weight: bold; } - dfn { font-style: italic; } - -hr { - height: 0; - -moz-box-sizing: content-box; - box-sizing: content-box; +h1 { + margin: .67em 0; + font-size: 2em; } - mark { color: #000; background: #ff0; } - -code, -kbd, -pre, -samp { - font-family: monospace, serif; - font-size: 1em; -} - -pre { - white-space: pre-wrap; -} - -q { - quotes: "\201C" "\201D" "\2018" "\2019"; -} - small { font-size: 80%; } - sub, sup { position: relative; @@ -126,110 +77,126 @@ sup { line-height: 0; vertical-align: baseline; } - sup { - top: -0.5em; + top: -.5em; } - sub { - bottom: -0.25em; + bottom: -.25em; } - img { border: 0; } - svg:not(:root) { overflow: hidden; } - figure { - margin: 0; + margin: 1em 40px; } - -fieldset { - padding: 0.35em 0.625em 0.75em; - margin: 0 2px; - border: 1px solid #c0c0c0; +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; } - -legend { - padding: 0; - border: 0; +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; } - button, input, +optgroup, select, textarea { margin: 0; - font-family: inherit; - font-size: 100%; + font: inherit; + color: inherit; } - -button, -input { - line-height: normal; +button { + overflow: visible; } - button, select { text-transform: none; } - button, html input[type="button"], input[type="reset"], input[type="submit"] { - cursor: pointer; -webkit-appearance: button; + cursor: pointer; } - button[disabled], html input[disabled] { cursor: default; } - +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} input[type="checkbox"], input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; padding: 0; - box-sizing: border-box; } - +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } - input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } - -button::-moz-focus-inner, -input::-moz-focus-inner { +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { padding: 0; border: 0; } - textarea { overflow: auto; - vertical-align: top; } - +optgroup { + font-weight: bold; +} table { - border-collapse: collapse; border-spacing: 0; + border-collapse: collapse; } - +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ @media print { - * { + *, + *:before, + *:after { color: #000 !important; text-shadow: none !important; background: transparent !important; - box-shadow: none !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; } a, a:visited { @@ -241,14 +208,14 @@ table { abbr[title]:after { content: " (" attr(title) ")"; } - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { + a[href^="#"]:after, + a[href^="javascript:"]:after { content: ""; } pre, blockquote { border: 1px solid #999; + page-break-inside: avoid; } thead { @@ -261,9 +228,6 @@ table { img { max-width: 100% !important; } - @page { - margin: 2cm .5cm; - } p, h2, h3 { @@ -277,10 +241,6 @@ table { .navbar { display: none; } - .table td, - .table th { - background-color: #fff !important; - } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; @@ -291,2947 +251,3325 @@ table { .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'; -*, -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; + 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; -html { - font-size: 62.5%; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } - -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.428571429; - color: #333333; - background-color: #ffffff; +.glyphicon-asterisk:before { + content: "\2a"; } - -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; +.glyphicon-plus:before { + content: "\2b"; } - -button, -input, -select[multiple], -textarea { - background-image: none; +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; } - -a { - color: #428bca; - text-decoration: none; +.glyphicon-minus:before { + content: "\2212"; } - -a:hover, -a:focus { - color: #2a6496; - text-decoration: underline; +.glyphicon-cloud:before { + content: "\2601"; } - -a:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; +.glyphicon-envelope:before { + content: "\2709"; } - -img { - vertical-align: middle; +.glyphicon-pencil:before { + content: "\270f"; } - -.img-responsive { - display: block; - height: auto; - max-width: 100%; +.glyphicon-glass:before { + content: "\e001"; } - -.img-rounded { - border-radius: 6px; +.glyphicon-music:before { + content: "\e002"; } - -.img-thumbnail { - display: inline-block; - height: auto; - max-width: 100%; - padding: 4px; - line-height: 1.428571429; - background-color: #ffffff; - border: 1px solid #dddddd; - border-radius: 4px; - -webkit-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; +.glyphicon-search:before { + content: "\e003"; } - -.img-circle { - border-radius: 50%; +.glyphicon-heart:before { + content: "\e005"; } - -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eeeeee; +.glyphicon-star:before { + content: "\e006"; } - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0 0 0 0); - border: 0; +.glyphicon-star-empty:before { + content: "\e007"; } - -p { - margin: 0 0 10px; +.glyphicon-user:before { + content: "\e008"; } - -.lead { - margin-bottom: 20px; - font-size: 16.099999999999998px; - font-weight: 200; - line-height: 1.4; +.glyphicon-film:before { + content: "\e009"; } - -@media (min-width: 768px) { - .lead { - font-size: 21px; - } +.glyphicon-th-large:before { + content: "\e010"; } - -small { - font-size: 85%; +.glyphicon-th:before { + content: "\e011"; } - -cite { - font-style: normal; +.glyphicon-th-list:before { + content: "\e012"; } - -.text-muted { - color: #999999; +.glyphicon-ok:before { + content: "\e013"; } - -.text-primary { - color: #428bca; +.glyphicon-remove:before { + content: "\e014"; } - -.text-warning { - color: #c09853; +.glyphicon-zoom-in:before { + content: "\e015"; } - -.text-danger { - color: #b94a48; +.glyphicon-zoom-out:before { + content: "\e016"; } - -.text-success { - color: #468847; +.glyphicon-off:before { + content: "\e017"; } - -.text-info { - color: #3a87ad; +.glyphicon-signal:before { + content: "\e018"; } - -.text-left { - text-align: left; +.glyphicon-cog:before { + content: "\e019"; } - -.text-right { - text-align: right; +.glyphicon-trash:before { + content: "\e020"; } - -.text-center { - text-align: center; +.glyphicon-home:before { + content: "\e021"; } - -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-weight: 500; - line-height: 1.1; +.glyphicon-file:before { + content: "\e022"; } - -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: #999999; +.glyphicon-time:before { + content: "\e023"; } - -h1, -h2, -h3 { - margin-top: 20px; - margin-bottom: 10px; +.glyphicon-road:before { + content: "\e024"; } - -h4, -h5, -h6 { - margin-top: 10px; - margin-bottom: 10px; +.glyphicon-download-alt:before { + content: "\e025"; } - -h1, -.h1 { - font-size: 36px; +.glyphicon-download:before { + content: "\e026"; } - -h2, -.h2 { - font-size: 30px; +.glyphicon-upload:before { + content: "\e027"; } - -h3, -.h3 { - font-size: 24px; +.glyphicon-inbox:before { + content: "\e028"; } - -h4, -.h4 { - font-size: 18px; +.glyphicon-play-circle:before { + content: "\e029"; } - -h5, -.h5 { - font-size: 14px; +.glyphicon-repeat:before { + content: "\e030"; } - -h6, -.h6 { - font-size: 12px; +.glyphicon-refresh:before { + content: "\e031"; } - -h1 small, -.h1 small { - font-size: 24px; +.glyphicon-list-alt:before { + content: "\e032"; } - -h2 small, -.h2 small { - font-size: 18px; +.glyphicon-lock:before { + content: "\e033"; } - -h3 small, -.h3 small, -h4 small, -.h4 small { - font-size: 14px; +.glyphicon-flag:before { + content: "\e034"; } - -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eeeeee; +.glyphicon-headphones:before { + content: "\e035"; } - -ul, -ol { - margin-top: 0; - margin-bottom: 10px; +.glyphicon-volume-off:before { + content: "\e036"; } - -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; +.glyphicon-volume-down:before { + content: "\e037"; } - -.list-unstyled { - padding-left: 0; - list-style: none; +.glyphicon-volume-up:before { + content: "\e038"; } - -.list-inline { - padding-left: 0; - list-style: none; +.glyphicon-qrcode:before { + content: "\e039"; } - -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; +.glyphicon-barcode:before { + content: "\e040"; } - -dl { - margin-bottom: 20px; +.glyphicon-tag:before { + content: "\e041"; } - -dt, -dd { - line-height: 1.428571429; +.glyphicon-tags:before { + content: "\e042"; } - -dt { - font-weight: bold; +.glyphicon-book:before { + content: "\e043"; } - -dd { - margin-left: 0; +.glyphicon-bookmark:before { + content: "\e044"; } - -@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; - } - .dl-horizontal dd:before, - .dl-horizontal dd:after { - display: table; - content: " "; - } - .dl-horizontal dd:after { - clear: both; - } - .dl-horizontal dd:before, - .dl-horizontal dd:after { - display: table; - content: " "; - } - .dl-horizontal dd:after { - clear: both; - } +.glyphicon-print:before { + content: "\e045"; } - -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; +.glyphicon-camera:before { + content: "\e046"; } - -abbr.initialism { - font-size: 90%; - text-transform: uppercase; +.glyphicon-font:before { + content: "\e047"; } - -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - border-left: 5px solid #eeeeee; +.glyphicon-bold:before { + content: "\e048"; } - -blockquote p { - font-size: 17.5px; - font-weight: 300; - line-height: 1.25; +.glyphicon-italic:before { + content: "\e049"; } - -blockquote p:last-child { - margin-bottom: 0; +.glyphicon-text-height:before { + content: "\e050"; } - -blockquote small { - display: block; - line-height: 1.428571429; - color: #999999; +.glyphicon-text-width:before { + content: "\e051"; } - -blockquote small:before { - content: '\2014 \00A0'; +.glyphicon-align-left:before { + content: "\e052"; } - -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; +.glyphicon-align-center:before { + content: "\e053"; } - -blockquote.pull-right p, -blockquote.pull-right small { - text-align: right; +.glyphicon-align-right:before { + content: "\e054"; } - -blockquote.pull-right small:before { - content: ''; +.glyphicon-align-justify:before { + content: "\e055"; } - -blockquote.pull-right small:after { - content: '\00A0 \2014'; +.glyphicon-list:before { + content: "\e056"; } - -q:before, -q:after, -blockquote:before, -blockquote:after { - content: ""; +.glyphicon-indent-left:before { + content: "\e057"; } - -address { - display: block; - margin-bottom: 20px; - font-style: normal; - line-height: 1.428571429; +.glyphicon-indent-right:before { + content: "\e058"; } - -code, -pre { - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; +.glyphicon-facetime-video:before { + content: "\e059"; } - -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - white-space: nowrap; - background-color: #f9f2f4; - border-radius: 4px; +.glyphicon-picture:before { + content: "\e060"; } - -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.428571429; - color: #333333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #cccccc; - border-radius: 4px; +.glyphicon-map-marker:before { + content: "\e062"; } - -pre.prettyprint { - margin-bottom: 20px; +.glyphicon-adjust:before { + content: "\e063"; } - -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border: 0; +.glyphicon-tint:before { + content: "\e064"; } - -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; +.glyphicon-edit:before { + content: "\e065"; } - -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; +.glyphicon-share:before { + content: "\e066"; } - -.container:before, -.container:after { - display: table; - content: " "; +.glyphicon-check:before { + content: "\e067"; } - -.container:after { - clear: both; +.glyphicon-move:before { + content: "\e068"; } - -.container:before, -.container:after { - display: table; - content: " "; +.glyphicon-step-backward:before { + content: "\e069"; } - -.container:after { - clear: both; +.glyphicon-fast-backward:before { + content: "\e070"; } - -.row { - margin-right: -15px; - margin-left: -15px; +.glyphicon-backward:before { + content: "\e071"; } - -.row:before, -.row:after { - display: table; - content: " "; +.glyphicon-play:before { + content: "\e072"; } - -.row:after { - clear: both; +.glyphicon-pause:before { + content: "\e073"; } - -.row:before, -.row:after { - display: table; - content: " "; +.glyphicon-stop:before { + content: "\e074"; } - -.row:after { - clear: both; +.glyphicon-forward:before { + content: "\e075"; } - -.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, -.col-sm-1, -.col-sm-2, -.col-sm-3, -.col-sm-4, -.col-sm-5, -.col-sm-6, -.col-sm-7, -.col-sm-8, -.col-sm-9, -.col-sm-10, -.col-sm-11, -.col-sm-12, -.col-md-1, -.col-md-2, -.col-md-3, -.col-md-4, -.col-md-5, -.col-md-6, -.col-md-7, -.col-md-8, -.col-md-9, -.col-md-10, -.col-md-11, -.col-md-12, -.col-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 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; +.glyphicon-fast-forward:before { + content: "\e076"; } - -.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 { - float: left; +.glyphicon-step-forward:before { + content: "\e077"; } - -.col-xs-1 { - width: 8.333333333333332%; +.glyphicon-eject:before { + content: "\e078"; } - -.col-xs-2 { - width: 16.666666666666664%; +.glyphicon-chevron-left:before { + content: "\e079"; } - -.col-xs-3 { - width: 25%; +.glyphicon-chevron-right:before { + content: "\e080"; } - -.col-xs-4 { - width: 33.33333333333333%; +.glyphicon-plus-sign:before { + content: "\e081"; } - -.col-xs-5 { - width: 41.66666666666667%; +.glyphicon-minus-sign:before { + content: "\e082"; } - -.col-xs-6 { - width: 50%; +.glyphicon-remove-sign:before { + content: "\e083"; } - -.col-xs-7 { - width: 58.333333333333336%; +.glyphicon-ok-sign:before { + content: "\e084"; } - -.col-xs-8 { - width: 66.66666666666666%; +.glyphicon-question-sign:before { + content: "\e085"; } - -.col-xs-9 { - width: 75%; +.glyphicon-info-sign:before { + content: "\e086"; } - -.col-xs-10 { - width: 83.33333333333334%; +.glyphicon-screenshot:before { + content: "\e087"; } - -.col-xs-11 { - width: 91.66666666666666%; +.glyphicon-remove-circle:before { + content: "\e088"; } - -.col-xs-12 { - width: 100%; +.glyphicon-ok-circle:before { + content: "\e089"; } - -@media (min-width: 768px) { - .container { - max-width: 750px; - } - .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 { - float: left; - } - .col-sm-1 { - width: 8.333333333333332%; - } - .col-sm-2 { - width: 16.666666666666664%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-4 { - width: 33.33333333333333%; - } - .col-sm-5 { - width: 41.66666666666667%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-7 { - width: 58.333333333333336%; - } - .col-sm-8 { - width: 66.66666666666666%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-10 { - width: 83.33333333333334%; - } - .col-sm-11 { - width: 91.66666666666666%; - } - .col-sm-12 { - width: 100%; - } - .col-sm-push-1 { - left: 8.333333333333332%; - } - .col-sm-push-2 { - left: 16.666666666666664%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-4 { - left: 33.33333333333333%; - } - .col-sm-push-5 { - left: 41.66666666666667%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-7 { - left: 58.333333333333336%; - } - .col-sm-push-8 { - left: 66.66666666666666%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-10 { - left: 83.33333333333334%; - } - .col-sm-push-11 { - left: 91.66666666666666%; - } - .col-sm-pull-1 { - right: 8.333333333333332%; - } - .col-sm-pull-2 { - right: 16.666666666666664%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-4 { - right: 33.33333333333333%; - } - .col-sm-pull-5 { - right: 41.66666666666667%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-7 { - right: 58.333333333333336%; - } - .col-sm-pull-8 { - right: 66.66666666666666%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-10 { - right: 83.33333333333334%; - } - .col-sm-pull-11 { - right: 91.66666666666666%; - } - .col-sm-offset-1 { - margin-left: 8.333333333333332%; - } - .col-sm-offset-2 { - margin-left: 16.666666666666664%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-4 { - margin-left: 33.33333333333333%; - } - .col-sm-offset-5 { - margin-left: 41.66666666666667%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-7 { - margin-left: 58.333333333333336%; - } - .col-sm-offset-8 { - margin-left: 66.66666666666666%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-10 { - margin-left: 83.33333333333334%; - } - .col-sm-offset-11 { - margin-left: 91.66666666666666%; - } +.glyphicon-ban-circle:before { + content: "\e090"; } - -@media (min-width: 992px) { - .container { - max-width: 970px; - } - .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 { - float: left; - } - .col-md-1 { - width: 8.333333333333332%; - } - .col-md-2 { - width: 16.666666666666664%; - } - .col-md-3 { - width: 25%; - } - .col-md-4 { - width: 33.33333333333333%; - } - .col-md-5 { - width: 41.66666666666667%; - } - .col-md-6 { - width: 50%; - } - .col-md-7 { - width: 58.333333333333336%; - } - .col-md-8 { - width: 66.66666666666666%; - } - .col-md-9 { - width: 75%; - } - .col-md-10 { - width: 83.33333333333334%; - } - .col-md-11 { - width: 91.66666666666666%; - } - .col-md-12 { - width: 100%; - } - .col-md-push-0 { - left: auto; - } - .col-md-push-1 { - left: 8.333333333333332%; - } - .col-md-push-2 { - left: 16.666666666666664%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-4 { - left: 33.33333333333333%; - } - .col-md-push-5 { - left: 41.66666666666667%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-7 { - left: 58.333333333333336%; - } - .col-md-push-8 { - left: 66.66666666666666%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-10 { - left: 83.33333333333334%; - } - .col-md-push-11 { - left: 91.66666666666666%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-pull-1 { - right: 8.333333333333332%; - } - .col-md-pull-2 { - right: 16.666666666666664%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-4 { - right: 33.33333333333333%; - } - .col-md-pull-5 { - right: 41.66666666666667%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-7 { - right: 58.333333333333336%; - } - .col-md-pull-8 { - right: 66.66666666666666%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-10 { - right: 83.33333333333334%; - } - .col-md-pull-11 { - right: 91.66666666666666%; - } - .col-md-offset-0 { - margin-left: 0; - } - .col-md-offset-1 { - margin-left: 8.333333333333332%; - } - .col-md-offset-2 { - margin-left: 16.666666666666664%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-4 { - margin-left: 33.33333333333333%; - } - .col-md-offset-5 { - margin-left: 41.66666666666667%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-7 { - margin-left: 58.333333333333336%; - } - .col-md-offset-8 { - margin-left: 66.66666666666666%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-10 { - margin-left: 83.33333333333334%; - } - .col-md-offset-11 { - margin-left: 91.66666666666666%; - } +.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"; } - -@media (min-width: 1200px) { - .container { - max-width: 1170px; - } - .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 { - float: left; - } - .col-lg-1 { - width: 8.333333333333332%; - } - .col-lg-2 { - width: 16.666666666666664%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-4 { - width: 33.33333333333333%; - } - .col-lg-5 { - width: 41.66666666666667%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-7 { - width: 58.333333333333336%; - } - .col-lg-8 { - width: 66.66666666666666%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-10 { - width: 83.33333333333334%; - } - .col-lg-11 { - width: 91.66666666666666%; - } - .col-lg-12 { - width: 100%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-push-1 { - left: 8.333333333333332%; - } - .col-lg-push-2 { - left: 16.666666666666664%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-4 { - left: 33.33333333333333%; - } - .col-lg-push-5 { - left: 41.66666666666667%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-7 { - left: 58.333333333333336%; - } - .col-lg-push-8 { - left: 66.66666666666666%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-10 { - left: 83.33333333333334%; - } - .col-lg-push-11 { - left: 91.66666666666666%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-pull-1 { - right: 8.333333333333332%; - } - .col-lg-pull-2 { - right: 16.666666666666664%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-4 { - right: 33.33333333333333%; - } - .col-lg-pull-5 { - right: 41.66666666666667%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-7 { - right: 58.333333333333336%; - } - .col-lg-pull-8 { - right: 66.66666666666666%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-10 { - right: 83.33333333333334%; - } - .col-lg-pull-11 { - right: 91.66666666666666%; - } - .col-lg-offset-0 { - margin-left: 0; - } - .col-lg-offset-1 { - margin-left: 8.333333333333332%; - } - .col-lg-offset-2 { - margin-left: 16.666666666666664%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-4 { - margin-left: 33.33333333333333%; - } - .col-lg-offset-5 { - margin-left: 41.66666666666667%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-7 { - margin-left: 58.333333333333336%; - } - .col-lg-offset-8 { - margin-left: 66.66666666666666%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-10 { - margin-left: 83.33333333333334%; - } - .col-lg-offset-11 { - margin-left: 91.66666666666666%; - } +.glyphicon-hd-video:before { + content: "\e187"; } - -table { - max-width: 100%; - background-color: transparent; +.glyphicon-subtitles:before { + content: "\e188"; } - -th { - text-align: left; +.glyphicon-sound-stereo:before { + content: "\e189"; } - -.table { - width: 100%; - margin-bottom: 20px; +.glyphicon-sound-dolby:before { + content: "\e190"; } - -.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.428571429; - vertical-align: top; - border-top: 1px solid #dddddd; +.glyphicon-sound-5-1:before { + content: "\e191"; } - -.table thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #dddddd; +.glyphicon-sound-6-1:before { + content: "\e192"; } - -.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; +.glyphicon-sound-7-1:before { + content: "\e193"; } - -.table tbody + tbody { - border-top: 2px solid #dddddd; +.glyphicon-copyright-mark:before { + content: "\e194"; } - -.table .table { - background-color: #ffffff; +.glyphicon-registration-mark:before { + content: "\e195"; } - -.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; +.glyphicon-cloud-download:before { + content: "\e197"; } - -.table-bordered { - border: 1px solid #dddddd; +.glyphicon-cloud-upload:before { + content: "\e198"; } - -.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 #dddddd; +.glyphicon-tree-conifer:before { + content: "\e199"; } - -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; +.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; -.table-striped > tbody > tr:nth-child(odd) > td, -.table-striped > tbody > tr:nth-child(odd) > th { - background-color: #f9f9f9; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - -.table-hover > tbody > tr:hover > td, -.table-hover > tbody > tr:hover > th { - background-color: #f5f5f5; +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; } - -table col[class*="col-"] { - display: table-column; - float: none; +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; } - -table td[class*="col-"], -table th[class*="col-"] { - display: table-cell; - float: none; +a { + color: #337ab7; + text-decoration: 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; +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; } - -.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; - border-color: #d6e9c6; +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; } - -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td { - background-color: #d0e9c6; - border-color: #c9e2b3; +figure { + margin: 0; } - -.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; - border-color: #eed3d7; +img { + vertical-align: middle; } - -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td { - background-color: #ebcccc; - border-color: #e6c1c7; +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; } - -.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; - border-color: #fbeed5; +.img-rounded { + border-radius: 6px; } - -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td { - background-color: #faf2cc; - border-color: #f8e5be; +.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; } - -@media (max-width: 768px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-x: scroll; - overflow-y: hidden; - border: 1px solid #dddddd; - } - .table-responsive > .table { - margin-bottom: 0; - background-color: #fff; - } - .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 > thead > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > thead > tr:last-child > td, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } +.img-circle { + border-radius: 50%; } - -fieldset { - padding: 0; - margin: 0; +hr { + margin-top: 20px; + margin-bottom: 20px; border: 0; + border-top: 1px solid #eee; } - -legend { - display: block; - width: 100%; +.sr-only { + position: absolute; + width: 1px; + height: 1px; padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333333; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); border: 0; - border-bottom: 1px solid #e5e5e5; } - -label { - display: inline-block; - margin-bottom: 5px; - font-weight: bold; +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; } - -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; +[role="button"] { + cursor: pointer; } - -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - /* IE8-9 */ - - line-height: normal; +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; } - -input[type="file"] { - display: block; +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%; } - -select[multiple], -select[size] { - height: auto; +h1, +.h1 { + font-size: 36px; } - -select optgroup { - font-family: inherit; - font-size: inherit; - font-style: inherit; +h2, +.h2 { + font-size: 30px; } - -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; +h3, +.h3 { + font-size: 24px; } - -input[type="number"]::-webkit-outer-spin-button, -input[type="number"]::-webkit-inner-spin-button { - height: auto; +h4, +.h4 { + font-size: 18px; } - -.form-control:-moz-placeholder { - color: #999999; +h5, +.h5 { + font-size: 14px; } - -.form-control::-moz-placeholder { - color: #999999; +h6, +.h6 { + font-size: 12px; } - -.form-control:-ms-input-placeholder { - color: #999999; +p { + margin: 0 0 10px; } - -.form-control::-webkit-input-placeholder { - color: #999999; +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; } - -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.428571429; - color: #555555; - vertical-align: middle; - background-color: #ffffff; - border: 1px solid #cccccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; - transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +@media (min-width: 768px) { + .lead { + font-size: 21px; + } } - -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +small, +.small { + font-size: 85%; } - -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - cursor: not-allowed; - background-color: #eeeeee; +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; } - -textarea.form-control { - height: auto; +.text-left { + text-align: left; } - -.form-group { - margin-bottom: 15px; +.text-right { + text-align: right; } - -.radio, -.checkbox { - display: block; - min-height: 20px; - padding-left: 20px; - margin-top: 10px; - margin-bottom: 10px; - vertical-align: middle; +.text-center { + text-align: center; } - -.radio label, -.checkbox label { - display: inline; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; +.text-justify { + text-align: justify; } - -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - float: left; - margin-left: -20px; +.text-nowrap { + white-space: nowrap; } - -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; +.text-lowercase { + text-transform: lowercase; } - -.radio-inline, -.checkbox-inline { - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; +.text-uppercase { + text-transform: uppercase; } - -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; +.text-capitalize { + text-transform: capitalize; } - -input[type="radio"][disabled], -input[type="checkbox"][disabled], -.radio[disabled], -.radio-inline[disabled], -.checkbox[disabled], -.checkbox-inline[disabled], -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"], -fieldset[disabled] .radio, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; +.text-muted { + color: #777; } - -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; +.text-primary { + color: #337ab7; } - -select.input-sm { - height: 30px; - line-height: 30px; +a.text-primary:hover, +a.text-primary:focus { + color: #286090; } - -textarea.input-sm { - height: auto; +.text-success { + color: #3c763d; } - -.input-lg { - height: 45px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; +a.text-success:hover, +a.text-success:focus { + color: #2b542c; } - -select.input-lg { - height: 45px; - line-height: 45px; +.text-info { + color: #31708f; } - -textarea.input-lg { - height: auto; +a.text-info:hover, +a.text-info:focus { + color: #245269; } - -.has-warning .help-block, -.has-warning .control-label { - color: #c09853; +.text-warning { + color: #8a6d3b; } - -.has-warning .form-control { - border-color: #c09853; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +a.text-warning:hover, +a.text-warning:focus { + color: #66512c; } - -.has-warning .form-control:focus { - border-color: #a47e3c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e; +.text-danger { + color: #a94442; } - -.has-warning .input-group-addon { - color: #c09853; - background-color: #fcf8e3; - border-color: #c09853; +a.text-danger:hover, +a.text-danger:focus { + color: #843534; } - -.has-error .help-block, -.has-error .control-label { - color: #b94a48; +.bg-primary { + color: #fff; + background-color: #337ab7; } - -.has-error .form-control { - border-color: #b94a48; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +a.bg-primary:hover, +a.bg-primary:focus { + background-color: #286090; } - -.has-error .form-control:focus { - border-color: #953b39; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392; +.bg-success { + background-color: #dff0d8; } - -.has-error .input-group-addon { - color: #b94a48; - background-color: #f2dede; - border-color: #b94a48; +a.bg-success:hover, +a.bg-success:focus { + background-color: #c1e2b3; } - -.has-success .help-block, -.has-success .control-label { - color: #468847; +.bg-info { + background-color: #d9edf7; } - -.has-success .form-control { - border-color: #468847; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +a.bg-info:hover, +a.bg-info:focus { + background-color: #afd9ee; } - -.has-success .form-control:focus { - border-color: #356635; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b; +.bg-warning { + background-color: #fcf8e3; } - -.has-success .input-group-addon { - color: #468847; - background-color: #dff0d8; - border-color: #468847; +a.bg-warning:hover, +a.bg-warning:focus { + background-color: #f7ecb5; } - -.form-control-static { - padding-top: 7px; - margin-bottom: 0; +.bg-danger { + background-color: #f2dede; } - -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; +a.bg-danger:hover, +a.bg-danger:focus { + background-color: #e4b9b9; } - -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - padding-left: 0; - margin-top: 0; - margin-bottom: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - float: none; - margin-left: 0; - } +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; } - -.form-horizontal .control-label, -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; +ul, +ol { margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { margin-bottom: 0; } - -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; +.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; } - -.form-horizontal .form-group:before, -.form-horizontal .form-group:after { - display: table; - content: " "; +dl { + margin-top: 0; + margin-bottom: 20px; } - -.form-horizontal .form-group:after { - clear: both; +dt, +dd { + line-height: 1.42857143; } - -.form-horizontal .form-group:before, -.form-horizontal .form-group:after { - display: table; - content: " "; +dt { + font-weight: bold; } - -.form-horizontal .form-group:after { - clear: both; +dd { + margin-left: 0; } - @media (min-width: 768px) { - .form-horizontal .control-label { + .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; } } - -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.428571429; - text-align: center; - white-space: nowrap; - vertical-align: middle; - cursor: pointer; - border: 1px solid transparent; - border-radius: 4px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - -o-user-select: none; - user-select: none; +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; } - -.btn:focus { - outline: thin dotted #333; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; +.initialism { + font-size: 90%; + text-transform: uppercase; } - -.btn:hover, -.btn:focus { - color: #333333; - text-decoration: none; +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; } - -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; } - -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - pointer-events: none; - cursor: not-allowed; - opacity: 0.65; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; } - -.btn-default { - color: #333333; - background-color: #ffffff; - border-color: #cccccc; +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; } - -.btn-default:hover, -.btn-default:focus, -.btn-default:active, -.btn-default.active, -.open .dropdown-toggle.btn-default { - color: #333333; - background-color: #ebebeb; - border-color: #adadad; +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; } - -.btn-default:active, -.btn-default.active, -.open .dropdown-toggle.btn-default { - background-image: none; +.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: ''; } - -.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: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: #ffffff; - border-color: #cccccc; +.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'; } - -.btn-primary { - color: #ffffff; - background-color: #428bca; - border-color: #357ebd; +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; } - -.btn-primary:hover, -.btn-primary:focus, -.btn-primary:active, -.btn-primary.active, -.open .dropdown-toggle.btn-primary { - color: #ffffff; - background-color: #3276b1; - border-color: #285e8e; +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } - -.btn-primary:active, -.btn-primary.active, -.open .dropdown-toggle.btn-primary { - background-image: none; +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; } - -.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: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: #428bca; - border-color: #357ebd; +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); } - -.btn-warning { - color: #ffffff; - background-color: #f0ad4e; - border-color: #eea236; +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; } - -.btn-warning:hover, -.btn-warning:focus, -.btn-warning:active, -.btn-warning.active, -.open .dropdown-toggle.btn-warning { - color: #ffffff; - background-color: #ed9c28; - border-color: #d58512; +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; } - -.btn-warning:active, -.btn-warning.active, -.open .dropdown-toggle.btn-warning { - background-image: none; +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; } - -.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: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; +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; } - -.btn-danger { - color: #ffffff; - background-color: #d9534f; - border-color: #d43f3a; +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } - -.btn-danger:hover, -.btn-danger:focus, -.btn-danger:active, -.btn-danger.active, -.open .dropdown-toggle.btn-danger { - color: #ffffff; - background-color: #d2322d; - border-color: #ac2925; +@media (min-width: 768px) { + .container { + width: 750px; + } } - -.btn-danger:active, -.btn-danger.active, -.open .dropdown-toggle.btn-danger { - background-image: none; +@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; } - -.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: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; +.row { + margin-right: -15px; + margin-left: -15px; } - -.btn-success { - color: #ffffff; - background-color: #5cb85c; - border-color: #4cae4c; +.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; } - -.btn-success:hover, -.btn-success:focus, -.btn-success:active, -.btn-success.active, -.open .dropdown-toggle.btn-success { - color: #ffffff; - background-color: #47a447; - border-color: #398439; +.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; } - -.btn-success:active, -.btn-success.active, -.open .dropdown-toggle.btn-success { - background-image: none; +.col-xs-12 { + width: 100%; } - -.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: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; +.col-xs-11 { + width: 91.66666667%; } - -.btn-info { - color: #ffffff; - background-color: #5bc0de; - border-color: #46b8da; +.col-xs-10 { + width: 83.33333333%; } - -.btn-info:hover, -.btn-info:focus, -.btn-info:active, -.btn-info.active, -.open .dropdown-toggle.btn-info { - color: #ffffff; - background-color: #39b3d7; - border-color: #269abc; +.col-xs-9 { + width: 75%; } - -.btn-info:active, -.btn-info.active, -.open .dropdown-toggle.btn-info { - background-image: none; +.col-xs-8 { + width: 66.66666667%; } - -.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: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; +.col-xs-7 { + width: 58.33333333%; } - -.btn-link { - font-weight: normal; - color: #428bca; - cursor: pointer; - border-radius: 0; +.col-xs-6 { + width: 50%; } - -.btn-link, -.btn-link:active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; +.col-xs-5 { + width: 41.66666667%; } - -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; +.col-xs-4 { + width: 33.33333333%; } - -.btn-link:hover, -.btn-link:focus { - color: #2a6496; - text-decoration: underline; - background-color: transparent; +.col-xs-3 { + width: 25%; } - -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #999999; - text-decoration: none; +.col-xs-2 { + width: 16.66666667%; } - -.btn-lg { - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; +.col-xs-1 { + width: 8.33333333%; } - -.btn-sm, -.btn-xs { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; +.col-xs-pull-12 { + right: 100%; } - -.btn-xs { - padding: 1px 5px; +.col-xs-pull-11 { + right: 91.66666667%; } - -.btn-block { - display: block; - width: 100%; - padding-right: 0; - padding-left: 0; +.col-xs-pull-10 { + right: 83.33333333%; } - -.btn-block + .btn-block { - margin-top: 5px; +.col-xs-pull-9 { + right: 75%; } - -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; +.col-xs-pull-8 { + right: 66.66666667%; } - -.fade { - opacity: 0; - -webkit-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; +.col-xs-pull-7 { + right: 58.33333333%; } - -.fade.in { - opacity: 1; +.col-xs-pull-6 { + right: 50%; } - -.collapse { - display: none; +.col-xs-pull-5 { + right: 41.66666667%; } - -.collapse.in { - display: block; +.col-xs-pull-4 { + right: 33.33333333%; } - -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - transition: height 0.35s ease; +.col-xs-pull-3 { + right: 25%; } - -@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.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); +.col-xs-pull-2 { + right: 16.66666667%; } - -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - -webkit-font-smoothing: antialiased; - font-style: normal; - font-weight: normal; - line-height: 1; +.col-xs-pull-1 { + right: 8.33333333%; } - -.glyphicon-asterisk:before { - content: "\2a"; +.col-xs-pull-0 { + right: auto; } - -.glyphicon-plus:before { - content: "\2b"; +.col-xs-push-12 { + left: 100%; } - -.glyphicon-euro:before { - content: "\20ac"; +.col-xs-push-11 { + left: 91.66666667%; } - -.glyphicon-minus:before { - content: "\2212"; +.col-xs-push-10 { + left: 83.33333333%; } - -.glyphicon-cloud:before { - content: "\2601"; +.col-xs-push-9 { + left: 75%; } - -.glyphicon-envelope:before { - content: "\2709"; +.col-xs-push-8 { + left: 66.66666667%; } - -.glyphicon-pencil:before { - content: "\270f"; +.col-xs-push-7 { + left: 58.33333333%; } - -.glyphicon-glass:before { - content: "\e001"; +.col-xs-push-6 { + left: 50%; } - -.glyphicon-music:before { - content: "\e002"; +.col-xs-push-5 { + left: 41.66666667%; } - -.glyphicon-search:before { - content: "\e003"; +.col-xs-push-4 { + left: 33.33333333%; } - -.glyphicon-heart:before { - content: "\e005"; +.col-xs-push-3 { + left: 25%; } - -.glyphicon-star:before { - content: "\e006"; +.col-xs-push-2 { + left: 16.66666667%; } - -.glyphicon-star-empty:before { - content: "\e007"; +.col-xs-push-1 { + left: 8.33333333%; } - -.glyphicon-user:before { - content: "\e008"; +.col-xs-push-0 { + left: auto; } - -.glyphicon-film:before { - content: "\e009"; +.col-xs-offset-12 { + margin-left: 100%; } - -.glyphicon-th-large:before { - content: "\e010"; +.col-xs-offset-11 { + margin-left: 91.66666667%; } - -.glyphicon-th:before { - content: "\e011"; +.col-xs-offset-10 { + margin-left: 83.33333333%; } - -.glyphicon-th-list:before { - content: "\e012"; +.col-xs-offset-9 { + margin-left: 75%; } - -.glyphicon-ok:before { - content: "\e013"; +.col-xs-offset-8 { + margin-left: 66.66666667%; } - -.glyphicon-remove:before { - content: "\e014"; +.col-xs-offset-7 { + margin-left: 58.33333333%; } - -.glyphicon-zoom-in:before { - content: "\e015"; +.col-xs-offset-6 { + margin-left: 50%; } - -.glyphicon-zoom-out:before { - content: "\e016"; +.col-xs-offset-5 { + margin-left: 41.66666667%; } - -.glyphicon-off:before { - content: "\e017"; +.col-xs-offset-4 { + margin-left: 33.33333333%; } - -.glyphicon-signal:before { - content: "\e018"; +.col-xs-offset-3 { + margin-left: 25%; } - -.glyphicon-cog:before { - content: "\e019"; +.col-xs-offset-2 { + margin-left: 16.66666667%; } - -.glyphicon-trash:before { - content: "\e020"; +.col-xs-offset-1 { + margin-left: 8.33333333%; } - -.glyphicon-home:before { - content: "\e021"; +.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; + } } - -.glyphicon-file:before { - content: "\e022"; +@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; + } } - -.glyphicon-time:before { - content: "\e023"; +table { + background-color: transparent; } - -.glyphicon-road:before { - content: "\e024"; +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; } - -.glyphicon-download-alt:before { - content: "\e025"; +th { + text-align: left; } - -.glyphicon-download:before { - content: "\e026"; +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; } - -.glyphicon-upload:before { - content: "\e027"; +.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; } - -.glyphicon-inbox:before { - content: "\e028"; +.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; } - -.glyphicon-play-circle:before { - content: "\e029"; +.table > tbody + tbody { + border-top: 2px solid #ddd; } - -.glyphicon-repeat:before { - content: "\e030"; +.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; } - -.glyphicon-refresh:before { - content: "\e031"; +.table-bordered { + border: 1px solid #ddd; } - -.glyphicon-list-alt:before { - content: "\e032"; +.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; } - -.glyphicon-flag:before { - content: "\e034"; +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; } - -.glyphicon-headphones:before { - content: "\e035"; +.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #f9f9f9; } - -.glyphicon-volume-off:before { - content: "\e036"; +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; } - -.glyphicon-volume-down:before { - content: "\e037"; +table col[class*="col-"] { + position: static; + display: table-column; + float: none; } - -.glyphicon-volume-up:before { - content: "\e038"; +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; } - -.glyphicon-qrcode:before { - content: "\e039"; +.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; } - -.glyphicon-barcode:before { - content: "\e040"; +.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; } - -.glyphicon-tag:before { - content: "\e041"; +.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; } - -.glyphicon-tags:before { - content: "\e042"; +.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; } - -.glyphicon-book:before { - content: "\e043"; +.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; } - -.glyphicon-print:before { - content: "\e045"; +.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; } - -.glyphicon-font:before { - content: "\e047"; +.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; } - -.glyphicon-bold:before { - content: "\e048"; +.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; } - -.glyphicon-italic:before { - content: "\e049"; +.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; } - -.glyphicon-text-height:before { - content: "\e050"; +.table-responsive { + min-height: .01%; + overflow-x: auto; } - -.glyphicon-text-width:before { - content: "\e051"; +@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; + } } - -.glyphicon-align-left:before { - content: "\e052"; +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; } - -.glyphicon-align-center:before { - content: "\e053"; +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; } - -.glyphicon-align-right:before { - content: "\e054"; +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; } - -.glyphicon-align-justify:before { - content: "\e055"; +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } - -.glyphicon-list:before { - content: "\e056"; +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; } - -.glyphicon-indent-left:before { - content: "\e057"; +input[type="file"] { + display: block; } - -.glyphicon-indent-right:before { - content: "\e058"; +input[type="range"] { + display: block; + width: 100%; } - -.glyphicon-facetime-video:before { - content: "\e059"; +select[multiple], +select[size] { + height: auto; } - -.glyphicon-picture:before { - content: "\e060"; +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; } - -.glyphicon-map-marker:before { - content: "\e062"; +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; } - -.glyphicon-adjust:before { - content: "\e063"; +.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; } - -.glyphicon-tint:before { - content: "\e064"; +.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); } - -.glyphicon-edit:before { - content: "\e065"; +.form-control::-moz-placeholder { + color: #999; + opacity: 1; } - -.glyphicon-share:before { - content: "\e066"; +.form-control:-ms-input-placeholder { + color: #999; } - -.glyphicon-check:before { - content: "\e067"; +.form-control::-webkit-input-placeholder { + color: #999; } - -.glyphicon-move:before { - content: "\e068"; +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #eee; + opacity: 1; } - -.glyphicon-step-backward:before { - content: "\e069"; +.form-control[disabled], +fieldset[disabled] .form-control { + cursor: not-allowed; } - -.glyphicon-fast-backward:before { - content: "\e070"; +textarea.form-control { + height: auto; } - -.glyphicon-backward:before { - content: "\e071"; +input[type="search"] { + -webkit-appearance: none; } - -.glyphicon-play:before { - content: "\e072"; +@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; + } } - -.glyphicon-pause:before { - content: "\e073"; +.form-group { + margin-bottom: 15px; } - -.glyphicon-stop:before { - content: "\e074"; +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; } - -.glyphicon-forward:before { - content: "\e075"; +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; } - -.glyphicon-fast-forward:before { - content: "\e076"; +.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; } - -.glyphicon-step-forward:before { - content: "\e077"; +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; } - -.glyphicon-eject:before { - content: "\e078"; +.radio-inline, +.checkbox-inline { + position: relative; + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; } - -.glyphicon-chevron-left:before { - content: "\e079"; +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; } - -.glyphicon-chevron-right:before { - content: "\e080"; +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; } - -.glyphicon-plus-sign:before { - content: "\e081"; +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; } - -.glyphicon-minus-sign:before { - content: "\e082"; +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; } - -.glyphicon-remove-sign:before { - content: "\e083"; +.form-control-static { + min-height: 34px; + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; } - -.glyphicon-ok-sign:before { - content: "\e084"; +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; } - -.glyphicon-question-sign:before { - content: "\e085"; +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; } - -.glyphicon-info-sign:before { - content: "\e086"; +select.input-sm { + height: 30px; + line-height: 30px; } - -.glyphicon-screenshot:before { - content: "\e087"; +textarea.input-sm, +select[multiple].input-sm { + height: auto; } - -.glyphicon-remove-circle:before { - content: "\e088"; +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; } - -.glyphicon-ok-circle:before { - content: "\e089"; +.form-group-sm select.form-control { + height: 30px; + line-height: 30px; } - -.glyphicon-ban-circle:before { - content: "\e090"; +.form-group-sm textarea.form-control, +.form-group-sm select[multiple].form-control { + height: auto; } - -.glyphicon-arrow-left:before { - content: "\e091"; +.form-group-sm .form-control-static { + height: 30px; + min-height: 32px; + padding: 6px 10px; + font-size: 12px; + line-height: 1.5; } - -.glyphicon-arrow-right:before { - content: "\e092"; +.input-lg { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; } - -.glyphicon-arrow-up:before { - content: "\e093"; +select.input-lg { + height: 46px; + line-height: 46px; } - -.glyphicon-arrow-down:before { - content: "\e094"; +textarea.input-lg, +select[multiple].input-lg { + height: auto; } - -.glyphicon-share-alt:before { - content: "\e095"; +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; } - -.glyphicon-resize-full:before { - content: "\e096"; +.form-group-lg select.form-control { + height: 46px; + line-height: 46px; } - -.glyphicon-resize-small:before { - content: "\e097"; +.form-group-lg textarea.form-control, +.form-group-lg select[multiple].form-control { + height: auto; } - -.glyphicon-exclamation-sign:before { - content: "\e101"; +.form-group-lg .form-control-static { + height: 46px; + min-height: 38px; + padding: 11px 16px; + font-size: 18px; + line-height: 1.3333333; } - -.glyphicon-gift:before { - content: "\e102"; +.has-feedback { + position: relative; } - -.glyphicon-leaf:before { - content: "\e103"; +.has-feedback .form-control { + padding-right: 42.5px; } - -.glyphicon-eye-open:before { - content: "\e105"; +.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; } - -.glyphicon-eye-close:before { - content: "\e106"; +.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; } - -.glyphicon-warning-sign:before { - content: "\e107"; +.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; } - -.glyphicon-plane:before { - content: "\e108"; +.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); } - -.glyphicon-random:before { - content: "\e110"; +.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; } - -.glyphicon-comment:before { - content: "\e111"; +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; } - -.glyphicon-magnet:before { - content: "\e112"; +.has-success .form-control-feedback { + color: #3c763d; } - -.glyphicon-chevron-up:before { - content: "\e113"; +.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; } - -.glyphicon-chevron-down:before { - content: "\e114"; +.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); } - -.glyphicon-retweet:before { - content: "\e115"; +.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; } - -.glyphicon-shopping-cart:before { - content: "\e116"; +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; } - -.glyphicon-folder-close:before { - content: "\e117"; +.has-warning .form-control-feedback { + color: #8a6d3b; } - -.glyphicon-folder-open:before { - content: "\e118"; +.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; } - -.glyphicon-resize-vertical:before { - content: "\e119"; +.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); } - -.glyphicon-resize-horizontal:before { - content: "\e120"; +.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; } - -.glyphicon-hdd:before { - content: "\e121"; +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; } - -.glyphicon-bullhorn:before { - content: "\e122"; +.has-error .form-control-feedback { + color: #a94442; } - -.glyphicon-certificate:before { - content: "\e124"; +.has-feedback label ~ .form-control-feedback { + top: 25px; } - -.glyphicon-thumbs-up:before { - content: "\e125"; +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; } - -.glyphicon-thumbs-down:before { - content: "\e126"; +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; } - -.glyphicon-hand-right:before { - content: "\e127"; +@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; + } } - -.glyphicon-hand-left:before { - content: "\e128"; +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; } - -.glyphicon-hand-up:before { - content: "\e129"; +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; } - -.glyphicon-hand-down:before { - content: "\e130"; +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; } - -.glyphicon-circle-arrow-right:before { - content: "\e131"; +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } } - -.glyphicon-circle-arrow-left:before { - content: "\e132"; +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; } - -.glyphicon-circle-arrow-up:before { - content: "\e133"; +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.333333px; + font-size: 18px; + } } - -.glyphicon-circle-arrow-down:before { - content: "\e134"; +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + font-size: 12px; + } } - -.glyphicon-globe:before { - content: "\e135"; +.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; } - -.glyphicon-tasks:before { - content: "\e137"; +.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; } - -.glyphicon-filter:before { - content: "\e138"; +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; } - -.glyphicon-fullscreen:before { - content: "\e140"; +.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); } - -.glyphicon-dashboard:before { - content: "\e141"; +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; } - -.glyphicon-heart-empty:before { - content: "\e143"; +a.btn.disabled, +fieldset[disabled] a.btn { + pointer-events: none; } - -.glyphicon-link:before { - content: "\e144"; +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; } - -.glyphicon-phone:before { - content: "\e145"; +.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; } - -.glyphicon-usd:before { - content: "\e148"; +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; } - -.glyphicon-gbp:before { - content: "\e149"; +.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; } - -.glyphicon-sort:before { - content: "\e150"; +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; } - -.glyphicon-sort-by-alphabet:before { - content: "\e151"; +.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; } - -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; +.btn-default .badge { + color: #fff; + background-color: #333; } - -.glyphicon-sort-by-order:before { - content: "\e153"; +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; } - -.glyphicon-sort-by-order-alt:before { - content: "\e154"; +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #286090; + border-color: #122b40; } - -.glyphicon-sort-by-attributes:before { - content: "\e155"; +.btn-primary:hover { + color: #fff; + background-color: #286090; + border-color: #204d74; } - -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; +.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; } - -.glyphicon-unchecked:before { - content: "\e157"; +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; } - -.glyphicon-expand:before { - content: "\e158"; +.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; } - -.glyphicon-collapse-down:before { - content: "\e159"; +.btn-primary .badge { + color: #337ab7; + background-color: #fff; } - -.glyphicon-collapse-up:before { - content: "\e160"; +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; } - -.glyphicon-log-in:before { - content: "\e161"; +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #449d44; + border-color: #255625; } - -.glyphicon-flash:before { - content: "\e162"; +.btn-success:hover { + color: #fff; + background-color: #449d44; + border-color: #398439; } - -.glyphicon-log-out:before { - content: "\e163"; +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; } - -.glyphicon-new-window:before { - content: "\e164"; +.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; } - -.glyphicon-record:before { - content: "\e165"; +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; } - -.glyphicon-save:before { - content: "\e166"; +.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; } - -.glyphicon-open:before { - content: "\e167"; +.btn-success .badge { + color: #5cb85c; + background-color: #fff; } - -.glyphicon-saved:before { - content: "\e168"; +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; } - -.glyphicon-import:before { - content: "\e169"; +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #31b0d5; + border-color: #1b6d85; } - -.glyphicon-export:before { - content: "\e170"; +.btn-info:hover { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; } - -.glyphicon-send:before { - content: "\e171"; +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; } - -.glyphicon-floppy-disk:before { - content: "\e172"; +.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; } - -.glyphicon-floppy-saved:before { - content: "\e173"; +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; } - -.glyphicon-floppy-remove:before { - content: "\e174"; +.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; } - -.glyphicon-floppy-save:before { - content: "\e175"; +.btn-info .badge { + color: #5bc0de; + background-color: #fff; } - -.glyphicon-floppy-open:before { - content: "\e176"; +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; } - -.glyphicon-credit-card:before { - content: "\e177"; +.btn-warning:focus, +.btn-warning.focus { + color: #fff; + background-color: #ec971f; + border-color: #985f0d; } - -.glyphicon-transfer:before { - content: "\e178"; +.btn-warning:hover { + color: #fff; + background-color: #ec971f; + border-color: #d58512; } - -.glyphicon-cutlery:before { - content: "\e179"; +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; } - -.glyphicon-header:before { - content: "\e180"; +.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; } - -.glyphicon-compressed:before { - content: "\e181"; +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; } - -.glyphicon-earphone:before { - content: "\e182"; +.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; } - -.glyphicon-phone-alt:before { - content: "\e183"; +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; } - -.glyphicon-tower:before { - content: "\e184"; +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; } - -.glyphicon-stats:before { - content: "\e185"; +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #c9302c; + border-color: #761c19; } - -.glyphicon-sd-video:before { - content: "\e186"; +.btn-danger:hover { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; } - -.glyphicon-hd-video:before { - content: "\e187"; +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; } - -.glyphicon-subtitles:before { - content: "\e188"; +.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; } - -.glyphicon-sound-stereo:before { - content: "\e189"; +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; } - -.glyphicon-sound-dolby:before { - content: "\e190"; +.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; } - -.glyphicon-sound-5-1:before { - content: "\e191"; +.btn-danger .badge { + color: #d9534f; + background-color: #fff; } - -.glyphicon-sound-6-1:before { - content: "\e192"; +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; } - -.glyphicon-sound-7-1:before { - content: "\e193"; +.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; } - -.glyphicon-copyright-mark:before { - content: "\e194"; +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; } - -.glyphicon-registration-mark:before { - content: "\e195"; +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; } - -.glyphicon-cloud-download:before { - content: "\e197"; +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; } - -.glyphicon-cloud-upload:before { - content: "\e198"; +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.3333333; + border-radius: 6px; } - -.glyphicon-tree-conifer:before { - content: "\e199"; +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; } - -.glyphicon-tree-deciduous:before { - content: "\e200"; +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; } - -.glyphicon-briefcase:before { - content: "\1f4bc"; +.btn-block { + display: block; + width: 100%; } - -.glyphicon-calendar:before { - content: "\1f4c5"; +.btn-block + .btn-block { + margin-top: 5px; } - -.glyphicon-pushpin:before { - content: "\1f4cc"; +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; } - -.glyphicon-paperclip:before { - content: "\1f4ce"; +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; } - -.glyphicon-camera:before { - content: "\1f4f7"; +.fade.in { + opacity: 1; } - -.glyphicon-lock:before { - content: "\1f512"; +.collapse { + display: none; } - -.glyphicon-bell:before { - content: "\1f514"; +.collapse.in { + display: block; } - -.glyphicon-bookmark:before { - content: "\1f516"; +tr.collapse.in { + display: table-row; } - -.glyphicon-fire:before { - content: "\1f525"; +tbody.collapse.in { + display: table-row-group; } - -.glyphicon-wrench:before { - content: "\1f527"; +.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 solid #000000; + border-top: 4px dashed; + border-top: 4px solid \9; border-right: 4px solid transparent; - border-bottom: 0 dotted; border-left: 4px solid transparent; - content: ""; } - +.dropup, .dropdown { position: relative; } - .dropdown-toggle:focus { outline: 0; } - .dropdown-menu { position: absolute; top: 100%; @@ -3243,85 +3581,85 @@ input[type="button"].btn-block { padding: 5px 0; margin: 2px 0 0; font-size: 14px; + text-align: left; list-style: none; - background-color: #ffffff; - border: 1px solid #cccccc; - border: 1px solid rgba(0, 0, 0, 0.15); + 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, 0.175); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-clip: padding-box; + -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.428571429; - color: #333333; + line-height: 1.42857143; + color: #333; white-space: nowrap; } - .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { - color: #ffffff; + color: #262626; text-decoration: none; - background-color: #428bca; + background-color: #f5f5f5; } - .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { - color: #ffffff; + color: #fff; text-decoration: none; - background-color: #428bca; + background-color: #337ab7; outline: 0; } - .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { - color: #999999; + 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); + 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.428571429; - color: #999999; + line-height: 1.42857143; + color: #777; + white-space: nowrap; } - .dropdown-backdrop { position: fixed; top: 0; @@ -3330,70 +3668,44 @@ input[type="button"].btn-block { left: 0; z-index: 990; } - .pull-right > .dropdown-menu { right: 0; left: auto; } - .dropup .caret, .navbar-fixed-bottom .dropdown .caret { - border-top: 0 dotted; - border-bottom: 4px solid #000000; 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: 1px; + 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-default .caret { - border-top-color: #333333; -} - -.btn-primary .caret, -.btn-success .caret, -.btn-warning .caret, -.btn-danger .caret, -.btn-info .caret { - border-top-color: #fff; -} - -.dropup .btn-default .caret { - border-bottom-color: #333333; -} - -.dropup .btn-primary .caret, -.dropup .btn-success .caret, -.dropup .btn-warning .caret, -.dropup .btn-danger .caret, -.dropup .btn-info .caret { - border-bottom-color: #fff; -} - .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, @@ -3404,175 +3716,96 @@ input[type="button"].btn-block { .btn-group-vertical > .btn.active { z-index: 2; } - -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus { - outline: none; -} - .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:before, -.btn-toolbar:after { - display: table; - content: " "; -} - -.btn-toolbar:after { - clear: both; -} - -.btn-toolbar:before, -.btn-toolbar:after { - display: table; - content: " "; -} - -.btn-toolbar:after { - clear: both; +.btn-toolbar { + margin-left: -5px; } - -.btn-toolbar .btn-group { +.btn-toolbar .btn, +.btn-toolbar .btn-group, +.btn-toolbar .input-group { float: left; } - -.btn-toolbar > .btn + .btn, -.btn-toolbar > .btn-group + .btn, -.btn-toolbar > .btn + .btn-group, -.btn-toolbar > .btn-group + .btn-group { +.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-bottom-left-radius: 0; 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 > .btn:last-child, -.btn-group > .btn-group:first-child > .dropdown-toggle { +.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 > .btn:first-child { - border-bottom-left-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-xs > .btn { - padding: 5px 10px; - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} - -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} - -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.33; - border-radius: 6px; -} - .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, 0.125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + -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-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } - -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after { - display: table; - content: " "; -} - -.btn-group-vertical > .btn-group:after { - clear: both; -} - -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after { - display: table; - content: " "; -} - -.btn-group-vertical > .btn-group:after { - clear: both; -} - .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, @@ -3580,96 +3813,97 @@ input[type="button"].btn-block { 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; - border-top-left-radius: 0; } - .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } - -.btn-group-vertical > .btn-group:first-child > .btn:last-child, -.btn-group-vertical > .btn-group:first-child > .dropdown-toggle { +.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 > .btn:first-child { - border-top-right-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%; - border-collapse: separate; table-layout: fixed; + border-collapse: separate; } - -.btn-group-justified .btn { +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { display: table-cell; float: none; width: 1%; } - -[data-toggle="buttons"] > .btn > input[type="radio"], -[data-toggle="buttons"] > .btn > input[type="checkbox"] { - display: none; +.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.col { +.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: 45px; + height: 46px; padding: 10px 16px; font-size: 18px; - line-height: 1.33; + 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: 45px; - line-height: 45px; + 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 { +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 { @@ -3679,448 +3913,338 @@ textarea.input-group-lg > .input-group-btn > .btn { 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 { +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: #eeeeee; - border: 1px solid #cccccc; + 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: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) { - border-bottom-left-radius: 0; +.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: -4px; + 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:before, -.nav:after { - display: table; - content: " "; -} - -.nav:after { - clear: both; -} - -.nav:before, -.nav:after { - display: table; - content: " "; -} - -.nav:after { - clear: both; -} - .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: #eeeeee; + background-color: #eee; } - .nav > li.disabled > a { - color: #999999; + color: #777; } - .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { - color: #999999; + color: #777; text-decoration: none; cursor: not-allowed; background-color: transparent; } - .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { - background-color: #eeeeee; - border-color: #428bca; + 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 #dddddd; + border-bottom: 1px solid #ddd; } - .nav-tabs > li { float: left; margin-bottom: -1px; } - .nav-tabs > li > a { margin-right: 2px; - line-height: 1.428571429; + line-height: 1.42857143; border: 1px solid transparent; border-radius: 4px 4px 0 0; } - .nav-tabs > li > a:hover { - border-color: #eeeeee #eeeeee #dddddd; + border-color: #eee #eee #ddd; } - .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { - color: #555555; + color: #555; cursor: default; - background-color: #ffffff; - border: 1px solid #dddddd; + 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-bottom: 1px solid #dddddd; + border-radius: 4px; } - -.nav-tabs.nav-justified > .active > a { - border-bottom-color: #ffffff; +.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: 5px; + 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: #ffffff; - background-color: #428bca; + 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-bottom: 1px solid #dddddd; -} - -.nav-tabs-justified > .active > a { - border-bottom-color: #ffffff; -} - -.tabbable:before, -.tabbable:after { - display: table; - content: " "; -} - -.tabbable:after { - clear: both; -} - -.tabbable:before, -.tabbable:after { - display: table; - content: " "; + border-radius: 4px; } - -.tabbable:after { - clear: both; +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; } - -.tab-content > .tab-pane, -.pill-content > .pill-pane { +@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, -.pill-content > .active { +.tab-content > .active { display: block; } - -.nav .caret { - border-top-color: #428bca; - border-bottom-color: #428bca; -} - -.nav a:hover .caret { - border-top-color: #2a6496; - border-bottom-color: #2a6496; -} - .nav-tabs .dropdown-menu { margin-top: -1px; - border-top-right-radius: 0; border-top-left-radius: 0; + border-top-right-radius: 0; } - .navbar { position: relative; - z-index: 1000; min-height: 50px; margin-bottom: 20px; border: 1px solid transparent; } - -.navbar:before, -.navbar:after { - display: table; - content: " "; -} - -.navbar:after { - clear: both; -} - -.navbar:before, -.navbar:after { - display: table; - content: " "; -} - -.navbar:after { - clear: both; -} - @media (min-width: 768px) { .navbar { border-radius: 4px; } } - -.navbar-header:before, -.navbar-header:after { - display: table; - content: " "; -} - -.navbar-header:after { - clear: both; -} - -.navbar-header:before, -.navbar-header:after { - display: table; - content: " "; -} - -.navbar-header:after { - clear: both; -} - @media (min-width: 768px) { .navbar-header { float: left; } } - .navbar-collapse { - max-height: 340px; padding-right: 15px; padding-left: 15px; overflow-x: visible; - border-top: 1px solid transparent; - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -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:before, -.navbar-collapse:after { - display: table; - content: " "; -} - -.navbar-collapse:after { - clear: both; -} - -.navbar-collapse:before, -.navbar-collapse:after { - display: table; - content: " "; -} - -.navbar-collapse:after { - clear: both; -} - .navbar-collapse.in { overflow-y: auto; } - @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; - box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; } .navbar-collapse.collapse { display: block !important; @@ -4131,84 +4255,90 @@ textarea.input-group-sm > .input-group-btn > .btn { .navbar-collapse.in { overflow-y: visible; } - .navbar-collapse .navbar-nav.navbar-left:first-child { - margin-left: -15px; - } - .navbar-collapse .navbar-nav.navbar-right:last-child { - margin-right: -15px; + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; } - .navbar-collapse .navbar-text:last-child { - margin-right: 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 > .navbar-collapse { +.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 > .navbar-collapse { + .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; - border-width: 0 0 1px; + z-index: 1030; } - @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } - .navbar-fixed-top { top: 0; - z-index: 1030; + 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 .navbar-brand, + .navbar > .container-fluid .navbar-brand { margin-left: -15px; } } - .navbar-toggle { position: relative; float: right; @@ -4217,37 +4347,35 @@ textarea.input-group-sm > .input-group-btn > .btn { 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; @@ -4256,7 +4384,8 @@ textarea.input-group-sm > .input-group-btn > .btn { margin-top: 0; background-color: transparent; border: 0; - box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { @@ -4270,7 +4399,6 @@ textarea.input-group-sm > .input-group-btn > .btn { background-image: none; } } - @media (min-width: 768px) { .navbar-nav { float: left; @@ -4284,16 +4412,6 @@ textarea.input-group-sm > .input-group-btn > .btn { padding-bottom: 15px; } } - -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - } -} - .navbar-form { padding: 10px 15px; margin-top: 8px; @@ -4302,10 +4420,9 @@ textarea.input-group-sm > .input-group-btn > .btn { margin-left: -15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + -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; @@ -4314,27 +4431,56 @@ textarea.input-group-sm > .input-group-btn > .btn { } .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; - padding-left: 0; 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"] { - float: none; + 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; @@ -4347,278 +4493,251 @@ textarea.input-group-sm > .input-group-btn > .btn { box-shadow: none; } } - .navbar-nav > li > .dropdown-menu { margin-top: 0; - border-top-right-radius: 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-nav.pull-right > li > .dropdown-menu, -.navbar-nav > li > .dropdown-menu.pull-right { - right: 0; - left: auto; -} - .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 { - float: left; 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: #777777; + color: #777; } - .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } - .navbar-default .navbar-text { - color: #777777; + color: #777; } - .navbar-default .navbar-nav > li > a { - color: #777777; + color: #777; } - .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { - color: #333333; + 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: #555555; + 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: #cccccc; + color: #ccc; background-color: transparent; } - .navbar-default .navbar-toggle { - border-color: #dddddd; + border-color: #ddd; } - .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { - background-color: #dddddd; + background-color: #ddd; } - .navbar-default .navbar-toggle .icon-bar { - background-color: #cccccc; + background-color: #888; } - .navbar-default .navbar-collapse, .navbar-default .navbar-form { - border-color: #e6e6e6; -} - -.navbar-default .navbar-nav > .dropdown > a:hover .caret, -.navbar-default .navbar-nav > .dropdown > a:focus .caret { - border-top-color: #333333; - border-bottom-color: #333333; + border-color: #e7e7e7; } - .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { - color: #555555; + color: #555; background-color: #e7e7e7; } - -.navbar-default .navbar-nav > .open > a .caret, -.navbar-default .navbar-nav > .open > a:hover .caret, -.navbar-default .navbar-nav > .open > a:focus .caret { - border-top-color: #555555; - border-bottom-color: #555555; -} - -.navbar-default .navbar-nav > .dropdown > a .caret { - border-top-color: #777777; - border-bottom-color: #777777; -} - @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777777; + color: #777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333333; + 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: #555555; + 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: #cccccc; + color: #ccc; background-color: transparent; } } - .navbar-default .navbar-link { - color: #777777; + color: #777; } - .navbar-default .navbar-link:hover { - color: #333333; + 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: #222222; + background-color: #222; border-color: #080808; } - .navbar-inverse .navbar-brand { - color: #999999; + color: #9d9d9d; } - .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { - color: #ffffff; + color: #fff; background-color: transparent; } - .navbar-inverse .navbar-text { - color: #999999; + color: #9d9d9d; } - .navbar-inverse .navbar-nav > li > a { - color: #999999; + color: #9d9d9d; } - .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { - color: #ffffff; + 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: #ffffff; + 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: #444444; + color: #444; background-color: transparent; } - .navbar-inverse .navbar-toggle { - border-color: #333333; + border-color: #333; } - .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { - background-color: #333333; + background-color: #333; } - .navbar-inverse .navbar-toggle .icon-bar { - background-color: #ffffff; + 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: #ffffff; + color: #fff; background-color: #080808; } - -.navbar-inverse .navbar-nav > .dropdown > a:hover .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - -.navbar-inverse .navbar-nav > .dropdown > a .caret { - border-top-color: #999999; - border-bottom-color: #999999; -} - -.navbar-inverse .navbar-nav > .open > a .caret, -.navbar-inverse .navbar-nav > .open > a:hover .caret, -.navbar-inverse .navbar-nav > .open > a:focus .caret { - border-top-color: #ffffff; - border-bottom-color: #ffffff; -} - @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: #999999; + color: #9d9d9d; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #ffffff; + 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: #ffffff; + 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: #444444; + color: #444; background-color: transparent; } } - .navbar-inverse .navbar-link { - color: #999999; + color: #9d9d9d; } - .navbar-inverse .navbar-link:hover { - color: #ffffff; + 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; @@ -4626,64 +4745,58 @@ textarea.input-group-sm > .input-group-btn > .btn { background-color: #f5f5f5; border-radius: 4px; } - .breadcrumb > li { display: inline-block; } - .breadcrumb > li + li:before { padding: 0 5px; - color: #cccccc; + color: #ccc; content: "/\00a0"; } - .breadcrumb > .active { - color: #999999; + 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.428571429; + line-height: 1.42857143; + color: #337ab7; text-decoration: none; - background-color: #ffffff; - border: 1px solid #dddddd; + background-color: #fff; + border: 1px solid #ddd; } - .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; - border-bottom-left-radius: 4px; 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 { - background-color: #eeeeee; + z-index: 3; + color: #23527c; + background-color: #eee; + border-color: #ddd; } - .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, @@ -4691,201 +4804,159 @@ textarea.input-group-sm > .input-group-btn > .btn { .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; - color: #ffffff; + color: #fff; cursor: default; - background-color: #428bca; - border-color: #428bca; + 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: #999999; + color: #777; cursor: not-allowed; - background-color: #ffffff; - border-color: #dddddd; + 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-bottom-left-radius: 6px; 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-bottom-left-radius: 3px; 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:before, -.pager:after { - display: table; - content: " "; -} - -.pager:after { - clear: both; -} - -.pager:before, -.pager:after { - display: table; - content: " "; -} - -.pager:after { - clear: both; -} - .pager li { display: inline; } - .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; - background-color: #ffffff; - border: 1px solid #dddddd; + background-color: #fff; + border: 1px solid #ddd; border-radius: 15px; } - .pager li > a:hover, .pager li > a:focus { text-decoration: none; - background-color: #eeeeee; + 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: #999999; + color: #777; cursor: not-allowed; - background-color: #ffffff; + background-color: #fff; } - .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; - color: #ffffff; + color: #fff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } - -.label[href]:hover, -.label[href]:focus { - color: #ffffff; +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: #999999; + background-color: #777; } - .label-default[href]:hover, .label-default[href]:focus { - background-color: #808080; + background-color: #5e5e5e; } - .label-primary { - background-color: #428bca; + background-color: #337ab7; } - .label-primary[href]:hover, .label-primary[href]:focus { - background-color: #3071a9; + 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; @@ -4893,204 +4964,187 @@ textarea.input-group-sm > .input-group-btn > .btn { font-size: 12px; font-weight: bold; line-height: 1; - color: #ffffff; + color: #fff; text-align: center; white-space: nowrap; - vertical-align: baseline; - background-color: #999999; + 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: #ffffff; + color: #fff; text-decoration: none; cursor: pointer; } - -.btn .badge { - position: relative; - top: -1px; -} - -a.list-group-item.active > .badge, +.list-group-item.active > .badge, .nav-pills > .active > a > .badge { - color: #428bca; - background-color: #ffffff; + 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: 30px; + padding-top: 30px; + padding-bottom: 30px; margin-bottom: 30px; - font-size: 21px; - font-weight: 200; - line-height: 2.1428571435; color: inherit; - background-color: #eeeeee; + background-color: #eee; } - -.jumbotron h1 { - line-height: 1; +.jumbotron h1, +.jumbotron .h1 { color: inherit; } - .jumbotron p { - line-height: 1.4; + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; } - -.container .jumbotron { +.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 .jumbotron, + .container-fluid .jumbotron { padding-right: 60px; padding-left: 60px; } - .jumbotron h1 { + .jumbotron h1, + .jumbotron .h1 { font-size: 63px; } } - .thumbnail { - display: inline-block; display: block; - height: auto; - max-width: 100%; padding: 4px; - line-height: 1.428571429; - background-color: #ffffff; - border: 1px solid #dddddd; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; border-radius: 4px; - -webkit-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; -} - -.thumbnail > img { - display: block; - height: auto; - max-width: 100%; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; } - -a.thumbnail:hover, -a.thumbnail:focus { - border-color: #428bca; -} - -.thumbnail > img { +.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: #333333; + 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-dismissable, +.alert-dismissible { padding-right: 35px; } - -.alert-dismissable .close { +.alert-dismissable .close, +.alert-dismissible .close { position: relative; top: -2px; right: -21px; color: inherit; } - .alert-success { - color: #468847; + color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } - .alert-success hr { border-top-color: #c9e2b3; } - .alert-success .alert-link { - color: #356635; + color: #2b542c; } - .alert-info { - color: #3a87ad; + color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } - .alert-info hr { border-top-color: #a6e1ec; } - .alert-info .alert-link { - color: #2d6987; + color: #245269; } - .alert-warning { - color: #c09853; + color: #8a6d3b; background-color: #fcf8e3; - border-color: #fbeed5; + border-color: #faebcc; } - .alert-warning hr { - border-top-color: #f8e5be; + border-top-color: #f7e1b5; } - .alert-warning .alert-link { - color: #a47e3c; + color: #66512c; } - .alert-danger { - color: #b94a48; + color: #a94442; background-color: #f2dede; - border-color: #eed3d7; + border-color: #ebccd1; } - .alert-danger hr { - border-top-color: #e6c1c7; + border-top-color: #e4b9c0; } - .alert-danger .alert-link { - color: #953b39; + color: #843534; } - @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; @@ -5099,8 +5153,7 @@ a.thumbnail:focus { background-position: 0 0; } } - -@-moz-keyframes progress-bar-stripes { +@-o-keyframes progress-bar-stripes { from { background-position: 40px 0; } @@ -5108,16 +5161,6 @@ a.thumbnail:focus { background-position: 0 0; } } - -@-o-keyframes progress-bar-stripes { - from { - background-position: 0 0; - } - to { - background-position: 40px 0; - } -} - @keyframes progress-bar-stripes { from { background-position: 40px 0; @@ -5126,426 +5169,699 @@ a.thumbnail:focus { 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, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -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; - color: #ffffff; + line-height: 20px; + color: #fff; text-align: center; - background-color: #428bca; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-transition: width 0.6s ease; - transition: width 0.6s ease; -} - -.progress-striped .progress-bar { - background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - 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); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-size: 40px 40px; -} - -.progress.active .progress-bar { + 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; - -moz-animation: progress-bar-stripes 2s linear infinite; - -ms-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-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - 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); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-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-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - 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); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-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-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - 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); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-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-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent)); - 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); - background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-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, -.media .media { - margin-top: 15px; -} - -.media:first-child { - margin-top: 0; +.media-body { + width: 10000px; } - .media-object { display: block; } - -.media-heading { - margin: 0 0 5px; +.media-object.img-thumbnail { + max-width: none; } - +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, .media > .pull-left { - margin-right: 10px; + padding-right: 10px; } - -.media > .pull-right { - margin-left: 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: #ffffff; - border: 1px solid #dddddd; + background-color: #fff; + border: 1px solid #ddd; } - .list-group-item:first-child { - border-top-right-radius: 4px; 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; } - -.list-group-item > .badge { - float: right; -} - -.list-group-item > .badge + .badge { - margin-right: 5px; -} - -a.list-group-item { - color: #555555; +a.list-group-item, +button.list-group-item { + color: #555; } - -a.list-group-item .list-group-item-heading { - color: #333333; +a.list-group-item .list-group-item-heading, +button.list-group-item .list-group-item-heading { + color: #333; } - a.list-group-item:hover, -a.list-group-item:focus { +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: #ffffff; - background-color: #428bca; - border-color: #428bca; + 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: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: #e1edf7; + 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: #ffffff; + background-color: #fff; border: 1px solid transparent; border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + -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-body:before, -.panel-body:after { - display: table; - content: " "; +.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-body:after { - clear: both; +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; } - -.panel-body:before, -.panel-body:after { - display: table; - content: " "; +.panel-title > a, +.panel-title > small, +.panel-title > .small, +.panel-title > small > a, +.panel-title > .small > a { + color: inherit; } - -.panel-body:after { - clear: both; +.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 > .list-group, +.panel > .panel-collapse > .list-group { margin-bottom: 0; } - -.panel > .list-group .list-group-item { +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { border-width: 1px 0; + border-radius: 0; } - -.panel > .list-group .list-group-item:first-child { - border-top-right-radius: 0; - border-top-left-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 .list-group-item:last-child { +.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; } - -.panel > .table { +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { margin-bottom: 0; } - -.panel > .panel-body + .table { - border-top: 1px solid #dddddd; +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; } - -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; +.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-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; +.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-title > a { - color: inherit; +.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-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #dddddd; +.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; - overflow: hidden; 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 { - border-top: 1px solid #dddddd; +.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 #dddddd; + border-bottom: 1px solid #ddd; } - .panel-default { - border-color: #dddddd; + border-color: #ddd; } - .panel-default > .panel-heading { - color: #333333; + color: #333; background-color: #f5f5f5; - border-color: #dddddd; + border-color: #ddd; } - -.panel-default > .panel-heading + .panel-collapse .panel-body { - border-top-color: #dddddd; +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; } - -.panel-default > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #dddddd; +.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: #428bca; + border-color: #337ab7; } - .panel-primary > .panel-heading { - color: #ffffff; - background-color: #428bca; - border-color: #428bca; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; } - -.panel-primary > .panel-heading + .panel-collapse .panel-body { - border-top-color: #428bca; +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; } - -.panel-primary > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #428bca; +.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: #468847; + color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } - -.panel-success > .panel-heading + .panel-collapse .panel-body { +.panel-success > .panel-heading + .panel-collapse > .panel-body { border-top-color: #d6e9c6; } - -.panel-success > .panel-footer + .panel-collapse .panel-body { +.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: #fbeed5; + border-color: #faebcc; } - .panel-warning > .panel-heading { - color: #c09853; + color: #8a6d3b; background-color: #fcf8e3; - border-color: #fbeed5; + border-color: #faebcc; } - -.panel-warning > .panel-heading + .panel-collapse .panel-body { - border-top-color: #fbeed5; +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; } - -.panel-warning > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #fbeed5; +.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: #eed3d7; + border-color: #ebccd1; } - .panel-danger > .panel-heading { - color: #b94a48; + color: #a94442; background-color: #f2dede; - border-color: #eed3d7; + border-color: #ebccd1; } - -.panel-danger > .panel-heading + .panel-collapse .panel-body { - border-top-color: #eed3d7; +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; } - -.panel-danger > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #eed3d7; +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; } - -.panel-info { - border-color: #bce8f1; +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; } - -.panel-info > .panel-heading { - color: #3a87ad; - background-color: #d9edf7; - border-color: #bce8f1; +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; } - -.panel-info > .panel-heading + .panel-collapse .panel-body { - border-top-color: #bce8f1; +.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; } - -.panel-info > .panel-footer + .panel-collapse .panel-body { - border-bottom-color: #bce8f1; +.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive-4by3 { + padding-bottom: 75%; } - .well { min-height: 20px; padding: 19px; @@ -5553,251 +5869,222 @@ a.list-group-item:focus { background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + -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, 0.15); + 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: #000000; - text-shadow: 0 1px 0 #ffffff; - opacity: 0.2; + color: #000; + text-shadow: 0 1px 0 #fff; filter: alpha(opacity=20); + opacity: .2; } - .close:hover, .close:focus { - color: #000000; + color: #000; text-decoration: none; cursor: pointer; - opacity: 0.5; filter: alpha(opacity=50); + opacity: .5; } - button.close { + -webkit-appearance: none; padding: 0; cursor: pointer; background: transparent; border: 0; - -webkit-appearance: none; } - .modal-open { overflow: hidden; } - -body.modal-open, -.modal-open .navbar-fixed-top, -.modal-open .navbar-fixed-bottom { - margin-right: 15px; -} - .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; - z-index: 1040; + z-index: 1050; display: none; - overflow: auto; - overflow-y: scroll; + 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%); - -webkit-transition: -webkit-transform 0.3s ease-out; - -moz-transition: -moz-transform 0.3s ease-out; - -o-transition: -o-transform 0.3s ease-out; - transition: transform 0.3s ease-out; } - .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 { - z-index: 1050; + position: relative; width: auto; - padding: 10px; - margin-right: auto; - margin-left: auto; + margin: 10px; } - .modal-content { position: relative; - background-color: #ffffff; - border: 1px solid #999999; - border: 1px solid rgba(0, 0, 0, 0.2); + 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: none; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); - box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); - background-clip: padding-box; + 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: 1030; - background-color: #000000; + z-index: 1040; + background-color: #000; } - .modal-backdrop.fade { - opacity: 0; filter: alpha(opacity=0); + opacity: 0; } - .modal-backdrop.in { - opacity: 0.5; filter: alpha(opacity=50); + opacity: .5; } - .modal-header { - min-height: 16.428571429px; + min-height: 16.42857143px; padding: 15px; border-bottom: 1px solid #e5e5e5; } - .modal-header .close { margin-top: -2px; } - .modal-title { margin: 0; - line-height: 1.428571429; + line-height: 1.42857143; } - .modal-body { position: relative; - padding: 20px; + padding: 15px; } - .modal-footer { - padding: 19px 20px 20px; - margin-top: 15px; + padding: 15px; text-align: right; border-top: 1px solid #e5e5e5; } - -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} - -.modal-footer:after { - clear: both; -} - -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} - -.modal-footer:after { - clear: both; -} - .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; } - -@media screen and (min-width: 768px) { +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { .modal-dialog { - right: auto; - left: 50%; width: 600px; - padding-top: 30px; - padding-bottom: 30px; + margin: 30px auto; } .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + -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: 1030; + z-index: 1070; display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 12px; - line-height: 1.4; - opacity: 0; + 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); - visibility: visible; -} + opacity: 0; + line-break: auto; +} .tooltip.in { - opacity: 0.9; 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: #ffffff; + color: #fff; text-align: center; - text-decoration: none; - background-color: #000000; + background-color: #000; border-radius: 4px; } - .tooltip-arrow { position: absolute; width: 0; @@ -5805,119 +6092,121 @@ body.modal-open, border-color: transparent; border-style: solid; } - .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; - border-top-color: #000000; border-width: 5px 5px 0; + border-top-color: #000; } - .tooltip.top-left .tooltip-arrow { + right: 5px; bottom: 0; - left: 5px; - border-top-color: #000000; + margin-bottom: -5px; border-width: 5px 5px 0; + border-top-color: #000; } - .tooltip.top-right .tooltip-arrow { - right: 5px; bottom: 0; - border-top-color: #000000; + 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-right-color: #000000; border-width: 5px 5px 5px 0; + border-right-color: #000; } - .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; - border-left-color: #000000; border-width: 5px 0 5px 5px; + border-left-color: #000; } - .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; - border-bottom-color: #000000; border-width: 0 5px 5px; + border-bottom-color: #000; } - .tooltip.bottom-left .tooltip-arrow { top: 0; - left: 5px; - border-bottom-color: #000000; + right: 5px; + margin-top: -5px; border-width: 0 5px 5px; + border-bottom-color: #000; } - .tooltip.bottom-right .tooltip-arrow { top: 0; - right: 5px; - border-bottom-color: #000000; + left: 5px; + margin-top: -5px; border-width: 0 5px 5px; + border-bottom-color: #000; } - .popover { position: absolute; top: 0; left: 0; - z-index: 1010; + 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: #ffffff; - border: 1px solid #cccccc; - border: 1px solid rgba(0, 0, 0, 0.2); + 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, 0.2); - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); - background-clip: padding-box; -} + -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; - font-weight: normal; - line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; } - .popover-content { padding: 9px 14px; } - -.popover .arrow, -.popover .arrow:after { +.popover > .arrow, +.popover > .arrow:after { position: absolute; display: block; width: 0; @@ -5925,147 +6214,153 @@ body.modal-open, border-color: transparent; border-style: solid; } - -.popover .arrow { +.popover > .arrow { border-width: 11px; } - -.popover .arrow:after { - border-width: 10px; +.popover > .arrow:after { content: ""; + border-width: 10px; } - -.popover.top .arrow { +.popover.top > .arrow { bottom: -11px; left: 50%; margin-left: -11px; - border-top-color: #999999; - border-top-color: rgba(0, 0, 0, 0.25); + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); border-bottom-width: 0; } - -.popover.top .arrow:after { +.popover.top > .arrow:after { bottom: 1px; margin-left: -10px; - border-top-color: #ffffff; - border-bottom-width: 0; content: " "; + border-top-color: #fff; + border-bottom-width: 0; } - -.popover.right .arrow { +.popover.right > .arrow { top: 50%; left: -11px; margin-top: -11px; - border-right-color: #999999; - border-right-color: rgba(0, 0, 0, 0.25); + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); border-left-width: 0; } - -.popover.right .arrow:after { +.popover.right > .arrow:after { bottom: -10px; left: 1px; - border-right-color: #ffffff; - border-left-width: 0; content: " "; + border-right-color: #fff; + border-left-width: 0; } - -.popover.bottom .arrow { +.popover.bottom > .arrow { top: -11px; left: 50%; margin-left: -11px; - border-bottom-color: #999999; - border-bottom-color: rgba(0, 0, 0, 0.25); border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); } - -.popover.bottom .arrow:after { +.popover.bottom > .arrow:after { top: 1px; margin-left: -10px; - border-bottom-color: #ffffff; - border-top-width: 0; content: " "; + border-top-width: 0; + border-bottom-color: #fff; } - -.popover.left .arrow { +.popover.left > .arrow { top: 50%; right: -11px; margin-top: -11px; - border-left-color: #999999; - border-left-color: rgba(0, 0, 0, 0.25); border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); } - -.popover.left .arrow:after { +.popover.left > .arrow:after { right: 1px; bottom: -10px; - border-left-color: #ffffff; - border-right-width: 0; 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: 0.6s ease-in-out left; - transition: 0.6s ease-in-out left; + -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 { - display: block; - height: auto; - max-width: 100%; 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; @@ -6073,69 +6368,71 @@ body.modal-open, left: 0; width: 15%; font-size: 20px; - color: #ffffff; + color: #fff; text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); - opacity: 0.5; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); filter: alpha(opacity=50); + opacity: .5; } - .carousel-control.left { - background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001))); - background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); - background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); - background-repeat: repeat-x; + 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-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5))); - background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); - background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); - background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); - background-repeat: repeat-x; + 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: #ffffff; + color: #fff; text-decoration: none; - opacity: 0.9; 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%; - left: 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; - margin-top: -10px; - margin-left: -10px; 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; @@ -6147,7 +6444,6 @@ body.modal-open, text-align: center; list-style: none; } - .carousel-indicators li { display: inline-block; width: 10px; @@ -6155,17 +6451,17 @@ body.modal-open, margin: 1px; text-indent: -999px; cursor: pointer; - border: 1px solid #ffffff; + 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: #ffffff; + background-color: #fff; } - .carousel-caption { position: absolute; right: 15%; @@ -6174,24 +6470,31 @@ body.modal-open, z-index: 10; padding-top: 20px; padding-bottom: 20px; - color: #ffffff; + color: #fff; text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + 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; - margin-left: -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%; @@ -6201,37 +6504,76 @@ body.modal-open, bottom: 20px; } } - .clearfix:before, -.clearfix:after { +.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 { +.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; @@ -6239,563 +6581,185 @@ body.modal-open, background-color: transparent; border: 0; } - +.hidden { + display: none !important; +} .affix { position: fixed; } - @-ms-viewport { width: device-width; } - -@media screen and (max-width: 400px) { - @-ms-viewport { - width: 320px; - } -} - -.hidden { - display: none !important; - visibility: hidden !important; -} - -.visible-xs { - display: none !important; -} - -tr.visible-xs { - display: none !important; -} - -th.visible-xs, -td.visible-xs { - display: none !important; -} - -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} - -@media (min-width: 768px) and (max-width: 991px) { - .visible-xs.visible-sm { - display: block !important; - } - tr.visible-xs.visible-sm { - display: table-row !important; - } - th.visible-xs.visible-sm, - td.visible-xs.visible-sm { - display: table-cell !important; - } -} - -@media (min-width: 992px) and (max-width: 1199px) { - .visible-xs.visible-md { - display: block !important; - } - tr.visible-xs.visible-md { - display: table-row !important; - } - th.visible-xs.visible-md, - td.visible-xs.visible-md { - display: table-cell !important; - } -} - -@media (min-width: 1200px) { - .visible-xs.visible-lg { - display: block !important; - } - tr.visible-xs.visible-lg { - display: table-row !important; - } - th.visible-xs.visible-lg, - td.visible-xs.visible-lg { - display: table-cell !important; - } -} - -.visible-sm { - display: none !important; -} - -tr.visible-sm { - display: none !important; -} - -th.visible-sm, -td.visible-sm { - display: none !important; -} - -@media (max-width: 767px) { - .visible-sm.visible-xs { - display: block !important; - } - tr.visible-sm.visible-xs { - display: table-row !important; - } - th.visible-sm.visible-xs, - td.visible-sm.visible-xs { - display: table-cell !important; - } -} - -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} - -@media (min-width: 992px) and (max-width: 1199px) { - .visible-sm.visible-md { - display: block !important; - } - tr.visible-sm.visible-md { - display: table-row !important; - } - th.visible-sm.visible-md, - td.visible-sm.visible-md { - display: table-cell !important; - } -} - -@media (min-width: 1200px) { - .visible-sm.visible-lg { - display: block !important; - } - tr.visible-sm.visible-lg { - display: table-row !important; - } - th.visible-sm.visible-lg, - td.visible-sm.visible-lg { - display: table-cell !important; - } -} - -.visible-md { - display: none !important; -} - -tr.visible-md { +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { display: none !important; } - -th.visible-md, -td.visible-md { +.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-md.visible-xs { + .visible-xs { display: block !important; } - tr.visible-md.visible-xs { - display: table-row !important; - } - th.visible-md.visible-xs, - td.visible-md.visible-xs { - display: table-cell !important; - } -} - -@media (min-width: 768px) and (max-width: 991px) { - .visible-md.visible-sm { - display: block !important; + table.visible-xs { + display: table !important; } - tr.visible-md.visible-sm { + tr.visible-xs { display: table-row !important; } - th.visible-md.visible-sm, - td.visible-md.visible-sm { + th.visible-xs, + td.visible-xs { display: table-cell !important; } } - -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { +@media (max-width: 767px) { + .visible-xs-block { display: block !important; } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } } - -@media (min-width: 1200px) { - .visible-md.visible-lg { - display: block !important; - } - tr.visible-md.visible-lg { - display: table-row !important; - } - th.visible-md.visible-lg, - td.visible-md.visible-lg { - display: table-cell !important; +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; } } - -.visible-lg { - display: none !important; -} - -tr.visible-lg { - display: none !important; -} - -th.visible-lg, -td.visible-lg { - display: none !important; -} - @media (max-width: 767px) { - .visible-lg.visible-xs { - display: block !important; - } - tr.visible-lg.visible-xs { - display: table-row !important; - } - th.visible-lg.visible-xs, - td.visible-lg.visible-xs { - display: table-cell !important; + .visible-xs-inline-block { + display: inline-block !important; } } - @media (min-width: 768px) and (max-width: 991px) { - .visible-lg.visible-sm { + .visible-sm { display: block !important; } - tr.visible-lg.visible-sm { - display: table-row !important; - } - th.visible-lg.visible-sm, - td.visible-lg.visible-sm { - display: table-cell !important; - } -} - -@media (min-width: 992px) and (max-width: 1199px) { - .visible-lg.visible-md { - display: block !important; + table.visible-sm { + display: table !important; } - tr.visible-lg.visible-md { + tr.visible-sm { display: table-row !important; } - th.visible-lg.visible-md, - td.visible-lg.visible-md { + th.visible-sm, + td.visible-sm { display: table-cell !important; } } - -@media (min-width: 1200px) { - .visible-lg { +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { display: block !important; } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} - -.hidden-xs { - display: block !important; -} - -tr.hidden-xs { - display: table-row !important; -} - -th.hidden-xs, -td.hidden-xs { - display: table-cell !important; } - -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } - tr.hidden-xs { - display: none !important; - } - th.hidden-xs, - td.hidden-xs { - display: none !important; +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; } } - @media (min-width: 768px) and (max-width: 991px) { - .hidden-xs.hidden-sm { - display: none !important; - } - tr.hidden-xs.hidden-sm { - display: none !important; - } - th.hidden-xs.hidden-sm, - td.hidden-xs.hidden-sm { - display: none !important; + .visible-sm-inline-block { + display: inline-block !important; } } - @media (min-width: 992px) and (max-width: 1199px) { - .hidden-xs.hidden-md { - display: none !important; - } - tr.hidden-xs.hidden-md { - display: none !important; - } - th.hidden-xs.hidden-md, - td.hidden-xs.hidden-md { - display: none !important; + .visible-md { + display: block !important; } -} - -@media (min-width: 1200px) { - .hidden-xs.hidden-lg { - display: none !important; + table.visible-md { + display: table !important; } - tr.hidden-xs.hidden-lg { - display: none !important; + tr.visible-md { + display: table-row !important; } - th.hidden-xs.hidden-lg, - td.hidden-xs.hidden-lg { - display: none !important; + th.visible-md, + td.visible-md { + display: table-cell !important; } } - -.hidden-sm { - display: block !important; -} - -tr.hidden-sm { - display: table-row !important; -} - -th.hidden-sm, -td.hidden-sm { - display: table-cell !important; -} - -@media (max-width: 767px) { - .hidden-sm.hidden-xs { - display: none !important; - } - tr.hidden-sm.hidden-xs { - display: none !important; - } - th.hidden-sm.hidden-xs, - td.hidden-sm.hidden-xs { - display: none !important; +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; } } - -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } - tr.hidden-sm { - display: none !important; - } - th.hidden-sm, - td.hidden-sm { - display: none !important; +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; } } - @media (min-width: 992px) and (max-width: 1199px) { - .hidden-sm.hidden-md { - display: none !important; - } - tr.hidden-sm.hidden-md { - display: none !important; - } - th.hidden-sm.hidden-md, - td.hidden-sm.hidden-md { - display: none !important; + .visible-md-inline-block { + display: inline-block !important; } } - @media (min-width: 1200px) { - .hidden-sm.hidden-lg { - display: none !important; - } - tr.hidden-sm.hidden-lg { - display: none !important; - } - th.hidden-sm.hidden-lg, - td.hidden-sm.hidden-lg { - display: none !important; + .visible-lg { + display: block !important; } -} - -.hidden-md { - display: block !important; -} - -tr.hidden-md { - display: table-row !important; -} - -th.hidden-md, -td.hidden-md { - display: table-cell !important; -} - -@media (max-width: 767px) { - .hidden-md.hidden-xs { - display: none !important; + table.visible-lg { + display: table !important; } - tr.hidden-md.hidden-xs { - display: none !important; + tr.visible-lg { + display: table-row !important; } - th.hidden-md.hidden-xs, - td.hidden-md.hidden-xs { - display: none !important; + th.visible-lg, + td.visible-lg { + display: table-cell !important; } } - -@media (min-width: 768px) and (max-width: 991px) { - .hidden-md.hidden-sm { - display: none !important; - } - tr.hidden-md.hidden-sm { - display: none !important; - } - th.hidden-md.hidden-sm, - td.hidden-md.hidden-sm { - display: none !important; +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; } } - -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } - tr.hidden-md { - display: none !important; - } - th.hidden-md, - td.hidden-md { - display: none !important; +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; } } - @media (min-width: 1200px) { - .hidden-md.hidden-lg { - display: none !important; + .visible-lg-inline-block { + display: inline-block !important; } - tr.hidden-md.hidden-lg { - display: none !important; - } - th.hidden-md.hidden-lg, - td.hidden-md.hidden-lg { - display: none !important; - } -} - -.hidden-lg { - display: block !important; -} - -tr.hidden-lg { - display: table-row !important; -} - -th.hidden-lg, -td.hidden-lg { - display: table-cell !important; } - @media (max-width: 767px) { - .hidden-lg.hidden-xs { - display: none !important; - } - tr.hidden-lg.hidden-xs { - display: none !important; - } - th.hidden-lg.hidden-xs, - td.hidden-lg.hidden-xs { + .hidden-xs { display: none !important; } } - @media (min-width: 768px) and (max-width: 991px) { - .hidden-lg.hidden-sm { - display: none !important; - } - tr.hidden-lg.hidden-sm { - display: none !important; - } - th.hidden-lg.hidden-sm, - td.hidden-lg.hidden-sm { + .hidden-sm { display: none !important; } } - @media (min-width: 992px) and (max-width: 1199px) { - .hidden-lg.hidden-md { - display: none !important; - } - tr.hidden-lg.hidden-md { - display: none !important; - } - th.hidden-lg.hidden-md, - td.hidden-lg.hidden-md { + .hidden-md { display: none !important; } } - @media (min-width: 1200px) { .hidden-lg { display: none !important; } - tr.hidden-lg { - display: none !important; - } - th.hidden-lg, - td.hidden-lg { - display: none !important; - } } - .visible-print { display: none !important; } - -tr.visible-print { - display: none !important; -} - -th.visible-print, -td.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; } @@ -6803,14 +6767,34 @@ td.visible-print { td.visible-print { display: table-cell !important; } - .hidden-print { - display: none !important; +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; } - tr.hidden-print { - display: none !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; } - th.hidden-print, - td.hidden-print { +} +@media print { + .hidden-print { display: none !important; } -} \ No newline at end of file +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css.map b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css.map new file mode 100644 index 0000000000000000000000000000000000000000..9f60ed2b1bd4ed75d3b2861366a82bd678404963 --- /dev/null +++ b/OpenAuth.Identity/wwwroot/lib/bootstrap/css/bootstrap.css.map @@ -0,0 +1 @@ +{"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":"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,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;EA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DH;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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","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