diff --git a/.gitignore b/.gitignore index 940794e..61cea9d 100644 --- a/.gitignore +++ b/.gitignore @@ -286,3 +286,5 @@ __pycache__/ *.btm.cs *.odx.cs *.xsd.cs + +*.db diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 56e8e37..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md - "version": "0.2.0", - "configurations": [ - { - "name": "DiscordianAuth", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "buildAuth", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/DiscordianAuth/bin/Debug/netcoreapp2.0/DiscordianAuth.dll", - "args": [], - "cwd": "${workspaceFolder}/DiscordianAuth", - "stopAtEntry": false, - "internalConsoleOptions": "openOnSessionStart", - "launchBrowser": { - "enabled": true, - "args": "${auto-detect-url}", - "windows": { - "command": "cmd.exe", - "args": "/C start ${auto-detect-url}" - }, - "osx": { - "command": "open" - }, - "linux": { - "command": "xdg-open" - } - }, - "env": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "sourceFileMap": { - "/Views": "${workspaceFolder}/Views" - } - }, - { - "name": "DiscordianServer", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "buildServer", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/DiscordianServer/bin/Debug/netcoreapp2.1/DiscordianServer.dll", - "args": [], - "cwd": "${workspaceFolder}/DiscordianServer", - "stopAtEntry": false, - "internalConsoleOptions": "openOnSessionStart", - "launchBrowser": { - "enabled": true, - "args": "${auto-detect-url}", - "windows": { - "command": "cmd.exe", - "args": "/C start ${auto-detect-url}" - }, - "osx": { - "command": "open" - }, - "linux": { - "command": "xdg-open" - } - }, - "env": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "sourceFileMap": { - "/Views": "${workspaceFolder}/Views" - } - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach", - "processId": "${command:pickProcess}" - } - ,] -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 4edd880..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "buildServer", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/DiscordianServer/DiscordianServer.csproj" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "buildAuth", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/DiscordianAuth/DiscordianAuth.csproj" - ], - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/Api/Api.csproj b/Api/Api.csproj new file mode 100644 index 0000000..d3db56d --- /dev/null +++ b/Api/Api.csproj @@ -0,0 +1,65 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + + + + + + Config\EmailConnection.cs + + + Config\IdentityServerConnection.cs + + + Config\MongoConnections.cs + + + Config\MongoOptions.cs + + + + + + + + + + + + + + + ..\..\..\..\..\usr\local\share\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnet.webapi.client\5.2.6\lib\netstandard2.0\System.Net.Http.Formatting.dll + + + + + + + + + + + + + + + + diff --git a/Api/Api/IdentityServerClient.cs b/Api/Api/IdentityServerClient.cs new file mode 100644 index 0000000..a01d20e --- /dev/null +++ b/Api/Api/IdentityServerClient.cs @@ -0,0 +1,268 @@ +//---------------------- +// +// Generated using the NSwag toolchain v13.0.6.0 (NJsonSchema v10.0.23.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." + +namespace Api.Api +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.0.6.0 (NJsonSchema v10.0.23.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class IdentityServerClient + { + private string _baseUrl = ""; + private System.Net.Http.HttpClient _httpClient; + private System.Lazy _settings; + + public IdentityServerClient(string baseUrl, System.Net.Http.HttpClient httpClient) + { + BaseUrl = baseUrl; + _httpClient = httpClient; + _settings = new System.Lazy(() => + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + }); + } + + public string BaseUrl + { + get { return _baseUrl; } + set { _baseUrl = value; } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } } + + partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// Success + /// A server side error occurred. + public System.Threading.Tasks.Task> GetUserDetailsAsync(System.Collections.Generic.IEnumerable userIds) + { + return GetUserDetailsAsync(userIds, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// Success + /// A server side error occurred. + public async System.Threading.Tasks.Task> GetUserDetailsAsync(System.Collections.Generic.IEnumerable userIds, System.Threading.CancellationToken cancellationToken) + { + var urlBuilder_ = new System.Text.StringBuilder(); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Users"); + + var client_ = _httpClient; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(userIds, _settings.Value)); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + PrepareRequest(client_, request_, urlBuilder_); + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + try + { + var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = ((int)response_.StatusCode).ToString(); + if (status_ == "200") + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_).ConfigureAwait(false); + return objectResponse_.Object; + } + else + if (status_ != "200" && status_ != "204") + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + (int)response_.StatusCode + ").", (int)response_.StatusCode, responseData_, headers_, null); + } + + return default(System.Collections.Generic.ICollection); + } + finally + { + if (response_ != null) + response_.Dispose(); + } + } + } + finally + { + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value is System.Enum) + { + string name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + } + } + else if (value is bool) { + return System.Convert.ToString(value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value != null && value.GetType().IsArray) + { + var array = System.Linq.Enumerable.OfType((System.Array) value); + return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo))); + } + + return System.Convert.ToString(value, cultureInfo); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.0.23.0 (Newtonsoft.Json v11.0.0.0)")] + public partial class UserDetails + { + [Newtonsoft.Json.JsonProperty("displayName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string DisplayName { get; set; } + + [Newtonsoft.Json.JsonProperty("emailAddress", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string EmailAddress { get; set; } + + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Id { get; set; } + + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.0.6.0 (NJsonSchema v10.0.23.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + response.Substring(0, response.Length >= 512 ? 512 : response.Length), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "13.0.6.0 (NJsonSchema v10.0.23.0 (Newtonsoft.Json v11.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 1591 +#pragma warning restore 1573 +#pragma warning restore 472 +#pragma warning restore 114 +#pragma warning restore 108 \ No newline at end of file diff --git a/Api/Controllers/ClientConfigsController.cs b/Api/Controllers/ClientConfigsController.cs new file mode 100644 index 0000000..d0040c1 --- /dev/null +++ b/Api/Controllers/ClientConfigsController.cs @@ -0,0 +1,59 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Interfaces.UseCases.ClientConfig; +using Api.Extensions; +using Api.Models.Request.ClientConfig; +using Api.Presenters; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Produces("application/json")] + [Route("api/{organisation}/clients/configs")] + [Authorize] + [ApiController] + public class ClientConfigsController : ControllerBase + { + private readonly DefaultPresenter _defaultPresenter; + private readonly IGetClientConfigUseCase _getClientConfigUseCase; + private readonly IUpdateClientConfigUseCase _updateClientConfigUseCase; + + public ClientConfigsController(DefaultPresenter defaultPresenter, IGetClientConfigUseCase clientConfigUseCase, IUpdateClientConfigUseCase updateClientConfigUseCase) + { + _defaultPresenter = defaultPresenter; + _getClientConfigUseCase = clientConfigUseCase; + _updateClientConfigUseCase = updateClientConfigUseCase; + } + + [HttpGet("{config}", Name = "GetClientConfig")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetClientConfigAsync(Guid organisation, Guid config) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getClientConfigUseCase.HandleAsync(new GetClientConfigRequest(User.GetId(), organisation, config), + _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{config}", Name = "UpdateClientConfig")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> UpdateClientConfigAsync(Guid organisation, Guid config, UpdateClientConfigDTO request) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _updateClientConfigUseCase.HandleAsync( + new UpdateClientConfigRequest(User.GetId(), organisation, config, request.ClientEventGroups, request.UserEventGroups), + _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + } +} \ No newline at end of file diff --git a/Api/Controllers/ClientsController.cs b/Api/Controllers/ClientsController.cs new file mode 100644 index 0000000..fc32689 --- /dev/null +++ b/Api/Controllers/ClientsController.cs @@ -0,0 +1,108 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Interfaces.UseCases.Client; +using Api.Extensions; +using Api.Models.Request.Client; +using Api.Presenters; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Produces("application/json")] + [Route("api/{organisation}/clients")] + [Authorize] + [ApiController] + public class ClientsController : Controller + { + private readonly DefaultPresenter _defaultPresenter; + private readonly IGetClientsUseCase _getClientsUseCase; + private readonly ICreateClientUseCase _createClientUseCase; + private readonly IGetClientUseCase _getClientUseCase; + private readonly IUpdateClientUseCase _updateClientUseCase; + private readonly IDeleteClientUseCase _deleteClientUseCase; + + public ClientsController(DefaultPresenter defaultPresenter, IGetClientsUseCase getClientsUseCase, ICreateClientUseCase createClientUseCase, IGetClientUseCase getClientUseCase, IUpdateClientUseCase updateClientUseCase, IDeleteClientUseCase deleteClientUseCase) + { + _defaultPresenter = defaultPresenter; + _getClientsUseCase = getClientsUseCase; + _createClientUseCase = createClientUseCase; + _getClientUseCase = getClientUseCase; + _updateClientUseCase = updateClientUseCase; + _deleteClientUseCase = deleteClientUseCase; + } + + [HttpGet("", Name = "GetClients")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> GetClientsAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getClientsUseCase.HandleAsync(new GetClientsRequest(User.GetId(), organisation), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("", Name = "CreateClient")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> CreateClientAsync(Guid organisation, CreateClientDTO createClientDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _createClientUseCase.HandleAsync(new CreateClientRequest(User.GetId(), organisation, createClientDTO.Name, createClientDTO.Description), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpGet("{client}", Name = "GetClient")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> GetClientAsync(Guid organisation, Guid client) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getClientUseCase.HandleAsync(new GetClientRequest(User.GetId(), organisation, client), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{client}", Name = "UpdateClient")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> UpdateClientAsync(Guid organisation, Guid client, UpdateClientDTO updateClientDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _updateClientUseCase.HandleAsync(new UpdateClientRequest(User.GetId(), organisation, client, updateClientDTO.Name, updateClientDTO.Description), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpDelete("{client}", Name = "DeleteClient")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> DeleteClientAsync(Guid organisation, Guid client) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _deleteClientUseCase.HandleAsync(new DeleteClientRequest(User.GetId(), organisation, client), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + } +} \ No newline at end of file diff --git a/Api/Controllers/GroupsController.cs b/Api/Controllers/GroupsController.cs new file mode 100644 index 0000000..19b8f56 --- /dev/null +++ b/Api/Controllers/GroupsController.cs @@ -0,0 +1,108 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Interfaces.UseCases.Group; +using Api.Extensions; +using Api.Models.Request.Group; +using Api.Presenters; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Produces("application/json")] + [Route("api/{organisation}/groups")] + [Authorize] + [ApiController] + public class GroupsController : Controller + { + private readonly DefaultPresenter _defaultPresenter; + private readonly IGetGroupsUseCase _getGroupsUseCase; + private readonly ICreateGroupUseCase _createGroupUseCase; + private readonly IGetGroupUseCase _getGroupUseCase; + private readonly IUpdateGroupUseCase _updateGroupUseCase; + private readonly IDeleteGroupUseCase _deleteGroupUseCase; + + public GroupsController(DefaultPresenter defaultPresenter, IGetGroupsUseCase getGroupsUseCase, ICreateGroupUseCase createGroupUseCase, IGetGroupUseCase getGroupUseCase, IUpdateGroupUseCase updateGroupUseCase, IDeleteGroupUseCase deleteGroupUseCase) + { + _defaultPresenter = defaultPresenter; + _getGroupsUseCase = getGroupsUseCase; + _createGroupUseCase = createGroupUseCase; + _getGroupUseCase = getGroupUseCase; + _updateGroupUseCase = updateGroupUseCase; + _deleteGroupUseCase = deleteGroupUseCase; + } + + [HttpGet("", Name = "GetGroups")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> GetGroupsAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getGroupsUseCase.HandleAsync(new GetGroupsRequest(User.GetId(), organisation), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("", Name = "CreateGroup")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> CreateGroupAsync(Guid organisation, CreateGroupDTO createGroupDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _createGroupUseCase.HandleAsync(new CreateGroupRequest(User.GetId(), organisation, createGroupDTO.Name, createGroupDTO.Description, createGroupDTO.ClientIds), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpGet("{group}", Name = "GetGroup")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> GetGroupAsync(Guid organisation, Guid group) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getGroupUseCase.HandleAsync(new GetGroupRequest(User.GetId(), organisation, group), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{group}", Name = "UpdateGroup")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> UpdateGroupAsync(Guid organisation, Guid group, UpdateGroupDTO updateGroupDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _updateGroupUseCase.HandleAsync(new UpdateGroupRequest(User.GetId(), organisation, group, updateGroupDTO.Name, updateGroupDTO.Description, updateGroupDTO.ClientIds), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpDelete("{group}", Name = "DeleteGroup")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> DeleteGroupAsync(Guid organisation, Guid group) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _deleteGroupUseCase.HandleAsync(new DeleteGroupRequest(User.GetId(), organisation, group), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + } +} \ No newline at end of file diff --git a/Api/Controllers/InvitesController.cs b/Api/Controllers/InvitesController.cs new file mode 100644 index 0000000..abbb489 --- /dev/null +++ b/Api/Controllers/InvitesController.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Interfaces.UseCases.Invite; +using Api.Extensions; +using Api.Models.Request.Invite; +using Api.Presenters; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Produces("application/json")] + [Route("api/{organisation}/invites")] + [Authorize] + [ApiController] + public class InvitesController : Controller + { + private readonly DefaultPresenter _defaultPresenter; + private readonly ICreateInviteUseCase _createInviteUseCase; + private readonly IUseInviteUseCase _useInviteUseCase; + + public InvitesController(DefaultPresenter defaultPresenter, ICreateInviteUseCase createInviteUseCase, IUseInviteUseCase useInviteUseCase) + { + _defaultPresenter = defaultPresenter; + _createInviteUseCase = createInviteUseCase; + _useInviteUseCase = useInviteUseCase; + } + + [HttpPost("", Name = "CreateInvite")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> CreateInviteAsync(Guid organisation, CreateInviteDTO createDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _createInviteUseCase.HandleAsync(new CreateInviteRequest(User.GetId(), organisation, createDTO.EmailAddress), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{token}", Name = "UseInvite")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task UseInviteAsync(Guid organisation, string token) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _useInviteUseCase.HandleAsync(new UseInviteRequest(User.GetId(), organisation, User.GetClaim("EmailAddress"), token), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + } +} \ No newline at end of file diff --git a/Api/Controllers/OrganisationsController.cs b/Api/Controllers/OrganisationsController.cs new file mode 100644 index 0000000..8ba1641 --- /dev/null +++ b/Api/Controllers/OrganisationsController.cs @@ -0,0 +1,142 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Interfaces.UseCases.Organisation; +using Api.Core.Interfaces.UseCases.OrganisationUser; +using Api.Extensions; +using Api.Models.Request.Organisation; +using Api.Presenters; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Produces("application/json")] + [Route("api/")] + [Authorize] + [ApiController] + public class OrganisationsController : Controller + { + private readonly DefaultPresenter _defaultPresenter; + private readonly GetOrganisationPresenter _getOrganisationPresenter; + private readonly IGetOrganisationsUseCase _getOrganisationsUseCase; + private readonly ICreateOrganisationUseCase _createOrganisationUseCase; + private readonly IGetOrganisationUseCase _getOrganisationUseCase; + private readonly IUpdateOrganisationUseCase _updateOrganisationUseCase; + private readonly IDeleteOrganisationUseCase _deleteOrganisationUseCase; + private readonly IDeleteOrganisationUserUseCase _deleteOrganisationUserUseCase; + + public OrganisationsController(DefaultPresenter defaultPresenter, GetOrganisationPresenter getOrganisationPresenter, IGetOrganisationsUseCase getOrganisationsUseCase, ICreateOrganisationUseCase createOrganisationUseCase, IGetOrganisationUseCase getOrganisationUseCase, IUpdateOrganisationUseCase updateOrganisationUseCase, IDeleteOrganisationUseCase deleteOrganisationUseCase, IDeleteOrganisationUserUseCase deleteOrganisationUserUseCase) + { + _defaultPresenter = defaultPresenter; + _getOrganisationPresenter = getOrganisationPresenter; + _getOrganisationsUseCase = getOrganisationsUseCase; + _createOrganisationUseCase = createOrganisationUseCase; + _getOrganisationUseCase = getOrganisationUseCase; + _updateOrganisationUseCase = updateOrganisationUseCase; + _deleteOrganisationUseCase = deleteOrganisationUseCase; + _deleteOrganisationUserUseCase = deleteOrganisationUserUseCase; + } + + [HttpGet("organisations", Name = "GetOrganisations")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetOrganisationsAsync() + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getOrganisationsUseCase.HandleAsync(new GetOrganisationsRequest(User.GetId()), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("organisations", Name = "CreateOrganisation")] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> CreateOrganisationAsync(CreateOrganisationDTO orgDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _createOrganisationUseCase.HandleAsync(new CreateOrganisationRequest(User.GetId(), orgDTO.Name), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpGet("{organisation}", Name = "GetOrganisation")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> GetOrganisationAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + //We don't provide the user ID because it: + //Allows us to get the name and ID of an organisation without being a part of it. + //Used for displaying what organisation an invite belongs to usually. + await _getOrganisationUseCase.HandleAsync(new GetOrganisationRequest(organisation), _getOrganisationPresenter); + + return _getOrganisationPresenter.ContentResult; + } + + [HttpGet("{organisation}/details", Name = "GetOrganisationDetails")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> GetOrganisationDetailsAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getOrganisationUseCase.HandleAsync(new GetOrganisationRequest(User.GetId(), organisation), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{organisation}", Name = "UpdateOrganisation")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> UpdateOrganisationAsync(Guid organisation, UpdateOrganisationDTO updateOrganisationDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _updateOrganisationUseCase.HandleAsync(new UpdateOrganisationRequest(User.GetId(), organisation, updateOrganisationDTO.Name), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpDelete("{organisation}", Name = "DeleteOrganisation")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> DeleteOrganisationAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _deleteOrganisationUseCase.HandleAsync(new DeleteOrganisationRequest(User.GetId(), organisation), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{organisation}/leave", Name = "LeaveOrganisation")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + public async Task> LeaveOrganisationAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _deleteOrganisationUserUseCase.HandleAsync(new DeleteOrganisationUserRequest(User.GetId(), organisation, User.GetId()), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + } +} \ No newline at end of file diff --git a/Api/Controllers/UsersController.cs b/Api/Controllers/UsersController.cs new file mode 100644 index 0000000..cfe09d3 --- /dev/null +++ b/Api/Controllers/UsersController.cs @@ -0,0 +1,79 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Interfaces.UseCases.OrganisationUser; +using Api.Extensions; +using Api.Models.Request.OrganisationUser; +using Api.Presenters; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Api.Controllers +{ + [Produces("application/json")] + [Route("api/{organisation}/users")] + [Authorize] + [ApiController] + public class UsersController : Controller + { + private readonly DefaultPresenter _defaultPresenter; + private readonly IGetOrganisationUsersUseCase _getOrganisationUsersUseCase; + private readonly IGetOrganisationUserUseCase _getOrganisationUserUseCase; + private readonly IUpdateOrganisationUserUseCase _updateOrganisationUserUseCase; + private readonly IDeleteOrganisationUserUseCase _deleteOrganisationUserUseCase; + + public UsersController(DefaultPresenter defaultPresenter, IGetOrganisationUsersUseCase getOrganisationUsersUseCase, IGetOrganisationUserUseCase getOrganisationUserUseCase, IUpdateOrganisationUserUseCase updateOrganisationUserUseCase, IDeleteOrganisationUserUseCase deleteOrganisationUserUseCase) + { + _defaultPresenter = defaultPresenter; + _getOrganisationUsersUseCase = getOrganisationUsersUseCase; + _getOrganisationUserUseCase = getOrganisationUserUseCase; + _updateOrganisationUserUseCase = updateOrganisationUserUseCase; + _deleteOrganisationUserUseCase = deleteOrganisationUserUseCase; + } + + [HttpGet("", Name = "GetUsers")] + public async Task> GetUsersAsync(Guid organisation) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getOrganisationUsersUseCase.HandleAsync(new GetOrganisationUsersRequest(User.GetId(), organisation), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpGet("{user}", Name = "GetUser")] + public async Task> GetUserAsync(Guid organisation, string user) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _getOrganisationUserUseCase.HandleAsync(new GetOrganisationUserRequest(User.GetId(), organisation, user), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpPost("{user}", Name = "UpdateUser")] + public async Task> UpdateUserAsync(Guid organisation, string user, UpdateOrganisationUserDTO userUpdateDTO) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _updateOrganisationUserUseCase.HandleAsync(new UpdateOrganisationUserRequest(User.GetId(), organisation, user, userUpdateDTO.Permissions), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + + [HttpDelete("{user}", Name = "DeleteUser")] + public async Task> DeleteUserAsync(Guid organisation, string user) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + await _deleteOrganisationUserUseCase.HandleAsync(new DeleteOrganisationUserRequest(User.GetId(), organisation, user), _defaultPresenter); + + return _defaultPresenter.ContentResult; + } + } +} \ No newline at end of file diff --git a/Api/Dockerfile b/Api/Dockerfile new file mode 100644 index 0000000..8f325e3 --- /dev/null +++ b/Api/Dockerfile @@ -0,0 +1,37 @@ +FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env + +#Setup required components of Api +WORKDIR /ChatChain/ChatChainCommon +COPY ./ChatChainCommon/*.csproj ./ +RUN dotnet restore +COPY ./ChatChainCommon ./ + +WORKDIR /ChatChain/Core.Core +COPY ./Core.Core/*.csproj ./ +RUN dotnet restore +COPY ./Core.Core ./ + +WORKDIR /ChatChain/Core.Infrastructure +COPY ./Core.Infrastructure/*.csproj ./ +RUN dotnet restore +COPY ./Core.Infrastructure ./ + +WORKDIR /ChatChain/Api + +# Copy csproj and restore as distinct layers +COPY ./Api/*.csproj ./ +RUN dotnet restore + +# Copy everything else and build +COPY ./Api ./ +RUN dotnet publish -c Release -o out + +# Build runtime image +FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 +WORKDIR /ChatChain/Api +COPY --from=build-env /ChatChain/Api/out . + +ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.2.1/wait /wait +RUN chmod +x /wait + +CMD /wait && dotnet Api.dll \ No newline at end of file diff --git a/Api/Extensions/ClaimsPrincipleExtension.cs b/Api/Extensions/ClaimsPrincipleExtension.cs new file mode 100644 index 0000000..8f64098 --- /dev/null +++ b/Api/Extensions/ClaimsPrincipleExtension.cs @@ -0,0 +1,18 @@ +using System.Linq; +using System.Security.Claims; + +namespace Api.Extensions +{ + public static class ClaimsPrincipleExtension + { + public static string GetId(this ClaimsPrincipal principal) + { + return GetClaim(principal, "sub"); + } + + public static string GetClaim(this ClaimsPrincipal principal, string claimType) + { + return principal.Claims.First(claim => claim.Type.Equals(claimType)).Value; + } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Client/CreateClientDTO.cs b/Api/Models/Request/Client/CreateClientDTO.cs new file mode 100644 index 0000000..d3924f2 --- /dev/null +++ b/Api/Models/Request/Client/CreateClientDTO.cs @@ -0,0 +1,10 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Api.Models.Request.Client +{ + public class CreateClientDTO + { + public string Name { get; set; } + + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Client/UpdateClientDTO.cs b/Api/Models/Request/Client/UpdateClientDTO.cs new file mode 100644 index 0000000..bef47d3 --- /dev/null +++ b/Api/Models/Request/Client/UpdateClientDTO.cs @@ -0,0 +1,10 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Api.Models.Request.Client +{ + public class UpdateClientDTO + { + public string Name { get; set; } + + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/ClientConfig/UpdateClientConfigDTO.cs b/Api/Models/Request/ClientConfig/UpdateClientConfigDTO.cs new file mode 100644 index 0000000..aed4839 --- /dev/null +++ b/Api/Models/Request/ClientConfig/UpdateClientConfigDTO.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace Api.Models.Request.ClientConfig +{ + public class UpdateClientConfigDTO + { + public IEnumerable ClientEventGroups { get; set; } + + public IEnumerable UserEventGroups { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Group/CreateGroupDTO.cs b/Api/Models/Request/Group/CreateGroupDTO.cs new file mode 100644 index 0000000..618f427 --- /dev/null +++ b/Api/Models/Request/Group/CreateGroupDTO.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace Api.Models.Request.Group +{ + public class CreateGroupDTO + { + public string Name { get; set; } + + public string Description { get; set; } + + public IList ClientIds { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Group/UpdateGroupDTO.cs b/Api/Models/Request/Group/UpdateGroupDTO.cs new file mode 100644 index 0000000..58dcb30 --- /dev/null +++ b/Api/Models/Request/Group/UpdateGroupDTO.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace Api.Models.Request.Group +{ + public class UpdateGroupDTO + { + public string Name { get; set; } + + public string Description { get; set; } + + public IList ClientIds { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Invite/CreateInviteDTO.cs b/Api/Models/Request/Invite/CreateInviteDTO.cs new file mode 100644 index 0000000..c202160 --- /dev/null +++ b/Api/Models/Request/Invite/CreateInviteDTO.cs @@ -0,0 +1,8 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Api.Models.Request.Invite +{ + public class CreateInviteDTO + { + public string EmailAddress { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Organisation/CreateOrganisationDTO.cs b/Api/Models/Request/Organisation/CreateOrganisationDTO.cs new file mode 100644 index 0000000..1e66c07 --- /dev/null +++ b/Api/Models/Request/Organisation/CreateOrganisationDTO.cs @@ -0,0 +1,8 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Api.Models.Request.Organisation +{ + public class CreateOrganisationDTO + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/Organisation/UpdateOrganisationDTO.cs b/Api/Models/Request/Organisation/UpdateOrganisationDTO.cs new file mode 100644 index 0000000..9581ba2 --- /dev/null +++ b/Api/Models/Request/Organisation/UpdateOrganisationDTO.cs @@ -0,0 +1,8 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Api.Models.Request.Organisation +{ + public class UpdateOrganisationDTO + { + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Api/Models/Request/OrganisationUser/UpdateOrganisationUserDTO.cs b/Api/Models/Request/OrganisationUser/UpdateOrganisationUserDTO.cs new file mode 100644 index 0000000..9513010 --- /dev/null +++ b/Api/Models/Request/OrganisationUser/UpdateOrganisationUserDTO.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Api.Core.Entities; +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace Api.Models.Request.OrganisationUser +{ + public class UpdateOrganisationUserDTO + { + public IList Permissions { get; set; } + } +} \ No newline at end of file diff --git a/Api/Presenters/DefaultPresenter.cs b/Api/Presenters/DefaultPresenter.cs new file mode 100644 index 0000000..6e94c0e --- /dev/null +++ b/Api/Presenters/DefaultPresenter.cs @@ -0,0 +1,22 @@ +using Api.Core.Interfaces; +using Api.Serialization; +using Microsoft.AspNetCore.Http; + +namespace Api.Presenters +{ + public class DefaultPresenter : IOutputPort + { + public JsonContentResult ContentResult { get; } + + public DefaultPresenter() + { + ContentResult = new JsonContentResult(); + } + + public void Handle(UseCaseResponseMessage response) + { + ContentResult.StatusCode = response.Success ? StatusCodes.Status200OK : StatusCodes.Status400BadRequest; + ContentResult.Content = JsonSerializer.SerializeObject(response); + } + } +} \ No newline at end of file diff --git a/Api/Presenters/GetOrganisationPresenter.cs b/Api/Presenters/GetOrganisationPresenter.cs new file mode 100644 index 0000000..a39b7f1 --- /dev/null +++ b/Api/Presenters/GetOrganisationPresenter.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using Api.Core.DTO; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Serialization; +using Microsoft.AspNetCore.Http; +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace Api.Presenters +{ + public class GetOrganisationPresenter : IOutputPort + { + public JsonContentResult ContentResult { get; } + + public GetOrganisationPresenter() + { + ContentResult = new JsonContentResult(); + } + + public void Handle(GetOrganisationResponse response) + { + ContentResult.StatusCode = response.Success ? StatusCodes.Status200OK : StatusCodes.Status400BadRequest; + ContentResult.Content = JsonSerializer.SerializeObject(new ResponseObjectDTO + { + Organisation = response.Organisation.ToOrganisation(), + Success = response.Success, + Message = response.Message, + CheckedPermissions = response.CheckedPermissions, + Errors = response.Errors + }); + } + } + + public class ResponseObjectDTO + { + public Organisation Organisation { get; set; } + + public bool Success { get; set; } + + public string Message { get; set; } + + public bool CheckedPermissions { get; set;} + + public IEnumerable Errors { get; set; } + } +} \ No newline at end of file diff --git a/Api/Presenters/JsonContentResult.cs b/Api/Presenters/JsonContentResult.cs new file mode 100644 index 0000000..4d43c57 --- /dev/null +++ b/Api/Presenters/JsonContentResult.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; + +namespace Api.Presenters +{ + public class JsonContentResult : ContentResult + { + public JsonContentResult() + { + ContentType = "application/json"; + } + } +} \ No newline at end of file diff --git a/Api/Program.cs b/Api/Program.cs new file mode 100644 index 0000000..b00f82e --- /dev/null +++ b/Api/Program.cs @@ -0,0 +1,51 @@ +using System.IO; +using System.Linq; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using NSwag; +using NSwag.CodeGeneration.CSharp; + +namespace Api +{ + public static class Program + { + public static void Main(string[] args) + { + if (args.ElementAtOrDefault(0) == "generate") + { + GenerateClientStuffAsync(args.ElementAtOrDefault(1)); + } + else + { + CreateWebHostBuilder(args).Build().Run(); + } + } + + private static void GenerateClientStuffAsync(string swaggerJson) + { + OpenApiDocument document = + OpenApiDocument.FromUrlAsync(swaggerJson ?? "http://localhost:5001/swagger/v1/swagger.json").Result; + + CSharpClientGeneratorSettings settings = new CSharpClientGeneratorSettings + { + ClassName = "IdentityServerClient", + CSharpGeneratorSettings = + { + Namespace = "Api.Api" + } + }; + + CSharpClientGenerator generator = new CSharpClientGenerator(document, settings); + string code = generator.GenerateFile(); + + using (StreamWriter outputFile = new StreamWriter(Path.Combine("Api", "IdentityServerClient.cs"))) + { + outputFile.Write(code); + } + } + + private static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} \ No newline at end of file diff --git a/Api/Serialization/JsonSerializer.cs b/Api/Serialization/JsonSerializer.cs new file mode 100644 index 0000000..0717c15 --- /dev/null +++ b/Api/Serialization/JsonSerializer.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; + +namespace Api.Serialization +{ + public sealed class JsonSerializer + { + private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + ContractResolver = new JsonContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + Converters = new List { new StringEnumConverter() } + }; + + public static string SerializeObject(object o) + { + return JsonConvert.SerializeObject(o, Formatting.Indented, Settings); + } + + private sealed class JsonContractResolver : DefaultContractResolver + { + } + } +} \ No newline at end of file diff --git a/Api/Startup.cs b/Api/Startup.cs new file mode 100644 index 0000000..66062c5 --- /dev/null +++ b/Api/Startup.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using Api.Core; +using Api.Infrastructure; +using Api.Infrastructure.Data.Mapping; +using Api.Infrastructure.Data.MongoDB; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using AutoMapper; +using ChatChainCommon.Config; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using StackExchange.Redis; +using Swashbuckle.AspNetCore.Swagger; + +namespace Api +{ + public class Startup + { + public Startup(IHostingEnvironment env) + { + IConfigurationBuilder builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true); + + builder.AddEnvironmentVariables(options => { options.Prefix = "ChatChain_Api_"; }); + _configuration = builder.Build(); + } + + private readonly IConfigurationRoot _configuration; + + // This method gets called by the runtime. Use this method to add services to the container. + public IServiceProvider ConfigureServices(IServiceCollection services) + { + string redisConnectionVariable = _configuration.GetValue("RedisConnection"); + ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisConnectionVariable); + services.AddSingleton(redis); + + services.AddMvc() + .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) + .AddJsonOptions(options => + { + options.SerializerSettings.Converters.Add(new StringEnumConverter()); + options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + }) + .AddControllersAsServices(); + + IdentityServerConnection identityServerConnection = new IdentityServerConnection(); + _configuration.GetSection("IdentityServerConnection").Bind(identityServerConnection); + services.AddSingleton(identityServerConnection); + + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + services.AddAuthentication("Bearer") + .AddJwtBearer("Bearer", options => + { + options.Authority = identityServerConnection.ServerUrl; + options.RequireHttpsMetadata = false; + + options.Audience = "ChatChainAPI"; + }); + + services.AddSwaggerGen(options => + { + options.SwaggerDoc("v1", new Info + { + Version = "v1", + Title = "ChatChain WebApp API", + Description = "ChatChain's WebApp API for client, group and organisation management" + } ); + + options.AddSecurityDefinition("Bearer", new ApiKeyScheme + { + Description = "JWT Authorization header using the Bearer scheme, Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = "header", + Type = "apiKey" + }); + + Dictionary> security = new Dictionary> + { + {"Bearer", new string[] {} } + }; + options.AddSecurityRequirement(security); + }); + + services.AddAutoMapper(typeof(DataProfile)); + + MongoConnectionOptions mongoConnectionOptions = new MongoConnectionOptions(); + _configuration.GetSection(nameof(MongoConnectionOptions)).Bind(mongoConnectionOptions); + services.AddSingleton(mongoConnectionOptions); + + ContainerBuilder builder = new ContainerBuilder(); + builder.RegisterModule(new CoreModule()); + builder.RegisterModule(new InfrastructureModule()); + + builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Presenter")) + .SingleInstance(); + + builder.Populate(services); + + IContainer container = builder.Build(); + return new AutofacServiceProvider(container); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public static void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseHsts(); + } + + app.UseAuthentication(); + app.UseMvc(); + + app.UseSwagger(); + + app.UseSwaggerUI(options => + { + options.SwaggerEndpoint("/swagger/v1/swagger.json", "ChatChain WebApp API V1"); + options.RoutePrefix = string.Empty; + }); + + ConfigureMongoDriver2IgnoreExtraElements(); + } + + /// + /// Configure Classes to ignore Extra Elements (e.g. _Id) when deserializing + /// As we are using "IdentityServer4.Models" we cannot add something like "[BsonIgnore]" + /// + private static void ConfigureMongoDriver2IgnoreExtraElements() + { + BsonClassMap.RegisterClassMap(cm => + { + cm.AutoMap(); + cm.SetIgnoreExtraElements(true); + }); + } + } +} \ No newline at end of file diff --git a/Api/app.config b/Api/app.config new file mode 100644 index 0000000..a6ad828 --- /dev/null +++ b/Api/app.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DiscordianServer/published/appsettings.Development.json b/Api/appsettings.Development.json similarity index 96% rename from DiscordianServer/published/appsettings.Development.json rename to Api/appsettings.Development.json index 0623a3f..e203e94 100644 --- a/DiscordianServer/published/appsettings.Development.json +++ b/Api/appsettings.Development.json @@ -1,4 +1,4 @@ -{ +{ "Logging": { "LogLevel": { "Default": "Debug", diff --git a/DiscordianServer/appsettings.json b/Api/appsettings.json similarity index 70% rename from DiscordianServer/appsettings.json rename to Api/appsettings.json index 09cf536..def9159 100644 --- a/DiscordianServer/appsettings.json +++ b/Api/appsettings.json @@ -1,7 +1,8 @@ -{ +{ "Logging": { "LogLevel": { "Default": "Warning" } - } + }, + "AllowedHosts": "*" } diff --git a/Assets/ChatChainBackground.png b/Assets/ChatChainBackground.png new file mode 100644 index 0000000..f84b2a8 Binary files /dev/null and b/Assets/ChatChainBackground.png differ diff --git a/Assets/ChatChainBackground.xcf b/Assets/ChatChainBackground.xcf new file mode 100644 index 0000000..a1efa3d Binary files /dev/null and b/Assets/ChatChainBackground.xcf differ diff --git a/ChatChainCommon/ChatChainCommon.csproj b/ChatChainCommon/ChatChainCommon.csproj new file mode 100644 index 0000000..22bfd8d --- /dev/null +++ b/ChatChainCommon/ChatChainCommon.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp2.2 + + + + + + + + + diff --git a/ChatChainCommon/Config/ApiConnection.cs b/ChatChainCommon/Config/ApiConnection.cs new file mode 100644 index 0000000..0e815e0 --- /dev/null +++ b/ChatChainCommon/Config/ApiConnection.cs @@ -0,0 +1,8 @@ +namespace ChatChainCommon.Config +{ + public class ApiConnection + { + // ReSharper disable once UnusedAutoPropertyAccessor.Global + public string ServerUrl { get; set; } + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/EmailConnection.cs b/ChatChainCommon/Config/EmailConnection.cs new file mode 100644 index 0000000..307b0bf --- /dev/null +++ b/ChatChainCommon/Config/EmailConnection.cs @@ -0,0 +1,14 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global + +namespace ChatChainCommon.Config +{ + public class EmailConnection + { + public string Username { get; set; } + public string Password { get; set; } + public string Host { get; set; } + public int Port { get; set; } = 587; + public bool Ssl { get; set; } = true; + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/IdentityServer/ClientConfig.cs b/ChatChainCommon/Config/IdentityServer/ClientConfig.cs new file mode 100644 index 0000000..c3db6a7 --- /dev/null +++ b/ChatChainCommon/Config/IdentityServer/ClientConfig.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using IdentityServer4; + +namespace ChatChainCommon.Config.IdentityServer +{ + public class ClientConfig + { + public string ClientId { get; set; } + + public string ClientName { get; set; } + + public string FrontChannelLogoutUri { get; set; } + + public ICollection RedirectUris { get; set; } + + public ICollection PostLogoutRedirectUris { get; set; } + + public ICollection AllowedCorsOrigins { get; set; } + + public ICollection AllowedGrantTypes { get; set; } + + public ICollection Secrets { get; set; } = new List(); + + public ICollection AllowedScopes { get; set; } = new List + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + "ChatChainAPI" + }; + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/IdentityServer/ClientsConfig.cs b/ChatChainCommon/Config/IdentityServer/ClientsConfig.cs new file mode 100644 index 0000000..1e6a1aa --- /dev/null +++ b/ChatChainCommon/Config/IdentityServer/ClientsConfig.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace ChatChainCommon.Config.IdentityServer +{ + public class ClientsConfig + { + public List ClientConfigs { get; set; } + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/IdentityServer/IdentityConfig.cs b/ChatChainCommon/Config/IdentityServer/IdentityConfig.cs new file mode 100644 index 0000000..74263af --- /dev/null +++ b/ChatChainCommon/Config/IdentityServer/IdentityConfig.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using IdentityServer4.Models; + +namespace ChatChainCommon.Config.IdentityServer +{ + public class IdentityConfig + { + + //API that are allowed to access the Auth server + public static IEnumerable GetApis() + { + return new List + { + new ApiResource("ChatChain", "ChatChain API"), + new ApiResource("ChatChainAPI", "ChatChain WebApp API") + }; + } + + // scopes define the resources in your system + public static IEnumerable GetIdentityResources() + { + return new List + { + new IdentityResources.OpenId(), + new IdentityResources.Profile() + }; + } + + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/IdentityServer/IdentityServerOptions.cs b/ChatChainCommon/Config/IdentityServer/IdentityServerOptions.cs new file mode 100644 index 0000000..eaeea20 --- /dev/null +++ b/ChatChainCommon/Config/IdentityServer/IdentityServerOptions.cs @@ -0,0 +1,10 @@ +namespace ChatChainCommon.Config.IdentityServer +{ + public class IdentityServerOptions + { + public string ServerUrl { get; set; } + public string ServerOrigin { get; set; } + public string SigningPath { get; set; } + public string SigningPassword { get; set; } + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/IdentityServerConnection.cs b/ChatChainCommon/Config/IdentityServerConnection.cs new file mode 100644 index 0000000..4abea5d --- /dev/null +++ b/ChatChainCommon/Config/IdentityServerConnection.cs @@ -0,0 +1,13 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global + +namespace ChatChainCommon.Config +{ + public class IdentityServerConnection + { + public string ServerUrl { get; set; } + + public string ClientId { get; set; } + + public string ClientSecret { get; set; } + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/MongoConnections.cs b/ChatChainCommon/Config/MongoConnections.cs new file mode 100644 index 0000000..11d35e1 --- /dev/null +++ b/ChatChainCommon/Config/MongoConnections.cs @@ -0,0 +1,10 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace ChatChainCommon.Config +{ + public class MongoConnections + { + public MongoOptions IdentityConnection { get; set; } + public MongoOptions IdentityServerConnection { get; set; } + public MongoOptions ChatChainGroups { get; set; } + } +} \ No newline at end of file diff --git a/ChatChainCommon/Config/MongoOptions.cs b/ChatChainCommon/Config/MongoOptions.cs new file mode 100644 index 0000000..1d8de17 --- /dev/null +++ b/ChatChainCommon/Config/MongoOptions.cs @@ -0,0 +1,9 @@ +namespace ChatChainCommon.Config +{ + public class MongoOptions + { + public string ConnectionString { get; set; } + + public string DatabaseName { get; set; } = "ChatChain"; + } +} \ No newline at end of file diff --git a/ChatChainCommon/IdentityServerRepository/IRepository.cs b/ChatChainCommon/IdentityServerRepository/IRepository.cs new file mode 100644 index 0000000..0cf96ac --- /dev/null +++ b/ChatChainCommon/IdentityServerRepository/IRepository.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace ChatChainCommon.IdentityServerRepository +{ + /// + /// Basic interface with a few methods for adding, deleting, and querying data. + /// + public interface IRepository + { + IQueryable All() where T : class, new(); + IQueryable Where(Expression> expression) where T : class, new(); + T Single(Expression> expression) where T : class, new(); + void Delete(Expression> expression) where T : class, new(); + void Add(T item) where T : class, new(); + void Add(IEnumerable items) where T : class, new(); + bool CollectionExists() where T : class, new(); + void Update(Expression> expression, T item) where T : class, new(); + } +} \ No newline at end of file diff --git a/ChatChainCommon/IdentityServerRepository/MongoRepository.cs b/ChatChainCommon/IdentityServerRepository/MongoRepository.cs new file mode 100644 index 0000000..4110417 --- /dev/null +++ b/ChatChainCommon/IdentityServerRepository/MongoRepository.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using ChatChainCommon.Config; +using MongoDB.Bson; +using MongoDB.Driver; + +namespace ChatChainCommon.IdentityServerRepository +{ + public class MongoRepository : IRepository + { + private static IMongoDatabase _database; + + /// + /// This Contructor leverages .NET Core built-in DI + /// + public MongoRepository(MongoConnections mongoConnections) + { + IMongoClient client = new MongoClient(mongoConnections.IdentityServerConnection.ConnectionString); + _database = client.GetDatabase(mongoConnections.IdentityServerConnection.DatabaseName); + } + + public IQueryable All() where T : class, new() + { + return _database.GetCollection(typeof(T).Name).AsQueryable(); + } + + public IQueryable Where(Expression> expression) where T : class, new() + { + return All().Where(expression); + } + + public void Delete(Expression> predicate) where T : class, new() + { + _database.GetCollection(typeof(T).Name).DeleteMany(predicate); + } + public T Single(Expression> expression) where T : class, new() + { + return All().Where(expression).SingleOrDefault(); + } + + public bool CollectionExists() where T : class, new() + { + IMongoCollection collection = _database.GetCollection(typeof(T).Name); + BsonDocument filter = new BsonDocument(); + long totalCount = collection.CountDocuments(filter); + return totalCount > 0; + } + + public void Update(Expression> expression, T item) where T : class, new() + { + _database.GetCollection(typeof(T).Name).ReplaceOne(expression, item); + } + + public void Add(T item) where T : class, new() + { + _database.GetCollection(typeof(T).Name).InsertOne(item); + } + + public void Add(IEnumerable items) where T : class, new() + { + _database.GetCollection(typeof(T).Name).InsertMany(items); + } + } +} \ No newline at end of file diff --git a/ChatChainCommon/IdentityServerStore/CustomClientStore.cs b/ChatChainCommon/IdentityServerStore/CustomClientStore.cs new file mode 100644 index 0000000..2b90161 --- /dev/null +++ b/ChatChainCommon/IdentityServerStore/CustomClientStore.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ChatChainCommon.IdentityServerRepository; +using IdentityServer4.Models; +using IdentityServer4.Stores; + +namespace ChatChainCommon.IdentityServerStore +{ + public class CustomClientStore : ICustomClientStore, IClientStore + { + private readonly IRepository _dbRepository; + private readonly IEnumerable _clients; + + public CustomClientStore(IRepository repository, IEnumerable clients = null) + { + _dbRepository = repository; + _clients = clients; + } + + public Task FindClientByIdAsync(string clientId) + { + IEnumerable query = + from lClient in _clients + where lClient.ClientId == clientId + select lClient; + + Client client = query.SingleOrDefault() ?? _dbRepository.Single(c => c.ClientId == clientId); + + return Task.FromResult(client); + } + + public void RemoveClient(string clientId) + { + _dbRepository.Delete(c => c.ClientId == clientId); + } + + public void AddClient(Client client) + { + _dbRepository.Add(client); + } + } +} \ No newline at end of file diff --git a/ChatChainCommon/IdentityServerStore/CustomPersistedGrantStore.cs b/ChatChainCommon/IdentityServerStore/CustomPersistedGrantStore.cs new file mode 100644 index 0000000..23fd063 --- /dev/null +++ b/ChatChainCommon/IdentityServerStore/CustomPersistedGrantStore.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ChatChainCommon.IdentityServerRepository; +using IdentityServer4.Models; +using IdentityServer4.Stores; + +namespace ChatChainCommon.IdentityServerStore +{ + public class CustomPersistedGrantStore: IPersistedGrantStore + { + private readonly IRepository _dbRepository; + + public CustomPersistedGrantStore(IRepository repository) + { + _dbRepository = repository; + } + + public Task> GetAllAsync(string subjectId) + { + IQueryable result = _dbRepository.Where(i => i.SubjectId == subjectId); + return Task.FromResult(result.AsEnumerable()); + } + + public Task GetAsync(string key) + { + PersistedGrant result = _dbRepository.Single(i => i.Key == key); + return Task.FromResult(result); + } + + public Task RemoveAllAsync(string subjectId, string clientId) + { + _dbRepository.Delete(i => i.SubjectId == subjectId && i.ClientId == clientId); + return Task.FromResult(0); + } + + public Task RemoveAllAsync(string subjectId, string clientId, string type) + { + _dbRepository.Delete(i => i.SubjectId == subjectId && i.ClientId == clientId && i.Type == type); + return Task.FromResult(0); + } + + public Task RemoveAsync(string key) + { + _dbRepository.Delete(i => i.Key == key); + return Task.FromResult(0); + } + + public Task StoreAsync(PersistedGrant grant) + { + _dbRepository.Add(grant); + return Task.FromResult(0); + } + + } +} \ No newline at end of file diff --git a/ChatChainCommon/IdentityServerStore/CustomResourceStore.cs b/ChatChainCommon/IdentityServerStore/CustomResourceStore.cs new file mode 100644 index 0000000..d28e8a4 --- /dev/null +++ b/ChatChainCommon/IdentityServerStore/CustomResourceStore.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using ChatChainCommon.IdentityServerRepository; +using IdentityServer4.Models; +using IdentityServer4.Stores; + +namespace ChatChainCommon.IdentityServerStore +{ + public class CustomResourceStore : IResourceStore + { + private readonly IRepository _dbRepository; + + public CustomResourceStore(IRepository repository) + { + _dbRepository = repository; + } + + private IEnumerable GetAllApiResources() + { + return _dbRepository.All(); + } + + private IEnumerable GetAllIdentityResources() + { + return _dbRepository.All(); + } + + public Task FindApiResourceAsync(string name) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); + + return Task.FromResult(_dbRepository.Single(a => a.Name == name)); + } + + public Task> FindApiResourcesByScopeAsync(IEnumerable scopeNames) + { + IQueryable list = _dbRepository.Where(a => a.Scopes.Any(s => scopeNames.Contains(s.Name))); + + return Task.FromResult(list.AsEnumerable()); + } + + public Task> FindIdentityResourcesByScopeAsync(IEnumerable scopeNames) + { + IQueryable list = _dbRepository.Where(e => scopeNames.Contains(e.Name)); + + return Task.FromResult(list.AsEnumerable()); + } + + public Task GetAllResourcesAsync() + { + Resources result = new Resources(GetAllIdentityResources(), GetAllApiResources()); + return Task.FromResult(result); + } + + private Func BuildPredicate(Func predicate) + { + return predicate; + } + } +} \ No newline at end of file diff --git a/ChatChainCommon/IdentityServerStore/ICustomClientStore.cs b/ChatChainCommon/IdentityServerStore/ICustomClientStore.cs new file mode 100644 index 0000000..0bc7a0d --- /dev/null +++ b/ChatChainCommon/IdentityServerStore/ICustomClientStore.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; +using IdentityServer4.Models; + +namespace ChatChainCommon.IdentityServerStore +{ + public interface ICustomClientStore + { + Task FindClientByIdAsync(string clientId); + void RemoveClient(string clientId); + void AddClient(Client client); + } +} \ No newline at end of file diff --git a/ChatChainServer.sln b/ChatChainServer.sln new file mode 100644 index 0000000..c634fc6 --- /dev/null +++ b/ChatChainServer.sln @@ -0,0 +1,175 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityServer", "IdentityServer\IdentityServer.csproj", "{9EDFF919-735A-4056-8EAB-FCDBA059035C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApp", "WebApp\WebApp.csproj", "{57FCBDA3-818C-4919-B38B-B876A392E195}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatChainCommon", "ChatChainCommon\ChatChainCommon.csproj", "{625CB9AB-A2C2-4116-B3FE-36B783687043}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api", "Api\Api.csproj", "{1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiSolution", "ApiSolution", "{3352A1A1-D1DB-4F2D-88F5-0FAB97B59B7A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Tests", "Core.Tests\Core.Tests.csproj", "{E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Core", "Core.Core\Core.Core.csproj", "{76A49E50-08D6-4D95-8C0F-E667E7C08CD6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Infrastructure", "Core.Infrastructure\Core.Infrastructure.csproj", "{9B8A995C-972D-4210-8097-66BD4563F9AC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HubSolution", "HubSolution", "{B768C083-5E5D-4E4F-95D1-F156DB6E6D23}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hub", "Hub\Hub.csproj", "{A81BDE9B-EACA-4132-A9E7-7730AE9C093B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{70CA39A2-CB91-4034-A8E9-8586E8B274E9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hub.Core", "Hub.Core\Hub.Core.csproj", "{20E30831-6D2D-4428-871E-73B4F0FFDCF7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hub.Infrastructure", "Hub.Infrastructure\Hub.Infrastructure.csproj", "{3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Debug|x64.ActiveCfg = Debug|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Debug|x64.Build.0 = Debug|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Debug|x86.ActiveCfg = Debug|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Debug|x86.Build.0 = Debug|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Release|Any CPU.Build.0 = Release|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Release|x64.ActiveCfg = Release|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Release|x64.Build.0 = Release|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Release|x86.ActiveCfg = Release|Any CPU + {9EDFF919-735A-4056-8EAB-FCDBA059035C}.Release|x86.Build.0 = Release|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Debug|x64.ActiveCfg = Debug|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Debug|x64.Build.0 = Debug|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Debug|x86.ActiveCfg = Debug|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Debug|x86.Build.0 = Debug|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Release|Any CPU.Build.0 = Release|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Release|x64.ActiveCfg = Release|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Release|x64.Build.0 = Release|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Release|x86.ActiveCfg = Release|Any CPU + {57FCBDA3-818C-4919-B38B-B876A392E195}.Release|x86.Build.0 = Release|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Debug|Any CPU.Build.0 = Debug|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Debug|x64.ActiveCfg = Debug|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Debug|x64.Build.0 = Debug|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Debug|x86.ActiveCfg = Debug|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Debug|x86.Build.0 = Debug|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Release|Any CPU.ActiveCfg = Release|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Release|Any CPU.Build.0 = Release|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Release|x64.ActiveCfg = Release|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Release|x64.Build.0 = Release|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Release|x86.ActiveCfg = Release|Any CPU + {625CB9AB-A2C2-4116-B3FE-36B783687043}.Release|x86.Build.0 = Release|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Debug|x64.Build.0 = Debug|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Debug|x86.Build.0 = Debug|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Release|Any CPU.Build.0 = Release|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Release|x64.ActiveCfg = Release|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Release|x64.Build.0 = Release|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Release|x86.ActiveCfg = Release|Any CPU + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4}.Release|x86.Build.0 = Release|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Debug|x64.ActiveCfg = Debug|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Debug|x64.Build.0 = Debug|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Debug|x86.ActiveCfg = Debug|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Debug|x86.Build.0 = Debug|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Release|Any CPU.Build.0 = Release|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Release|x64.ActiveCfg = Release|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Release|x64.Build.0 = Release|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Release|x86.ActiveCfg = Release|Any CPU + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF}.Release|x86.Build.0 = Release|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Debug|x64.ActiveCfg = Debug|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Debug|x64.Build.0 = Debug|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Debug|x86.ActiveCfg = Debug|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Debug|x86.Build.0 = Debug|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Release|Any CPU.Build.0 = Release|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Release|x64.ActiveCfg = Release|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Release|x64.Build.0 = Release|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Release|x86.ActiveCfg = Release|Any CPU + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6}.Release|x86.Build.0 = Release|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Debug|x64.Build.0 = Debug|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Debug|x86.ActiveCfg = Debug|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Debug|x86.Build.0 = Debug|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Release|Any CPU.Build.0 = Release|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Release|x64.ActiveCfg = Release|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Release|x64.Build.0 = Release|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Release|x86.ActiveCfg = Release|Any CPU + {9B8A995C-972D-4210-8097-66BD4563F9AC}.Release|x86.Build.0 = Release|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Debug|x64.ActiveCfg = Debug|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Debug|x64.Build.0 = Debug|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Debug|x86.ActiveCfg = Debug|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Debug|x86.Build.0 = Debug|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Release|Any CPU.Build.0 = Release|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Release|x64.ActiveCfg = Release|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Release|x64.Build.0 = Release|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Release|x86.ActiveCfg = Release|Any CPU + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B}.Release|x86.Build.0 = Release|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Debug|x64.ActiveCfg = Debug|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Debug|x64.Build.0 = Debug|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Debug|x86.ActiveCfg = Debug|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Debug|x86.Build.0 = Debug|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Release|Any CPU.Build.0 = Release|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Release|x64.ActiveCfg = Release|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Release|x64.Build.0 = Release|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Release|x86.ActiveCfg = Release|Any CPU + {20E30831-6D2D-4428-871E-73B4F0FFDCF7}.Release|x86.Build.0 = Release|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Debug|x64.Build.0 = Debug|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Debug|x86.ActiveCfg = Debug|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Debug|x86.Build.0 = Debug|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Release|Any CPU.Build.0 = Release|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Release|x64.ActiveCfg = Release|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Release|x64.Build.0 = Release|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Release|x86.ActiveCfg = Release|Any CPU + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1EDD0F7F-0095-4428-BFDF-7A4EF5ADF6A4} = {3352A1A1-D1DB-4F2D-88F5-0FAB97B59B7A} + {A81BDE9B-EACA-4132-A9E7-7730AE9C093B} = {B768C083-5E5D-4E4F-95D1-F156DB6E6D23} + {76A49E50-08D6-4D95-8C0F-E667E7C08CD6} = {70CA39A2-CB91-4034-A8E9-8586E8B274E9} + {9B8A995C-972D-4210-8097-66BD4563F9AC} = {70CA39A2-CB91-4034-A8E9-8586E8B274E9} + {E380A4AB-8E67-4B3E-A087-EA684CF1ADAF} = {70CA39A2-CB91-4034-A8E9-8586E8B274E9} + {20E30831-6D2D-4428-871E-73B4F0FFDCF7} = {B768C083-5E5D-4E4F-95D1-F156DB6E6D23} + {3CB5DDA1-BDF7-4991-9C47-22C4DBE718D9} = {B768C083-5E5D-4E4F-95D1-F156DB6E6D23} + EndGlobalSection +EndGlobal diff --git a/ChatChainServer.sln.DotSettings b/ChatChainServer.sln.DotSettings new file mode 100644 index 0000000..fba49d1 --- /dev/null +++ b/ChatChainServer.sln.DotSettings @@ -0,0 +1,4 @@ + + API + IS + True \ No newline at end of file diff --git a/Core.Core/Core.Core.csproj b/Core.Core/Core.Core.csproj new file mode 100644 index 0000000..4577a3a --- /dev/null +++ b/Core.Core/Core.Core.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp2.2 + Api.Core + + + + + + + diff --git a/Core.Core/CoreModule.cs b/Core.Core/CoreModule.cs new file mode 100644 index 0000000..4569daa --- /dev/null +++ b/Core.Core/CoreModule.cs @@ -0,0 +1,65 @@ +using Api.Core.Interfaces.UseCases.Client; +using Api.Core.Interfaces.UseCases.ClientConfig; +using Api.Core.Interfaces.UseCases.Group; +using Api.Core.Interfaces.UseCases.Invite; +using Api.Core.Interfaces.UseCases.Organisation; +using Api.Core.Interfaces.UseCases.OrganisationUser; +using Api.Core.UseCases.Client; +using Api.Core.UseCases.ClientConfig; +using Api.Core.UseCases.Group; +using Api.Core.UseCases.Invite; +using Api.Core.UseCases.Organisation; +using Api.Core.UseCases.OrganisationUser; +using Autofac; + +namespace Api.Core +{ + public class CoreModule : Module + { + protected override void Load(ContainerBuilder builder) + { + // Client + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + // Client Config + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + + // Group + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + // Invite + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + // Organisation + builder.RegisterType().As() + .InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + + // Organisation User + builder.RegisterType().As() + .InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + builder.RegisterType().As() + .InstancePerLifetimeScope(); + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/Error.cs b/Core.Core/DTO/Error.cs new file mode 100644 index 0000000..396eff6 --- /dev/null +++ b/Core.Core/DTO/Error.cs @@ -0,0 +1,14 @@ +namespace Api.Core.DTO +{ + public class Error + { + public string Code { get; } + public string Description { get; } + + public Error(string code, string description) + { + Code = code; + Description = description; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/BaseGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/BaseGatewayResponse.cs new file mode 100644 index 0000000..542ecef --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/BaseGatewayResponse.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses +{ + public class BaseGatewayResponse + { + public bool Success { get; } + public IEnumerable Errors { get; } + + public BaseGatewayResponse(bool success = false, IEnumerable errors = null) + { + Success = success; + Errors = errors; + if (errors == null) + Errors = new List(); + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Client/CreateClientGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Client/CreateClientGatewayResponse.cs new file mode 100644 index 0000000..95ff2fa --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Client/CreateClientGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Client +{ + public class CreateClientGatewayResponse : BaseGatewayResponse + { + public Entities.Client Client { get; } + + public CreateClientGatewayResponse(Entities.Client client, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Client = client; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Client/GetClientGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Client/GetClientGatewayResponse.cs new file mode 100644 index 0000000..ef53737 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Client/GetClientGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Client +{ + public class GetClientGatewayResponse : BaseGatewayResponse + { + public Entities.Client Client { get; } + + public GetClientGatewayResponse(Entities.Client client, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Client = client; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Client/GetClientsGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Client/GetClientsGatewayResponse.cs new file mode 100644 index 0000000..03f2ebb --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Client/GetClientsGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Client +{ + public class GetClientsGatewayResponse : BaseGatewayResponse + { + public IEnumerable Clients { get; } + + public GetClientsGatewayResponse(IEnumerable clients, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Clients = clients; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Client/UpdateClientGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Client/UpdateClientGatewayResponse.cs new file mode 100644 index 0000000..dee4d8c --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Client/UpdateClientGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Client +{ + public class UpdateClientGatewayResponse : BaseGatewayResponse + { + public Entities.Client Client { get; } + + public UpdateClientGatewayResponse(Entities.Client client, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Client = client; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/CreateClientConfigGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/CreateClientConfigGatewayResponse.cs new file mode 100644 index 0000000..aa47c6f --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/CreateClientConfigGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.ClientConfig +{ + public class CreateClientConfigGatewayResponse : BaseGatewayResponse + { + public Entities.ClientConfig ClientConfig { get; } + + public CreateClientConfigGatewayResponse(Entities.ClientConfig clientConfig, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + ClientConfig = clientConfig; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/GetClientConfigGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/GetClientConfigGatewayResponse.cs new file mode 100644 index 0000000..e2c99f5 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/GetClientConfigGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.ClientConfig +{ + public class GetClientConfigGatewayResponse : BaseGatewayResponse + { + public Entities.ClientConfig ClientConfig { get; } + + public GetClientConfigGatewayResponse(Entities.ClientConfig clientConfig, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + ClientConfig = clientConfig; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/UpdateClientConfigGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/UpdateClientConfigGatewayResponse.cs new file mode 100644 index 0000000..e980c14 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/ClientConfig/UpdateClientConfigGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.ClientConfig +{ + public class UpdateClientConfigGatewayResponse : BaseGatewayResponse + { + public Entities.ClientConfig ClientConfig { get; } + + public UpdateClientConfigGatewayResponse(Entities.ClientConfig clientConfig, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + ClientConfig = clientConfig; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Group/CreateGroupGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Group/CreateGroupGatewayResponse.cs new file mode 100644 index 0000000..01f7150 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Group/CreateGroupGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Group +{ + public class CreateGroupGatewayResponse : BaseGatewayResponse + { + public Entities.Group Group { get; } + + public CreateGroupGatewayResponse(Entities.Group group, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Group = group; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Group/GetGroupGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Group/GetGroupGatewayResponse.cs new file mode 100644 index 0000000..818ea64 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Group/GetGroupGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Group +{ + public class GetGroupGatewayResponse : BaseGatewayResponse + { + public Entities.Group Group { get; } + + public GetGroupGatewayResponse(Entities.Group group, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Group = group; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Group/GetGroupsGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Group/GetGroupsGatewayResponse.cs new file mode 100644 index 0000000..1e3f6cc --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Group/GetGroupsGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Group +{ + public class GetGroupsGatewayResponse : BaseGatewayResponse + { + public IEnumerable Groups { get; } + + public GetGroupsGatewayResponse(IEnumerable groups, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Groups = groups; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Group/UpdateGroupGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Group/UpdateGroupGatewayResponse.cs new file mode 100644 index 0000000..132a9dc --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Group/UpdateGroupGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Group +{ + public class UpdateGroupGatewayResponse : BaseGatewayResponse + { + public Entities.Group Group { get; } + + public UpdateGroupGatewayResponse(Entities.Group group, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Group = group; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/IS4Client/CreateIS4ClientGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/IS4Client/CreateIS4ClientGatewayResponse.cs new file mode 100644 index 0000000..9e3d0fb --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/IS4Client/CreateIS4ClientGatewayResponse.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.IS4Client +{ + public class CreateIS4ClientGatewayResponse : BaseGatewayResponse + { + public CreateIS4ClientGatewayResponse(bool success = false, + IEnumerable errors = null) : base(success, errors) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Invite/CreateInviteGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Invite/CreateInviteGatewayResponse.cs new file mode 100644 index 0000000..605a2ec --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Invite/CreateInviteGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Invite +{ + public class CreateInviteGatewayResponse : BaseGatewayResponse + { + public Entities.Invite Invite { get; } + + public CreateInviteGatewayResponse(Entities.Invite invite, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Invite = invite; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Invite/GetInviteGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Invite/GetInviteGatewayResponse.cs new file mode 100644 index 0000000..a1bc026 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Invite/GetInviteGatewayResponse.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Invite +{ + public class GetInviteGatewayResponse : BaseGatewayResponse + { + public Entities.Invite Invite { get; } + + public GetInviteGatewayResponse(Entities.Invite invite, bool success = false, IEnumerable errors = null) + : base(success, errors) + { + Invite = invite; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Organisation/CreateOrganisationGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/CreateOrganisationGatewayResponse.cs new file mode 100644 index 0000000..8015689 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/CreateOrganisationGatewayResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Api.Core.Entities; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Organisation +{ + public class CreateOrganisationGatewayResponse : BaseGatewayResponse + { + public OrganisationDetails Organisation { get; } + + public CreateOrganisationGatewayResponse(OrganisationDetails organisation, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Organisation = organisation; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Organisation/GetOrganisationGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/GetOrganisationGatewayResponse.cs new file mode 100644 index 0000000..2b1853f --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/GetOrganisationGatewayResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Api.Core.Entities; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Organisation +{ + public class GetOrganisationGatewayResponse : BaseGatewayResponse + { + public OrganisationDetails Organisation { get; } + + public GetOrganisationGatewayResponse(OrganisationDetails organisation, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Organisation = organisation; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Organisation/GetOrganisationsGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/GetOrganisationsGatewayResponse.cs new file mode 100644 index 0000000..892e295 --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/GetOrganisationsGatewayResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Api.Core.Entities; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Organisation +{ + public class GetOrganisationsGatewayResponse : BaseGatewayResponse + { + public IEnumerable Organisations { get; } + + public GetOrganisationsGatewayResponse(IEnumerable organisations, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Organisations = organisations; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/GatewayResponses/Repositories/Organisation/UpdateOrganisationGatewayResponse.cs b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/UpdateOrganisationGatewayResponse.cs new file mode 100644 index 0000000..3c8aa5a --- /dev/null +++ b/Core.Core/DTO/GatewayResponses/Repositories/Organisation/UpdateOrganisationGatewayResponse.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Api.Core.Entities; + +namespace Api.Core.DTO.GatewayResponses.Repositories.Organisation +{ + public class UpdateOrganisationGatewayResponse : BaseGatewayResponse + { + public OrganisationDetails Organisation { get; } + + public UpdateOrganisationGatewayResponse(OrganisationDetails organisation, bool success = false, + IEnumerable errors = null) : base(success, errors) + { + Organisation = organisation; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Client/CreateClientRequest.cs b/Core.Core/DTO/UseCaseRequests/Client/CreateClientRequest.cs new file mode 100644 index 0000000..026f469 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Client/CreateClientRequest.cs @@ -0,0 +1,25 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Client +{ + public class CreateClientRequest : DefaultUseCaseRequest + { + public string Name { get; } + + public string Description { get; } + + public CreateClientRequest(string userId, Guid organisationId, string name, string description) : base(userId, + organisationId) + { + Name = name; + Description = description; + } + + public CreateClientRequest(Guid organisationId, string name, string description) : base(organisationId) + { + Name = name; + Description = description; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Client/DeleteClientRequest.cs b/Core.Core/DTO/UseCaseRequests/Client/DeleteClientRequest.cs new file mode 100644 index 0000000..a4a4f35 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Client/DeleteClientRequest.cs @@ -0,0 +1,20 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Client +{ + public class DeleteClientRequest : DefaultUseCaseRequest + { + public Guid ClientId { get; } + + public DeleteClientRequest(string userId, Guid organisationId, Guid clientId) : base(userId, organisationId) + { + ClientId = clientId; + } + + public DeleteClientRequest(Guid organisationId, Guid clientId) : base(organisationId) + { + ClientId = clientId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Client/GetClientRequest.cs b/Core.Core/DTO/UseCaseRequests/Client/GetClientRequest.cs new file mode 100644 index 0000000..6c84052 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Client/GetClientRequest.cs @@ -0,0 +1,20 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Client +{ + public class GetClientRequest : DefaultUseCaseRequest + { + public Guid ClientId { get; } + + public GetClientRequest(string userId, Guid organisationId, Guid clientId) : base(userId, organisationId) + { + ClientId = clientId; + } + + public GetClientRequest(Guid organisationId, Guid clientId) : base(organisationId) + { + ClientId = clientId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Client/GetClientsRequest.cs b/Core.Core/DTO/UseCaseRequests/Client/GetClientsRequest.cs new file mode 100644 index 0000000..2db176f --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Client/GetClientsRequest.cs @@ -0,0 +1,16 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Client +{ + public class GetClientsRequest : DefaultUseCaseRequest + { + public GetClientsRequest(string userId, Guid organisationId) : base(userId, organisationId) + { + } + + public GetClientsRequest(Guid organisationId) : base(organisationId) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Client/UpdateClientRequest.cs b/Core.Core/DTO/UseCaseRequests/Client/UpdateClientRequest.cs new file mode 100644 index 0000000..2a2366d --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Client/UpdateClientRequest.cs @@ -0,0 +1,30 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Client +{ + public class UpdateClientRequest : DefaultUseCaseRequest + { + public Guid ClientId { get; } + + public string Name { get; } + + public string Description { get; } + + public UpdateClientRequest(string userId, Guid organisationId, Guid clientId, string name, string description) : + base(userId, organisationId) + { + ClientId = clientId; + Name = name; + Description = description; + } + + public UpdateClientRequest(Guid organisationId, Guid clientId, string name, string description) : base( + organisationId) + { + ClientId = clientId; + Name = name; + Description = description; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/ClientConfig/GetClientConfigRequest.cs b/Core.Core/DTO/UseCaseRequests/ClientConfig/GetClientConfigRequest.cs new file mode 100644 index 0000000..c707d71 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/ClientConfig/GetClientConfigRequest.cs @@ -0,0 +1,21 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.ClientConfig +{ + public class GetClientConfigRequest : DefaultUseCaseRequest + { + public Guid ClientConfigId { get; } + + public GetClientConfigRequest(string userId, Guid organisationId, Guid clientConfigId) : base(userId, + organisationId) + { + ClientConfigId = clientConfigId; + } + + public GetClientConfigRequest(Guid organisationId, Guid clientConfigId) : base(organisationId) + { + ClientConfigId = clientConfigId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/ClientConfig/UpdateClientConfigRequest.cs b/Core.Core/DTO/UseCaseRequests/ClientConfig/UpdateClientConfigRequest.cs new file mode 100644 index 0000000..c3df70e --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/ClientConfig/UpdateClientConfigRequest.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.ClientConfig +{ + public class UpdateClientConfigRequest : DefaultUseCaseRequest + { + public Guid ClientConfigId { get; } + + public IEnumerable ClientEventGroups { get; } + + public IEnumerable UserEventGroups { get; } + + public UpdateClientConfigRequest(string userId, Guid organisationId, Guid clientConfigId, + IEnumerable clientEventGroups, IEnumerable userEventGroups) : base(userId, organisationId) + { + ClientConfigId = clientConfigId; + ClientEventGroups = clientEventGroups; + UserEventGroups = userEventGroups; + } + + public UpdateClientConfigRequest(Guid organisationId, Guid clientConfigId, IEnumerable clientEventGroups, + IEnumerable userEventGroups) : base(organisationId) + { + ClientConfigId = clientConfigId; + ClientEventGroups = clientEventGroups; + UserEventGroups = userEventGroups; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Group/CreateGroupRequest.cs b/Core.Core/DTO/UseCaseRequests/Group/CreateGroupRequest.cs new file mode 100644 index 0000000..8fe2e41 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Group/CreateGroupRequest.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Group +{ + public class CreateGroupRequest : DefaultUseCaseRequest + { + public string Name { get; } + + public string Description { get; } + + public IList ClientIds { get; } + + public CreateGroupRequest(string userId, Guid organisationId, string name, string description, + IList clientIds) : base(userId, organisationId) + { + Name = name; + Description = description; + ClientIds = clientIds; + } + + public CreateGroupRequest(Guid organisationId, string name, string description, IList clientIds) : base( + organisationId) + { + Name = name; + Description = description; + ClientIds = clientIds; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Group/DeleteGroupRequest.cs b/Core.Core/DTO/UseCaseRequests/Group/DeleteGroupRequest.cs new file mode 100644 index 0000000..5231458 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Group/DeleteGroupRequest.cs @@ -0,0 +1,20 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Group +{ + public class DeleteGroupRequest : DefaultUseCaseRequest + { + public Guid GroupId { get; } + + public DeleteGroupRequest(string userId, Guid organisationId, Guid groupId) : base(userId, organisationId) + { + GroupId = groupId; + } + + public DeleteGroupRequest(Guid organisationId, Guid groupId) : base(organisationId) + { + GroupId = groupId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Group/GetGroupRequest.cs b/Core.Core/DTO/UseCaseRequests/Group/GetGroupRequest.cs new file mode 100644 index 0000000..ff0f212 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Group/GetGroupRequest.cs @@ -0,0 +1,20 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Group +{ + public class GetGroupRequest : DefaultUseCaseRequest + { + public Guid GroupId { get; } + + public GetGroupRequest(string userId, Guid organisationId, Guid groupId) : base(userId, organisationId) + { + GroupId = groupId; + } + + public GetGroupRequest(Guid organisationId, Guid groupId) : base(organisationId) + { + GroupId = groupId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Group/GetGroupsRequest.cs b/Core.Core/DTO/UseCaseRequests/Group/GetGroupsRequest.cs new file mode 100644 index 0000000..21c6820 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Group/GetGroupsRequest.cs @@ -0,0 +1,16 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Group +{ + public class GetGroupsRequest : DefaultUseCaseRequest + { + public GetGroupsRequest(string userId, Guid organisationId) : base(userId, organisationId) + { + } + + public GetGroupsRequest(Guid organisationId) : base(organisationId) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Group/UpdateGroupRequest.cs b/Core.Core/DTO/UseCaseRequests/Group/UpdateGroupRequest.cs new file mode 100644 index 0000000..1cea7cf --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Group/UpdateGroupRequest.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Group +{ + public class UpdateGroupRequest : DefaultUseCaseRequest + { + public Guid GroupId { get; } + + public string Name { get; } + + public string Description { get; } + + public IList ClientIds { get; } + + public UpdateGroupRequest(string userId, Guid organisationId, Guid groupId, string name, string description, + IList clientIds) : base(userId, organisationId) + { + GroupId = groupId; + Name = name; + Description = description; + ClientIds = clientIds; + } + + public UpdateGroupRequest(Guid organisationId, Guid groupId, string name, string description, + IList clientIds) : base(organisationId) + { + GroupId = groupId; + Name = name; + Description = description; + ClientIds = clientIds; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Invite/CreateInviteRequest.cs b/Core.Core/DTO/UseCaseRequests/Invite/CreateInviteRequest.cs new file mode 100644 index 0000000..8210cf9 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Invite/CreateInviteRequest.cs @@ -0,0 +1,21 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Invite +{ + public class CreateInviteRequest : DefaultUseCaseRequest + { + public string EmailAddress { get; } + + public CreateInviteRequest(string userId, Guid organisationId, string emailAddress) : base(userId, + organisationId) + { + EmailAddress = emailAddress; + } + + public CreateInviteRequest(Guid organisationId, string emailAddress) : base(organisationId) + { + EmailAddress = emailAddress; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Invite/UseInviteRequest.cs b/Core.Core/DTO/UseCaseRequests/Invite/UseInviteRequest.cs new file mode 100644 index 0000000..5fb9e32 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Invite/UseInviteRequest.cs @@ -0,0 +1,25 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Invite +{ + public class UseInviteRequest : DefaultUseCaseRequest + { + public string UserEmailAddress { get; } + + public string Token { get; } + + public UseInviteRequest(string userId, Guid organisationId, string userEmailAddress, string token) : base( + userId, organisationId) + { + UserEmailAddress = userEmailAddress; + Token = token; + } + + public UseInviteRequest(Guid organisationId, string userEmailAddress, string token) : base(organisationId) + { + UserEmailAddress = userEmailAddress; + Token = token; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Organisation/CreateOrganisationRequest.cs b/Core.Core/DTO/UseCaseRequests/Organisation/CreateOrganisationRequest.cs new file mode 100644 index 0000000..b1fe393 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Organisation/CreateOrganisationRequest.cs @@ -0,0 +1,23 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Organisation +{ + public class CreateOrganisationRequest : UseCaseRequest + { + public string UserId { get; } + + public string Name { get; } + + public CreateOrganisationRequest(string userId, string name) + { + UserId = userId ?? throw new InvalidOperationException(); + Name = name; + } + + public CreateOrganisationRequest(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Organisation/DeleteOrganisationRequest.cs b/Core.Core/DTO/UseCaseRequests/Organisation/DeleteOrganisationRequest.cs new file mode 100644 index 0000000..fe8bc7f --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Organisation/DeleteOrganisationRequest.cs @@ -0,0 +1,23 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Organisation +{ + public class DeleteOrganisationRequest : UseCaseRequest + { + public string UserId { get; } + + public Guid Id { get; } + + public DeleteOrganisationRequest(string userId, Guid id) + { + UserId = userId ?? throw new InvalidOperationException(); + Id = id; + } + + public DeleteOrganisationRequest(Guid id) + { + Id = id; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Organisation/GetOrganisationRequest.cs b/Core.Core/DTO/UseCaseRequests/Organisation/GetOrganisationRequest.cs new file mode 100644 index 0000000..ff9b1e3 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Organisation/GetOrganisationRequest.cs @@ -0,0 +1,16 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Organisation +{ + public class GetOrganisationRequest : DefaultUseCaseRequest + { + public GetOrganisationRequest(string userId, Guid organisationId) : base(userId, organisationId) + { + } + + public GetOrganisationRequest(Guid organisationId) : base(organisationId) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Organisation/GetOrganisationsRequest.cs b/Core.Core/DTO/UseCaseRequests/Organisation/GetOrganisationsRequest.cs new file mode 100644 index 0000000..437c514 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Organisation/GetOrganisationsRequest.cs @@ -0,0 +1,19 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Organisation +{ + public class GetOrganisationsRequest : UseCaseRequest + { + public string UserId { get; } + + public GetOrganisationsRequest(string userId) + { + UserId = userId ?? throw new InvalidOperationException(); + } + + public GetOrganisationsRequest() + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/Organisation/UpdateOrganisationRequest.cs b/Core.Core/DTO/UseCaseRequests/Organisation/UpdateOrganisationRequest.cs new file mode 100644 index 0000000..4d30b42 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/Organisation/UpdateOrganisationRequest.cs @@ -0,0 +1,20 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.Organisation +{ + public class UpdateOrganisationRequest : DefaultUseCaseRequest + { + public string Name { get; } + + public UpdateOrganisationRequest(string userId, Guid organisationId, string name) : base(userId, organisationId) + { + Name = name; + } + + public UpdateOrganisationRequest(Guid organisationId, string name) : base(organisationId) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/OrganisationUser/DeleteOrganisationUserRequest.cs b/Core.Core/DTO/UseCaseRequests/OrganisationUser/DeleteOrganisationUserRequest.cs new file mode 100644 index 0000000..48943ef --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/OrganisationUser/DeleteOrganisationUserRequest.cs @@ -0,0 +1,21 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.OrganisationUser +{ + public class DeleteOrganisationUserRequest : DefaultUseCaseRequest + { + public string OrganisationUserId { get; } + + public DeleteOrganisationUserRequest(string userId, Guid organisationId, string organisationUserId) : base( + userId, organisationId) + { + OrganisationUserId = organisationUserId; + } + + public DeleteOrganisationUserRequest(Guid organisationId, string organisationUserId) : base(organisationId) + { + OrganisationUserId = organisationUserId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/OrganisationUser/GetOrganisationUserRequest.cs b/Core.Core/DTO/UseCaseRequests/OrganisationUser/GetOrganisationUserRequest.cs new file mode 100644 index 0000000..ef672f2 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/OrganisationUser/GetOrganisationUserRequest.cs @@ -0,0 +1,21 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.OrganisationUser +{ + public class GetOrganisationUserRequest : DefaultUseCaseRequest + { + public string OrganisationUserId { get; } + + public GetOrganisationUserRequest(string userId, Guid organisationId, string organisationUserId) : base(userId, + organisationId) + { + OrganisationUserId = organisationUserId; + } + + public GetOrganisationUserRequest(Guid organisationId, string organisationUserId) : base(organisationId) + { + OrganisationUserId = organisationUserId; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/OrganisationUser/GetOrganisationUsersRequest.cs b/Core.Core/DTO/UseCaseRequests/OrganisationUser/GetOrganisationUsersRequest.cs new file mode 100644 index 0000000..d3f5394 --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/OrganisationUser/GetOrganisationUsersRequest.cs @@ -0,0 +1,16 @@ +using System; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.OrganisationUser +{ + public class GetOrganisationUsersRequest : DefaultUseCaseRequest + { + public GetOrganisationUsersRequest(string userId, Guid organisationId) : base(userId, organisationId) + { + } + + public GetOrganisationUsersRequest(Guid organisationId) : base(organisationId) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseRequests/OrganisationUser/UpdateOrganisationUserRequest.cs b/Core.Core/DTO/UseCaseRequests/OrganisationUser/UpdateOrganisationUserRequest.cs new file mode 100644 index 0000000..9fc992c --- /dev/null +++ b/Core.Core/DTO/UseCaseRequests/OrganisationUser/UpdateOrganisationUserRequest.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Api.Core.Entities; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseRequests.OrganisationUser +{ + public class UpdateOrganisationUserRequest : DefaultUseCaseRequest + { + public string OrganisationUserId { get; } + + public IList Permissions { get; } + + public UpdateOrganisationUserRequest(string userId, Guid organisationId, string organisationUserId, + IList permissions) : base(userId, organisationId) + { + OrganisationUserId = organisationUserId; + Permissions = permissions; + } + + public UpdateOrganisationUserRequest(Guid organisationId, string organisationUserId, + IList permissions) : base(organisationId) + { + OrganisationUserId = organisationUserId; + Permissions = permissions; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Client/CreateClientResponse.cs b/Core.Core/DTO/UseCaseResponses/Client/CreateClientResponse.cs new file mode 100644 index 0000000..a6dbe5e --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Client/CreateClientResponse.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Client +{ + public class CreateClientResponse : DefaultUseCaseResponseMessage + { + public Entities.Client Client { get; } + + public Entities.ClientConfig ClientConfig { get; } + + public string Password { get; } + + public CreateClientResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public CreateClientResponse(Entities.Client client, Entities.ClientConfig clientConfig, string password, + Entities.Organisation organisation, Entities.OrganisationUser user, bool checkedPermissions = false, + bool success = false, string message = null) : base(organisation, user, checkedPermissions, success, + message) + { + Client = client; + ClientConfig = clientConfig; + Password = password; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Client/DeleteClientResponse.cs b/Core.Core/DTO/UseCaseResponses/Client/DeleteClientResponse.cs new file mode 100644 index 0000000..1e9f654 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Client/DeleteClientResponse.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Client +{ + public class DeleteClientResponse : DefaultUseCaseResponseMessage + { + public DeleteClientResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public DeleteClientResponse(Entities.Organisation organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, string message = null) : base(organisation, user, + checkedPermissions, success, message) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Client/GetClientResponse.cs b/Core.Core/DTO/UseCaseResponses/Client/GetClientResponse.cs new file mode 100644 index 0000000..30e5ca5 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Client/GetClientResponse.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Client +{ + public class GetClientResponse : DefaultUseCaseResponseMessage + { + public Entities.Client Client { get; } + + public GetClientResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public GetClientResponse(Entities.Client client, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Client = client; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Client/GetClientsResponse.cs b/Core.Core/DTO/UseCaseResponses/Client/GetClientsResponse.cs new file mode 100644 index 0000000..9fba961 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Client/GetClientsResponse.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Client +{ + public class GetClientsResponse : DefaultUseCaseResponseMessage + { + public IEnumerable Clients { get; } + + public GetClientsResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public GetClientsResponse(IEnumerable clients, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Clients = clients; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Client/UpdateClientResponse.cs b/Core.Core/DTO/UseCaseResponses/Client/UpdateClientResponse.cs new file mode 100644 index 0000000..2625ff6 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Client/UpdateClientResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Client +{ + public class UpdateClientResponse : DefaultUseCaseResponseMessage + { + public Entities.Client Client { get; } + + public UpdateClientResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public UpdateClientResponse(Entities.Client client, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Client = client; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/ClientConfig/GetClientConfigResponse.cs b/Core.Core/DTO/UseCaseResponses/ClientConfig/GetClientConfigResponse.cs new file mode 100644 index 0000000..c62aa8f --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/ClientConfig/GetClientConfigResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.ClientConfig +{ + public class GetClientConfigResponse : DefaultUseCaseResponseMessage + { + public Entities.ClientConfig ClientConfig { get; } + + public GetClientConfigResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public GetClientConfigResponse(Entities.ClientConfig clientConfig, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + ClientConfig = clientConfig; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/ClientConfig/UpdateClientConfigResponse.cs b/Core.Core/DTO/UseCaseResponses/ClientConfig/UpdateClientConfigResponse.cs new file mode 100644 index 0000000..20d5b6c --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/ClientConfig/UpdateClientConfigResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.ClientConfig +{ + public class UpdateClientConfigResponse : DefaultUseCaseResponseMessage + { + public Entities.ClientConfig ClientConfig { get; } + + public UpdateClientConfigResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public UpdateClientConfigResponse(Entities.ClientConfig clientConfig, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + ClientConfig = clientConfig; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Group/CreateGroupResponse.cs b/Core.Core/DTO/UseCaseResponses/Group/CreateGroupResponse.cs new file mode 100644 index 0000000..4b30e09 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Group/CreateGroupResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Group +{ + public class CreateGroupResponse : DefaultUseCaseResponseMessage + { + public Entities.Group Group { get; } + + public CreateGroupResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public CreateGroupResponse(Entities.Group group, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Group = group; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Group/DeleteGroupResponse.cs b/Core.Core/DTO/UseCaseResponses/Group/DeleteGroupResponse.cs new file mode 100644 index 0000000..16c26e3 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Group/DeleteGroupResponse.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Group +{ + public class DeleteGroupResponse : DefaultUseCaseResponseMessage + { + public DeleteGroupResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public DeleteGroupResponse(Entities.Organisation organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, string message = null) : base(organisation, user, + checkedPermissions, success, message) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Group/GetGroupResponse.cs b/Core.Core/DTO/UseCaseResponses/Group/GetGroupResponse.cs new file mode 100644 index 0000000..9978e19 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Group/GetGroupResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Group +{ + public class GetGroupResponse : DefaultUseCaseResponseMessage + { + public Entities.Group Group { get; } + + public GetGroupResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public GetGroupResponse(Entities.Group group, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Group = group; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Group/GetGroupsResponse.cs b/Core.Core/DTO/UseCaseResponses/Group/GetGroupsResponse.cs new file mode 100644 index 0000000..ea2418b --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Group/GetGroupsResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Group +{ + public class GetGroupsResponse : DefaultUseCaseResponseMessage + { + public IEnumerable Groups { get; } + + public GetGroupsResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public GetGroupsResponse(IEnumerable groups, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Groups = groups; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Group/UpdateGroupResponse.cs b/Core.Core/DTO/UseCaseResponses/Group/UpdateGroupResponse.cs new file mode 100644 index 0000000..94b2ed8 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Group/UpdateGroupResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Group +{ + public class UpdateGroupResponse : DefaultUseCaseResponseMessage + { + public Entities.Group Group { get; } + + public UpdateGroupResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public UpdateGroupResponse(Entities.Group group, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Group = group; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Invite/CreateInviteResponse.cs b/Core.Core/DTO/UseCaseResponses/Invite/CreateInviteResponse.cs new file mode 100644 index 0000000..f9fa1ee --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Invite/CreateInviteResponse.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Invite +{ + public class CreateInviteResponse : DefaultUseCaseResponseMessage + { + public Entities.Invite Invite { get; } + + public CreateInviteResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public CreateInviteResponse(Entities.Invite invite, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + Invite = invite; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Invite/UseInviteResponse.cs b/Core.Core/DTO/UseCaseResponses/Invite/UseInviteResponse.cs new file mode 100644 index 0000000..75f205b --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Invite/UseInviteResponse.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Invite +{ + public class UseInviteResponse : DefaultUseCaseResponseMessage + { + public UseInviteResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public UseInviteResponse(Entities.Organisation organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, string message = null) : base(organisation, user, + checkedPermissions, success, message) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Organisation/CreateOrganisationResponse.cs b/Core.Core/DTO/UseCaseResponses/Organisation/CreateOrganisationResponse.cs new file mode 100644 index 0000000..723ca44 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Organisation/CreateOrganisationResponse.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Api.Core.Entities; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Organisation +{ + public class CreateOrganisationResponse : UseCaseResponseMessage + { + public OrganisationDetails Organisation { get; } + + public Entities.OrganisationUser User { get; } + + public CreateOrganisationResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public CreateOrganisationResponse(OrganisationDetails organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, + string message = null) : base(checkedPermissions, success, + message) + { + Organisation = organisation; + User = user; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Organisation/DeleteOrganisationResponse.cs b/Core.Core/DTO/UseCaseResponses/Organisation/DeleteOrganisationResponse.cs new file mode 100644 index 0000000..80652d5 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Organisation/DeleteOrganisationResponse.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Organisation +{ + public class DeleteOrganisationResponse : UseCaseResponseMessage + { + public DeleteOrganisationResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public DeleteOrganisationResponse(bool checkedPermissions = false, bool success = false, + string message = null) : base(checkedPermissions, success, + message) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Organisation/GetOrganisationResponse.cs b/Core.Core/DTO/UseCaseResponses/Organisation/GetOrganisationResponse.cs new file mode 100644 index 0000000..3b1cde9 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Organisation/GetOrganisationResponse.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Api.Core.Entities; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Organisation +{ + public class GetOrganisationResponse : UseCaseResponseMessage + { + public OrganisationDetails Organisation { get; } + + public Entities.OrganisationUser User { get; } + + public GetOrganisationResponse(IEnumerable errors, bool checkedPermissions = false, bool success = false, + string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public GetOrganisationResponse(OrganisationDetails organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, string message = null) : + base(checkedPermissions, success, message) + { + Organisation = organisation; + User = user; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Organisation/GetOrganisationsResponse.cs b/Core.Core/DTO/UseCaseResponses/Organisation/GetOrganisationsResponse.cs new file mode 100644 index 0000000..c8f4646 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Organisation/GetOrganisationsResponse.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Api.Core.Entities; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Organisation +{ + public class GetOrganisationsResponse : UseCaseResponseMessage + { + public IEnumerable Organisations { get; } + + public IDictionary Users { get; } + + public GetOrganisationsResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public GetOrganisationsResponse(IEnumerable organisations, + IDictionary users, bool checkedPermissions = false, bool success = false, + string message = null) : base(checkedPermissions, success, message) + { + Organisations = organisations; + Users = users; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/Organisation/UpdateOrganisationResponse.cs b/Core.Core/DTO/UseCaseResponses/Organisation/UpdateOrganisationResponse.cs new file mode 100644 index 0000000..c94d93a --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/Organisation/UpdateOrganisationResponse.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.Organisation +{ + public class UpdateOrganisationResponse : UseCaseResponseMessage + { + public Entities.OrganisationDetails Organisation { get; } + + public Entities.OrganisationUser User { get; } + + public UpdateOrganisationResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public UpdateOrganisationResponse(Entities.OrganisationDetails organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, + string message = null) : base(checkedPermissions, success, + message) + { + Organisation = organisation; + User = user; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/OrganisationUser/DeleteOrganisationUserResponse.cs b/Core.Core/DTO/UseCaseResponses/OrganisationUser/DeleteOrganisationUserResponse.cs new file mode 100644 index 0000000..6a97243 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/OrganisationUser/DeleteOrganisationUserResponse.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.OrganisationUser +{ + public class DeleteOrganisationUserResponse : DefaultUseCaseResponseMessage + { + public DeleteOrganisationUserResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public DeleteOrganisationUserResponse(Entities.Organisation organisation, Entities.OrganisationUser user, + bool checkedPermissions = false, bool success = false, string message = null) : base(organisation, user, + checkedPermissions, success, message) + { + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/OrganisationUser/GetOrganisationUserResponse.cs b/Core.Core/DTO/UseCaseResponses/OrganisationUser/GetOrganisationUserResponse.cs new file mode 100644 index 0000000..ad422e5 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/OrganisationUser/GetOrganisationUserResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.OrganisationUser +{ + public class GetOrganisationUserResponse : DefaultUseCaseResponseMessage + { + public Entities.OrganisationUser RequestedUser { get; } + + public GetOrganisationUserResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public GetOrganisationUserResponse(Entities.OrganisationUser requestedUser, Entities.Organisation organisation, + Entities.OrganisationUser user, bool checkedPermissions = false, bool success = false, + string message = null) : base(organisation, user, checkedPermissions, success, message) + { + RequestedUser = requestedUser; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/OrganisationUser/GetOrganisationUsersResponse.cs b/Core.Core/DTO/UseCaseResponses/OrganisationUser/GetOrganisationUsersResponse.cs new file mode 100644 index 0000000..eb6d424 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/OrganisationUser/GetOrganisationUsersResponse.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.OrganisationUser +{ + public class GetOrganisationUsersResponse : DefaultUseCaseResponseMessage + { + public IEnumerable RequestedUsers { get; } + + public GetOrganisationUsersResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : base(errors, checkedPermissions, success, message) + { + } + + public GetOrganisationUsersResponse(IEnumerable requestedUsers, + Entities.Organisation organisation, Entities.OrganisationUser user, bool checkedPermissions = false, + bool success = false, string message = null) : base(organisation, user, checkedPermissions, success, + message) + { + RequestedUsers = requestedUsers; + } + } +} \ No newline at end of file diff --git a/Core.Core/DTO/UseCaseResponses/OrganisationUser/UpdateOrganisationUserResponse.cs b/Core.Core/DTO/UseCaseResponses/OrganisationUser/UpdateOrganisationUserResponse.cs new file mode 100644 index 0000000..2f7ec23 --- /dev/null +++ b/Core.Core/DTO/UseCaseResponses/OrganisationUser/UpdateOrganisationUserResponse.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Api.Core.Interfaces; + +namespace Api.Core.DTO.UseCaseResponses.OrganisationUser +{ + public class UpdateOrganisationUserResponse : DefaultUseCaseResponseMessage + { + public Entities.OrganisationUser RequestedUser { get; } + + public UpdateOrganisationUserResponse(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : + base(errors, checkedPermissions, success, message) + { + } + + public UpdateOrganisationUserResponse(Entities.OrganisationUser requestedUser, + Entities.Organisation organisation, Entities.OrganisationUser user, bool checkedPermissions = false, + bool success = false, string message = null) : base(organisation, user, checkedPermissions, success, + message) + { + RequestedUser = requestedUser; + } + } +} \ No newline at end of file diff --git a/Core.Core/Entities/Client.cs b/Core.Core/Entities/Client.cs new file mode 100644 index 0000000..e50f27a --- /dev/null +++ b/Core.Core/Entities/Client.cs @@ -0,0 +1,15 @@ +using System; + +namespace Api.Core.Entities +{ + public class Client + { + public Guid Id { get; set; } + + public Guid OwnerId { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/Core.Core/Entities/ClientConfig.cs b/Core.Core/Entities/ClientConfig.cs new file mode 100644 index 0000000..c29bbe0 --- /dev/null +++ b/Core.Core/Entities/ClientConfig.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Api.Core.Entities +{ + public class ClientConfig + { + public Guid Id { get; set; } + + public Guid OwnerId { get; set; } + + public IEnumerable ClientEventGroups { get; set; } = new List(); + + public IEnumerable UserEventGroups { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Core.Core/Entities/Group.cs b/Core.Core/Entities/Group.cs new file mode 100644 index 0000000..2b3e0af --- /dev/null +++ b/Core.Core/Entities/Group.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Api.Core.Entities +{ + public class Group + { + public Guid Id { get; set; } + + public Guid OwnerId { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + public IList ClientIds { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Core.Core/Entities/IS4Client.cs b/Core.Core/Entities/IS4Client.cs new file mode 100644 index 0000000..4a9fcbd --- /dev/null +++ b/Core.Core/Entities/IS4Client.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Api.Core.Entities +{ + public class IS4Client + { + public Guid Id { get; set; } + + public Guid OwnerId { get; set; } + + public ICollection AllowedGrantTypes { get; set; } = new List(); + + public ICollection AllowedScopes { get; set; } = new List(); + + public bool AllowOfflineAccess { get; set; } + } +} \ No newline at end of file diff --git a/Core.Core/Entities/Invite.cs b/Core.Core/Entities/Invite.cs new file mode 100644 index 0000000..de26f66 --- /dev/null +++ b/Core.Core/Entities/Invite.cs @@ -0,0 +1,13 @@ +using System; + +namespace Api.Core.Entities +{ + public class Invite + { + public Guid OrganisationId { get; set; } + + public string Token { get; set; } + + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/Core.Core/Entities/Organisation.cs b/Core.Core/Entities/Organisation.cs new file mode 100644 index 0000000..cfaf235 --- /dev/null +++ b/Core.Core/Entities/Organisation.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Api.Core.Entities +{ + public class Organisation + { + public Guid Id { get; set; } + + public string Name { get; set; } + + public string Owner { get; set; } + } + + public class OrganisationDetails : Organisation + { + public IEnumerable Users { get; set; } = new List(); + + //Since this is a DTO object, we sometimes wish to only return the Base class, not the derived one. Hence, this method. + public Organisation ToOrganisation() + { + return new Organisation + { + Id = Id, + Name = Name, + Owner = Owner + }; + } + + public bool UserIsOwner(string userId) + { + return Owner != null && Owner.Equals(userId); + } + + public bool UserIsMember(string userId) + { + return Users.Select(u => u.Id).Contains(userId); + } + + public bool UserHasPermission(string userId, OrganisationPermissions permissions) + { + if (Users == null || !Users.Select(u => u.Id).Contains(userId)) + return false; + + if (Owner != null && Owner.Equals(userId)) + return true; + + OrganisationUser orgUser = Users.First(u => u.Id == userId); + + return orgUser.Permissions.Contains(OrganisationPermissions.All) || + orgUser.Permissions.Contains(permissions); + } + } +} \ No newline at end of file diff --git a/Core.Core/Entities/OrganisationPermissions.cs b/Core.Core/Entities/OrganisationPermissions.cs new file mode 100644 index 0000000..c4009f4 --- /dev/null +++ b/Core.Core/Entities/OrganisationPermissions.cs @@ -0,0 +1,24 @@ +namespace Api.Core.Entities +{ + public enum OrganisationPermissions + { + //EDIT + EditClients, + EditGroups, + EditOrgUsers, + EditOrg, + + //CREATE + CreateClients, + CreateGroups, + CreateOrgUsers, + + //DELETE + DeleteClients, + DeleteGroups, + DeleteOrgUsers, + + //ALL + All + } +} \ No newline at end of file diff --git a/Core.Core/Entities/OrganisationUser.cs b/Core.Core/Entities/OrganisationUser.cs new file mode 100644 index 0000000..162b245 --- /dev/null +++ b/Core.Core/Entities/OrganisationUser.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Api.Core.Entities +{ + public class OrganisationUser + { + public string Id { get; set; } + + public IList Permissions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/DefaultUseCaseRequest.cs b/Core.Core/Interfaces/DefaultUseCaseRequest.cs new file mode 100644 index 0000000..6bae747 --- /dev/null +++ b/Core.Core/Interfaces/DefaultUseCaseRequest.cs @@ -0,0 +1,24 @@ +using System; + +namespace Api.Core.Interfaces +{ + public abstract class DefaultUseCaseRequest : UseCaseRequest + { + public string UserId { get; } + + public Guid OrganisationId { get; } + + protected DefaultUseCaseRequest(string userId, Guid organisationId) + { + // makes sure that any request made that expects to use a UserID doesn't have a null one. small layer of security. + UserId = userId ?? throw new InvalidOperationException(); + OrganisationId = organisationId; + } + + protected DefaultUseCaseRequest(Guid organisationId) + { + UserId = null; + OrganisationId = organisationId; + } + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/DefaultUseCaseResponseMessage.cs b/Core.Core/Interfaces/DefaultUseCaseResponseMessage.cs new file mode 100644 index 0000000..ad702b5 --- /dev/null +++ b/Core.Core/Interfaces/DefaultUseCaseResponseMessage.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Api.Core.DTO; +using Api.Core.Entities; + +namespace Api.Core.Interfaces +{ + public abstract class DefaultUseCaseResponseMessage : UseCaseResponseMessage + { + public Organisation Organisation { get; } + + public OrganisationUser User { get; } + + protected DefaultUseCaseResponseMessage(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) : base(errors, checkedPermissions, success, message) + { + } + + protected DefaultUseCaseResponseMessage(Organisation organisation, OrganisationUser user, + bool checkedPermissions = false, bool success = false, string message = null) : base(checkedPermissions, + success, message) + { + Organisation = organisation; + User = user; + } + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Gateways/Repositories/IClientConfigRepository.cs b/Core.Core/Interfaces/Gateways/Repositories/IClientConfigRepository.cs new file mode 100644 index 0000000..75c5b19 --- /dev/null +++ b/Core.Core/Interfaces/Gateways/Repositories/IClientConfigRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.Entities; + +namespace Api.Core.Interfaces.Gateways.Repositories +{ + public interface IClientConfigRepository + { + Task Get(Guid id); + + Task Create(ClientConfig clientConfig); + + Task Update(Guid id, ClientConfig clientConfig); + + Task DeleteForOwner(Guid ownerId); + + Task Delete(Guid id); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Gateways/Repositories/IClientRepository.cs b/Core.Core/Interfaces/Gateways/Repositories/IClientRepository.cs new file mode 100644 index 0000000..eeda098 --- /dev/null +++ b/Core.Core/Interfaces/Gateways/Repositories/IClientRepository.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.Entities; + +namespace Api.Core.Interfaces.Gateways.Repositories +{ + public interface IClientRepository + { + Task GetForOwner(Guid ownerId); + + Task Get(Guid id); + + Task Create(Client client); + + Task Update(Guid clientId, Client client); + + Task DeleteForOwner(Guid ownerId); + + Task Delete(Guid id); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Gateways/Repositories/IGroupRepository.cs b/Core.Core/Interfaces/Gateways/Repositories/IGroupRepository.cs new file mode 100644 index 0000000..cd4c549 --- /dev/null +++ b/Core.Core/Interfaces/Gateways/Repositories/IGroupRepository.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.Entities; + +namespace Api.Core.Interfaces.Gateways.Repositories +{ + public interface IGroupRepository + { + Task GetForOwner(Guid ownerId); + + Task GetForClient(Guid clientId); + + Task Get(Guid id); + + Task Create(Group group); + + Task Update(Guid groupId, Group group); + + Task DeleteForOwner(Guid ownerId); + + Task Delete(Guid id); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Gateways/Repositories/IIS4ClientRepository.cs b/Core.Core/Interfaces/Gateways/Repositories/IIS4ClientRepository.cs new file mode 100644 index 0000000..ee8b46f --- /dev/null +++ b/Core.Core/Interfaces/Gateways/Repositories/IIS4ClientRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.IS4Client; +using Api.Core.Entities; + +namespace Api.Core.Interfaces.Gateways.Repositories +{ + public interface IIS4ClientRepository + { + Task Create(IS4Client is4Client, string password); + + Task DeleteForOwner(Guid ownerId); + + Task Delete(Guid id); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Gateways/Repositories/IInviteRepository.cs b/Core.Core/Interfaces/Gateways/Repositories/IInviteRepository.cs new file mode 100644 index 0000000..a0e368b --- /dev/null +++ b/Core.Core/Interfaces/Gateways/Repositories/IInviteRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.Entities; + +namespace Api.Core.Interfaces.Gateways.Repositories +{ + public interface IInviteRepository + { + Task Get(Guid organisationId, string token); + + Task Create(Invite invite); + + Task Delete(Guid organisationId, string token); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Gateways/Repositories/IOrganisationRepository.cs b/Core.Core/Interfaces/Gateways/Repositories/IOrganisationRepository.cs new file mode 100644 index 0000000..b66f31f --- /dev/null +++ b/Core.Core/Interfaces/Gateways/Repositories/IOrganisationRepository.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.Entities; + +namespace Api.Core.Interfaces.Gateways.Repositories +{ + public interface IOrganisationRepository + { + Task GetForUser(string userId); + + Task Get(Guid id); + + Task Create(OrganisationDetails organisation); + + Task Update(Guid organisationId, OrganisationDetails organisation); + + Task Delete(Guid id); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/IOutputPort.cs b/Core.Core/Interfaces/IOutputPort.cs new file mode 100644 index 0000000..ead5705 --- /dev/null +++ b/Core.Core/Interfaces/IOutputPort.cs @@ -0,0 +1,7 @@ +namespace Api.Core.Interfaces +{ + public interface IOutputPort + { + void Handle(TUseCaseResponse response); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/IUseCaseRequest.cs b/Core.Core/Interfaces/IUseCaseRequest.cs new file mode 100644 index 0000000..1160a6a --- /dev/null +++ b/Core.Core/Interfaces/IUseCaseRequest.cs @@ -0,0 +1,6 @@ +namespace Api.Core.Interfaces +{ + public interface IUseCaseRequest + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/IUseCaseRequestHandler.cs b/Core.Core/Interfaces/IUseCaseRequestHandler.cs new file mode 100644 index 0000000..ee23ea0 --- /dev/null +++ b/Core.Core/Interfaces/IUseCaseRequestHandler.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace Api.Core.Interfaces +{ + public interface IUseCaseRequestHandler + where TUseCaseRequest : UseCaseRequest + { + Task HandleAsync(TUseCaseRequest message, IOutputPort outputPort); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/Services/IPasswordGenerator.cs b/Core.Core/Interfaces/Services/IPasswordGenerator.cs new file mode 100644 index 0000000..523d545 --- /dev/null +++ b/Core.Core/Interfaces/Services/IPasswordGenerator.cs @@ -0,0 +1,22 @@ +namespace Api.Core.Interfaces.Services +{ + public interface IPasswordGenerator + { + string Generate( + int requiredLengthMin = 60, + int requiredLengthMax = 68, + int requiredUniqueChars = 8, + bool requireDigit = true, + bool requireLowercase = true, + bool requireNonAlphanumeric = true, + bool requireUppercase = true); + + string GenerateNoSpecial( + int requiredLengthMin = 60, + int requiredLengthMax = 68, + int requiredUniqueChars = 8, + bool requireDigit = true, + bool requireLowercase = true, + bool requireUppercase = true); + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCaseRequest.cs b/Core.Core/Interfaces/UseCaseRequest.cs new file mode 100644 index 0000000..f9a8c73 --- /dev/null +++ b/Core.Core/Interfaces/UseCaseRequest.cs @@ -0,0 +1,8 @@ +using System; + +namespace Api.Core.Interfaces +{ + public abstract class UseCaseRequest : EventArgs + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCaseResponseMessage.cs b/Core.Core/Interfaces/UseCaseResponseMessage.cs new file mode 100644 index 0000000..dce351e --- /dev/null +++ b/Core.Core/Interfaces/UseCaseResponseMessage.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Api.Core.DTO; + +namespace Api.Core.Interfaces +{ + public abstract class UseCaseResponseMessage + { + public bool Success { get; } + + public string Message { get; } + + public bool CheckedPermissions { get; } + + public IEnumerable Errors { get; } + + protected UseCaseResponseMessage(IEnumerable errors, bool checkedPermissions = false, + bool success = false, string message = null) + { + CheckedPermissions = checkedPermissions; + Errors = errors; + Success = success; + Message = message; + } + + protected UseCaseResponseMessage(bool checkedPermissions = false, bool success = false, string message = null) + { + CheckedPermissions = checkedPermissions; + Success = success; + Message = message; + Errors = new List(); + } + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Client/ICreateClientUseCase.cs b/Core.Core/Interfaces/UseCases/Client/ICreateClientUseCase.cs new file mode 100644 index 0000000..7d826a7 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Client/ICreateClientUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; + +namespace Api.Core.Interfaces.UseCases.Client +{ + public interface ICreateClientUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Client/IDeleteClientUseCase.cs b/Core.Core/Interfaces/UseCases/Client/IDeleteClientUseCase.cs new file mode 100644 index 0000000..e86f45b --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Client/IDeleteClientUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; + +namespace Api.Core.Interfaces.UseCases.Client +{ + public interface IDeleteClientUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Client/IGetClientUseCase.cs b/Core.Core/Interfaces/UseCases/Client/IGetClientUseCase.cs new file mode 100644 index 0000000..7556ef3 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Client/IGetClientUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; + +namespace Api.Core.Interfaces.UseCases.Client +{ + public interface IGetClientUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Client/IGetClientsUseCase.cs b/Core.Core/Interfaces/UseCases/Client/IGetClientsUseCase.cs new file mode 100644 index 0000000..59f9801 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Client/IGetClientsUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; + +namespace Api.Core.Interfaces.UseCases.Client +{ + public interface IGetClientsUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Client/IUpdateClientUseCase.cs b/Core.Core/Interfaces/UseCases/Client/IUpdateClientUseCase.cs new file mode 100644 index 0000000..ea810f2 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Client/IUpdateClientUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; + +namespace Api.Core.Interfaces.UseCases.Client +{ + public interface IUpdateClientUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/ClientConfig/IGetClientConfigUseCase.cs b/Core.Core/Interfaces/UseCases/ClientConfig/IGetClientConfigUseCase.cs new file mode 100644 index 0000000..c4c5bb5 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/ClientConfig/IGetClientConfigUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; + +namespace Api.Core.Interfaces.UseCases.ClientConfig +{ + public interface IGetClientConfigUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/ClientConfig/IUpdateClientConfigUseCase.cs b/Core.Core/Interfaces/UseCases/ClientConfig/IUpdateClientConfigUseCase.cs new file mode 100644 index 0000000..115b377 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/ClientConfig/IUpdateClientConfigUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; + +namespace Api.Core.Interfaces.UseCases.ClientConfig +{ + public interface + IUpdateClientConfigUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Group/ICreateGroupUseCase.cs b/Core.Core/Interfaces/UseCases/Group/ICreateGroupUseCase.cs new file mode 100644 index 0000000..ca54ba2 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Group/ICreateGroupUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; + +namespace Api.Core.Interfaces.UseCases.Group +{ + public interface ICreateGroupUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Group/IDeleteGroupUseCase.cs b/Core.Core/Interfaces/UseCases/Group/IDeleteGroupUseCase.cs new file mode 100644 index 0000000..31f80c3 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Group/IDeleteGroupUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; + +namespace Api.Core.Interfaces.UseCases.Group +{ + public interface IDeleteGroupUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Group/IGetGroupUseCase.cs b/Core.Core/Interfaces/UseCases/Group/IGetGroupUseCase.cs new file mode 100644 index 0000000..9690604 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Group/IGetGroupUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; + +namespace Api.Core.Interfaces.UseCases.Group +{ + public interface IGetGroupUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Group/IGetGroupsUseCase.cs b/Core.Core/Interfaces/UseCases/Group/IGetGroupsUseCase.cs new file mode 100644 index 0000000..d0806bf --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Group/IGetGroupsUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; + +namespace Api.Core.Interfaces.UseCases.Group +{ + public interface IGetGroupsUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Group/IUpdateGroupUseCase.cs b/Core.Core/Interfaces/UseCases/Group/IUpdateGroupUseCase.cs new file mode 100644 index 0000000..c691f09 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Group/IUpdateGroupUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; + +namespace Api.Core.Interfaces.UseCases.Group +{ + public interface IUpdateGroupUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Invite/ICreateInviteUseCase.cs b/Core.Core/Interfaces/UseCases/Invite/ICreateInviteUseCase.cs new file mode 100644 index 0000000..457f74c --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Invite/ICreateInviteUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; + +namespace Api.Core.Interfaces.UseCases.Invite +{ + public interface ICreateInviteUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Invite/IUseInviteUseCase.cs b/Core.Core/Interfaces/UseCases/Invite/IUseInviteUseCase.cs new file mode 100644 index 0000000..05cf22d --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Invite/IUseInviteUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; + +namespace Api.Core.Interfaces.UseCases.Invite +{ + public interface IUseInviteUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Organisation/ICreateGroupUseCase.cs b/Core.Core/Interfaces/UseCases/Organisation/ICreateGroupUseCase.cs new file mode 100644 index 0000000..4473b28 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Organisation/ICreateGroupUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; + +namespace Api.Core.Interfaces.UseCases.Organisation +{ + public interface + ICreateOrganisationUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Organisation/IDeleteOrganisationUseCase.cs b/Core.Core/Interfaces/UseCases/Organisation/IDeleteOrganisationUseCase.cs new file mode 100644 index 0000000..908a698 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Organisation/IDeleteOrganisationUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; + +namespace Api.Core.Interfaces.UseCases.Organisation +{ + public interface + IDeleteOrganisationUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Organisation/IGetOrganisationUseCase.cs b/Core.Core/Interfaces/UseCases/Organisation/IGetOrganisationUseCase.cs new file mode 100644 index 0000000..9fec012 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Organisation/IGetOrganisationUseCase.cs @@ -0,0 +1,9 @@ +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; + +namespace Api.Core.Interfaces.UseCases.Organisation +{ + public interface IGetOrganisationUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Organisation/IGetOrganisationsUseCase.cs b/Core.Core/Interfaces/UseCases/Organisation/IGetOrganisationsUseCase.cs new file mode 100644 index 0000000..9fb2824 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Organisation/IGetOrganisationsUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; + +namespace Api.Core.Interfaces.UseCases.Organisation +{ + public interface + IGetOrganisationsUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/Organisation/IUpdateOrganisationUseCase.cs b/Core.Core/Interfaces/UseCases/Organisation/IUpdateOrganisationUseCase.cs new file mode 100644 index 0000000..16bed40 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/Organisation/IUpdateOrganisationUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; + +namespace Api.Core.Interfaces.UseCases.Organisation +{ + public interface + IUpdateOrganisationUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/OrganisationUser/IDeleteOrganisationUserUseCase.cs b/Core.Core/Interfaces/UseCases/OrganisationUser/IDeleteOrganisationUserUseCase.cs new file mode 100644 index 0000000..f1ab20d --- /dev/null +++ b/Core.Core/Interfaces/UseCases/OrganisationUser/IDeleteOrganisationUserUseCase.cs @@ -0,0 +1,11 @@ +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; + +namespace Api.Core.Interfaces.UseCases.OrganisationUser +{ + public interface + IDeleteOrganisationUserUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/OrganisationUser/IGetOrganisationUserUseCase.cs b/Core.Core/Interfaces/UseCases/OrganisationUser/IGetOrganisationUserUseCase.cs new file mode 100644 index 0000000..a767bd9 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/OrganisationUser/IGetOrganisationUserUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; + +namespace Api.Core.Interfaces.UseCases.OrganisationUser +{ + public interface + IGetOrganisationUserUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/OrganisationUser/IGetOrganisationUsersUseCase.cs b/Core.Core/Interfaces/UseCases/OrganisationUser/IGetOrganisationUsersUseCase.cs new file mode 100644 index 0000000..67722f0 --- /dev/null +++ b/Core.Core/Interfaces/UseCases/OrganisationUser/IGetOrganisationUsersUseCase.cs @@ -0,0 +1,10 @@ +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; + +namespace Api.Core.Interfaces.UseCases.OrganisationUser +{ + public interface + IGetOrganisationUsersUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/Interfaces/UseCases/OrganisationUser/IUpdateOrganisationUserUseCase.cs b/Core.Core/Interfaces/UseCases/OrganisationUser/IUpdateOrganisationUserUseCase.cs new file mode 100644 index 0000000..c7b9e7f --- /dev/null +++ b/Core.Core/Interfaces/UseCases/OrganisationUser/IUpdateOrganisationUserUseCase.cs @@ -0,0 +1,11 @@ +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; + +namespace Api.Core.Interfaces.UseCases.OrganisationUser +{ + public interface + IUpdateOrganisationUserUseCase : IUseCaseRequestHandler + { + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Client/CreateClientUseCase.cs b/Core.Core/UseCases/Client/CreateClientUseCase.cs new file mode 100644 index 0000000..d080a94 --- /dev/null +++ b/Core.Core/UseCases/Client/CreateClientUseCase.cs @@ -0,0 +1,122 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.IS4Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Core.Interfaces.UseCases.Client; + +namespace Api.Core.UseCases.Client +{ + public class CreateClientUseCase : ICreateClientUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientRepository _clientRepository; + private readonly IClientConfigRepository _clientConfigRepository; + private readonly IIS4ClientRepository _is4ClientRepository; + private readonly IPasswordGenerator _passwordGenerator; + + public CreateClientUseCase(IOrganisationRepository organisationRepository, IClientRepository clientRepository, IClientConfigRepository clientConfigRepository, IIS4ClientRepository is4ClientRepository, IPasswordGenerator passwordGenerator) + { + _organisationRepository = organisationRepository; + _clientRepository = clientRepository; + _clientConfigRepository = clientConfigRepository; + _is4ClientRepository = is4ClientRepository; + _passwordGenerator = passwordGenerator; + } + + public async Task HandleAsync(CreateClientRequest message, IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new CreateClientResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.CreateClients)) + { + outputPort.Handle(new CreateClientResponse(new[] {new Error("404", "Organisation Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + Guid clientId = Guid.NewGuid(); + string password = _passwordGenerator.Generate(); + + IS4Client is4Client = new IS4Client + { + Id = clientId, + OwnerId = organisationGatewayResponse.Organisation.Id, + AllowedGrantTypes = new[] {"client_credentials"}, + AllowedScopes = new[] {"ChatChain"}, + AllowOfflineAccess = true + }; + + CreateIS4ClientGatewayResponse is4ClientGatewayResponse = await _is4ClientRepository.Create(is4Client, password); + + if (!is4ClientGatewayResponse.Success) + { + outputPort.Handle(new CreateClientResponse(is4ClientGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.Client client = new Entities.Client + { + Id = clientId, + OwnerId = organisationGatewayResponse.Organisation.Id, + Name = message.Name, + Description = message.Description + }; + + CreateClientGatewayResponse clientGatewayResponse = await _clientRepository.Create(client); + + if (!clientGatewayResponse.Success) + { + outputPort.Handle(new CreateClientResponse(clientGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.ClientConfig clientConfig = new Entities.ClientConfig + { + Id = clientId, + OwnerId = organisationGatewayResponse.Organisation.Id + }; + + CreateClientConfigGatewayResponse clientConfigGatewayResponse = + await _clientConfigRepository.Create(clientConfig); + + if (!clientConfigGatewayResponse.Success) + { + outputPort.Handle(new CreateClientResponse(clientConfigGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new CreateClientResponse(clientGatewayResponse.Client, + clientConfigGatewayResponse.ClientConfig, password, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Client/DeleteClientUseCase.cs b/Core.Core/UseCases/Client/DeleteClientUseCase.cs new file mode 100644 index 0000000..af6818b --- /dev/null +++ b/Core.Core/UseCases/Client/DeleteClientUseCase.cs @@ -0,0 +1,110 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Client; + +namespace Api.Core.UseCases.Client +{ + public class DeleteClientUseCase : IDeleteClientUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientRepository _clientRepository; + private readonly IClientConfigRepository _clientConfigRepository; + private readonly IIS4ClientRepository _is4ClientRepository; + + public DeleteClientUseCase(IOrganisationRepository organisationRepository, IClientRepository clientRepository, + IClientConfigRepository clientConfigRepository, IIS4ClientRepository is4ClientRepository) + { + _organisationRepository = organisationRepository; + _clientRepository = clientRepository; + _clientConfigRepository = clientConfigRepository; + _is4ClientRepository = is4ClientRepository; + } + + public async Task HandleAsync(DeleteClientRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new DeleteClientResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.DeleteClients)) + { + outputPort.Handle(new DeleteClientResponse(new[] {new Error("404", "Client Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetClientGatewayResponse getConfigGatewayResponse = + await _clientRepository.Get(message.ClientId); + + if (!getConfigGatewayResponse.Success) + { + outputPort.Handle(new DeleteClientResponse(getConfigGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (getConfigGatewayResponse.Client.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api clients can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new DeleteClientResponse(new[] {new Error("404", "Client Not Found")}, + message.UserId != null)); + return false; + } + + BaseGatewayResponse is4ClientGatewayResponse = + await _is4ClientRepository.Delete(message.ClientId); + + if (!is4ClientGatewayResponse.Success) + { + outputPort.Handle(new DeleteClientResponse(is4ClientGatewayResponse.Errors, message.UserId != null)); + return false; + } + + BaseGatewayResponse clientGatewayResponse = + await _clientRepository.Delete(message.ClientId); + + if (!clientGatewayResponse.Success) + { + outputPort.Handle(new DeleteClientResponse(clientGatewayResponse.Errors, message.UserId != null)); + return false; + } + + BaseGatewayResponse clientConfigGatewayResponse = + await _clientConfigRepository.Delete(message.ClientId); + + if (!clientConfigGatewayResponse.Success) + { + outputPort.Handle(new DeleteClientResponse(clientConfigGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new DeleteClientResponse(organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Client/GetClientUseCase.cs b/Core.Core/UseCases/Client/GetClientUseCase.cs new file mode 100644 index 0000000..b1bb405 --- /dev/null +++ b/Core.Core/UseCases/Client/GetClientUseCase.cs @@ -0,0 +1,77 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Client; + +namespace Api.Core.UseCases.Client +{ + public class GetClientUseCase : IGetClientUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientRepository _clientRepository; + + public GetClientUseCase(IOrganisationRepository organisationRepository, + IClientRepository clientRepository) + { + _organisationRepository = organisationRepository; + _clientRepository = clientRepository; + } + + public async Task HandleAsync(GetClientRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetClientResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetClientResponse(new[] {new Error("404", "Client Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetClientGatewayResponse clientGatewayResponse = + await _clientRepository.Get(message.ClientId); + + if (!clientGatewayResponse.Success) + { + outputPort.Handle(new GetClientResponse(clientGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (clientGatewayResponse.Client.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api client's can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new GetClientResponse(new[] {new Error("404", "Client Not Found")}, + message.UserId != null)); + return false; + } + + outputPort.Handle(new GetClientResponse(clientGatewayResponse.Client, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Client/GetClientsUseCase.cs b/Core.Core/UseCases/Client/GetClientsUseCase.cs new file mode 100644 index 0000000..a2ad3d6 --- /dev/null +++ b/Core.Core/UseCases/Client/GetClientsUseCase.cs @@ -0,0 +1,69 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Client; + +namespace Api.Core.UseCases.Client +{ + public class GetClientsUseCase : IGetClientsUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientRepository _clientRepository; + + public GetClientsUseCase(IOrganisationRepository organisationRepository, + IClientRepository clientRepository) + { + _organisationRepository = organisationRepository; + _clientRepository = clientRepository; + } + + public async Task HandleAsync(GetClientsRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetClientsResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetClientsResponse(new[] {new Error("404", "Clients Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetClientsGatewayResponse clientsGatewayResponse = + await _clientRepository.GetForOwner(organisationGatewayResponse.Organisation.Id); + + if (!clientsGatewayResponse.Success) + { + outputPort.Handle(new GetClientsResponse(clientsGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new GetClientsResponse(clientsGatewayResponse.Clients, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Client/UpdateClientUseCase.cs b/Core.Core/UseCases/Client/UpdateClientUseCase.cs new file mode 100644 index 0000000..4056c88 --- /dev/null +++ b/Core.Core/UseCases/Client/UpdateClientUseCase.cs @@ -0,0 +1,90 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Client; + +namespace Api.Core.UseCases.Client +{ + public class UpdateClientUseCase : IUpdateClientUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientRepository _clientRepository; + + public UpdateClientUseCase(IOrganisationRepository organisationRepository, + IClientRepository clientRepository) + { + _organisationRepository = organisationRepository; + _clientRepository = clientRepository; + } + + public async Task HandleAsync(UpdateClientRequest message, IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new UpdateClientResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.EditClients)) + { + outputPort.Handle(new UpdateClientResponse(new[] {new Error("404", "Client Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetClientGatewayResponse getConfigGatewayResponse = + await _clientRepository.Get(message.ClientId); + + if (!getConfigGatewayResponse.Success) + { + outputPort.Handle(new UpdateClientResponse(getConfigGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (getConfigGatewayResponse.Client.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api clients can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new UpdateClientResponse(new[] {new Error("404", "Client Not Found")}, + message.UserId != null)); + return false; + } + + getConfigGatewayResponse.Client.Name = message.Name; + getConfigGatewayResponse.Client.Description = message.Description; + + UpdateClientGatewayResponse clientGatewayResponse = + await _clientRepository.Update(message.ClientId, getConfigGatewayResponse.Client); + + if (!clientGatewayResponse.Success) + { + outputPort.Handle(new UpdateClientResponse(clientGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new UpdateClientResponse(clientGatewayResponse.Client, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/ClientConfig/GetClientConfigUseCase.cs b/Core.Core/UseCases/ClientConfig/GetClientConfigUseCase.cs new file mode 100644 index 0000000..b0e0b19 --- /dev/null +++ b/Core.Core/UseCases/ClientConfig/GetClientConfigUseCase.cs @@ -0,0 +1,76 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.ClientConfig; + +namespace Api.Core.UseCases.ClientConfig +{ + public class GetClientConfigUseCase : IGetClientConfigUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientConfigRepository _clientConfigRepository; + + public GetClientConfigUseCase(IOrganisationRepository organisationRepository, + IClientConfigRepository clientConfigRepository) + { + _organisationRepository = organisationRepository; + _clientConfigRepository = clientConfigRepository; + } + + public async Task HandleAsync(GetClientConfigRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetClientConfigResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetClientConfigResponse(new[] {new Error("404", "Client Config Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + DTO.GatewayResponses.Repositories.ClientConfig.GetClientConfigGatewayResponse configGatewayResponse = + await _clientConfigRepository.Get(message.ClientConfigId); + + if (!configGatewayResponse.Success) + { + outputPort.Handle(new GetClientConfigResponse(configGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (configGatewayResponse.ClientConfig.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api client's can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new GetClientConfigResponse(new[] {new Error("404", "Client Config Not Found")}, + message.UserId != null)); + return false; + } + + outputPort.Handle(new GetClientConfigResponse(configGatewayResponse.ClientConfig, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/ClientConfig/UpdateClientConfigUseCase.cs b/Core.Core/UseCases/ClientConfig/UpdateClientConfigUseCase.cs new file mode 100644 index 0000000..f389f45 --- /dev/null +++ b/Core.Core/UseCases/ClientConfig/UpdateClientConfigUseCase.cs @@ -0,0 +1,93 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.ClientConfig; + +namespace Api.Core.UseCases.ClientConfig +{ + public sealed class UpdateClientConfigUseCase : IUpdateClientConfigUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientConfigRepository _clientConfigRepository; + + public UpdateClientConfigUseCase(IOrganisationRepository organisationRepository, + IClientConfigRepository clientConfigRepository) + { + _organisationRepository = organisationRepository; + _clientConfigRepository = clientConfigRepository; + } + + public async Task HandleAsync(UpdateClientConfigRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new UpdateClientConfigResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.EditClients)) + { + outputPort.Handle(new UpdateClientConfigResponse( + new[] {new Error("404", "Client Config Not Found")}, message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetClientConfigGatewayResponse getConfigGatewayResponse = + await _clientConfigRepository.Get(message.ClientConfigId); + + if (!getConfigGatewayResponse.Success) + { + outputPort.Handle(new UpdateClientConfigResponse(getConfigGatewayResponse.Errors, + message.UserId != null)); + return false; + } + + if (getConfigGatewayResponse.ClientConfig.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api clients can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new UpdateClientConfigResponse(new[] {new Error("404", "Client Config Not Found")}, + message.UserId != null)); + return false; + } + + getConfigGatewayResponse.ClientConfig.ClientEventGroups = message.ClientEventGroups; + getConfigGatewayResponse.ClientConfig.UserEventGroups = message.UserEventGroups; + + UpdateClientConfigGatewayResponse configGatewayResponse = + await _clientConfigRepository.Update(getConfigGatewayResponse.ClientConfig.Id, + getConfigGatewayResponse.ClientConfig); + + if (!configGatewayResponse.Success) + { + outputPort.Handle(new UpdateClientConfigResponse(configGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new UpdateClientConfigResponse(configGatewayResponse.ClientConfig, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Group/CreateGroupUseCase.cs b/Core.Core/UseCases/Group/CreateGroupUseCase.cs new file mode 100644 index 0000000..8c4b3d8 --- /dev/null +++ b/Core.Core/UseCases/Group/CreateGroupUseCase.cs @@ -0,0 +1,79 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Group; + +namespace Api.Core.UseCases.Group +{ + public class CreateGroupUseCase : ICreateGroupUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IGroupRepository _groupRepository; + + public CreateGroupUseCase(IOrganisationRepository organisationRepository, + IGroupRepository groupRepository) + { + _organisationRepository = organisationRepository; + _groupRepository = groupRepository; + } + + public async Task HandleAsync(CreateGroupRequest message, IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new CreateGroupResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.CreateGroups)) + { + outputPort.Handle(new CreateGroupResponse(new[] {new Error("404", "Organisation Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + Entities.Group group = new Entities.Group + { + Id = Guid.NewGuid(), + OwnerId = organisationGatewayResponse.Organisation.Id, + Name = message.Name, + Description = message.Description, + ClientIds = message.ClientIds + }; + + CreateGroupGatewayResponse groupGatewayResponse = await _groupRepository.Create(group); + + if (!groupGatewayResponse.Success) + { + outputPort.Handle(new CreateGroupResponse(groupGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new CreateGroupResponse(groupGatewayResponse.Group, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Group/DeleteGroupUseCase.cs b/Core.Core/UseCases/Group/DeleteGroupUseCase.cs new file mode 100644 index 0000000..c4dd67a --- /dev/null +++ b/Core.Core/UseCases/Group/DeleteGroupUseCase.cs @@ -0,0 +1,88 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Group; + +namespace Api.Core.UseCases.Group +{ + public class DeleteGroupUseCase : IDeleteGroupUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IGroupRepository _groupRepository; + + public DeleteGroupUseCase(IOrganisationRepository organisationRepository, + IGroupRepository groupRepository) + { + _organisationRepository = organisationRepository; + _groupRepository = groupRepository; + } + + public async Task HandleAsync(DeleteGroupRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new DeleteGroupResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.DeleteGroups)) + { + outputPort.Handle(new DeleteGroupResponse(new[] {new Error("404", "Group Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetGroupGatewayResponse getConfigGatewayResponse = + await _groupRepository.Get(message.GroupId); + + if (!getConfigGatewayResponse.Success) + { + outputPort.Handle(new DeleteGroupResponse(getConfigGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (getConfigGatewayResponse.Group.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api groups can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new DeleteGroupResponse(new[] {new Error("404", "Group Not Found")}, + message.UserId != null)); + return false; + } + + BaseGatewayResponse groupGatewayResponse = + await _groupRepository.Delete(getConfigGatewayResponse.Group.Id); + + if (!groupGatewayResponse.Success) + { + outputPort.Handle(new DeleteGroupResponse(groupGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new DeleteGroupResponse(organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Group/GetGroupUseCase.cs b/Core.Core/UseCases/Group/GetGroupUseCase.cs new file mode 100644 index 0000000..ee0f23f --- /dev/null +++ b/Core.Core/UseCases/Group/GetGroupUseCase.cs @@ -0,0 +1,77 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Group; + +namespace Api.Core.UseCases.Group +{ + public class GetGroupUseCase : IGetGroupUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IGroupRepository _groupRepository; + + public GetGroupUseCase(IOrganisationRepository organisationRepository, + IGroupRepository groupRepository) + { + _organisationRepository = organisationRepository; + _groupRepository = groupRepository; + } + + public async Task HandleAsync(GetGroupRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetGroupResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetGroupResponse(new[] {new Error("404", "Group Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetGroupGatewayResponse groupGatewayResponse = + await _groupRepository.Get(message.GroupId); + + if (!groupGatewayResponse.Success) + { + outputPort.Handle(new GetGroupResponse(groupGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (groupGatewayResponse.Group.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api Group's can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new GetGroupResponse(new[] {new Error("404", "Group Not Found")}, + message.UserId != null)); + return false; + } + + outputPort.Handle(new GetGroupResponse(groupGatewayResponse.Group, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Group/GetGroupsUseCase.cs b/Core.Core/UseCases/Group/GetGroupsUseCase.cs new file mode 100644 index 0000000..c46c78a --- /dev/null +++ b/Core.Core/UseCases/Group/GetGroupsUseCase.cs @@ -0,0 +1,69 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Group; + +namespace Api.Core.UseCases.Group +{ + public class GetGroupsUseCase : IGetGroupsUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IGroupRepository _groupRepository; + + public GetGroupsUseCase(IOrganisationRepository organisationRepository, + IGroupRepository groupRepository) + { + _organisationRepository = organisationRepository; + _groupRepository = groupRepository; + } + + public async Task HandleAsync(GetGroupsRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetGroupsResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetGroupsResponse(new[] {new Error("404", "Groups Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetGroupsGatewayResponse groupsGatewayResponse = + await _groupRepository.GetForOwner(organisationGatewayResponse.Organisation.Id); + + if (!groupsGatewayResponse.Success) + { + outputPort.Handle(new GetGroupsResponse(groupsGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new GetGroupsResponse(groupsGatewayResponse.Groups, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Group/UpdateGroupUseCase.cs b/Core.Core/UseCases/Group/UpdateGroupUseCase.cs new file mode 100644 index 0000000..296de12 --- /dev/null +++ b/Core.Core/UseCases/Group/UpdateGroupUseCase.cs @@ -0,0 +1,96 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Group; + +namespace Api.Core.UseCases.Group +{ + public class UpdateGroupUseCase : IUpdateGroupUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IGroupRepository _groupRepository; + + public UpdateGroupUseCase(IOrganisationRepository organisationRepository, + IGroupRepository groupRepository) + { + _organisationRepository = organisationRepository; + _groupRepository = groupRepository; + } + + public async Task HandleAsync(UpdateGroupRequest message, IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new UpdateGroupResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.EditGroups)) + { + outputPort.Handle(new UpdateGroupResponse(new[] {new Error("404", "Group Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + GetGroupGatewayResponse getConfigGatewayResponse = + await _groupRepository.Get(message.GroupId); + + if (!getConfigGatewayResponse.Success) + { + outputPort.Handle(new UpdateGroupResponse(getConfigGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (getConfigGatewayResponse.Group.OwnerId != organisationGatewayResponse.Organisation.Id) + { + //Return an error identical to the "404" so that api groups can't tell the difference between it being non-existent, or not allowed + outputPort.Handle(new UpdateGroupResponse(new[] {new Error("404", "Group Not Found")}, + message.UserId != null)); + return false; + } + + if (message.Name != null) + getConfigGatewayResponse.Group.Name = message.Name; + + if (message.Description != null) + getConfigGatewayResponse.Group.Description = message.Description; + + if (message.ClientIds != null) + getConfigGatewayResponse.Group.ClientIds = message.ClientIds; + + UpdateGroupGatewayResponse groupGatewayResponse = + await _groupRepository.Update(message.GroupId, getConfigGatewayResponse.Group); + + if (!groupGatewayResponse.Success) + { + outputPort.Handle(new UpdateGroupResponse(groupGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new UpdateGroupResponse(groupGatewayResponse.Group, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Invite/CreateInviteUseCase.cs b/Core.Core/UseCases/Invite/CreateInviteUseCase.cs new file mode 100644 index 0000000..236d0e1 --- /dev/null +++ b/Core.Core/UseCases/Invite/CreateInviteUseCase.cs @@ -0,0 +1,79 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Core.Interfaces.UseCases.Invite; + +namespace Api.Core.UseCases.Invite +{ + public class CreateInviteUseCase : ICreateInviteUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IInviteRepository _inviteRepository; + private readonly IPasswordGenerator _passwordGenerator; + + public CreateInviteUseCase(IOrganisationRepository organisationRepository, IInviteRepository inviteRepository, + IPasswordGenerator passwordGenerator) + { + _organisationRepository = organisationRepository; + _inviteRepository = inviteRepository; + _passwordGenerator = passwordGenerator; + } + + public async Task HandleAsync(CreateInviteRequest message, IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new CreateInviteResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.CreateOrgUsers)) + { + outputPort.Handle(new CreateInviteResponse(new[] {new Error("404", "Organisation Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + Entities.Invite invite = new Entities.Invite + { + OrganisationId = organisationGatewayResponse.Organisation.Id, + Email = message.EmailAddress, + Token = _passwordGenerator.GenerateNoSpecial() + }; + + CreateInviteGatewayResponse inviteGatewayResponse = await _inviteRepository.Create(invite); + + if (!inviteGatewayResponse.Success) + { + outputPort.Handle(new CreateInviteResponse(inviteGatewayResponse.Errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new CreateInviteResponse(inviteGatewayResponse.Invite, + organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Invite/UseInviteUseCase.cs b/Core.Core/UseCases/Invite/UseInviteUseCase.cs new file mode 100644 index 0000000..e26cc42 --- /dev/null +++ b/Core.Core/UseCases/Invite/UseInviteUseCase.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Invite; + +namespace Api.Core.UseCases.Invite +{ + public class UseInviteUseCase : IUseInviteUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IInviteRepository _inviteRepository; + + public UseInviteUseCase(IOrganisationRepository organisationRepository, IInviteRepository inviteRepository) + { + _organisationRepository = organisationRepository; + _inviteRepository = inviteRepository; + } + + public async Task HandleAsync(UseInviteRequest message, IOutputPort outputPort) + { + GetOrganisationGatewayResponse getOrganisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!getOrganisationGatewayResponse.Success) + { + outputPort.Handle( + new UseInviteResponse(getOrganisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + GetInviteGatewayResponse getInviteGatewayResponse = + await _inviteRepository.Get(getOrganisationGatewayResponse.Organisation.Id, message.Token); + + if (!getInviteGatewayResponse.Success) + { + outputPort.Handle( + new UseInviteResponse(getInviteGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (!getInviteGatewayResponse.Invite.Email.Equals(message.UserEmailAddress)) + { + outputPort.Handle( + new UseInviteResponse(new[] {new Error("404", "Organisation Not Found")}, message.UserId != null)); + return false; + } + + // As a failsafe we always attempt to delete an invite before adding the user to the organisation. + + BaseGatewayResponse deleteInviteGatewayResponse = + await _inviteRepository.Delete(getOrganisationGatewayResponse.Organisation.Id, message.Token); + + if (!deleteInviteGatewayResponse.Success) + { + outputPort.Handle(new UseInviteResponse(deleteInviteGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = new Entities.OrganisationUser + { + Id = message.UserId + }; + + List users = getOrganisationGatewayResponse.Organisation.Users.ToList(); + users.Add(organisationUser); + getOrganisationGatewayResponse.Organisation.Users = users; + + UpdateOrganisationGatewayResponse updateOrganisationGatewayResponse = + await _organisationRepository.Update(getOrganisationGatewayResponse.Organisation.Id, + getOrganisationGatewayResponse.Organisation); + + if (!updateOrganisationGatewayResponse.Success) + { + outputPort.Handle(new UseInviteResponse(updateOrganisationGatewayResponse.Errors, + message.UserId != null)); + return false; + } + + outputPort.Handle(new UseInviteResponse(getOrganisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Organisation/CreateOrganisationUseCase.cs b/Core.Core/UseCases/Organisation/CreateOrganisationUseCase.cs new file mode 100644 index 0000000..0f71ed6 --- /dev/null +++ b/Core.Core/UseCases/Organisation/CreateOrganisationUseCase.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Organisation; + +namespace Api.Core.UseCases.Organisation +{ + public class CreateOrganisationUseCase : ICreateOrganisationUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public CreateOrganisationUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(CreateOrganisationRequest message, + IOutputPort outputPort) + { + OrganisationDetails organisation = new OrganisationDetails + { + Id = Guid.NewGuid(), + Name = message.Name, + Owner = message.UserId, + Users = new List + { + new Entities.OrganisationUser + { + Id = message.UserId + } + } + }; + + CreateOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Create(organisation); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new CreateOrganisationResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + organisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + + outputPort.Handle(new CreateOrganisationResponse(organisationGatewayResponse.Organisation, organisationUser, + message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Organisation/DeleteOrganisationUseCase.cs b/Core.Core/UseCases/Organisation/DeleteOrganisationUseCase.cs new file mode 100644 index 0000000..d415a92 --- /dev/null +++ b/Core.Core/UseCases/Organisation/DeleteOrganisationUseCase.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Organisation; + +namespace Api.Core.UseCases.Organisation +{ + public class DeleteOrganisationUseCase : IDeleteOrganisationUseCase + { + private readonly IOrganisationRepository _organisationRepository; + private readonly IClientConfigRepository _clientConfigRepository; + private readonly IClientRepository _clientRepository; + private readonly IIS4ClientRepository _is4ClientRepository; + private readonly IGroupRepository _groupRepository; + + public DeleteOrganisationUseCase(IOrganisationRepository organisationRepository, IClientConfigRepository clientConfigRepository, IClientRepository clientRepository, IIS4ClientRepository is4ClientRepository, IGroupRepository groupRepository) + { + _organisationRepository = organisationRepository; + _clientConfigRepository = clientConfigRepository; + _clientRepository = clientRepository; + _is4ClientRepository = is4ClientRepository; + _groupRepository = groupRepository; + } + + public async Task HandleAsync(DeleteOrganisationRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse getOrganisationGatewayResponse = + await _organisationRepository.Get(message.Id); + + if (!getOrganisationGatewayResponse.Success) + { + outputPort.Handle( + new DeleteOrganisationResponse(getOrganisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + if (message.UserId != null && !getOrganisationGatewayResponse.Organisation.UserIsOwner(message.UserId)) + { + outputPort.Handle(new DeleteOrganisationResponse(new[] {new Error("404", "Organisation Not Found")}, + message.UserId != null)); + return false; + } + + BaseGatewayResponse organisationGatewayResponse = + await _organisationRepository.Delete(getOrganisationGatewayResponse.Organisation.Id); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle(new DeleteOrganisationResponse(organisationGatewayResponse.Errors, + message.UserId != null)); + return false; + } + + //Instead of returning when one fails, we run them all and then return all errors together. + //This allows the organisation to be cleaned up as much as possible + bool failure = false; + + BaseGatewayResponse clientConfigGatewayResponse = + await _clientConfigRepository.DeleteForOwner(getOrganisationGatewayResponse.Organisation.Id); + + if (!clientConfigGatewayResponse.Success) failure = true; + + BaseGatewayResponse is4ClientGatewayResponse = + await _is4ClientRepository.DeleteForOwner(getOrganisationGatewayResponse.Organisation.Id); + + if (!is4ClientGatewayResponse.Success) failure = true; + + BaseGatewayResponse clientGatewayResponse = + await _clientRepository.DeleteForOwner(getOrganisationGatewayResponse.Organisation.Id); + + if (!clientGatewayResponse.Success) failure = true; + + BaseGatewayResponse groupGatewayResponse = + await _groupRepository.DeleteForOwner(getOrganisationGatewayResponse.Organisation.Id); + + if (!groupGatewayResponse.Success) failure = true; + + if (failure) + { + List errors = new List(); + errors.AddRange(clientConfigGatewayResponse.Errors); + errors.AddRange(is4ClientGatewayResponse.Errors); + errors.AddRange(clientGatewayResponse.Errors); + errors.AddRange(groupGatewayResponse.Errors); + outputPort.Handle(new DeleteOrganisationResponse(errors, message.UserId != null)); + return false; + } + + outputPort.Handle(new DeleteOrganisationResponse(message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Organisation/GetOrganisationUseCase.cs b/Core.Core/UseCases/Organisation/GetOrganisationUseCase.cs new file mode 100644 index 0000000..42ae782 --- /dev/null +++ b/Core.Core/UseCases/Organisation/GetOrganisationUseCase.cs @@ -0,0 +1,54 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Organisation; + +namespace Api.Core.UseCases.Organisation +{ + public class GetOrganisationUseCase : IGetOrganisationUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public GetOrganisationUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(GetOrganisationRequest message, + IOutputPort outputPort) + { + DTO.GatewayResponses.Repositories.Organisation.GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetOrganisationResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetOrganisationResponse(new[] {new Error("404", "Organisation Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + outputPort.Handle( + new GetOrganisationResponse(organisationGatewayResponse.Organisation, organisationUser, + message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Organisation/GetOrganisationsUseCase.cs b/Core.Core/UseCases/Organisation/GetOrganisationsUseCase.cs new file mode 100644 index 0000000..1d368db --- /dev/null +++ b/Core.Core/UseCases/Organisation/GetOrganisationsUseCase.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Organisation; + +namespace Api.Core.UseCases.Organisation +{ + public class GetOrganisationsUseCase : IGetOrganisationsUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public GetOrganisationsUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(GetOrganisationsRequest message, + IOutputPort outputPort) + { + GetOrganisationsGatewayResponse organisationsGatewayResponse = + await _organisationRepository.GetForUser(message.UserId); + + if (!organisationsGatewayResponse.Success) + { + outputPort.Handle( + new GetOrganisationsResponse(organisationsGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Dictionary organisationUsers = + new Dictionary(); + + if (message.UserId != null) + foreach (OrganisationDetails organisationDetails in organisationsGatewayResponse.Organisations) + organisationUsers.Add(organisationDetails.Id, + organisationDetails.Users.First(u => u.Id == message.UserId)); + + outputPort.Handle( + new GetOrganisationsResponse(organisationsGatewayResponse.Organisations, organisationUsers, + message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/Organisation/UpdateOrganisationUseCase.cs b/Core.Core/UseCases/Organisation/UpdateOrganisationUseCase.cs new file mode 100644 index 0000000..d4ea842 --- /dev/null +++ b/Core.Core/UseCases/Organisation/UpdateOrganisationUseCase.cs @@ -0,0 +1,70 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.Organisation; + +namespace Api.Core.UseCases.Organisation +{ + public class UpdateOrganisationUseCase : IUpdateOrganisationUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public UpdateOrganisationUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(UpdateOrganisationRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse getOrganisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!getOrganisationGatewayResponse.Success) + { + outputPort.Handle( + new UpdateOrganisationResponse(getOrganisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!getOrganisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.EditOrg)) + { + outputPort.Handle(new UpdateOrganisationResponse(new[] {new Error("404", "Organisation Not Found")}, + message.UserId != null)); + return false; + } + + organisationUser = + getOrganisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + getOrganisationGatewayResponse.Organisation.Name = message.Name; + + UpdateOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Update(getOrganisationGatewayResponse.Organisation.Id, + getOrganisationGatewayResponse.Organisation); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle(new UpdateOrganisationResponse(organisationGatewayResponse.Errors, + message.UserId != null)); + return false; + } + + outputPort.Handle(new UpdateOrganisationResponse(organisationGatewayResponse.Organisation, + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/OrganisationUser/DeleteOrganisationUserUseCase.cs b/Core.Core/UseCases/OrganisationUser/DeleteOrganisationUserUseCase.cs new file mode 100644 index 0000000..39a7373 --- /dev/null +++ b/Core.Core/UseCases/OrganisationUser/DeleteOrganisationUserUseCase.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.OrganisationUser; + +namespace Api.Core.UseCases.OrganisationUser +{ + public class DeleteOrganisationUserUseCase : IDeleteOrganisationUserUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public DeleteOrganisationUserUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(DeleteOrganisationUserRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new DeleteOrganisationUserResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.DeleteOrgUsers)) + { + outputPort.Handle(new DeleteOrganisationUserResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + organisationUser = organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + if (!organisationGatewayResponse.Organisation.UserIsMember(message.OrganisationUserId) || + organisationGatewayResponse.Organisation.UserIsOwner(message.OrganisationUserId)) + { + outputPort.Handle(new DeleteOrganisationUserResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + Entities.OrganisationUser userToRemove = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.OrganisationUserId); + List users = organisationGatewayResponse.Organisation.Users.ToList(); + users.Remove(userToRemove); + organisationGatewayResponse.Organisation.Users = users; + + UpdateOrganisationGatewayResponse updateOrganisationGatewayResponse = + await _organisationRepository.Update(message.OrganisationId, organisationGatewayResponse.Organisation); + + if (!updateOrganisationGatewayResponse.Success) + { + outputPort.Handle( + new DeleteOrganisationUserResponse(updateOrganisationGatewayResponse.Errors, + message.UserId != null)); + return false; + } + + outputPort.Handle( + new DeleteOrganisationUserResponse(updateOrganisationGatewayResponse.Organisation.ToOrganisation(), organisationUser, + message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/OrganisationUser/GetOrganisationUserUseCase.cs b/Core.Core/UseCases/OrganisationUser/GetOrganisationUserUseCase.cs new file mode 100644 index 0000000..5674192 --- /dev/null +++ b/Core.Core/UseCases/OrganisationUser/GetOrganisationUserUseCase.cs @@ -0,0 +1,64 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.OrganisationUser; + +namespace Api.Core.UseCases.OrganisationUser +{ + public class GetOrganisationUserUseCase : IGetOrganisationUserUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public GetOrganisationUserUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(GetOrganisationUserRequest message, + IOutputPort outputPort) + { + DTO.GatewayResponses.Repositories.Organisation.GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetOrganisationUserResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetOrganisationUserResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + organisationUser = organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + if (!organisationGatewayResponse.Organisation.UserIsMember(message.OrganisationUserId)) + { + outputPort.Handle(new GetOrganisationUserResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + Entities.OrganisationUser requestedOrganisationUser = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.OrganisationUserId); + + outputPort.Handle( + new GetOrganisationUserResponse(requestedOrganisationUser, organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/OrganisationUser/GetOrganisationUsersUseCase.cs b/Core.Core/UseCases/OrganisationUser/GetOrganisationUsersUseCase.cs new file mode 100644 index 0000000..e02a4ca --- /dev/null +++ b/Core.Core/UseCases/OrganisationUser/GetOrganisationUsersUseCase.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.OrganisationUser; + +namespace Api.Core.UseCases.OrganisationUser +{ + public class GetOrganisationUsersUseCase : IGetOrganisationUsersUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public GetOrganisationUsersUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(GetOrganisationUsersRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new GetOrganisationUsersResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserIsMember(message.UserId)) + { + outputPort.Handle(new GetOrganisationUsersResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + organisationUser = organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + IEnumerable organisationUsers = organisationGatewayResponse.Organisation.Users; + + outputPort.Handle( + new GetOrganisationUsersResponse(organisationUsers, organisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Core/UseCases/OrganisationUser/UpdateOrganisationUserUseCase.cs b/Core.Core/UseCases/OrganisationUser/UpdateOrganisationUserUseCase.cs new file mode 100644 index 0000000..a7d423a --- /dev/null +++ b/Core.Core/UseCases/OrganisationUser/UpdateOrganisationUserUseCase.cs @@ -0,0 +1,80 @@ +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.UseCases.OrganisationUser; + +namespace Api.Core.UseCases.OrganisationUser +{ + public class UpdateOrganisationUserUseCase : IUpdateOrganisationUserUseCase + { + private readonly IOrganisationRepository _organisationRepository; + + public UpdateOrganisationUserUseCase(IOrganisationRepository organisationRepository) + { + _organisationRepository = organisationRepository; + } + + public async Task HandleAsync(UpdateOrganisationUserRequest message, + IOutputPort outputPort) + { + GetOrganisationGatewayResponse organisationGatewayResponse = + await _organisationRepository.Get(message.OrganisationId); + + if (!organisationGatewayResponse.Success) + { + outputPort.Handle( + new UpdateOrganisationUserResponse(organisationGatewayResponse.Errors, message.UserId != null)); + return false; + } + + Entities.OrganisationUser organisationUser = null; + + if (message.UserId != null) + { + if (!organisationGatewayResponse.Organisation.UserHasPermission(message.UserId, + OrganisationPermissions.EditOrgUsers)) + { + outputPort.Handle(new UpdateOrganisationUserResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + organisationUser = organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.UserId); + } + + if (!organisationGatewayResponse.Organisation.UserIsMember(message.OrganisationUserId) || + organisationGatewayResponse.Organisation.UserIsOwner(message.OrganisationUserId)) + { + outputPort.Handle(new UpdateOrganisationUserResponse( + new[] {new Error("404", "Organisation User Not Found")}, message.UserId != null)); + return false; + } + + Entities.OrganisationUser userToUpdate = + organisationGatewayResponse.Organisation.Users.First(u => u.Id == message.OrganisationUserId); + userToUpdate.Permissions = message.Permissions; + + UpdateOrganisationGatewayResponse updateOrganisationGatewayResponse = + await _organisationRepository.Update(message.OrganisationId, organisationGatewayResponse.Organisation); + + if (!updateOrganisationGatewayResponse.Success) + { + outputPort.Handle( + new UpdateOrganisationUserResponse(updateOrganisationGatewayResponse.Errors, + message.UserId != null)); + return false; + } + + outputPort.Handle( + new UpdateOrganisationUserResponse(userToUpdate, updateOrganisationGatewayResponse.Organisation.ToOrganisation(), + organisationUser, message.UserId != null, true)); + return true; + } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Core.Infrastructure.csproj b/Core.Infrastructure/Core.Infrastructure.csproj new file mode 100644 index 0000000..99ccc9e --- /dev/null +++ b/Core.Infrastructure/Core.Infrastructure.csproj @@ -0,0 +1,26 @@ + + + + netcoreapp2.2 + Api.Infrastructure + + + + + + + + + + + + + ..\..\..\..\..\Users\Asher\.nuget\packages\microsoft.extensions.options\2.1.0\lib\netstandard2.0\Microsoft.Extensions.Options.dll + + + + + + + + diff --git a/Core.Infrastructure/Data/Mapping/DataProfile.cs b/Core.Infrastructure/Data/Mapping/DataProfile.cs new file mode 100644 index 0000000..12ca4a9 --- /dev/null +++ b/Core.Infrastructure/Data/Mapping/DataProfile.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using Api.Core.Entities; +using Api.Infrastructure.Data.MongoDB.Repositories; +using AutoMapper; + +namespace Api.Infrastructure.Data.Mapping +{ + public class DataProfile : Profile + { + public DataProfile() + { + CreateMap().ConstructUsing(config => new MongoClientConfig + { + Id = config.Id, + OwnerId = config.OwnerId, + ClientEventGroups = config.ClientEventGroups, + UserEventGroups = config.UserEventGroups + }); + CreateMap().ConstructUsing(mConfig => new ClientConfig + { + Id = mConfig.Id, + OwnerId = mConfig.OwnerId, + ClientEventGroups = mConfig.ClientEventGroups, + UserEventGroups = mConfig.UserEventGroups + }); + + CreateMap().ConstructUsing((organisation, context) => new MongoOrganisation + { + Id = organisation.Id, + Name = organisation.Name, + Owner = organisation.Name, + Users = context.Mapper.Map>(organisation.Users) + }); + CreateMap().ConstructUsing((mOrganisation, context) => new OrganisationDetails + { + Id = mOrganisation.Id, + Name = mOrganisation.Name, + Owner = mOrganisation.Owner, + Users = context.Mapper.Map>(mOrganisation.Users) + }); + + CreateMap().ConstructUsing(user => new MongoOrganisationUser + { + Id = user.Id, + Permissions = user.Permissions + }); + CreateMap().ConstructUsing(mUser => new OrganisationUser + { + Id = mUser.Id, + Permissions = mUser.Permissions + }); + + CreateMap().ConstructUsing(client => new MongoChatChainClient + { + Id = client.Id, + OwnerId = client.OwnerId, + Description = client.Description, + Name = client.Name + }); + CreateMap().ConstructUsing(mClient => new Client + { + Id = mClient.Id, + OwnerId = mClient.OwnerId, + Description = mClient.Description, + Name = mClient.Name + }); + + CreateMap().ConstructUsing(client => new MongoIS4Client + { + Id = client.Id, + ClientId = client.Id, + OwnerId = client.OwnerId, + AllowedGrantTypes = client.AllowedGrantTypes, + AllowedScopes = client.AllowedScopes, + AllowOfflineAccess = client.AllowOfflineAccess + }); + CreateMap().ConstructUsing(mClient => new IS4Client + { + Id = mClient.ClientId, + OwnerId = mClient.OwnerId, + AllowedGrantTypes = mClient.AllowedGrantTypes, + AllowedScopes = mClient.AllowedScopes, + AllowOfflineAccess = mClient.AllowOfflineAccess + }); + + CreateMap().ConstructUsing(mClient => new IdentityServer4.Models.Client + { + ClientId = mClient.Id.ToString(), + AllowedGrantTypes = mClient.AllowedGrantTypes, + ClientSecrets = mClient.ClientSecrets, + AllowedScopes = mClient.AllowedScopes, + AllowOfflineAccess = mClient.AllowOfflineAccess + }); + + CreateMap().ConstructUsing(group => new MongoGroup + { + Id = group.Id, + OwnerId = group.OwnerId, + Description = group.Description, + Name = group.Name, + ClientIds = group.ClientIds + }); + CreateMap().ConstructUsing(mGroup => new Group + { + Id = mGroup.Id, + OwnerId = mGroup.OwnerId, + Description = mGroup.Description, + Name = mGroup.Name, + ClientIds = mGroup.ClientIds + }); + } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/MongoDB/MongoConnectionOptions.cs b/Core.Infrastructure/Data/MongoDB/MongoConnectionOptions.cs new file mode 100644 index 0000000..63c9fa7 --- /dev/null +++ b/Core.Infrastructure/Data/MongoDB/MongoConnectionOptions.cs @@ -0,0 +1,16 @@ +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Api.Infrastructure.Data.MongoDB +{ + public class MongoConnectionOptions + { + public class Connection + { + public string ConnectionString { get; set; } + public string DatabaseName { get; set; } + } + + public Connection ChatChainAPI { get; set; } + + public Connection IdentityServer { get; set; } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/MongoDB/Repositories/ClientConfigRepository.cs b/Core.Infrastructure/Data/MongoDB/Repositories/ClientConfigRepository.cs new file mode 100644 index 0000000..c458d22 --- /dev/null +++ b/Core.Infrastructure/Data/MongoDB/Repositories/ClientConfigRepository.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using AutoMapper; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; + +namespace Api.Infrastructure.Data.MongoDB.Repositories +{ + public class ClientConfigRepository : IClientConfigRepository + { + private readonly IMongoCollection _clientConfigs; + private readonly IMapper _mapper; + + public ClientConfigRepository(MongoConnectionOptions mongoConnectionOptions, IMapper mapper) + { + MongoClient client = new MongoClient(mongoConnectionOptions.ChatChainAPI.ConnectionString); + IMongoDatabase database = client.GetDatabase(mongoConnectionOptions.ChatChainAPI.DatabaseName); + _clientConfigs = database.GetCollection("ClientConfigs"); + _mapper = mapper; + } + + public async Task Get(Guid id) + { + try + { + IAsyncCursor cursor = await _clientConfigs.FindAsync(c => c.Id.Equals(id)); + MongoClientConfig mongoClientConfig = await cursor.FirstOrDefaultAsync(); + return mongoClientConfig == null + ? new GetClientConfigGatewayResponse(null, false, new[] {new Error("404", "Client Config Not Found")}) + : new GetClientConfigGatewayResponse(_mapper.Map(mongoClientConfig), true); + } + catch (MongoException e) + { + return new GetClientConfigGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Create(ClientConfig clientConfig) + { + try + { + MongoClientConfig mongoClientConfig = _mapper.Map(clientConfig); + await _clientConfigs.InsertOneAsync(mongoClientConfig); + return new CreateClientConfigGatewayResponse(_mapper.Map(mongoClientConfig), true); + } + catch (MongoException e) + { + return new CreateClientConfigGatewayResponse(null, false, e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Update(Guid id, ClientConfig clientConfig) + { + try + { + IAsyncCursor cursor = await _clientConfigs.FindAsync(c => c.Id.Equals(id)); + MongoClientConfig mongoClientConfig = await cursor.FirstOrDefaultAsync(); + if (mongoClientConfig == null) + return new UpdateClientConfigGatewayResponse(null, false, + new[] {new Error("404", "Client Config Not Found")}); + + mongoClientConfig = _mapper.Map(clientConfig); + await _clientConfigs.ReplaceOneAsync(config => config.Id == id, mongoClientConfig); + return new UpdateClientConfigGatewayResponse(_mapper.Map(mongoClientConfig), true); + } + catch (MongoException e) + { + return new UpdateClientConfigGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task DeleteForOwner(Guid ownerId) + { + try + { + await _clientConfigs.DeleteManyAsync(config => config.OwnerId.Equals(ownerId)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Delete(Guid id) + { + try + { + await _clientConfigs.DeleteOneAsync(config => config.Id.Equals(id)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + } + + [BsonIgnoreExtraElements] + public class MongoClientConfig + { + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + [BsonRepresentation(BsonType.String)] public Guid OwnerId { get; set; } + + [BsonRepresentation(BsonType.String)] public IEnumerable ClientEventGroups { get; set; } + + [BsonRepresentation(BsonType.String)] public IEnumerable UserEventGroups { get; set; } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/MongoDB/Repositories/ClientRepository.cs b/Core.Infrastructure/Data/MongoDB/Repositories/ClientRepository.cs new file mode 100644 index 0000000..342a0b9 --- /dev/null +++ b/Core.Infrastructure/Data/MongoDB/Repositories/ClientRepository.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.Interfaces.Gateways.Repositories; +using AutoMapper; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using Client = Api.Core.Entities.Client; + +namespace Api.Infrastructure.Data.MongoDB.Repositories +{ + public class ClientRepository : IClientRepository + { + private readonly IMongoCollection _clients; + private readonly IMapper _mapper; + + public ClientRepository(MongoConnectionOptions mongoConnectionOptions, IMapper mapper) + { + MongoClient client = new MongoClient(mongoConnectionOptions.ChatChainAPI.ConnectionString); + IMongoDatabase database = client.GetDatabase(mongoConnectionOptions.ChatChainAPI.DatabaseName); + _clients = database.GetCollection("Clients"); + _mapper = mapper; + } + + public async Task GetForOwner(Guid ownerId) + { + try + { + IAsyncCursor + cursor = await _clients.FindAsync(org => org.OwnerId.Equals(ownerId)); + return new GetClientsGatewayResponse(_mapper.Map>(await cursor.ToListAsync()), true); + } + catch (MongoException e) + { + return new GetClientsGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Get(Guid id) + { + try + { + IAsyncCursor cursor = await _clients.FindAsync(client => client.Id == id); + MongoChatChainClient mongoClient = await cursor.FirstOrDefaultAsync(); + return mongoClient == null + ? new GetClientGatewayResponse(null, false, new[] {new Error("404", "Client Not Found")}) + : new GetClientGatewayResponse(_mapper.Map(mongoClient), true); + } + catch (MongoException e) + { + return new GetClientGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Create(Client client) + { + try + { + MongoChatChainClient mongoClient = _mapper.Map(client); + await _clients.InsertOneAsync(mongoClient); + return new CreateClientGatewayResponse(_mapper.Map(mongoClient), true); + } + catch (MongoException e) + { + return new CreateClientGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Update(Guid clientId, Client client) + { + try + { + IAsyncCursor cursor = await _clients.FindAsync(org => org.Id == clientId); + MongoChatChainClient mongoClient = await cursor.FirstOrDefaultAsync(); + if (mongoClient == null) + return new UpdateClientGatewayResponse(null, false, new[] {new Error("404", "Client Not Found")}); + + mongoClient = _mapper.Map(client); + await _clients.ReplaceOneAsync(org => org.Id.Equals(clientId), mongoClient); + return new UpdateClientGatewayResponse(_mapper.Map(mongoClient), true); + } + catch (MongoException e) + { + return new UpdateClientGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task DeleteForOwner(Guid ownerId) + { + try + { + await _clients.DeleteManyAsync(config => config.OwnerId.Equals(ownerId)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Delete(Guid id) + { + try + { + await _clients.DeleteOneAsync(config => config.Id.Equals(id)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + } + + [BsonIgnoreExtraElements] + public class MongoChatChainClient + { + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + [BsonRepresentation(BsonType.String)] + public Guid OwnerId { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/MongoDB/Repositories/GroupRepository.cs b/Core.Infrastructure/Data/MongoDB/Repositories/GroupRepository.cs new file mode 100644 index 0000000..59b9549 --- /dev/null +++ b/Core.Infrastructure/Data/MongoDB/Repositories/GroupRepository.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using AutoMapper; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; + +namespace Api.Infrastructure.Data.MongoDB.Repositories +{ + public class GroupRepository : IGroupRepository + { + private readonly IMongoCollection _groups; + private readonly IMapper _mapper; + + public GroupRepository(MongoConnectionOptions mongoConnectionOptions, IMapper mapper) + { + MongoClient client = new MongoClient(mongoConnectionOptions.ChatChainAPI.ConnectionString); + IMongoDatabase database = client.GetDatabase(mongoConnectionOptions.ChatChainAPI.DatabaseName); + _groups = database.GetCollection("Groups"); + _mapper = mapper; + } + + public async Task GetForOwner(Guid ownerId) + { + try + { + IAsyncCursor + cursor = await _groups.FindAsync(group => group.OwnerId.Equals(ownerId)); + return new GetGroupsGatewayResponse(_mapper.Map>(await cursor.ToListAsync()), true); + } + catch (MongoException e) + { + return new GetGroupsGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task GetForClient(Guid clientId) + { + try + { + IAsyncCursor + cursor = await _groups.FindAsync(group => group.ClientIds.Contains(clientId)); + return new GetGroupsGatewayResponse(_mapper.Map>(await cursor.ToListAsync()), true); + } + catch (MongoException e) + { + return new GetGroupsGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Get(Guid id) + { + try + { + IAsyncCursor cursor = await _groups.FindAsync(group => group.Id == id); + MongoGroup mongoIS4Group = await cursor.FirstOrDefaultAsync(); + return mongoIS4Group == null + ? new GetGroupGatewayResponse(null, false, new[] {new Error("404", "Group Not Found")}) + : new GetGroupGatewayResponse(_mapper.Map(mongoIS4Group), true); + } + catch (MongoException e) + { + return new GetGroupGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Create(Group group) + { + try + { + MongoGroup mongoIS4Group = _mapper.Map(group); + await _groups.InsertOneAsync(mongoIS4Group); + return new CreateGroupGatewayResponse(_mapper.Map(mongoIS4Group), true); + } + catch (MongoException e) + { + return new CreateGroupGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Update(Guid groupId, Group group) + { + try + { + IAsyncCursor cursor = await _groups.FindAsync(org => org.Id == groupId); + MongoGroup mongoIS4Group = await cursor.FirstOrDefaultAsync(); + if (mongoIS4Group == null) + return new UpdateGroupGatewayResponse(null, false, new[] {new Error("404", "Group Not Found")}); + + mongoIS4Group = _mapper.Map(group); + await _groups.ReplaceOneAsync(org => org.Id.Equals(groupId), mongoIS4Group); + return new UpdateGroupGatewayResponse(_mapper.Map(mongoIS4Group), true); + } + catch (MongoException e) + { + return new UpdateGroupGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task DeleteForOwner(Guid ownerId) + { + try + { + await _groups.DeleteManyAsync(config => config.OwnerId.Equals(ownerId)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Delete(Guid id) + { + try + { + await _groups.DeleteOneAsync(config => config.Id.Equals(id)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + } + + [BsonIgnoreExtraElements] + public class MongoGroup + { + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + [BsonRepresentation(BsonType.String)] + public Guid OwnerId { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + + [BsonRepresentation(BsonType.String)] + public IList ClientIds { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/MongoDB/Repositories/IS4ClientRepository.cs b/Core.Infrastructure/Data/MongoDB/Repositories/IS4ClientRepository.cs new file mode 100644 index 0000000..bc75a1c --- /dev/null +++ b/Core.Infrastructure/Data/MongoDB/Repositories/IS4ClientRepository.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.IS4Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using AutoMapper; +using IdentityServer4.Models; +using IdentityServer4.Stores; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using Client = IdentityServer4.Models.Client; + +namespace Api.Infrastructure.Data.MongoDB.Repositories +{ + public class IS4ClientRepository : IIS4ClientRepository, IClientStore + { + private readonly IMongoCollection _clients; + private readonly IMapper _mapper; + + public IS4ClientRepository(MongoConnectionOptions mongoConnectionOptions, IMapper mapper) + { + MongoClient client = new MongoClient(mongoConnectionOptions.IdentityServer.ConnectionString); + IMongoDatabase database = client.GetDatabase(mongoConnectionOptions.IdentityServer.DatabaseName); + _clients = database.GetCollection("Client"); + _mapper = mapper; + } + + public async Task Create(IS4Client is4Client, string password) + { + try + { + MongoIS4Client mongoClient = _mapper.Map(is4Client); + mongoClient.ClientSecrets = new List{new Secret(password.Sha256())}; + await _clients.InsertOneAsync(mongoClient); + return new CreateIS4ClientGatewayResponse(true); + } + catch (MongoException e) + { + return new CreateIS4ClientGatewayResponse(false, e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task DeleteForOwner(Guid ownerId) + { + try + { + await _clients.DeleteManyAsync(config => config.OwnerId.Equals(ownerId)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Delete(Guid id) + { + try + { + await _clients.DeleteOneAsync(config => config.Id.Equals(id)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task FindClientByIdAsync(string clientId) + { + IAsyncCursor cursor = await _clients.FindAsync(c => c.Id.ToString().Equals(clientId)); + MongoIS4Client mongoIS4Client = await cursor.FirstOrDefaultAsync(); + + return _mapper.Map(mongoIS4Client); + } + } + + [BsonIgnoreExtraElements] + public class MongoIS4Client + { + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + [BsonRepresentation(BsonType.String)] + public Guid ClientId { get; set; } + + [BsonRepresentation(BsonType.String)] + public Guid OwnerId { get; set; } + + public ICollection AllowedGrantTypes { get; set; } = new List(); + + public ICollection ClientSecrets { get; set; } = new List(); + + public ICollection AllowedScopes { get; set; } = new List(); + + public bool AllowOfflineAccess { get; set; } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/MongoDB/Repositories/OrganisationRepository.cs b/Core.Infrastructure/Data/MongoDB/Repositories/OrganisationRepository.cs new file mode 100644 index 0000000..3477b74 --- /dev/null +++ b/Core.Infrastructure/Data/MongoDB/Repositories/OrganisationRepository.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using AutoMapper; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; + +namespace Api.Infrastructure.Data.MongoDB.Repositories +{ + public class OrganisationRepository : IOrganisationRepository + { + private readonly IMongoCollection _organisations; + private readonly IMapper _mapper; + + public OrganisationRepository(MongoConnectionOptions mongoConnectionOptions, IMapper mapper) + { + MongoClient client = new MongoClient(mongoConnectionOptions.ChatChainAPI.ConnectionString); + IMongoDatabase database = client.GetDatabase(mongoConnectionOptions.ChatChainAPI.DatabaseName); + _organisations = database.GetCollection("Organisations"); + _mapper = mapper; + } + + public async Task GetForUser(string userId) + { + try + { + if (userId == null) + { + IAsyncCursor cursor = await _organisations.FindAsync(org => true); + return new GetOrganisationsGatewayResponse(_mapper.Map>(await cursor.ToListAsync()), true); + } + else + { + FilterDefinition filter = Builders.Filter.ElemMatch(org => org.Users, user => user.Id.Equals(userId)); + IAsyncCursor + cursor = await _organisations.FindAsync(filter); + return new GetOrganisationsGatewayResponse(_mapper.Map>(await cursor.ToListAsync()), true); + } + } + catch (MongoException e) + { + return new GetOrganisationsGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Get(Guid id) + { + try + { + IAsyncCursor cursor = await _organisations.FindAsync(org => org.Id == id); + MongoOrganisation mongoOrganisation = await cursor.FirstOrDefaultAsync(); + return mongoOrganisation == null + ? new GetOrganisationGatewayResponse(null, false, new[] {new Error("404", "Organisation Not Found")}) + : new GetOrganisationGatewayResponse(_mapper.Map(mongoOrganisation), true); + } + catch (MongoException e) + { + return new GetOrganisationGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Create(OrganisationDetails organisation) + { + try + { + MongoOrganisation mongoOrganisation = _mapper.Map(organisation); + await _organisations.InsertOneAsync(mongoOrganisation); + return new CreateOrganisationGatewayResponse(_mapper.Map(mongoOrganisation), true); + } + catch (MongoException e) + { + return new CreateOrganisationGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Update(Guid organisationId, OrganisationDetails organisation) + { + try + { + IAsyncCursor cursor = await _organisations.FindAsync(org => org.Id == organisationId); + MongoOrganisation mongoOrganisation = await cursor.FirstOrDefaultAsync(); + if (mongoOrganisation == null) + return new UpdateOrganisationGatewayResponse(null, false, new[] {new Error("404", "Organisation Not Found")}); + + mongoOrganisation = _mapper.Map(organisation); + await _organisations.ReplaceOneAsync(org => org.Id.Equals(organisationId), mongoOrganisation); + return new UpdateOrganisationGatewayResponse(_mapper.Map(mongoOrganisation), true); + } + catch (MongoException e) + { + return new UpdateOrganisationGatewayResponse(null, false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + + public async Task Delete(Guid id) + { + try + { + await _organisations.DeleteOneAsync(config => config.Id.Equals(id)); + return new BaseGatewayResponse(true); + } + catch (MongoException e) + { + return new BaseGatewayResponse(false, + e.ErrorLabels.Select(label => new Error(e.HResult.ToString(), e.Message))); + } + } + } + + [BsonIgnoreExtraElements] + public class MongoOrganisation + { + [BsonId] + [BsonRepresentation(BsonType.String)] + public Guid Id { get; set; } + + public string Name { get; set; } + + public string Owner { get; set; } + + public IEnumerable Users { get; set; } + } + + [BsonIgnoreExtraElements] + public class MongoOrganisationUser + { + public string Id { get; set; } + + [BsonRepresentation(BsonType.String)] + public IList Permissions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/Redis/Repositories/InviteRepository.cs b/Core.Infrastructure/Data/Redis/Repositories/InviteRepository.cs new file mode 100644 index 0000000..f817ef2 --- /dev/null +++ b/Core.Infrastructure/Data/Redis/Repositories/InviteRepository.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Newtonsoft.Json; +using StackExchange.Redis; + +namespace Api.Infrastructure.Data.Redis.Repositories +{ + public class InviteRepository : IInviteRepository + { + private readonly IDatabaseAsync _redisDatabase; + + public InviteRepository(IConnectionMultiplexer redisConnection) + { + _redisDatabase = redisConnection.GetDatabase(1); + } + + public async Task Get(Guid organisationId, string token) + { + foreach (RedisValue redisValue in await _redisDatabase.SetMembersAsync(organisationId.ToString())) + { + Invite invite = JsonConvert.DeserializeObject(redisValue); + if (invite.Token.Equals(token)) + return new GetInviteGatewayResponse(invite, true); + } + return new GetInviteGatewayResponse(null, false, new[] {new Error("404", "Invite Not Found")}); + } + + public async Task Create(Invite invite) + { + foreach (RedisValue redisValue in await _redisDatabase.SetMembersAsync(invite.OrganisationId.ToString())) + { + Invite orgInvite = JsonConvert.DeserializeObject(redisValue); + if (invite.Email != orgInvite.Email) continue; + + await _redisDatabase.SetRemoveAsync(invite.OrganisationId.ToString(), redisValue); + break; + } + await _redisDatabase.SetAddAsync(invite.OrganisationId.ToString(), JsonConvert.SerializeObject(invite)); + await _redisDatabase.KeyExpireAsync(invite.OrganisationId.ToString(), DateTime.Now.AddDays(1)); + + return new CreateInviteGatewayResponse(invite, true); + } + + public async Task Delete(Guid organisationId, string token) + { + foreach (RedisValue redisValue in await _redisDatabase.SetMembersAsync(organisationId.ToString())) + { + Invite orgInvite = JsonConvert.DeserializeObject(redisValue); + if (orgInvite.Token != token) continue; + + await _redisDatabase.SetRemoveAsync(organisationId.ToString(), redisValue); + return new BaseGatewayResponse(true); + } + return new BaseGatewayResponse(false, new[] {new Error("404", "Invite Not Found")}); + } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/Data/Services/PasswordGenerator.cs b/Core.Infrastructure/Data/Services/PasswordGenerator.cs new file mode 100644 index 0000000..6aa0817 --- /dev/null +++ b/Core.Infrastructure/Data/Services/PasswordGenerator.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Api.Core.Interfaces.Services; +// ReSharper disable StringLiteralTypo + +namespace Api.Infrastructure.Data.Services +{ + public class PasswordGenerator : IPasswordGenerator + { + public string Generate( + int requiredLengthMin = 60, + int requiredLengthMax = 68, + int requiredUniqueChars = 8, + bool requireDigit = true, + bool requireLowercase = true, + bool requireNonAlphanumeric = true, + bool requireUppercase = true) + { + string[] randomChars = { + "ABCDEFGHJKLMNOPQRSTUVWXYZ", // uppercase + "abcdefghijkmnopqrstuvwxyz", // lowercase + "0123456789", // digits + "!@$?_-" // non-alphanumeric + }; + Random rand = new Random(Environment.TickCount); + List chars = new List(); + + if (requireUppercase) + chars.Insert(rand.Next(0, chars.Count), + randomChars[0][rand.Next(0, randomChars[0].Length)]); + + if (requireLowercase) + chars.Insert(rand.Next(0, chars.Count), + randomChars[1][rand.Next(0, randomChars[1].Length)]); + + if (requireDigit) + chars.Insert(rand.Next(0, chars.Count), + randomChars[2][rand.Next(0, randomChars[2].Length)]); + + if (requireNonAlphanumeric) + chars.Insert(rand.Next(0, chars.Count), + randomChars[3][rand.Next(0, randomChars[3].Length)]); + + int requiredLength = rand.Next(requiredLengthMin, requiredLengthMax); + + for (int i = chars.Count; i < requiredLength + || chars.Distinct().Count() < requiredUniqueChars; i++) + { + string rcs = randomChars[rand.Next(0, randomChars.Length)]; + chars.Insert(rand.Next(0, chars.Count), + rcs[rand.Next(0, rcs.Length)]); + } + + return new string(chars.ToArray()); + } + + public string GenerateNoSpecial( + int requiredLengthMin = 60, + int requiredLengthMax = 68, + int requiredUniqueChars = 8, + bool requireDigit = true, + bool requireLowercase = true, + bool requireUppercase = true) + { + string[] randomChars = { + "ABCDEFGHJKLMNOPQRSTUVWXYZ", // uppercase + "abcdefghijkmnopqrstuvwxyz", // lowercase + "0123456789" // digits + }; + Random rand = new Random(Environment.TickCount); + List chars = new List(); + + if (requireUppercase) + chars.Insert(rand.Next(0, chars.Count), + randomChars[0][rand.Next(0, randomChars[0].Length)]); + + if (requireLowercase) + chars.Insert(rand.Next(0, chars.Count), + randomChars[1][rand.Next(0, randomChars[1].Length)]); + + if (requireDigit) + chars.Insert(rand.Next(0, chars.Count), + randomChars[2][rand.Next(0, randomChars[2].Length)]); + + int requiredLength = rand.Next(requiredLengthMin, requiredLengthMax); + + for (int i = chars.Count; i < requiredLength + || chars.Distinct().Count() < requiredUniqueChars; i++) + { + string rcs = randomChars[rand.Next(0, randomChars.Length)]; + chars.Insert(rand.Next(0, chars.Count), + rcs[rand.Next(0, rcs.Length)]); + } + + return new string(chars.ToArray()); + } + } +} \ No newline at end of file diff --git a/Core.Infrastructure/InfrastructureModule.cs b/Core.Infrastructure/InfrastructureModule.cs new file mode 100644 index 0000000..04d1e0d --- /dev/null +++ b/Core.Infrastructure/InfrastructureModule.cs @@ -0,0 +1,24 @@ +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Infrastructure.Data.MongoDB.Repositories; +using Api.Infrastructure.Data.Redis.Repositories; +using Api.Infrastructure.Data.Services; +using Autofac; + +namespace Api.Infrastructure +{ + public class InfrastructureModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + + builder.RegisterType().As().InstancePerLifetimeScope(); + builder.RegisterType().As().InstancePerLifetimeScope(); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Core.Tests.csproj b/Core.Tests/Core.Tests.csproj new file mode 100644 index 0000000..d42bd1b --- /dev/null +++ b/Core.Tests/Core.Tests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.2 + + false + + Api.Tests + + + + + + + + + + + + + + diff --git a/Core.Tests/MockObjects/MockOutputPort.cs b/Core.Tests/MockObjects/MockOutputPort.cs new file mode 100644 index 0000000..598c104 --- /dev/null +++ b/Core.Tests/MockObjects/MockOutputPort.cs @@ -0,0 +1,15 @@ +using Api.Core.Interfaces; + +namespace Api.Tests.MockObjects +{ + public class MockOutputPort : IOutputPort + where TUseCaseResponseMessage : UseCaseResponseMessage + { + public TUseCaseResponseMessage Response { get; private set; } + + public void Handle(TUseCaseResponseMessage response) + { + Response = response; + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientConfigTests/GetTests.cs b/Core.Tests/Permissions/ClientConfigTests/GetTests.cs new file mode 100644 index 0000000..bd8d36d --- /dev/null +++ b/Core.Tests/Permissions/ClientConfigTests/GetTests.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.ClientConfig; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientConfigTests +{ + public class GetTests + { + [Fact] + public async Task GetClientConfig_NonOrgUser_False() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Guid configId = Guid.NewGuid(); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + Id = configId, + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new GetClientConfigRequest(Guid.NewGuid().ToString(), Guid.NewGuid(), Guid.NewGuid()), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClientConfig_OrgUser_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Guid configId = Guid.NewGuid(); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + Id = configId, + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetClientConfigRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task GetClientConfig_OrgOwner_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = userId.ToString(), + Id = orgId, + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Guid configId = Guid.NewGuid(); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + Id = configId, + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetClientConfigRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientConfigTests/UpdateTests.cs b/Core.Tests/Permissions/ClientConfigTests/UpdateTests.cs new file mode 100644 index 0000000..78ae32e --- /dev/null +++ b/Core.Tests/Permissions/ClientConfigTests/UpdateTests.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.ClientConfig; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientConfigTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateClientConfig_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), new List(), + new List()), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task UpdateClientConfig_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), new List(), + new List()), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.All)] + public async Task UpdateClientConfig_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), new List(), + new List()), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + + [Fact] + public async Task UpdateClientConfig_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock ClientConfig with ownerId set to OrgId + ClientConfig mockClientConfig = new ClientConfig + { + OwnerId = orgId + }; + + // Mock ClientConfig Repo that returns mockClientConfig + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // The Use Case we are testing (UpdateClientConfig) + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), new List(), + new List()), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientTests/CreateTests.cs b/Core.Tests/Permissions/ClientTests/CreateTests.cs new file mode 100644 index 0000000..95999d7 --- /dev/null +++ b/Core.Tests/Permissions/ClientTests/CreateTests.cs @@ -0,0 +1,396 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.IS4Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientTests +{ + public class CreateTests + { + [Fact] + public async Task CreateClient_NonOrgUser_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateClientRequest(Guid.NewGuid().ToString(), orgId, clientName, clientDescription), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + [InlineData(OrganisationPermissions.EditClients)] + public async Task CreateClient_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateClientRequest(userId.ToString(), orgId, clientName, clientDescription), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.All)] + public async Task CreateClient_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateClientRequest(userId.ToString(), orgId, clientName, clientDescription), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task CreateClient_OrgOwner_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateClientRequest(userId.ToString(), orgId, clientName, clientDescription), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientTests/DeleteTests.cs b/Core.Tests/Permissions/ClientTests/DeleteTests.cs new file mode 100644 index 0000000..6f7b281 --- /dev/null +++ b/Core.Tests/Permissions/ClientTests/DeleteTests.cs @@ -0,0 +1,411 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteClient_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteClientRequest(userId.ToString(), Guid.NewGuid(), mockClient.Id), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + Assert.False(configDeleteRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task DeleteClient_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteClientRequest(userId.ToString(), Guid.NewGuid(), mockClient.Id), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + Assert.False(configDeleteRan); + } + + [Theory] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.All)] + public async Task DeleteClient_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteClientRequest(userId.ToString(), Guid.NewGuid(), mockClient.Id), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(deleteRan); + Assert.True(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteClientRequest(userId.ToString(), Guid.NewGuid(), mockClient.Id), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(deleteRan); + Assert.True(configDeleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientTests/GetTests.cs b/Core.Tests/Permissions/ClientTests/GetTests.cs new file mode 100644 index 0000000..8b10484 --- /dev/null +++ b/Core.Tests/Permissions/ClientTests/GetTests.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientTests +{ + public class GetTests + { + [Fact] + public async Task GetClient_NonOrgUser_False() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new GetClientRequest(Guid.NewGuid().ToString(), Guid.NewGuid(), Guid.NewGuid()), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClient_OrgUser_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetClientRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task GetClient_OrgOwner_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = userId.ToString(), + Id = orgId, + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetClientRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientTests/GetsTests.cs b/Core.Tests/Permissions/ClientTests/GetsTests.cs new file mode 100644 index 0000000..b0fdb61 --- /dev/null +++ b/Core.Tests/Permissions/ClientTests/GetsTests.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientTests +{ + public class GetsTests + { + [Fact] + public async Task GetClients_NonOrgUser_False() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.GetForOwner(It.IsAny())) + .ReturnsAsync(new GetClientsGatewayResponse(new List {mockClient}, true)); + + // The Use Case we are testing (UpdateClient) + GetClientsUseCase useCase = + new GetClientsUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync(new GetClientsRequest(Guid.NewGuid().ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Clients Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClients_OrgUser_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.GetForOwner(It.IsAny())) + .ReturnsAsync(new GetClientsGatewayResponse(new List {mockClient}, true)); + + // The Use Case we are testing (UpdateClient) + GetClientsUseCase useCase = + new GetClientsUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync(new GetClientsRequest(userId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/ClientTests/UpdateTests.cs b/Core.Tests/Permissions/ClientTests/UpdateTests.cs new file mode 100644 index 0000000..3d67f67 --- /dev/null +++ b/Core.Tests/Permissions/ClientTests/UpdateTests.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.ClientTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateClient_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + UpdateClientUseCase useCase = new UpdateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", ""), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task UpdateClient_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + UpdateClientUseCase useCase = new UpdateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", ""), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.All)] + public async Task UpdateClient_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + UpdateClientUseCase useCase = new UpdateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", ""), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task UpdateClient_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Client with ownerId set to OrgId + Client mockClient = new Client + { + OwnerId = orgId + }; + + // Mock Client Repo that returns mockClient + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // The Use Case we are testing (UpdateClient) + UpdateClientUseCase useCase = new UpdateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateClientRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", ""), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/GroupTests/CreateTests.cs b/Core.Tests/Permissions/GroupTests/CreateTests.cs new file mode 100644 index 0000000..a5e6b58 --- /dev/null +++ b/Core.Tests/Permissions/GroupTests/CreateTests.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.GroupTests +{ + public class CreateTests + { + [Fact] + public async Task CreateGroup_NonOrgUser_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateGroupRequest(Guid.NewGuid().ToString(), orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task CreateGroup_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateGroupRequest(userId.ToString(), orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.All)] + public async Task CreateGroup_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateGroupRequest(userId.ToString(), orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task CreateGroup_OrgOwner_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new CreateGroupRequest(userId.ToString(), orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/GroupTests/DeleteTests.cs b/Core.Tests/Permissions/GroupTests/DeleteTests.cs new file mode 100644 index 0000000..d96958d --- /dev/null +++ b/Core.Tests/Permissions/GroupTests/DeleteTests.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.GroupTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteGroup_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (DeleteGroup) + DeleteGroupUseCase useCase = new DeleteGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteGroupRequest(userId.ToString(), Guid.NewGuid(), mockGroup.Id), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task DeleteGroup_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (DeleteGroup) + DeleteGroupUseCase useCase = new DeleteGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteGroupRequest(userId.ToString(), Guid.NewGuid(), mockGroup.Id), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + } + + [Theory] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.All)] + public async Task DeleteGroup_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (DeleteGroup) + DeleteGroupUseCase useCase = new DeleteGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteGroupRequest(userId.ToString(), Guid.NewGuid(), mockGroup.Id), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(deleteRan); + } + + [Fact] + public async Task DeleteGroup_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (DeleteGroup) + DeleteGroupUseCase useCase = new DeleteGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteGroupRequest(userId.ToString(), Guid.NewGuid(), mockGroup.Id), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(deleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/GroupTests/GetTests.cs b/Core.Tests/Permissions/GroupTests/GetTests.cs new file mode 100644 index 0000000..6d15e6f --- /dev/null +++ b/Core.Tests/Permissions/GroupTests/GetTests.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.GroupTests +{ + public class GetTests + { + [Fact] + public async Task GetGroup_NonOrgUser_False() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new GetGroupRequest(Guid.NewGuid().ToString(), Guid.NewGuid(), Guid.NewGuid()), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetGroup_OrgUser_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetGroupRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task GetGroup_OrgOwner_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = userId.ToString(), + Id = orgId, + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetGroupRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/GroupTests/GetsTests.cs b/Core.Tests/Permissions/GroupTests/GetsTests.cs new file mode 100644 index 0000000..e15d2fb --- /dev/null +++ b/Core.Tests/Permissions/GroupTests/GetsTests.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.GroupTests +{ + public class GetsTests + { + [Fact] + public async Task GetGroups_NonOrgUser_False() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.GetForOwner(It.IsAny())) + .ReturnsAsync(new GetGroupsGatewayResponse(new List {mockGroup}, true)); + + // The Use Case we are testing (UpdateGroup) + GetGroupsUseCase useCase = + new GetGroupsUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync(new GetGroupsRequest(Guid.NewGuid().ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Groups Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetGroups_OrgUser_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.GetForOwner(It.IsAny())) + .ReturnsAsync(new GetGroupsGatewayResponse(new List {mockGroup}, true)); + + // The Use Case we are testing (UpdateGroup) + GetGroupsUseCase useCase = + new GetGroupsUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetGroupsRequest(userId.ToString(), Guid.NewGuid()), mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/GroupTests/UpdateTests.cs b/Core.Tests/Permissions/GroupTests/UpdateTests.cs new file mode 100644 index 0000000..46990a6 --- /dev/null +++ b/Core.Tests/Permissions/GroupTests/UpdateTests.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.GroupTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateGroup_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + UpdateGroupUseCase useCase = new UpdateGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateGroupRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", "", new List()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task UpdateGroup_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + UpdateGroupUseCase useCase = new UpdateGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateGroupRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", "", new List()), + mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.All)] + public async Task UpdateGroup_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + UpdateGroupUseCase useCase = new UpdateGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateGroupRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", "", new List()), + mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + + [Fact] + public async Task UpdateGroup_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Mock Group with ownerId set to OrgId + Group mockGroup = new Group + { + OwnerId = orgId + }; + + // Mock Group Repo that returns mockGroup + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // The Use Case we are testing (UpdateGroup) + UpdateGroupUseCase useCase = new UpdateGroupUseCase(mockOrganisationRepository.Object, + mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateGroupRequest(userId.ToString(), Guid.NewGuid(), Guid.NewGuid(), "", "", new List()), + mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/InviteTests/CreateTests.cs b/Core.Tests/Permissions/InviteTests/CreateTests.cs new file mode 100644 index 0000000..6361a9c --- /dev/null +++ b/Core.Tests/Permissions/InviteTests/CreateTests.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Core.UseCases.Invite; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.InviteTests +{ + public class CreateTests + { + [Fact] + public async Task CreateInvite_NonOrgUser_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Id = orgId + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(mockInvite, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(Guid.NewGuid().ToString(), orgId, inviteEmailAddress), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task CreateInvite_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(mockInvite, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(userId.ToString(), orgId, inviteEmailAddress), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Theory] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.All)] + public async Task CreateInvite_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(mockInvite, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(userId.ToString(), orgId, inviteEmailAddress), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task CreateInvite_OrgOwner_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(mockInvite, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(userId.ToString(), orgId, inviteEmailAddress), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/InviteTests/UseTests.cs b/Core.Tests/Permissions/InviteTests/UseTests.cs new file mode 100644 index 0000000..8eb887b --- /dev/null +++ b/Core.Tests/Permissions/InviteTests/UseTests.cs @@ -0,0 +1,162 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Invite; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.InviteTests +{ + public class UseTests + { + [Fact] + public async Task UseInvite_NonOrgUser_True() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(mockInvite, true)); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UseInviteRequest(Guid.NewGuid().ToString(), orgId, inviteEmailAddress, inviteToken), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(inviteDeleteRan); + Assert.True(orgUpdateRan); + } + + [Fact] + public async Task UseInvite_NotMatchingEmailAddress_False() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId + }; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(mockInvite, true)); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new UseInviteRequest(Guid.NewGuid().ToString(), orgId, "", inviteToken), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(orgUpdateRan); + Assert.False(inviteDeleteRan); + + // For the first "control" test we'll assert these variables to double check that everything is working. + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationTests/DeleteTests.cs b/Core.Tests/Permissions/OrganisationTests/DeleteTests.cs new file mode 100644 index 0000000..79fd958 --- /dev/null +++ b/Core.Tests/Permissions/OrganisationTests/DeleteTests.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteOrganisation_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + Guid ownerId = Guid.NewGuid(); + + // Mock Organisation with ownerId set to OrgId + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = ownerId.ToString() + }; + + // Mock Organisation Repo that returns mockOrganisation + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationRequest(userId.ToString(), Guid.NewGuid()), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + Assert.False(clientConfigDeleteRan); + Assert.False(is4ClientDeleteRan); + Assert.False(clientDeleteRan); + Assert.False(groupDeleteRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + [InlineData(OrganisationPermissions.All)] + public async Task DeleteOrganisation_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationRequest(userId.ToString(), Guid.NewGuid()), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + Assert.False(clientConfigDeleteRan); + Assert.False(is4ClientDeleteRan); + Assert.False(clientDeleteRan); + Assert.False(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationRequest(userId.ToString(), Guid.NewGuid()), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationTests/GetTests.cs b/Core.Tests/Permissions/OrganisationTests/GetTests.cs new file mode 100644 index 0000000..9527d37 --- /dev/null +++ b/Core.Tests/Permissions/OrganisationTests/GetTests.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationTests +{ + public class GetTests + { + [Fact] + public async Task GetOrganisation_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync( + new GetOrganisationGatewayResponse(new OrganisationDetails {Owner = Guid.NewGuid().ToString()}, + true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUseCase useCase = new GetOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync(new GetOrganisationRequest(userId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetOrganisation_OrgUser_True() + { + // Arrange \\ + + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUseCase useCase = new GetOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync(new GetOrganisationRequest(userId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task GetOrganisation_OrgOwner_True() + { + // Arrange \\ + + Guid ownerId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = ownerId.ToString(), + Users = new[] {new OrganisationUser {Id = ownerId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUseCase useCase = new GetOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync(new GetOrganisationRequest(ownerId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationTests/UpdateTests.cs b/Core.Tests/Permissions/OrganisationTests/UpdateTests.cs new file mode 100644 index 0000000..469987d --- /dev/null +++ b/Core.Tests/Permissions/OrganisationTests/UpdateTests.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateOrganisation_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + Guid ownerId = Guid.NewGuid(); + + // Mock Organisation with ownerId set to OrgId + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = ownerId.ToString() + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisation) + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationRequest(userId.ToString(), Guid.NewGuid(), ""), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task UpdateOrganisation_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisation) + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationRequest(userId.ToString(), Guid.NewGuid(), ""), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.All)] + public async Task UpdateOrganisation_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisation) + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationRequest(userId.ToString(), Guid.NewGuid(), ""), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + + [Fact] + public async Task UpdateOrganisation_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] {new OrganisationUser {Id = userId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisation) + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationRequest(userId.ToString(), Guid.NewGuid(), ""), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationUserTests/DeleteTests.cs b/Core.Tests/Permissions/OrganisationUserTests/DeleteTests.cs new file mode 100644 index 0000000..fac48ee --- /dev/null +++ b/Core.Tests/Permissions/OrganisationUserTests/DeleteTests.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationUserTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteOrganisationUser_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + Guid deleteUserId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List {new OrganisationUser {Id = deleteUserId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (DeleteOrganisationUser) + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), deleteUserId.ToString()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + public async Task DeleteOrganisationUser_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + Guid deleteUserId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + }, + new OrganisationUser + { + Id = deleteUserId.ToString() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (DeleteOrganisationUser) + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), deleteUserId.ToString()), + mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.All)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task DeleteOrganisationUser_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + Guid deleteUserId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + }, + new OrganisationUser + { + Id = deleteUserId.ToString() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (DeleteOrganisationUser) + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), deleteUserId.ToString()), + mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + + [Fact] + public async Task DeleteOrganisationUser_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + Guid deleteUserId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] + {new OrganisationUser {Id = userId.ToString()}, new OrganisationUser {Id = deleteUserId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (DeleteOrganisationUser) + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new DeleteOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), deleteUserId.ToString()), + mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationUserTests/GetTests.cs b/Core.Tests/Permissions/OrganisationUserTests/GetTests.cs new file mode 100644 index 0000000..1d50cc2 --- /dev/null +++ b/Core.Tests/Permissions/OrganisationUserTests/GetTests.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationUserTests +{ + public class GetTests + { + [Fact] + public async Task GetOrganisationUser_NonOrgUser_False() + { + // Arrange \\ + Guid requestedUserId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = requestedUserId.ToString(), + Users = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUserUseCase useCase = new GetOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new GetOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetOrganisationUser_OrgUser_True() + { + // Arrange \\ + + Guid requestedUserId = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Users = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = new List() + }, + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUserUseCase useCase = new GetOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new GetOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task GetOrganisationUser_OrgOwner_True() + { + // Arrange \\ + + Guid requestedUserId = Guid.NewGuid(); + Guid ownerId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = ownerId.ToString(), + Users = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = new List() + }, + new OrganisationUser + { + Id = ownerId.ToString() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUserUseCase useCase = new GetOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new GetOrganisationUserRequest(ownerId.ToString(), Guid.NewGuid(), requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationUserTests/GetsTests.cs b/Core.Tests/Permissions/OrganisationUserTests/GetsTests.cs new file mode 100644 index 0000000..681fced --- /dev/null +++ b/Core.Tests/Permissions/OrganisationUserTests/GetsTests.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationUserTests +{ + public class GetsTests + { + [Fact] + public async Task GetOrganisationUsers_NonOrgUser_False() + { + // Arrange \\ + Guid requestedUserId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = requestedUserId.ToString(), + Users = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUsersUseCase useCase = new GetOrganisationUsersUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetOrganisationUsersRequest(userId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetOrganisationUsers_OrgUser_True() + { + // Arrange \\ + + Guid requestedUserId = Guid.NewGuid(); + Guid userId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = Guid.NewGuid().ToString(), + Users = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = new List() + }, + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUsersUseCase useCase = new GetOrganisationUsersUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetOrganisationUsersRequest(userId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + + [Fact] + public async Task GetOrganisationUsers_OrgOwner_True() + { + // Arrange \\ + + Guid requestedUserId = Guid.NewGuid(); + Guid ownerId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Owner = ownerId.ToString(), + Users = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = new List() + }, + new OrganisationUser + { + Id = ownerId.ToString() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateClientConfig) + GetOrganisationUsersUseCase useCase = new GetOrganisationUsersUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = + await useCase.HandleAsync(new GetOrganisationUsersRequest(ownerId.ToString(), Guid.NewGuid()), + mockOutputPort); + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + } + } +} \ No newline at end of file diff --git a/Core.Tests/Permissions/OrganisationUserTests/UpdateTests.cs b/Core.Tests/Permissions/OrganisationUserTests/UpdateTests.cs new file mode 100644 index 0000000..bb94cc5 --- /dev/null +++ b/Core.Tests/Permissions/OrganisationUserTests/UpdateTests.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.Permissions.OrganisationUserTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateOrganisationUser_NonOrgUser_False() + { + // Arrange \\ + + // GUID of org we're testing with, + Guid orgId = Guid.NewGuid(); + + Guid updateUserId = Guid.NewGuid(); + + // Mock organisation with ID set + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List {new OrganisationUser {Id = updateUserId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisationUser) + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // GUID of the anonymous user we're testing with + Guid userId = Guid.NewGuid(); + + // Act \\ + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), updateUserId.ToString(), + new List()), mockOutputPort); + + // Assert \\ + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.EditGroups)] + [InlineData(OrganisationPermissions.EditClients)] + [InlineData(OrganisationPermissions.EditOrg)] + [InlineData(OrganisationPermissions.CreateGroups)] + [InlineData(OrganisationPermissions.CreateClients)] + [InlineData(OrganisationPermissions.CreateOrgUsers)] + [InlineData(OrganisationPermissions.DeleteClients)] + [InlineData(OrganisationPermissions.DeleteGroups)] + [InlineData(OrganisationPermissions.DeleteOrgUsers)] + public async Task UpdateOrganisationUser_WrongPermission_False(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + Guid updateUserId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + }, + new OrganisationUser + { + Id = updateUserId.ToString() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisationUser) + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), updateUserId.ToString(), + new List()), mockOutputPort); + + // Assert + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Theory] + [InlineData(OrganisationPermissions.All)] + [InlineData(OrganisationPermissions.EditOrgUsers)] + public async Task UpdateOrganisationUser_CorrectPermission_True(OrganisationPermissions permission) + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + Guid updateUserId = Guid.NewGuid(); + + // Mock organisation with User set to userId with No permissions + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = new List + { + new OrganisationUser + { + Id = userId.ToString(), + Permissions = new List + { + permission + } + }, + new OrganisationUser + { + Id = updateUserId.ToString() + } + } + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisationUser) + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), updateUserId.ToString(), + new List()), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + + [Fact] + public async Task UpdateOrganisationUser_OrgOwner_True() + { + // Arrange + + // GUID of org we're testing with + Guid orgId = Guid.NewGuid(); + + // GUID of user we're testing with + Guid userId = Guid.NewGuid(); + + Guid updateUserId = Guid.NewGuid(); + + // Mock organisation with Owner set as UserID + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = userId.ToString(), + Users = new[] + {new OrganisationUser {Id = userId.ToString()}, new OrganisationUser {Id = updateUserId.ToString()}} + }; + + // Mock Organisation Repo that returns mockOrganisation + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // The Use Case we are testing (UpdateOrganisationUser) + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + //3. The output port is the mechanism to pass response data from the use case to a Presenter + //for final preparation to deliver to the UI/web page/api response etc. + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act + + //3. We need a request model to cary the data into the use case for the upper layer (UI, Controller, etc.) + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(userId.ToString(), Guid.NewGuid(), updateUserId.ToString(), + new List()), mockOutputPort); + + // Assert + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientConfigTests/GetTests.cs b/Core.Tests/UseCases/ClientConfigTests/GetTests.cs new file mode 100644 index 0000000..84fa616 --- /dev/null +++ b/Core.Tests/UseCases/ClientConfigTests/GetTests.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.ClientConfig; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientConfigTests +{ + public class GetTests + { + [Fact] + public async Task GetClientConfig_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + IEnumerable clientEventGroups = new List {Guid.NewGuid()}; + IEnumerable userEventGroups = new List {Guid.NewGuid()}; + + ClientConfig mockClientConfig = new ClientConfig + { + Id = configId, + OwnerId = orgId, + ClientEventGroups = clientEventGroups, + UserEventGroups = userEventGroups + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientConfigRequest(orgId, configId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.ClientConfig.Id, configId); + + // Assert ClientConfig Values + Assert.Equal(mockOutputPort.Response.ClientConfig.OwnerId, orgId); + Assert.Equal(mockOutputPort.Response.ClientConfig.ClientEventGroups, clientEventGroups); + Assert.Equal(mockOutputPort.Response.ClientConfig.UserEventGroups, userEventGroups); + } + + [Fact] + public async Task GetClientConfig_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + ClientConfig mockClientConfig = new ClientConfig {Id = configId, OwnerId = orgId}; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientConfigRequest(orgId, configId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClientConfig_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(null, false, + new[] {new Error("404", "Client Config Not Found")})); + + // Use Case and OutputPort + + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientConfigRequest(orgId, configId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClientConfig_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + ClientConfig mockClientConfig = new ClientConfig {Id = configId, OwnerId = Guid.NewGuid()}; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + GetClientConfigUseCase useCase = + new GetClientConfigUseCase(mockOrganisationRepository.Object, mockClientConfigRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientConfigRequest(orgId, configId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientConfigTests/UpdateTests.cs b/Core.Tests/UseCases/ClientConfigTests/UpdateTests.cs new file mode 100644 index 0000000..33b7211 --- /dev/null +++ b/Core.Tests/UseCases/ClientConfigTests/UpdateTests.cs @@ -0,0 +1,323 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.ClientConfig; +using Api.Core.DTO.UseCaseResponses.ClientConfig; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.ClientConfig; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientConfigTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateClientConfig_True() + { + // Arrange \\ + + // Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + IEnumerable clientEventGroups = new List {Guid.NewGuid()}; + IEnumerable userEventGroups = new List {Guid.NewGuid()}; + + ClientConfig mockClientConfig = new ClientConfig + { + Id = configId, + OwnerId = orgId, + ClientEventGroups = clientEventGroups, + UserEventGroups = userEventGroups + }; + + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(configId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + + // Act \\ + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(orgId, configId, clientEventGroups, userEventGroups), mockOutputPort); + + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.Message); + + Assert.True(updateRan); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.ClientConfig.Id, configId); + + // Assert Updated Values. + Assert.Equal(mockOutputPort.Response.ClientConfig.ClientEventGroups, clientEventGroups); + Assert.Equal(mockOutputPort.Response.ClientConfig.UserEventGroups, userEventGroups); + } + + [Fact] + public async Task UpdateClientConfig_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + ClientConfig mockClientConfig = new ClientConfig {Id = configId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(configId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(orgId, configId, new List(), new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateClientConfig_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + ClientConfig mockClientConfig = new ClientConfig {Id = configId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(configId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(null, false, + new[] {new Error("404", "Client Config Not Found")})); + + // Use Case and OutputPort + + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(orgId, configId, new List(), new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateClientConfig_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + ClientConfig mockClientConfig = new ClientConfig {Id = configId, OwnerId = Guid.NewGuid()}; + + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(configId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(mockClientConfig, true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(orgId, configId, new List(), new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateClientConfig_RepoUpdateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //ClientConfig Arrange + + Guid configId = Guid.NewGuid(); + + ClientConfig mockClientConfig = new ClientConfig {Id = configId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(configId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientConfigGatewayResponse(null, false, + new[] {new Error("500", "Client Config Update Failed")})); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(configId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + UpdateClientConfigUseCase useCase = new UpdateClientConfigUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateClientConfigRequest(orgId, configId, new List(), new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Update Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientTests/CreateTests.cs b/Core.Tests/UseCases/ClientTests/CreateTests.cs new file mode 100644 index 0000000..0a43beb --- /dev/null +++ b/Core.Tests/UseCases/ClientTests/CreateTests.cs @@ -0,0 +1,440 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.IS4Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientTests +{ + public class CreateTests + { + [Fact] + public async Task CreateClient_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + + IEnumerable clientEventGroups = new List {Guid.NewGuid()}; + IEnumerable userEventGroups = new List {Guid.NewGuid()}; + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId, + ClientEventGroups = clientEventGroups, + UserEventGroups = userEventGroups + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new CreateClientRequest(orgId, clientName, clientDescription), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Client.OwnerId, orgId); + Assert.Equal(mockOutputPort.Response.ClientConfig.OwnerId, orgId); + + // Assert Password Value + Assert.Equal(mockOutputPort.Response.Password, password); + + // Assert Client Values + Assert.Equal(mockOutputPort.Response.Client.Id, clientId); + Assert.Equal(mockOutputPort.Response.Client.Name, clientName); + Assert.Equal(mockOutputPort.Response.Client.Description, clientDescription); + + // Assert Client Config Values + Assert.Equal(mockOutputPort.Response.ClientConfig.Id, clientId); + Assert.Equal(mockOutputPort.Response.ClientConfig.ClientEventGroups, clientEventGroups); + Assert.Equal(mockOutputPort.Response.ClientConfig.UserEventGroups, userEventGroups); + } + + [Fact] + public async Task CreateClient_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new CreateClientRequest(orgId, clientName, clientDescription), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task CreateClient_IS4ClientRepoCreateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(false, new[] {new Error("500", "IS4Client Create Failed")})); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new CreateClientRequest(orgId, clientName, clientDescription), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("IS4Client Create Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task CreateClient_ClientRepoCreateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(null, false, + new[] {new Error("500", "Client Create Failed")})); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(mockClientConfig, true)); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new CreateClientRequest(orgId, clientName, clientDescription), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Create Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task CreateClient_ClientConfigRepoCreateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + const string password = "123"; + + Guid clientId = Guid.NewGuid(); + + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Create(It.IsAny(), It.IsAny())) + .ReturnsAsync(new CreateIS4ClientGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientGatewayResponse(mockClient, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(password); + + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateClientConfigGatewayResponse(null, false, + new[] {new Error("500", "Client Config Create Failed")})); + // Use Case and OutputPort + + CreateClientUseCase useCase = new CreateClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new CreateClientRequest(orgId, clientName, clientDescription), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Create Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientTests/DeleteTests.cs b/Core.Tests/UseCases/ClientTests/DeleteTests.cs new file mode 100644 index 0000000..90e6ff4 --- /dev/null +++ b/Core.Tests/UseCases/ClientTests/DeleteTests.cs @@ -0,0 +1,591 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.ClientConfig; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteClient_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + + Assert.True(is4DeleteRan); + Assert.True(deleteRan); + Assert.True(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Client Arrange + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(is4DeleteRan); + Assert.False(deleteRan); + Assert.False(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + Guid clientId = Guid.NewGuid(); + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(null, false, new[] {new Error("404", "Client Not Found")})); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(is4DeleteRan); + Assert.False(deleteRan); + Assert.False(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = Guid.NewGuid()}; + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(is4DeleteRan); + Assert.False(deleteRan); + Assert.False(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_IS4ClientRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "IS4Client Delete Failed")})); + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("IS4Client Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(is4DeleteRan); + Assert.False(deleteRan); + Assert.False(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_ClientRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Client Delete Failed")})); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(is4DeleteRan); + Assert.True(deleteRan); + Assert.False(configDeleteRan); + } + + [Fact] + public async Task DeleteClient_ClientConfigRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + bool is4DeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => is4DeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool deleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + ClientConfig mockClientConfig = new ClientConfig + { + Id = clientId, + OwnerId = orgId + }; + + bool configDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => configDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Client Config Delete Failed")})); + mockClientConfigRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientConfigGatewayResponse(mockClientConfig, true)); + + // Use Case and OutputPort + + DeleteClientUseCase useCase = new DeleteClientUseCase(mockOrganisationRepository.Object, + mockClientRepository.Object, mockClientConfigRepository.Object, mockIS4ClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(is4DeleteRan); + Assert.True(deleteRan); + Assert.True(configDeleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientTests/GetTests.cs b/Core.Tests/UseCases/ClientTests/GetTests.cs new file mode 100644 index 0000000..65838a1 --- /dev/null +++ b/Core.Tests/UseCases/ClientTests/GetTests.cs @@ -0,0 +1,229 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientTests +{ + public class GetTests + { + [Fact] + public async Task GetClient_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Client.Id, clientId); + + // Assert Client Values + Assert.Equal(mockOutputPort.Response.Client.OwnerId, orgId); + Assert.Equal(mockOutputPort.Response.Client.Name, clientName); + Assert.Equal(mockOutputPort.Response.Client.Description, clientDescription); + } + + [Fact] + public async Task GetClient_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClient_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(null, false, new[] {new Error("404", "Client Not Found")})); + + // Use Case and OutputPort + + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClient_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = Guid.NewGuid()}; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + GetClientUseCase useCase = + new GetClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientRequest(orgId, clientId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientTests/GetsTests.cs b/Core.Tests/UseCases/ClientTests/GetsTests.cs new file mode 100644 index 0000000..857bb29 --- /dev/null +++ b/Core.Tests/UseCases/ClientTests/GetsTests.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientTests +{ + public class GetsTests + { + [Fact] + public async Task GetClients_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + + Client mockClient = new Client + { + Id = clientId, + OwnerId = orgId, + Name = clientName, + Description = clientDescription + }; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.GetForOwner(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetClientsGatewayResponse(new List {mockClient}, true)); + + // Use Case and OutputPort + + GetClientsUseCase useCase = + new GetClientsUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientsRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Contains(mockClient, mockOutputPort.Response.Clients); + } + + [Fact] + public async Task GetClients_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.GetForOwner(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetClientsGatewayResponse(new List {mockClient}, true)); + + // Use Case and OutputPort + + GetClientsUseCase useCase = + new GetClientsUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientsRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetClients_RepoGetErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.GetForOwner(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync( + new GetClientsGatewayResponse(null, false, new[] {new Error("500", "Clients Get Failed")})); + + // Use Case and OutputPort + + GetClientsUseCase useCase = + new GetClientsUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetClientsRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Clients Get Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/ClientTests/UpdateTests.cs b/Core.Tests/UseCases/ClientTests/UpdateTests.cs new file mode 100644 index 0000000..a936324 --- /dev/null +++ b/Core.Tests/UseCases/ClientTests/UpdateTests.cs @@ -0,0 +1,310 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Client; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Client; +using Api.Core.DTO.UseCaseResponses.Client; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Client; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.ClientTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateClient_True() + { + // Arrange \\ + + // Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Client Arrange + + Guid clientId = Guid.NewGuid(); + + // Update Values + const string clientName = "Test Client"; + const string clientDescription = "Test Description"; + + Client mockClient = new Client + {Id = clientId, OwnerId = orgId, Name = clientName, Description = clientDescription}; + + bool updateRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(clientId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + UpdateClientUseCase useCase = + new UpdateClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + + // Act \\ + bool response = + await useCase.HandleAsync(new UpdateClientRequest(orgId, clientId, clientName, clientDescription), + mockOutputPort); + + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.Message); + + Assert.True(updateRan); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Client.Id, clientId); + + // Assert Updated Values. + Assert.Equal(mockOutputPort.Response.Client.Name, clientName); + Assert.Equal(mockOutputPort.Response.Client.Description, clientDescription); + } + + [Fact] + public async Task UpdateClient_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(clientId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + UpdateClientUseCase useCase = + new UpdateClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateClientRequest(orgId, clientId, "", ""), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateClient_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(clientId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(null, false, new[] {new Error("404", "Client Not Found")})); + + // Use Case and OutputPort + + UpdateClientUseCase useCase = + new UpdateClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateClientRequest(orgId, clientId, "", ""), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateClient_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = Guid.NewGuid()}; + + bool updateRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(clientId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientGatewayResponse(mockClient, true)); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + UpdateClientUseCase useCase = + new UpdateClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateClientRequest(orgId, clientId, "", ""), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateClient_RepoUpdateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Client Arrange + + Guid clientId = Guid.NewGuid(); + + Client mockClient = new Client {Id = clientId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(clientId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateClientGatewayResponse(null, false, + new[] {new Error("500", "Client Update Failed")})); + mockClientRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(clientId)))) + .ReturnsAsync(new GetClientGatewayResponse(mockClient, true)); + + // Use Case and OutputPort + + UpdateClientUseCase useCase = + new UpdateClientUseCase(mockOrganisationRepository.Object, mockClientRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateClientRequest(orgId, clientId, "", ""), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Update Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/GroupTests/CreateTests.cs b/Core.Tests/UseCases/GroupTests/CreateTests.cs new file mode 100644 index 0000000..5f1a78d --- /dev/null +++ b/Core.Tests/UseCases/GroupTests/CreateTests.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.GroupTests +{ + public class CreateTests + { + [Fact] + public async Task CreateGroup_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId, + Name = groupName, + Description = groupDescription, + ClientIds = clientIds + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateGroupRequest(orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Group.OwnerId, orgId); + + // Assert Group Values + Assert.Equal(mockOutputPort.Response.Group.Name, groupName); + Assert.Equal(mockOutputPort.Response.Group.Description, groupDescription); + Assert.Equal(mockOutputPort.Response.Group.ClientIds, clientIds); + } + + [Fact] + public async Task CreateGroup_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Group mockGroup = new Group + { + Id = Guid.NewGuid(), + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateGroupRequest(orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task CreateGroup_RepoCreateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List(); + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateGroupGatewayResponse(null, false, + new[] {new Error("500", "Group Create Failed")})); + + // Use Case and OutputPort + + CreateGroupUseCase useCase = + new CreateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateGroupRequest(orgId, groupName, groupDescription, clientIds), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Create Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/GroupTests/DeleteTests.cs b/Core.Tests/UseCases/GroupTests/DeleteTests.cs new file mode 100644 index 0000000..26b08fc --- /dev/null +++ b/Core.Tests/UseCases/GroupTests/DeleteTests.cs @@ -0,0 +1,306 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.GroupTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteGroup_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + + Group mockGroup = new Group + { + Id = groupId, + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + DeleteGroupUseCase useCase = + new DeleteGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + + Assert.True(deleteRan); + } + + [Fact] + public async Task DeleteGroup_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + DeleteGroupUseCase useCase = + new DeleteGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + } + + [Fact] + public async Task DeleteGroup_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(null, false, new[] {new Error("404", "Group Not Found")})); + + // Use Case and OutputPort + + DeleteGroupUseCase useCase = + new DeleteGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + } + + [Fact] + public async Task DeleteGroup_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = Guid.NewGuid()}; + + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + DeleteGroupUseCase useCase = + new DeleteGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + } + + [Fact] + public async Task DeleteGroup_RepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + bool deleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Delete(It.IsAny())) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Group Delete Failed")})); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + DeleteGroupUseCase useCase = + new DeleteGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/GroupTests/GetTests.cs b/Core.Tests/UseCases/GroupTests/GetTests.cs new file mode 100644 index 0000000..9e61485 --- /dev/null +++ b/Core.Tests/UseCases/GroupTests/GetTests.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.GroupTests +{ + public class GetTests + { + [Fact] + public async Task GetGroup_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + List clientIds = new List {Guid.NewGuid()}; + + Group mockGroup = new Group + { + Id = groupId, + OwnerId = orgId, + Name = groupName, + Description = groupDescription, + ClientIds = clientIds + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Group.Id, groupId); + + // Assert Group Values + Assert.Equal(mockOutputPort.Response.Group.OwnerId, orgId); + Assert.Equal(mockOutputPort.Response.Group.Name, groupName); + Assert.Equal(mockOutputPort.Response.Group.Description, groupDescription); + Assert.Equal(mockOutputPort.Response.Group.ClientIds, clientIds); + } + + [Fact] + public async Task GetGroup_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetGroup_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(null, false, new[] {new Error("404", "Group Not Found")})); + + // Use Case and OutputPort + + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetGroup_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = Guid.NewGuid()}; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + GetGroupUseCase useCase = + new GetGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupRequest(orgId, groupId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/GroupTests/GetsTests.cs b/Core.Tests/UseCases/GroupTests/GetsTests.cs new file mode 100644 index 0000000..a20078c --- /dev/null +++ b/Core.Tests/UseCases/GroupTests/GetsTests.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.GroupTests +{ + public class GetsTests + { + [Fact] + public async Task GetGroups_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them down below. + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + + Group mockGroup = new Group + { + Id = groupId, + OwnerId = orgId, + Name = groupName, + Description = groupDescription + }; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.GetForOwner(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetGroupsGatewayResponse(new List {mockGroup}, true)); + + // Use Case and OutputPort + + GetGroupsUseCase useCase = + new GetGroupsUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupsRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Contains(mockGroup, mockOutputPort.Response.Groups); + } + + [Fact] + public async Task GetGroups_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.GetForOwner(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetGroupsGatewayResponse(new List {mockGroup}, true)); + + // Use Case and OutputPort + + GetGroupsUseCase useCase = + new GetGroupsUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupsRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetGroups_RepoGetErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.GetForOwner(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetGroupsGatewayResponse(null, false, new[] {new Error("500", "Groups Get Failed")})); + + // Use Case and OutputPort + + GetGroupsUseCase useCase = + new GetGroupsUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetGroupsRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Groups Get Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/GroupTests/UpdateTests.cs b/Core.Tests/UseCases/GroupTests/UpdateTests.cs new file mode 100644 index 0000000..99a2db6 --- /dev/null +++ b/Core.Tests/UseCases/GroupTests/UpdateTests.cs @@ -0,0 +1,316 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Group; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Group; +using Api.Core.DTO.UseCaseResponses.Group; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Group; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.GroupTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateGroup_True() + { + // Arrange \\ + + // Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Group Arrange + + Guid groupId = Guid.NewGuid(); + + // Update Values + const string groupName = "Test Group"; + const string groupDescription = "Test Description"; + IList clientIds = new List {Guid.NewGuid()}; + + Group mockGroup = new Group + {Id = groupId, OwnerId = orgId, Name = groupName, Description = groupDescription}; + + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(groupId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + UpdateGroupUseCase useCase = + new UpdateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + + // Act \\ + bool response = await useCase.HandleAsync( + new UpdateGroupRequest(orgId, groupId, groupName, groupDescription, clientIds), mockOutputPort); + + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.Message); + + Assert.True(updateRan); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Group.Id, groupId); + + // Assert Updated Values. + Assert.Equal(mockOutputPort.Response.Group.Name, groupName); + Assert.Equal(mockOutputPort.Response.Group.Description, groupDescription); + Assert.Equal(mockOutputPort.Response.Group.ClientIds, clientIds); + } + + [Fact] + public async Task UpdateGroup_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(groupId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + UpdateGroupUseCase useCase = + new UpdateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateGroupRequest(orgId, groupId, "", "", new List()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateGroup_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(groupId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(null, false, new[] {new Error("404", "Group Not Found")})); + + // Use Case and OutputPort + + UpdateGroupUseCase useCase = + new UpdateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateGroupRequest(orgId, groupId, "", "", new List()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateGroup_NotInOrganisation_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = Guid.NewGuid()}; + + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(groupId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(mockGroup, true)); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + UpdateGroupUseCase useCase = + new UpdateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateGroupRequest(orgId, groupId, "", "", new List()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateGroup_RepoUpdateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Group Arrange + + Guid groupId = Guid.NewGuid(); + + Group mockGroup = new Group {Id = groupId, OwnerId = orgId}; + + bool updateRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(groupId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateGroupGatewayResponse(null, false, + new[] {new Error("500", "Group Update Failed")})); + mockGroupRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(groupId)))) + .ReturnsAsync(new GetGroupGatewayResponse(mockGroup, true)); + + // Use Case and OutputPort + + UpdateGroupUseCase useCase = + new UpdateGroupUseCase(mockOrganisationRepository.Object, mockGroupRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UpdateGroupRequest(orgId, groupId, "", "", new List()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Update Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/InviteTests/CreateTests.cs b/Core.Tests/UseCases/InviteTests/CreateTests.cs new file mode 100644 index 0000000..17bc69d --- /dev/null +++ b/Core.Tests/UseCases/InviteTests/CreateTests.cs @@ -0,0 +1,208 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.Interfaces.Services; +using Api.Core.UseCases.Invite; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.InviteTests +{ + public class CreateTests + { + [Fact] + public async Task CreateInvite_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(mockInvite, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(orgId, inviteEmailAddress), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Invite.OrganisationId, orgId); + + // Assert Invite Values + Assert.Equal(mockOutputPort.Response.Invite.Email, inviteEmailAddress); + Assert.Equal(mockOutputPort.Response.Invite.Token, inviteToken); + } + + [Fact] + public async Task CreateInvite_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(mockInvite, true)); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(orgId, inviteEmailAddress), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task CreateInvite_RepoCreateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + //For the first "control" test we'll set all the variables and assert them down below. + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateInviteGatewayResponse(null, false, + new[] {new Error("500", "Invite Create Failed")})); + + Mock mockPasswordGenerator = new Mock(); + mockPasswordGenerator + .Setup(repo => repo.Generate(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(inviteToken); + + // Use Case and OutputPort + + CreateInviteUseCase useCase = new CreateInviteUseCase(mockOrganisationRepository.Object, + mockInviteRepository.Object, mockPasswordGenerator.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateInviteRequest(orgId, inviteEmailAddress), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Invite Create Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/InviteTests/UseTests.cs b/Core.Tests/UseCases/InviteTests/UseTests.cs new file mode 100644 index 0000000..fa7ad7b --- /dev/null +++ b/Core.Tests/UseCases/InviteTests/UseTests.cs @@ -0,0 +1,362 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Invite; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Invite; +using Api.Core.DTO.UseCaseResponses.Invite; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Invite; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.InviteTests +{ + public class UseTests + { + [Fact] + public async Task UseInvite_True() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(mockInvite, true)); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UseInviteRequest(orgId, inviteEmailAddress, inviteToken), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.NotNull(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(orgUpdateRan); + Assert.True(inviteDeleteRan); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Null(mockOutputPort.Response.User.Id); + } + + [Fact] + public async Task UseInvite_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(mockInvite, true)); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UseInviteRequest(orgId, inviteEmailAddress, inviteToken), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(orgUpdateRan); + Assert.False(inviteDeleteRan); + } + + [Fact] + public async Task UseInvite_NonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UseInviteRequest(orgId, inviteEmailAddress, inviteToken), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(orgUpdateRan); + Assert.False(inviteDeleteRan); + } + + [Fact] + public async Task UseInvite_InviteRepDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(mockInvite, true)); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Invite Delete Failed")})); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UseInviteRequest(orgId, inviteEmailAddress, inviteToken), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Invite Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(orgUpdateRan); + Assert.True(inviteDeleteRan); + } + + [Fact] + public async Task UseInvite_OrgRepUpdateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + Guid orgId = Guid.NewGuid(); + + OrganisationDetails mockOrganisation = new OrganisationDetails {Id = orgId}; + + bool orgUpdateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Update(It.IsAny(), It.IsAny())) + .Callback(() => orgUpdateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(null, false, + new[] {new Error("500", "Organisation Update Failed")})); + + //Invite Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string inviteEmailAddress = "Test@email.com"; + const string inviteToken = "123"; + + Invite mockInvite = new Invite + { + OrganisationId = orgId, + Email = inviteEmailAddress, + Token = inviteToken + }; + + bool inviteDeleteRan = false; + Mock mockInviteRepository = new Mock(); + mockInviteRepository + .Setup(repo => repo.Get(It.IsAny(), It.IsAny())) + .ReturnsAsync(new GetInviteGatewayResponse(mockInvite, true)); + mockInviteRepository + .Setup(repo => repo.Delete(It.IsAny(), It.IsAny())) + .Callback(() => inviteDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // Use Case and OutputPort + + UseInviteUseCase useCase = + new UseInviteUseCase(mockOrganisationRepository.Object, mockInviteRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new UseInviteRequest(orgId, inviteEmailAddress, inviteToken), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Update Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(orgUpdateRan); + Assert.True(inviteDeleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationTests/CreateTests.cs b/Core.Tests/UseCases/OrganisationTests/CreateTests.cs new file mode 100644 index 0000000..6fbca3d --- /dev/null +++ b/Core.Tests/UseCases/OrganisationTests/CreateTests.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationTests +{ + public class CreateTests + { + [Fact] + public async Task CreateOrganisation_True() + { + // Arrange \\ + + //Organisation Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + Guid orgId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + Guid ownerId = Guid.NewGuid(); + + List organisationUsers = new List + { + new OrganisationUser + { + Id = ownerId.ToString(), + Permissions = new List + { + OrganisationPermissions.All + } + } + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Name = organisationName, + Owner = ownerId.ToString(), + Users = organisationUsers + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateOrganisationGatewayResponse(mockOrganisation, true)); + + // Use Case and OutputPort + + CreateOrganisationUseCase useCase = new CreateOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new CreateOrganisationRequest(ownerId.ToString(), organisationName), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.NotNull(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Organisation Values + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.Organisation.Name, organisationName); + Assert.Equal(mockOutputPort.Response.Organisation.Owner, ownerId.ToString()); + Assert.Equal(mockOutputPort.Response.Organisation.Users, organisationUsers); + } + + [Fact] + public async Task CreateOrganisation_RepoCreateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + //For the first "control" test we'll set all the variables and assert them down below. + const string organisationName = "Test Organisation"; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Create(It.IsAny())) + .ReturnsAsync(new CreateOrganisationGatewayResponse(null, false, + new[] {new Error("500", "Organisation Create Failed")})); + + // Use Case and OutputPort + + CreateOrganisationUseCase useCase = new CreateOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new CreateOrganisationRequest(organisationName), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Create Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationTests/DeleteTests.cs b/Core.Tests/UseCases/OrganisationTests/DeleteTests.cs new file mode 100644 index 0000000..cf47ce9 --- /dev/null +++ b/Core.Tests/UseCases/OrganisationTests/DeleteTests.cs @@ -0,0 +1,698 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteOrganisation_True() + { + // Arrange \\ + + // Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + + // Act \\ + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.Message); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_NonExistent_False() + { + // Arrange \\ + + Guid organisationId = Guid.NewGuid(); + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(deleteRan); + Assert.False(clientConfigDeleteRan); + Assert.False(is4ClientDeleteRan); + Assert.False(clientDeleteRan); + Assert.False(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_OrganisationRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Organisation Delete Failed")})); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + Assert.False(clientConfigDeleteRan); + Assert.False(is4ClientDeleteRan); + Assert.False(clientDeleteRan); + Assert.False(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_ClientConfigRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Client Config Delete Failed")})); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_IS4ClientRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "IS4Client Delete Failed")})); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("IS4Client Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_ClientRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Client Delete Failed")})); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_GroupRepoDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Group Delete Failed")})); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Group Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + + [Fact] + public async Task DeleteOrganisation_ClientConfigAndClientAndGroupReposDeleteErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Delete Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool deleteRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Delete(It.Is(id => id.Equals(organisationId)))) + .Callback(() => deleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + bool clientConfigDeleteRan = false; + Mock mockClientConfigRepository = new Mock(); + mockClientConfigRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientConfigDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Client Config Delete Failed")})); + + bool is4ClientDeleteRan = false; + Mock mockIS4ClientRepository = new Mock(); + mockIS4ClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => is4ClientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "IS4Client Delete Failed")})); + + bool clientDeleteRan = false; + Mock mockClientRepository = new Mock(); + mockClientRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => clientDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Client Delete Failed")})); + + bool groupDeleteRan = false; + Mock mockGroupRepository = new Mock(); + mockGroupRepository + .Setup(repo => repo.DeleteForOwner(It.IsAny())) + .Callback(() => groupDeleteRan = true) + .ReturnsAsync(new BaseGatewayResponse(false, new[] {new Error("500", "Group Delete Failed")})); + + // The Use Case we are testing (DeleteOrganisation) + DeleteOrganisationUseCase useCase = new DeleteOrganisationUseCase(mockOrganisationRepository.Object, + mockClientConfigRepository.Object, mockClientRepository.Object, mockIS4ClientRepository.Object, mockGroupRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new DeleteOrganisationRequest(organisationId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Client Config Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + Assert.Contains("IS4Client Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + Assert.Contains("Client Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + Assert.Contains("Group Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(deleteRan); + Assert.True(clientConfigDeleteRan); + Assert.True(is4ClientDeleteRan); + Assert.True(clientDeleteRan); + Assert.True(groupDeleteRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationTests/GetTests.cs b/Core.Tests/UseCases/OrganisationTests/GetTests.cs new file mode 100644 index 0000000..fa98d3a --- /dev/null +++ b/Core.Tests/UseCases/OrganisationTests/GetTests.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationTests +{ + public class GetTests + { + [Fact] + public async Task GetOrganisation_True() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them on the other side. + string orgOwnerId = Guid.NewGuid().ToString(); + string orgName = Guid.NewGuid().ToString(); + IEnumerable orgUsers = new List(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = orgOwnerId, + Name = orgName, + Users = orgUsers + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.IsAny())) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + GetOrganisationUseCase useCase = new GetOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + + // Assert Org Values + Assert.Equal(mockOutputPort.Response.Organisation.Owner, orgOwnerId); + Assert.Equal(mockOutputPort.Response.Organisation.Name, orgName); + Assert.Equal(mockOutputPort.Response.Organisation.Users, orgUsers); + } + + [Fact] + public async Task GetOrganisation_NonExistent_False() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + GetOrganisationUseCase useCase = new GetOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationTests/GetsTests.cs b/Core.Tests/UseCases/OrganisationTests/GetsTests.cs new file mode 100644 index 0000000..2760095 --- /dev/null +++ b/Core.Tests/UseCases/OrganisationTests/GetsTests.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationTests +{ + public class GetsTests + { + [Fact] + public async Task GetOrganisations_True() + { + // Arrange \\ + + Guid orgId = Guid.NewGuid(); + + Guid userId = Guid.NewGuid(); + + //For the first "control" test we'll set all the variables and assert them on the other side. + string orgOwnerId = Guid.NewGuid().ToString(); + string orgName = Guid.NewGuid().ToString(); + IEnumerable orgUsers = new List + {new OrganisationUser {Id = userId.ToString()}}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = orgOwnerId, + Name = orgName, + Users = orgUsers + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.GetForUser(It.IsAny())) + .ReturnsAsync(new GetOrganisationsGatewayResponse(new[] {mockOrganisation}, true)); + + GetOrganisationsUseCase useCase = new GetOrganisationsUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationsRequest(userId.ToString()), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.True(mockOutputPort.Response.CheckedPermissions); + Assert.NotEmpty(mockOutputPort.Response.Users); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.Contains(mockOrganisation, mockOutputPort.Response.Organisations); + Assert.Contains(userId.ToString(), mockOutputPort.Response.Users.Values.Select(u => u.Id)); + } + + [Fact] + public async Task GetOrganisations_RepoGetsErrors_False() + { + // Arrange \\ + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.GetForUser(It.IsAny())) + .ReturnsAsync(new GetOrganisationsGatewayResponse(null, false, + new[] {new Error("500", "Organisation GetForUser Failed")})); + + GetOrganisationsUseCase useCase = new GetOrganisationsUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationsRequest(), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation GetForUser Failed", + mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationTests/UpdateTests.cs b/Core.Tests/UseCases/OrganisationTests/UpdateTests.cs new file mode 100644 index 0000000..511cd16 --- /dev/null +++ b/Core.Tests/UseCases/OrganisationTests/UpdateTests.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.Organisation; +using Api.Core.DTO.UseCaseResponses.Organisation; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.Organisation; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateOrganisation_True() + { + // Arrange \\ + + // Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Update Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Update(It.Is(id => id.Equals(organisationId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Use Case and OutputPort + + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + + // Act \\ + bool response = await useCase.HandleAsync(new UpdateOrganisationRequest(organisationId, organisationName), + mockOutputPort); + + + // Assert \\ + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.Message); + + Assert.True(updateRan); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, organisationId); + + // Assert Updated Values. + Assert.Equal(mockOutputPort.Response.Organisation.Name, organisationName); + Assert.Equal(mockOutputPort.Response.Organisation.Owner, ownerId.ToString()); + Assert.Equal(mockOutputPort.Response.Organisation.Users, users); + } + + [Fact] + public async Task UpdateOrganisation_NonExistent_False() + { + // Arrange \\ + + Guid organisationId = Guid.NewGuid(); + + // Update Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Update(It.Is(id => id.Equals(organisationId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + // Use Case and OutputPort + + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new UpdateOrganisationRequest(organisationId, ""), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateOrganisation_RepoUpdateErrors_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid organisationId = Guid.NewGuid(); + + // Update Values + Guid ownerId = Guid.NewGuid(); + const string organisationName = "Test Organisation"; + IList users = new List {new OrganisationUser()}; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = organisationId, + Owner = ownerId.ToString(), + Name = organisationName, + Users = users + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => + repo.Update(It.Is(id => id.Equals(organisationId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(null, false, + new[] {new Error("500", "Organisation Update Failed")})); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(organisationId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Use Case and OutputPort + + UpdateOrganisationUseCase useCase = new UpdateOrganisationUseCase(mockOrganisationRepository.Object); + + MockOutputPort + mockOutputPort = new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new UpdateOrganisationRequest(organisationId, ""), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Update Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationUserTests/DeleteTests.cs b/Core.Tests/UseCases/OrganisationUserTests/DeleteTests.cs new file mode 100644 index 0000000..89c0bd9 --- /dev/null +++ b/Core.Tests/UseCases/OrganisationUserTests/DeleteTests.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationUserTests +{ + public class DeleteTests + { + [Fact] + public async Task DeleteOrganisationUser_True() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + IList userPermissions = new List + {OrganisationPermissions.All}; + List orgUsers = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = userPermissions + } + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = orgUsers + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new DeleteOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + } + + [Fact] + public async Task DeleteOrganisationUser_NonExistentOrganisation_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(new OrganisationDetails(), true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new DeleteOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task DeleteOrganisationUser_NonExistent_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + string orgOwnerId = Guid.NewGuid().ToString(); + string orgName = Guid.NewGuid().ToString(); + List orgUsers = new List(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = orgOwnerId, + Name = orgName, + Users = orgUsers + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new DeleteOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task DeleteOrganisationUser_RepoDeleteErrors_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + IList userPermissions = new List + {OrganisationPermissions.All}; + List orgUsers = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = userPermissions + } + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = orgUsers + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(null, false, + new[] {new Error("500", "Organisation Delete Failed")})); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + DeleteOrganisationUserUseCase + useCase = new DeleteOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = + await useCase.HandleAsync(new DeleteOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Delete Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationUserTests/GetTests.cs b/Core.Tests/UseCases/OrganisationUserTests/GetTests.cs new file mode 100644 index 0000000..1ad94af --- /dev/null +++ b/Core.Tests/UseCases/OrganisationUserTests/GetTests.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationUserTests +{ + public class GetTests + { + [Fact] + public async Task GetOrganisationUser_True() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + IList userPermissions = new List + {OrganisationPermissions.All}; + List orgUsers = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = userPermissions + } + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = orgUsers + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + GetOrganisationUserUseCase useCase = new GetOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.RequestedUser.Id, requestedUserId.ToString()); + + // Assert Org Values + Assert.Equal(mockOutputPort.Response.RequestedUser.Permissions, userPermissions); + } + + [Fact] + public async Task GetOrganisationUser_NonExistentOrganisation_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + GetOrganisationUserUseCase useCase = new GetOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.RequestedUser); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + + [Fact] + public async Task GetOrganisationUser_NonExistent_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + string orgOwnerId = Guid.NewGuid().ToString(); + string orgName = Guid.NewGuid().ToString(); + List orgUsers = new List(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = orgOwnerId, + Name = orgName, + Users = orgUsers + }; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + GetOrganisationUserUseCase useCase = new GetOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationUserRequest(orgId, requestedUserId.ToString()), + mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.RequestedUser); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationUserTests/GetsTests.cs b/Core.Tests/UseCases/OrganisationUserTests/GetsTests.cs new file mode 100644 index 0000000..a846033 --- /dev/null +++ b/Core.Tests/UseCases/OrganisationUserTests/GetsTests.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationUserTests +{ + public class GetsTests + { + [Fact] + public async Task GetOrganisationUsers_True() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Guid organisationUserId = Guid.NewGuid(); + + OrganisationUser mockOrganisationUser = new OrganisationUser + { + Id = organisationUserId.ToString() + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + {Id = orgId, Users = new[] {mockOrganisationUser}}; + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + // Use Case and OutputPort + + GetOrganisationUsersUseCase useCase = new GetOrganisationUsersUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationUsersRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + Assert.Null(mockOutputPort.Response.User); + + // For the first "control" test we'll assert these variables to double check that everything is working. + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Contains(mockOrganisationUser, mockOutputPort.Response.RequestedUsers); + } + + [Fact] + public async Task GetOrganisationUsers_OrganisationNonExistent_False() + { + // Arrange \\ + + //Organisation Arrange + + Guid orgId = Guid.NewGuid(); + + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + // Use Case and OutputPort + + GetOrganisationUsersUseCase useCase = new GetOrganisationUsersUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync(new GetOrganisationUsersRequest(orgId), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.User); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + } + } +} \ No newline at end of file diff --git a/Core.Tests/UseCases/OrganisationUserTests/UpdateTests.cs b/Core.Tests/UseCases/OrganisationUserTests/UpdateTests.cs new file mode 100644 index 0000000..ab21260 --- /dev/null +++ b/Core.Tests/UseCases/OrganisationUserTests/UpdateTests.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Api.Core.DTO; +using Api.Core.DTO.GatewayResponses.Repositories.Organisation; +using Api.Core.DTO.UseCaseRequests.OrganisationUser; +using Api.Core.DTO.UseCaseResponses.OrganisationUser; +using Api.Core.Entities; +using Api.Core.Interfaces.Gateways.Repositories; +using Api.Core.UseCases.OrganisationUser; +using Api.Tests.MockObjects; +using Moq; +using Xunit; + +namespace Api.Tests.UseCases.OrganisationUserTests +{ + public class UpdateTests + { + [Fact] + public async Task UpdateOrganisationUser_True() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + IList userPermissions = new List + {OrganisationPermissions.All}; + List orgUsers = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = userPermissions + } + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = orgUsers + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(orgId, requestedUserId.ToString(), userPermissions), mockOutputPort); + + // Assert \\ + + Assert.True(response); + Assert.True(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Empty(mockOutputPort.Response.Errors); + + Assert.True(updateRan); + + // Assert Value Ids + Assert.Equal(mockOutputPort.Response.Organisation.Id, orgId); + Assert.Equal(mockOutputPort.Response.RequestedUser.Id, requestedUserId.ToString()); + + // Assert Updated Values + Assert.Equal(mockOutputPort.Response.RequestedUser.Permissions, userPermissions); + } + + [Fact] + public async Task UpdateOrganisationUser_NonExistentOrganisation_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(new OrganisationDetails(), true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(null, false, + new[] {new Error("404", "Organisation Not Found")})); + + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(orgId, requestedUserId.ToString(), + new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.RequestedUser); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateOrganisationUser_NonExistent_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + string orgOwnerId = Guid.NewGuid().ToString(); + string orgName = Guid.NewGuid().ToString(); + List orgUsers = new List(); + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Owner = orgOwnerId, + Name = orgName, + Users = orgUsers + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(mockOrganisation, true)); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(orgId, requestedUserId.ToString(), + new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.RequestedUser); + Assert.Contains("404", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation User Not Found", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.False(updateRan); + } + + [Fact] + public async Task UpdateOrganisationUser_RepoUpdateErrors_False() + { + Guid requestedUserId = Guid.NewGuid(); + + Guid orgId = Guid.NewGuid(); + //For the first "control" test we'll set all the variables and assert them on the other side. + IList userPermissions = new List + {OrganisationPermissions.All}; + List orgUsers = new List + { + new OrganisationUser + { + Id = requestedUserId.ToString(), + Permissions = userPermissions + } + }; + + OrganisationDetails mockOrganisation = new OrganisationDetails + { + Id = orgId, + Users = orgUsers + }; + + bool updateRan = false; + Mock mockOrganisationRepository = new Mock(); + mockOrganisationRepository + .Setup(repo => repo.Update(It.Is(id => id.Equals(orgId)), It.IsAny())) + .Callback(() => updateRan = true) + .ReturnsAsync(new UpdateOrganisationGatewayResponse(null, false, + new[] {new Error("500", "Organisation Update Failed")})); + mockOrganisationRepository + .Setup(repo => repo.Get(It.Is(id => id.Equals(orgId)))) + .ReturnsAsync(new GetOrganisationGatewayResponse(mockOrganisation, true)); + + UpdateOrganisationUserUseCase + useCase = new UpdateOrganisationUserUseCase(mockOrganisationRepository.Object); + + MockOutputPort mockOutputPort = + new MockOutputPort(); + + // Act \\ + + bool response = await useCase.HandleAsync( + new UpdateOrganisationUserRequest(orgId, requestedUserId.ToString(), + new List()), mockOutputPort); + + // Assert \\ + + Assert.False(response); + Assert.False(mockOutputPort.Response.Success); + Assert.False(mockOutputPort.Response.CheckedPermissions); + Assert.Null(mockOutputPort.Response.RequestedUser); + Assert.Contains("500", mockOutputPort.Response.Errors.Select(e => e.Code)); + Assert.Contains("Organisation Update Failed", mockOutputPort.Response.Errors.Select(e => e.Description)); + + Assert.True(updateRan); + } + } +} \ No newline at end of file diff --git a/DiscordianAuth/AspIdUsers.db b/DiscordianAuth/AspIdUsers.db deleted file mode 100644 index baeabd8..0000000 Binary files a/DiscordianAuth/AspIdUsers.db and /dev/null differ diff --git a/DiscordianAuth/Config.cs b/DiscordianAuth/Config.cs deleted file mode 100644 index 2667a64..0000000 --- a/DiscordianAuth/Config.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; -using System.Collections.Generic; - -namespace DiscordianAuth -{ - public static class Config - { - public static IEnumerable GetIdentityResources() - { - return new IdentityResource[] - { - new IdentityResources.OpenId(), - new IdentityResources.Profile(), - }; - } - - public static IEnumerable GetApis() - { - return new ApiResource[] - { - new ApiResource("api1", "My API #1") - }; - } - - public static IEnumerable GetClients() - { - return new[] - { - // client credentials flow client - new Client - { - ClientId = "client", - ClientName = "Client Credentials Client", - - AllowedGrantTypes = GrantTypes.ClientCredentials, - ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, - - AllowedScopes = { "api1" } - }, - - // MVC client using hybrid flow - new Client - { - ClientId = "mvc", - ClientName = "MVC Client", - - AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, - ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) }, - - RedirectUris = { "http://localhost:5001/signin-oidc" }, - FrontChannelLogoutUri = "http://localhost:5001/signout-oidc", - PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" }, - - AllowOfflineAccess = true, - AllowedScopes = { "openid", "profile", "api1" } - }, - - // SPA client using implicit flow - new Client - { - ClientId = "spa", - ClientName = "SPA Client", - ClientUri = "http://identityserver.io", - - AllowedGrantTypes = GrantTypes.Implicit, - AllowAccessTokensViaBrowser = true, - - RedirectUris = - { - "http://localhost:5002/index.html", - "http://localhost:5002/callback.html", - "http://localhost:5002/silent.html", - "http://localhost:5002/popup.html", - }, - - PostLogoutRedirectUris = { "http://localhost:5002/index.html" }, - AllowedCorsOrigins = { "http://localhost:5002" }, - - AllowedScopes = { "openid", "profile", "api1" } - } - }; - } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Data/ApplicationDbContext.cs b/DiscordianAuth/Data/ApplicationDbContext.cs deleted file mode 100644 index 232a464..0000000 --- a/DiscordianAuth/Data/ApplicationDbContext.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; -using DiscordianAuth.Models; - -namespace DiscordianAuth.Data -{ - public class ApplicationDbContext : IdentityDbContext - { - public ApplicationDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - // Customize the ASP.NET Identity model and override the defaults if needed. - // For example, you can rename the ASP.NET Identity table names and more. - // Add your customizations after calling base.OnModelCreating(builder); - } - } -} diff --git a/DiscordianAuth/Data/Migrations/20180109192453_CreateIdentitySchema.Designer.cs b/DiscordianAuth/Data/Migrations/20180109192453_CreateIdentitySchema.Designer.cs deleted file mode 100644 index bc40ece..0000000 --- a/DiscordianAuth/Data/Migrations/20180109192453_CreateIdentitySchema.Designer.cs +++ /dev/null @@ -1,227 +0,0 @@ -// -using DiscordianAuth.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using System; - -namespace DiscordianAuth.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20180109192453_CreateIdentitySchema")] - partial class CreateIdentitySchema - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); - - modelBuilder.Entity("DiscordianAuth.Models.ApplicationUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed"); - - b.Property("LockoutEnabled"); - - b.Property("LockoutEnd"); - - b.Property("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash"); - - b.Property("PhoneNumber"); - - b.Property("PhoneNumberConfirmed"); - - b.Property("SecurityStamp"); - - b.Property("TwoFactorEnabled"); - - b.Property("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Name") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("RoleId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider"); - - b.Property("ProviderKey"); - - b.Property("ProviderDisplayName"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider"); - - b.Property("Name"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/DiscordianAuth/Data/Migrations/20180109192453_CreateIdentitySchema.cs b/DiscordianAuth/Data/Migrations/20180109192453_CreateIdentitySchema.cs deleted file mode 100644 index 2322bef..0000000 --- a/DiscordianAuth/Data/Migrations/20180109192453_CreateIdentitySchema.cs +++ /dev/null @@ -1,217 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; - -namespace DiscordianAuth.Data.Migrations -{ - public partial class CreateIdentitySchema : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "AspNetRoles", - columns: table => new - { - Id = table.Column(nullable: false), - ConcurrencyStamp = table.Column(nullable: true), - Name = table.Column(maxLength: 256, nullable: true), - NormalizedName = table.Column(maxLength: 256, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoles", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetUsers", - columns: table => new - { - Id = table.Column(nullable: false), - AccessFailedCount = table.Column(nullable: false), - ConcurrencyStamp = table.Column(nullable: true), - Email = table.Column(maxLength: 256, nullable: true), - EmailConfirmed = table.Column(nullable: false), - LockoutEnabled = table.Column(nullable: false), - LockoutEnd = table.Column(nullable: true), - NormalizedEmail = table.Column(maxLength: 256, nullable: true), - NormalizedUserName = table.Column(maxLength: 256, nullable: true), - PasswordHash = table.Column(nullable: true), - PhoneNumber = table.Column(nullable: true), - PhoneNumberConfirmed = table.Column(nullable: false), - SecurityStamp = table.Column(nullable: true), - TwoFactorEnabled = table.Column(nullable: false), - UserName = table.Column(maxLength: 256, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUsers", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "AspNetRoleClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserClaims", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - ClaimType = table.Column(nullable: true), - ClaimValue = table.Column(nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); - table.ForeignKey( - name: "FK_AspNetUserClaims_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserLogins", - columns: table => new - { - LoginProvider = table.Column(nullable: false), - ProviderKey = table.Column(nullable: false), - ProviderDisplayName = table.Column(nullable: true), - UserId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK_AspNetUserLogins_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserRoles", - columns: table => new - { - UserId = table.Column(nullable: false), - RoleId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetRoles_RoleId", - column: x => x.RoleId, - principalTable: "AspNetRoles", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AspNetUserRoles_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "AspNetUserTokens", - columns: table => new - { - UserId = table.Column(nullable: false), - LoginProvider = table.Column(nullable: false), - Name = table.Column(nullable: false), - Value = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK_AspNetUserTokens_AspNetUsers_UserId", - column: x => x.UserId, - principalTable: "AspNetUsers", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_AspNetRoleClaims_RoleId", - table: "AspNetRoleClaims", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "AspNetRoles", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserClaims_UserId", - table: "AspNetUserClaims", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserLogins_UserId", - table: "AspNetUserLogins", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_AspNetUserRoles_RoleId", - table: "AspNetUserRoles", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "AspNetUsers", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "AspNetUsers", - column: "NormalizedUserName", - unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AspNetRoleClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserClaims"); - - migrationBuilder.DropTable( - name: "AspNetUserLogins"); - - migrationBuilder.DropTable( - name: "AspNetUserRoles"); - - migrationBuilder.DropTable( - name: "AspNetUserTokens"); - - migrationBuilder.DropTable( - name: "AspNetRoles"); - - migrationBuilder.DropTable( - name: "AspNetUsers"); - } - } -} diff --git a/DiscordianAuth/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/DiscordianAuth/Data/Migrations/ApplicationDbContextModelSnapshot.cs deleted file mode 100644 index 5147599..0000000 --- a/DiscordianAuth/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ /dev/null @@ -1,226 +0,0 @@ -// -using DiscordianAuth.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using System; - -namespace DiscordianAuth.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - partial class ApplicationDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); - - modelBuilder.Entity("DiscordianAuth.Models.ApplicationUser", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AccessFailedCount"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Email") - .HasMaxLength(256); - - b.Property("EmailConfirmed"); - - b.Property("LockoutEnabled"); - - b.Property("LockoutEnd"); - - b.Property("NormalizedEmail") - .HasMaxLength(256); - - b.Property("NormalizedUserName") - .HasMaxLength(256); - - b.Property("PasswordHash"); - - b.Property("PhoneNumber"); - - b.Property("PhoneNumberConfirmed"); - - b.Property("SecurityStamp"); - - b.Property("TwoFactorEnabled"); - - b.Property("UserName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasName("UserNameIndex"); - - b.ToTable("AspNetUsers"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken(); - - b.Property("Name") - .HasMaxLength(256); - - b.Property("NormalizedName") - .HasMaxLength(256); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasName("RoleNameIndex"); - - b.ToTable("AspNetRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("RoleId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ClaimType"); - - b.Property("ClaimValue"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider"); - - b.Property("ProviderKey"); - - b.Property("ProviderDisplayName"); - - b.Property("UserId") - .IsRequired(); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId"); - - b.Property("RoleId"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId"); - - b.Property("LoginProvider"); - - b.Property("Name"); - - b.Property("Value"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade); - - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("DiscordianAuth.Models.ApplicationUser") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/DiscordianAuth/DiscordianAuth.csproj b/DiscordianAuth/DiscordianAuth.csproj deleted file mode 100644 index 578b9ce..0000000 --- a/DiscordianAuth/DiscordianAuth.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - netcoreapp2.0 - aspnet-Host-12ED8ECC-9EF1-4D31-87B4-1405B3198E5E - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DiscordianAuth/Models/ApplicationUser.cs b/DiscordianAuth/Models/ApplicationUser.cs deleted file mode 100644 index 95d4c61..0000000 --- a/DiscordianAuth/Models/ApplicationUser.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNetCore.Identity; - -namespace DiscordianAuth.Models -{ - // Add profile data for application users by adding properties to the ApplicationUser class - public class ApplicationUser : IdentityUser - { - } -} diff --git a/DiscordianAuth/Program.cs b/DiscordianAuth/Program.cs deleted file mode 100644 index 4ad68a5..0000000 --- a/DiscordianAuth/Program.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Logging; -using Serilog; -using Serilog.Events; -using Serilog.Sinks.SystemConsole.Themes; -using System.Linq; - -namespace DiscordianAuth -{ - public class Program - { - public static void Main(string[] args) - { - var seed = args.Any(x => x == "/seed"); - if (seed) args = args.Except(new[] { "/seed" }).ToArray(); - - var host = BuildWebHost(args); - - if (seed) - { - SeedData.EnsureSeedData(host.Services); - return; - } - - host.Run(); - } - - public static IWebHost BuildWebHost(string[] args) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) - .MinimumLevel.Override("System", LogEventLevel.Warning) - .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) - .Enrich.FromLogContext() - .WriteTo.File(@"identityserver4_log.txt") - .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate) - .CreateLogger(); - - return WebHost.CreateDefaultBuilder(args) - .UseStartup() - .ConfigureLogging(builder => - { - builder.ClearProviders(); - builder.AddSerilog(); - }) - .Build(); - } - } -} diff --git a/DiscordianAuth/Quickstart/Account/AccountController.cs b/DiscordianAuth/Quickstart/Account/AccountController.cs deleted file mode 100644 index f212f6d..0000000 --- a/DiscordianAuth/Quickstart/Account/AccountController.cs +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using IdentityServer4.Events; -using IdentityServer4.Models; -using Microsoft.AspNetCore.Identity; -using DiscordianAuth.Models; -using IdentityServer4.Extensions; -using System.Security.Principal; -using System.Security.Claims; -using IdentityModel; -using System.Linq; -using System; -using System.Collections.Generic; - -namespace IdentityServer4.Quickstart.UI -{ - [SecurityHeaders] - public class AccountController : Controller - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IAuthenticationSchemeProvider _schemeProvider; - private readonly IEventService _events; - - public AccountController( - UserManager userManager, - SignInManager signInManager, - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IAuthenticationSchemeProvider schemeProvider, - IEventService events) - { - _userManager = userManager; - _signInManager = signInManager; - _interaction = interaction; - _clientStore = clientStore; - _schemeProvider = schemeProvider; - _events = events; - } - - /// - /// Show login page - /// - [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 await ExternalLogin(vm.ExternalLoginScheme, returnUrl); - } - - return View(vm); - } - - /// - /// Handle postback from username/password login - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Login(LoginInputModel model, string button) - { - if (button != "login") - { - // the user clicked the "cancel" button - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - 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 - return Redirect(model.ReturnUrl); - } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } - } - - if (ModelState.IsValid) - { - var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true); - if (result.Succeeded) - { - var user = await _userManager.FindByNameAsync(model.Username); - await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName)); - - // make sure the returnUrl is still valid, and if so redirect back to authorize endpoint or a local page - // the IsLocalUrl check is only necessary if you want to support additional local pages, otherwise IsValidReturnUrl is more strict - if (_interaction.IsValidReturnUrl(model.ReturnUrl) || Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - - return Redirect("~/"); - } - - await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials")); - - ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage); - } - - // something went wrong, show form with error - var vm = await BuildLoginViewModelAsync(model); - return View(vm); - } - - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public async Task ExternalLogin(string provider, string returnUrl) - { - if (AccountOptions.WindowsAuthenticationSchemeName == provider) - { - // windows authentication needs special handling - return await ProcessWindowsLoginAsync(returnUrl); - } - else - { - // start challenge and roundtrip the return URL and - var props = new AuthenticationProperties() - { - RedirectUri = Url.Action("ExternalLoginCallback"), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", provider }, - } - }; - return Challenge(props, provider); - } - } - - /// - /// Post processing of external authentication - /// - [HttpGet] - public async Task ExternalLoginCallback() - { - // read external identity from the temporary cookie - var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme); - if (result?.Succeeded != true) - { - throw new Exception("External authentication error"); - } - - // lookup our user and external provider info - var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(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 = await AutoProvisionUserAsync(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 - // we must issue the cookie maually, and can't use the SignInManager because - // it doesn't expose an API to issue additional claims from the login workflow - var principal = await _signInManager.CreateUserPrincipalAsync(user); - additionalLocalClaims.AddRange(principal.Claims); - var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id; - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name)); - await HttpContext.SignInAsync(user.Id, name, provider, localSignInProps, additionalLocalClaims.ToArray()); - - // delete temporary cookie used during external authentication - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - // validate return URL and redirect back to authorization endpoint or a local page - var returnUrl = result.Properties.Items["returnUrl"]; - if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl)) - { - return Redirect(returnUrl); - } - - return Redirect("~/"); - } - - /// - /// 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 _signInManager.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); - } - - /*****************************************/ - /* helper APIs for the AccountController */ - /*****************************************/ - private async Task BuildLoginViewModelAsync(string returnUrl) - { - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (context?.IdP != null) - { - // this is meant to short circuit the UI and only trigger the one external IdP - return new LoginViewModel - { - EnableLocalLogin = false, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - ExternalProviders = new ExternalProvider[] { new ExternalProvider { AuthenticationScheme = context.IdP } } - }; - } - - 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; - } - - 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, tresting windows - // auth the same as any other external authentication mechanism - var props = new AuthenticationProperties() - { - RedirectUri = Url.Action("ExternalLoginCallback"), - 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 async Task<(ApplicationUser user, string provider, string providerUserId, IEnumerable claims)> - FindUserFromExternalProviderAsync(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 = await _userManager.FindByLoginAsync(provider, providerUserId); - - return (user, provider, providerUserId, claims); - } - - private async Task AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable claims) - { - // create a list of claims that we want to transfer into our store - var filtered = new List(); - - // user's display name - var name = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Name)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value; - if (name != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, name)); - } - else - { - var first = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value; - var last = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value; - if (first != null && last != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last)); - } - else if (first != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, first)); - } - else if (last != null) - { - filtered.Add(new Claim(JwtClaimTypes.Name, last)); - } - } - - // email - var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ?? - claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value; - if (email != null) - { - filtered.Add(new Claim(JwtClaimTypes.Email, email)); - } - - var user = new ApplicationUser - { - UserName = Guid.NewGuid().ToString(), - }; - var identityResult = await _userManager.CreateAsync(user); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - - if (filtered.Any()) - { - identityResult = await _userManager.AddClaimsAsync(user, filtered); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - } - - identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider)); - if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); - - 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) - { - } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Account/AccountOptions.cs b/DiscordianAuth/Quickstart/Account/AccountOptions.cs deleted file mode 100644 index 18f98f2..0000000 --- a/DiscordianAuth/Quickstart/Account/AccountOptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; - -namespace IdentityServer4.Quickstart.UI -{ - public class AccountOptions - { - public static bool AllowLocalLogin = true; - public static bool AllowRememberLogin = true; - public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); - - public static bool ShowLogoutPrompt = true; - public static bool AutomaticRedirectAfterSignOut = false; - - // 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/DiscordianAuth/Quickstart/Account/ExternalProvider.cs b/DiscordianAuth/Quickstart/Account/ExternalProvider.cs deleted file mode 100644 index 8bc8e71..0000000 --- a/DiscordianAuth/Quickstart/Account/ExternalProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - public class ExternalProvider - { - public string DisplayName { get; set; } - public string AuthenticationScheme { get; set; } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Account/LoggedOutViewModel.cs b/DiscordianAuth/Quickstart/Account/LoggedOutViewModel.cs deleted file mode 100644 index da45c51..0000000 --- a/DiscordianAuth/Quickstart/Account/LoggedOutViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - public class LoggedOutViewModel - { - public string PostLogoutRedirectUri { get; set; } - public string ClientName { get; set; } - public string SignOutIframeUrl { get; set; } - - public bool AutomaticRedirectAfterSignOut { get; set; } - - public string LogoutId { get; set; } - public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; - public string ExternalAuthenticationScheme { get; set; } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Account/LoginInputModel.cs b/DiscordianAuth/Quickstart/Account/LoginInputModel.cs deleted file mode 100644 index 66c9cc4..0000000 --- a/DiscordianAuth/Quickstart/Account/LoginInputModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.ComponentModel.DataAnnotations; - -namespace IdentityServer4.Quickstart.UI -{ - public class LoginInputModel - { - [Required] - public string Username { get; set; } - [Required] - public string Password { get; set; } - public bool RememberLogin { get; set; } - public string ReturnUrl { get; set; } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Account/LoginViewModel.cs b/DiscordianAuth/Quickstart/Account/LoginViewModel.cs deleted file mode 100644 index 2d62f89..0000000 --- a/DiscordianAuth/Quickstart/Account/LoginViewModel.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace IdentityServer4.Quickstart.UI -{ - public class LoginViewModel : LoginInputModel - { - public bool AllowRememberLogin { get; set; } - public bool EnableLocalLogin { get; set; } - - public IEnumerable ExternalProviders { get; set; } - 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/DiscordianAuth/Quickstart/Account/LogoutInputModel.cs b/DiscordianAuth/Quickstart/Account/LogoutInputModel.cs deleted file mode 100644 index 8962b1f..0000000 --- a/DiscordianAuth/Quickstart/Account/LogoutInputModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - public class LogoutInputModel - { - public string LogoutId { get; set; } - } -} diff --git a/DiscordianAuth/Quickstart/Account/LogoutViewModel.cs b/DiscordianAuth/Quickstart/Account/LogoutViewModel.cs deleted file mode 100644 index a99f03d..0000000 --- a/DiscordianAuth/Quickstart/Account/LogoutViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - public class LogoutViewModel : LogoutInputModel - { - public bool ShowLogoutPrompt { get; set; } - } -} diff --git a/DiscordianAuth/Quickstart/Consent/ConsentController.cs b/DiscordianAuth/Quickstart/Consent/ConsentController.cs deleted file mode 100644 index cbc0250..0000000 --- a/DiscordianAuth/Quickstart/Consent/ConsentController.cs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Linq; -using System.Threading.Tasks; - -namespace IdentityServer4.Quickstart.UI -{ - /// - /// 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 ILogger _logger; - - public ConsentController( - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IResourceStore resourceStore, - ILogger logger) - { - _interaction = interaction; - _clientStore = clientStore; - _resourceStore = resourceStore; - _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) - { - return Redirect(result.RedirectUri); - } - - if (result.HasValidationError) - { - ModelState.AddModelError("", 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(); - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model.Button == "no") - { - grantedConsent = ConsentResponse.Denied; - } - // user clicked 'yes' - validate the data - else if (model.Button == "yes" && model != null) - { - // 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() - }; - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // validate return url is still valid - var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - if (request == null) return result; - - // 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; - } - 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(); - vm.RememberConsent = model?.RememberConsent ?? true; - vm.ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(); - - vm.ReturnUrl = returnUrl; - - vm.ClientName = client.ClientName ?? client.ClientId; - vm.ClientUrl = client.ClientUri; - vm.ClientLogoUrl = client.LogoUri; - vm.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/DiscordianAuth/Quickstart/Consent/ConsentInputModel.cs b/DiscordianAuth/Quickstart/Consent/ConsentInputModel.cs deleted file mode 100644 index eb42805..0000000 --- a/DiscordianAuth/Quickstart/Consent/ConsentInputModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace IdentityServer4.Quickstart.UI -{ - public class ConsentInputModel - { - public string Button { get; set; } - public IEnumerable ScopesConsented { get; set; } - public bool RememberConsent { get; set; } - public string ReturnUrl { get; set; } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Consent/ConsentOptions.cs b/DiscordianAuth/Quickstart/Consent/ConsentOptions.cs deleted file mode 100644 index 0fc990a..0000000 --- a/DiscordianAuth/Quickstart/Consent/ConsentOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - public class ConsentOptions - { - public static bool EnableOfflineAccess = true; - public static string OfflineAccessDisplayName = "Offline Access"; - public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; - - public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; - public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; - } -} diff --git a/DiscordianAuth/Quickstart/Consent/ConsentViewModel.cs b/DiscordianAuth/Quickstart/Consent/ConsentViewModel.cs deleted file mode 100644 index e1c0b00..0000000 --- a/DiscordianAuth/Quickstart/Consent/ConsentViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace IdentityServer4.Quickstart.UI -{ - public class ConsentViewModel : ConsentInputModel - { - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public bool AllowRememberConsent { get; set; } - - public IEnumerable IdentityScopes { get; set; } - public IEnumerable ResourceScopes { get; set; } - } -} diff --git a/DiscordianAuth/Quickstart/Consent/ProcessConsentResult.cs b/DiscordianAuth/Quickstart/Consent/ProcessConsentResult.cs deleted file mode 100644 index c705c14..0000000 --- a/DiscordianAuth/Quickstart/Consent/ProcessConsentResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - public class ProcessConsentResult - { - public bool IsRedirect => RedirectUri != null; - public string RedirectUri { 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/DiscordianAuth/Quickstart/Consent/ScopeViewModel.cs b/DiscordianAuth/Quickstart/Consent/ScopeViewModel.cs deleted file mode 100644 index 24cc22c..0000000 --- a/DiscordianAuth/Quickstart/Consent/ScopeViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace IdentityServer4.Quickstart.UI -{ - 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/DiscordianAuth/Quickstart/Diagnostics/DiagnosticsController.cs b/DiscordianAuth/Quickstart/Diagnostics/DiagnosticsController.cs deleted file mode 100644 index dbd7ed1..0000000 --- a/DiscordianAuth/Quickstart/Diagnostics/DiagnosticsController.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace IdentityServer4.Quickstart.UI -{ - [SecurityHeaders] - [Authorize] - public class DiagnosticsController : Controller - { - public async Task Index() - { - var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; - if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) - { - return NotFound(); - } - - var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); - return View(model); - } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/DiscordianAuth/Quickstart/Diagnostics/DiagnosticsViewModel.cs deleted file mode 100644 index 3daebd4..0000000 --- a/DiscordianAuth/Quickstart/Diagnostics/DiagnosticsViewModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityModel; -using Microsoft.AspNetCore.Authentication; -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Text; - -namespace IdentityServer4.Quickstart.UI -{ - public class DiagnosticsViewModel - { - public DiagnosticsViewModel(AuthenticateResult result) - { - AuthenticateResult = result; - - if (result.Properties.Items.ContainsKey("client_list")) - { - var encoded = result.Properties.Items["client_list"]; - var bytes = Base64Url.Decode(encoded); - var value = Encoding.UTF8.GetString(bytes); - - Clients = JsonConvert.DeserializeObject(value); - } - } - - public AuthenticateResult AuthenticateResult { get; } - public IEnumerable Clients { get; } = new List(); - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Grants/GrantsController.cs b/DiscordianAuth/Quickstart/Grants/GrantsController.cs deleted file mode 100644 index a0b42ad..0000000 --- a/DiscordianAuth/Quickstart/Grants/GrantsController.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; - -namespace IdentityServer4.Quickstart.UI -{ - /// - /// This sample controller allows a user to revoke grants given to clients - /// - [SecurityHeaders] - [Authorize] - public class GrantsController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clients; - private readonly IResourceStore _resources; - - public GrantsController(IIdentityServerInteractionService interaction, - IClientStore clients, - IResourceStore resources) - { - _interaction = interaction; - _clients = clients; - _resources = resources; - } - - /// - /// 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); - 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/DiscordianAuth/Quickstart/Grants/GrantsViewModel.cs b/DiscordianAuth/Quickstart/Grants/GrantsViewModel.cs deleted file mode 100644 index 4fa818e..0000000 --- a/DiscordianAuth/Quickstart/Grants/GrantsViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; - -namespace IdentityServer4.Quickstart.UI -{ - public class GrantsViewModel - { - public IEnumerable Grants { get; set; } - } - - public class GrantViewModel - { - public string ClientId { get; set; } - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public 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/DiscordianAuth/Quickstart/Home/ErrorViewModel.cs b/DiscordianAuth/Quickstart/Home/ErrorViewModel.cs deleted file mode 100644 index c1a27b8..0000000 --- a/DiscordianAuth/Quickstart/Home/ErrorViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; - -namespace IdentityServer4.Quickstart.UI -{ - public class ErrorViewModel - { - public ErrorMessage Error { get; set; } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/Home/HomeController.cs b/DiscordianAuth/Quickstart/Home/HomeController.cs deleted file mode 100644 index 80beb21..0000000 --- a/DiscordianAuth/Quickstart/Home/HomeController.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Services; -using Microsoft.AspNetCore.Mvc; -using System.Threading.Tasks; - -namespace IdentityServer4.Quickstart.UI -{ - [SecurityHeaders] - public class HomeController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - - public HomeController(IIdentityServerInteractionService interaction) - { - _interaction = interaction; - } - - public IActionResult Index() - { - return View(); - } - - /// - /// Shows the error page - /// - public async Task Error(string errorId) - { - var vm = new ErrorViewModel(); - - // retrieve error details from identityserver - var message = await _interaction.GetErrorContextAsync(errorId); - if (message != null) - { - vm.Error = message; - } - - return View("Error", vm); - } - } -} \ No newline at end of file diff --git a/DiscordianAuth/Quickstart/SecurityHeadersAttribute.cs b/DiscordianAuth/Quickstart/SecurityHeadersAttribute.cs deleted file mode 100644 index dcdf6f5..0000000 --- a/DiscordianAuth/Quickstart/SecurityHeadersAttribute.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; - -namespace IdentityServer4.Quickstart.UI -{ - public class SecurityHeadersAttribute : ActionFilterAttribute - { - public override void OnResultExecuting(ResultExecutingContext context) - { - var result = context.Result; - if (result is ViewResult) - { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options - if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) - { - context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff"); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options - if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) - { - context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN"); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy - var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; - // also consider adding upgrade-insecure-requests once you have HTTPS in place for production - //csp += "upgrade-insecure-requests;"; - // also an example if you need client images to be displayed from twitter - // csp += "img-src 'self' https://pbs.twimg.com;"; - - // once for standards compliant browsers - if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) - { - context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp); - } - // and once again for IE - if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) - { - context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp); - } - - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy - var referrer_policy = "no-referrer"; - if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) - { - context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy); - } - } - } - } -} diff --git a/DiscordianAuth/SeedData.cs b/DiscordianAuth/SeedData.cs deleted file mode 100644 index 0a6fba4..0000000 --- a/DiscordianAuth/SeedData.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Linq; -using System.Security.Claims; -using IdentityModel; -using DiscordianAuth.Data; -using DiscordianAuth.Models; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; - -namespace DiscordianAuth -{ - public class SeedData - { - public static void EnsureSeedData(IServiceProvider serviceProvider) - { - using (var scope = serviceProvider.GetRequiredService().CreateScope()) - { - var context = scope.ServiceProvider.GetService(); - context.Database.Migrate(); - - var userMgr = scope.ServiceProvider.GetRequiredService>(); - var alice = userMgr.FindByNameAsync("alice").Result; - if (alice == null) - { - alice = new ApplicationUser - { - UserName = "alice" - }; - var result = userMgr.CreateAsync(alice, "Pass123$").Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - - result = userMgr.AddClaimsAsync(alice, new Claim[]{ - new Claim(JwtClaimTypes.Name, "Alice Smith"), - new Claim(JwtClaimTypes.GivenName, "Alice"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), - new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), - new Claim(JwtClaimTypes.WebSite, "http://alice.com"), - new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json) - }).Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - Console.WriteLine("alice created"); - } - else - { - Console.WriteLine("alice already exists"); - } - - var bob = userMgr.FindByNameAsync("bob").Result; - if (bob == null) - { - bob = new ApplicationUser - { - UserName = "bob" - }; - var result = userMgr.CreateAsync(bob, "Pass123$").Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - - result = userMgr.AddClaimsAsync(bob, new Claim[]{ - new Claim(JwtClaimTypes.Name, "Bob Smith"), - new Claim(JwtClaimTypes.GivenName, "Bob"), - new Claim(JwtClaimTypes.FamilyName, "Smith"), - new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), - new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), - new Claim(JwtClaimTypes.WebSite, "http://bob.com"), - new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json), - new Claim("location", "somewhere") - }).Result; - if (!result.Succeeded) - { - throw new Exception(result.Errors.First().Description); - } - Console.WriteLine("bob created"); - } - else - { - Console.WriteLine("bob already exists"); - } - } - } - } -} diff --git a/DiscordianAuth/Startup.cs b/DiscordianAuth/Startup.cs deleted file mode 100644 index 1833795..0000000 --- a/DiscordianAuth/Startup.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using DiscordianAuth.Data; -using DiscordianAuth.Models; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System; - -namespace DiscordianAuth -{ - public class Startup - { - public IConfiguration Configuration { get; } - public IHostingEnvironment Environment { get; } - - public Startup(IConfiguration configuration, IHostingEnvironment environment) - { - Configuration = configuration; - Environment = environment; - } - - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(options => - options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); - - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - - services.AddMvc(); - - services.Configure(iis => - { - iis.AuthenticationDisplayName = "Windows"; - iis.AutomaticAuthentication = false; - }); - - var builder = services.AddIdentityServer(options => - { - options.Events.RaiseErrorEvents = true; - options.Events.RaiseInformationEvents = true; - options.Events.RaiseFailureEvents = true; - options.Events.RaiseSuccessEvents = true; - }) - .AddInMemoryIdentityResources(Config.GetIdentityResources()) - .AddInMemoryApiResources(Config.GetApis()) - .AddInMemoryClients(Config.GetClients()) - .AddAspNetIdentity(); - - if (Environment.IsDevelopment()) - { - builder.AddDeveloperSigningCredential(); - } - else - { - throw new Exception("need to configure key material"); - } - - services.AddAuthentication() - .AddGoogle(options => - { - options.ClientId = "708996912208-9m4dkjb5hscn7cjrn5u0r4tbgkbj1fko.apps.googleusercontent.com"; - options.ClientSecret = "wdfPY6t8H8cecgjlxud__4Gh"; - }); - } - - public void Configure(IApplicationBuilder app) - { - if (Environment.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseDatabaseErrorPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - } - - app.UseStaticFiles(); - app.UseIdentityServer(); - app.UseMvcWithDefaultRoute(); - } - } -} diff --git a/DiscordianAuth/Views/Account/LoggedOut.cshtml b/DiscordianAuth/Views/Account/LoggedOut.cshtml deleted file mode 100644 index 99599c0..0000000 --- a/DiscordianAuth/Views/Account/LoggedOut.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@model LoggedOutViewModel - -@{ - // set this so the layout rendering sees an anonymous user - ViewData["signed-out"] = true; -} - - - -@section scripts -{ - @if (Model.AutomaticRedirectAfterSignOut) - { - - } -} diff --git a/DiscordianAuth/Views/Account/Login.cshtml b/DiscordianAuth/Views/Account/Login.cshtml deleted file mode 100644 index 029b44c..0000000 --- a/DiscordianAuth/Views/Account/Login.cshtml +++ /dev/null @@ -1,92 +0,0 @@ -@model LoginViewModel - - \ No newline at end of file diff --git a/DiscordianAuth/Views/Account/Logout.cshtml b/DiscordianAuth/Views/Account/Logout.cshtml deleted file mode 100644 index e04a5fc..0000000 --- a/DiscordianAuth/Views/Account/Logout.cshtml +++ /dev/null @@ -1,21 +0,0 @@ -@model LogoutViewModel - -
- - -
-
-

Would you like to logout of IdentityServer?

-
- -
-
- -
-
-
-
-
-
\ No newline at end of file diff --git a/DiscordianAuth/Views/Consent/Index.cshtml b/DiscordianAuth/Views/Consent/Index.cshtml deleted file mode 100644 index b3a2f64..0000000 --- a/DiscordianAuth/Views/Consent/Index.cshtml +++ /dev/null @@ -1,82 +0,0 @@ -@model ConsentViewModel - - \ No newline at end of file diff --git a/DiscordianAuth/Views/Consent/_ScopeListItem.cshtml b/DiscordianAuth/Views/Consent/_ScopeListItem.cshtml deleted file mode 100644 index e3fa22d..0000000 --- a/DiscordianAuth/Views/Consent/_ScopeListItem.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -@model ScopeViewModel - -
  • - - @if (Model.Required) - { - (required) - } - @if (Model.Description != null) - { - - } -
  • \ No newline at end of file diff --git a/DiscordianAuth/Views/Diagnostics/Index.cshtml b/DiscordianAuth/Views/Diagnostics/Index.cshtml deleted file mode 100644 index 13e0118..0000000 --- a/DiscordianAuth/Views/Diagnostics/Index.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@model DiagnosticsViewModel - -

    Authentication cookie

    - -

    Claims

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

    Properties

    -
    - @foreach (var prop in Model.AuthenticateResult.Properties.Items) - { -
    @prop.Key
    -
    @prop.Value
    - } -
    - -@if (Model.Clients.Any()) -{ -

    Clients

    -
      - @foreach (var client in Model.Clients) - { -
    • @client
    • - } -
    -} \ No newline at end of file diff --git a/DiscordianAuth/Views/Grants/Index.cshtml b/DiscordianAuth/Views/Grants/Index.cshtml deleted file mode 100644 index c1e1009..0000000 --- a/DiscordianAuth/Views/Grants/Index.cshtml +++ /dev/null @@ -1,79 +0,0 @@ -@model 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/DiscordianAuth/Views/Home/Index.cshtml b/DiscordianAuth/Views/Home/Index.cshtml deleted file mode 100644 index f5b97ea..0000000 --- a/DiscordianAuth/Views/Home/Index.cshtml +++ /dev/null @@ -1,35 +0,0 @@ -
    - - -
    -
    -

    - 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/DiscordianAuth/Views/Shared/Error.cshtml b/DiscordianAuth/Views/Shared/Error.cshtml deleted file mode 100644 index 90ab6fc..0000000 --- a/DiscordianAuth/Views/Shared/Error.cshtml +++ /dev/null @@ -1,42 +0,0 @@ -@using Microsoft.AspNetCore.Hosting -@model ErrorViewModel -@inject IHostingEnvironment host - -@{ - var error = Model?.Error?.Error; - var errorDescription = host.IsDevelopment() ? Model?.Error?.ErrorDescription : null; - 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/DiscordianAuth/Views/Shared/_Layout.cshtml b/DiscordianAuth/Views/Shared/_Layout.cshtml deleted file mode 100644 index 3e0422e..0000000 --- a/DiscordianAuth/Views/Shared/_Layout.cshtml +++ /dev/null @@ -1,61 +0,0 @@ -@using IdentityServer4.Extensions -@{ - string name = null; - if (!true.Equals(ViewData["signed-out"])) - { - name = Context.User?.GetDisplayName(); - } -} - - - - - - - IdentityServer4 - - - - - - - - -
    - @RenderBody() -
    - - - - @RenderSection("scripts", required: false) - - diff --git a/DiscordianAuth/Views/Shared/_ValidationSummary.cshtml b/DiscordianAuth/Views/Shared/_ValidationSummary.cshtml deleted file mode 100644 index 674d68d..0000000 --- a/DiscordianAuth/Views/Shared/_ValidationSummary.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@if (ViewContext.ModelState.IsValid == false) -{ -
    - Error -
    -
    -} \ No newline at end of file diff --git a/DiscordianAuth/appsettings.json b/DiscordianAuth/appsettings.json deleted file mode 100644 index 1ca0fb6..0000000 --- a/DiscordianAuth/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Data Source=AspIdUsers.db;" - } -} \ No newline at end of file diff --git a/DiscordianAuth/identityserver4_log.txt b/DiscordianAuth/identityserver4_log.txt deleted file mode 100644 index 823bc98..0000000 --- a/DiscordianAuth/identityserver4_log.txt +++ /dev/null @@ -1,282 +0,0 @@ -2018-04-15 13:41:43.923 +02:00 [DBG] Using Identity.Application as default scheme for authentication -2018-04-15 13:41:43.949 +02:00 [DBG] Using Identity.External as default scheme for sign-in -2018-04-15 13:41:43.949 +02:00 [DBG] Using Identity.External as default scheme for sign-out -2018-04-15 13:41:43.950 +02:00 [DBG] Using Identity.Application as default scheme for challenge -2018-04-15 13:41:43.950 +02:00 [DBG] Using Identity.Application as default scheme for forbid -2018-04-15 13:41:45.461 +02:00 [WRN] The 'bool' property 'UserEditable' on entity type 'IdentityExpressClaimType' is configured with a database-generated default. This default will always be used when the property has the value 'false', since this is the CLR default for the 'bool' type. Consider using the nullable 'bool?' type instead so that the default will only be used when the property value is 'null'. -2018-04-15 13:41:46.366 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:15.213 +02:00 [DBG] Request path /.well-known/openid-configuration matched to endpoint type Discovery -2018-04-15 13:42:15.245 +02:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint -2018-04-15 13:42:15.245 +02:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration -2018-04-15 13:42:15.252 +02:00 [DBG] Start discovery request -2018-04-15 13:42:15.470 +02:00 [DBG] Found ["openid","profile"] as all scopes in database -2018-04-15 13:42:27.852 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:49.777 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:55.419 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:56.114 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:56.932 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:57.293 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:57.658 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:42:58.269 +02:00 [FTL] ****** Using non-production Community Edition ****** -2018-04-15 13:48:34.633 +02:00 [INF] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. -2018-04-15 13:48:34.681 +02:00 [DBG] Using Identity.Application as default scheme for authentication -2018-04-15 13:48:34.685 +02:00 [DBG] Using Identity.External as default scheme for sign-in -2018-04-15 13:48:34.685 +02:00 [DBG] Using Identity.External as default scheme for sign-out -2018-04-15 13:48:34.685 +02:00 [DBG] Using Identity.Application as default scheme for challenge -2018-04-15 13:48:34.685 +02:00 [DBG] Using Identity.Application as default scheme for forbid -2018-04-15 13:48:45.217 +02:00 [INF] AuthenticationScheme: Identity.Application was challenged. -2018-04-15 13:50:47.292 +02:00 [INF] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. -2018-04-15 13:50:47.333 +02:00 [DBG] Using Identity.Application as default scheme for authentication -2018-04-15 13:50:47.335 +02:00 [DBG] Using Identity.External as default scheme for sign-in -2018-04-15 13:50:47.335 +02:00 [DBG] Using Identity.External as default scheme for sign-out -2018-04-15 13:50:47.336 +02:00 [DBG] Using Identity.Application as default scheme for challenge -2018-04-15 13:50:47.336 +02:00 [DBG] Using Identity.Application as default scheme for forbid -2018-04-15 13:50:53.128 +02:00 [INF] AuthenticationScheme: Identity.Application was challenged. -2018-04-15 13:51:23.356 +02:00 [ERR] Failed executing DbCommand (5ms) [Parameters=[@__normalizedUserName_0='?'], CommandType='"Text"', CommandTimeout='30'] -SELECT "u"."Id", "u"."AccessFailedCount", "u"."ConcurrencyStamp", "u"."Email", "u"."EmailConfirmed", "u"."LockoutEnabled", "u"."LockoutEnd", "u"."NormalizedEmail", "u"."NormalizedUserName", "u"."PasswordHash", "u"."PhoneNumber", "u"."PhoneNumberConfirmed", "u"."SecurityStamp", "u"."TwoFactorEnabled", "u"."UserName" -FROM "AspNetUsers" AS "u" -WHERE "u"."NormalizedUserName" = @__normalizedUserName_0 -LIMIT 1 -Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: AspNetUsers'. - at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) - at Microsoft.Data.Sqlite.SqliteCommand.d__52.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.d__17.MoveNext() -2018-04-15 13:51:23.435 +02:00 [ERR] An exception occurred in the database while iterating the results of a query for context type 'DiscordianAuth.Data.ApplicationDbContext'. -Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: AspNetUsers'. - at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) - at Microsoft.Data.Sqlite.SqliteCommand.d__52.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.d__17.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__9.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.d__165`1.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.TaskResultAsyncEnumerable`1.Enumerator.d__3.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.d__7.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.AsyncIterator`1.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.d__5.MoveNext() -Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: AspNetUsers'. - at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) - at Microsoft.Data.Sqlite.SqliteCommand.d__52.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.d__17.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__9.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.d__165`1.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.TaskResultAsyncEnumerable`1.Enumerator.d__3.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.d__7.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.AsyncIterator`1.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.d__5.MoveNext() -2018-04-15 13:51:23.485 +02:00 [ERR] An exception occurred in the database while iterating the results of a query for context type 'DiscordianAuth.Data.ApplicationDbContext'. -Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: AspNetUsers'. - at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) - at Microsoft.Data.Sqlite.SqliteCommand.d__52.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.d__17.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__9.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.d__165`1.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.TaskResultAsyncEnumerable`1.Enumerator.d__3.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.d__7.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.AsyncIterator`1.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.d__5.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.d__23`1.MoveNext() -Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: AspNetUsers'. - at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) - at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) - at Microsoft.Data.Sqlite.SqliteCommand.d__52.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.d__17.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.d__9.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.d__165`1.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.TaskResultAsyncEnumerable`1.Enumerator.d__3.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.SelectEnumerableAsyncIterator`2.d__7.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult() - at System.Linq.AsyncEnumerable.AsyncIterator`1.d__10.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.d__5.MoveNext() ---- End of stack trace from previous location where exception was thrown --- - at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() - at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) - at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() - at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.d__23`1.MoveNext() -2018-04-15 13:52:27.472 +02:00 [INF] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. -2018-04-15 13:52:27.513 +02:00 [DBG] Using Identity.Application as default scheme for authentication -2018-04-15 13:52:27.516 +02:00 [DBG] Using Identity.External as default scheme for sign-in -2018-04-15 13:52:27.516 +02:00 [DBG] Using Identity.External as default scheme for sign-out -2018-04-15 13:52:27.516 +02:00 [DBG] Using Identity.Application as default scheme for challenge -2018-04-15 13:52:27.516 +02:00 [DBG] Using Identity.Application as default scheme for forbid -2018-04-15 13:53:13.482 +02:00 [INF] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. -2018-04-15 13:53:13.518 +02:00 [DBG] Using Identity.Application as default scheme for authentication -2018-04-15 13:53:13.521 +02:00 [DBG] Using Identity.External as default scheme for sign-in -2018-04-15 13:53:13.521 +02:00 [DBG] Using Identity.External as default scheme for sign-out -2018-04-15 13:53:13.522 +02:00 [DBG] Using Identity.Application as default scheme for challenge -2018-04-15 13:53:13.522 +02:00 [DBG] Using Identity.Application as default scheme for forbid -2018-04-15 13:53:51.471 +02:00 [INF] You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation. -2018-04-15 13:53:51.511 +02:00 [DBG] Using Identity.Application as default scheme for authentication -2018-04-15 13:53:51.514 +02:00 [DBG] Using Identity.External as default scheme for sign-in -2018-04-15 13:53:51.514 +02:00 [DBG] Using Identity.External as default scheme for sign-out -2018-04-15 13:53:51.514 +02:00 [DBG] Using Identity.Application as default scheme for challenge -2018-04-15 13:53:51.514 +02:00 [DBG] Using Identity.Application as default scheme for forbid -2018-04-15 13:54:36.966 +02:00 [INF] AuthenticationScheme: Identity.Application was challenged. -2018-04-15 13:54:46.168 +02:00 [DBG] Augmenting SignInContext -2018-04-15 13:54:46.170 +02:00 [DBG] Adding idp claim with value: local -2018-04-15 13:54:46.170 +02:00 [DBG] Adding amr claim with value: pwd -2018-04-15 13:54:46.170 +02:00 [DBG] Adding auth_time claim with value: 1523793286 -2018-04-15 13:54:46.184 +02:00 [INF] AuthenticationScheme: Identity.Application signed in. -2018-04-15 13:54:46.268 +02:00 [INF] { - "Name": "User Login Success", - "Category": "Authentication", - "EventType": "Success", - "Id": 1000, - "Username": "alice", - "SubjectId": "498fc8ae-1714-42bb-9617-69972d42699e", - "DisplayName": "alice", - "Endpoint": "UI", - "ActivityId": "0HLD2OU7BVMBV:00000004", - "TimeStamp": "2018-04-15T11:54:46Z", - "ProcessId": 22880, - "LocalIpAddress": "::1:5000", - "RemoteIpAddress": "::1" -} -2018-04-15 13:54:46.278 +02:00 [INF] AuthenticationScheme: Identity.Application was successfully authenticated. -2018-04-15 13:54:46.279 +02:00 [INF] AuthenticationScheme: Identity.Application was successfully authenticated. diff --git a/DiscordianAuth/tempkey.rsa b/DiscordianAuth/tempkey.rsa deleted file mode 100644 index 2a9a1da..0000000 --- a/DiscordianAuth/tempkey.rsa +++ /dev/null @@ -1 +0,0 @@ -{"KeyId":"9f9cd2263761247a5ac831545a950db3","Parameters":{"D":"wtQFu3xBT5NqzmIVDNAe2W7PHPCBjhxvvMTbWY5pGML5IRLU8xLGaFwLAlSw2DmC4ZuDoiaW8Mar6xMeV4uwOdErD97FKs3ELcyjSZ6pCviejiW2NF7oH22KvaoQh7MDx6RV0RNoZZ5P4eHCRopBMEzE3CPmXjX9QiGFnSQsANX+7RHKe9FhXAzo76R0I7nk/E5fJvAEQ+NXlmbaPZAUc5Iw6MA4wt08T7pqgCprnrKsiLA1pidXvH+oXdMl3+ts7mauiQgdIgri4X59Knk3JE1t12zx3Ef7DnZ404jjwUWTq2K7sykA8vedsWi31PTEXoGImOaXpCV71ZyybTTW1Q==","DP":"5QKtzxtj+8BGMqYhcvqe1MEdN9xnzL4KObHezWPv4MqyLdwq1jtn29GMtkzBQ/06gacMBJQ5eWZfAcLEl/EZkwSIdUB63Q/20ysYzCFTFSA5PYKkRQTFWGMagcT+deb3BlRZgrwb/UCm/dzBilIiZDWTxbpKnGI/S89alpDQTks=","DQ":"ve+MuqywKj8ZyvDitRlg/qOQNSxzPmzLCj5s/xDLRv18tjk+5jgNezyJOVaDcXebkF3rHOAYNh+QCrOUz8+P0ECVVAWuXO1eZB8suxJVV2zGv90afgaEmvUSyqc2ESq8N2lp6r+GmzCb1mo+w2pGEBlLhnPmUieBY6Ez+CbrFes=","Exponent":"AQAB","InverseQ":"c53BWfnEQ3iEdUOsRf1wikbWASZbpXUkEZuJg+Baa0eZ8ZcWpZ34VcDma4QhBNH5/lt+V/Y3RLXw0CDYao/gkk6orietgln08FURPHvZ8QjGWlADZhbL/xQtoAp4MdbR9P91CAmlui5nMfZmzuoRbekLGvTcO0KEeatd3NWnFDI=","Modulus":"0qfJKOeaptt48IZJ3VjFzWJJql7uCjzeYbWXMOLdXi40blFt802N4qgrV+LJY7s8xLmAiGl9vFIbq+FghdfmpftMeJ1OFjg6RAtXK5UODvvN1hhr/uPCagJDt20u1WU9XcIhs4AZ63oJUWGk5mdMMLsSqLuRieL7cX5Pxs3iZb5AvvIz+DU5UqltwnrsMgdOqXdUY3QzSfGj6in4lzWNii1u3HQwCrHHGIK/6R9fI0nqNTlXMJgtS7mO4uTQ+UqMPWlV4jsYKJChNDfaq9jmnxOWdFi8yBjokmxkBPtgUicIIp3PTbwo435FvIlYvrpS7O1gv/oOstTH8ma3WZlAYQ==","P":"/k1Z9M445ZBDeIepq2QvNSO4S5XMDKOXiEzmHUpkCVztpmvyvMXWc4Qafkp1Bcr2Ty9ejazf7juKGt/s0oBxR+nOf5/hLNUhktBeas8rGsOk4i23mq2yJTWy6FPfYGZ+FPcV57mn+ptY7VWS2ojsX7jCaFhiVgSfWr/coYd0Hh8=","Q":"1A/VjHok89MYz60XaBt8IkOntuQjTbr8Swl7fHAp5ttBXEvs3E610RK+AnehzBsdKdgBVocbZNOL8uE4fQeJ939QzZ58CT6g8diNUFEV+aaLDDBik9dqyFGY+k8ApItJTwCKz12PB52bOaAVjT44LJ8PrXK1SbeXw0SfS6q50X8="}} \ No newline at end of file diff --git a/DiscordianAuth/wwwroot/css/global.css b/DiscordianAuth/wwwroot/css/global.css new file mode 100644 index 0000000..bb07529 --- /dev/null +++ b/DiscordianAuth/wwwroot/css/global.css @@ -0,0 +1,285 @@ +@charset "utf-8"; +@import url('https://fonts.googleapis.com/css?family=Roboto'); + +.home-content{ + margin-top: 15px; + padding: 40px; + background: + url(../images/bg_corner_top_left.jpg) top left no-repeat, + url(../images/bg_corner_top_right.jpg) top right no-repeat, + url(../images/bg_corner_bottom_left.jpg) bottom left no-repeat, + url(../images/bg_corner_bottom_right.jpg) bottom right no-repeat, + url(../images/bg_middle_left.jpg) left center repeat-y, + url(../images/bg_middle_right.jpg) right center repeat-y, + url(../images/bg_top.jpg) top center repeat-x, + url(../images/bg_middle_bottom.jpg) bottom center repeat-x; + width: 100%; +} + +/* BACKGROUND STYLES */ +.bghome { + background: + linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), + url(../images/minecraft.jpg) center/cover no-repeat; + width: 100%; + height: 100%; +} + +.bg { + background-color: #fffaee; +} + +.bgscroll { + background: + url(../images/bg_corner_top_left.jpg) top left no-repeat, + url(../images/bg_corner_top_right.jpg) top right no-repeat, + url(../images/bg_corner_bottom_left.jpg) bottom left no-repeat, + url(../images/bg_corner_bottom_right.jpg) bottom right no-repeat, + url(../images/bg_middle_left.jpg) left center repeat-y, + url(../images/bg_middle_right.jpg) right center repeat-y, + url(../images/bg_top.jpg) top center repeat-x, + url(../images/bg_middle_bottom.jpg) bottom center repeat-x; + width: 100%; +} + + + +/* MAKE BACKGROUND FOR HOMEPAGE FIT THE SCREEN AT A CERTAIN BREAKPOINT */ +@media screen and (min-width: 768px) { + .bghome { + height: 100vh; + } + + .bgscroll { + height: 100%; + } +} + +@media screen and (max-width: 768px) { + .home { + padding-bottom: 50px; + } +} + +/* GLOBAL STYLES */ +@font-face { + font-family: 'Comic Book'; + src: url("../font/Comic Book.otf"); +} + +@font-face { + font-family: 'Colonial Dame'; + src: url("../font/Colonial Dame.ttf"); +} + +body { + margin: 0; + font-family: Roboto, sans-serif; +} + +@media (min-width: 1500px) { + .container { + max-width: 1450px; + } +} + +@media (max-width: 576px) { + .container { + padding-left: 40px; + padding-right: 40px; + } +} + +h1.header { + font-family: "Colonial Dame", sans-serif; + font-size: 8rem; + line-height: 100px; + padding: 50px 2px 10px 2px; + margin: 2px; + background: #EEE; + color: transparent; + text-shadow: -2px -2px 3px rgba(255,255,255,.5), + -3px -2px 2px #EEE, + -3px -2px 0 #000, + 0 1px 0 #000; + -webkit-background-clip: text; + background-clip: text; + text-align: center; +} + +@media screen and (max-width: 991px) { + h1.header { + font-size: 5rem; + } +} + +@media screen and (max-width: 576px) { + h1.header { + font-size: 4rem; + } +} + +.navbar.main { + font-family: "Comic Book", sans-serif; + background: url("../images/NavbarRibbon.png") center/100% 100% no-repeat; + width: 99%; + height: 68px; + color: white; + font-size: 12pt; + margin: auto; +} + +@media (max-width: 991px) { + .navbar.main { + background: url("../images/navbarribbon_small.png") center/100% 100% no-repeat; + } +} + +.navbar-toggler-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); +} + +.navbar.main a:link, +.navbar.main a:visited { + color: #fff; +} + +.active, +.navbar.main a:hover, +.navbar.main a:focus { + text-decoration: none; + text-shadow: 0 0 10px rgba(255, 255, 255, 1); +} + +.navbar.secondary { + font-family: "Comic Book", Roboto, sans-serif; + background: url("../images/Ribbon.png") center/100% 100% no-repeat; + width: 99%; + height: 28px; + color: white; + font-size: 12pt; + margin: 10px auto 0; +} + +@media (max-width: 991px) { + .navbar.secondary { + display: none; + } +} + +.navbar .nav:nth-child(1) { + padding-bottom: 6px; +} + +.dropdown { + cursor: pointer; +} + +.dropdown-menu { + background-color: #FDF6E5!important; + border: 3px solid #E0D2AE; +} + +.dropdown-item { + color: #000!important; +} + +.dropdown-item .fa { + padding-right: 10px; +} + +.ribbon { + background: url("../images/Ribbon.png") center/100% 100% no-repeat; + width: 99%; + text-align: center; + height: 28px; + margin: auto; + color: #fff; +} + +/* SIDEBAR */ +.navbar-side { + height: 100%; + width: 25%; + position: fixed; + top: 0; + left: 0; + padding: 0; + list-style: none; + border-right: 3px solid #E0D2AE; + background-color: #FDF6E5; + overflow-y: scroll; + z-index: 1000; + -webkit-transform: translateX(-100%); + -ms-transform: translateX(-100%); + transform: translateX(-100%); + -webkit-transition: 300ms ease; + transition: 300ms ease; +} + +.sidenav-user-box { + margin: 20px 0 0; +} + +.sidenav-user-box img { + display: block; + margin: 0 auto; +} + +.sidenav-user-box p { + display: block; + padding-top: .5rem; + color: #000000!important; +} + +@media screen and (max-width: 768px) { + .navbar-side { + width: 35%; + } +} + +@media screen and (max-width: 576px) { + .navbar-side { + width: 50%; + } +} + +.reveal { + -webkit-transform: translateX(-0%); + -ms-transform: translateX(-0%); + transform: translateX(-0%); + -webkit-transition: 300ms ease; + transition: 300ms ease; +} + +.navbar-side-item { + padding: .2rem 0; + margin: 0; +} + +.navbar-side-item .dropdown-menu { + position: static; + float: none; +} + +.side-link { + padding-left: 1rem; + color: #000000!important; +} + +.side-link .fa { + padding-right: 10px; +} + +.overlay { + position: fixed; + display: none; + top: 0; + left: 0; + right: 0; + bottom: 0; + cursor: pointer; + background: rgba(0, 0, 0, 0.8); + opacity: 0.6; + z-index: 990; +} \ No newline at end of file diff --git a/DiscordianAuth/wwwroot/font/Colonial Dame.ttf b/DiscordianAuth/wwwroot/font/Colonial Dame.ttf new file mode 100644 index 0000000..eaaf031 Binary files /dev/null and b/DiscordianAuth/wwwroot/font/Colonial Dame.ttf differ diff --git a/DiscordianAuth/wwwroot/font/Comic Book.otf b/DiscordianAuth/wwwroot/font/Comic Book.otf new file mode 100644 index 0000000..0ee8a2c Binary files /dev/null and b/DiscordianAuth/wwwroot/font/Comic Book.otf differ diff --git a/DiscordianAuth/wwwroot/images/NavbarRibbon.png b/DiscordianAuth/wwwroot/images/NavbarRibbon.png new file mode 100644 index 0000000..7fe01ab Binary files /dev/null and b/DiscordianAuth/wwwroot/images/NavbarRibbon.png differ diff --git a/DiscordianAuth/wwwroot/images/bg_corner_bottom_left.jpg b/DiscordianAuth/wwwroot/images/bg_corner_bottom_left.jpg new file mode 100644 index 0000000..4fdfa3c Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_corner_bottom_left.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_corner_bottom_right.jpg b/DiscordianAuth/wwwroot/images/bg_corner_bottom_right.jpg new file mode 100644 index 0000000..79ac129 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_corner_bottom_right.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_corner_top_left.jpg b/DiscordianAuth/wwwroot/images/bg_corner_top_left.jpg new file mode 100644 index 0000000..c49c7d4 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_corner_top_left.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_corner_top_right.jpg b/DiscordianAuth/wwwroot/images/bg_corner_top_right.jpg new file mode 100644 index 0000000..138f8ad Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_corner_top_right.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_middle_bottom.jpg b/DiscordianAuth/wwwroot/images/bg_middle_bottom.jpg new file mode 100644 index 0000000..8789036 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_middle_bottom.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_middle_left.jpg b/DiscordianAuth/wwwroot/images/bg_middle_left.jpg new file mode 100644 index 0000000..20c0432 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_middle_left.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_middle_right.jpg b/DiscordianAuth/wwwroot/images/bg_middle_right.jpg new file mode 100644 index 0000000..901e80e Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_middle_right.jpg differ diff --git a/DiscordianAuth/wwwroot/images/bg_top.jpg b/DiscordianAuth/wwwroot/images/bg_top.jpg new file mode 100644 index 0000000..015010d Binary files /dev/null and b/DiscordianAuth/wwwroot/images/bg_top.jpg differ diff --git a/DiscordianAuth/wwwroot/images/minecraft.jpg b/DiscordianAuth/wwwroot/images/minecraft.jpg new file mode 100644 index 0000000..5022a96 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/minecraft.jpg differ diff --git a/DiscordianAuth/wwwroot/images/navbarribbon_small.png b/DiscordianAuth/wwwroot/images/navbarribbon_small.png new file mode 100644 index 0000000..914f4f9 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/navbarribbon_small.png differ diff --git a/DiscordianAuth/wwwroot/images/ribbon_bg_68pxh.png b/DiscordianAuth/wwwroot/images/ribbon_bg_68pxh.png new file mode 100644 index 0000000..914f4f9 Binary files /dev/null and b/DiscordianAuth/wwwroot/images/ribbon_bg_68pxh.png differ diff --git a/DiscordianServer.sln b/DiscordianServer.sln deleted file mode 100644 index eb61d9c..0000000 --- a/DiscordianServer.sln +++ /dev/null @@ -1,48 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscordianServer", "DiscordianServer\DiscordianServer.csproj", "{76BEF781-8644-4C45-9DD7-04E0369B97A4}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscordianAuth", "DiscordianAuth\DiscordianAuth.csproj", "{7E542C81-BCEB-4CD9-88EF-36D120BDD46E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Debug|x64.ActiveCfg = Debug|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Debug|x64.Build.0 = Debug|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Debug|x86.ActiveCfg = Debug|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Debug|x86.Build.0 = Debug|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Release|Any CPU.Build.0 = Release|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Release|x64.ActiveCfg = Release|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Release|x64.Build.0 = Release|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Release|x86.ActiveCfg = Release|Any CPU - {76BEF781-8644-4C45-9DD7-04E0369B97A4}.Release|x86.Build.0 = Release|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Debug|x64.ActiveCfg = Debug|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Debug|x64.Build.0 = Debug|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Debug|x86.ActiveCfg = Debug|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Debug|x86.Build.0 = Debug|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Release|Any CPU.Build.0 = Release|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Release|x64.ActiveCfg = Release|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Release|x64.Build.0 = Release|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Release|x86.ActiveCfg = Release|Any CPU - {7E542C81-BCEB-4CD9-88EF-36D120BDD46E}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/DiscordianServer/DiscordianServer.csproj b/DiscordianServer/DiscordianServer.csproj deleted file mode 100644 index 522599e..0000000 --- a/DiscordianServer/DiscordianServer.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - netcoreapp2.1 - - - - - - - - - - - - - - - - - diff --git a/DiscordianServer/Dockerfile b/DiscordianServer/Dockerfile deleted file mode 100644 index 4d12849..0000000 --- a/DiscordianServer/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM microsoft/dotnet-nightly:2.1-sdk as builder - -RUN mkdir -p /root/src/app/discordianserver -WORKDIR /root/src/app/discordianserver - -COPY DiscordianServer.csproj . -RUN dotnet restore ./DiscordianServer.csproj - -COPY . . -RUN dotnet publish -c release -o published - -FROM microsoft/dotnet-nightly:2.1-aspnetcore-runtime-alpine - -WORKDIR /root/ -COPY --from=builder /root/src/app/discordianserver/published . -EXPOSE 80/tcp -CMD ["dotnet", "./DiscordianServer.dll"] \ No newline at end of file diff --git a/DiscordianServer/Hubs/DiscordianHub.cs b/DiscordianServer/Hubs/DiscordianHub.cs deleted file mode 100644 index 689024e..0000000 --- a/DiscordianServer/Hubs/DiscordianHub.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; - -namespace DiscordianServer.Hubs -{ - - public class DiscordianHub:Hub - { - - private readonly ILogger logger; - - public DiscordianHub(ILogger logger) - { - this.logger = logger; - } - - public async Task DiscordChatMessage(string author, string channel, string message) - { - logger.LogInformation($"Discord: {author} in channel {channel} - {message}"); - await Clients.All.SendAsync("DiscordChatMessage", author, channel, message); - } - - public async Task MinecraftChatMessage(string author, string server, string message) - { - logger.LogInformation($"Minecraft: {author} in server {server} - {message}"); - await Clients.All.SendAsync("MinecraftChatMessage", author, server, message); - } - } -} \ No newline at end of file diff --git a/DiscordianServer/Pages/About.cshtml b/DiscordianServer/Pages/About.cshtml deleted file mode 100644 index 3c090d1..0000000 --- a/DiscordianServer/Pages/About.cshtml +++ /dev/null @@ -1,9 +0,0 @@ -@page -@model AboutModel -@{ - ViewData["Title"] = "About"; -} -

    @ViewData["Title"]

    -

    @Model.Message

    - -

    Use this area to provide additional information.

    diff --git a/DiscordianServer/Pages/About.cshtml.cs b/DiscordianServer/Pages/About.cshtml.cs deleted file mode 100644 index e0f1a2e..0000000 --- a/DiscordianServer/Pages/About.cshtml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace DiscordianServer.Pages -{ - public class AboutModel : PageModel - { - public string Message { get; set; } - - public void OnGet() - { - Message = "Your application description page."; - } - } -} diff --git a/DiscordianServer/Pages/Contact.cshtml b/DiscordianServer/Pages/Contact.cshtml deleted file mode 100644 index b683c82..0000000 --- a/DiscordianServer/Pages/Contact.cshtml +++ /dev/null @@ -1,19 +0,0 @@ -@page -@model ContactModel -@{ - ViewData["Title"] = "Contact"; -} -

    @ViewData["Title"]

    -

    @Model.Message

    - -
    - One Microsoft Way
    - Redmond, WA 98052-6399
    - P: - 425.555.0100 -
    - -
    - Support: Support@example.com
    - Marketing: Marketing@example.com -
    diff --git a/DiscordianServer/Pages/Contact.cshtml.cs b/DiscordianServer/Pages/Contact.cshtml.cs deleted file mode 100644 index 55d7b6a..0000000 --- a/DiscordianServer/Pages/Contact.cshtml.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace DiscordianServer.Pages -{ - public class ContactModel : PageModel - { - public string Message { get; set; } - - public void OnGet() - { - Message = "Your contact page."; - } - } -} diff --git a/DiscordianServer/Pages/Index.cshtml b/DiscordianServer/Pages/Index.cshtml deleted file mode 100644 index 728fdfc..0000000 --- a/DiscordianServer/Pages/Index.cshtml +++ /dev/null @@ -1,108 +0,0 @@ -@page -@model IndexModel -@{ - ViewData["Title"] = "Home page"; -} - - - - diff --git a/DiscordianServer/Pages/Index.cshtml.cs b/DiscordianServer/Pages/Index.cshtml.cs deleted file mode 100644 index b3462f0..0000000 --- a/DiscordianServer/Pages/Index.cshtml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace DiscordianServer.Pages -{ - public class IndexModel : PageModel - { - public void OnGet() - { - - } - } -} diff --git a/DiscordianServer/Pages/Privacy.cshtml b/DiscordianServer/Pages/Privacy.cshtml deleted file mode 100644 index f3787ba..0000000 --- a/DiscordianServer/Pages/Privacy.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@page -@model PrivacyModel -@{ - ViewData["Title"] = "Privacy Policy"; -} -

    @ViewData["Title"]

    - -

    Use this page to detail your site's privacy policy.

    \ No newline at end of file diff --git a/DiscordianServer/Pages/Privacy.cshtml.cs b/DiscordianServer/Pages/Privacy.cshtml.cs deleted file mode 100644 index a4064e9..0000000 --- a/DiscordianServer/Pages/Privacy.cshtml.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace DiscordianServer.Pages -{ - public class PrivacyModel : PageModel - { - public void OnGet() - { - } - } -} \ No newline at end of file diff --git a/DiscordianServer/Pages/_Layout.cshtml b/DiscordianServer/Pages/_Layout.cshtml deleted file mode 100644 index eea3a06..0000000 --- a/DiscordianServer/Pages/_Layout.cshtml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - @ViewData["Title"] - DiscordianServer - - - - - - - - - - - - - - - -
    - @RenderBody() -
    -
    -

    © 2018 - DiscordianServer

    -
    -
    - - - - - - - - - - - - - @RenderSection("Scripts", required: false) - - diff --git a/DiscordianServer/Pages/_ViewImports.cshtml b/DiscordianServer/Pages/_ViewImports.cshtml deleted file mode 100644 index f002073..0000000 --- a/DiscordianServer/Pages/_ViewImports.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@using DiscordianServer -@namespace DiscordianServer.Pages -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/DiscordianServer/Startup.cs b/DiscordianServer/Startup.cs deleted file mode 100644 index dfe2e29..0000000 --- a/DiscordianServer/Startup.cs +++ /dev/null @@ -1,56 +0,0 @@ -using DiscordianServer.Hubs; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace DiscordianServer -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.Configure(options => - { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.MinimumSameSitePolicy = SameSiteMode.None; - }); - - services.AddMvc(); - - services.AddSignalR(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); - } - - app.UseStaticFiles(); - app.UseCookiePolicy(); - - app.UseMvc(); - - app.UseSignalR(routes => - { - routes.MapHub("/hubs/discordian"); - }); - } - } -} diff --git a/DiscordianServer/bundleconfig.json b/DiscordianServer/bundleconfig.json deleted file mode 100644 index 6d3f9a5..0000000 --- a/DiscordianServer/bundleconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -// Configure bundling and minification for the project. -// More info at https://go.microsoft.com/fwlink/?LinkId=808241 -[ - { - "outputFileName": "wwwroot/css/site.min.css", - // An array of relative input file paths. Globbing patterns supported - "inputFiles": [ - "wwwroot/css/site.css" - ] - }, - { - "outputFileName": "wwwroot/js/site.min.js", - "inputFiles": [ - "wwwroot/js/site.js" - ], - // Optionally specify minification options - "minify": { - "enabled": true, - "renameLocals": true - }, - // Optionally generate .map file - "sourceMap": false - } -] diff --git a/DiscordianServer/published/DiscordianServer.PrecompiledViews.dll b/DiscordianServer/published/DiscordianServer.PrecompiledViews.dll deleted file mode 100644 index d3963b7..0000000 Binary files a/DiscordianServer/published/DiscordianServer.PrecompiledViews.dll and /dev/null differ diff --git a/DiscordianServer/published/DiscordianServer.deps.json b/DiscordianServer/published/DiscordianServer.deps.json deleted file mode 100644 index 64c3b24..0000000 --- a/DiscordianServer/published/DiscordianServer.deps.json +++ /dev/null @@ -1,5199 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETCoreApp,Version=v2.1", - "signature": "b074aadb418662c54a5a55ee96048f11002da8ae" - }, - "compilationOptions": { - "defines": [ - "TRACE", - "RELEASE", - "NETCOREAPP2_1" - ], - "languageVersion": "", - "platform": "", - "allowUnsafe": false, - "warningsAsErrors": false, - "optimize": true, - "keyFile": "", - "emitEntryPoint": true, - "xmlDoc": false, - "debugType": "portable" - }, - "targets": { - ".NETCoreApp,Version=v2.1": { - "DiscordianServer/1.0.0": { - "dependencies": { - "Microsoft.AspNetCore.App": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authorization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authorization.Policy": "2.1.0-preview1-final", - "Microsoft.AspNetCore.SignalR": "1.0.0-preview1-final", - "Microsoft.NETCore.App": "2.1.0-preview1-26216-03" - }, - "runtime": { - "DiscordianServer.dll": {} - }, - "compile": { - "DiscordianServer.dll": {} - } - }, - "Microsoft.NETCore.App/2.1.0-preview1-26216-03": { - "dependencies": { - "Microsoft.NETCore.DotNetHostPolicy": "2.1.0-preview1-26216-03", - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "NETStandard.Library": "2.0.1" - }, - "compile": { - "ref/netcoreapp2.1/Microsoft.CSharp.dll": {}, - "ref/netcoreapp2.1/Microsoft.VisualBasic.dll": {}, - "ref/netcoreapp2.1/Microsoft.Win32.Primitives.dll": {}, - "ref/netcoreapp2.1/System.AppContext.dll": {}, - "ref/netcoreapp2.1/System.Buffers.dll": {}, - "ref/netcoreapp2.1/System.Collections.Concurrent.dll": {}, - "ref/netcoreapp2.1/System.Collections.Immutable.dll": {}, - "ref/netcoreapp2.1/System.Collections.NonGeneric.dll": {}, - "ref/netcoreapp2.1/System.Collections.Specialized.dll": {}, - "ref/netcoreapp2.1/System.Collections.dll": {}, - "ref/netcoreapp2.1/System.ComponentModel.Annotations.dll": {}, - "ref/netcoreapp2.1/System.ComponentModel.DataAnnotations.dll": {}, - "ref/netcoreapp2.1/System.ComponentModel.EventBasedAsync.dll": {}, - "ref/netcoreapp2.1/System.ComponentModel.Primitives.dll": {}, - "ref/netcoreapp2.1/System.ComponentModel.TypeConverter.dll": {}, - "ref/netcoreapp2.1/System.ComponentModel.dll": {}, - "ref/netcoreapp2.1/System.Configuration.dll": {}, - "ref/netcoreapp2.1/System.Console.dll": {}, - "ref/netcoreapp2.1/System.Core.dll": {}, - "ref/netcoreapp2.1/System.Data.Common.dll": {}, - "ref/netcoreapp2.1/System.Data.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.Contracts.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.Debug.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.DiagnosticSource.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.FileVersionInfo.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.Process.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.StackTrace.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.TextWriterTraceListener.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.Tools.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.TraceSource.dll": {}, - "ref/netcoreapp2.1/System.Diagnostics.Tracing.dll": {}, - "ref/netcoreapp2.1/System.Drawing.Primitives.dll": {}, - "ref/netcoreapp2.1/System.Drawing.dll": {}, - "ref/netcoreapp2.1/System.Dynamic.Runtime.dll": {}, - "ref/netcoreapp2.1/System.Globalization.Calendars.dll": {}, - "ref/netcoreapp2.1/System.Globalization.Extensions.dll": {}, - "ref/netcoreapp2.1/System.Globalization.dll": {}, - "ref/netcoreapp2.1/System.IO.Compression.Brotli.dll": {}, - "ref/netcoreapp2.1/System.IO.Compression.FileSystem.dll": {}, - "ref/netcoreapp2.1/System.IO.Compression.ZipFile.dll": {}, - "ref/netcoreapp2.1/System.IO.Compression.dll": {}, - "ref/netcoreapp2.1/System.IO.FileSystem.DriveInfo.dll": {}, - "ref/netcoreapp2.1/System.IO.FileSystem.Primitives.dll": {}, - "ref/netcoreapp2.1/System.IO.FileSystem.Watcher.dll": {}, - "ref/netcoreapp2.1/System.IO.FileSystem.dll": {}, - "ref/netcoreapp2.1/System.IO.IsolatedStorage.dll": {}, - "ref/netcoreapp2.1/System.IO.MemoryMappedFiles.dll": {}, - "ref/netcoreapp2.1/System.IO.Pipes.dll": {}, - "ref/netcoreapp2.1/System.IO.UnmanagedMemoryStream.dll": {}, - "ref/netcoreapp2.1/System.IO.dll": {}, - "ref/netcoreapp2.1/System.Linq.Expressions.dll": {}, - "ref/netcoreapp2.1/System.Linq.Parallel.dll": {}, - "ref/netcoreapp2.1/System.Linq.Queryable.dll": {}, - "ref/netcoreapp2.1/System.Linq.dll": {}, - "ref/netcoreapp2.1/System.Memory.dll": {}, - "ref/netcoreapp2.1/System.Net.Http.dll": {}, - "ref/netcoreapp2.1/System.Net.HttpListener.dll": {}, - "ref/netcoreapp2.1/System.Net.Mail.dll": {}, - "ref/netcoreapp2.1/System.Net.NameResolution.dll": {}, - "ref/netcoreapp2.1/System.Net.NetworkInformation.dll": {}, - "ref/netcoreapp2.1/System.Net.Ping.dll": {}, - "ref/netcoreapp2.1/System.Net.Primitives.dll": {}, - "ref/netcoreapp2.1/System.Net.Requests.dll": {}, - "ref/netcoreapp2.1/System.Net.Security.dll": {}, - "ref/netcoreapp2.1/System.Net.ServicePoint.dll": {}, - "ref/netcoreapp2.1/System.Net.Sockets.dll": {}, - "ref/netcoreapp2.1/System.Net.WebClient.dll": {}, - "ref/netcoreapp2.1/System.Net.WebHeaderCollection.dll": {}, - "ref/netcoreapp2.1/System.Net.WebProxy.dll": {}, - "ref/netcoreapp2.1/System.Net.WebSockets.Client.dll": {}, - "ref/netcoreapp2.1/System.Net.WebSockets.dll": {}, - "ref/netcoreapp2.1/System.Net.dll": {}, - "ref/netcoreapp2.1/System.Numerics.Vectors.dll": {}, - "ref/netcoreapp2.1/System.Numerics.dll": {}, - "ref/netcoreapp2.1/System.ObjectModel.dll": {}, - "ref/netcoreapp2.1/System.Reflection.DispatchProxy.dll": {}, - "ref/netcoreapp2.1/System.Reflection.Emit.ILGeneration.dll": {}, - "ref/netcoreapp2.1/System.Reflection.Emit.Lightweight.dll": {}, - "ref/netcoreapp2.1/System.Reflection.Emit.dll": {}, - "ref/netcoreapp2.1/System.Reflection.Extensions.dll": {}, - "ref/netcoreapp2.1/System.Reflection.Metadata.dll": {}, - "ref/netcoreapp2.1/System.Reflection.Primitives.dll": {}, - "ref/netcoreapp2.1/System.Reflection.TypeExtensions.dll": {}, - "ref/netcoreapp2.1/System.Reflection.dll": {}, - "ref/netcoreapp2.1/System.Resources.Reader.dll": {}, - "ref/netcoreapp2.1/System.Resources.ResourceManager.dll": {}, - "ref/netcoreapp2.1/System.Resources.Writer.dll": {}, - "ref/netcoreapp2.1/System.Runtime.CompilerServices.VisualC.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Extensions.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Handles.dll": {}, - "ref/netcoreapp2.1/System.Runtime.InteropServices.RuntimeInformation.dll": {}, - "ref/netcoreapp2.1/System.Runtime.InteropServices.WindowsRuntime.dll": {}, - "ref/netcoreapp2.1/System.Runtime.InteropServices.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Intrinsics.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Loader.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Numerics.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Serialization.Formatters.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Serialization.Json.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Serialization.Primitives.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Serialization.Xml.dll": {}, - "ref/netcoreapp2.1/System.Runtime.Serialization.dll": {}, - "ref/netcoreapp2.1/System.Runtime.dll": {}, - "ref/netcoreapp2.1/System.Security.Claims.dll": {}, - "ref/netcoreapp2.1/System.Security.Cryptography.Algorithms.dll": {}, - "ref/netcoreapp2.1/System.Security.Cryptography.Csp.dll": {}, - "ref/netcoreapp2.1/System.Security.Cryptography.Encoding.dll": {}, - "ref/netcoreapp2.1/System.Security.Cryptography.Primitives.dll": {}, - "ref/netcoreapp2.1/System.Security.Cryptography.X509Certificates.dll": {}, - "ref/netcoreapp2.1/System.Security.Principal.dll": {}, - "ref/netcoreapp2.1/System.Security.SecureString.dll": {}, - "ref/netcoreapp2.1/System.Security.dll": {}, - "ref/netcoreapp2.1/System.ServiceModel.Web.dll": {}, - "ref/netcoreapp2.1/System.ServiceProcess.dll": {}, - "ref/netcoreapp2.1/System.Text.Encoding.Extensions.dll": {}, - "ref/netcoreapp2.1/System.Text.Encoding.dll": {}, - "ref/netcoreapp2.1/System.Text.RegularExpressions.dll": {}, - "ref/netcoreapp2.1/System.Threading.Overlapped.dll": {}, - "ref/netcoreapp2.1/System.Threading.Tasks.Dataflow.dll": {}, - "ref/netcoreapp2.1/System.Threading.Tasks.Extensions.dll": {}, - "ref/netcoreapp2.1/System.Threading.Tasks.Parallel.dll": {}, - "ref/netcoreapp2.1/System.Threading.Tasks.dll": {}, - "ref/netcoreapp2.1/System.Threading.Thread.dll": {}, - "ref/netcoreapp2.1/System.Threading.ThreadPool.dll": {}, - "ref/netcoreapp2.1/System.Threading.Timer.dll": {}, - "ref/netcoreapp2.1/System.Threading.dll": {}, - "ref/netcoreapp2.1/System.Transactions.Local.dll": {}, - "ref/netcoreapp2.1/System.Transactions.dll": {}, - "ref/netcoreapp2.1/System.ValueTuple.dll": {}, - "ref/netcoreapp2.1/System.Web.HttpUtility.dll": {}, - "ref/netcoreapp2.1/System.Web.dll": {}, - "ref/netcoreapp2.1/System.Windows.dll": {}, - "ref/netcoreapp2.1/System.Xml.Linq.dll": {}, - "ref/netcoreapp2.1/System.Xml.ReaderWriter.dll": {}, - "ref/netcoreapp2.1/System.Xml.Serialization.dll": {}, - "ref/netcoreapp2.1/System.Xml.XDocument.dll": {}, - "ref/netcoreapp2.1/System.Xml.XPath.XDocument.dll": {}, - "ref/netcoreapp2.1/System.Xml.XPath.dll": {}, - "ref/netcoreapp2.1/System.Xml.XmlDocument.dll": {}, - "ref/netcoreapp2.1/System.Xml.XmlSerializer.dll": {}, - "ref/netcoreapp2.1/System.Xml.dll": {}, - "ref/netcoreapp2.1/System.dll": {}, - "ref/netcoreapp2.1/WindowsBase.dll": {}, - "ref/netcoreapp2.1/mscorlib.dll": {}, - "ref/netcoreapp2.1/netstandard.dll": {} - } - }, - "Microsoft.NETCore.DotNetAppHost/2.1.0-preview1-26216-03": {}, - "Microsoft.NETCore.DotNetHostPolicy/2.1.0-preview1-26216-03": { - "dependencies": { - "Microsoft.NETCore.DotNetHostResolver": "2.1.0-preview1-26216-03" - } - }, - "Microsoft.NETCore.DotNetHostResolver/2.1.0-preview1-26216-03": { - "dependencies": { - "Microsoft.NETCore.DotNetAppHost": "2.1.0-preview1-26216-03" - } - }, - "Microsoft.NETCore.Platforms/2.1.0-preview1-26216-02": {}, - "NETStandard.Library/2.0.1": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02" - } - }, - "Libuv/1.10.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02" - }, - "compileOnly": true - }, - "Microsoft.AspNetCore/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Diagnostics": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.IISIntegration": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Https": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.CommandLine": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Json": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.UserSecrets": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Console": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Debug": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Antiforgery/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebUtilities": "2.1.0-preview1-final", - "Microsoft.Extensions.ObjectPool": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.App/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Antiforgery": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Cookies": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Facebook": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Google": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.JwtBearer": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.MicrosoftAccount": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.OpenIdConnect": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authentication.Twitter": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authorization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authorization.Policy": "2.1.0-preview1-final", - "Microsoft.AspNetCore.AzureAppServicesIntegration": "2.1.0-preview1-final", - "Microsoft.AspNetCore.CookiePolicy": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Cors": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Cryptography.Internal": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Cryptography.KeyDerivation": "2.1.0-preview1-final", - "Microsoft.AspNetCore.DataProtection": "2.1.0-preview1-final", - "Microsoft.AspNetCore.DataProtection.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.DataProtection.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Diagnostics": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Html.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Features": "2.1.0-preview1-final", - "Microsoft.AspNetCore.HttpOverrides": "2.1.0-preview1-final", - "Microsoft.AspNetCore.HttpsPolicy": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Identity": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Identity.UI": "2.1.0-preview1-final", - "Microsoft.AspNetCore.JsonPatch": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Localization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Localization.Routing": "2.1.0-preview1-final", - "Microsoft.AspNetCore.MiddlewareAnalysis": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.ApiExplorer": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Cors": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Formatters.Xml": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Localization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Razor": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Razor.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.RazorPages": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.TagHelpers": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.0-preview1-final", - "Microsoft.AspNetCore.NodeServices": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Owin": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Protocols.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor.Design": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor.Language": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor.Runtime": "2.1.0-preview1-final", - "Microsoft.AspNetCore.ResponseCaching": "2.1.0-preview1-final", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.ResponseCompression": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Rewrite": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.HttpSys": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.IISIntegration": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Https": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Session": "2.1.0-preview1-final", - "Microsoft.AspNetCore.SignalR": "1.0.0-preview1-final", - "Microsoft.AspNetCore.SignalR.Common": "1.0.0-preview1-final", - "Microsoft.AspNetCore.SignalR.Core": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Abstractions": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Common.Http": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Http": "1.0.0-preview1-final", - "Microsoft.AspNetCore.SpaServices": "2.1.0-preview1-final", - "Microsoft.AspNetCore.SpaServices.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.StaticFiles": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebSockets": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebUtilities": "2.1.0-preview1-final", - "Microsoft.CodeAnalysis.Razor": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.Attributes": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.Design": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.InMemory": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.SqlServer": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.Tools": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Memory": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.SqlServer": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Binder": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.CommandLine": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Ini": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Json": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.UserSecrets": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Xml": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.DiagnosticAdapter": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Composite": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Embedded": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0-preview1-final", - "Microsoft.Extensions.FileSystemGlobbing": "2.1.0-preview1-final", - "Microsoft.Extensions.Hosting": "2.1.0-preview1-final", - "Microsoft.Extensions.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Http": "2.1.0-preview1-final", - "Microsoft.Extensions.Identity.Core": "2.1.0-preview1-final", - "Microsoft.Extensions.Identity.Stores": "2.1.0-preview1-final", - "Microsoft.Extensions.Localization": "2.1.0-preview1-final", - "Microsoft.Extensions.Localization.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.AzureAppServices": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Console": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Debug": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.EventSource": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.TraceSource": "2.1.0-preview1-final", - "Microsoft.Extensions.ObjectPool": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "Microsoft.Extensions.Options.ConfigurationExtensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final", - "Microsoft.Extensions.WebEncoders": "2.1.0-preview1-final", - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final" - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.DataProtection": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "Microsoft.Extensions.WebEncoders": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.Cookies/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.Core/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.Facebook/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Facebook.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.Google/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Google.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.JwtBearer/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0-preview1-final", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.2.0-preview2-41113220915" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.MicrosoftAccount/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.MicrosoftAccount.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.OAuth/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OAuth.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.OpenIdConnect/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0-preview1-final", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.2.0-preview2-41113220915" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OpenIdConnect.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authentication.Twitter/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Twitter.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authorization/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Authorization.Policy/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authorization": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.AzureAppServicesIntegration/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.AzureAppServices": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.AzureAppServicesIntegration.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.CookiePolicy/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.CookiePolicy.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Cors/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Cryptography.Internal/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Cryptography.KeyDerivation/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Cryptography.Internal": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.DataProtection/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Cryptography.Internal": "2.1.0-preview1-final", - "Microsoft.AspNetCore.DataProtection.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "Microsoft.Win32.Registry": "4.5.0-preview1-26216-02", - "System.Security.Cryptography.Xml": "4.5.0-preview1-26216-02", - "System.Security.Principal.Windows": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.DataProtection.Abstractions/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.DataProtection.Extensions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Extensions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Diagnostics/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebUtilities": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02", - "System.Reflection.Metadata": "1.6.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Diagnostics.Abstractions/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Hosting/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0-preview1-final", - "Microsoft.Extensions.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02", - "System.Reflection.Metadata": "1.6.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Hosting.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Hosting.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Html.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "System.Text.Encodings.Web": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Http/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebUtilities": "2.1.0-preview1-final", - "Microsoft.Extensions.ObjectPool": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Http.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.1.0-preview1-final", - "System.Text.Encodings.Web": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Http.Extensions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final", - "System.Buffers": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Http.Features/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.HttpOverrides/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.HttpOverrides.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.HttpsPolicy/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Binder": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.HttpsPolicy.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Identity/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Cookies": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Cryptography.KeyDerivation": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Identity.Core": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Identity.EntityFrameworkCore/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Identity": "2.1.0-preview1-final", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0-preview1-final", - "Microsoft.Extensions.Identity.Stores": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Identity.UI/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Identity": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc": "2.1.0-preview1-final", - "Microsoft.AspNetCore.StaticFiles": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Embedded": "2.1.0-preview1-final", - "Microsoft.Extensions.Identity.Stores": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.PrecompiledViews.dll": {}, - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.JsonPatch/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.CSharp": "4.5.0-preview1-26216-02", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Localization/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Localization.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Localization.Routing/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Localization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.Routing.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.MiddlewareAnalysis/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.MiddlewareAnalysis.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.ApiExplorer": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Cors": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Localization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.RazorPages": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.TagHelpers": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor.Design": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Memory": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0-preview1-final", - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.ApiExplorer/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Core/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Authorization.Policy": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyModel": "2.1.0-preview1-26216-03", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02", - "System.Threading.Tasks.Extensions": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Cors/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Cors": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.DataAnnotations/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final", - "Microsoft.Extensions.Localization": "2.1.0-preview1-final", - "System.ComponentModel.Annotations": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Formatters.Json/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.JsonPatch": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Formatters.Xml/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Xml.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Localization/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Localization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Razor": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final", - "Microsoft.Extensions.Localization": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Razor/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor.Runtime": "2.1.0-preview1-final", - "Microsoft.CodeAnalysis.CSharp": "2.6.1", - "Microsoft.CodeAnalysis.Razor": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Memory": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Composite": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Razor.Extensions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Razor.Language": "2.1.0-preview1-final", - "Microsoft.CodeAnalysis.Razor": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.RazorPages": "2.1.0-preview1-final" - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.RazorPages/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.TagHelpers/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor.Runtime": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Memory": "2.1.0-preview1-final", - "Microsoft.Extensions.FileSystemGlobbing": "2.1.0-preview1-final", - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Mvc.ViewFeatures/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Antiforgery": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Html.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.0-preview1-final", - "Microsoft.Extensions.WebEncoders": "2.1.0-preview1-final", - "Newtonsoft.Json.Bson": "1.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.NodeServices/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Console": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.NodeServices.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Owin/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Owin.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Protocols.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.1.0-preview1-final", - "System.Buffers": "4.5.0-preview1-26216-02", - "System.IO.Pipelines": "0.1.0-preview1-180216-4", - "System.Memory": "4.5.0-preview1-26216-02", - "System.Numerics.Vectors": "4.5.0-preview1-26216-02", - "System.Runtime.CompilerServices.Unsafe": "4.5.0-preview1-26216-02", - "System.Text.Encodings.Web.Utf8": "0.1.0-preview1-180216-4" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Protocols.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Razor/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Html.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Razor.Design/2.1.0-preview1-final": { - "compileOnly": true - }, - "Microsoft.AspNetCore.Razor.Language/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Razor.Runtime/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Html.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Razor": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.ResponseCaching/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Memory": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.ResponseCaching.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.ResponseCompression/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCompression.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Rewrite/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Rewrite.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Routing/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.ObjectPool": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Routing.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.HttpSys/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting": "2.1.0-preview1-final", - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final", - "Microsoft.Win32.Registry": "4.5.0-preview1-26216-02", - "System.Security.Principal.Windows": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.HttpSys.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.IISIntegration/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.HttpOverrides": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.Buffers": "4.5.0-preview1-26216-02", - "System.IO.Pipelines": "0.1.0-preview1-180216-4", - "System.Memory": "4.5.0-preview1-26216-02", - "System.Numerics.Vectors": "4.5.0-preview1-26216-02", - "System.Runtime.CompilerServices.Unsafe": "4.5.0-preview1-26216-02", - "System.Security.Principal.Windows": "4.5.0-preview1-26216-02", - "System.Text.Encodings.Web.Utf8": "0.1.0-preview1-180216-4" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.Kestrel/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Core": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Https": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.Kestrel.Core/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebUtilities": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Binder": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final", - "System.Memory": "4.5.0-preview1-26216-02", - "System.Runtime.CompilerServices.Unsafe": "4.5.0-preview1-26216-02", - "System.Security.Cryptography.Cng": "4.5.0-preview1-26216-02", - "System.Threading.Tasks.Extensions": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.Kestrel.Https/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Core": "2.1.0-preview1-final" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Protocols.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/2.1.0-preview1-final": { - "dependencies": { - "Libuv": "1.10.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Session/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Session.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.SignalR/1.0.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.SignalR.Core": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Http": "1.0.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.SignalR.Common/1.0.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1", - "System.Buffers": "4.5.0-preview1-26216-02", - "System.Memory": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Common.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.SignalR.Core/1.0.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authorization": "2.1.0-preview1-final", - "Microsoft.AspNetCore.SignalR.Common": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Abstractions": "1.0.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1", - "System.Reflection.Emit": "4.3.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Core.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Sockets/1.0.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Abstractions": "1.0.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "System.Threading.Channels": "4.5.0-preview1-26216-02", - "System.Threading.Tasks.Extensions": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Sockets.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Sockets.Abstractions/1.0.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.1.0-preview1-final", - "System.Threading.Channels": "4.5.0-preview1-26216-02", - "System.Threading.Tasks.Extensions": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Sockets.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Sockets.Common.Http/1.0.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Sockets.Common.Http.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.Sockets.Http/1.0.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Authorization.Policy": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Routing": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Sockets": "1.0.0-preview1-final", - "Microsoft.AspNetCore.Sockets.Common.Http": "1.0.0-preview1-final", - "Microsoft.AspNetCore.WebSockets": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1", - "System.Memory": "4.5.0-preview1-26216-02", - "System.Threading.Channels": "4.5.0-preview1-26216-02", - "System.Threading.Tasks.Extensions": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Sockets.Http.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.SpaServices/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Mvc.TagHelpers": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.0-preview1-final", - "Microsoft.AspNetCore.NodeServices": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.SpaServices.Extensions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.SpaServices": "2.1.0-preview1-final", - "Microsoft.AspNetCore.StaticFiles": "2.1.0-preview1-final", - "Microsoft.AspNetCore.WebSockets": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.Extensions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.StaticFiles/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.WebEncoders": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.WebSockets/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.Net.WebSockets.WebSocketProtocol": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebSockets.dll": {} - }, - "compileOnly": true - }, - "Microsoft.AspNetCore.WebUtilities/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Net.Http.Headers": "2.1.0-preview1-final", - "System.Text.Encodings.Web": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": {} - }, - "compileOnly": true - }, - "Microsoft.CodeAnalysis.Analyzers/1.1.0": { - "compileOnly": true - }, - "Microsoft.CodeAnalysis.Common/2.6.1": { - "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "1.1.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Collections.Immutable": "1.5.0-preview1-26216-02", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.FileVersionInfo": "4.3.0", - "System.Diagnostics.StackTrace": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Metadata": "1.6.0-preview1-26216-02", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.CodePages": "4.5.0-preview1-26216-02", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Parallel": "4.3.0", - "System.Threading.Thread": "4.3.0", - "System.ValueTuple": "4.5.0-preview1-26216-02", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XPath.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/Microsoft.CodeAnalysis.dll": {} - }, - "compileOnly": true - }, - "Microsoft.CodeAnalysis.CSharp/2.6.1": { - "dependencies": { - "Microsoft.CodeAnalysis.Common": "2.6.1" - }, - "compile": { - "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll": {} - }, - "compileOnly": true - }, - "Microsoft.CodeAnalysis.Razor/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Razor.Language": "2.1.0-preview1-final", - "Microsoft.CodeAnalysis.CSharp": "2.6.1", - "Microsoft.CodeAnalysis.Common": "2.6.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll": {} - }, - "compileOnly": true - }, - "Microsoft.CSharp/4.5.0-preview1-26216-02": { - "compileOnly": true - }, - "Microsoft.DotNet.PlatformAbstractions/2.1.0-preview1-26216-03": { - "dependencies": { - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.EntityFrameworkCore.Attributes": "2.1.0-preview1-final", - "Microsoft.Extensions.Caching.Memory": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Remotion.Linq": "2.2.0-alpha-002", - "System.Collections.Immutable": "1.5.0-preview1-26216-02", - "System.ComponentModel.Annotations": "4.5.0-preview1-26216-02", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02", - "System.Interactive.Async": "3.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore.Attributes/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Attributes.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore.Design/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.CSharp": "4.5.0-preview1-26216-02", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Design.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore.InMemory/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.EntityFrameworkCore": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.InMemory.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore.Relational/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.EntityFrameworkCore": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Relational.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore.SqlServer/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "2.1.0-preview1-final", - "System.Data.SqlClient": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.SqlServer.dll": {} - }, - "compileOnly": true - }, - "Microsoft.EntityFrameworkCore.Tools/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.EntityFrameworkCore.Design": "2.1.0-preview1-final" - }, - "compileOnly": true - }, - "Microsoft.Extensions.Caching.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Caching.Memory/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Caching.SqlServer/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.Data.SqlClient": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.SqlServer.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.Binder/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.CommandLine/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.CommandLine.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.Ini/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Ini.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.Json/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.UserSecrets/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.Json": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Configuration.Xml/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0-preview1-final", - "System.Security.Cryptography.Xml": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Xml.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.DependencyInjection/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.DependencyModel/2.1.0-preview1-26216-03": { - "dependencies": { - "Microsoft.DotNet.PlatformAbstractions": "2.1.0-preview1-26216-03", - "Newtonsoft.Json": "10.0.1", - "System.Diagnostics.Debug": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Linq": "4.3.0" - }, - "compile": { - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.DiagnosticAdapter/2.1.0-preview1-final": { - "dependencies": { - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netcoreapp2.0/Microsoft.Extensions.DiagnosticAdapter.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.FileProviders.Composite/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.FileProviders.Embedded/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.FileProviders.Physical/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.FileSystemGlobbing": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.FileSystemGlobbing/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Hosting/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0-preview1-final", - "Microsoft.Extensions.Hosting.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Hosting.Abstractions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Http/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Http.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Identity.Core/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.AspNetCore.Cryptography.KeyDerivation": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.ComponentModel.Annotations": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Identity.Core.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Identity.Stores/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Identity.Core": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "System.ComponentModel.Annotations": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Identity.Stores.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Localization/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Localization.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Localization.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Localization.Abstractions/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.Binder": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.Abstractions/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.AzureAppServices/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Json": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Configuration": "2.1.0-preview1-final", - "System.ValueTuple": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.AzureAppServices.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.Configuration/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Options.ConfigurationExtensions": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.Console/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Microsoft.Extensions.Logging.Configuration": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.Debug/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Logging": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Debug.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.EventSource/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Logging": "2.1.0-preview1-final", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.EventSource.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Logging.TraceSource/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Logging": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.TraceSource.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.ObjectPool/2.1.0-preview1-final": { - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Options/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Options.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Configuration.Binder": "2.1.0-preview1-final", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.Primitives/2.1.0-preview1-final": { - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Extensions.WebEncoders/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0-preview1-final", - "Microsoft.Extensions.Options": "2.1.0-preview1-final", - "System.Text.Encodings.Web": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll": {} - }, - "compileOnly": true - }, - "Microsoft.IdentityModel.Logging/5.2.0-preview2-41113220915": { - "dependencies": { - "NETStandard.Library": "2.0.1", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Logging.dll": {} - }, - "compileOnly": true - }, - "Microsoft.IdentityModel.Protocols/5.2.0-preview2-41113220915": { - "dependencies": { - "Microsoft.IdentityModel.Logging": "5.2.0-preview2-41113220915", - "Microsoft.IdentityModel.Tokens": "5.2.0-preview2-41113220915", - "NETStandard.Library": "2.0.1", - "System.Collections.Specialized": "4.3.0", - "System.Diagnostics.Contracts": "4.3.0", - "System.Net.Http": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.dll": {} - }, - "compileOnly": true - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect/5.2.0-preview2-41113220915": { - "dependencies": { - "Microsoft.IdentityModel.Protocols": "5.2.0-preview2-41113220915", - "NETStandard.Library": "2.0.1", - "Newtonsoft.Json": "10.0.1", - "System.Dynamic.Runtime": "4.3.0", - "System.IdentityModel.Tokens.Jwt": "5.2.0-preview2-41113220915" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {} - }, - "compileOnly": true - }, - "Microsoft.IdentityModel.Tokens/5.2.0-preview2-41113220915": { - "dependencies": { - "Microsoft.IdentityModel.Logging": "5.2.0-preview2-41113220915", - "NETStandard.Library": "2.0.1", - "Newtonsoft.Json": "10.0.1", - "System.Collections": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Serialization.Xml": "4.3.0", - "System.Security.Claims": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.dll": {} - }, - "compileOnly": true - }, - "Microsoft.Net.Http.Headers/2.1.0-preview1-final": { - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.0-preview1-final", - "System.Buffers": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": {} - }, - "compileOnly": true - }, - "Microsoft.NETCore.Targets/1.1.0": { - "compileOnly": true - }, - "Microsoft.Win32.Registry/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Buffers": "4.5.0-preview1-26216-02", - "System.Security.AccessControl": "4.5.0-preview1-26216-02", - "System.Security.Principal.Windows": "4.5.0-preview1-26216-02" - }, - "compile": { - "ref/netstandard2.0/Microsoft.Win32.Registry.dll": {} - }, - "compileOnly": true - }, - "Newtonsoft.Json/10.0.1": { - "dependencies": { - "Microsoft.CSharp": "4.5.0-preview1-26216-02", - "System.Collections": "4.3.0", - "System.ComponentModel.TypeConverter": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Runtime.Serialization.Formatters": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/Newtonsoft.Json.dll": {} - }, - "compileOnly": true - }, - "Newtonsoft.Json.Bson/1.0.1": { - "dependencies": { - "NETStandard.Library": "2.0.1", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard1.3/Newtonsoft.Json.Bson.dll": {} - }, - "compileOnly": true - }, - "Remotion.Linq/2.2.0-alpha-002": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Linq.Queryable": "4.0.1", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "lib/netstandard1.0/Remotion.Linq.dll": {} - }, - "compileOnly": true - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.native.System/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compileOnly": true - }, - "runtime.native.System.Data.SqlClient.sni/4.4.0": { - "dependencies": { - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" - }, - "compileOnly": true - }, - "runtime.native.System.IO.Compression/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compileOnly": true - }, - "runtime.native.System.Net.Http/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compileOnly": true - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" - }, - "compileOnly": true - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compileOnly": true - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "compileOnly": true - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "compileOnly": true - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "compileOnly": true - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "compileOnly": true - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "compileOnly": true - }, - "System.AppContext/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Buffers/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02" - }, - "compileOnly": true - }, - "System.Buffers.Primitives/0.1.0-preview1-180216-4": { - "dependencies": { - "NETStandard.Library": "2.0.1", - "System.Buffers": "4.5.0-preview1-26216-02", - "System.Memory": "4.5.0-preview1-26216-02", - "System.Runtime.CompilerServices.Unsafe": "4.5.0-preview1-26216-02", - "System.ValueTuple": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard1.1/System.Buffers.Primitives.dll": {} - }, - "compileOnly": true - }, - "System.Collections/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Collections.Concurrent/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compileOnly": true - }, - "System.Collections.Immutable/1.5.0-preview1-26216-02": { - "compileOnly": true - }, - "System.Collections.NonGeneric/4.3.0": { - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.Collections.Specialized/4.3.0": { - "dependencies": { - "System.Collections.NonGeneric": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.ComponentModel/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.ComponentModel.Annotations/4.5.0-preview1-26216-02": { - "compileOnly": true - }, - "System.ComponentModel.Primitives/4.3.0": { - "dependencies": { - "System.ComponentModel": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.ComponentModel.TypeConverter/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.NonGeneric": "4.3.0", - "System.Collections.Specialized": "4.3.0", - "System.ComponentModel": "4.3.0", - "System.ComponentModel.Primitives": "4.3.0", - "System.Globalization": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.Console/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compileOnly": true - }, - "System.Data.SqlClient/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.Win32.Registry": "4.5.0-preview1-26216-02", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02", - "System.Memory": "4.5.0-preview1-26216-02", - "System.Security.Principal.Windows": "4.5.0-preview1-26216-02", - "System.Text.Encoding.CodePages": "4.5.0-preview1-26216-02", - "runtime.native.System.Data.SqlClient.sni": "4.4.0" - }, - "compile": { - "ref/netstandard2.0/System.Data.SqlClient.dll": {} - }, - "compileOnly": true - }, - "System.Diagnostics.Contracts/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Diagnostics.Debug/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Diagnostics.DiagnosticSource/4.5.0-preview1-26216-02": { - "compileOnly": true - }, - "System.Diagnostics.FileVersionInfo/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Reflection.Metadata": "1.6.0-preview1-26216-02", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - "compileOnly": true - }, - "System.Diagnostics.StackTrace/4.3.0": { - "dependencies": { - "System.IO.FileSystem": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Metadata": "1.6.0-preview1-26216-02", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Diagnostics.Tools/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Diagnostics.Tracing/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Dynamic.Runtime/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.Globalization/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Globalization.Calendars/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Globalization.Extensions/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - "compileOnly": true - }, - "System.IdentityModel.Tokens.Jwt/5.2.0-preview2-41113220915": { - "dependencies": { - "Microsoft.IdentityModel.Tokens": "5.2.0-preview2-41113220915", - "NETStandard.Library": "2.0.1", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard1.4/System.IdentityModel.Tokens.Jwt.dll": {} - }, - "compileOnly": true - }, - "System.Interactive.Async/3.1.1": { - "dependencies": { - "NETStandard.Library": "2.0.1" - }, - "compile": { - "lib/netstandard1.3/System.Interactive.Async.dll": {} - }, - "compileOnly": true - }, - "System.IO/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compileOnly": true - }, - "System.IO.Compression/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Buffers": "4.5.0-preview1-26216-02", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - }, - "compileOnly": true - }, - "System.IO.FileSystem/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compileOnly": true - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.IO.Pipelines/0.1.0-preview1-180216-4": { - "dependencies": { - "System.Buffers.Primitives": "0.1.0-preview1-180216-4" - }, - "compile": { - "lib/netcoreapp2.1/System.IO.Pipelines.dll": {} - }, - "compileOnly": true - }, - "System.Linq/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compileOnly": true - }, - "System.Linq.Expressions/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.Linq.Queryable/4.0.1": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Memory/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02" - }, - "compileOnly": true - }, - "System.Net.Http/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.5.0-preview1-26216-02", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compileOnly": true - }, - "System.Net.Primitives/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compileOnly": true - }, - "System.Net.WebSockets.WebSocketProtocol/4.5.0-preview1-26216-02": { - "compile": { - "ref/netstandard2.0/System.Net.WebSockets.WebSocketProtocol.dll": {} - }, - "compileOnly": true - }, - "System.Numerics.Vectors/4.5.0-preview1-26216-02": { - "compileOnly": true - }, - "System.ObjectModel/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.Private.DataContractSerialization/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0", - "System.Xml.XmlSerializer": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection.Emit/4.3.0": { - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection.Extensions/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection.Metadata/1.6.0-preview1-26216-02": { - "dependencies": { - "System.Collections.Immutable": "1.5.0-preview1-26216-02" - }, - "compileOnly": true - }, - "System.Reflection.Primitives/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Reflection.TypeExtensions/4.3.0": { - "dependencies": { - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Resources.ResourceManager/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compileOnly": true - }, - "System.Runtime.CompilerServices.Unsafe/4.5.0-preview1-26216-02": { - "compile": { - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} - }, - "compileOnly": true - }, - "System.Runtime.Extensions/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.Handles/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.InteropServices/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.Numerics/4.3.0": { - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.Serialization.Formatters/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.Serialization.Primitives/4.3.0": { - "dependencies": { - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Runtime.Serialization.Xml/4.3.0": { - "dependencies": { - "System.IO": "4.3.0", - "System.Private.DataContractSerialization": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.AccessControl/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Security.Principal.Windows": "4.5.0-preview1-26216-02" - }, - "compile": { - "ref/netstandard2.0/System.Security.AccessControl.dll": {} - }, - "compileOnly": true - }, - "System.Security.Claims/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Security.Principal": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.Algorithms/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.Cng/4.5.0-preview1-26216-02": { - "compile": { - "ref/netcoreapp2.1/System.Security.Cryptography.Cng.dll": {} - }, - "compileOnly": true - }, - "System.Security.Cryptography.Csp/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.5.0-preview1-26216-02", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Cryptography.Xml/4.5.0-preview1-26216-02": { - "dependencies": { - "System.Security.Permissions": "4.5.0-preview1-26216-02" - }, - "compile": { - "ref/netstandard2.0/System.Security.Cryptography.Xml.dll": {} - }, - "compileOnly": true - }, - "System.Security.Permissions/4.5.0-preview1-26216-02": { - "dependencies": { - "System.Security.AccessControl": "4.5.0-preview1-26216-02" - }, - "compile": { - "ref/netstandard2.0/System.Security.Permissions.dll": {} - }, - "compileOnly": true - }, - "System.Security.Principal/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Security.Principal.Windows/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02" - }, - "compile": { - "ref/netstandard2.0/System.Security.Principal.Windows.dll": {} - }, - "compileOnly": true - }, - "System.Text.Encoding/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Text.Encoding.CodePages/4.5.0-preview1-26216-02": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02" - }, - "compileOnly": true - }, - "System.Text.Encoding.Extensions/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compileOnly": true - }, - "System.Text.Encodings.Web/4.5.0-preview1-26216-02": { - "compile": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": {} - }, - "compileOnly": true - }, - "System.Text.Encodings.Web.Utf8/0.1.0-preview1-180216-4": { - "dependencies": { - "NETStandard.Library": "2.0.1", - "System.Text.Primitives": "0.1.0-preview1-180216-4" - }, - "compile": { - "lib/netstandard1.1/System.Text.Encodings.Web.Utf8.dll": {} - }, - "compileOnly": true - }, - "System.Text.Primitives/0.1.0-preview1-180216-4": { - "dependencies": { - "NETStandard.Library": "2.0.1", - "System.Buffers.Primitives": "0.1.0-preview1-180216-4", - "System.Numerics.Vectors": "4.5.0-preview1-26216-02" - }, - "compile": { - "lib/netstandard1.1/System.Text.Primitives.dll": {} - }, - "compileOnly": true - }, - "System.Text.RegularExpressions/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Threading/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compileOnly": true - }, - "System.Threading.Channels/4.5.0-preview1-26216-02": { - "compile": { - "lib/netstandard2.0/System.Threading.Channels.dll": {} - }, - "compileOnly": true - }, - "System.Threading.Tasks/4.3.0": { - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0-preview1-26216-02", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.Threading.Tasks.Extensions/4.5.0-preview1-26216-02": { - "compileOnly": true - }, - "System.Threading.Tasks.Parallel/4.3.0": { - "dependencies": { - "System.Collections.Concurrent": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compileOnly": true - }, - "System.Threading.Thread/4.3.0": { - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compileOnly": true - }, - "System.ValueTuple/4.5.0-preview1-26216-02": { - "compileOnly": true - }, - "System.Xml.ReaderWriter/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.0-preview1-26216-02" - }, - "compileOnly": true - }, - "System.Xml.XDocument/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compileOnly": true - }, - "System.Xml.XmlDocument/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compileOnly": true - }, - "System.Xml.XmlSerializer/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - }, - "compileOnly": true - }, - "System.Xml.XPath/4.3.0": { - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compileOnly": true - }, - "System.Xml.XPath.XDocument/4.3.0": { - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XPath": "4.3.0" - }, - "compileOnly": true - } - } - }, - "libraries": { - "DiscordianServer/1.0.0": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "Microsoft.NETCore.App/2.1.0-preview1-26216-03": { - "type": "package", - "serviceable": true, - "sha512": "sha512-76o+qUv4nMXBgOoe8TiaxtrrzKcLF2aDt6LTsQRErOhGM25dsKssmxjUyJOZSD08FMIszDU2AAixtFZSsDr98Q==", - "path": "microsoft.netcore.app/2.1.0-preview1-26216-03", - "hashPath": "microsoft.netcore.app.2.1.0-preview1-26216-03.nupkg.sha512" - }, - "Microsoft.NETCore.DotNetAppHost/2.1.0-preview1-26216-03": { - "type": "package", - "serviceable": true, - "sha512": "sha512-TzUgHVt/7hw5Ea/agrXcs0ATN1UXGO2eWfbJ2jE7tEixn/OjUKsFOAp1G2+by2VzaMexr/RgmZ4+PiZFHEwy3Q==", - "path": "microsoft.netcore.dotnetapphost/2.1.0-preview1-26216-03", - "hashPath": "microsoft.netcore.dotnetapphost.2.1.0-preview1-26216-03.nupkg.sha512" - }, - "Microsoft.NETCore.DotNetHostPolicy/2.1.0-preview1-26216-03": { - "type": "package", - "serviceable": true, - "sha512": "sha512-8GVVXAaCfbGcnYffzuRq8ZinphQCXIYSWalgp4xoJz73pCAVi3CaWBBhFLBZrlMsXbkIPDjkzBiDMUDcbGmzkg==", - "path": "microsoft.netcore.dotnethostpolicy/2.1.0-preview1-26216-03", - "hashPath": "microsoft.netcore.dotnethostpolicy.2.1.0-preview1-26216-03.nupkg.sha512" - }, - "Microsoft.NETCore.DotNetHostResolver/2.1.0-preview1-26216-03": { - "type": "package", - "serviceable": true, - "sha512": "sha512-C/KrTMazxlJCwmi746lx1/QwaFrs3NBacr8JwKR1xBoBfF3i+eLY1tfE9XR+6dKYql3VaATsb4/PwSmLuW5nnQ==", - "path": "microsoft.netcore.dotnethostresolver/2.1.0-preview1-26216-03", - "hashPath": "microsoft.netcore.dotnethostresolver.2.1.0-preview1-26216-03.nupkg.sha512" - }, - "Microsoft.NETCore.Platforms/2.1.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-N6EA09LfBNpWTV/fjuAEhcsrl5iD+vIKML0P2TxrwiUd8ThCvOM4cdvimqGhJuBeHkgdSl+sFi0HvWnTkhin0g==", - "path": "microsoft.netcore.platforms/2.1.0-preview1-26216-02", - "hashPath": "microsoft.netcore.platforms.2.1.0-preview1-26216-02.nupkg.sha512" - }, - "NETStandard.Library/2.0.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-oA6nwv9MhEKYvLpjZ0ggSpb1g4CQViDVQjLUcDWg598jtvJbpfeP2reqwI1GLW2TbxC/Ml7xL6BBR1HmKPXlTg==", - "path": "netstandard.library/2.0.1", - "hashPath": "netstandard.library.2.0.1.nupkg.sha512" - }, - "Libuv/1.10.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-GsCf4q+eyaI49rCPlgYxdxa1SQCysXFFdSJWdstrwxytg4+VPYLYrXD4AT2rjHVJ+UF7SSWX9CapWEYaU4ejVQ==", - "path": "libuv/1.10.0", - "hashPath": "libuv.1.10.0.nupkg.sha512" - }, - "Microsoft.AspNetCore/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-PRLM3rRxshu51DxfN3Ydr4RSkdGG5/79jfdrUJtlF3nKxx9PiWEifiRwN49L6zSnVuwDGJEsJYfU4C/AP46pnA==", - "path": "microsoft.aspnetcore/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Antiforgery/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-H8SJDZMdkxPnMa3DuKsTHDdR7m9jgBr4FiwivK0ZDULscWC4/QzMlXONbtGi6mBL801N7+TrbY+s1I1LihmmdA==", - "path": "microsoft.aspnetcore.antiforgery/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.antiforgery.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.App/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Vit6ddojTEaSBz5m+CM+4rZ+yGZk8xPVbzKyXf+zDD8uC3yr2s2qGGgo6+opFhLMGWHFWECTd16NhrExOWX70w==", - "path": "microsoft.aspnetcore.app/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.app.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-6NenR/eMrg5k6zKxPLHewUXT+jF92iuqjL27DcNm0uSRdMe5WzIekxHqBpymIa6cCgUMqlKY8R8NcCgiWdy4aQ==", - "path": "microsoft.aspnetcore.authentication/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-4E2avGIWrPI0jEcVcq/jtaXPH9QFCd6Yp9nvJND2LDAUgHKFZhkFSMht3CYPqGSoL2ROTxcyuloTmoEZklXR8w==", - "path": "microsoft.aspnetcore.authentication.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.Cookies/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-KwtYCGTKoIEfVbdFtsLlJ60d3TBtHcyQA4tRd1oobnLamIMAohbqiYvXK3fdkd3dOJ5fol45AJctNQUP/RFY7Q==", - "path": "microsoft.aspnetcore.authentication.cookies/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.cookies.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.Core/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-H6VwALWypyI8Pb2oOhaFQD1e3KHYbNARWbLePYmTofZPWM+SVwtI8kf/HaoLNeTBYvnyKmKYH3tSschFftrOJg==", - "path": "microsoft.aspnetcore.authentication.core/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.core.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.Facebook/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ySMQcq4jDU8jLD6xcJ6YapcuVOzEnVSlkvRmRyf0xKLu0oXVwgaauLSmRLJM02wcWCamZX2IUWmwaDAEt8o8+A==", - "path": "microsoft.aspnetcore.authentication.facebook/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.facebook.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.Google/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-U7CxtB7/E7gzY0T3F5dctfjV3uHGDyQeiwsASPYSbsqy3Uw4CyIwnd0lOWgh+/Op24gObRu2a+Xqrv2jEfAaIw==", - "path": "microsoft.aspnetcore.authentication.google/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.google.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.JwtBearer/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-eFdxEqlZF9mU5UXf0S8mW9Xmr2UpK1yAcHK0tf5NljerMM6sLqyO1arqTXDcTdCCuD+qIHIZ4S/PTa+wt/e5qg==", - "path": "microsoft.aspnetcore.authentication.jwtbearer/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.jwtbearer.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.MicrosoftAccount/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-hpSHqC6SuQgoE6g7zFwJ5QfLtcmVi2plzQqv5AE8D+WSoA5tA44ayl4hzaGcrJdX44xUlwzcBNhRGW13gS7Nag==", - "path": "microsoft.aspnetcore.authentication.microsoftaccount/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.microsoftaccount.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.OAuth/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-+td3xz3IhAzlEfF/zksft7Jvhl4spmlGNuraaxrd1jfsFvrQSe2FKyv/BsshfJbMHwb2+KQ3Um1ACgs5D7+Q7w==", - "path": "microsoft.aspnetcore.authentication.oauth/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.oauth.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.OpenIdConnect/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-7uAzzQ0a3C20TIh90jnNBAPoScdTGzNMmJVs9NAofCH9EqaNO0F7zZqPYOsE4/Neu8AOhGpUoFdVN7cZY9Re5Q==", - "path": "microsoft.aspnetcore.authentication.openidconnect/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.openidconnect.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authentication.Twitter/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-1CLhKOURaTHl5rR/ky7EbjlneiAmJM3gkxwbY5DplzRBSABI/Pli2OskcDLfqvjaPMONIgGTQanAc7UAGqmsIQ==", - "path": "microsoft.aspnetcore.authentication.twitter/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authentication.twitter.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authorization/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-dfBDcFz0xBMqOPOZvrSMWXlYi0y6vqfxqN7Ze2wzdzwgdKm3qEGMeFgLz9YVCbfTlhtfLFrLaG3fPruz51B7KQ==", - "path": "microsoft.aspnetcore.authorization/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authorization.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Authorization.Policy/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ZLx9NV5k94iB8I/saMoGh5eB0soEcP+I6leXgXwKnIqXgppGGVAyC141L0lsq27XEwl4KhC/pXQgQTa+p9/S+Q==", - "path": "microsoft.aspnetcore.authorization.policy/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.authorization.policy.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.AzureAppServicesIntegration/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-GyU4hDCniHHrUX2XL5lZV5LlvY0UusMinmlyCcgyt05IugdyhWry27VL2x0da1ds9tdw0em/orvx+vUVi5r6HA==", - "path": "microsoft.aspnetcore.azureappservicesintegration/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.azureappservicesintegration.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.CookiePolicy/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-gq0fljc7kB27fIej9dbVf7ca6/do+7/l1dI4axtdkTuw/zRyOHIbBnwK4KQOHsN2rQEXmf1H3WMX3eP8p4wFAg==", - "path": "microsoft.aspnetcore.cookiepolicy/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.cookiepolicy.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Cors/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-mPMHgFXJE+9JHl/aZ8S/NBh7k9B2tOAxv89zUQ5/5McBqzJyVAinwb9b7/hQLDfO9g1uHRErpKU43egNdlEpTw==", - "path": "microsoft.aspnetcore.cors/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.cors.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Cryptography.Internal/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-wSevyqMiNJSUY/YWiZna+NVpAttQh0xuCz+RE4KOV/UAJ11WO5e+wbz7ThWQjqAH0+BqiInd3Ljna4ASo+cflw==", - "path": "microsoft.aspnetcore.cryptography.internal/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.cryptography.internal.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Cryptography.KeyDerivation/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-wqqDL6WRYaThYuPYkU5EBSUznddctDHsgHraAMdZj+WMOLsWjH/xyeW5MG07V0Q5rFB5PVvwn84xLKyiReaVyA==", - "path": "microsoft.aspnetcore.cryptography.keyderivation/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.cryptography.keyderivation.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.DataProtection/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-535UbygsPiTjutxzFaacmbyUG/1ItqO8dahnRinaondjob/V/Ph8zBnH/TpwV8NRckw2ZE6uf9wrAUlYktbdcg==", - "path": "microsoft.aspnetcore.dataprotection/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.dataprotection.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.DataProtection.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LFmzG7PEqo3frYW88cV+2e0X7zrkwE2xUs0A0jPneKzdo8s6rI+l5P31WCUygyZ5U0tF0lPVVq9ayZ0Uy8ctuA==", - "path": "microsoft.aspnetcore.dataprotection.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.dataprotection.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.DataProtection.Extensions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-gB/E7Uo52WX2d67CM1aTBrxhH4smODclFLnCAcYTTDtjQWn7j276LgKdgl+AML+NoUANe2ONKiUcX0RzAxszoQ==", - "path": "microsoft.aspnetcore.dataprotection.extensions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.dataprotection.extensions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Diagnostics/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-hVQ+bASBY/CWLtdChMITxkAh7uMuf7lz2cON2uLV//8dtFHWc2iGMhQuEpQvfTIVVWqICIqixwXXuvi4rspO1g==", - "path": "microsoft.aspnetcore.diagnostics/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.diagnostics.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Diagnostics.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-W3L6r7rAwRxfraP+lU3W/YWgRq+jKVcGrm21jug4kNVCKjcg1XWG/g6zMc+pVErI2nhzZG67qhYWmp1hrFQVnA==", - "path": "microsoft.aspnetcore.diagnostics.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.diagnostics.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Jj1wEqhe0252sM4ye3RkKjLO0A6eYf8Hhdp+LK29KHBP231ySch1HjT+ENowv2VvUNRPloWZuH8P+uzSjfwj7g==", - "path": "microsoft.aspnetcore.diagnostics.entityframeworkcore/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.diagnostics.entityframeworkcore.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Hosting/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-K13MbdxKPfnNx4E2QRAsJ20cwHAjz9aAuM9OL3lHPl4RCug+zRJ621Ah3TqRlPi7OQYbjx0dAtux7WqEDqUYkw==", - "path": "microsoft.aspnetcore.hosting/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.hosting.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Hosting.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Ay+Ws07RYt4x4r9+Cs26dfXLAAck51IXST4tevNRXYB4UqIDgtyTM+TdITqRnsyMYJQGPLR0MPj8dYUfjKn/XQ==", - "path": "microsoft.aspnetcore.hosting.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.hosting.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Ykf4cWZoFKEIfWJyvy56x5dMLFSkVqmKVpXF2BxmUSFcw8xlu0/5/MU1M8jvXNVdsWp1unI7sfwYee+LZYRYzw==", - "path": "microsoft.aspnetcore.hosting.server.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.hosting.server.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Html.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-mF3vkPXzQo9Y+Wv0yAeSdAvUIRFzH94boshwYAwjWAUSPAHwnIpxqJztdBEvgSGfUBxKKcX1Z9Rjd/kYX/EkLQ==", - "path": "microsoft.aspnetcore.html.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.html.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-lqgGspDIVACXBWQPU/D8wtTAi8H3rvzFR3RfO9j3E0wbSZxZqoT5wAVq4YAwpfyVFtTmFc11fgIe4lWH7aH+kA==", - "path": "microsoft.aspnetcore.http/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.http.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-AKilg08c0c1FAuvzPjmUxacT6Kdpnx1bqfuKe3hDt3EIaZvh6/q2Zlpn+G+Xszqi37njCUx41PVCukFCprkOqw==", - "path": "microsoft.aspnetcore.http.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.http.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Extensions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LEUUtD/T6c9hTr5KE1kAJKD9qFgv0YT5SUozO1MxGC7AN9DpQ8DJHLYkqFKBdWWklDETv61AMJJM06lkI07ugQ==", - "path": "microsoft.aspnetcore.http.extensions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.http.extensions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Features/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-5oHKTHjWX918O7Otw8xcerPm4C/Um7JQ3habwMRSRj22lSzuGcZDV9hp/dqGMs6326+d7PLJcsjyqoY3Pg1vUg==", - "path": "microsoft.aspnetcore.http.features/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.http.features.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.HttpOverrides/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HTOSkX/OYoRGDXltcrquNJvoF8NZpBj3W827VtiC53c7b8wn5ZibaK7MugVExpsoQXqStNImW9xGKqSYtNkGOA==", - "path": "microsoft.aspnetcore.httpoverrides/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.httpoverrides.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.HttpsPolicy/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-wpdVia3rIPoMsvT9Mul7F2YP/bSyl7+/fTG3j9qYRejqT+AK4aHr6jddvMHLGOJus50bfOAJUccijNbij5lnyQ==", - "path": "microsoft.aspnetcore.httpspolicy/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.httpspolicy.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Identity/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-2f+v5doDQdW+5jnMdCNLggiK4qFQ4y1CVv6KlW5SdbzoY7yRdzzPnCkU+5kuR3ZnysJY+Wezyq1O5+k/NiQ7Bg==", - "path": "microsoft.aspnetcore.identity/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.identity.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Identity.EntityFrameworkCore/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-w3eYo7YeYU+jSHx5AI4Q2bJt+DVCzuSILRnmdHhIL+F69auEgAdsKUw4ic5CfeV5dHE1Ixyy9UTy6tOOEEBvhQ==", - "path": "microsoft.aspnetcore.identity.entityframeworkcore/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.identity.entityframeworkcore.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Identity.UI/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LsPS2+I9LD9wHsolkrKlNjZBTrnXGlO0oFz+Lw7q/tLcTRDWC2LSnEVhOZ97GwZ65tZPdRd48klrI90C8jmpSQ==", - "path": "microsoft.aspnetcore.identity.ui/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.identity.ui.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.JsonPatch/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-DguJZhbyGnYbtcNZmTwpW4o5Tz/khIftORl/mVjd74i30tMaC6xs6y6A/1AzGj3z1sBlbPjhQYcOo0xrtN/7Jw==", - "path": "microsoft.aspnetcore.jsonpatch/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.jsonpatch.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Localization/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-qKzUOQkJ8ygfw7LsN0OF77F++ktJR6RsL2gIKCEWwuD0Cdsg0SWN6EL4MqbXzRIKoLPT+OJthDgT7c25m2vlWw==", - "path": "microsoft.aspnetcore.localization/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.localization.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Localization.Routing/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ITU6/o9r417+Fv4kVBjs5V61cOxnypbgrkKPKIlE3UGbKYwLW0WJKTU1hnn8lKKryngrk2+8vU+ZapTCrQf4/A==", - "path": "microsoft.aspnetcore.localization.routing/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.localization.routing.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.MiddlewareAnalysis/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-g7jS2pIiL19cFaH548WQqcToGt7UtzyPV3GHT77a2jbsfoGvnVc6FD+ceqQpojgCy8J8zxq5QL2Ebn2+f6pwcw==", - "path": "microsoft.aspnetcore.middlewareanalysis/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.middlewareanalysis.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-hIlJlznVm4k9JPNRP8ePBzbUtKAsST6f+UOTwBRccQY5NaTDC7FBFTJdLtxTPfjUuXDmC+Ya5uuV1p/K41CdOw==", - "path": "microsoft.aspnetcore.mvc/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Pf+OIoO2rLcJECyPow8KLoWKozVxbXnSh6FOyNBreSdzwUIvjVnfBa4yBoYT/2ygiVuxKwbE8x20TuRBaHLwfQ==", - "path": "microsoft.aspnetcore.mvc.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.ApiExplorer/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Efgbul2tcFSznBxGahcHRu78zNElMGy0XNvjH58GlN04HW5IlDRBXBPUzIfhSPTNS4XcE4Lw2j+jOfLSi2Lv1Q==", - "path": "microsoft.aspnetcore.mvc.apiexplorer/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.apiexplorer.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Core/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3OmovhNB0rlVrLtQgORYBgr2yfrvQi4H3/Mfm72gEn2ACZwywJhSoR9TqNQDZXwN3uHOy7q2UpjgPebGMfOo+w==", - "path": "microsoft.aspnetcore.mvc.core/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.core.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Cors/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-htfqgdxJRnsw5cmw0Pvxwanc75LY63kSPxOMNVsTNSUaMb+n7EPDRfS+d4s2adKJ/JX87aTSRErIb1kbgB+zPA==", - "path": "microsoft.aspnetcore.mvc.cors/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.cors.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.DataAnnotations/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-TVszQyyh0mJ4miEES9MWEsHMTGVwmgF36lbCN4oGjkLsJJWCDvqce6KfIZVOMz3GzaFBSaoUkuhgMoJtBQnLxw==", - "path": "microsoft.aspnetcore.mvc.dataannotations/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.dataannotations.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Formatters.Json/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ggahWVSlOlQQcTi/7ycGevQRBJlQSRSqZ3bW8h7VzRztQeHMsaJS20poHMFpvqtEqvJxmUCNcb/7mRi44V0+jg==", - "path": "microsoft.aspnetcore.mvc.formatters.json/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.formatters.json.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Formatters.Xml/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-YOOQAueiVLkGekcr8E1JB70dZTHlW9CIUTTv7hVRAWxZN0cWNf4dTXnx3I8w5yE1gjeDph5IZXmsfT/pVEY6Zw==", - "path": "microsoft.aspnetcore.mvc.formatters.xml/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.formatters.xml.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Localization/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-RseLtXXanO7JNInVzPPtbxVWp2W1CfuNrobtQngn6TTv8Xm6p8Rqc76i0nkbiC2IAWhGintUSeJyC6zNVGFoMA==", - "path": "microsoft.aspnetcore.mvc.localization/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.localization.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Razor/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-MzEzMWMGl8CCAVaq2BPZcoelMhx5++1wpeVquksVenclumptJsLdZC12oU1f+tfO6VxnxC0f9A2yO3fcVblObQ==", - "path": "microsoft.aspnetcore.mvc.razor/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.razor.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Razor.Extensions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-lkjqpiiCEszJ/i2gto/reO4Z8EqnkMdvbOpJNVb2LRDHwraqYIT4HHz14vUlpUtc++beWqTTLQHGvQKh7lGX9Q==", - "path": "microsoft.aspnetcore.mvc.razor.extensions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.razor.extensions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-QkBQOQZ50w6qu3xroXoZlFrxeeBAnAIy8vUSabCKRbREPF4tyMJZ3UU2/QiSKOlAieMm+obCv/lJUITl5GwSew==", - "path": "microsoft.aspnetcore.mvc.razor.viewcompilation/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.razor.viewcompilation.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.RazorPages/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Vnss3aQKIelMDEXiJSgWBGwzSpvUFFhrj12Tfhm6HTtv7S+GQDpur8qzGxQlLcXEmTiqAo1DNKChBU1UGE0VGg==", - "path": "microsoft.aspnetcore.mvc.razorpages/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.razorpages.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.TagHelpers/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-P/gA7JaYKBXj//hZpMnU9k/Or7PkdPwxQClz0hSGzMoYS7TvxrUbSng2PpNBE3bdbp1CVYBFAs1sCwZWtopvrg==", - "path": "microsoft.aspnetcore.mvc.taghelpers/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.taghelpers.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Mvc.ViewFeatures/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-7UKrUvz1tQKiEscQemStmZBkCZBv+FHPU5Oxx7gARI4X8tNTYmvZ7GHh70YaYfPeVtWjCsSKPozyBmTqmWjGnw==", - "path": "microsoft.aspnetcore.mvc.viewfeatures/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.mvc.viewfeatures.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.NodeServices/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-hyWdx0ZsQ2pBSZ3mGHRs9KlaXCd6vTmHkCHINRz6CPaQMg5IMip1jgCro3t4nWP754pXcXDFHIGae8Df70YyWw==", - "path": "microsoft.aspnetcore.nodeservices/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.nodeservices.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Owin/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-x/rFWARYwf2MM+8jEk9fuiJLBaA62GtIO3DTLkmOU2jNSwTtgHdwZ/55Qwe87DxhaeXia2x3BjLzxQwSP2kt/A==", - "path": "microsoft.aspnetcore.owin/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.owin.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Protocols.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Ohv49Y1fNeEiWajR8pK/bAMKA1oQL5thM37YpqtpKswKEA94S7UbMjpvxtId+ysdJp4GGCRwkB07OIqp1Rknbg==", - "path": "microsoft.aspnetcore.protocols.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.protocols.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Razor/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-L8RbVK0Xz+IGktjqQBM4wwAAVFzyAfFuBUesZ/oHzIeCBE9d3lKbndvG7Q9CMhKo6tn80jyHDjlN9qwjmS+llg==", - "path": "microsoft.aspnetcore.razor/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.razor.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Razor.Design/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ufxFgsWDpMH38pjAevTMgUtIHeh5lCACA5cuVbRISVySMypiSgmvB3h4zYiQnJrXKC7DfAzGDkWAy/9Xhm4/Bw==", - "path": "microsoft.aspnetcore.razor.design/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.razor.design.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Razor.Language/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-AN31fuZNEkey+5n/zDyH8mSWSonydifTBqDRPiMP2kLh/rIutoQsIDrBH1L0zW0oAkLlGVMwIZn5g3lzmgAI4A==", - "path": "microsoft.aspnetcore.razor.language/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.razor.language.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Razor.Runtime/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-R4wCXiIA2mACLWiJnhWnDcRGHI7IU11SvMBnBv/o/ACVj/gMxlJ2KrpCpMhNhbsL9xYcAEJ8b4ntm1LRCgDgsQ==", - "path": "microsoft.aspnetcore.razor.runtime/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.razor.runtime.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.ResponseCaching/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-zYj/Pjf3GbNZm11zQHjPk0W5u4nJ2QdC1btLH1kf8qLyOfOJn9SEkKocH+YyJlMN/7gxFuk2XG+Z8izx0iYDYA==", - "path": "microsoft.aspnetcore.responsecaching/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.responsecaching.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.ResponseCaching.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-eHhcZNxLiRrpvlNxGmOJ4yZlaNhFnThceGwKnIAqGkeab9qv99jBfKPW7N90EDhlXgswW/BfO3yeR26Ra1wbeA==", - "path": "microsoft.aspnetcore.responsecaching.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.responsecaching.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.ResponseCompression/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-+edz0N74AMaRaDhUiVyya3ep+3auUFOalsa+kuNw2pTiWaMIxZkIyY1dQsXdV6lYLV4oObBy2ZbV6unMryedpg==", - "path": "microsoft.aspnetcore.responsecompression/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.responsecompression.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Rewrite/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Zawrsf55zhfkzB4lA8SncwzBdla7B4ZJc2Hgcq9XNAXQyRA6QtEkIxo57ftMxbyEJfsXi9wFDB8C1G3Skpah1w==", - "path": "microsoft.aspnetcore.rewrite/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.rewrite.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Routing/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-mxLUhW765o2ogNy/cwUfju8M8gk0xKLu2CyFnVNrnmFDfmrrdFfMdRyHRdUm3Sr8BRB7uoKeqTEeQw5Lh+B22Q==", - "path": "microsoft.aspnetcore.routing/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.routing.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Routing.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-onuSpitMYKkx40THHYmqepOc4Bh7TYq2WTNwIaZcJTjo/TH8nLkyVv2so6yQwHeNm03k/1e2NOmpqDrqHK+W8g==", - "path": "microsoft.aspnetcore.routing.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.routing.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.HttpSys/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-H1UaipbY1LziKzNLgcYbXjJMzWTV3bfrCAk9fc21X9uiVg6s0TeDiQw8b1MxzHVk3qeK1K9DYCFaYmoAHC8nqw==", - "path": "microsoft.aspnetcore.server.httpsys/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.httpsys.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.IISIntegration/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-XdVf4cShb86e/MGWhdeakRg8A4T9v8+ptF1feL+jiZB3L7Cxtfy9staqdSwel7jfBVl8ivDidN7MCt6xVy5jmw==", - "path": "microsoft.aspnetcore.server.iisintegration/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.iisintegration.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.Kestrel/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-8TSWrB+vaZxIgI7Sre7jyTh1/Qp/floWix/8DnL+8UeKOyPe1pvQmOICiMMphKrzBa0dI5MhFX6KUuDb11xuRQ==", - "path": "microsoft.aspnetcore.server.kestrel/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.kestrel.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.Kestrel.Core/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-KGGD8vW39CRXmVtNF+8boY/wEajbya6/Us8krs460M5yZM+PtLnW90izT2Ap6mHC/qrTkcf5o/+RL6KXpqu2uA==", - "path": "microsoft.aspnetcore.server.kestrel.core/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.kestrel.core.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.Kestrel.Https/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kjuVvmebEKvC1vsuw6KnYTQPPVuI2TewIuJM2jdNgIHgEAsRjm7+g6wuU1GnCa7wA2JTVEWwTCCyUuwrpr4SEw==", - "path": "microsoft.aspnetcore.server.kestrel.https/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.kestrel.https.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-nH1EXRBJDSqjx4W9q1abdkF/dF14HmUp6RjhDHe/OB8NNgoj8JVFLgAmJbVCO8kQPIO6Vrw13ZtjVUxp/KzFGA==", - "path": "microsoft.aspnetcore.server.kestrel.transport.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.kestrel.transport.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-wSK3MRi7hrWds3nMlnylBL5x/G6TiCI3jxYXqQ2qi9DtPhlA1EiU9LhAIQwSRylHka/uX5i6yfJXwQjsnttJcQ==", - "path": "microsoft.aspnetcore.server.kestrel.transport.libuv/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.kestrel.transport.libuv.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-G2YNUliOtrcYkb+ULE6eLU/t5PKxKtsTq4i7lolLpyTUDpzwDpyYu/SvYhv4PDB27hdLO9S5h6uvrIggzxPehw==", - "path": "microsoft.aspnetcore.server.kestrel.transport.sockets/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.server.kestrel.transport.sockets.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Session/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-IvKJsYNAHXgrjip8CCIKxwollWiGz71eXVQx+N+73CJVXG1dJ5GXjXamE99jxf8Qs+jGhp+ytyueVAd0pUZCog==", - "path": "microsoft.aspnetcore.session/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.session.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.SignalR/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-NP0ytW/ETaYb/PxJ3O4L0RU2wv30qZuev3dRcFlLZRHCB6S9tLdx91VeXmkGZCLoNvtlHzFhtvE2Cd5hEatejA==", - "path": "microsoft.aspnetcore.signalr/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.signalr.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.SignalR.Common/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-I77+UK1fsvd1iDIyYc1oiLv1vyvJThr1WsFVWrSBvO2B5bDgF1CkMpY3/axojWjtB1xYm5pakKTDT09qzG9Tcw==", - "path": "microsoft.aspnetcore.signalr.common/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.signalr.common.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.SignalR.Core/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-FXze4v0V8hm5U8I1Pw75EXENMhkU5YmcFad0zt5XDaKOGMMMtD8WmmwibOCj3RFLoXCNIyecvIxxduLNeLJJSg==", - "path": "microsoft.aspnetcore.signalr.core/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.signalr.core.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Sockets/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-OuK+AQX4uKZhUF3aumnwvJB6rzuB48pmQpa5toOHAJagyhre4YLrnc0lZcGmEPw1imDIYAtRi/TaGDtd4L3zXw==", - "path": "microsoft.aspnetcore.sockets/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.sockets.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Sockets.Abstractions/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-N9t0H+x3V4O677aG86nzGOXnwWn6HBssh6HsvuSAYmVs+XqfBy0zmeGvLuWsZZr/qLuI+jdCT/BfsKr3M85YUg==", - "path": "microsoft.aspnetcore.sockets.abstractions/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.sockets.abstractions.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Sockets.Common.Http/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-vF0XJeFd0BHJAofiBcfmFArJpHoKDahH8Je+E98/VlBfCR0JtMtOELAy1n7MU7k+fpGynxpJP4yjZfGS/TUfMA==", - "path": "microsoft.aspnetcore.sockets.common.http/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.sockets.common.http.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.Sockets.Http/1.0.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-j9MdNvDNx+pp5aTPhMS5YTILBhWli2vR71GWvRjLnkSbT8/8kP1e0WbYESmrk6gbG6b1J+SMbf2tNiWe9He2CA==", - "path": "microsoft.aspnetcore.sockets.http/1.0.0-preview1-final", - "hashPath": "microsoft.aspnetcore.sockets.http.1.0.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.SpaServices/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ywDf8TQddsonmIRpChL17NWf6nUBJpby2KQqde2zzSaBWDpisWwavSffudAx9tZQ/EzzUbCLik9Ab61H6OOx9Q==", - "path": "microsoft.aspnetcore.spaservices/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.spaservices.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.SpaServices.Extensions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-GObkmwG3sIgq3ce6km7yvuso8EbCrAfwSCyrW9KJgU2XuvbpySyLANvuh8NZ4iXCog8z0jMtbXXQgXzfdddKlA==", - "path": "microsoft.aspnetcore.spaservices.extensions/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.spaservices.extensions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.StaticFiles/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-8bKCH/ogDGnHCMwY/ogQMYEExfSw6m64PLXCNY6E7pGVQ7jJEpCXqOW3fVRPyqHgr+9KAEqM4Kuq2jCZNzP3Kg==", - "path": "microsoft.aspnetcore.staticfiles/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.staticfiles.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.WebSockets/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-unGwXphQe5ZgHR8xwVj8cOW1DZ1F60bQmjl24fCT8tXy1M3xkqFNZd4d79dV5CC9K7VlKmIXAoG8BfVnHjpCyA==", - "path": "microsoft.aspnetcore.websockets/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.websockets.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.AspNetCore.WebUtilities/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-nPgGnXcc687MGF0PW2ixMk1l1t7VLAg87BNocTReRDG6BMmeeBM0JuiYz9jjRzIyJP+nFKB4xrJrCzyfmBG3Aw==", - "path": "microsoft.aspnetcore.webutilities/2.1.0-preview1-final", - "hashPath": "microsoft.aspnetcore.webutilities.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.Analyzers/1.1.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HS3iRWZKcUw/8eZ/08GXKY2Bn7xNzQPzf8gRPHGSowX7u7XXu9i9YEaBeBNKUXWfI7qjvT2zXtLUvbN0hds8vg==", - "path": "microsoft.codeanalysis.analyzers/1.1.0", - "hashPath": "microsoft.codeanalysis.analyzers.1.1.0.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.Common/2.6.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-FUDSP9wUdFrVQlSvfzpDWdeeoYHAyhednROVZVEccDDo8XV58NYAg5aNYWVnwsYfbhyFwAk/xFNXK6vi9c6vYg==", - "path": "microsoft.codeanalysis.common/2.6.1", - "hashPath": "microsoft.codeanalysis.common.2.6.1.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.CSharp/2.6.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HodSc/cNtkTMufeYqliX6/RPH66/n8fRRHkmzP0R7ezr5b/DmPyMjiTBr1mOs5NbXyqjSTTSdPJ4xfBlp2NREQ==", - "path": "microsoft.codeanalysis.csharp/2.6.1", - "hashPath": "microsoft.codeanalysis.csharp.2.6.1.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.Razor/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-t4oAefBFnpciHp/U/GC+Exv3vPGWtm19exdNlT/UocjqeLMiLvH6WtVaeX50jnHPtyfdBNUDakedKjnjZ2IjDg==", - "path": "microsoft.codeanalysis.razor/2.1.0-preview1-final", - "hashPath": "microsoft.codeanalysis.razor.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.CSharp/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-/ftJnETqUtjRGwxgXt9oB7GHoANb0wZZDyWtk2OtAi6N5P/UENj1N4c2wfHHHCAQHhYozVYvJ2E77v5ftrUPWw==", - "path": "microsoft.csharp/4.5.0-preview1-26216-02", - "hashPath": "microsoft.csharp.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "Microsoft.DotNet.PlatformAbstractions/2.1.0-preview1-26216-03": { - "type": "package", - "serviceable": true, - "sha512": "sha512-GGe0r0sLBKr/o4bxDmrgLm06hQJZbo82NyKzyAJ18I5yF0Fhkle40GvddDg4CgRtgoAFXMrtRnHsB7XJaPwQEg==", - "path": "microsoft.dotnet.platformabstractions/2.1.0-preview1-26216-03", - "hashPath": "microsoft.dotnet.platformabstractions.2.1.0-preview1-26216-03.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-EQnsSvDVHaVIJg3hnSgwM6sTg4tIvbzUD/sizlP8lou3JQrLRAvqMrCxB/4juKi8Q83Z/sp7m7X8lOOZP5dHcw==", - "path": "microsoft.entityframeworkcore/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore.Attributes/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-nqz7XK6E2yVhCJJDKf/EVqBGKxVmzcJvz5jTbAjmt7b2E9H/6ehJYuacv7R/baUed5wLPPOBNeqmpLWw0lm1PA==", - "path": "microsoft.entityframeworkcore.attributes/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.attributes.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore.Design/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Wl/ZzZaUlg2yGTOOMhRU4YayFIl7ybFctBYGSLDqxEesC5hjsIo28GnuzLShfPkgSva9hOOs99Tlrze4KnM6/Q==", - "path": "microsoft.entityframeworkcore.design/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.design.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore.InMemory/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-CG9uQ0GybQYKi+8tkS7a7GVQ/hfpeDymyxqrupctu1PocRu6Dwarq6Y3UlacYYMZ/m6ijDbnhLhCP6zUJwT/jw==", - "path": "microsoft.entityframeworkcore.inmemory/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.inmemory.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore.Relational/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-R9ScM9qvAgSOOX1xQCq/FTvMDIW/THU270I94gpBiKFVNIFj6duzf/ZzS6t1v+6hPxhC7Fm1XSZc00zfUrRR4A==", - "path": "microsoft.entityframeworkcore.relational/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.relational.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore.SqlServer/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-yuH5p5svL3DRuean25etwMfsCLW5fccMcJFCUq2kSi8rEsnrvPtE/79PGlIXvNLjRGffvCJoZvxFFJwPKmSLRg==", - "path": "microsoft.entityframeworkcore.sqlserver/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.sqlserver.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.EntityFrameworkCore.Tools/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3GHK7KxPeXP2K5HeJGSCJjB5Q6q7pTS4j18oD+5bxgx0txg5c7nH836tH1TUFtSlrpETX2/IW5Juxr+KeBDtRQ==", - "path": "microsoft.entityframeworkcore.tools/2.1.0-preview1-final", - "hashPath": "microsoft.entityframeworkcore.tools.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Caching.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-NboMvHqry97piq8MA0X2dwyVzbMeY/O2JvZFyMNicZNifNkDq/ViLryDYyfk1qJfCdKs5pmEJtrmGIStUzUZQw==", - "path": "microsoft.extensions.caching.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.caching.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Caching.Memory/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-5lPhwB4PQf2KDIr2uxd+P1BT+y2sDVz2Q9Prc0Z6twboZqvdbJV6fC9FEwJDSIhrLwkYIcTyzwpHOI7+oDWXLg==", - "path": "microsoft.extensions.caching.memory/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.caching.memory.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Caching.SqlServer/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-cMYdYXWF6Z1fymyPXUVowpVZViW59+iv7fqA+qOyD8AMVoYFSPp2uwRyKsrireQwTe0JH35z+T0G54MKZpcg+A==", - "path": "microsoft.extensions.caching.sqlserver/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.caching.sqlserver.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-VST9w4+xLD24+peRpTfD4nVHOQ3dyLP+ZDo3ep9fW/wLF2hMAfmwuAl1XNsXB7z4u8rAcRRXwUfVJ2r/4iAmvA==", - "path": "microsoft.extensions.configuration/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-N5FDg7GMpV/3bhdEVK7ozUVsAoyn/O2GEUMNRxEv8kPOqLZsuIA6cAEhexOOdAXM4oIIebXYFLrWa/4eWTn2pg==", - "path": "microsoft.extensions.configuration.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Binder/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-hLCCPB91edULQkKcx8gmNl+180P2Q72f748hhjVSr9onDO2mfP7YtuQT8lfjAYt9laVZ9kN//FZ6k4rZwAyw8g==", - "path": "microsoft.extensions.configuration.binder/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.binder.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.CommandLine/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-c/EgVfjl1d9t9l1GediZQgESQSyXVgG/hIU5yuV5E2aCjNDCI1loIwm14dXuV7AKiDQMnwsmNrQKYvIxuWFVaA==", - "path": "microsoft.extensions.configuration.commandline/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.commandline.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-wg8RlAgpblbmMVhLdoGUYKBgcGVVs8mFRWEwGZbQ3WJmmkwzIuGBSt7ROLTHc4eOp0s9rdUz5mI21NbuyV6hmw==", - "path": "microsoft.extensions.configuration.environmentvariables/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.environmentvariables.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-H29FiZ/lZjjKR6WAusH8NGPW1WSllxpfbMonvccKloN4kzeZ67oM3y4o6I2niPAgjoW5cvPOByoJYrFl284Wvg==", - "path": "microsoft.extensions.configuration.fileextensions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.fileextensions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Ini/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-P0yI7HB7xLQm3kRijzw8liyaJpOcB9aeX5wCWS0jzdg7YzV+sARA29aqD0JSF983WwiFbUYWEFNeFwrVvo1fSw==", - "path": "microsoft.extensions.configuration.ini/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.ini.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Json/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ZvEPZ3aIhkzB8fNi2QX2mVLkelJzVKQod1gD8SvOPa9BsBTkPf6jSlxVQ8xbEMU8Abo2w/Fio7PMICC9ynM9JA==", - "path": "microsoft.extensions.configuration.json/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.json.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.UserSecrets/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-fB/ARW86/I6SzxVcj2mWNqhC7NVv6lY3JLxzozrlcsedeP4RlZmQhpGQsiwrUBOAGq/3FIsfDMunh2AyaSCTtA==", - "path": "microsoft.extensions.configuration.usersecrets/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.usersecrets.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Xml/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-EfhsaQzF6+BiWxdnrD2G8p0F978TeoydZGxPU9S81b9SKqSWJVXjGRAv6csK4xGSaeLarhmRDHCcUyfqKZny+g==", - "path": "microsoft.extensions.configuration.xml/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.configuration.xml.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.DependencyInjection/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-UuyuXJ92DAsrSuDgwQuLq933QIRcl7YsVt1g+gP7aLrh5VXul+oyuG48jcJWJHVTYc9ndKNJV4mRBeEH9hElLA==", - "path": "microsoft.extensions.dependencyinjection/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.dependencyinjection.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-STPLX09ZK9GWBJqfwz8f2mqcGQFZl4IWR4NhklZxI6EKhXRHBNBFH/ytjWv7SBSTgK/xPuwGBR2oPTSwodZaJg==", - "path": "microsoft.extensions.dependencyinjection.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.dependencyinjection.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.DependencyModel/2.1.0-preview1-26216-03": { - "type": "package", - "serviceable": true, - "sha512": "sha512-U5hfDdjZv/e6f0LnU4b0iv2CY9ZjYZI27i8Xb3C64bZVQFFuwvglZunoB4UD8edpfL0fh92MwxBmwNSfSRuOOg==", - "path": "microsoft.extensions.dependencymodel/2.1.0-preview1-26216-03", - "hashPath": "microsoft.extensions.dependencymodel.2.1.0-preview1-26216-03.nupkg.sha512" - }, - "Microsoft.Extensions.DiagnosticAdapter/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-fJzbpTKe0GIkns/w9bgLV1E6Vb8QG7HkWLg0CiaZ6DclYvtBn49Zr97KQu2nPzWLZVAtixNJ692lf8tSH0k05w==", - "path": "microsoft.extensions.diagnosticadapter/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.diagnosticadapter.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-TvFzcEMmQy0+6A7wQERWZO+lVub0lqz42TWDPvgrjz0pjdNRY3QbjjZ7OtXeBTsf6TbAP+p2kRWBeUTlHq6Xxg==", - "path": "microsoft.extensions.fileproviders.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.fileproviders.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.FileProviders.Composite/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-PfpZAeBM5YNZCpD2jcXWCgoxR48pirmZO2IdV+lhe/OheN3Whe+l13roZ3jhTSHKiqZJFZWeI/R4bel3fdHF1g==", - "path": "microsoft.extensions.fileproviders.composite/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.fileproviders.composite.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.FileProviders.Embedded/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Y85GEOYZtR8VxEkN/WlXVtYLFYmfzNtEoG6Qm9S0tpaHRkTnLxOcsONRErwiqgSbqckJlMn9BDyF0DEZOE5rug==", - "path": "microsoft.extensions.fileproviders.embedded/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.fileproviders.embedded.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.FileProviders.Physical/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kQLTl4PPKqI0nDXQFQBfff7A1nUWdEVkp3BI9NkUdutPpG7q/l+qJyyzOzVIsZrSArHBIqR/H9hfZSBGkOV+9w==", - "path": "microsoft.extensions.fileproviders.physical/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.fileproviders.physical.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.FileSystemGlobbing/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kp0CpgzAMOCl5Do/JH/iXSRMtQY6+ZWi3SGiU8tnR4pHbE2nxczsXIhXa2TusSWihStfUJZR4fA21Bo5qJZJnw==", - "path": "microsoft.extensions.filesystemglobbing/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.filesystemglobbing.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Hosting/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LBB54ZVGTN64Xe9zLaRtb7OCEbM6J9IUBOYipt9XcU/nU8PjqYT2CpUtA4qspHXroyzxT+d7IIVnhiZ3IeAFsQ==", - "path": "microsoft.extensions.hosting/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.hosting.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Hosting.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-jKbEEfewNYu2Rp2odp4F5MaBCOhlk0ti1Q6GyBBBSsYeiGeBfk9LRP13H6IKovDfebq8jsMdJk1B53wmMOYkBQ==", - "path": "microsoft.extensions.hosting.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.hosting.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Http/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-x0iQkdiaZz0qv6wj0B11ZwAXZU3+n7fpcO989Oxk2zWbAlMzrqEYSGN1Lf/Be+DOVi7OAi8U3rSYaitusUVPgA==", - "path": "microsoft.extensions.http/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.http.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Identity.Core/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-w3i1Upl7B8fyzrNKuU3R088VRYlwXlUCI7mdn0c5mRvDMbKUzi6OFt/gtWzZyFoEjhAEi6rGMRS8JG45TZX5qw==", - "path": "microsoft.extensions.identity.core/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.identity.core.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Identity.Stores/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-oag4dO4GLU1l0syiuB8GWH5pCuW9On1+eUy7vC29uOxuUWdiMkJY27BfKBCtfhpDXBrEr3Pa+MKfQeEWzZD+iQ==", - "path": "microsoft.extensions.identity.stores/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.identity.stores.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Localization/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-hY+n9jTNxJDj/Mqrrs2IOswVggbXMzVzrW3d6L4aigCH5QH1QqgOMHTV3f48T56xZg7cQZT34NNwhr7cgSgI1g==", - "path": "microsoft.extensions.localization/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.localization.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Localization.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-fU+DB1rLwxD1SuALA8ZtsviGa3apOhC83xQOKlIrqRUrZCDr5MRzsUB1bcR1tgdHpQq4uok9LIOvG6HvvQ+sSA==", - "path": "microsoft.extensions.localization.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.localization.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-+TvZZd6V4tgQaXdXjsUlVgNqc/eL2XvZ4St+CcG6fN+3z0NQVW9RrOR3TgL28h0hOCHVVNwz01CD0lAGtAooZw==", - "path": "microsoft.extensions.logging/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.Abstractions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-sYca3eodiObIgTYZ9FaiKgNecvdB3pXXnoUPLl0VAK68abmBQf2lHULODQFk5qSbzORRRyNIXiCc1WL8G35C4A==", - "path": "microsoft.extensions.logging.abstractions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.abstractions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.AzureAppServices/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-tQFw/VVMOyon3Esiw+iEZ8pGh2McMEQ0q9XKUAVPZ31foABra/GsaKSsLt/KWGHjGPRKMequ0vTknqAZ4k649w==", - "path": "microsoft.extensions.logging.azureappservices/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.azureappservices.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.Configuration/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Y10lYtbdgbYtOf7OeOcvV2O6Z3YA9iWCTnEV6A2mBfXNPxAX8dPK8A9/wZWUMKNlBfJ+naRZWflde9AoTSXboQ==", - "path": "microsoft.extensions.logging.configuration/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.configuration.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.Console/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-l1KWRt/vm07z//JD+EUTMSqybouJfLXDo8zRbwqUEorV5zVK2duzbTEvtV9wzqGGpvy+QOQR1q+wnfIqaqCJEw==", - "path": "microsoft.extensions.logging.console/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.console.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.Debug/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-I3pkqDGmcvoa0nntM3+zPZKx/4Ts2gWh0mRYCXIQkZQ2v7m5QRtEe0tR64SdHmrCtyUZ/uwWuSrSj5rjUUShQw==", - "path": "microsoft.extensions.logging.debug/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.debug.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.EventSource/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-QLrPW0XAglebOgxnFZRDIehM4uxOpwzvVi6GwIoVe/uFEHBH+h1EhkCxCmehQBTyoLzg/oBzdmFV0UUCpOslGA==", - "path": "microsoft.extensions.logging.eventsource/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.eventsource.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.TraceSource/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-vgalmhZPTtNtDBXyEJcYwByIun+8eZ+iBm8pJtCIjVZEl20WDSfMD74XtmsvK1gJUO5X4Lauo7sX5pK0gd2GOA==", - "path": "microsoft.extensions.logging.tracesource/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.logging.tracesource.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.ObjectPool/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-lCOjN5GJB9TUgsiYpxSsHK6D0C7rp2qkP+fRXv70DBMS5HM59wRLIc6Hx1ZgRiougXit+RbuRWEs4MRB1sHD6w==", - "path": "microsoft.extensions.objectpool/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.objectpool.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Options/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-C0AgjA2ZY3tbe2L/8TceIEDHg71M6liK+kqn7Xm0PoN1LCoXyd9rKHGYnU+Ie+vteDJTNiQKlj4kNd+vXSH+HQ==", - "path": "microsoft.extensions.options/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.options.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-04GXpRPABkknH/hZp9gIuSKP8B1yTRwC1rH0jINfixI5oxQYZCWg7aXFEWebNXYbFDO+aJ/qF384FlwbMur4vg==", - "path": "microsoft.extensions.options.configurationextensions/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.options.configurationextensions.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.Primitives/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-RbZvvDkV9jJBJ4V+XEW2RWLFemmDmrFWERGYh15j8oUjqDLMEtxR1p9a4cnAUe203WO+UjmF0Vxddxn990Rm3A==", - "path": "microsoft.extensions.primitives/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.primitives.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.Extensions.WebEncoders/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-8lc5SMtHKuq/41OU7crDyOvg4/Cop5M+4FEAHdpbvUiKPGPaH0+BFDENahPmO4bu2OknZJBuOzrKvlXa40e1ZA==", - "path": "microsoft.extensions.webencoders/2.1.0-preview1-final", - "hashPath": "microsoft.extensions.webencoders.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.IdentityModel.Logging/5.2.0-preview2-41113220915": { - "type": "package", - "serviceable": true, - "sha512": "sha512-YQxFeh7B9rYoRL4Nk6xTQsylN7B/+gVC7fF125mzdu6VI2gEDXY5IvTyG5r+2t2d5tt2JhfNCRSW+48N/zUIKA==", - "path": "microsoft.identitymodel.logging/5.2.0-preview2-41113220915", - "hashPath": "microsoft.identitymodel.logging.5.2.0-preview2-41113220915.nupkg.sha512" - }, - "Microsoft.IdentityModel.Protocols/5.2.0-preview2-41113220915": { - "type": "package", - "serviceable": true, - "sha512": "sha512-z9egaDxViByst3nCKc5E2hv1ceOy9Kca89WnACpViHVrHEpvozHBVuG8wd+J5wVkFUakFTGZ4CodCTPkgqyJXw==", - "path": "microsoft.identitymodel.protocols/5.2.0-preview2-41113220915", - "hashPath": "microsoft.identitymodel.protocols.5.2.0-preview2-41113220915.nupkg.sha512" - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect/5.2.0-preview2-41113220915": { - "type": "package", - "serviceable": true, - "sha512": "sha512-k5ObksE+Acl1iZ8HVJjZUgGtDwYHemZy8ham78IShI3QmmYsdgMVZLgSf8to14iNQBe4i5gDZxaQkBMjMbUebg==", - "path": "microsoft.identitymodel.protocols.openidconnect/5.2.0-preview2-41113220915", - "hashPath": "microsoft.identitymodel.protocols.openidconnect.5.2.0-preview2-41113220915.nupkg.sha512" - }, - "Microsoft.IdentityModel.Tokens/5.2.0-preview2-41113220915": { - "type": "package", - "serviceable": true, - "sha512": "sha512-1UHm2s/HBOLFlX03efyh4PZyl3Jaxwy3xwI6cHyf9G+Qq+Cdn8taLeP46FI9me7Ymvkutr0JopT+QviGfYpsKg==", - "path": "microsoft.identitymodel.tokens/5.2.0-preview2-41113220915", - "hashPath": "microsoft.identitymodel.tokens.5.2.0-preview2-41113220915.nupkg.sha512" - }, - "Microsoft.Net.Http.Headers/2.1.0-preview1-final": { - "type": "package", - "serviceable": true, - "sha512": "sha512-0KuhbwJhZzn3WZj+ItTknHnWnKfAL5qR8g2vGranm5oUCk2xM5NRvq7HsKmUFJxlNTimFmB6vqXBhMNVbXIPDw==", - "path": "microsoft.net.http.headers/2.1.0-preview1-final", - "hashPath": "microsoft.net.http.headers.2.1.0-preview1-final.nupkg.sha512" - }, - "Microsoft.NETCore.Targets/1.1.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==", - "path": "microsoft.netcore.targets/1.1.0", - "hashPath": "microsoft.netcore.targets.1.1.0.nupkg.sha512" - }, - "Microsoft.Win32.Registry/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-mUV6C2rmDOqRgvBau/GbC3zJrN7BWnjvmexb71gGl39Vtarj1x7Ia4Pw62kScH4PfwgaUabF5K4pPwjwLJNVkQ==", - "path": "microsoft.win32.registry/4.5.0-preview1-26216-02", - "hashPath": "microsoft.win32.registry.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "Newtonsoft.Json/10.0.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-UnonIWGhnlppobY1ItCmu/lQgBeYKYIjHvywpM2YXhS22NnFuaI/iwi5XuLwzfpc+wXKgu68vi5XWbH+x1X5rg==", - "path": "newtonsoft.json/10.0.1", - "hashPath": "newtonsoft.json.10.0.1.nupkg.sha512" - }, - "Newtonsoft.Json.Bson/1.0.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-TtDg/qmALb9rVhcd0jSjjwBF2WCCZJuIa9gMx7PEmOGaQAIiv5MjnindzPp1fTOLcgQcqSEaMCs1cq2h4osZGg==", - "path": "newtonsoft.json.bson/1.0.1", - "hashPath": "newtonsoft.json.bson.1.0.1.nupkg.sha512" - }, - "Remotion.Linq/2.2.0-alpha-002": { - "type": "package", - "serviceable": true, - "sha512": "sha512-dKP10OaohVMe5yLqLcsDWsoIk9NngEZV6RExdRHtGtpeeBMt7uI9E/Dt9ve1rS1pE/aW4068A08aa4fFIZ8sAQ==", - "path": "remotion.linq/2.2.0-alpha-002", - "hashPath": "remotion.linq.2.2.0-alpha-002.nupkg.sha512" - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==", - "path": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==", - "path": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==", - "path": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.native.System/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", - "path": "runtime.native.system/4.3.0", - "hashPath": "runtime.native.system.4.3.0.nupkg.sha512" - }, - "runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==", - "path": "runtime.native.system.data.sqlclient.sni/4.4.0", - "hashPath": "runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512" - }, - "runtime.native.System.IO.Compression/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", - "path": "runtime.native.system.io.compression/4.3.0", - "hashPath": "runtime.native.system.io.compression.4.3.0.nupkg.sha512" - }, - "runtime.native.System.Net.Http/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", - "path": "runtime.native.system.net.http/4.3.0", - "hashPath": "runtime.native.system.net.http.4.3.0.nupkg.sha512" - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", - "path": "runtime.native.system.security.cryptography.apple/4.3.0", - "hashPath": "runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512" - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", - "path": "runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==", - "path": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==", - "path": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.0", - "hashPath": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.0.nupkg.sha512" - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==", - "path": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==", - "path": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==", - "path": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==", - "path": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.0", - "hashPath": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg==", - "path": "runtime.win-arm64.runtime.native.system.data.sqlclient.sni/4.4.0", - "hashPath": "runtime.win-arm64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512" - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ==", - "path": "runtime.win-x64.runtime.native.system.data.sqlclient.sni/4.4.0", - "hashPath": "runtime.win-x64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512" - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==", - "path": "runtime.win-x86.runtime.native.system.data.sqlclient.sni/4.4.0", - "hashPath": "runtime.win-x86.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512" - }, - "System.AppContext/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", - "path": "system.appcontext/4.3.0", - "hashPath": "system.appcontext.4.3.0.nupkg.sha512" - }, - "System.Buffers/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kKos0xcIDZNQPGFms7OpVH+L+f7bRBIu75LhzYNI3/aKjK6NXAWUMZvtakrsL4JvheNIwh7P4E2pbD8TlE2rVg==", - "path": "system.buffers/4.5.0-preview1-26216-02", - "hashPath": "system.buffers.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Buffers.Primitives/0.1.0-preview1-180216-4": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ohM0EOdIcFADAKH6hQwsyaMEwu1H/otqMXIe5GjaCD4pQGRnxUgQwFcXNmY1CiHMRi3xW32BENJPdmuE4jkEFQ==", - "path": "system.buffers.primitives/0.1.0-preview1-180216-4", - "hashPath": "system.buffers.primitives.0.1.0-preview1-180216-4.nupkg.sha512" - }, - "System.Collections/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", - "path": "system.collections/4.3.0", - "hashPath": "system.collections.4.3.0.nupkg.sha512" - }, - "System.Collections.Concurrent/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", - "path": "system.collections.concurrent/4.3.0", - "hashPath": "system.collections.concurrent.4.3.0.nupkg.sha512" - }, - "System.Collections.Immutable/1.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-DurWfMkyXGQhaZ7Fd3MmcCu6cw5wtM/dG5fmITZTbNHlf104KJHJL1UrsaU9apEZnSyXI1PnKwi99omgwppLgA==", - "path": "system.collections.immutable/1.5.0-preview1-26216-02", - "hashPath": "system.collections.immutable.1.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Collections.NonGeneric/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", - "path": "system.collections.nongeneric/4.3.0", - "hashPath": "system.collections.nongeneric.4.3.0.nupkg.sha512" - }, - "System.Collections.Specialized/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", - "path": "system.collections.specialized/4.3.0", - "hashPath": "system.collections.specialized.4.3.0.nupkg.sha512" - }, - "System.ComponentModel/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", - "path": "system.componentmodel/4.3.0", - "hashPath": "system.componentmodel.4.3.0.nupkg.sha512" - }, - "System.ComponentModel.Annotations/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-dQGK4S1A8yJqNZal/7U1Vlgk0r6d0Cc8+v8l20f7lxGeJDP+Wso8QNk2uNMRj7z/siWL//o+5YRyW74j40+EKw==", - "path": "system.componentmodel.annotations/4.5.0-preview1-26216-02", - "hashPath": "system.componentmodel.annotations.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.ComponentModel.Primitives/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", - "path": "system.componentmodel.primitives/4.3.0", - "hashPath": "system.componentmodel.primitives.4.3.0.nupkg.sha512" - }, - "System.ComponentModel.TypeConverter/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", - "path": "system.componentmodel.typeconverter/4.3.0", - "hashPath": "system.componentmodel.typeconverter.4.3.0.nupkg.sha512" - }, - "System.Console/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", - "path": "system.console/4.3.0", - "hashPath": "system.console.4.3.0.nupkg.sha512" - }, - "System.Data.SqlClient/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-yR2f63uEOlpq0ZwbZv6nVbwizDtaJfG/GMzzU04I4mvt25RlBOwfr0ff/+NTZfa168eO+tWNmM3ce8RHz4G1rA==", - "path": "system.data.sqlclient/4.5.0-preview1-26216-02", - "hashPath": "system.data.sqlclient.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Diagnostics.Contracts/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-eelRRbnm+OloiQvp9CXS0ixjNQldjjkHO4iIkR5XH2VIP8sUB/SIpa1TdUW6/+HDcQ+MlhP3pNa1u5SbzYuWGA==", - "path": "system.diagnostics.contracts/4.3.0", - "hashPath": "system.diagnostics.contracts.4.3.0.nupkg.sha512" - }, - "System.Diagnostics.Debug/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", - "path": "system.diagnostics.debug/4.3.0", - "hashPath": "system.diagnostics.debug.4.3.0.nupkg.sha512" - }, - "System.Diagnostics.DiagnosticSource/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-qQbPDCBq+e7mhLHWtXneGR2/wu4vwJtcwvNfUFEAsUQDPgkC//NiUjaztAJbDcR6xqQ7HH1a2p3mRldJoRumNg==", - "path": "system.diagnostics.diagnosticsource/4.5.0-preview1-26216-02", - "hashPath": "system.diagnostics.diagnosticsource.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Diagnostics.FileVersionInfo/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-AcZ/E+mZKdYfNdSrg/kO9Bv17U19Naa86VPWXLFPFibWxUmhA3dpKlYFkFKwYfKWBGpDuRme4kETW7qhmTFqaA==", - "path": "system.diagnostics.fileversioninfo/4.3.0", - "hashPath": "system.diagnostics.fileversioninfo.4.3.0.nupkg.sha512" - }, - "System.Diagnostics.StackTrace/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-BiHg0vgtd35/DM9jvtaC1eKRpWZxr0gcQd643ABG7GnvSlf5pOkY2uyd42mMOJoOmKvnpNj0F4tuoS1pacTwYw==", - "path": "system.diagnostics.stacktrace/4.3.0", - "hashPath": "system.diagnostics.stacktrace.4.3.0.nupkg.sha512" - }, - "System.Diagnostics.Tools/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", - "path": "system.diagnostics.tools/4.3.0", - "hashPath": "system.diagnostics.tools.4.3.0.nupkg.sha512" - }, - "System.Diagnostics.Tracing/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", - "path": "system.diagnostics.tracing/4.3.0", - "hashPath": "system.diagnostics.tracing.4.3.0.nupkg.sha512" - }, - "System.Dynamic.Runtime/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", - "path": "system.dynamic.runtime/4.3.0", - "hashPath": "system.dynamic.runtime.4.3.0.nupkg.sha512" - }, - "System.Globalization/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", - "path": "system.globalization/4.3.0", - "hashPath": "system.globalization.4.3.0.nupkg.sha512" - }, - "System.Globalization.Calendars/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", - "path": "system.globalization.calendars/4.3.0", - "hashPath": "system.globalization.calendars.4.3.0.nupkg.sha512" - }, - "System.Globalization.Extensions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", - "path": "system.globalization.extensions/4.3.0", - "hashPath": "system.globalization.extensions.4.3.0.nupkg.sha512" - }, - "System.IdentityModel.Tokens.Jwt/5.2.0-preview2-41113220915": { - "type": "package", - "serviceable": true, - "sha512": "sha512-dCpeGcrM2kYP7Ylh7AGPJh/D87MsdVg9YcjjBue+niok7cN5Jd/fmZIDbwMMDDqaFmjcGz5YZVoMHknIF+88Cw==", - "path": "system.identitymodel.tokens.jwt/5.2.0-preview2-41113220915", - "hashPath": "system.identitymodel.tokens.jwt.5.2.0-preview2-41113220915.nupkg.sha512" - }, - "System.Interactive.Async/3.1.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-e4TWsjKrnnkLmq++62YtBWqAQLDJOTgC8hsBfPHx3t/qeWRnI+8YLmWZNMpmCQljRGAPpJon+tAWMoD9qGZHQA==", - "path": "system.interactive.async/3.1.1", - "hashPath": "system.interactive.async.3.1.1.nupkg.sha512" - }, - "System.IO/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", - "path": "system.io/4.3.0", - "hashPath": "system.io.4.3.0.nupkg.sha512" - }, - "System.IO.Compression/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", - "path": "system.io.compression/4.3.0", - "hashPath": "system.io.compression.4.3.0.nupkg.sha512" - }, - "System.IO.FileSystem/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", - "path": "system.io.filesystem/4.3.0", - "hashPath": "system.io.filesystem.4.3.0.nupkg.sha512" - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", - "path": "system.io.filesystem.primitives/4.3.0", - "hashPath": "system.io.filesystem.primitives.4.3.0.nupkg.sha512" - }, - "System.IO.Pipelines/0.1.0-preview1-180216-4": { - "type": "package", - "serviceable": true, - "sha512": "sha512-uYHy6tUC9kPVyqrHu3nHM1oI++CAzi5Jf4Cs8FuKQX60ZR1JvL1rfTboJgV7D0wtem62kRjDFTnt72iCoxT/FA==", - "path": "system.io.pipelines/0.1.0-preview1-180216-4", - "hashPath": "system.io.pipelines.0.1.0-preview1-180216-4.nupkg.sha512" - }, - "System.Linq/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", - "path": "system.linq/4.3.0", - "hashPath": "system.linq.4.3.0.nupkg.sha512" - }, - "System.Linq.Expressions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", - "path": "system.linq.expressions/4.3.0", - "hashPath": "system.linq.expressions.4.3.0.nupkg.sha512" - }, - "System.Linq.Queryable/4.0.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Yn/WfYe9RoRfmSLvUt2JerP0BTGGykCZkQPgojaxgzF2N0oPo+/AhB8TXOpdCcNlrG3VRtsamtK2uzsp3cqRVw==", - "path": "system.linq.queryable/4.0.1", - "hashPath": "system.linq.queryable.4.0.1.nupkg.sha512" - }, - "System.Memory/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-/qkNiTfseQ35guuSKxqw6cjEYnh+ijwI941QS5669VtTSSXOoyHWUO6JARIvSQ+A2yYFVdKEvTkKrMfaqdyOaw==", - "path": "system.memory/4.5.0-preview1-26216-02", - "hashPath": "system.memory.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Net.Http/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", - "path": "system.net.http/4.3.0", - "hashPath": "system.net.http.4.3.0.nupkg.sha512" - }, - "System.Net.Primitives/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", - "path": "system.net.primitives/4.3.0", - "hashPath": "system.net.primitives.4.3.0.nupkg.sha512" - }, - "System.Net.WebSockets.WebSocketProtocol/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-4EgLycyiBt0/L0un9LsEbkr0b4yEhVMVKZnj63tQ/NaT2vZ9tykmyYkFHEbgU9rRYkgeI2ZaBEOrY384Nf179w==", - "path": "system.net.websockets.websocketprotocol/4.5.0-preview1-26216-02", - "hashPath": "system.net.websockets.websocketprotocol.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Numerics.Vectors/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-FGIOma5l5mVHfnG8Ry5k3WqjWeDGFp1381xGpqwyUBWLs6kzm9Jbss1dUIRoOG4SU55nC0/28toJcq1sYfxPRw==", - "path": "system.numerics.vectors/4.5.0-preview1-26216-02", - "hashPath": "system.numerics.vectors.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.ObjectModel/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", - "path": "system.objectmodel/4.3.0", - "hashPath": "system.objectmodel.4.3.0.nupkg.sha512" - }, - "System.Private.DataContractSerialization/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-yDaJ2x3mMmjdZEDB4IbezSnCsnjQ4BxinKhRAaP6kEgL6Bb6jANWphs5SzyD8imqeC/3FxgsuXT6ykkiH1uUmA==", - "path": "system.private.datacontractserialization/4.3.0", - "hashPath": "system.private.datacontractserialization.4.3.0.nupkg.sha512" - }, - "System.Reflection/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", - "path": "system.reflection/4.3.0", - "hashPath": "system.reflection.4.3.0.nupkg.sha512" - }, - "System.Reflection.Emit/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", - "path": "system.reflection.emit/4.3.0", - "hashPath": "system.reflection.emit.4.3.0.nupkg.sha512" - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", - "path": "system.reflection.emit.ilgeneration/4.3.0", - "hashPath": "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512" - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", - "path": "system.reflection.emit.lightweight/4.3.0", - "hashPath": "system.reflection.emit.lightweight.4.3.0.nupkg.sha512" - }, - "System.Reflection.Extensions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", - "path": "system.reflection.extensions/4.3.0", - "hashPath": "system.reflection.extensions.4.3.0.nupkg.sha512" - }, - "System.Reflection.Metadata/1.6.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-XsUrFreU15Xo1k/umbz0EuKnXG+da/XWlEkxKrBDJc+DHjGNrmbiyR+3TMbNCFHAVREg7aHOufVuVR9djrpg5Q==", - "path": "system.reflection.metadata/1.6.0-preview1-26216-02", - "hashPath": "system.reflection.metadata.1.6.0-preview1-26216-02.nupkg.sha512" - }, - "System.Reflection.Primitives/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", - "path": "system.reflection.primitives/4.3.0", - "hashPath": "system.reflection.primitives.4.3.0.nupkg.sha512" - }, - "System.Reflection.TypeExtensions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", - "path": "system.reflection.typeextensions/4.3.0", - "hashPath": "system.reflection.typeextensions.4.3.0.nupkg.sha512" - }, - "System.Resources.ResourceManager/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", - "path": "system.resources.resourcemanager/4.3.0", - "hashPath": "system.resources.resourcemanager.4.3.0.nupkg.sha512" - }, - "System.Runtime/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", - "path": "system.runtime/4.3.0", - "hashPath": "system.runtime.4.3.0.nupkg.sha512" - }, - "System.Runtime.CompilerServices.Unsafe/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-YH6QzLUg6VzyejRsW9no/xlulcpKjfOC27jWmijyhw8A42ldukXHQDSaE0CP+khK0m2iqAaXoQ0BU49FUDw7Lw==", - "path": "system.runtime.compilerservices.unsafe/4.5.0-preview1-26216-02", - "hashPath": "system.runtime.compilerservices.unsafe.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Runtime.Extensions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", - "path": "system.runtime.extensions/4.3.0", - "hashPath": "system.runtime.extensions.4.3.0.nupkg.sha512" - }, - "System.Runtime.Handles/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", - "path": "system.runtime.handles/4.3.0", - "hashPath": "system.runtime.handles.4.3.0.nupkg.sha512" - }, - "System.Runtime.InteropServices/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", - "path": "system.runtime.interopservices/4.3.0", - "hashPath": "system.runtime.interopservices.4.3.0.nupkg.sha512" - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", - "path": "system.runtime.interopservices.runtimeinformation/4.3.0", - "hashPath": "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512" - }, - "System.Runtime.Numerics/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", - "path": "system.runtime.numerics/4.3.0", - "hashPath": "system.runtime.numerics.4.3.0.nupkg.sha512" - }, - "System.Runtime.Serialization.Formatters/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-7zmtzzrGK2+Wqgvq47434JKESw89iJx45UuYjNSsqnhYZIN8ezEp22cYnqScVhyZ7ZDb0Nc2RN9Us0m7v2HnxA==", - "path": "system.runtime.serialization.formatters/4.3.0", - "hashPath": "system.runtime.serialization.formatters.4.3.0.nupkg.sha512" - }, - "System.Runtime.Serialization.Primitives/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Wz+0KOukJGAlXjtKr+5Xpuxf8+c8739RI1C+A2BoQZT+wMCCoMDDdO8/4IRHfaVINqL78GO8dW8G2lW/e45Mcw==", - "path": "system.runtime.serialization.primitives/4.3.0", - "hashPath": "system.runtime.serialization.primitives.4.3.0.nupkg.sha512" - }, - "System.Runtime.Serialization.Xml/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-nUQx/5OVgrqEba3+j7OdiofvVq9koWZAC7Z3xGI8IIViZqApWnZ5+lLcwYgTlbkobrl/Rat+Jb8GeD4WQESD2A==", - "path": "system.runtime.serialization.xml/4.3.0", - "hashPath": "system.runtime.serialization.xml.4.3.0.nupkg.sha512" - }, - "System.Security.AccessControl/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-qyp9D+SY0lSpPcw5ZXw3xCbRtU4ZpezZv+IomYMY1EUGEC/NrOKxZJtYYNVgwHLvcZvd6qzcrLUY/lQTKCk/fw==", - "path": "system.security.accesscontrol/4.5.0-preview1-26216-02", - "hashPath": "system.security.accesscontrol.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Security.Claims/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-P/+BR/2lnc4PNDHt/TPBAWHVMLMRHsyYZbU1NphW4HIWzCggz8mJbTQQ3MKljFE7LS3WagmVFuBgoLcFzYXlkA==", - "path": "system.security.claims/4.3.0", - "hashPath": "system.security.claims.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.Algorithms/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", - "path": "system.security.cryptography.algorithms/4.3.0", - "hashPath": "system.security.cryptography.algorithms.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.Cng/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ykGw2lZAetS+MklQp3qc4/tMjJGd0sqxHC96hjVTmhqw7u3FO2BlIOjNzNrfy06yKQQFt1EzzNY63Dc2bgBe4w==", - "path": "system.security.cryptography.cng/4.5.0-preview1-26216-02", - "hashPath": "system.security.cryptography.cng.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Security.Cryptography.Csp/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", - "path": "system.security.cryptography.csp/4.3.0", - "hashPath": "system.security.cryptography.csp.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", - "path": "system.security.cryptography.encoding/4.3.0", - "hashPath": "system.security.cryptography.encoding.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", - "path": "system.security.cryptography.openssl/4.3.0", - "hashPath": "system.security.cryptography.openssl.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", - "path": "system.security.cryptography.primitives/4.3.0", - "hashPath": "system.security.cryptography.primitives.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", - "path": "system.security.cryptography.x509certificates/4.3.0", - "hashPath": "system.security.cryptography.x509certificates.4.3.0.nupkg.sha512" - }, - "System.Security.Cryptography.Xml/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-kv1n+CwlaqioSczXUm4E0zZtpjgIgaROgwr4TpsIyCFv1wck7frrgCk81vwdkZxsVTzxcpc0S0t+jNX0Jy/vZg==", - "path": "system.security.cryptography.xml/4.5.0-preview1-26216-02", - "hashPath": "system.security.cryptography.xml.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Security.Permissions/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-9uSZWTkta318q2RSPwCnU54CLI5+QBm/TZT3rhx3+5YvetwJU1iz+0B/yk8xGMKA0rZ4LoHw9KVsppzDY2n75g==", - "path": "system.security.permissions/4.5.0-preview1-26216-02", - "hashPath": "system.security.permissions.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Security.Principal/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-I1tkfQlAoMM2URscUtpcRo/hX0jinXx6a/KUtEQoz3owaYwl3qwsO8cbzYVVnjxrzxjHo3nJC+62uolgeGIS9A==", - "path": "system.security.principal/4.3.0", - "hashPath": "system.security.principal.4.3.0.nupkg.sha512" - }, - "System.Security.Principal.Windows/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-FSFnv5QNuwSUgXio+n3tIvBERejx+X9RQvTNluy2odHg2dYW9tsaMf3TX/Ur8qW+RXiqftbr3NVxfab/FwozTA==", - "path": "system.security.principal.windows/4.5.0-preview1-26216-02", - "hashPath": "system.security.principal.windows.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Text.Encoding/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", - "path": "system.text.encoding/4.3.0", - "hashPath": "system.text.encoding.4.3.0.nupkg.sha512" - }, - "System.Text.Encoding.CodePages/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-7Q+JhgeJqHOZe0Xfj0unHSMJEivHQY1GrculQ0dITPxituCc0R+IiefsYGR1MTkyhjQW/77TonmtxRQjgf37CA==", - "path": "system.text.encoding.codepages/4.5.0-preview1-26216-02", - "hashPath": "system.text.encoding.codepages.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Text.Encoding.Extensions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", - "path": "system.text.encoding.extensions/4.3.0", - "hashPath": "system.text.encoding.extensions.4.3.0.nupkg.sha512" - }, - "System.Text.Encodings.Web/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-H8D/AIHqiYzdylyPnX6xA0LIz+1K2ZlWJpPfTP6JL9oq4kJ3oe2toTyovS+mELlmdD81b2RQbFUgmiCs7bg3CA==", - "path": "system.text.encodings.web/4.5.0-preview1-26216-02", - "hashPath": "system.text.encodings.web.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Text.Encodings.Web.Utf8/0.1.0-preview1-180216-4": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JrvrTZufwvKPIyUw9Ab1PSh82hHTjRy+Hvz0DkLMf2nk4CoBA7GhDMi/ocZP05fu4nzvJYEixIBPrz8IbVwm5g==", - "path": "system.text.encodings.web.utf8/0.1.0-preview1-180216-4", - "hashPath": "system.text.encodings.web.utf8.0.1.0-preview1-180216-4.nupkg.sha512" - }, - "System.Text.Primitives/0.1.0-preview1-180216-4": { - "type": "package", - "serviceable": true, - "sha512": "sha512-b4iUuhXc+doIRHZaxcm3HEeYoeLL8+GmHHy2NXql1axBoBk+YIjHlVTrbU8vA0xHbIx7j9UAzGjI8ilBJ0NvoA==", - "path": "system.text.primitives/0.1.0-preview1-180216-4", - "hashPath": "system.text.primitives.0.1.0-preview1-180216-4.nupkg.sha512" - }, - "System.Text.RegularExpressions/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", - "path": "system.text.regularexpressions/4.3.0", - "hashPath": "system.text.regularexpressions.4.3.0.nupkg.sha512" - }, - "System.Threading/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", - "path": "system.threading/4.3.0", - "hashPath": "system.threading.4.3.0.nupkg.sha512" - }, - "System.Threading.Channels/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-b3wFmR3iaNEk7eU9rcsn6iUvSaQYSbIAT/Tnd1oUFuPSvBaEPeyC+vaoYW80Wtaiwr07xbsVRCDlQGoiW3bWFg==", - "path": "system.threading.channels/4.5.0-preview1-26216-02", - "hashPath": "system.threading.channels.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Threading.Tasks/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", - "path": "system.threading.tasks/4.3.0", - "hashPath": "system.threading.tasks.4.3.0.nupkg.sha512" - }, - "System.Threading.Tasks.Extensions/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-K8c/MsUhrxl60TSeXJLs4yAy6A3mtzmWGu9HcP1bgW/gSRNDm267ESkls2n4OzEW7wF/OwszGw9O/X1xJtaeQw==", - "path": "system.threading.tasks.extensions/4.5.0-preview1-26216-02", - "hashPath": "system.threading.tasks.extensions.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Threading.Tasks.Parallel/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-cbjBNZHf/vQCfcdhzx7knsiygoCKgxL8mZOeocXZn5gWhCdzHIq6bYNKWX0LAJCWYP7bds4yBK8p06YkP0oa0g==", - "path": "system.threading.tasks.parallel/4.3.0", - "hashPath": "system.threading.tasks.parallel.4.3.0.nupkg.sha512" - }, - "System.Threading.Thread/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==", - "path": "system.threading.thread/4.3.0", - "hashPath": "system.threading.thread.4.3.0.nupkg.sha512" - }, - "System.ValueTuple/4.5.0-preview1-26216-02": { - "type": "package", - "serviceable": true, - "sha512": "sha512-azzokbU/I1xEuP+htdvnDHnioIytkhNMMNGJ4EgBt+cb/Gb0+m/niHKjiYFjNmN/edQOz75WYZ1OVQRvEOCwwA==", - "path": "system.valuetuple/4.5.0-preview1-26216-02", - "hashPath": "system.valuetuple.4.5.0-preview1-26216-02.nupkg.sha512" - }, - "System.Xml.ReaderWriter/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", - "path": "system.xml.readerwriter/4.3.0", - "hashPath": "system.xml.readerwriter.4.3.0.nupkg.sha512" - }, - "System.Xml.XDocument/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", - "path": "system.xml.xdocument/4.3.0", - "hashPath": "system.xml.xdocument.4.3.0.nupkg.sha512" - }, - "System.Xml.XmlDocument/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", - "path": "system.xml.xmldocument/4.3.0", - "hashPath": "system.xml.xmldocument.4.3.0.nupkg.sha512" - }, - "System.Xml.XmlSerializer/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-MYoTCP7EZ98RrANESW05J5ZwskKDoN0AuZ06ZflnowE50LTpbR5yRg3tHckTVm5j/m47stuGgCrCHWePyHS70Q==", - "path": "system.xml.xmlserializer/4.3.0", - "hashPath": "system.xml.xmlserializer.4.3.0.nupkg.sha512" - }, - "System.Xml.XPath/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Fkti6YYbIfCxLmm+UZf5QqfiUPHGIJTh/7N62cb+oqseFMWqMpXwp5TDv8kZdlIyi1UVReLzTS5Po1X5hdA/gw==", - "path": "system.xml.xpath/4.3.0", - "hashPath": "system.xml.xpath.4.3.0.nupkg.sha512" - }, - "System.Xml.XPath.XDocument/4.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LOSG7roULLzbOVIFHXIFHQSm4J+X2AmCEAHv2Pz2Po/RqMU/it/LtHuqjhruh/nxUJcYntAMknH1XuRS1SHNZw==", - "path": "system.xml.xpath.xdocument/4.3.0", - "hashPath": "system.xml.xpath.xdocument.4.3.0.nupkg.sha512" - } - } -} \ No newline at end of file diff --git a/DiscordianServer/published/DiscordianServer.dll b/DiscordianServer/published/DiscordianServer.dll deleted file mode 100644 index 2d9f4bb..0000000 Binary files a/DiscordianServer/published/DiscordianServer.dll and /dev/null differ diff --git a/DiscordianServer/published/DiscordianServer.runtimeconfig.json b/DiscordianServer/published/DiscordianServer.runtimeconfig.json deleted file mode 100644 index 9954eb6..0000000 --- a/DiscordianServer/published/DiscordianServer.runtimeconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "runtimeOptions": { - "tfm": "netcoreapp2.1", - "framework": { - "name": "Microsoft.AspNetCore.App", - "version": "2.1.0-preview1-final" - }, - "configProperties": { - "System.GC.Server": true - } - } -} \ No newline at end of file diff --git a/DiscordianServer/published/bundleconfig.json b/DiscordianServer/published/bundleconfig.json deleted file mode 100644 index 6d3f9a5..0000000 --- a/DiscordianServer/published/bundleconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -// Configure bundling and minification for the project. -// More info at https://go.microsoft.com/fwlink/?LinkId=808241 -[ - { - "outputFileName": "wwwroot/css/site.min.css", - // An array of relative input file paths. Globbing patterns supported - "inputFiles": [ - "wwwroot/css/site.css" - ] - }, - { - "outputFileName": "wwwroot/js/site.min.js", - "inputFiles": [ - "wwwroot/js/site.js" - ], - // Optionally specify minification options - "minify": { - "enabled": true, - "renameLocals": true - }, - // Optionally generate .map file - "sourceMap": false - } -] diff --git a/DiscordianServer/published/web.config b/DiscordianServer/published/web.config deleted file mode 100644 index 6a912e3..0000000 --- a/DiscordianServer/published/web.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/DiscordianServer/published/wwwroot/css/site.css b/DiscordianServer/published/wwwroot/css/site.css deleted file mode 100644 index 465ee54..0000000 --- a/DiscordianServer/published/wwwroot/css/site.css +++ /dev/null @@ -1,35 +0,0 @@ -body { - padding-top: 50px; - padding-bottom: 20px; -} - -/* Wrapping element */ -/* Set some basic padding to keep content from hitting the edges */ -.body-content { - padding-left: 15px; - padding-right: 15px; -} - -/* Carousel */ -.carousel-caption p { - font-size: 20px; - line-height: 1.4; -} - -/* Make .svg files in the carousel display properly in older browsers */ -.carousel-inner .item img[src$=".svg"] { - width: 100%; -} - -/* QR code generator */ -#qrCode { - margin: 15px; -} - -/* Hide/rearrange for smaller screens */ -@media screen and (max-width: 767px) { - /* Hide captions */ - .carousel-caption { - display: none; - } -} diff --git a/DiscordianServer/published/wwwroot/favicon.ico b/DiscordianServer/published/wwwroot/favicon.ico deleted file mode 100644 index a3a7999..0000000 Binary files a/DiscordianServer/published/wwwroot/favicon.ico and /dev/null differ diff --git a/DiscordianServer/published/wwwroot/images/banner3.svg b/DiscordianServer/published/wwwroot/images/banner3.svg deleted file mode 100644 index 9be2c25..0000000 --- a/DiscordianServer/published/wwwroot/images/banner3.svg +++ /dev/null @@ -1 +0,0 @@ -banner3b \ No newline at end of file diff --git a/DiscordianServer/published/wwwroot/js/site.js b/DiscordianServer/published/wwwroot/js/site.js deleted file mode 100644 index 82ecce7..0000000 --- a/DiscordianServer/published/wwwroot/js/site.js +++ /dev/null @@ -1 +0,0 @@ -// Write your Javascript code. diff --git a/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css b/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css deleted file mode 100644 index 280171c..0000000 --- a/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css +++ /dev/null @@ -1,587 +0,0 @@ -/*! - * 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, #0272408 0%, #0f0f0f 100%); - background-image: -o-linear-gradient(top, #0272408 0%, #0f0f0f 100%); - background-image: -webkit-gradient(linear, left top, left bottom, from(#0272408), to(#0f0f0f)); - background-image: linear-gradient(to bottom, #0272408 0%, #0f0f0f 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0272408', 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/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css.map b/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css.map deleted file mode 100644 index 5509b5b..0000000 --- a/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap-theme.css.map +++ /dev/null @@ -1 +0,0 @@ -{"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":"AAAA;;;;GAIG;ACeH;;;;;;EAME,yCAAA;EC2CA,4FAAA;EACQ,oFAAA;CFvDT;ACgBC;;;;;;;;;;;;ECsCA,yDAAA;EACQ,iDAAA;CFxCT;ACMC;;;;;;;;;;;;;;;;;;ECiCA,yBAAA;EACQ,iBAAA;CFnBT;AC/BD;;;;;;EAuBI,kBAAA;CDgBH;ACyBC;;EAEE,uBAAA;CDvBH;AC4BD;EErEI,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;EAuC2C,0BAAA;EAA2B,mBAAA;CDjBvE;ACpBC;;EAEE,0BAAA;EACA,6BAAA;CDsBH;ACnBC;;EAEE,0BAAA;EACA,sBAAA;CDqBH;ACfG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6BL;ACbD;EEtEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8DD;AC5DC;;EAEE,0BAAA;EACA,6BAAA;CD8DH;AC3DC;;EAEE,0BAAA;EACA,sBAAA;CD6DH;ACvDG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqEL;ACpDD;EEvEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsGD;ACpGC;;EAEE,0BAAA;EACA,6BAAA;CDsGH;ACnGC;;EAEE,0BAAA;EACA,sBAAA;CDqGH;AC/FG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6GL;AC3FD;EExEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ID;AC5IC;;EAEE,0BAAA;EACA,6BAAA;CD8IH;AC3IC;;EAEE,0BAAA;EACA,sBAAA;CD6IH;ACvIG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqJL;AClID;EEzEI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CDsLD;ACpLC;;EAEE,0BAAA;EACA,6BAAA;CDsLH;ACnLC;;EAEE,0BAAA;EACA,sBAAA;CDqLH;AC/KG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CD6LL;ACzKD;EE1EI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EAEA,uHAAA;ECnBF,oEAAA;EH4CA,4BAAA;EACA,sBAAA;CD8ND;AC5NC;;EAEE,0BAAA;EACA,6BAAA;CD8NH;AC3NC;;EAEE,0BAAA;EACA,sBAAA;CD6NH;ACvNG;;;;;;;;;;;;;;;;;;EAME,0BAAA;EACA,uBAAA;CDqOL;AC1MD;;EClCE,mDAAA;EACQ,2CAAA;CFgPT;ACrMD;;EE3FI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF0FF,0BAAA;CD2MD;ACzMD;;;EEhGI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFgGF,0BAAA;CD+MD;ACtMD;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EH+HA,mBAAA;ECjEA,4FAAA;EACQ,oFAAA;CF8QT;ACjND;;EE7GI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,yDAAA;EACQ,iDAAA;CFwRT;AC9MD;;EAEE,+CAAA;CDgND;AC5MD;EEhII,sEAAA;EACA,iEAAA;EACA,2FAAA;EAAA,oEAAA;EACA,4BAAA;EACA,uHAAA;ECnBF,oEAAA;EHkJA,mBAAA;CDkND;ACrND;;EEhII,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;ED2CF,wDAAA;EACQ,gDAAA;CF+ST;AC/ND;;EAYI,0CAAA;CDuNH;AClND;;;EAGE,iBAAA;CDoND;AC/LD;EAfI;;;IAGE,YAAA;IE7JF,yEAAA;IACA,oEAAA;IACA,8FAAA;IAAA,uEAAA;IACA,4BAAA;IACA,uHAAA;GH+WD;CACF;AC3MD;EACE,8CAAA;EC3HA,2FAAA;EACQ,mFAAA;CFyUT;ACnMD;EEtLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+MD;AC1MD;EEvLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuND;ACjND;EExLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CD+ND;ACxND;EEzLI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EF8KF,sBAAA;CDuOD;ACxND;EEjMI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH4ZH;ACrND;EE3MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHmaH;AC3ND;EE5MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH0aH;ACjOD;EE7MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHibH;ACvOD;EE9MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHwbH;AC7OD;EE/MI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH+bH;AChPD;EElLI,8MAAA;EACA,yMAAA;EACA,sMAAA;CHqaH;AC5OD;EACE,mBAAA;EC9KA,mDAAA;EACQ,2CAAA;CF6ZT;AC7OD;;;EAGE,8BAAA;EEnOE,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFiOF,sBAAA;CDmPD;ACxPD;;;EAQI,kBAAA;CDqPH;AC3OD;ECnME,kDAAA;EACQ,0CAAA;CFibT;ACrOD;EE5PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHoeH;AC3OD;EE7PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CH2eH;ACjPD;EE9PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHkfH;ACvPD;EE/PI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHyfH;AC7PD;EEhQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHggBH;ACnQD;EEjQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;CHugBH;ACnQD;EExQI,yEAAA;EACA,oEAAA;EACA,8FAAA;EAAA,uEAAA;EACA,4BAAA;EACA,uHAAA;EFsQF,sBAAA;EC3NA,0FAAA;EACQ,kFAAA;CFqeT","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, #0272408 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #0272408 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #0272408 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0272408', 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/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap.css deleted file mode 100644 index e972e13..0000000 --- a/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap.css +++ /dev/null @@ -1,6757 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -/*! 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, -figcaption, -figure, -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], -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -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; -} -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; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - 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; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 11px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled: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 { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled: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 { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled: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 { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled: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 { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled: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 { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled: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 { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-left-radius: 4px; - 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-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group .form-control:focus { - z-index: 3; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #0272408; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #0272408; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #0272408; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #0272408; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #0272408; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #0272408; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 2; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 3; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - padding-right: 15px; - padding-left: 15px; - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - background-color: rgba(0, 0, 0, 0); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -10px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -10px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -10px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-header:before, -.modal-header:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-header:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map b/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map deleted file mode 100644 index 5147db9..0000000 --- a/DiscordianServer/published/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["bootstrap.css","less/normalize.less","less/print.less","less/glyphicons.less","less/scaffolding.less","less/mixins/vendor-prefixes.less","less/mixins/tab-focus.less","less/mixins/image.less","less/type.less","less/mixins/text-emphasis.less","less/mixins/background-variant.less","less/mixins/text-overflow.less","less/code.less","less/grid.less","less/mixins/grid.less","less/mixins/grid-framework.less","less/tables.less","less/mixins/table-row.less","less/forms.less","less/mixins/forms.less","less/buttons.less","less/mixins/buttons.less","less/mixins/opacity.less","less/component-animations.less","less/dropdowns.less","less/mixins/nav-divider.less","less/mixins/reset-filter.less","less/button-groups.less","less/mixins/border-radius.less","less/input-groups.less","less/navs.less","less/navbar.less","less/mixins/nav-vertical-align.less","less/utilities.less","less/breadcrumbs.less","less/pagination.less","less/mixins/pagination.less","less/pager.less","less/labels.less","less/mixins/labels.less","less/badges.less","less/jumbotron.less","less/thumbnails.less","less/alerts.less","less/mixins/alerts.less","less/progress-bars.less","less/mixins/gradients.less","less/mixins/progress-bar.less","less/media.less","less/list-group.less","less/mixins/list-group.less","less/panels.less","less/mixins/panels.less","less/responsive-embed.less","less/wells.less","less/close.less","less/modals.less","less/tooltip.less","less/mixins/reset-text.less","less/popovers.less","less/carousel.less","less/mixins/clearfix.less","less/mixins/center-block.less","less/mixins/hide-text.less","less/responsive-utilities.less","less/mixins/responsive-visibility.less"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,4EAA4E;ACG5E;EACE,wBAAA;EACA,2BAAA;EACA,+BAAA;CDDD;ACQD;EACE,UAAA;CDND;ACmBD;;;;;;;;;;;;;EAaE,eAAA;CDjBD;ACyBD;;;;EAIE,sBAAA;EACA,yBAAA;CDvBD;AC+BD;EACE,cAAA;EACA,UAAA;CD7BD;ACqCD;;EAEE,cAAA;CDnCD;AC6CD;EACE,8BAAA;CD3CD;ACmDD;;EAEE,WAAA;CDjDD;AC2DD;EACE,0BAAA;CDzDD;ACgED;;EAEE,kBAAA;CD9DD;ACqED;EACE,mBAAA;CDnED;AC2ED;EACE,eAAA;EACA,iBAAA;CDzED;ACgFD;EACE,iBAAA;EACA,YAAA;CD9ED;ACqFD;EACE,eAAA;CDnFD;AC0FD;;EAEE,eAAA;EACA,eAAA;EACA,mBAAA;EACA,yBAAA;CDxFD;AC2FD;EACE,YAAA;CDzFD;AC4FD;EACE,gBAAA;CD1FD;ACoGD;EACE,UAAA;CDlGD;ACyGD;EACE,iBAAA;CDvGD;ACiHD;EACE,iBAAA;CD/GD;ACsHD;EACE,gCAAA;KAAA,6BAAA;UAAA,wBAAA;EACA,UAAA;CDpHD;AC2HD;EACE,eAAA;CDzHD;ACgID;;;;EAIE,kCAAA;EACA,eAAA;CD9HD;ACgJD;;;;;EAKE,eAAA;EACA,cAAA;EACA,UAAA;CD9ID;ACqJD;EACE,kBAAA;CDnJD;AC6JD;;EAEE,qBAAA;CD3JD;ACsKD;;;;EAIE,2BAAA;EACA,gBAAA;CDpKD;AC2KD;;EAEE,gBAAA;CDzKD;ACgLD;;EAEE,UAAA;EACA,WAAA;CD9KD;ACsLD;EACE,oBAAA;CDpLD;AC+LD;;EAEE,+BAAA;KAAA,4BAAA;UAAA,uBAAA;EACA,WAAA;CD7LD;ACsMD;;EAEE,aAAA;CDpMD;AC4MD;EACE,8BAAA;EACA,gCAAA;KAAA,6BAAA;UAAA,wBAAA;CD1MD;ACmND;;EAEE,yBAAA;CDjND;ACwND;EACE,0BAAA;EACA,cAAA;EACA,+BAAA;CDtND;AC8ND;EACE,UAAA;EACA,WAAA;CD5ND;ACmOD;EACE,eAAA;CDjOD;ACyOD;EACE,kBAAA;CDvOD;ACiPD;EACE,0BAAA;EACA,kBAAA;CD/OD;ACkPD;;EAEE,WAAA;CDhPD;AACD,qFAAqF;AElFrF;EA7FI;;;IAGI,mCAAA;IACA,uBAAA;IACA,oCAAA;YAAA,4BAAA;IACA,6BAAA;GFkLL;EE/KC;;IAEI,2BAAA;GFiLL;EE9KC;IACI,6BAAA;GFgLL;EE7KC;IACI,8BAAA;GF+KL;EE1KC;;IAEI,YAAA;GF4KL;EEzKC;;IAEI,uBAAA;IACA,yBAAA;GF2KL;EExKC;IACI,4BAAA;GF0KL;EEvKC;;IAEI,yBAAA;GFyKL;EEtKC;IACI,2BAAA;GFwKL;EErKC;;;IAGI,WAAA;IACA,UAAA;GFuKL;EEpKC;;IAEI,wBAAA;GFsKL;EEhKC;IACI,cAAA;GFkKL;EEhKC;;IAGQ,kCAAA;GFiKT;EE9JC;IACI,uBAAA;GFgKL;EE7JC;IACI,qCAAA;GF+JL;EEhKC;;IAKQ,kCAAA;GF+JT;EE5JC;;IAGQ,kCAAA;GF6JT;CACF;AGnPD;EACE,oCAAA;EACA,sDAAA;EACA,gYAAA;CHqPD;AG7OD;EACE,mBAAA;EACA,SAAA;EACA,sBAAA;EACA,oCAAA;EACA,mBAAA;EACA,oBAAA;EACA,eAAA;EACA,oCAAA;EACA,mCAAA;CH+OD;AG3OmC;EAAW,iBAAA;CH8O9C;AG7OmC;EAAW,iBAAA;CHgP9C;AG9OmC;;EAAW,iBAAA;CHkP9C;AGjPmC;EAAW,iBAAA;CHoP9C;AGnPmC;EAAW,iBAAA;CHsP9C;AGrPmC;EAAW,iBAAA;CHwP9C;AGvPmC;EAAW,iBAAA;CH0P9C;AGzPmC;EAAW,iBAAA;CH4P9C;AG3PmC;EAAW,iBAAA;CH8P9C;AG7PmC;EAAW,iBAAA;CHgQ9C;AG/PmC;EAAW,iBAAA;CHkQ9C;AGjQmC;EAAW,iBAAA;CHoQ9C;AGnQmC;EAAW,iBAAA;CHsQ9C;AGrQmC;EAAW,iBAAA;CHwQ9C;AGvQmC;EAAW,iBAAA;CH0Q9C;AGzQmC;EAAW,iBAAA;CH4Q9C;AG3QmC;EAAW,iBAAA;CH8Q9C;AG7QmC;EAAW,iBAAA;CHgR9C;AG/QmC;EAAW,iBAAA;CHkR9C;AGjRmC;EAAW,iBAAA;CHoR9C;AGnRmC;EAAW,iBAAA;CHsR9C;AGrRmC;EAAW,iBAAA;CHwR9C;AGvRmC;EAAW,iBAAA;CH0R9C;AGzRmC;EAAW,iBAAA;CH4R9C;AG3RmC;EAAW,iBAAA;CH8R9C;AG7RmC;EAAW,iBAAA;CHgS9C;AG/RmC;EAAW,iBAAA;CHkS9C;AGjSmC;EAAW,iBAAA;CHoS9C;AGnSmC;EAAW,iBAAA;CHsS9C;AGrSmC;EAAW,iBAAA;CHwS9C;AGvSmC;EAAW,iBAAA;CH0S9C;AGzSmC;EAAW,iBAAA;CH4S9C;AG3SmC;EAAW,iBAAA;CH8S9C;AG7SmC;EAAW,iBAAA;CHgT9C;AG/SmC;EAAW,iBAAA;CHkT9C;AGjTmC;EAAW,iBAAA;CHoT9C;AGnTmC;EAAW,iBAAA;CHsT9C;AGrTmC;EAAW,iBAAA;CHwT9C;AGvTmC;EAAW,iBAAA;CH0T9C;AGzTmC;EAAW,iBAAA;CH4T9C;AG3TmC;EAAW,iBAAA;CH8T9C;AG7TmC;EAAW,iBAAA;CHgU9C;AG/TmC;EAAW,iBAAA;CHkU9C;AGjUmC;EAAW,iBAAA;CHoU9C;AGnUmC;EAAW,iBAAA;CHsU9C;AGrUmC;EAAW,iBAAA;CHwU9C;AGvUmC;EAAW,iBAAA;CH0U9C;AGzUmC;EAAW,iBAAA;CH4U9C;AG3UmC;EAAW,iBAAA;CH8U9C;AG7UmC;EAAW,iBAAA;CHgV9C;AG/UmC;EAAW,iBAAA;CHkV9C;AGjVmC;EAAW,iBAAA;CHoV9C;AGnVmC;EAAW,iBAAA;CHsV9C;AGrVmC;EAAW,iBAAA;CHwV9C;AGvVmC;EAAW,iBAAA;CH0V9C;AGzVmC;EAAW,iBAAA;CH4V9C;AG3VmC;EAAW,iBAAA;CH8V9C;AG7VmC;EAAW,iBAAA;CHgW9C;AG/VmC;EAAW,iBAAA;CHkW9C;AGjWmC;EAAW,iBAAA;CHoW9C;AGnWmC;EAAW,iBAAA;CHsW9C;AGrWmC;EAAW,iBAAA;CHwW9C;AGvWmC;EAAW,iBAAA;CH0W9C;AGzWmC;EAAW,iBAAA;CH4W9C;AG3WmC;EAAW,iBAAA;CH8W9C;AG7WmC;EAAW,iBAAA;CHgX9C;AG/WmC;EAAW,iBAAA;CHkX9C;AGjXmC;EAAW,iBAAA;CHoX9C;AGnXmC;EAAW,iBAAA;CHsX9C;AGrXmC;EAAW,iBAAA;CHwX9C;AGvXmC;EAAW,iBAAA;CH0X9C;AGzXmC;EAAW,iBAAA;CH4X9C;AG3XmC;EAAW,iBAAA;CH8X9C;AG7XmC;EAAW,iBAAA;CHgY9C;AG/XmC;EAAW,iBAAA;CHkY9C;AGjYmC;EAAW,iBAAA;CHoY9C;AGnYmC;EAAW,iBAAA;CHsY9C;AGrYmC;EAAW,iBAAA;CHwY9C;AGvYmC;EAAW,iBAAA;CH0Y9C;AGzYmC;EAAW,iBAAA;CH4Y9C;AG3YmC;EAAW,iBAAA;CH8Y9C;AG7YmC;EAAW,iBAAA;CHgZ9C;AG/YmC;EAAW,iBAAA;CHkZ9C;AGjZmC;EAAW,iBAAA;CHoZ9C;AGnZmC;EAAW,iBAAA;CHsZ9C;AGrZmC;EAAW,iBAAA;CHwZ9C;AGvZmC;EAAW,iBAAA;CH0Z9C;AGzZmC;EAAW,iBAAA;CH4Z9C;AG3ZmC;EAAW,iBAAA;CH8Z9C;AG7ZmC;EAAW,iBAAA;CHga9C;AG/ZmC;EAAW,iBAAA;CHka9C;AGjamC;EAAW,iBAAA;CHoa9C;AGnamC;EAAW,iBAAA;CHsa9C;AGramC;EAAW,iBAAA;CHwa9C;AGvamC;EAAW,iBAAA;CH0a9C;AGzamC;EAAW,iBAAA;CH4a9C;AG3amC;EAAW,iBAAA;CH8a9C;AG7amC;EAAW,iBAAA;CHgb9C;AG/amC;EAAW,iBAAA;CHkb9C;AGjbmC;EAAW,iBAAA;CHob9C;AGnbmC;EAAW,iBAAA;CHsb9C;AGrbmC;EAAW,iBAAA;CHwb9C;AGvbmC;EAAW,iBAAA;CH0b9C;AGzbmC;EAAW,iBAAA;CH4b9C;AG3bmC;EAAW,iBAAA;CH8b9C;AG7bmC;EAAW,iBAAA;CHgc9C;AG/bmC;EAAW,iBAAA;CHkc9C;AGjcmC;EAAW,iBAAA;CHoc9C;AGncmC;EAAW,iBAAA;CHsc9C;AGrcmC;EAAW,iBAAA;CHwc9C;AGvcmC;EAAW,iBAAA;CH0c9C;AGzcmC;EAAW,iBAAA;CH4c9C;AG3cmC;EAAW,iBAAA;CH8c9C;AG7cmC;EAAW,iBAAA;CHgd9C;AG/cmC;EAAW,iBAAA;CHkd9C;AGjdmC;EAAW,iBAAA;CHod9C;AGndmC;EAAW,iBAAA;CHsd9C;AGrdmC;EAAW,iBAAA;CHwd9C;AGvdmC;EAAW,iBAAA;CH0d9C;AGzdmC;EAAW,iBAAA;CH4d9C;AG3dmC;EAAW,iBAAA;CH8d9C;AG7dmC;EAAW,iBAAA;CHge9C;AG/dmC;EAAW,iBAAA;CHke9C;AGjemC;EAAW,iBAAA;CHoe9C;AGnemC;EAAW,iBAAA;CHse9C;AGremC;EAAW,iBAAA;CHwe9C;AGvemC;EAAW,iBAAA;CH0e9C;AGzemC;EAAW,iBAAA;CH4e9C;AG3emC;EAAW,iBAAA;CH8e9C;AG7emC;EAAW,iBAAA;CHgf9C;AG/emC;EAAW,iBAAA;CHkf9C;AGjfmC;EAAW,iBAAA;CHof9C;AGnfmC;EAAW,iBAAA;CHsf9C;AGrfmC;EAAW,iBAAA;CHwf9C;AGvfmC;EAAW,iBAAA;CH0f9C;AGzfmC;EAAW,iBAAA;CH4f9C;AG3fmC;EAAW,iBAAA;CH8f9C;AG7fmC;EAAW,iBAAA;CHggB9C;AG/fmC;EAAW,iBAAA;CHkgB9C;AGjgBmC;EAAW,iBAAA;CHogB9C;AGngBmC;EAAW,iBAAA;CHsgB9C;AGrgBmC;EAAW,iBAAA;CHwgB9C;AGvgBmC;EAAW,iBAAA;CH0gB9C;AGzgBmC;EAAW,iBAAA;CH4gB9C;AG3gBmC;EAAW,iBAAA;CH8gB9C;AG7gBmC;EAAW,iBAAA;CHghB9C;AG/gBmC;EAAW,iBAAA;CHkhB9C;AGjhBmC;EAAW,iBAAA;CHohB9C;AGnhBmC;EAAW,iBAAA;CHshB9C;AGrhBmC;EAAW,iBAAA;CHwhB9C;AGvhBmC;EAAW,iBAAA;CH0hB9C;AGzhBmC;EAAW,iBAAA;CH4hB9C;AG3hBmC;EAAW,iBAAA;CH8hB9C;AG7hBmC;EAAW,iBAAA;CHgiB9C;AG/hBmC;EAAW,iBAAA;CHkiB9C;AGjiBmC;EAAW,iBAAA;CHoiB9C;AGniBmC;EAAW,iBAAA;CHsiB9C;AGriBmC;EAAW,iBAAA;CHwiB9C;AGviBmC;EAAW,iBAAA;CH0iB9C;AGziBmC;EAAW,iBAAA;CH4iB9C;AG3iBmC;EAAW,iBAAA;CH8iB9C;AG7iBmC;EAAW,iBAAA;CHgjB9C;AG/iBmC;EAAW,iBAAA;CHkjB9C;AGjjBmC;EAAW,iBAAA;CHojB9C;AGnjBmC;EAAW,iBAAA;CHsjB9C;AGrjBmC;EAAW,iBAAA;CHwjB9C;AGvjBmC;EAAW,iBAAA;CH0jB9C;AGzjBmC;EAAW,iBAAA;CH4jB9C;AG3jBmC;EAAW,iBAAA;CH8jB9C;AG7jBmC;EAAW,iBAAA;CHgkB9C;AG/jBmC;EAAW,iBAAA;CHkkB9C;AGjkBmC;EAAW,iBAAA;CHokB9C;AGnkBmC;EAAW,iBAAA;CHskB9C;AGrkBmC;EAAW,iBAAA;CHwkB9C;AGvkBmC;EAAW,iBAAA;CH0kB9C;AGzkBmC;EAAW,iBAAA;CH4kB9C;AG3kBmC;EAAW,iBAAA;CH8kB9C;AG7kBmC;EAAW,iBAAA;CHglB9C;AG/kBmC;EAAW,iBAAA;CHklB9C;AGjlBmC;EAAW,iBAAA;CHolB9C;AGnlBmC;EAAW,iBAAA;CHslB9C;AGrlBmC;EAAW,iBAAA;CHwlB9C;AGvlBmC;EAAW,iBAAA;CH0lB9C;AGzlBmC;EAAW,iBAAA;CH4lB9C;AG3lBmC;EAAW,iBAAA;CH8lB9C;AG7lBmC;EAAW,iBAAA;CHgmB9C;AG/lBmC;EAAW,iBAAA;CHkmB9C;AGjmBmC;EAAW,iBAAA;CHomB9C;AGnmBmC;EAAW,iBAAA;CHsmB9C;AGrmBmC;EAAW,iBAAA;CHwmB9C;AGvmBmC;EAAW,iBAAA;CH0mB9C;AGzmBmC;EAAW,iBAAA;CH4mB9C;AG3mBmC;EAAW,iBAAA;CH8mB9C;AG7mBmC;EAAW,iBAAA;CHgnB9C;AG/mBmC;EAAW,iBAAA;CHknB9C;AGjnBmC;EAAW,iBAAA;CHonB9C;AGnnBmC;EAAW,iBAAA;CHsnB9C;AGrnBmC;EAAW,iBAAA;CHwnB9C;AGvnBmC;EAAW,iBAAA;CH0nB9C;AGznBmC;EAAW,iBAAA;CH4nB9C;AG3nBmC;EAAW,iBAAA;CH8nB9C;AG7nBmC;EAAW,iBAAA;CHgoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AG/nBmC;EAAW,iBAAA;CHkoB9C;AGjoBmC;EAAW,iBAAA;CHooB9C;AGnoBmC;EAAW,iBAAA;CHsoB9C;AGroBmC;EAAW,iBAAA;CHwoB9C;AGvoBmC;EAAW,iBAAA;CH0oB9C;AGzoBmC;EAAW,iBAAA;CH4oB9C;AG3oBmC;EAAW,iBAAA;CH8oB9C;AG7oBmC;EAAW,iBAAA;CHgpB9C;AG/oBmC;EAAW,iBAAA;CHkpB9C;AGjpBmC;EAAW,iBAAA;CHopB9C;AGnpBmC;EAAW,iBAAA;CHspB9C;AGrpBmC;EAAW,iBAAA;CHwpB9C;AGvpBmC;EAAW,iBAAA;CH0pB9C;AGzpBmC;EAAW,iBAAA;CH4pB9C;AG3pBmC;EAAW,iBAAA;CH8pB9C;AG7pBmC;EAAW,iBAAA;CHgqB9C;AG/pBmC;EAAW,iBAAA;CHkqB9C;AGjqBmC;EAAW,iBAAA;CHoqB9C;AGnqBmC;EAAW,iBAAA;CHsqB9C;AGrqBmC;EAAW,iBAAA;CHwqB9C;AGvqBmC;EAAW,iBAAA;CH0qB9C;AGzqBmC;EAAW,iBAAA;CH4qB9C;AG3qBmC;EAAW,iBAAA;CH8qB9C;AG7qBmC;EAAW,iBAAA;CHgrB9C;AG/qBmC;EAAW,iBAAA;CHkrB9C;AGjrBmC;EAAW,iBAAA;CHorB9C;AGnrBmC;EAAW,iBAAA;CHsrB9C;AGrrBmC;EAAW,iBAAA;CHwrB9C;AGvrBmC;EAAW,iBAAA;CH0rB9C;AGzrBmC;EAAW,iBAAA;CH4rB9C;AG3rBmC;EAAW,iBAAA;CH8rB9C;AG7rBmC;EAAW,iBAAA;CHgsB9C;AG/rBmC;EAAW,iBAAA;CHksB9C;AGjsBmC;EAAW,iBAAA;CHosB9C;AGnsBmC;EAAW,iBAAA;CHssB9C;AGrsBmC;EAAW,iBAAA;CHwsB9C;AGvsBmC;EAAW,iBAAA;CH0sB9C;AGzsBmC;EAAW,iBAAA;CH4sB9C;AG3sBmC;EAAW,iBAAA;CH8sB9C;AG7sBmC;EAAW,iBAAA;CHgtB9C;AG/sBmC;EAAW,iBAAA;CHktB9C;AGjtBmC;EAAW,iBAAA;CHotB9C;AGntBmC;EAAW,iBAAA;CHstB9C;AGrtBmC;EAAW,iBAAA;CHwtB9C;AGvtBmC;EAAW,iBAAA;CH0tB9C;AGztBmC;EAAW,iBAAA;CH4tB9C;AG3tBmC;EAAW,iBAAA;CH8tB9C;AG7tBmC;EAAW,iBAAA;CHguB9C;AG/tBmC;EAAW,iBAAA;CHkuB9C;AGjuBmC;EAAW,iBAAA;CHouB9C;AGnuBmC;EAAW,iBAAA;CHsuB9C;AGruBmC;EAAW,iBAAA;CHwuB9C;AGvuBmC;EAAW,iBAAA;CH0uB9C;AGzuBmC;EAAW,iBAAA;CH4uB9C;AG3uBmC;EAAW,iBAAA;CH8uB9C;AG7uBmC;EAAW,iBAAA;CHgvB9C;AIthCD;ECgEE,+BAAA;EACG,4BAAA;EACK,uBAAA;CLy9BT;AIxhCD;;EC6DE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL+9BT;AIthCD;EACE,gBAAA;EACA,8CAAA;CJwhCD;AIrhCD;EACE,4DAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;CJuhCD;AInhCD;;;;EAIE,qBAAA;EACA,mBAAA;EACA,qBAAA;CJqhCD;AI/gCD;EACE,eAAA;EACA,sBAAA;CJihCD;AI/gCC;;EAEE,eAAA;EACA,2BAAA;CJihCH;AI9gCC;EEnDA,2CAAA;EACA,qBAAA;CNokCD;AIvgCD;EACE,UAAA;CJygCD;AIngCD;EACE,uBAAA;CJqgCD;AIjgCD;;;;;EGvEE,eAAA;EACA,gBAAA;EACA,aAAA;CP+kCD;AIrgCD;EACE,mBAAA;CJugCD;AIjgCD;EACE,aAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EC6FA,yCAAA;EACK,oCAAA;EACG,iCAAA;EEvLR,sBAAA;EACA,gBAAA;EACA,aAAA;CP+lCD;AIjgCD;EACE,mBAAA;CJmgCD;AI7/BD;EACE,iBAAA;EACA,oBAAA;EACA,UAAA;EACA,8BAAA;CJ+/BD;AIv/BD;EACE,mBAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,WAAA;EACA,iBAAA;EACA,uBAAA;EACA,UAAA;CJy/BD;AIj/BC;;EAEE,iBAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;EACA,kBAAA;EACA,WAAA;CJm/BH;AIx+BD;EACE,gBAAA;CJ0+BD;AQjoCD;;;;;;;;;;;;EAEE,qBAAA;EACA,iBAAA;EACA,iBAAA;EACA,eAAA;CR6oCD;AQlpCD;;;;;;;;;;;;;;;;;;;;;;;;EASI,oBAAA;EACA,eAAA;EACA,eAAA;CRmqCH;AQ/pCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRoqCD;AQxqCD;;;;;;;;;;;;EAQI,eAAA;CR8qCH;AQ3qCD;;;;;;EAGE,iBAAA;EACA,oBAAA;CRgrCD;AQprCD;;;;;;;;;;;;EAQI,eAAA;CR0rCH;AQtrCD;;EAAU,gBAAA;CR0rCT;AQzrCD;;EAAU,gBAAA;CR6rCT;AQ5rCD;;EAAU,gBAAA;CRgsCT;AQ/rCD;;EAAU,gBAAA;CRmsCT;AQlsCD;;EAAU,gBAAA;CRssCT;AQrsCD;;EAAU,gBAAA;CRysCT;AQnsCD;EACE,iBAAA;CRqsCD;AQlsCD;EACE,oBAAA;EACA,gBAAA;EACA,iBAAA;EACA,iBAAA;CRosCD;AQ/rCD;EAwOA;IA1OI,gBAAA;GRqsCD;CACF;AQ7rCD;;EAEE,eAAA;CR+rCD;AQ5rCD;;EAEE,0BAAA;EACA,cAAA;CR8rCD;AQ1rCD;EAAuB,iBAAA;CR6rCtB;AQ5rCD;EAAuB,kBAAA;CR+rCtB;AQ9rCD;EAAuB,mBAAA;CRisCtB;AQhsCD;EAAuB,oBAAA;CRmsCtB;AQlsCD;EAAuB,oBAAA;CRqsCtB;AQlsCD;EAAuB,0BAAA;CRqsCtB;AQpsCD;EAAuB,0BAAA;CRusCtB;AQtsCD;EAAuB,2BAAA;CRysCtB;AQtsCD;EACE,eAAA;CRwsCD;AQtsCD;ECrGE,eAAA;CT8yCD;AS7yCC;;EAEE,eAAA;CT+yCH;AQ1sCD;ECxGE,eAAA;CTqzCD;ASpzCC;;EAEE,eAAA;CTszCH;AQ9sCD;EC3GE,eAAA;CT4zCD;AS3zCC;;EAEE,eAAA;CT6zCH;AQltCD;EC9GE,eAAA;CTm0CD;ASl0CC;;EAEE,eAAA;CTo0CH;AQttCD;ECjHE,eAAA;CT00CD;ASz0CC;;EAEE,eAAA;CT20CH;AQttCD;EAGE,YAAA;EE3HA,0BAAA;CVk1CD;AUj1CC;;EAEE,0BAAA;CVm1CH;AQxtCD;EE9HE,0BAAA;CVy1CD;AUx1CC;;EAEE,0BAAA;CV01CH;AQ5tCD;EEjIE,0BAAA;CVg2CD;AU/1CC;;EAEE,0BAAA;CVi2CH;AQhuCD;EEpIE,0BAAA;CVu2CD;AUt2CC;;EAEE,0BAAA;CVw2CH;AQpuCD;EEvIE,0BAAA;CV82CD;AU72CC;;EAEE,0BAAA;CV+2CH;AQnuCD;EACE,oBAAA;EACA,oBAAA;EACA,iCAAA;CRquCD;AQ7tCD;;EAEE,cAAA;EACA,oBAAA;CR+tCD;AQluCD;;;;EAMI,iBAAA;CRkuCH;AQ3tCD;EACE,gBAAA;EACA,iBAAA;CR6tCD;AQztCD;EALE,gBAAA;EACA,iBAAA;EAMA,kBAAA;CR4tCD;AQ9tCD;EAKI,sBAAA;EACA,kBAAA;EACA,mBAAA;CR4tCH;AQvtCD;EACE,cAAA;EACA,oBAAA;CRytCD;AQvtCD;;EAEE,wBAAA;CRytCD;AQvtCD;EACE,kBAAA;CRytCD;AQvtCD;EACE,eAAA;CRytCD;AQhsCD;EA6EA;IAvFM,YAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IGtNJ,iBAAA;IACA,wBAAA;IACA,oBAAA;GXq6CC;EQ7nCH;IAhFM,mBAAA;GRgtCH;CACF;AQvsCD;;EAGE,aAAA;EACA,kCAAA;CRwsCD;AQtsCD;EACE,eAAA;EA9IqB,0BAAA;CRu1CtB;AQpsCD;EACE,mBAAA;EACA,iBAAA;EACA,kBAAA;EACA,+BAAA;CRssCD;AQjsCG;;;EACE,iBAAA;CRqsCL;AQ/sCD;;;EAmBI,eAAA;EACA,eAAA;EACA,wBAAA;EACA,eAAA;CRisCH;AQ/rCG;;;EACE,uBAAA;CRmsCL;AQ3rCD;;EAEE,oBAAA;EACA,gBAAA;EACA,gCAAA;EACA,eAAA;EACA,kBAAA;CR6rCD;AQvrCG;;;;;;EAAW,YAAA;CR+rCd;AQ9rCG;;;;;;EACE,uBAAA;CRqsCL;AQ/rCD;EACE,oBAAA;EACA,mBAAA;EACA,wBAAA;CRisCD;AYv+CD;;;;EAIE,+DAAA;CZy+CD;AYr+CD;EACE,iBAAA;EACA,eAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CZu+CD;AYn+CD;EACE,iBAAA;EACA,eAAA;EACA,YAAA;EACA,uBAAA;EACA,mBAAA;EACA,uDAAA;UAAA,+CAAA;CZq+CD;AY3+CD;EASI,WAAA;EACA,gBAAA;EACA,kBAAA;EACA,yBAAA;UAAA,iBAAA;CZq+CH;AYh+CD;EACE,eAAA;EACA,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,sBAAA;EACA,sBAAA;EACA,eAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;CZk+CD;AY7+CD;EAeI,WAAA;EACA,mBAAA;EACA,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,iBAAA;CZi+CH;AY59CD;EACE,kBAAA;EACA,mBAAA;CZ89CD;AaxhDD;ECHE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;Cd8hDD;AaxhDC;EAqEF;IAvEI,aAAA;Gb8hDD;CACF;Aa1hDC;EAkEF;IApEI,aAAA;GbgiDD;CACF;Aa5hDD;EA+DA;IAjEI,cAAA;GbkiDD;CACF;AazhDD;ECvBE,mBAAA;EACA,kBAAA;EACA,mBAAA;EACA,oBAAA;CdmjDD;AathDD;ECvBE,mBAAA;EACA,oBAAA;CdgjDD;AehjDG;EACE,mBAAA;EAEA,gBAAA;EAEA,mBAAA;EACA,oBAAA;CfgjDL;AehiDG;EACE,YAAA;CfkiDL;Ae3hDC;EACE,YAAA;Cf6hDH;Ae9hDC;EACE,oBAAA;CfgiDH;AejiDC;EACE,oBAAA;CfmiDH;AepiDC;EACE,WAAA;CfsiDH;AeviDC;EACE,oBAAA;CfyiDH;Ae1iDC;EACE,oBAAA;Cf4iDH;Ae7iDC;EACE,WAAA;Cf+iDH;AehjDC;EACE,oBAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,WAAA;CfwjDH;AezjDC;EACE,oBAAA;Cf2jDH;Ae5jDC;EACE,mBAAA;Cf8jDH;AehjDC;EACE,YAAA;CfkjDH;AenjDC;EACE,oBAAA;CfqjDH;AetjDC;EACE,oBAAA;CfwjDH;AezjDC;EACE,WAAA;Cf2jDH;Ae5jDC;EACE,oBAAA;Cf8jDH;Ae/jDC;EACE,oBAAA;CfikDH;AelkDC;EACE,WAAA;CfokDH;AerkDC;EACE,oBAAA;CfukDH;AexkDC;EACE,oBAAA;Cf0kDH;Ae3kDC;EACE,WAAA;Cf6kDH;Ae9kDC;EACE,oBAAA;CfglDH;AejlDC;EACE,mBAAA;CfmlDH;Ae/kDC;EACE,YAAA;CfilDH;AejmDC;EACE,WAAA;CfmmDH;AepmDC;EACE,mBAAA;CfsmDH;AevmDC;EACE,mBAAA;CfymDH;Ae1mDC;EACE,UAAA;Cf4mDH;Ae7mDC;EACE,mBAAA;Cf+mDH;AehnDC;EACE,mBAAA;CfknDH;AennDC;EACE,UAAA;CfqnDH;AetnDC;EACE,mBAAA;CfwnDH;AeznDC;EACE,mBAAA;Cf2nDH;Ae5nDC;EACE,UAAA;Cf8nDH;Ae/nDC;EACE,mBAAA;CfioDH;AeloDC;EACE,kBAAA;CfooDH;AehoDC;EACE,WAAA;CfkoDH;AepnDC;EACE,kBAAA;CfsnDH;AevnDC;EACE,0BAAA;CfynDH;Ae1nDC;EACE,0BAAA;Cf4nDH;Ae7nDC;EACE,iBAAA;Cf+nDH;AehoDC;EACE,0BAAA;CfkoDH;AenoDC;EACE,0BAAA;CfqoDH;AetoDC;EACE,iBAAA;CfwoDH;AezoDC;EACE,0BAAA;Cf2oDH;Ae5oDC;EACE,0BAAA;Cf8oDH;Ae/oDC;EACE,iBAAA;CfipDH;AelpDC;EACE,0BAAA;CfopDH;AerpDC;EACE,yBAAA;CfupDH;AexpDC;EACE,gBAAA;Cf0pDH;Aa1pDD;EElCI;IACE,YAAA;Gf+rDH;EexrDD;IACE,YAAA;Gf0rDD;Ee3rDD;IACE,oBAAA;Gf6rDD;Ee9rDD;IACE,oBAAA;GfgsDD;EejsDD;IACE,WAAA;GfmsDD;EepsDD;IACE,oBAAA;GfssDD;EevsDD;IACE,oBAAA;GfysDD;Ee1sDD;IACE,WAAA;Gf4sDD;Ee7sDD;IACE,oBAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,WAAA;GfqtDD;EettDD;IACE,oBAAA;GfwtDD;EeztDD;IACE,mBAAA;Gf2tDD;Ee7sDD;IACE,YAAA;Gf+sDD;EehtDD;IACE,oBAAA;GfktDD;EentDD;IACE,oBAAA;GfqtDD;EettDD;IACE,WAAA;GfwtDD;EeztDD;IACE,oBAAA;Gf2tDD;Ee5tDD;IACE,oBAAA;Gf8tDD;Ee/tDD;IACE,WAAA;GfiuDD;EeluDD;IACE,oBAAA;GfouDD;EeruDD;IACE,oBAAA;GfuuDD;EexuDD;IACE,WAAA;Gf0uDD;Ee3uDD;IACE,oBAAA;Gf6uDD;Ee9uDD;IACE,mBAAA;GfgvDD;Ee5uDD;IACE,YAAA;Gf8uDD;Ee9vDD;IACE,WAAA;GfgwDD;EejwDD;IACE,mBAAA;GfmwDD;EepwDD;IACE,mBAAA;GfswDD;EevwDD;IACE,UAAA;GfywDD;Ee1wDD;IACE,mBAAA;Gf4wDD;Ee7wDD;IACE,mBAAA;Gf+wDD;EehxDD;IACE,UAAA;GfkxDD;EenxDD;IACE,mBAAA;GfqxDD;EetxDD;IACE,mBAAA;GfwxDD;EezxDD;IACE,UAAA;Gf2xDD;Ee5xDD;IACE,mBAAA;Gf8xDD;Ee/xDD;IACE,kBAAA;GfiyDD;Ee7xDD;IACE,WAAA;Gf+xDD;EejxDD;IACE,kBAAA;GfmxDD;EepxDD;IACE,0BAAA;GfsxDD;EevxDD;IACE,0BAAA;GfyxDD;Ee1xDD;IACE,iBAAA;Gf4xDD;Ee7xDD;IACE,0BAAA;Gf+xDD;EehyDD;IACE,0BAAA;GfkyDD;EenyDD;IACE,iBAAA;GfqyDD;EetyDD;IACE,0BAAA;GfwyDD;EezyDD;IACE,0BAAA;Gf2yDD;Ee5yDD;IACE,iBAAA;Gf8yDD;Ee/yDD;IACE,0BAAA;GfizDD;EelzDD;IACE,yBAAA;GfozDD;EerzDD;IACE,gBAAA;GfuzDD;CACF;Aa/yDD;EE3CI;IACE,YAAA;Gf61DH;Eet1DD;IACE,YAAA;Gfw1DD;Eez1DD;IACE,oBAAA;Gf21DD;Ee51DD;IACE,oBAAA;Gf81DD;Ee/1DD;IACE,WAAA;Gfi2DD;Eel2DD;IACE,oBAAA;Gfo2DD;Eer2DD;IACE,oBAAA;Gfu2DD;Eex2DD;IACE,WAAA;Gf02DD;Ee32DD;IACE,oBAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,WAAA;Gfm3DD;Eep3DD;IACE,oBAAA;Gfs3DD;Eev3DD;IACE,mBAAA;Gfy3DD;Ee32DD;IACE,YAAA;Gf62DD;Ee92DD;IACE,oBAAA;Gfg3DD;Eej3DD;IACE,oBAAA;Gfm3DD;Eep3DD;IACE,WAAA;Gfs3DD;Eev3DD;IACE,oBAAA;Gfy3DD;Ee13DD;IACE,oBAAA;Gf43DD;Ee73DD;IACE,WAAA;Gf+3DD;Eeh4DD;IACE,oBAAA;Gfk4DD;Een4DD;IACE,oBAAA;Gfq4DD;Eet4DD;IACE,WAAA;Gfw4DD;Eez4DD;IACE,oBAAA;Gf24DD;Ee54DD;IACE,mBAAA;Gf84DD;Ee14DD;IACE,YAAA;Gf44DD;Ee55DD;IACE,WAAA;Gf85DD;Ee/5DD;IACE,mBAAA;Gfi6DD;Eel6DD;IACE,mBAAA;Gfo6DD;Eer6DD;IACE,UAAA;Gfu6DD;Eex6DD;IACE,mBAAA;Gf06DD;Ee36DD;IACE,mBAAA;Gf66DD;Ee96DD;IACE,UAAA;Gfg7DD;Eej7DD;IACE,mBAAA;Gfm7DD;Eep7DD;IACE,mBAAA;Gfs7DD;Eev7DD;IACE,UAAA;Gfy7DD;Ee17DD;IACE,mBAAA;Gf47DD;Ee77DD;IACE,kBAAA;Gf+7DD;Ee37DD;IACE,WAAA;Gf67DD;Ee/6DD;IACE,kBAAA;Gfi7DD;Eel7DD;IACE,0BAAA;Gfo7DD;Eer7DD;IACE,0BAAA;Gfu7DD;Eex7DD;IACE,iBAAA;Gf07DD;Ee37DD;IACE,0BAAA;Gf67DD;Ee97DD;IACE,0BAAA;Gfg8DD;Eej8DD;IACE,iBAAA;Gfm8DD;Eep8DD;IACE,0BAAA;Gfs8DD;Eev8DD;IACE,0BAAA;Gfy8DD;Ee18DD;IACE,iBAAA;Gf48DD;Ee78DD;IACE,0BAAA;Gf+8DD;Eeh9DD;IACE,yBAAA;Gfk9DD;Een9DD;IACE,gBAAA;Gfq9DD;CACF;Aa18DD;EE9CI;IACE,YAAA;Gf2/DH;Eep/DD;IACE,YAAA;Gfs/DD;Eev/DD;IACE,oBAAA;Gfy/DD;Ee1/DD;IACE,oBAAA;Gf4/DD;Ee7/DD;IACE,WAAA;Gf+/DD;EehgED;IACE,oBAAA;GfkgED;EengED;IACE,oBAAA;GfqgED;EetgED;IACE,WAAA;GfwgED;EezgED;IACE,oBAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,WAAA;GfihED;EelhED;IACE,oBAAA;GfohED;EerhED;IACE,mBAAA;GfuhED;EezgED;IACE,YAAA;Gf2gED;Ee5gED;IACE,oBAAA;Gf8gED;Ee/gED;IACE,oBAAA;GfihED;EelhED;IACE,WAAA;GfohED;EerhED;IACE,oBAAA;GfuhED;EexhED;IACE,oBAAA;Gf0hED;Ee3hED;IACE,WAAA;Gf6hED;Ee9hED;IACE,oBAAA;GfgiED;EejiED;IACE,oBAAA;GfmiED;EepiED;IACE,WAAA;GfsiED;EeviED;IACE,oBAAA;GfyiED;Ee1iED;IACE,mBAAA;Gf4iED;EexiED;IACE,YAAA;Gf0iED;Ee1jED;IACE,WAAA;Gf4jED;Ee7jED;IACE,mBAAA;Gf+jED;EehkED;IACE,mBAAA;GfkkED;EenkED;IACE,UAAA;GfqkED;EetkED;IACE,mBAAA;GfwkED;EezkED;IACE,mBAAA;Gf2kED;Ee5kED;IACE,UAAA;Gf8kED;Ee/kED;IACE,mBAAA;GfilED;EellED;IACE,mBAAA;GfolED;EerlED;IACE,UAAA;GfulED;EexlED;IACE,mBAAA;Gf0lED;Ee3lED;IACE,kBAAA;Gf6lED;EezlED;IACE,WAAA;Gf2lED;Ee7kED;IACE,kBAAA;Gf+kED;EehlED;IACE,0BAAA;GfklED;EenlED;IACE,0BAAA;GfqlED;EetlED;IACE,iBAAA;GfwlED;EezlED;IACE,0BAAA;Gf2lED;Ee5lED;IACE,0BAAA;Gf8lED;Ee/lED;IACE,iBAAA;GfimED;EelmED;IACE,0BAAA;GfomED;EermED;IACE,0BAAA;GfumED;EexmED;IACE,iBAAA;Gf0mED;Ee3mED;IACE,0BAAA;Gf6mED;Ee9mED;IACE,yBAAA;GfgnED;EejnED;IACE,gBAAA;GfmnED;CACF;AgBvrED;EACE,8BAAA;ChByrED;AgBvrED;EACE,iBAAA;EACA,oBAAA;EACA,eAAA;EACA,iBAAA;ChByrED;AgBvrED;EACE,iBAAA;ChByrED;AgBnrED;EACE,YAAA;EACA,gBAAA;EACA,oBAAA;ChBqrED;AgBxrED;;;;;;EAWQ,aAAA;EACA,wBAAA;EACA,oBAAA;EACA,2BAAA;ChBqrEP;AgBnsED;EAoBI,uBAAA;EACA,8BAAA;ChBkrEH;AgBvsED;;;;;;EA8BQ,cAAA;ChBirEP;AgB/sED;EAoCI,2BAAA;ChB8qEH;AgBltED;EAyCI,uBAAA;ChB4qEH;AgBrqED;;;;;;EAOQ,aAAA;ChBsqEP;AgB3pED;EACE,uBAAA;ChB6pED;AgB9pED;;;;;;EAQQ,uBAAA;ChB8pEP;AgBtqED;;EAeM,yBAAA;ChB2pEL;AgBjpED;EAEI,0BAAA;ChBkpEH;AgBzoED;EAEI,0BAAA;ChB0oEH;AgBjoED;EACE,iBAAA;EACA,YAAA;EACA,sBAAA;ChBmoED;AgB9nEG;;EACE,iBAAA;EACA,YAAA;EACA,oBAAA;ChBioEL;AiB7wEC;;;;;;;;;;;;EAOI,0BAAA;CjBoxEL;AiB9wEC;;;;;EAMI,0BAAA;CjB+wEL;AiBlyEC;;;;;;;;;;;;EAOI,0BAAA;CjByyEL;AiBnyEC;;;;;EAMI,0BAAA;CjBoyEL;AiBvzEC;;;;;;;;;;;;EAOI,0BAAA;CjB8zEL;AiBxzEC;;;;;EAMI,0BAAA;CjByzEL;AiB50EC;;;;;;;;;;;;EAOI,0BAAA;CjBm1EL;AiB70EC;;;;;EAMI,0BAAA;CjB80EL;AiBj2EC;;;;;;;;;;;;EAOI,0BAAA;CjBw2EL;AiBl2EC;;;;;EAMI,0BAAA;CjBm2EL;AgBjtED;EACE,iBAAA;EACA,kBAAA;ChBmtED;AgBtpED;EACA;IA3DI,YAAA;IACA,oBAAA;IACA,mBAAA;IACA,6CAAA;IACA,uBAAA;GhBotED;EgB7pEH;IAnDM,iBAAA;GhBmtEH;EgBhqEH;;;;;;IA1CY,oBAAA;GhBktET;EgBxqEH;IAlCM,UAAA;GhB6sEH;EgB3qEH;;;;;;IAzBY,eAAA;GhB4sET;EgBnrEH;;;;;;IArBY,gBAAA;GhBgtET;EgB3rEH;;;;IARY,iBAAA;GhBysET;CACF;AkBn6ED;EACE,WAAA;EACA,UAAA;EACA,UAAA;EAIA,aAAA;ClBk6ED;AkB/5ED;EACE,eAAA;EACA,YAAA;EACA,WAAA;EACA,oBAAA;EACA,gBAAA;EACA,qBAAA;EACA,eAAA;EACA,UAAA;EACA,iCAAA;ClBi6ED;AkB95ED;EACE,sBAAA;EACA,gBAAA;EACA,mBAAA;EACA,kBAAA;ClBg6ED;AkBr5ED;Eb4BE,+BAAA;EACG,4BAAA;EACK,uBAAA;CL43ET;AkBr5ED;;EAEE,gBAAA;EACA,mBAAA;EACA,oBAAA;ClBu5ED;AkBp5ED;EACE,eAAA;ClBs5ED;AkBl5ED;EACE,eAAA;EACA,YAAA;ClBo5ED;AkBh5ED;;EAEE,aAAA;ClBk5ED;AkB94ED;;;EZrEE,2CAAA;EACA,qBAAA;CNw9ED;AkB74ED;EACE,eAAA;EACA,iBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;ClB+4ED;AkBr3ED;EACE,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;EbxDA,yDAAA;EACQ,iDAAA;EAyHR,uFAAA;EACK,0EAAA;EACG,uEAAA;CLwzET;AmBh8EC;EACE,sBAAA;EACA,WAAA;EdUF,uFAAA;EACQ,+EAAA;CLy7ET;AKx5EC;EACE,YAAA;EACA,WAAA;CL05EH;AKx5EC;EAA0B,YAAA;CL25E3B;AK15EC;EAAgC,YAAA;CL65EjC;AkBj4EC;EACE,UAAA;EACA,8BAAA;ClBm4EH;AkB33EC;;;EAGE,0BAAA;EACA,WAAA;ClB63EH;AkB13EC;;EAEE,oBAAA;ClB43EH;AkBx3EC;EACE,aAAA;ClB03EH;AkB92ED;EACE,yBAAA;ClBg3ED;AkBx0ED;EAtBI;;;;IACE,kBAAA;GlBo2EH;EkBj2EC;;;;;;;;IAEE,kBAAA;GlBy2EH;EkBt2EC;;;;;;;;IAEE,kBAAA;GlB82EH;CACF;AkBp2ED;EACE,oBAAA;ClBs2ED;AkB91ED;;EAEE,mBAAA;EACA,eAAA;EACA,iBAAA;EACA,oBAAA;ClBg2ED;AkBr2ED;;EAQI,iBAAA;EACA,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,gBAAA;ClBi2EH;AkB91ED;;;;EAIE,mBAAA;EACA,mBAAA;EACA,mBAAA;ClBg2ED;AkB71ED;;EAEE,iBAAA;ClB+1ED;AkB31ED;;EAEE,mBAAA;EACA,sBAAA;EACA,mBAAA;EACA,iBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;ClB61ED;AkB31ED;;EAEE,cAAA;EACA,kBAAA;ClB61ED;AkBp1EC;;;;;;EAGE,oBAAA;ClBy1EH;AkBn1EC;;;;EAEE,oBAAA;ClBu1EH;AkBj1EC;;;;EAGI,oBAAA;ClBo1EL;AkBz0ED;EAEE,iBAAA;EACA,oBAAA;EAEA,iBAAA;EACA,iBAAA;ClBy0ED;AkBv0EC;;EAEE,gBAAA;EACA,iBAAA;ClBy0EH;AkB5zED;ECnQE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnBkkFD;AmBhkFC;EACE,aAAA;EACA,kBAAA;CnBkkFH;AmB/jFC;;EAEE,aAAA;CnBikFH;AkBx0ED;EAEI,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;ClBy0EH;AkB/0ED;EASI,aAAA;EACA,kBAAA;ClBy0EH;AkBn1ED;;EAcI,aAAA;ClBy0EH;AkBv1ED;EAiBI,aAAA;EACA,iBAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;ClBy0EH;AkBr0ED;EC/RE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBumFD;AmBrmFC;EACE,aAAA;EACA,kBAAA;CnBumFH;AmBpmFC;;EAEE,aAAA;CnBsmFH;AkBj1ED;EAEI,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;ClBk1EH;AkBx1ED;EASI,aAAA;EACA,kBAAA;ClBk1EH;AkB51ED;;EAcI,aAAA;ClBk1EH;AkBh2ED;EAiBI,aAAA;EACA,iBAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;ClBk1EH;AkBz0ED;EAEE,mBAAA;ClB00ED;AkB50ED;EAMI,sBAAA;ClBy0EH;AkBr0ED;EACE,mBAAA;EACA,OAAA;EACA,SAAA;EACA,WAAA;EACA,eAAA;EACA,YAAA;EACA,aAAA;EACA,kBAAA;EACA,mBAAA;EACA,qBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBr0ED;;;EAGE,YAAA;EACA,aAAA;EACA,kBAAA;ClBu0ED;AkBn0ED;;;;;;;;;;EC1ZI,eAAA;CnByuFH;AkB/0ED;ECtZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CL0rFT;AmBxuFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL+rFT;AkBz1ED;EC5YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBwuFH;AkB91ED;ECtYI,eAAA;CnBuuFH;AkB91ED;;;;;;;;;;EC7ZI,eAAA;CnBuwFH;AkB12ED;ECzZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLwtFT;AmBtwFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL6tFT;AkBp3ED;EC/YI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBswFH;AkBz3ED;ECzYI,eAAA;CnBqwFH;AkBz3ED;;;;;;;;;;EChaI,eAAA;CnBqyFH;AkBr4ED;EC5ZI,sBAAA;Ed+CF,yDAAA;EACQ,iDAAA;CLsvFT;AmBpyFG;EACE,sBAAA;Ed4CJ,0EAAA;EACQ,kEAAA;CL2vFT;AkB/4ED;EClZI,eAAA;EACA,sBAAA;EACA,0BAAA;CnBoyFH;AkBp5ED;EC5YI,eAAA;CnBmyFH;AkBh5EC;EACE,UAAA;ClBk5EH;AkBh5EC;EACE,OAAA;ClBk5EH;AkBx4ED;EACE,eAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;ClB04ED;AkBvzED;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBy3EH;EkBrvEH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBu3EH;EkB1vEH;IAxHM,sBAAA;GlBq3EH;EkB7vEH;IApHM,sBAAA;IACA,uBAAA;GlBo3EH;EkBjwEH;;;IA9GQ,YAAA;GlBo3EL;EkBtwEH;IAxGM,YAAA;GlBi3EH;EkBzwEH;IApGM,iBAAA;IACA,uBAAA;GlBg3EH;EkB7wEH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB62EH;EkBpxEH;;IAtFQ,gBAAA;GlB82EL;EkBxxEH;;IAjFM,mBAAA;IACA,eAAA;GlB62EH;EkB7xEH;IA3EM,OAAA;GlB22EH;CACF;AkBj2ED;;;;EASI,cAAA;EACA,iBAAA;EACA,iBAAA;ClB81EH;AkBz2ED;;EAiBI,iBAAA;ClB41EH;AkB72ED;EJthBE,mBAAA;EACA,oBAAA;Cds4FD;AkB10EC;EAyBF;IAnCM,kBAAA;IACA,iBAAA;IACA,iBAAA;GlBw1EH;CACF;AkBx3ED;EAwCI,YAAA;ClBm1EH;AkBr0EC;EAUF;IAdQ,kBAAA;IACA,gBAAA;GlB60EL;CACF;AkBn0EC;EAEF;IANQ,iBAAA;IACA,gBAAA;GlB20EL;CACF;AoBp6FD;EACE,sBAAA;EACA,iBAAA;EACA,oBAAA;EACA,mBAAA;EACA,uBAAA;EACA,+BAAA;MAAA,2BAAA;EACA,gBAAA;EACA,uBAAA;EACA,8BAAA;EACA,oBAAA;EC0CA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,mBAAA;EhB+JA,0BAAA;EACG,uBAAA;EACC,sBAAA;EACI,kBAAA;CL+tFT;AoBv6FG;;;;;;EdnBF,2CAAA;EACA,qBAAA;CNk8FD;AoB16FC;;;EAGE,YAAA;EACA,sBAAA;CpB46FH;AoBz6FC;;EAEE,WAAA;EACA,uBAAA;Ef2BF,yDAAA;EACQ,iDAAA;CLi5FT;AoBz6FC;;;EAGE,oBAAA;EE7CF,cAAA;EAGA,0BAAA;EjB8DA,yBAAA;EACQ,iBAAA;CL05FT;AoBz6FG;;EAEE,qBAAA;CpB26FL;AoBl6FD;EC3DE,YAAA;EACA,uBAAA;EACA,mBAAA;CrBg+FD;AqB99FC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBg+FP;AqB99FG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBs+FT;AqBn+FC;;;EAGE,uBAAA;CrBq+FH;AqBh+FG;;;;;;;;;EAGE,uBAAA;EACI,mBAAA;CrBw+FT;AoBv9FD;ECZI,YAAA;EACA,uBAAA;CrBs+FH;AoBx9FD;EC9DE,YAAA;EACA,0BAAA;EACA,sBAAA;CrByhGD;AqBvhGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrByhGP;AqBvhGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB+hGT;AqB5hGC;;;EAGE,uBAAA;CrB8hGH;AqBzhGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBiiGT;AoB7gGD;ECfI,eAAA;EACA,uBAAA;CrB+hGH;AoB7gGD;EClEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBklGD;AqBhlGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBklGP;AqBhlGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBwlGT;AqBrlGC;;;EAGE,uBAAA;CrBulGH;AqBllGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB0lGT;AoBlkGD;ECnBI,eAAA;EACA,uBAAA;CrBwlGH;AoBlkGD;ECtEE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB2oGD;AqBzoGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB2oGP;AqBzoGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBipGT;AqB9oGC;;;EAGE,uBAAA;CrBgpGH;AqB3oGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBmpGT;AoBvnGD;ECvBI,eAAA;EACA,uBAAA;CrBipGH;AoBvnGD;EC1EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrBosGD;AqBlsGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBosGP;AqBlsGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB0sGT;AqBvsGC;;;EAGE,uBAAA;CrBysGH;AqBpsGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrB4sGT;AoB5qGD;EC3BI,eAAA;EACA,uBAAA;CrB0sGH;AoB5qGD;EC9EE,YAAA;EACA,0BAAA;EACA,sBAAA;CrB6vGD;AqB3vGC;;EAEE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;EACE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGC;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrB6vGP;AqB3vGG;;;;;;;;;EAGE,YAAA;EACA,0BAAA;EACI,sBAAA;CrBmwGT;AqBhwGC;;;EAGE,uBAAA;CrBkwGH;AqB7vGG;;;;;;;;;EAGE,0BAAA;EACI,sBAAA;CrBqwGT;AoBjuGD;EC/BI,eAAA;EACA,uBAAA;CrBmwGH;AoB5tGD;EACE,eAAA;EACA,oBAAA;EACA,iBAAA;CpB8tGD;AoB5tGC;;;;;EAKE,8BAAA;EfnCF,yBAAA;EACQ,iBAAA;CLkwGT;AoB7tGC;;;;EAIE,0BAAA;CpB+tGH;AoB7tGC;;EAEE,eAAA;EACA,2BAAA;EACA,8BAAA;CpB+tGH;AoB3tGG;;;;EAEE,eAAA;EACA,sBAAA;CpB+tGL;AoBttGD;;ECxEE,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CrBkyGD;AoBztGD;;EC5EE,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrByyGD;AoB5tGD;;EChFE,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CrBgzGD;AoB3tGD;EACE,eAAA;EACA,YAAA;CpB6tGD;AoBztGD;EACE,gBAAA;CpB2tGD;AoBptGC;;;EACE,YAAA;CpBwtGH;AuBl3GD;EACE,WAAA;ElBoLA,yCAAA;EACK,oCAAA;EACG,iCAAA;CLisGT;AuBr3GC;EACE,WAAA;CvBu3GH;AuBn3GD;EACE,cAAA;CvBq3GD;AuBn3GC;EAAY,eAAA;CvBs3Gb;AuBr3GC;EAAY,mBAAA;CvBw3Gb;AuBv3GC;EAAY,yBAAA;CvB03Gb;AuBv3GD;EACE,mBAAA;EACA,UAAA;EACA,iBAAA;ElBuKA,gDAAA;EACQ,2CAAA;KAAA,wCAAA;EAOR,mCAAA;EACQ,8BAAA;KAAA,2BAAA;EAGR,yCAAA;EACQ,oCAAA;KAAA,iCAAA;CL2sGT;AwBr5GD;EACE,sBAAA;EACA,SAAA;EACA,UAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,yBAAA;EACA,oCAAA;EACA,mCAAA;CxBu5GD;AwBn5GD;;EAEE,mBAAA;CxBq5GD;AwBj5GD;EACE,WAAA;CxBm5GD;AwB/4GD;EACE,mBAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,YAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iBAAA;EACA,uBAAA;EACA,uBAAA;EACA,sCAAA;EACA,mBAAA;EnBsBA,oDAAA;EACQ,4CAAA;EmBrBR,qCAAA;UAAA,6BAAA;CxBk5GD;AwB74GC;EACE,SAAA;EACA,WAAA;CxB+4GH;AwBx6GD;ECzBE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBo8GD;AwB96GD;EAmCI,eAAA;EACA,kBAAA;EACA,YAAA;EACA,oBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxB84GH;AwBx4GC;;EAEE,sBAAA;EACA,eAAA;EACA,0BAAA;CxB04GH;AwBp4GC;;;EAGE,YAAA;EACA,sBAAA;EACA,WAAA;EACA,0BAAA;CxBs4GH;AwB73GC;;;EAGE,eAAA;CxB+3GH;AwB33GC;;EAEE,sBAAA;EACA,8BAAA;EACA,uBAAA;EE3GF,oEAAA;EF6GE,oBAAA;CxB63GH;AwBx3GD;EAGI,eAAA;CxBw3GH;AwB33GD;EAQI,WAAA;CxBs3GH;AwB92GD;EACE,WAAA;EACA,SAAA;CxBg3GD;AwBx2GD;EACE,QAAA;EACA,YAAA;CxB02GD;AwBt2GD;EACE,eAAA;EACA,kBAAA;EACA,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,oBAAA;CxBw2GD;AwBp2GD;EACE,gBAAA;EACA,QAAA;EACA,SAAA;EACA,UAAA;EACA,OAAA;EACA,aAAA;CxBs2GD;AwBl2GD;EACE,SAAA;EACA,WAAA;CxBo2GD;AwB51GD;;EAII,cAAA;EACA,0BAAA;EACA,4BAAA;EACA,YAAA;CxB41GH;AwBn2GD;;EAWI,UAAA;EACA,aAAA;EACA,mBAAA;CxB41GH;AwBv0GD;EAXE;IApEA,WAAA;IACA,SAAA;GxB05GC;EwBv1GD;IA1DA,QAAA;IACA,YAAA;GxBo5GC;CACF;A2BpiHD;;EAEE,mBAAA;EACA,sBAAA;EACA,uBAAA;C3BsiHD;A2B1iHD;;EAMI,mBAAA;EACA,YAAA;C3BwiHH;A2BtiHG;;;;;;;;EAIE,WAAA;C3B4iHL;A2BtiHD;;;;EAKI,kBAAA;C3BuiHH;A2BliHD;EACE,kBAAA;C3BoiHD;A2BriHD;;;EAOI,YAAA;C3BmiHH;A2B1iHD;;;EAYI,iBAAA;C3BmiHH;A2B/hHD;EACE,iBAAA;C3BiiHD;A2B7hHD;EACE,eAAA;C3B+hHD;A2B9hHC;EClDA,8BAAA;EACG,2BAAA;C5BmlHJ;A2B7hHD;;EC/CE,6BAAA;EACG,0BAAA;C5BglHJ;A2B5hHD;EACE,YAAA;C3B8hHD;A2B5hHD;EACE,iBAAA;C3B8hHD;A2B5hHD;;ECnEE,8BAAA;EACG,2BAAA;C5BmmHJ;A2B3hHD;ECjEE,6BAAA;EACG,0BAAA;C5B+lHJ;A2B1hHD;;EAEE,WAAA;C3B4hHD;A2B3gHD;EACE,kBAAA;EACA,mBAAA;C3B6gHD;A2B3gHD;EACE,mBAAA;EACA,oBAAA;C3B6gHD;A2BxgHD;EtB/CE,yDAAA;EACQ,iDAAA;CL0jHT;A2BxgHC;EtBnDA,yBAAA;EACQ,iBAAA;CL8jHT;A2BrgHD;EACE,eAAA;C3BugHD;A2BpgHD;EACE,wBAAA;EACA,uBAAA;C3BsgHD;A2BngHD;EACE,wBAAA;C3BqgHD;A2B9/GD;;;EAII,eAAA;EACA,YAAA;EACA,YAAA;EACA,gBAAA;C3B+/GH;A2BtgHD;EAcM,YAAA;C3B2/GL;A2BzgHD;;;;EAsBI,iBAAA;EACA,eAAA;C3By/GH;A2Bp/GC;EACE,iBAAA;C3Bs/GH;A2Bp/GC;EC3KA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5B4pHF;A2Bt/GC;EC/KA,2BAAA;EACC,0BAAA;EAOD,gCAAA;EACC,+BAAA;C5BkqHF;A2Bv/GD;EACE,iBAAA;C3By/GD;A2Bv/GD;;EC/KE,8BAAA;EACC,6BAAA;C5B0qHF;A2Bt/GD;EC7LE,2BAAA;EACC,0BAAA;C5BsrHF;A2Bl/GD;EACE,eAAA;EACA,YAAA;EACA,oBAAA;EACA,0BAAA;C3Bo/GD;A2Bx/GD;;EAOI,YAAA;EACA,oBAAA;EACA,UAAA;C3Bq/GH;A2B9/GD;EAYI,YAAA;C3Bq/GH;A2BjgHD;EAgBI,WAAA;C3Bo/GH;A2Bn+GD;;;;EAKM,mBAAA;EACA,uBAAA;EACA,qBAAA;C3Bo+GL;A6B9sHD;EACE,mBAAA;EACA,eAAA;EACA,0BAAA;C7BgtHD;A6B7sHC;EACE,YAAA;EACA,gBAAA;EACA,iBAAA;C7B+sHH;A6BxtHD;EAeI,mBAAA;EACA,WAAA;EAKA,YAAA;EAEA,YAAA;EACA,iBAAA;C7BusHH;A6BrsHG;EACE,WAAA;C7BusHL;A6B7rHD;;;EV0BE,aAAA;EACA,mBAAA;EACA,gBAAA;EACA,uBAAA;EACA,mBAAA;CnBwqHD;AmBtqHC;;;EACE,aAAA;EACA,kBAAA;CnB0qHH;AmBvqHC;;;;;;EAEE,aAAA;CnB6qHH;A6B/sHD;;;EVqBE,aAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;CnB+rHD;AmB7rHC;;;EACE,aAAA;EACA,kBAAA;CnBisHH;AmB9rHC;;;;;;EAEE,aAAA;CnBosHH;A6B7tHD;;;EAGE,oBAAA;C7B+tHD;A6B7tHC;;;EACE,iBAAA;C7BiuHH;A6B7tHD;;EAEE,UAAA;EACA,oBAAA;EACA,uBAAA;C7B+tHD;A6B1tHD;EACE,kBAAA;EACA,gBAAA;EACA,oBAAA;EACA,eAAA;EACA,eAAA;EACA,mBAAA;EACA,0BAAA;EACA,uBAAA;EACA,mBAAA;C7B4tHD;A6BztHC;EACE,kBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6BztHC;EACE,mBAAA;EACA,gBAAA;EACA,mBAAA;C7B2tHH;A6B/uHD;;EA0BI,cAAA;C7BytHH;A6BptHD;;;;;;;EDpGE,8BAAA;EACG,2BAAA;C5Bi0HJ;A6BrtHD;EACE,gBAAA;C7ButHD;A6BrtHD;;;;;;;EDxGE,6BAAA;EACG,0BAAA;C5Bs0HJ;A6BttHD;EACE,eAAA;C7BwtHD;A6BntHD;EACE,mBAAA;EAGA,aAAA;EACA,oBAAA;C7BmtHD;A6BxtHD;EAUI,mBAAA;C7BitHH;A6B3tHD;EAYM,kBAAA;C7BktHL;A6B/sHG;;;EAGE,WAAA;C7BitHL;A6B5sHC;;EAGI,mBAAA;C7B6sHL;A6B1sHC;;EAGI,WAAA;EACA,kBAAA;C7B2sHL;A8B12HD;EACE,iBAAA;EACA,gBAAA;EACA,iBAAA;C9B42HD;A8B/2HD;EAOI,mBAAA;EACA,eAAA;C9B22HH;A8Bn3HD;EAWM,mBAAA;EACA,eAAA;EACA,mBAAA;C9B22HL;A8B12HK;;EAEE,sBAAA;EACA,0BAAA;C9B42HP;A8Bv2HG;EACE,eAAA;C9By2HL;A8Bv2HK;;EAEE,eAAA;EACA,sBAAA;EACA,8BAAA;EACA,oBAAA;C9By2HP;A8Bl2HG;;;EAGE,0BAAA;EACA,sBAAA;C9Bo2HL;A8B74HD;ELHE,YAAA;EACA,cAAA;EACA,iBAAA;EACA,0BAAA;CzBm5HD;A8Bn5HD;EA0DI,gBAAA;C9B41HH;A8Bn1HD;EACE,8BAAA;C9Bq1HD;A8Bt1HD;EAGI,YAAA;EAEA,oBAAA;C9Bq1HH;A8B11HD;EASM,kBAAA;EACA,wBAAA;EACA,8BAAA;EACA,2BAAA;C9Bo1HL;A8Bn1HK;EACE,mCAAA;C9Bq1HP;A8B/0HK;;;EAGE,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,iCAAA;EACA,gBAAA;C9Bi1HP;A8B50HC;EAqDA,YAAA;EA8BA,iBAAA;C9B6vHD;A8Bh1HC;EAwDE,YAAA;C9B2xHH;A8Bn1HC;EA0DI,mBAAA;EACA,mBAAA;C9B4xHL;A8Bv1HC;EAgEE,UAAA;EACA,WAAA;C9B0xHH;A8B9wHD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9ByxHH;E8BztHH;IA9DQ,iBAAA;G9B0xHL;CACF;A8Bp2HC;EAuFE,gBAAA;EACA,mBAAA;C9BgxHH;A8Bx2HC;;;EA8FE,uBAAA;C9B+wHH;A8BjwHD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9B8wHH;E8B3uHH;;;IA9BM,0BAAA;G9B8wHH;CACF;A8B/2HD;EAEI,YAAA;C9Bg3HH;A8Bl3HD;EAMM,mBAAA;C9B+2HL;A8Br3HD;EASM,iBAAA;C9B+2HL;A8B12HK;;;EAGE,YAAA;EACA,0BAAA;C9B42HP;A8Bp2HD;EAEI,YAAA;C9Bq2HH;A8Bv2HD;EAIM,gBAAA;EACA,eAAA;C9Bs2HL;A8B11HD;EACE,YAAA;C9B41HD;A8B71HD;EAII,YAAA;C9B41HH;A8Bh2HD;EAMM,mBAAA;EACA,mBAAA;C9B61HL;A8Bp2HD;EAYI,UAAA;EACA,WAAA;C9B21HH;A8B/0HD;EA0DA;IAjEM,oBAAA;IACA,UAAA;G9B01HH;E8B1xHH;IA9DQ,iBAAA;G9B21HL;CACF;A8Bn1HD;EACE,iBAAA;C9Bq1HD;A8Bt1HD;EAKI,gBAAA;EACA,mBAAA;C9Bo1HH;A8B11HD;;;EAYI,uBAAA;C9Bm1HH;A8Br0HD;EA2BA;IApCM,8BAAA;IACA,2BAAA;G9Bk1HH;E8B/yHH;;;IA9BM,0BAAA;G9Bk1HH;CACF;A8Bz0HD;EAEI,cAAA;C9B00HH;A8B50HD;EAKI,eAAA;C9B00HH;A8Bj0HD;EAEE,iBAAA;EF3OA,2BAAA;EACC,0BAAA;C5B8iIF;A+BxiID;EACE,mBAAA;EACA,iBAAA;EACA,oBAAA;EACA,8BAAA;C/B0iID;A+BliID;EA8nBA;IAhoBI,mBAAA;G/BwiID;CACF;A+BzhID;EAgnBA;IAlnBI,YAAA;G/B+hID;CACF;A+BjhID;EACE,oBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,2DAAA;UAAA,mDAAA;EAEA,kCAAA;C/BkhID;A+BhhIC;EACE,iBAAA;C/BkhIH;A+Bt/HD;EA6jBA;IArlBI,YAAA;IACA,cAAA;IACA,yBAAA;YAAA,iBAAA;G/BkhID;E+BhhIC;IACE,0BAAA;IACA,wBAAA;IACA,kBAAA;IACA,6BAAA;G/BkhIH;E+B/gIC;IACE,oBAAA;G/BihIH;E+B5gIC;;;IAGE,gBAAA;IACA,iBAAA;G/B8gIH;CACF;A+B1gID;;EAGI,kBAAA;C/B2gIH;A+BtgIC;EAmjBF;;IArjBM,kBAAA;G/B6gIH;CACF;A+BpgID;;;;EAII,oBAAA;EACA,mBAAA;C/BsgIH;A+BhgIC;EAgiBF;;;;IAniBM,gBAAA;IACA,eAAA;G/B0gIH;CACF;A+B9/HD;EACE,cAAA;EACA,sBAAA;C/BggID;A+B3/HD;EA8gBA;IAhhBI,iBAAA;G/BigID;CACF;A+B7/HD;;EAEE,gBAAA;EACA,SAAA;EACA,QAAA;EACA,cAAA;C/B+/HD;A+Bz/HD;EAggBA;;IAlgBI,iBAAA;G/BggID;CACF;A+B9/HD;EACE,OAAA;EACA,sBAAA;C/BggID;A+B9/HD;EACE,UAAA;EACA,iBAAA;EACA,sBAAA;C/BggID;A+B1/HD;EACE,YAAA;EACA,mBAAA;EACA,gBAAA;EACA,kBAAA;EACA,aAAA;C/B4/HD;A+B1/HC;;EAEE,sBAAA;C/B4/HH;A+BrgID;EAaI,eAAA;C/B2/HH;A+Bl/HD;EALI;;IAEE,mBAAA;G/B0/HH;CACF;A+Bh/HD;EACE,mBAAA;EACA,aAAA;EACA,mBAAA;EACA,kBAAA;EC9LA,gBAAA;EACA,mBAAA;ED+LA,8BAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;C/Bm/HD;A+B/+HC;EACE,WAAA;C/Bi/HH;A+B//HD;EAmBI,eAAA;EACA,YAAA;EACA,YAAA;EACA,mBAAA;C/B++HH;A+BrgID;EAyBI,gBAAA;C/B++HH;A+Bz+HD;EAqbA;IAvbI,cAAA;G/B++HD;CACF;A+Bt+HD;EACE,oBAAA;C/Bw+HD;A+Bz+HD;EAII,kBAAA;EACA,qBAAA;EACA,kBAAA;C/Bw+HH;A+B58HC;EA2YF;IAjaM,iBAAA;IACA,YAAA;IACA,YAAA;IACA,cAAA;IACA,8BAAA;IACA,UAAA;IACA,yBAAA;YAAA,iBAAA;G/Bs+HH;E+B3kHH;;IAxZQ,2BAAA;G/Bu+HL;E+B/kHH;IArZQ,kBAAA;G/Bu+HL;E+Bt+HK;;IAEE,uBAAA;G/Bw+HP;CACF;A+Bt9HD;EA+XA;IA1YI,YAAA;IACA,UAAA;G/Bq+HD;E+B5lHH;IAtYM,YAAA;G/Bq+HH;E+B/lHH;IApYQ,kBAAA;IACA,qBAAA;G/Bs+HL;CACF;A+B39HD;EACE,mBAAA;EACA,oBAAA;EACA,mBAAA;EACA,kCAAA;EACA,qCAAA;E1B9NA,6FAAA;EACQ,qFAAA;E2B/DR,gBAAA;EACA,mBAAA;ChC4vID;AkBtuHD;EAwEA;IAtIM,sBAAA;IACA,iBAAA;IACA,uBAAA;GlBwyHH;EkBpqHH;IA/HM,sBAAA;IACA,YAAA;IACA,uBAAA;GlBsyHH;EkBzqHH;IAxHM,sBAAA;GlBoyHH;EkB5qHH;IApHM,sBAAA;IACA,uBAAA;GlBmyHH;EkBhrHH;;;IA9GQ,YAAA;GlBmyHL;EkBrrHH;IAxGM,YAAA;GlBgyHH;EkBxrHH;IApGM,iBAAA;IACA,uBAAA;GlB+xHH;EkB5rHH;;IA5FM,sBAAA;IACA,cAAA;IACA,iBAAA;IACA,uBAAA;GlB4xHH;EkBnsHH;;IAtFQ,gBAAA;GlB6xHL;EkBvsHH;;IAjFM,mBAAA;IACA,eAAA;GlB4xHH;EkB5sHH;IA3EM,OAAA;GlB0xHH;CACF;A+BpgIC;EAmWF;IAzWM,mBAAA;G/B8gIH;E+B5gIG;IACE,iBAAA;G/B8gIL;CACF;A+B7/HD;EAoVA;IA5VI,YAAA;IACA,UAAA;IACA,eAAA;IACA,gBAAA;IACA,eAAA;IACA,kBAAA;I1BzPF,yBAAA;IACQ,iBAAA;GLmwIP;CACF;A+BngID;EACE,cAAA;EHpUA,2BAAA;EACC,0BAAA;C5B00IF;A+BngID;EACE,iBAAA;EHzUA,6BAAA;EACC,4BAAA;EAOD,8BAAA;EACC,6BAAA;C5By0IF;A+B//HD;EChVE,gBAAA;EACA,mBAAA;ChCk1ID;A+BhgIC;ECnVA,iBAAA;EACA,oBAAA;ChCs1ID;A+BjgIC;ECtVA,iBAAA;EACA,oBAAA;ChC01ID;A+B3/HD;EChWE,iBAAA;EACA,oBAAA;ChC81ID;A+Bv/HD;EAsSA;IA1SI,YAAA;IACA,kBAAA;IACA,mBAAA;G/B+/HD;CACF;A+Bl+HD;EAhBE;IExWA,uBAAA;GjC81IC;E+Br/HD;IE5WA,wBAAA;IF8WE,oBAAA;G/Bu/HD;E+Bz/HD;IAKI,gBAAA;G/Bu/HH;CACF;A+B9+HD;EACE,0BAAA;EACA,sBAAA;C/Bg/HD;A+Bl/HD;EAKI,YAAA;C/Bg/HH;A+B/+HG;;EAEE,eAAA;EACA,8BAAA;C/Bi/HL;A+B1/HD;EAcI,YAAA;C/B++HH;A+B7/HD;EAmBM,YAAA;C/B6+HL;A+B3+HK;;EAEE,YAAA;EACA,8BAAA;C/B6+HP;A+Bz+HK;;;EAGE,YAAA;EACA,0BAAA;C/B2+HP;A+Bv+HK;;;EAGE,YAAA;EACA,8BAAA;C/By+HP;A+BjhID;EA8CI,mBAAA;C/Bs+HH;A+Br+HG;;EAEE,uBAAA;C/Bu+HL;A+BxhID;EAoDM,uBAAA;C/Bu+HL;A+B3hID;;EA0DI,sBAAA;C/Bq+HH;A+B99HK;;;EAGE,0BAAA;EACA,YAAA;C/Bg+HP;A+B/7HC;EAoKF;IA7LU,YAAA;G/B49HP;E+B39HO;;IAEE,YAAA;IACA,8BAAA;G/B69HT;E+Bz9HO;;;IAGE,YAAA;IACA,0BAAA;G/B29HT;E+Bv9HO;;;IAGE,YAAA;IACA,8BAAA;G/By9HT;CACF;A+B3jID;EA8GI,YAAA;C/Bg9HH;A+B/8HG;EACE,YAAA;C/Bi9HL;A+BjkID;EAqHI,YAAA;C/B+8HH;A+B98HG;;EAEE,YAAA;C/Bg9HL;A+B58HK;;;;EAEE,YAAA;C/Bg9HP;A+Bx8HD;EACE,uBAAA;EACA,sBAAA;C/B08HD;A+B58HD;EAKI,eAAA;C/B08HH;A+Bz8HG;;EAEE,YAAA;EACA,8BAAA;C/B28HL;A+Bp9HD;EAcI,eAAA;C/By8HH;A+Bv9HD;EAmBM,eAAA;C/Bu8HL;A+Br8HK;;EAEE,YAAA;EACA,8BAAA;C/Bu8HP;A+Bn8HK;;;EAGE,YAAA;EACA,0BAAA;C/Bq8HP;A+Bj8HK;;;EAGE,YAAA;EACA,8BAAA;C/Bm8HP;A+B3+HD;EA+CI,mBAAA;C/B+7HH;A+B97HG;;EAEE,uBAAA;C/Bg8HL;A+Bl/HD;EAqDM,uBAAA;C/Bg8HL;A+Br/HD;;EA2DI,sBAAA;C/B87HH;A+Bx7HK;;;EAGE,0BAAA;EACA,YAAA;C/B07HP;A+Bn5HC;EAwBF;IAvDU,sBAAA;G/Bs7HP;E+B/3HH;IApDU,0BAAA;G/Bs7HP;E+Bl4HH;IAjDU,eAAA;G/Bs7HP;E+Br7HO;;IAEE,YAAA;IACA,8BAAA;G/Bu7HT;E+Bn7HO;;;IAGE,YAAA;IACA,0BAAA;G/Bq7HT;E+Bj7HO;;;IAGE,YAAA;IACA,8BAAA;G/Bm7HT;CACF;A+B3hID;EA+GI,eAAA;C/B+6HH;A+B96HG;EACE,YAAA;C/Bg7HL;A+BjiID;EAsHI,eAAA;C/B86HH;A+B76HG;;EAEE,YAAA;C/B+6HL;A+B36HK;;;;EAEE,YAAA;C/B+6HP;AkCzjJD;EACE,kBAAA;EACA,oBAAA;EACA,iBAAA;EACA,0BAAA;EACA,mBAAA;ClC2jJD;AkChkJD;EAQI,sBAAA;ClC2jJH;AkCnkJD;EAWM,kBAAA;EACA,eAAA;EACA,YAAA;ClC2jJL;AkCxkJD;EAkBI,eAAA;ClCyjJH;AmC7kJD;EACE,sBAAA;EACA,gBAAA;EACA,eAAA;EACA,mBAAA;CnC+kJD;AmCnlJD;EAOI,gBAAA;CnC+kJH;AmCtlJD;;EAUM,mBAAA;EACA,YAAA;EACA,kBAAA;EACA,wBAAA;EACA,sBAAA;EACA,eAAA;EACA,uBAAA;EACA,uBAAA;EACA,kBAAA;CnCglJL;AmC9kJG;;EAGI,eAAA;EPXN,+BAAA;EACG,4BAAA;C5B2lJJ;AmC7kJG;;EPvBF,gCAAA;EACG,6BAAA;C5BwmJJ;AmCxkJG;;;;EAEE,WAAA;EACA,eAAA;EACA,0BAAA;EACA,mBAAA;CnC4kJL;AmCtkJG;;;;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;EACA,gBAAA;CnC2kJL;AmCloJD;;;;;;EAkEM,eAAA;EACA,uBAAA;EACA,mBAAA;EACA,oBAAA;CnCwkJL;AmC/jJD;;EC3EM,mBAAA;EACA,gBAAA;EACA,uBAAA;CpC8oJL;AoC5oJG;;ERKF,+BAAA;EACG,4BAAA;C5B2oJJ;AoC3oJG;;ERTF,gCAAA;EACG,6BAAA;C5BwpJJ;AmC1kJD;;EChFM,kBAAA;EACA,gBAAA;EACA,iBAAA;CpC8pJL;AoC5pJG;;ERKF,+BAAA;EACG,4BAAA;C5B2pJJ;AoC3pJG;;ERTF,gCAAA;EACG,6BAAA;C5BwqJJ;AqC3qJD;EACE,gBAAA;EACA,eAAA;EACA,iBAAA;EACA,mBAAA;CrC6qJD;AqCjrJD;EAOI,gBAAA;CrC6qJH;AqCprJD;;EAUM,sBAAA;EACA,kBAAA;EACA,uBAAA;EACA,uBAAA;EACA,oBAAA;CrC8qJL;AqC5rJD;;EAmBM,sBAAA;EACA,0BAAA;CrC6qJL;AqCjsJD;;EA2BM,aAAA;CrC0qJL;AqCrsJD;;EAkCM,YAAA;CrCuqJL;AqCzsJD;;;;EA2CM,eAAA;EACA,uBAAA;EACA,oBAAA;CrCoqJL;AsCltJD;EACE,gBAAA;EACA,wBAAA;EACA,eAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,mBAAA;EACA,oBAAA;EACA,yBAAA;EACA,qBAAA;CtCotJD;AsChtJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CtCktJL;AsC7sJC;EACE,cAAA;CtC+sJH;AsC3sJC;EACE,mBAAA;EACA,UAAA;CtC6sJH;AsCtsJD;ECtCE,0BAAA;CvC+uJD;AuC5uJG;;EAEE,0BAAA;CvC8uJL;AsCzsJD;EC1CE,0BAAA;CvCsvJD;AuCnvJG;;EAEE,0BAAA;CvCqvJL;AsC5sJD;EC9CE,0BAAA;CvC6vJD;AuC1vJG;;EAEE,0BAAA;CvC4vJL;AsC/sJD;EClDE,0BAAA;CvCowJD;AuCjwJG;;EAEE,0BAAA;CvCmwJL;AsCltJD;ECtDE,0BAAA;CvC2wJD;AuCxwJG;;EAEE,0BAAA;CvC0wJL;AsCrtJD;EC1DE,0BAAA;CvCkxJD;AuC/wJG;;EAEE,0BAAA;CvCixJL;AwCnxJD;EACE,sBAAA;EACA,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;EACA,uBAAA;EACA,oBAAA;EACA,mBAAA;EACA,0BAAA;EACA,oBAAA;CxCqxJD;AwClxJC;EACE,cAAA;CxCoxJH;AwChxJC;EACE,mBAAA;EACA,UAAA;CxCkxJH;AwC/wJC;;EAEE,OAAA;EACA,iBAAA;CxCixJH;AwC5wJG;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;CxC8wJL;AwCzwJC;;EAEE,eAAA;EACA,uBAAA;CxC2wJH;AwCxwJC;EACE,aAAA;CxC0wJH;AwCvwJC;EACE,kBAAA;CxCywJH;AwCtwJC;EACE,iBAAA;CxCwwJH;AyCl0JD;EACE,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,eAAA;EACA,0BAAA;CzCo0JD;AyCz0JD;;EASI,eAAA;CzCo0JH;AyC70JD;EAaI,oBAAA;EACA,gBAAA;EACA,iBAAA;CzCm0JH;AyCl1JD;EAmBI,0BAAA;CzCk0JH;AyC/zJC;;EAEE,mBAAA;EACA,mBAAA;EACA,oBAAA;CzCi0JH;AyC31JD;EA8BI,gBAAA;CzCg0JH;AyC9yJD;EACA;IAfI,kBAAA;IACA,qBAAA;GzCg0JD;EyC9zJC;;IAEE,mBAAA;IACA,oBAAA;GzCg0JH;EyCvzJH;;IAJM,gBAAA;GzC+zJH;CACF;A0C52JD;EACE,eAAA;EACA,aAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;EACA,uBAAA;EACA,mBAAA;ErCiLA,4CAAA;EACK,uCAAA;EACG,oCAAA;CL8rJT;A0Cx3JD;;EAaI,kBAAA;EACA,mBAAA;C1C+2JH;A0C32JC;;;EAGE,sBAAA;C1C62JH;A0Cl4JD;EA0BI,aAAA;EACA,eAAA;C1C22JH;A2Cp4JD;EACE,cAAA;EACA,oBAAA;EACA,8BAAA;EACA,mBAAA;C3Cs4JD;A2C14JD;EAQI,cAAA;EAEA,eAAA;C3Co4JH;A2C94JD;EAeI,kBAAA;C3Ck4JH;A2Cj5JD;;EAqBI,iBAAA;C3Cg4JH;A2Cr5JD;EAyBI,gBAAA;C3C+3JH;A2Cv3JD;;EAEE,oBAAA;C3Cy3JD;A2C33JD;;EAMI,mBAAA;EACA,UAAA;EACA,aAAA;EACA,eAAA;C3Cy3JH;A2Cj3JD;ECvDE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C26JD;A2Ct3JD;EClDI,0BAAA;C5C26JH;A2Cz3JD;EC/CI,eAAA;C5C26JH;A2Cx3JD;EC3DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Cs7JD;A2C73JD;ECtDI,0BAAA;C5Cs7JH;A2Ch4JD;ECnDI,eAAA;C5Cs7JH;A2C/3JD;EC/DE,0BAAA;EACA,sBAAA;EACA,eAAA;C5Ci8JD;A2Cp4JD;EC1DI,0BAAA;C5Ci8JH;A2Cv4JD;ECvDI,eAAA;C5Ci8JH;A2Ct4JD;ECnEE,0BAAA;EACA,sBAAA;EACA,eAAA;C5C48JD;A2C34JD;EC9DI,0BAAA;C5C48JH;A2C94JD;EC3DI,eAAA;C5C48JH;A6C98JD;EACE;IAAQ,4BAAA;G7Ci9JP;E6Ch9JD;IAAQ,yBAAA;G7Cm9JP;CACF;A6Ch9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6Cx9JD;EACE;IAAQ,4BAAA;G7Cm9JP;E6Cl9JD;IAAQ,yBAAA;G7Cq9JP;CACF;A6C98JD;EACE,iBAAA;EACA,aAAA;EACA,oBAAA;EACA,0BAAA;EACA,mBAAA;ExCsCA,uDAAA;EACQ,+CAAA;CL26JT;A6C78JD;EACE,YAAA;EACA,UAAA;EACA,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,YAAA;EACA,mBAAA;EACA,0BAAA;ExCyBA,uDAAA;EACQ,+CAAA;EAyHR,oCAAA;EACK,+BAAA;EACG,4BAAA;CL+zJT;A6C18JD;;ECCI,8MAAA;EACA,yMAAA;EACA,sMAAA;EDAF,mCAAA;UAAA,2BAAA;C7C88JD;A6Cv8JD;;ExC5CE,2DAAA;EACK,sDAAA;EACG,mDAAA;CLu/JT;A6Cp8JD;EErEE,0BAAA;C/C4gKD;A+CzgKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C49JH;A6Cx8JD;EEzEE,0BAAA;C/CohKD;A+CjhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co+JH;A6C58JD;EE7EE,0BAAA;C/C4hKD;A+CzhKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9C4+JH;A6Ch9JD;EEjFE,0BAAA;C/CoiKD;A+CjiKC;EDgDE,8MAAA;EACA,yMAAA;EACA,sMAAA;C9Co/JH;AgD5iKD;EAEE,iBAAA;ChD6iKD;AgD3iKC;EACE,cAAA;ChD6iKH;AgDziKD;;EAEE,QAAA;EACA,iBAAA;ChD2iKD;AgDxiKD;EACE,eAAA;ChD0iKD;AgDviKD;EACE,eAAA;ChDyiKD;AgDtiKC;EACE,gBAAA;ChDwiKH;AgDpiKD;;EAEE,mBAAA;ChDsiKD;AgDniKD;;EAEE,oBAAA;ChDqiKD;AgDliKD;;;EAGE,oBAAA;EACA,oBAAA;ChDoiKD;AgDjiKD;EACE,uBAAA;ChDmiKD;AgDhiKD;EACE,uBAAA;ChDkiKD;AgD9hKD;EACE,cAAA;EACA,mBAAA;ChDgiKD;AgD1hKD;EACE,gBAAA;EACA,iBAAA;ChD4hKD;AiDnlKD;EAEE,oBAAA;EACA,gBAAA;CjDolKD;AiD5kKD;EACE,mBAAA;EACA,eAAA;EACA,mBAAA;EAEA,oBAAA;EACA,uBAAA;EACA,uBAAA;CjD6kKD;AiD1kKC;ErB3BA,6BAAA;EACC,4BAAA;C5BwmKF;AiD3kKC;EACE,iBAAA;ErBvBF,gCAAA;EACC,+BAAA;C5BqmKF;AiDpkKD;;EAEE,YAAA;CjDskKD;AiDxkKD;;EAKI,YAAA;CjDukKH;AiDnkKC;;;;EAEE,sBAAA;EACA,YAAA;EACA,0BAAA;CjDukKH;AiDnkKD;EACE,YAAA;EACA,iBAAA;CjDqkKD;AiDhkKC;;;EAGE,0BAAA;EACA,eAAA;EACA,oBAAA;CjDkkKH;AiDvkKC;;;EASI,eAAA;CjDmkKL;AiD5kKC;;;EAYI,eAAA;CjDqkKL;AiDhkKC;;;EAGE,WAAA;EACA,YAAA;EACA,0BAAA;EACA,sBAAA;CjDkkKH;AiDxkKC;;;;;;;;;EAYI,eAAA;CjDukKL;AiDnlKC;;;EAeI,eAAA;CjDykKL;AkD3qKC;EACE,eAAA;EACA,0BAAA;ClD6qKH;AkD3qKG;;EAEE,eAAA;ClD6qKL;AkD/qKG;;EAKI,eAAA;ClD8qKP;AkD3qKK;;;;EAEE,eAAA;EACA,0BAAA;ClD+qKP;AkD7qKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDkrKP;AkDxsKC;EACE,eAAA;EACA,0BAAA;ClD0sKH;AkDxsKG;;EAEE,eAAA;ClD0sKL;AkD5sKG;;EAKI,eAAA;ClD2sKP;AkDxsKK;;;;EAEE,eAAA;EACA,0BAAA;ClD4sKP;AkD1sKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD+sKP;AkDruKC;EACE,eAAA;EACA,0BAAA;ClDuuKH;AkDruKG;;EAEE,eAAA;ClDuuKL;AkDzuKG;;EAKI,eAAA;ClDwuKP;AkDruKK;;;;EAEE,eAAA;EACA,0BAAA;ClDyuKP;AkDvuKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClD4uKP;AkDlwKC;EACE,eAAA;EACA,0BAAA;ClDowKH;AkDlwKG;;EAEE,eAAA;ClDowKL;AkDtwKG;;EAKI,eAAA;ClDqwKP;AkDlwKK;;;;EAEE,eAAA;EACA,0BAAA;ClDswKP;AkDpwKK;;;;;;EAGE,YAAA;EACA,0BAAA;EACA,sBAAA;ClDywKP;AiDxqKD;EACE,cAAA;EACA,mBAAA;CjD0qKD;AiDxqKD;EACE,iBAAA;EACA,iBAAA;CjD0qKD;AmDpyKD;EACE,oBAAA;EACA,uBAAA;EACA,8BAAA;EACA,mBAAA;E9C0DA,kDAAA;EACQ,0CAAA;CL6uKT;AmDnyKD;EACE,cAAA;CnDqyKD;AmDhyKD;EACE,mBAAA;EACA,qCAAA;EvBpBA,6BAAA;EACC,4BAAA;C5BuzKF;AmDtyKD;EAMI,eAAA;CnDmyKH;AmD9xKD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,eAAA;CnDgyKD;AmDpyKD;;;;;EAWI,eAAA;CnDgyKH;AmD3xKD;EACE,mBAAA;EACA,0BAAA;EACA,2BAAA;EvBxCA,gCAAA;EACC,+BAAA;C5Bs0KF;AmDrxKD;;EAGI,iBAAA;CnDsxKH;AmDzxKD;;EAMM,oBAAA;EACA,iBAAA;CnDuxKL;AmDnxKG;;EAEI,cAAA;EvBvEN,6BAAA;EACC,4BAAA;C5B61KF;AmDjxKG;;EAEI,iBAAA;EvBvEN,gCAAA;EACC,+BAAA;C5B21KF;AmD1yKD;EvB1DE,2BAAA;EACC,0BAAA;C5Bu2KF;AmD7wKD;EAEI,oBAAA;CnD8wKH;AmD3wKD;EACE,oBAAA;CnD6wKD;AmDrwKD;;;EAII,iBAAA;CnDswKH;AmD1wKD;;;EAOM,mBAAA;EACA,oBAAA;CnDwwKL;AmDhxKD;;EvBzGE,6BAAA;EACC,4BAAA;C5B63KF;AmDrxKD;;;;EAmBQ,4BAAA;EACA,6BAAA;CnDwwKP;AmD5xKD;;;;;;;;EAwBU,4BAAA;CnD8wKT;AmDtyKD;;;;;;;;EA4BU,6BAAA;CnDoxKT;AmDhzKD;;EvBjGE,gCAAA;EACC,+BAAA;C5Bq5KF;AmDrzKD;;;;EAyCQ,+BAAA;EACA,gCAAA;CnDkxKP;AmD5zKD;;;;;;;;EA8CU,+BAAA;CnDwxKT;AmDt0KD;;;;;;;;EAkDU,gCAAA;CnD8xKT;AmDh1KD;;;;EA2DI,2BAAA;CnD2xKH;AmDt1KD;;EA+DI,cAAA;CnD2xKH;AmD11KD;;EAmEI,UAAA;CnD2xKH;AmD91KD;;;;;;;;;;;;EA0EU,eAAA;CnDkyKT;AmD52KD;;;;;;;;;;;;EA8EU,gBAAA;CnD4yKT;AmD13KD;;;;;;;;EAuFU,iBAAA;CnD6yKT;AmDp4KD;;;;;;;;EAgGU,iBAAA;CnD8yKT;AmD94KD;EAsGI,UAAA;EACA,iBAAA;CnD2yKH;AmDjyKD;EACE,oBAAA;CnDmyKD;AmDpyKD;EAKI,iBAAA;EACA,mBAAA;CnDkyKH;AmDxyKD;EASM,gBAAA;CnDkyKL;AmD3yKD;EAcI,iBAAA;CnDgyKH;AmD9yKD;;EAkBM,2BAAA;CnDgyKL;AmDlzKD;EAuBI,cAAA;CnD8xKH;AmDrzKD;EAyBM,8BAAA;CnD+xKL;AmDxxKD;EC1PE,mBAAA;CpDqhLD;AoDnhLC;EACE,eAAA;EACA,0BAAA;EACA,mBAAA;CpDqhLH;AoDxhLC;EAMI,uBAAA;CpDqhLL;AoD3hLC;EASI,eAAA;EACA,0BAAA;CpDqhLL;AoDlhLC;EAEI,0BAAA;CpDmhLL;AmDvyKD;EC7PE,sBAAA;CpDuiLD;AoDriLC;EACE,YAAA;EACA,0BAAA;EACA,sBAAA;CpDuiLH;AoD1iLC;EAMI,0BAAA;CpDuiLL;AoD7iLC;EASI,eAAA;EACA,uBAAA;CpDuiLL;AoDpiLC;EAEI,6BAAA;CpDqiLL;AmDtzKD;EChQE,sBAAA;CpDyjLD;AoDvjLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpDyjLH;AoD5jLC;EAMI,0BAAA;CpDyjLL;AoD/jLC;EASI,eAAA;EACA,0BAAA;CpDyjLL;AoDtjLC;EAEI,6BAAA;CpDujLL;AmDr0KD;ECnQE,sBAAA;CpD2kLD;AoDzkLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD2kLH;AoD9kLC;EAMI,0BAAA;CpD2kLL;AoDjlLC;EASI,eAAA;EACA,0BAAA;CpD2kLL;AoDxkLC;EAEI,6BAAA;CpDykLL;AmDp1KD;ECtQE,sBAAA;CpD6lLD;AoD3lLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD6lLH;AoDhmLC;EAMI,0BAAA;CpD6lLL;AoDnmLC;EASI,eAAA;EACA,0BAAA;CpD6lLL;AoD1lLC;EAEI,6BAAA;CpD2lLL;AmDn2KD;ECzQE,sBAAA;CpD+mLD;AoD7mLC;EACE,eAAA;EACA,0BAAA;EACA,sBAAA;CpD+mLH;AoDlnLC;EAMI,0BAAA;CpD+mLL;AoDrnLC;EASI,eAAA;EACA,0BAAA;CpD+mLL;AoD5mLC;EAEI,6BAAA;CpD6mLL;AqD7nLD;EACE,mBAAA;EACA,eAAA;EACA,UAAA;EACA,WAAA;EACA,iBAAA;CrD+nLD;AqDpoLD;;;;;EAYI,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,aAAA;EACA,YAAA;EACA,UAAA;CrD+nLH;AqD1nLD;EACE,uBAAA;CrD4nLD;AqDxnLD;EACE,oBAAA;CrD0nLD;AsDrpLD;EACE,iBAAA;EACA,cAAA;EACA,oBAAA;EACA,0BAAA;EACA,0BAAA;EACA,mBAAA;EjDwDA,wDAAA;EACQ,gDAAA;CLgmLT;AsD/pLD;EASI,mBAAA;EACA,kCAAA;CtDypLH;AsDppLD;EACE,cAAA;EACA,mBAAA;CtDspLD;AsDppLD;EACE,aAAA;EACA,mBAAA;CtDspLD;AuD5qLD;EACE,aAAA;EACA,gBAAA;EACA,kBAAA;EACA,eAAA;EACA,YAAA;EACA,0BAAA;EjCRA,aAAA;EAGA,0BAAA;CtBqrLD;AuD7qLC;;EAEE,YAAA;EACA,sBAAA;EACA,gBAAA;EjCfF,aAAA;EAGA,0BAAA;CtB6rLD;AuDzqLC;EACE,WAAA;EACA,gBAAA;EACA,wBAAA;EACA,UAAA;EACA,yBAAA;CvD2qLH;AwDhsLD;EACE,iBAAA;CxDksLD;AwD9rLD;EACE,cAAA;EACA,iBAAA;EACA,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,kCAAA;EAIA,WAAA;CxD6rLD;AwD1rLC;EnD+GA,sCAAA;EACI,kCAAA;EACC,iCAAA;EACG,8BAAA;EAkER,oDAAA;EAEK,0CAAA;EACG,oCAAA;CL6gLT;AwDhsLC;EnD2GA,mCAAA;EACI,+BAAA;EACC,8BAAA;EACG,2BAAA;CLwlLT;AwDpsLD;EACE,mBAAA;EACA,iBAAA;CxDssLD;AwDlsLD;EACE,mBAAA;EACA,YAAA;EACA,aAAA;CxDosLD;AwDhsLD;EACE,mBAAA;EACA,uBAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EnDaA,iDAAA;EACQ,yCAAA;EmDZR,qCAAA;UAAA,6BAAA;EAEA,WAAA;CxDksLD;AwD9rLD;EACE,gBAAA;EACA,OAAA;EACA,SAAA;EACA,UAAA;EACA,QAAA;EACA,cAAA;EACA,uBAAA;CxDgsLD;AwD9rLC;ElCrEA,WAAA;EAGA,yBAAA;CtBowLD;AwDjsLC;ElCtEA,aAAA;EAGA,0BAAA;CtBwwLD;AwDhsLD;EACE,cAAA;EACA,iCAAA;CxDksLD;AwD9rLD;EACE,iBAAA;CxDgsLD;AwD5rLD;EACE,UAAA;EACA,wBAAA;CxD8rLD;AwDzrLD;EACE,mBAAA;EACA,cAAA;CxD2rLD;AwDvrLD;EACE,cAAA;EACA,kBAAA;EACA,8BAAA;CxDyrLD;AwD5rLD;EAQI,iBAAA;EACA,iBAAA;CxDurLH;AwDhsLD;EAaI,kBAAA;CxDsrLH;AwDnsLD;EAiBI,eAAA;CxDqrLH;AwDhrLD;EACE,mBAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,iBAAA;CxDkrLD;AwDhqLD;EAZE;IACE,aAAA;IACA,kBAAA;GxD+qLD;EwD7qLD;InDvEA,kDAAA;IACQ,0CAAA;GLuvLP;EwD5qLD;IAAY,aAAA;GxD+qLX;CACF;AwD1qLD;EAFE;IAAY,aAAA;GxDgrLX;CACF;AyD/zLD;EACE,mBAAA;EACA,cAAA;EACA,eAAA;ECRA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;EDHA,gBAAA;EnCVA,WAAA;EAGA,yBAAA;CtBs1LD;AyD30LC;EnCdA,aAAA;EAGA,0BAAA;CtB01LD;AyD90LC;EAAW,iBAAA;EAAmB,eAAA;CzDk1L/B;AyDj1LC;EAAW,iBAAA;EAAmB,eAAA;CzDq1L/B;AyDp1LC;EAAW,gBAAA;EAAmB,eAAA;CzDw1L/B;AyDv1LC;EAAW,kBAAA;EAAmB,eAAA;CzD21L/B;AyDv1LD;EACE,iBAAA;EACA,iBAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,mBAAA;CzDy1LD;AyDr1LD;EACE,mBAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;CzDu1LD;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,WAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,UAAA;EACA,UAAA;EACA,oBAAA;EACA,wBAAA;EACA,uBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,QAAA;EACA,iBAAA;EACA,4BAAA;EACA,yBAAA;CzDq1LH;AyDn1LC;EACE,SAAA;EACA,SAAA;EACA,iBAAA;EACA,4BAAA;EACA,wBAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,kBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,WAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;AyDn1LC;EACE,OAAA;EACA,UAAA;EACA,iBAAA;EACA,wBAAA;EACA,0BAAA;CzDq1LH;A2Dl7LD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA;EACA,cAAA;EACA,iBAAA;EACA,aAAA;EDXA,4DAAA;EAEA,mBAAA;EACA,oBAAA;EACA,uBAAA;EACA,iBAAA;EACA,wBAAA;EACA,iBAAA;EACA,kBAAA;EACA,sBAAA;EACA,kBAAA;EACA,qBAAA;EACA,oBAAA;EACA,mBAAA;EACA,qBAAA;EACA,kBAAA;ECAA,gBAAA;EAEA,uBAAA;EACA,qCAAA;UAAA,6BAAA;EACA,uBAAA;EACA,qCAAA;EACA,mBAAA;EtD8CA,kDAAA;EACQ,0CAAA;CLk5LT;A2D77LC;EAAY,kBAAA;C3Dg8Lb;A2D/7LC;EAAY,kBAAA;C3Dk8Lb;A2Dj8LC;EAAY,iBAAA;C3Do8Lb;A2Dn8LC;EAAY,mBAAA;C3Ds8Lb;A2Dn8LD;EACE,UAAA;EACA,kBAAA;EACA,gBAAA;EACA,0BAAA;EACA,iCAAA;EACA,2BAAA;C3Dq8LD;A2Dl8LD;EACE,kBAAA;C3Do8LD;A2D57LC;;EAEE,mBAAA;EACA,eAAA;EACA,SAAA;EACA,UAAA;EACA,0BAAA;EACA,oBAAA;C3D87LH;A2D37LD;EACE,mBAAA;C3D67LD;A2D37LD;EACE,mBAAA;EACA,YAAA;C3D67LD;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,uBAAA;EACA,0BAAA;EACA,sCAAA;EACA,cAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,YAAA;EACA,mBAAA;EACA,uBAAA;EACA,uBAAA;C3D47LL;A2Dz7LC;EACE,SAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,4BAAA;EACA,wCAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,UAAA;EACA,cAAA;EACA,qBAAA;EACA,yBAAA;C3D47LL;A2Dz7LC;EACE,UAAA;EACA,mBAAA;EACA,oBAAA;EACA,6BAAA;EACA,yCAAA;EACA,WAAA;C3D27LH;A2D17LG;EACE,aAAA;EACA,SAAA;EACA,mBAAA;EACA,oBAAA;EACA,0BAAA;C3D47LL;A2Dx7LC;EACE,SAAA;EACA,aAAA;EACA,kBAAA;EACA,sBAAA;EACA,2BAAA;EACA,uCAAA;C3D07LH;A2Dz7LG;EACE,aAAA;EACA,WAAA;EACA,sBAAA;EACA,wBAAA;EACA,cAAA;C3D27LL;A4DpjMD;EACE,mBAAA;C5DsjMD;A4DnjMD;EACE,mBAAA;EACA,iBAAA;EACA,YAAA;C5DqjMD;A4DxjMD;EAMI,cAAA;EACA,mBAAA;EvD6KF,0CAAA;EACK,qCAAA;EACG,kCAAA;CLy4LT;A4D/jMD;;EAcM,eAAA;C5DqjML;A4D3hMC;EA4NF;IvD3DE,uDAAA;IAEK,6CAAA;IACG,uCAAA;IA7JR,oCAAA;IAEQ,4BAAA;IA+GR,4BAAA;IAEQ,oBAAA;GL86LP;E4DzjMG;;IvDmHJ,2CAAA;IACQ,mCAAA;IuDjHF,QAAA;G5D4jML;E4D1jMG;;IvD8GJ,4CAAA;IACQ,oCAAA;IuD5GF,QAAA;G5D6jML;E4D3jMG;;;IvDyGJ,wCAAA;IACQ,gCAAA;IuDtGF,QAAA;G5D8jML;CACF;A4DpmMD;;;EA6CI,eAAA;C5D4jMH;A4DzmMD;EAiDI,QAAA;C5D2jMH;A4D5mMD;;EAsDI,mBAAA;EACA,OAAA;EACA,YAAA;C5D0jMH;A4DlnMD;EA4DI,WAAA;C5DyjMH;A4DrnMD;EA+DI,YAAA;C5DyjMH;A4DxnMD;;EAmEI,QAAA;C5DyjMH;A4D5nMD;EAuEI,YAAA;C5DwjMH;A4D/nMD;EA0EI,WAAA;C5DwjMH;A4DhjMD;EACE,mBAAA;EACA,OAAA;EACA,QAAA;EACA,UAAA;EACA,WAAA;EtC9FA,aAAA;EAGA,0BAAA;EsC6FA,gBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;EACA,mCAAA;C5DmjMD;A4D9iMC;EdnGE,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9CopMH;A4DljMC;EACE,WAAA;EACA,SAAA;EdxGA,mGAAA;EACA,8FAAA;EACA,qHAAA;EAAA,+FAAA;EACA,4BAAA;EACA,uHAAA;C9C6pMH;A4DpjMC;;EAEE,WAAA;EACA,YAAA;EACA,sBAAA;EtCvHF,aAAA;EAGA,0BAAA;CtB4qMD;A4DtlMD;;;;EAuCI,mBAAA;EACA,SAAA;EACA,kBAAA;EACA,WAAA;EACA,sBAAA;C5DqjMH;A4DhmMD;;EA+CI,UAAA;EACA,mBAAA;C5DqjMH;A4DrmMD;;EAoDI,WAAA;EACA,oBAAA;C5DqjMH;A4D1mMD;;EAyDI,YAAA;EACA,aAAA;EACA,eAAA;EACA,mBAAA;C5DqjMH;A4DhjMG;EACE,iBAAA;C5DkjML;A4D9iMG;EACE,iBAAA;C5DgjML;A4DtiMD;EACE,mBAAA;EACA,aAAA;EACA,UAAA;EACA,YAAA;EACA,WAAA;EACA,kBAAA;EACA,gBAAA;EACA,iBAAA;EACA,mBAAA;C5DwiMD;A4DjjMD;EAYI,sBAAA;EACA,YAAA;EACA,aAAA;EACA,YAAA;EACA,oBAAA;EACA,uBAAA;EACA,oBAAA;EACA,gBAAA;EAWA,0BAAA;EACA,mCAAA;C5D8hMH;A4D7jMD;EAkCI,UAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;C5D8hMH;A4DvhMD;EACE,mBAAA;EACA,UAAA;EACA,WAAA;EACA,aAAA;EACA,YAAA;EACA,kBAAA;EACA,qBAAA;EACA,YAAA;EACA,mBAAA;EACA,0CAAA;C5DyhMD;A4DxhMC;EACE,kBAAA;C5D0hMH;A4Dj/LD;EAhCE;;;;IAKI,YAAA;IACA,aAAA;IACA,kBAAA;IACA,gBAAA;G5DmhMH;E4D3hMD;;IAYI,mBAAA;G5DmhMH;E4D/hMD;;IAgBI,oBAAA;G5DmhMH;E4D9gMD;IACE,UAAA;IACA,WAAA;IACA,qBAAA;G5DghMD;E4D5gMD;IACE,aAAA;G5D8gMD;CACF;A6D7wMC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEE,aAAA;EACA,eAAA;C7D6yMH;A6D3yMC;;;;;;;;;;;;;;;;EACE,YAAA;C7D4zMH;AiCp0MD;E6BRE,eAAA;EACA,kBAAA;EACA,mBAAA;C9D+0MD;AiCt0MD;EACE,wBAAA;CjCw0MD;AiCt0MD;EACE,uBAAA;CjCw0MD;AiCh0MD;EACE,yBAAA;CjCk0MD;AiCh0MD;EACE,0BAAA;CjCk0MD;AiCh0MD;EACE,mBAAA;CjCk0MD;AiCh0MD;E8BzBE,YAAA;EACA,mBAAA;EACA,kBAAA;EACA,8BAAA;EACA,UAAA;C/D41MD;AiC9zMD;EACE,yBAAA;CjCg0MD;AiCzzMD;EACE,gBAAA;CjC2zMD;AgE51MD;EACE,oBAAA;ChE81MD;AgEx1MD;;;;ECdE,yBAAA;CjE42MD;AgEv1MD;;;;;;;;;;;;EAYE,yBAAA;ChEy1MD;AgEl1MD;EA6IA;IC7LE,0BAAA;GjEs4MC;EiEr4MD;IAAU,0BAAA;GjEw4MT;EiEv4MD;IAAU,8BAAA;GjE04MT;EiEz4MD;;IACU,+BAAA;GjE44MT;CACF;AgE51MD;EAwIA;IA1II,0BAAA;GhEk2MD;CACF;AgE51MD;EAmIA;IArII,2BAAA;GhEk2MD;CACF;AgE51MD;EA8HA;IAhII,iCAAA;GhEk2MD;CACF;AgE31MD;EAwHA;IC7LE,0BAAA;GjEo6MC;EiEn6MD;IAAU,0BAAA;GjEs6MT;EiEr6MD;IAAU,8BAAA;GjEw6MT;EiEv6MD;;IACU,+BAAA;GjE06MT;CACF;AgEr2MD;EAmHA;IArHI,0BAAA;GhE22MD;CACF;AgEr2MD;EA8GA;IAhHI,2BAAA;GhE22MD;CACF;AgEr2MD;EAyGA;IA3GI,iCAAA;GhE22MD;CACF;AgEp2MD;EAmGA;IC7LE,0BAAA;GjEk8MC;EiEj8MD;IAAU,0BAAA;GjEo8MT;EiEn8MD;IAAU,8BAAA;GjEs8MT;EiEr8MD;;IACU,+BAAA;GjEw8MT;CACF;AgE92MD;EA8FA;IAhGI,0BAAA;GhEo3MD;CACF;AgE92MD;EAyFA;IA3FI,2BAAA;GhEo3MD;CACF;AgE92MD;EAoFA;IAtFI,iCAAA;GhEo3MD;CACF;AgE72MD;EA8EA;IC7LE,0BAAA;GjEg+MC;EiE/9MD;IAAU,0BAAA;GjEk+MT;EiEj+MD;IAAU,8BAAA;GjEo+MT;EiEn+MD;;IACU,+BAAA;GjEs+MT;CACF;AgEv3MD;EAyEA;IA3EI,0BAAA;GhE63MD;CACF;AgEv3MD;EAoEA;IAtEI,2BAAA;GhE63MD;CACF;AgEv3MD;EA+DA;IAjEI,iCAAA;GhE63MD;CACF;AgEt3MD;EAyDA;ICrLE,yBAAA;GjEs/MC;CACF;AgEt3MD;EAoDA;ICrLE,yBAAA;GjE2/MC;CACF;AgEt3MD;EA+CA;ICrLE,yBAAA;GjEggNC;CACF;AgEt3MD;EA0CA;ICrLE,yBAAA;GjEqgNC;CACF;AgEn3MD;ECnJE,yBAAA;CjEygND;AgEh3MD;EA4BA;IC7LE,0BAAA;GjEqhNC;EiEphND;IAAU,0BAAA;GjEuhNT;EiEthND;IAAU,8BAAA;GjEyhNT;EiExhND;;IACU,+BAAA;GjE2hNT;CACF;AgE93MD;EACE,yBAAA;ChEg4MD;AgE33MD;EAqBA;IAvBI,0BAAA;GhEi4MD;CACF;AgE/3MD;EACE,yBAAA;ChEi4MD;AgE53MD;EAcA;IAhBI,2BAAA;GhEk4MD;CACF;AgEh4MD;EACE,yBAAA;ChEk4MD;AgE73MD;EAOA;IATI,iCAAA;GhEm4MD;CACF;AgE53MD;EACA;ICrLE,yBAAA;GjEojNC;CACF","file":"bootstrap.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/*! 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: \"\\002a\";\n}\n.glyphicon-plus:before {\n content: \"\\002b\";\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: #fff;\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: 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: #fff;\n border: 1px solid #ddd;\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: #fff;\n background-color: #333;\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 #ccc;\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 #ddd;\n}\n.table > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid #ddd;\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 #ddd;\n}\n.table .table {\n background-color: #fff;\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 #ddd;\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 #ddd;\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 #ddd;\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: 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: #fff;\n background-image: none;\n border: 1px solid #ccc;\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: #999;\n opacity: 1;\n}\n.form-control:-ms-input-placeholder {\n color: #999;\n}\n.form-control::-webkit-input-placeholder {\n color: #999;\n}\n.form-control::-ms-expand {\n border: 0;\n background-color: transparent;\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: 11px;\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: 5px auto -webkit-focus-ring-color;\n outline-offset: -2px;\n}\n.btn:hover,\n.btn:focus,\n.btn.focus {\n color: #333;\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: #333;\n background-color: #fff;\n border-color: #ccc;\n}\n.btn-default:focus,\n.btn-default.focus {\n color: #333;\n background-color: #e6e6e6;\n border-color: #8c8c8c;\n}\n.btn-default:hover {\n color: #333;\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: #333;\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: #333;\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: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 background-color: #fff;\n border-color: #ccc;\n}\n.btn-default .badge {\n color: #fff;\n background-color: #333;\n}\n.btn-primary {\n color: #fff;\n background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary:focus,\n.btn-primary.focus {\n color: #fff;\n background-color: #286090;\n border-color: #122b40;\n}\n.btn-primary:hover {\n color: #fff;\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: #fff;\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: #fff;\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: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 background-color: #337ab7;\n border-color: #2e6da4;\n}\n.btn-primary .badge {\n color: #337ab7;\n background-color: #fff;\n}\n.btn-success {\n color: #fff;\n background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success:focus,\n.btn-success.focus {\n color: #fff;\n background-color: #449d44;\n border-color: #255625;\n}\n.btn-success:hover {\n color: #fff;\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: #fff;\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: #fff;\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: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 background-color: #5cb85c;\n border-color: #4cae4c;\n}\n.btn-success .badge {\n color: #5cb85c;\n background-color: #fff;\n}\n.btn-info {\n color: #fff;\n background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info:focus,\n.btn-info.focus {\n color: #fff;\n background-color: #31b0d5;\n border-color: #1b6d85;\n}\n.btn-info:hover {\n color: #fff;\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: #fff;\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: #fff;\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: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 background-color: #5bc0de;\n border-color: #46b8da;\n}\n.btn-info .badge {\n color: #5bc0de;\n background-color: #fff;\n}\n.btn-warning {\n color: #fff;\n background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning:focus,\n.btn-warning.focus {\n color: #fff;\n background-color: #ec971f;\n border-color: #985f0d;\n}\n.btn-warning:hover {\n color: #fff;\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: #fff;\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: #fff;\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: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 background-color: #f0ad4e;\n border-color: #eea236;\n}\n.btn-warning .badge {\n color: #f0ad4e;\n background-color: #fff;\n}\n.btn-danger {\n color: #fff;\n background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger:focus,\n.btn-danger.focus {\n color: #fff;\n background-color: #c9302c;\n border-color: #761c19;\n}\n.btn-danger:hover {\n color: #fff;\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: #fff;\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: #fff;\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: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 background-color: #d9534f;\n border-color: #d43f3a;\n}\n.btn-danger .badge {\n color: #d9534f;\n background-color: #fff;\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: #fff;\n border: 1px solid #ccc;\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: #fff;\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-top-left-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-top-right-radius: 0;\n border-top-left-radius: 0;\n border-bottom-right-radius: 4px;\n border-bottom-left-radius: 4px;\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 .form-control:focus {\n z-index: 3;\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 #ccc;\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 #ddd;\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 #ddd;\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: #fff;\n border: 1px solid #ddd;\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 #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs.nav-justified > li > a {\n border-bottom: 1px solid #ddd;\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: #fff;\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: #fff;\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 #ddd;\n}\n@media (min-width: 768px) {\n .nav-tabs-justified > li > a {\n border-bottom: 1px solid #ddd;\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: #fff;\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: #777;\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: #777;\n}\n.navbar-default .navbar-nav > li > a {\n color: #777;\n}\n.navbar-default .navbar-nav > li > a:hover,\n.navbar-default .navbar-nav > li > a:focus {\n color: #333;\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: #555;\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: #ccc;\n background-color: transparent;\n}\n.navbar-default .navbar-toggle {\n border-color: #ddd;\n}\n.navbar-default .navbar-toggle:hover,\n.navbar-default .navbar-toggle:focus {\n background-color: #ddd;\n}\n.navbar-default .navbar-toggle .icon-bar {\n background-color: #888;\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: #555;\n}\n@media (max-width: 767px) {\n .navbar-default .navbar-nav .open .dropdown-menu > li > a {\n color: #777;\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: #333;\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: #555;\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: #ccc;\n background-color: transparent;\n }\n}\n.navbar-default .navbar-link {\n color: #777;\n}\n.navbar-default .navbar-link:hover {\n color: #333;\n}\n.navbar-default .btn-link {\n color: #777;\n}\n.navbar-default .btn-link:hover,\n.navbar-default .btn-link:focus {\n color: #333;\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: #ccc;\n}\n.navbar-inverse {\n background-color: #222;\n border-color: #0272408;\n}\n.navbar-inverse .navbar-brand {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-brand:hover,\n.navbar-inverse .navbar-brand:focus {\n color: #fff;\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: #fff;\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: #fff;\n background-color: #0272408;\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: #444;\n background-color: transparent;\n}\n.navbar-inverse .navbar-toggle {\n border-color: #333;\n}\n.navbar-inverse .navbar-toggle:hover,\n.navbar-inverse .navbar-toggle:focus {\n background-color: #333;\n}\n.navbar-inverse .navbar-toggle .icon-bar {\n background-color: #fff;\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: #0272408;\n color: #fff;\n}\n@media (max-width: 767px) {\n .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\n border-color: #0272408;\n }\n .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\n background-color: #0272408;\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: #fff;\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: #fff;\n background-color: #0272408;\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: #444;\n background-color: transparent;\n }\n}\n.navbar-inverse .navbar-link {\n color: #9d9d9d;\n}\n.navbar-inverse .navbar-link:hover {\n color: #fff;\n}\n.navbar-inverse .btn-link {\n color: #9d9d9d;\n}\n.navbar-inverse .btn-link:hover,\n.navbar-inverse .btn-link:focus {\n color: #fff;\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: #444;\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: #ccc;\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: #fff;\n border: 1px solid #ddd;\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: 2;\n color: #23527c;\n background-color: #eeeeee;\n border-color: #ddd;\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: 3;\n color: #fff;\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: #fff;\n border-color: #ddd;\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: #fff;\n border: 1px solid #ddd;\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: #fff;\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: #fff;\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: #fff;\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: #fff;\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: #fff;\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: #fff;\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 padding-left: 15px;\n padding-right: 15px;\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: #fff;\n border: 1px solid #ddd;\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: #fff;\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: #fff;\n border: 1px solid #ddd;\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: #555;\n}\na.list-group-item .list-group-item-heading,\nbutton.list-group-item .list-group-item-heading {\n color: #333;\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: #555;\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: #fff;\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: #fff;\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 #ddd;\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 #ddd;\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 #ddd;\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 #ddd;\n}\n.panel-default {\n border-color: #ddd;\n}\n.panel-default > .panel-heading {\n color: #333333;\n background-color: #f5f5f5;\n border-color: #ddd;\n}\n.panel-default > .panel-heading + .panel-collapse > .panel-body {\n border-top-color: #ddd;\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: #ddd;\n}\n.panel-primary {\n border-color: #337ab7;\n}\n.panel-primary > .panel-heading {\n color: #fff;\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: #fff;\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: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: 0.2;\n filter: alpha(opacity=20);\n}\n.close:hover,\n.close:focus {\n color: #000;\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: #fff;\n border: 1px solid #999;\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: #000;\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}\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: #fff;\n text-align: center;\n background-color: #000;\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: #000;\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: #000;\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: #000;\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: #000;\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: #000;\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: #000;\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: #000;\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: #000;\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: #fff;\n background-clip: padding-box;\n border: 1px solid #ccc;\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: #fff;\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: #fff;\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: #fff;\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: #fff;\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: #fff;\n text-align: center;\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\n background-color: rgba(0, 0, 0, 0);\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: #fff;\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 #fff;\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: #fff;\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: #fff;\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: -10px;\n font-size: 30px;\n }\n .carousel-control .glyphicon-chevron-left,\n .carousel-control .icon-prev {\n margin-left: -10px;\n }\n .carousel-control .glyphicon-chevron-right,\n .carousel-control .icon-next {\n margin-right: -10px;\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-header:before,\n.modal-header: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-header: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: \"\\002a\"; } }\n.glyphicon-plus { &:before { content: \"\\002b\"; } }\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 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","// WebKit-style focus\n\n.tab-focus() {\n // WebKit-specific. Other browsers will keep their default outline style.\n // (Initially tried to also force default via `outline: initial`,\n // but that seems to erroneously remove the outline in Firefox altogether.)\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: @dl-horizontal-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: floor((@gutter / 2));\n padding-right: ceil((@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 // Unstyle the caret on ``\n// element gets special love because it's special, and that's a fact!\n.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n height: @input-height;\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n\n select& {\n height: @input-height;\n line-height: @input-height;\n }\n\n textarea&,\n select[multiple]& {\n height: auto;\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n touch-action: manipulation;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @btn-border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus,\n &.focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus,\n &.focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: @cursor-disabled;\n .opacity(.65);\n .box-shadow(none);\n }\n\n a& {\n &.disabled,\n fieldset[disabled] & {\n pointer-events: none; // Future-proof disabling of clicks on `` elements\n }\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n border-radius: 0;\n\n &,\n &:active,\n &.active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: @link-hover-decoration;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @btn-border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @btn-border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","// Button variants\n//\n// Easily pump out default styles, as well as :hover, :focus, :active,\n// and disabled options for all buttons\n\n.button-variant(@color; @background; @border) {\n color: @color;\n background-color: @background;\n border-color: @border;\n\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 25%);\n }\n &:hover {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n color: @color;\n background-color: darken(@background, 10%);\n border-color: darken(@border, 12%);\n\n &:hover,\n &:focus,\n &.focus {\n color: @color;\n background-color: darken(@background, 17%);\n border-color: darken(@border, 25%);\n }\n }\n &:active,\n &.active,\n .open > .dropdown-toggle& {\n background-image: none;\n }\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus,\n &.focus {\n background-color: @background;\n border-color: @border;\n }\n }\n\n .badge {\n color: @background;\n background-color: @color;\n }\n}\n\n// Button sizes\n.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n border-radius: @border-radius;\n}\n","// Opacity\n\n.opacity(@opacity) {\n opacity: @opacity;\n // IE8 filter\n @opacity-ie: (@opacity * 100);\n filter: ~\"alpha(opacity=@{opacity-ie})\";\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n\n &.in { display: block; }\n tr&.in { display: table-row; }\n tbody&.in { display: table-row-group; }\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition-property(~\"height, visibility\");\n .transition-duration(.35s);\n .transition-timing-function(ease);\n}\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base dashed;\n border-top: @caret-width-base solid ~\"\\9\"; // IE8\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropup,\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer)\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n\n // Nuke hover/focus effects\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: @cursor-disabled;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n white-space: nowrap; // as with > li > a\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base dashed;\n border-bottom: @caret-width-base solid ~\"\\9\"; // IE8\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 2px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n","// Horizontal dividers\n//\n// Dividers (basically an hr) within dropdowns and nav lists\n\n.nav-divider(@color: #e5e5e5) {\n height: 1px;\n margin: ((@line-height-computed / 2) - 1) 0;\n overflow: hidden;\n background-color: @color;\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","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn,\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\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) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\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\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n .border-top-radius(@btn-border-radius-base);\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n .border-top-radius(0);\n .border-bottom-radius(@btn-border-radius-base);\n }\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) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n\n > .btn-group .dropdown-menu {\n left: auto;\n }\n}\n\n\n// Checkbox and radio options\n//\n// In order to support the browser's form validation feedback, powered by the\n// `required` attribute, we have to \"hide\" the inputs via `clip`. We cannot use\n// `display: none;` or `visibility: hidden;` as that also hides the popover.\n// Simply visually hiding the inputs via `opacity` would leave them clickable in\n// certain cases which is prevented by using `clip` and `pointer-events`.\n// This way, we ensure a DOM element is visible to position the popover from.\n//\n// See https://github.com/twbs/bootstrap/pull/12794 and\n// https://github.com/twbs/bootstrap/pull/14559 for more information.\n\n[data-toggle=\"buttons\"] {\n > .btn,\n > .btn-group > .btn {\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0,0,0,0);\n pointer-events: none;\n }\n }\n}\n","// Single side border-radius\n\n.border-top-radius(@radius) {\n border-top-right-radius: @radius;\n border-top-left-radius: @radius;\n}\n.border-right-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-top-right-radius: @radius;\n}\n.border-bottom-radius(@radius) {\n border-bottom-right-radius: @radius;\n border-bottom-left-radius: @radius;\n}\n.border-left-radius(@radius) {\n border-bottom-left-radius: @radius;\n border-top-left-radius: @radius;\n}\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n\n &:focus {\n z-index: 3;\n }\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn {\n .input-lg();\n}\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn {\n .input-sm();\n}\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @input-border-radius;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @input-border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @input-border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\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-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-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n z-index: 2;\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: @cursor-disabled;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n .navbar-collapse {\n max-height: @navbar-collapse-max-height;\n\n @media (max-device-width: @screen-xs-min) and (orientation: landscape) {\n max-height: 200px;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\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; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n > img {\n display: block;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: 0;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .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 > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n\n &:last-child {\n margin-bottom: 0;\n }\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n margin-bottom: 0;\n .border-top-radius(@navbar-border-radius);\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n//\n// Declared after the navbar components to ensure more specificity on the margins.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right {\n .pull-right();\n margin-right: -@navbar-padding-horizontal;\n\n ~ .navbar-right {\n margin-right: 0;\n }\n }\n}\n\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n }\n }\n }\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n .btn-link {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n }\n }\n }\n}\n","// Navbar vertical align\n//\n// Vertically center elements in the navbar.\n// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.\n\n.navbar-vertical-align(@element-height) {\n margin-top: ((@navbar-height - @element-height) / 2);\n margin-bottom: ((@navbar-height - @element-height) / 2);\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 3;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: @cursor-disabled;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n","// Pagination\n\n.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {\n > li {\n > a,\n > span {\n padding: @padding-vertical @padding-horizontal;\n font-size: @font-size;\n line-height: @line-height;\n }\n &:first-child {\n > a,\n > span {\n .border-left-radius(@border-radius);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius);\n }\n }\n }\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: @cursor-disabled;\n }\n }\n}\n","//\n// Labels\n// --------------------------------------------------\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: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n a& {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","// Labels\n\n.label-variant(@color) {\n background-color: @color;\n\n &[href] {\n &:hover,\n &:focus {\n background-color: darken(@color, 10%);\n }\n }\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base class\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: middle;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n\n .btn-xs &,\n .btn-group-xs > .btn & {\n top: 0;\n padding: 1px 5px;\n }\n\n // Hover state, but only for links\n a& {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Account for badges in navs\n .list-group-item.active > &,\n .nav-pills > .active > a > & {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n }\n\n .list-group-item > & {\n float: right;\n }\n\n .list-group-item > & + & {\n margin-right: 5px;\n }\n\n .nav-pills > li > a > & {\n margin-left: 3px;\n }\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding-top: @jumbotron-padding;\n padding-bottom: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n > hr {\n border-top-color: darken(@jumbotron-bg, 10%);\n }\n\n .container &,\n .container-fluid & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n padding-left: (@grid-gutter-width / 2);\n padding-right: (@grid-gutter-width / 2);\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container &,\n .container-fluid & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: @jumbotron-heading-font-size;\n }\n }\n}\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\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(border .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissible alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0.\n.alert-dismissible {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","// Alerts\n\n.alert-variant(@background; @border; @text-color) {\n background-color: @background;\n border-color: @border;\n color: @text-color;\n\n hr {\n border-top-color: darken(@border, 5%);\n }\n .alert-link {\n color: darken(@text-color, 10%);\n }\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @progress-border-radius;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n//\n// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar-striped` class, which you just add to an existing\n// `.progress-bar`.\n.progress-striped .progress-bar,\n.progress-bar-striped {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n//\n// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the\n// `.progress-bar.active` approach.\n.progress.active .progress-bar,\n.progress-bar.active {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\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","// Progress bars\n\n.progress-bar-variant(@color) {\n background-color: @color;\n\n // Deprecated parent class requirement as of v3.2.0\n .progress-striped & {\n #gradient > .striped();\n }\n}\n",".media {\n // Proper spacing between instances of .media\n margin-top: 15px;\n\n &:first-child {\n margin-top: 0;\n }\n}\n\n.media,\n.media-body {\n zoom: 1;\n overflow: hidden;\n}\n\n.media-body {\n width: 10000px;\n}\n\n.media-object {\n display: block;\n\n // Fix collapse in webkit from max-width: 100% and display: table-cell.\n &.img-thumbnail {\n max-width: none;\n }\n}\n\n.media-right,\n.media > .pull-right {\n padding-left: 10px;\n}\n\n.media-left,\n.media > .pull-left {\n padding-right: 10px;\n}\n\n.media-left,\n.media-right,\n.media-body {\n display: table-cell;\n vertical-align: top;\n}\n\n.media-middle {\n vertical-align: middle;\n}\n\n.media-bottom {\n vertical-align: bottom;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n\n// Media list variation\n//\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on