58 Commits
v1.0 ... master

Author SHA1 Message Date
Ivan Paulovich
15ac2de003 docs 2018-03-23 19:10:45 -03:00
Ivan Paulovich
9334511f78 Unit Tests 2018-01-25 17:19:31 -02:00
Ivan Paulovich
4b5800e3fb Unit Tests 2018-01-25 14:38:28 -02:00
Ivan Paulovich
27c8075092 Docs 2017-12-27 09:22:32 -02:00
Ivan Paulovich
74b5c8e3a1 Set theme jekyll-theme-cayman 2017-12-22 11:15:21 -02:00
Ivan Paulovich
823a8c9d1b Consumer with pause/resume support 2017-12-21 17:46:59 -02:00
Ivan Paulovich
fc74b10766 Spliting projects 2017-12-21 15:59:03 -02:00
Ivan Paulovich
ffdc36dbd2 Merge pull request #4 from ivanpaulovich/dev
refactoring
2017-12-18 15:19:56 -02:00
Ivan Paulovich
81d8a76256 ok 2017-12-18 15:16:09 -02:00
Ivan Paulovich
3a414dbf79 refactoring 2017-12-18 15:00:24 -02:00
Ivan Paulovich
19b1c4295e ok 2017-11-10 09:24:18 -02:00
Ivan Paulovich
cd9bb683c8 ok 2017-11-10 09:23:45 -02:00
Ivan Paulovich
8a58fa6d34 tests 2017-11-03 23:23:57 -02:00
Ivan Paulovich
af0f787f6a Consumer App changed to console (bug fix) 2017-10-12 16:04:57 -03:00
Ivan Paulovich
ce55972d64 SRP 2017-10-10 17:23:40 -03:00
Ivan Paulovich
6d3db24188 CQRS 2017-10-10 17:18:23 -03:00
Ivan Paulovich
63b81a62e4 palestras antigas 2017-10-10 09:05:53 -03:00
Ivan Paulovich
16bc48a491 Merge pull request #2 from ivanpaulovich/dev
Complete refactoring to DIP architecture style
2017-10-06 15:40:45 -03:00
Ivan Paulovich
be322e581b full refactoring 2017-10-06 15:39:16 -03:00
Ivan Paulovich
884c3a6f34 refactoring 2017-10-06 13:06:00 -03:00
Ivan Paulovich
261c037f27 refactoring 2017-10-06 11:11:07 -03:00
Ivan Paulovich
457285e194 refactoring 2017-10-06 11:10:22 -03:00
Ivan Paulovich
cb274449a8 refactoring 2017-10-06 10:34:41 -03:00
Ivan Paulovich
9628a4f60b refactoring 2017-10-05 17:20:07 -03:00
Ivan Paulovich
dc690a49f0 refactoring 2017-10-05 17:08:45 -03:00
Ivan Paulovich
efaeedce3f DIP 2017-10-05 16:44:58 -03:00
Ivan Paulovich
9231f5a9a6 annotations 2017-10-02 17:14:26 -03:00
Ivan Paulovich
d25db6a360 data annotations 2017-10-02 17:04:10 -03:00
Ivan Paulovich
472185f752 Merge branch 'master' of https://github.com/ivanpaulovich/jambo 2017-10-02 14:26:50 -03:00
Ivan Paulovich
92287837ec workshop 2017-10-02 14:26:35 -03:00
Ivan Paulovich
fe2d38cd12 Set theme jekyll-theme-cayman 2017-09-27 11:22:04 -03:00
Ivan Paulovich
be6b993d03 Corrigindo erro de compilacao do ServiceBus.Kafka. Tks José! 2017-09-26 21:06:26 -03:00
Ivan Paulovich
c5551d84fa apresentacao 2017-09-25 10:39:32 -03:00
Ivan Paulovich
c3c5e3a729 apresentação final 2017-09-23 11:53:08 -03:00
Ivan Paulovich
86777211a4 apresentacao 2017-09-22 16:39:26 -03:00
Ivan Paulovich
a3c98f8bd7 apresentacao 2017-09-22 16:39:06 -03:00
Ivan Paulovich
fcbd1a7294 cores 2017-09-20 21:13:08 -03:00
Ivan Paulovich
92570ff64d compose 2017-09-20 14:05:46 -03:00
Ivan Paulovich
4bf8d9d583 ES 2017-09-20 11:16:42 -03:00
Ivan Paulovich
4a353a8baf principios 2017-09-20 11:08:48 -03:00
Ivan Paulovich
3271a92164 ok 2017-09-20 09:06:28 -03:00
Ivan Paulovich
d721e13108 event sourcing 2017-09-20 09:02:58 -03:00
Ivan Paulovich
46db1f45ac event sourcing 2017-09-19 22:13:31 -03:00
Ivan Paulovich
5ef48012fb event sourcing 2017-09-19 17:45:16 -03:00
Ivan Paulovich
1241a58f3a event sourcing 2017-09-19 17:23:15 -03:00
Ivan Paulovich
5a10794876 ok 2017-09-18 16:50:22 -03:00
Ivan Paulovich
a003da0240 ok 2017-09-18 16:41:14 -03:00
Ivan Paulovich
b21559bd0d Merge branch 'master' of https://github.com/ivanpaulovich/jambo 2017-09-18 15:57:43 -03:00
Ivan Paulovich
f99dcce2e0 ES 2017-09-18 15:57:27 -03:00
Ivan Paulovich
ed3afddf21 ok 2017-09-12 16:01:09 -03:00
Ivan Paulovich
847c3ee696 Ok 2017-09-11 10:06:13 -03:00
Ivan Paulovich
7175f13b21 ok 2017-09-10 21:28:24 -03:00
Ivan Paulovich
644bcf0294 ok 2017-09-10 16:16:25 -03:00
Ivan Paulovich
d409c29c73 ok 2017-09-10 16:02:48 -03:00
Ivan Paulovich
2b691e61bf ok 2017-09-10 15:59:51 -03:00
Ivan Paulovich
d0595f758e Merge branch 'master' of https://github.com/ivanpaulovich/jambo 2017-09-10 15:48:17 -03:00
Ivan Paulovich
4160881df9 ok 2017-09-10 15:34:15 -03:00
Ivan Paulovich
a426a1a3a4 ok 2017-09-10 08:17:35 -03:00
561 changed files with 1624 additions and 50973 deletions

BIN
Producer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

188
README.md
View File

@@ -1,78 +1,124 @@
Neste projeto experimental em Domain-Driven-Design com implementação de Aggregates + Event Sourcing + CQRS + Optimistic Concurrency a técnica de Event Sourcing é usada como ferramenta de auditoria das modificações do conteúdo do Blog. A fonte dos dados são os Domain Events registrados no Kafka, para permitir consultas de alta performance, foi criado um banco de dados MongoDB com o 'último estado já processado dos eventos'. Há um microsserviço auxiliar de autenticação. Tudo isso foi implementado em .NET Core/Standard compatível com Docker! Divirta-se!
A solution based on a Event-Driven architecture with DDD and CQRS. The solution contains the following applications.
* A Producer Web API which receives Commands to produce Domain Events. This one also receives Queries and returns JSON.
* A Consumer Console App that reads the Event Stream and do a projection to a MongoDB database.
* A Web API for authentication and JWT generation.
#### O Domínio
![Domain](https://github.com/ivanpaulovich/jambo/blob/master/images/Domain.png)
#### As Aplicações desta Solução
* **Producer**: Web API que recebe os comandos de edição de conteúdo, produz Eventos de Domínio e publica as mensagens em um tópico no Kafka.
* **Consumer**: Aplicativo Console que consome as mensagens do Kafka, deserializa em Eventos de Domínio e aplica nas agregações persistindo no MongoDB o novo estado.
* **Auth**: Web API que gera tokens de autenticação para acesso ao WebAPI.
#### Por onde começar?
Há duas formas de iniciar a solução.
##### 1. O jeito fácil
Resolver os [pré-requisitos](https://github.com/ivanpaulovich/jambo/#prerequisitos), definir o projeto inicial como sendo o `docker-compose` e então apertar `Ctrl+F5` para executar todas as aplicações. Se tudo estiver correto, digite `docker ps` no seu terminal para verificar em quais portas cada aplicação está executando. Será algo assim:
![Enviando comandos](https://github.com/ivanpaulovich/jambo/blob/master/images/Docker-PS.PNG)
A partir daí basta acessar:
* Auth em http://localhost:32775/swagger/
* Producer em http://localhost:32776/swagger/
Leia o [o jeito não tão fácil](https://github.com/ivanpaulovich/jambo/#2-o-jeito-não-tão-fácil) para entender como criar um Token no Auth API para consumir os serviços do Producer API via swagger.
##### 2. O jeito não tão fácil
A outra opção é inicializar aplicação por aplicação, seguindo o seguintes passos:
1. Execute o projeto **Jambo.Auth.WebAPI** e chame o método *Account/Token* com qualquer usuário e senha. *Guarde este token*.
![Auth](https://github.com/ivanpaulovich/jambo/blob/master/images/Auth.PNG)
![Auth com Token](https://github.com/ivanpaulovich/jambo/blob/master/images/Auth1.PNG)
3. Execute o projeto **Jambo.Producer.WebAPI** e clique no botão *Authorization* (topo direito da página).
Digite `bearer + valor_do_token` e clique em fechar. Algo assim:
![Autorizando](https://github.com/ivanpaulovich/jambo/blob/master/images/Producer.PNG)
Chame os métodos para manutenção dos dados do Blog, Posts e Comentários.
![Enviando comandos](https://github.com/ivanpaulovich/jambo/blob/master/images/Producer02.PNG)
2. Execute o projeto **Jambo.Consumer.Console** e garante que ele **contínua em execução**.
![Comsumer em execução](https://github.com/ivanpaulovich/jambo/blob/master/images/Consumer.PNG)
4. Visualize suas modificações
![Queries](https://github.com/ivanpaulovich/jambo/blob/master/images/Producer03.PNG)
#### Demo
*Em breve...*
#### Próximos passos?
1. Publicar os containers no Azure.
2. Criar um CI/CD para atualizar os containers a cada commit.
3. Criar testes de unidade, testes automatizados.
4. Consumir serviços externos.
5. Implementação alternativa de barramento: Azure Event Hubs
6. Implementação alternativa de snapshot: Azure Cosmos DB
#### Pré-requisitos
[Checkout the Source Code on GitHub](https://github.com/ivanpaulovich/jambo)
#### Requirements
* [Visual Studio 2017 + Update 3](https://www.visualstudio.com/en-us/news/releasenotes/vs2017-relnotes)
* [.NET SDK 2.0](https://www.microsoft.com/net/download/core)
* [Docker](https://docs.docker.com/docker-for-windows/install/) (Opcional)
* [Robomongo](https://robomongo.org/) (Opcional)
* [.NET CORE SDK 2.0](https://www.microsoft.com/net/download/core)
* [Docker](https://docs.docker.com/docker-for-windows/install/)
#### Agradecimentos
Obrigado aos amigos que me estimularam a criar este projeto e estão sempre contribuindo e dando feedback.
* [Vinicius Baldotto](https://github.com/Baldotto)
* [André Paulovich](https://github.com/andrepaulovich)
* André Mendes
#### Environment setup
Obrigado de verdade!
*If you already have valid connections for Kafka and MongoDB you could skip this step and go to Running the applications step.*
#### Deixe o seu feedback
Agradeço todo comentário sobre o projeto. Envie suas dúvidas e sugestões no [Fórum](https://github.com/ivanpaulovich/jambo/issues).
* Run the `./up-kafka-mongodb.sh` bash script to run Kafka and MongoDB as Docker Containers. Please wait until the ~800mb download to be complete.
#### Histórico de Versões
* 12/set/2017:
*Em breve...*
```
$ ./up-kafka-mongodb.sh
Pulling mongodb (mongo:latest)...
latest: Pulling from library/mongo
Digest: sha256:2c55bcc870c269771aeade05fc3dd3657800540e0a48755876a1dc70db1e76d9
Status: Downloaded newer image for mongo:latest
Pulling kafka (spotify/kafka:latest)...
latest: Pulling from spotify/kafka
Digest: sha256:cf8f8f760b48a07fb99df24fab8201ec8b647634751e842b67103a25a388981b
Status: Downloaded newer image for spotify/kafka:latest
Creating setup_mongodb_1 ...
Creating setup_kafka_1 ...
Creating setup_mongodb_1
Creating setup_mongodb_1 ... done
```
* Check if the data layer is ready with the following commands:
```
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mongo latest d22888af0ce0 17 hours ago 361MB
spotify/kafka latest a9e0a5b8b15e 11 months ago 443MB
```
```
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
32452776153f spotify/kafka "supervisord -n" 2 days ago Up 2 days 0.0.0.0:2181->2181/tcp, 0.0.0.0:9092->9092/tcp setup_kafka_1
ba28cf144478 mongo "docker-entrypoint..." 2 days ago Up 2 days 0.0.0.0:27017->27017/tcp setup_mongodb_1
```
If Kafka is running well it will be working with the `10.0.75.1:9092` connection string.
if MongoDB is running well it will be working at `mongodb://10.0.75.1:27017` connection string.
## Running the applications
You have two options to run the applications, one is by opening with Visual Studio 2017 and the other is by running dotnet core commands.
### Option 1 - Running with Visual Studio 2017
Open the three solutions on three Visual Studios them run the following projects.
* `Jambo.Auth.UI`.
* `Jambo.Consumer.UI`.
* `Jambo.Producer.UI`.
### Option 2 - Running with dotnet commands
#### How to run the Bearer Authencation API
1. Run the command `dotnet run` at `source\Auth\Jambo.Auth.UI` folder.
```
$ dotnet run
Using launch settings from C:\git\jambo\source\Auth\Jambo.Auth.UI\Properties\launchSettings.json...
Hosting environment: Development
Content root path: C:\git\jambo\source\Auth\Jambo.Auth.UI
Now listening on: http://localhost:16024
Application started. Press Ctrl+C to shut down.
```
2. Navigate to the Swagger UI at (eg. http://localhost:16024/swagger).
3. Post the following credentials:
```
{
"username": "ivanpaulovich",
"password": "mysecret"
}
```
4. __Store the Bearer Token__ because you will need the token value to log on Producer API.
```
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhYzA4MmE3OS1lMWY3LTQ4MTktYmU1Mi1hOTQwMTBkM2VjZTciLCJzdWIiOiJzdHJpbmciLCJleHAiOjE1MTI0Nzg5ODgsImlzcyI6Imh0dHA6Ly9teWFjY291bnRhcGkiLCJhdWQiOiJodHRwOi8vbXlhY2NvdW50YXBpIn0.9YKGmKaptLBDcExHhPOQ3_j9TsdbkcRf8ZtvIkdq8Go",
"expiration": "2017-12-05T13:03:08Z"
}
```
#### How to run the Consumer API
1. Run the command `dotnet run` at `source\Consumer\Jambo.Consumer.UI` folder
```
$ dotnet run
11/5/2017 11:17:20 AM Waiting for events..
11/5/2017 11:18:20 AM Waiting for events..
11/5/2017 11:19:20 AM Waiting for events..
11/5/2017 11:20:20 AM Waiting for events..
11/5/2017 11:21:20 AM Waiting for events..
11/5/2017 11:22:20 AM Waiting for events..
```
3. __Attention:__ keep the Console App running for events processing.
#### How to run the Producer API
![Authorization](https://github.com/ivanpaulovich/jambo/blob/master/Producer.png)
1. Run the command `dotnet run` at the `source\Producer\Jambo.Producer.UI` folder.
```
$ dotnet run
Using launch settings from C:\git\jambo\source\Producer\Jambo.Producer.UI\Properties\launchSettings.json...
Hosting environment: Development
Content root path: C:\git\jambo\source\Producer\Jambo.Producer.UI
Now listening on: http://localhost:16959
Application started. Press Ctrl+C to shut down.
```
2. Navigate to the Swagger UI (eg. http://localhost:14398/swagger).

1
_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-cayman

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

15
setup/docker-compose.yml Normal file
View File

@@ -0,0 +1,15 @@
version: '3'
services:
mongodb:
image: mongo
ports:
- 27017:27017
kafka:
image: spotify/kafka
ports:
- 2181:2181
- 9092:9092
environment:
- ADVERTISED_HOST=10.0.75.1
- ADVERTISED_PORT=9092

View File

@@ -0,0 +1 @@
docker-compose down

View File

@@ -0,0 +1 @@
docker-compose up -d

View File

@@ -0,0 +1,17 @@
namespace Jambo.Auth.Application.Commands
{
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
[DataContract]
public class LoginCommand
{
[Required]
[DataMember]
public string Username { get; set; }
[Required]
[DataMember]
public string Password { get; set; }
}
}

View File

@@ -9,8 +9,4 @@
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="CommandHandlers\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;NU1701;CS1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Auth.Application\Jambo.Auth.Application.csproj" />
</ItemGroup>
</Project>

View File

@@ -3,7 +3,7 @@
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:8546/",
"applicationUrl": "http://localhost:15877/",
"sslPort": 0
}
},
@@ -16,14 +16,14 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Jambo.Auth.WebAPI": {
"Jambo.Auth.Infrastructure": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "api/values",
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:8547/"
"applicationUrl": "http://localhost:15878/"
}
}
}
}

View File

@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Jambo.Auth.Application.Auth
namespace Jambo.Auth.UI
{
public class Config
{

View File

@@ -1,18 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;
using Jambo.Auth.Application.Auth;
using Jambo.Auth.Application.Commands;
namespace Jambo.Auth.WebAPI.Controllers
namespace Jambo.Auth.UI
{
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;
using Jambo.Auth.Application.Commands;
[Route("api/[controller]")]
public class AccountController : Controller
{
@@ -27,7 +24,12 @@ namespace Jambo.Auth.WebAPI.Controllers
[HttpPost]
public IActionResult Token([FromBody] LoginCommand loginCommand)
{
var token = GetJwtSecurityToken(loginCommand.Username);
//
// TODO: Implementar uma verificação
// se loginCommand.Username e loginCommand.Password são válidos.
//
var token = GetJwtSecurityToken(loginCommand);
return Ok(new
{
@@ -36,25 +38,25 @@ namespace Jambo.Auth.WebAPI.Controllers
});
}
private JwtSecurityToken GetJwtSecurityToken(string user)
private JwtSecurityToken GetJwtSecurityToken(LoginCommand user)
{
return new JwtSecurityToken(
config.Issuer,
config.Issuer,
GetTokenClaims(user),
expires: DateTime.UtcNow.AddMinutes(30),
expires: DateTime.UtcNow.AddDays(7),
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey)),
SecurityAlgorithms.HmacSha256)
);
}
private static IEnumerable<Claim> GetTokenClaims(string user)
private static IEnumerable<Claim> GetTokenClaims(LoginCommand user)
{
return new List<Claim>
{
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.Sub, user)
new Claim(JwtRegisteredClaimNames.Jti, user.Username),
new Claim(JwtRegisteredClaimNames.Sub, user.Password),
};
}
}

View File

@@ -3,4 +3,4 @@ ARG source
WORKDIR /app
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "Jambo.Auth.WebAPI.dll"]
ENTRYPOINT ["dotnet", "Jambo.Auth.UI.dll"]

View File

@@ -2,11 +2,16 @@
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;NU1701</NoWarn>
<DocumentationFile>bin\Debug\netcoreapp2.0\Jambo.Auth.UI.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
<None Include="appsettings.Development.json" />
<None Include="appsettings.json" />
</ItemGroup>
<ItemGroup>
@@ -14,10 +19,6 @@
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Auth.Application\Jambo.Auth.Application.csproj" />
</ItemGroup>

View File

@@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Jambo.Auth.WebAPI
namespace Jambo.Auth.UI
{
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
public class Program
{
public static void Main(string[] args)

View File

@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:16023/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Jambo.Auth.UI": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:16024/"
}
}
}

View File

@@ -0,0 +1,54 @@
namespace Jambo.Auth.UI
{
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Auth API",
Version = "v1",
Description = "The Auth API",
TermsOfService = "Terms Of Service"
});
});
services.AddScoped(
s => new UI.Config(Configuration.GetSection("Security").GetValue<string>("SecretKey"),
Configuration.GetSection("Security").GetValue<string>("Issuer")));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
}
}

View File

@@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Auth.Application", "Jambo.Auth.Application\Jambo.Auth.Application.csproj", "{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Auth.Infrastructure", "Jambo.Auth.Infrastructure\Jambo.Auth.Infrastructure.csproj", "{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jambo.Auth.UI", "Jambo.Auth.UI\Jambo.Auth.UI.csproj", "{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CADA8CE3-F53F-46FE-80B1-F898AEC640E1}.Release|Any CPU.Build.0 = Release|Any CPU
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D91971DB-5A62-4BB5-B310-6BF4E9C8596F}.Release|Any CPU.Build.0 = Release|Any CPU
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4BECC370-4749-48C2-82FF-47ADE3BE0FAA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54AE4BA0-6081-4892-A9B9-3F8D15D994C8}
EndGlobalSection
EndGlobal

View File

@@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
<ProjectReference Include="..\..\Shared\Jambo.Domain\Jambo.Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,76 @@
namespace Jambo.Consumer.Infrastructure.DataAccess
{
using Jambo.Domain.Model;
using Jambo.Domain.Model.Blogs;
using Jambo.Domain.Model.Posts;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
public class MongoContext
{
private readonly MongoClient mongoClient;
private readonly IMongoDatabase database;
public MongoContext(string connectionString, string databaseName)
{
this.mongoClient = new MongoClient(connectionString);
this.database = mongoClient.GetDatabase(databaseName);
Map();
}
public void DatabaseReset(string databaseName)
{
mongoClient.DropDatabase(databaseName);
}
public IMongoCollection<Blog> Blogs
{
get
{
return database.GetCollection<Blog>("Blogs");
}
}
public IMongoCollection<Post> Posts
{
get
{
return database.GetCollection<Post>("Posts");
}
}
private void Map()
{
BsonClassMap.RegisterClassMap<Entity>(cm =>
{
cm.MapIdProperty(c => c.Id);
});
BsonClassMap.RegisterClassMap<AggregateRoot>(cm =>
{
cm.MapProperty(c => c.Version).SetElementName("_version");
});
BsonClassMap.RegisterClassMap<Blog>(cm =>
{
cm.MapField("url").SetElementName("url");
cm.MapField("rating").SetElementName("rating");
cm.MapField("enabled").SetElementName("enabled");
});
BsonClassMap.RegisterClassMap<Post>(cm =>
{
cm.MapField("title").SetElementName("title");
cm.MapField("content").SetElementName("content");
cm.MapField("blogId").SetElementName("blogId");
cm.MapField("enabled").SetElementName("enabled");
cm.MapField("published").SetElementName("published");
});
BsonClassMap.RegisterClassMap<Comment>(cm =>
{
cm.MapField("message").SetElementName("message");
});
}
}
}

View File

@@ -1,12 +1,11 @@
using Jambo.Domain.Model.Blogs;
using Jambo.Domain.Model.Posts;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Jambo.Infrastructure.Repositories.Blogs
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs
{
using Jambo.Domain.Model.Blogs;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class BlogReadOnlyRepository : IBlogReadOnlyRepository
{
private readonly MongoContext _mongoContext;

View File

@@ -1,10 +1,10 @@
using Jambo.Domain.Model.Blogs;
using MongoDB.Driver;
using System;
using System.Threading.Tasks;
namespace Jambo.Infrastructure.Repositories.Blogs
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs
{
using Jambo.Domain.Model.Blogs;
using MongoDB.Driver;
using System;
using System.Threading.Tasks;
public class BlogWriteOnlyRepository : IBlogWriteOnlyRepository
{
private readonly MongoContext mongoContext;

View File

@@ -0,0 +1,28 @@
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts
{
using Jambo.Domain.Model.Posts;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class PostReadOnlyRepository : IPostReadOnlyRepository
{
private readonly MongoContext _mongoContext;
public PostReadOnlyRepository(MongoContext mongoContext)
{
_mongoContext = mongoContext;
}
public async Task<IEnumerable<Post>> GetBlogPosts(Guid blogId)
{
return await _mongoContext.Posts.Find(e => e.BlogId == blogId).ToListAsync();
}
public async Task<Post> GetPost(Guid id)
{
return await _mongoContext.Posts.Find(e => e.Id == id).SingleAsync();
}
}
}

View File

@@ -1,11 +1,10 @@
using Jambo.Domain.Model.Blogs;
using Jambo.Domain.Model.Posts;
using MongoDB.Driver;
using System;
using System.Threading.Tasks;
namespace Jambo.Infrastructure.Repositories.Posts
namespace Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts
{
using Jambo.Domain.Model.Posts;
using MongoDB.Driver;
using System;
using System.Threading.Tasks;
public class PostWriteOnlyRepository : IPostWriteOnlyRepository
{
private readonly MongoContext _mongoContext;

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.6.0" />
<PackageReference Include="Autofac.Configuration" Version="4.0.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
<PackageReference Include="MediatR" Version="3.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
<PackageReference Include="Confluent.Kafka" Version="0.11.0" NoWarn="NU1701" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
<ProjectReference Include="..\..\Shared\Jambo.Domain\Jambo.Domain.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,28 +1,21 @@
using Autofac;
using Jambo.Domain.Model.Blogs;
using Jambo.Domain.Model.Posts;
using Jambo.Infrastructure;
using Jambo.Infrastructure.Repositories;
using Jambo.Infrastructure.Repositories.Blogs;
using Jambo.Infrastructure.Repositories.Posts;
namespace Jambo.Consumer.IoC
namespace Jambo.Consumer.Infrastructure.Modules
{
using Autofac;
using Jambo.Domain.Model.Blogs;
using Jambo.Domain.Model.Posts;
using Jambo.Consumer.Infrastructure.DataAccess.Repositories.Blogs;
using Jambo.Consumer.Infrastructure.DataAccess.Repositories.Posts;
using Jambo.Consumer.Infrastructure.DataAccess;
public class ApplicationModule : Module
{
public readonly string connectionString;
public readonly string database;
public ApplicationModule(string connectionString, string database)
{
this.connectionString = connectionString;
this.database = database;
}
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
protected override void Load(ContainerBuilder builder)
{
MongoContext mongoContext = new MongoContext(connectionString, database);
mongoContext.DatabaseReset(database);
MongoContext mongoContext = new MongoContext(ConnectionString, DatabaseName);
mongoContext.DatabaseReset(DatabaseName);
builder.Register(c => mongoContext)
.As<MongoContext>().SingleInstance();

View File

@@ -0,0 +1,21 @@
namespace Jambo.Consumer.Infrastructure.Modules
{
using Autofac;
using Jambo.Consumer.Infrastructure.ServiceBus;
using Jambo.Domain.ServiceBus;
public class BusModule : Module
{
public string BrokerList { get; set; }
public string Topic { get; set; }
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<Bus>()
.As<ISubscriber>()
.WithParameter("brokerList", BrokerList)
.WithParameter("topic", Topic)
.SingleInstance();
}
}
}

View File

@@ -0,0 +1,38 @@
namespace Jambo.Consumer.Infrastructure.Modules
{
using Autofac;
using Autofac.Features.Variance;
using Jambo.Consumer.Application.DomainEventHandlers.Blogs;
using MediatR;
using System.Collections.Generic;
using System.Reflection;
public class MediatRModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new ContravariantRegistrationSource());
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
builder
.Register<SingleInstanceFactory>(ctx => {
var c = ctx.Resolve<IComponentContext>();
return t => { object o; return c.TryResolve(t, out o) ? o : null; };
})
.InstancePerLifetimeScope();
builder
.Register<MultiInstanceFactory>(ctx => {
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(typeof(BlogCreatedEventHandler).GetTypeInfo().Assembly).AsImplementedInterfaces(); // via assembly scan
}
}
}

View File

@@ -0,0 +1,121 @@
namespace Jambo.Consumer.Infrastructure.ServiceBus
{
using Confluent.Kafka;
using Confluent.Kafka.Serialization;
using Jambo.Domain.Exceptions;
using Jambo.Domain.Model;
using Jambo.Domain.ServiceBus;
using MediatR;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Text;
public class Bus : ISubscriber
{
public readonly string brokerList;
public readonly string topic;
private static Dictionary<string, object> constructConfig(string brokerList, bool enableAutoCommit) =>
new Dictionary<string, object>
{
{ "group.id", "jambo-consumer" },
{ "enable.auto.commit", enableAutoCommit },
{ "auto.commit.interval.ms", 5000 },
{ "statistics.interval.ms", 60000 },
{ "bootstrap.servers", brokerList },
{ "default.topic.config", new Dictionary<string, object>()
{
{ "auto.offset.reset", "smallest" }
}
}
};
public Bus(string brokerList, string topic)
{
this.brokerList = brokerList;
this.topic = topic;
}
public void Listen(IMediator mediator)
{
using (var consumer = new Consumer<string, string>(constructConfig(brokerList, true), new StringDeserializer(Encoding.UTF8), new StringDeserializer(Encoding.UTF8)))
{
consumer.OnPartitionEOF += (_, end)
=> Console.WriteLine($"Reached end of topic {end.Topic} partition {end.Partition}, next message will be at offset {end.Offset}");
consumer.OnError += (_, error)
=> Console.WriteLine($"Error: {error}");
consumer.OnConsumeError += (_, msg)
=> Console.WriteLine($"Error consuming from topic/partition/offset {msg.Topic}/{msg.Partition}/{msg.Offset}: {msg.Error}");
consumer.OnOffsetsCommitted += (_, commit) =>
{
Console.WriteLine($"[{string.Join(", ", commit.Offsets)}]");
if (commit.Error)
{
Console.WriteLine($"Failed to commit offsets: {commit.Error}");
}
Console.WriteLine($"Successfully committed offsets: [{string.Join(", ", commit.Offsets)}]");
};
consumer.OnPartitionsAssigned += (_, partitions) =>
{
Console.WriteLine($"Assigned partitions: [{string.Join(", ", partitions)}], member id: {consumer.MemberId}");
consumer.Assign(partitions);
};
consumer.OnPartitionsRevoked += (_, partitions) =>
{
Console.WriteLine($"Revoked partitions: [{string.Join(", ", partitions)}]");
consumer.Unassign();
};
consumer.OnStatistics += (_, json)
=> Console.WriteLine($"Statistics: {json}");
consumer.Subscribe(this.topic);
Console.WriteLine($"Subscribed to: [{string.Join(", ", consumer.Subscription)}]");
var cancelled = false;
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true; // prevent the process from terminating.
cancelled = true;
};
Console.WriteLine("Ctrl-C to exit.");
while (!cancelled)
{
Message<string, string> msg;
if (consumer.Consume(out msg, TimeSpan.FromSeconds(1)))
{
Console.WriteLine($"Topic: {msg.Topic} Partition: {msg.Partition} Offset: {msg.Offset} {msg.Value}");
try
{
Type eventType = Type.GetType(msg.Key);
DomainEvent domainEvent = (DomainEvent)JsonConvert.DeserializeObject(msg.Value, eventType);
mediator.Send(domainEvent).Wait();
}
catch (DomainException ex)
{
Console.WriteLine(ex.BusinessMessage);
}
catch (TransactionConflictException ex)
{
Console.WriteLine(ex.DomainEvent.ToString());
}
catch (JamboException ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
}
}

View File

@@ -3,4 +3,4 @@ ARG source
WORKDIR /app
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
ENTRYPOINT ["dotnet", "Jambo.Consumer.Console.dll"]
ENTRYPOINT ["dotnet", "Jambo.Consumer.UI.dll"]

View File

@@ -10,35 +10,26 @@
</PropertyGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.6.0" />
<PackageReference Include="Autofac.Configuration" Version="4.0.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
<PackageReference Include="MediatR" Version="3.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
<PackageReference Include="Confluent.Kafka" Version="0.11.0" NoWarn="NU1701" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Consumer.IoC\Jambo.Consumer.IoC.csproj" />
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
<ProjectReference Include="..\..\Shared\Jambo.Domain\Jambo.Domain.csproj" />
<ProjectReference Include="..\Jambo.Consumer.Infrastructure\Jambo.Consumer.Infrastructure.csproj" />
</ItemGroup>
<ItemGroup>
<None Update=".dockerignore">
<DependentUpon>Dockerfile</DependentUpon>
</None>
</ItemGroup>
</Project>

View File

@@ -1,20 +1,20 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
namespace Jambo.Consumer.Console
namespace Jambo.Consumer.Infrastructure
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
class Program
{
static void Main(string[] args)
public static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("autofac.json")
.AddEnvironmentVariables();
IConfigurationRoot configuration = builder.Build();
IServiceCollection serviceCollection = new ServiceCollection();
Startup startup = new Startup(configuration);

View File

@@ -0,0 +1,43 @@
namespace Jambo.Consumer.Infrastructure
{
using Autofac;
using Autofac.Configuration;
using Autofac.Extensions.DependencyInjection;
using Jambo.Domain.ServiceBus;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
IServiceProvider serviceProvider;
public IServiceProvider ConfigureServices(IServiceCollection services)
{
ContainerBuilder builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new ConfigurationModule(Configuration));
serviceProvider = new AutofacServiceProvider(builder.Build());
return serviceProvider;
}
public void Run()
{
IMediator mediator = serviceProvider.GetService<IMediator>();
ISubscriber subscriber = serviceProvider.GetService<ISubscriber>();
subscriber.Listen(mediator);
}
}
}

View File

@@ -0,0 +1,3 @@
{
}

View File

@@ -0,0 +1,24 @@
{
"defaultAssembly": "Jambo.Consumer.Infrastructure",
"modules": [
{
"type": "Jambo.Consumer.Infrastructure.Modules.ApplicationModule",
"properties": {
"ConnectionString": "mongodb://10.0.75.1:27017",
"DatabaseName": "jambov32"
}
},
{
"type": "Jambo.Consumer.Infrastructure.Modules.BusModule",
"properties": {
"BrokerList": "10.0.75.1:9092",
"Topic": "jambov32"
}
},
{
"type": "Jambo.Consumer.Infrastructure.Modules.MediatRModule",
"properties": {
}
}
]
}

View File

@@ -0,0 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.Application", "Jambo.Consumer.Application\Jambo.Consumer.Application.csproj", "{601F90B1-4BE4-462E-8595-AAAA49B80405}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.Infrastructure", "Jambo.Consumer.Infrastructure\Jambo.Consumer.Infrastructure.csproj", "{645C9138-DA59-48C8-A15E-D720874DD148}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Consumer.UI", "Jambo.Consumer.UI\Jambo.Consumer.UI.csproj", "{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Domain", "..\Shared\Jambo.Domain\Jambo.Domain.csproj", "{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Jambo.Domain.UnitTests", "..\Shared\Jambo.Domain.UnitTests\Jambo.Domain.UnitTests.csproj", "{04732413-3F55-4194-B406-2F84666B1F6C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Debug|Any CPU.Build.0 = Debug|Any CPU
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Release|Any CPU.ActiveCfg = Release|Any CPU
{601F90B1-4BE4-462E-8595-AAAA49B80405}.Release|Any CPU.Build.0 = Release|Any CPU
{645C9138-DA59-48C8-A15E-D720874DD148}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{645C9138-DA59-48C8-A15E-D720874DD148}.Debug|Any CPU.Build.0 = Debug|Any CPU
{645C9138-DA59-48C8-A15E-D720874DD148}.Release|Any CPU.ActiveCfg = Release|Any CPU
{645C9138-DA59-48C8-A15E-D720874DD148}.Release|Any CPU.Build.0 = Release|Any CPU
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E86878A7-CB0F-46A7-B918-BED5C45BA4C5}.Release|Any CPU.Build.0 = Release|Any CPU
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0475864-44AC-4A6A-9970-CC5EEA01C2CF}.Release|Any CPU.Build.0 = Release|Any CPU
{04732413-3F55-4194-B406-2F84666B1F6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04732413-3F55-4194-B406-2F84666B1F6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04732413-3F55-4194-B406-2F84666B1F6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04732413-3F55-4194-B406-2F84666B1F6C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {54AE4BA0-6081-4892-A9B9-3F8D15D994C8}
EndGlobalSection
EndGlobal

View File

@@ -1,14 +0,0 @@
using Jambo.Domain.ServiceBus;
using MediatR;
using System;
using System.Collections.Generic;
using System.Text;
namespace Jambo.Application.DomainEventHandlers
{
public interface IEvent<T> : IRequest<T>
where T : DomainEvent
{
}
}

View File

@@ -1,26 +0,0 @@
using MediatR;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Text;
namespace Jambo.Auth.Application.Commands
{
[DataContract]
public class LoginCommand : IRequest
{
[Required]
[DataMember]
public string Username { get; private set; }
[Required]
[DataMember]
public string Password { get; private set; }
public LoginCommand()
{
}
}
}

View File

@@ -1,8 +0,0 @@
using System;
namespace Jambo.Auth.IoC
{
public class Class1
{
}
}

View File

@@ -1,7 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -1,81 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Jambo.Auth.Application.Auth;
namespace Jambo.Auth.WebAPI
{
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.AddMvc();
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "Auth API",
Version = "v1",
Description = "The Auth API",
TermsOfService = "Terms Of Service"
});
});
services.AddScoped(
s => new Config(Configuration.GetSection("Security").GetValue<string>("SecretKey"),
Configuration.GetSection("Security").GetValue<string>("Issuer")));
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration.GetSection("Security").GetValue<string>("Issuer"),
ValidAudience = Configuration.GetSection("Security").GetValue<string>("Issuer"),
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(
Configuration.GetSection("Security").GetValue<string>("SecretKey")))
};
});
}
// 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();
}
app.UseMvc();
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
}
}

View File

@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Jambo.ServiceBus;
using Newtonsoft.Json;
using MediatR;
using System.Reflection;
using Autofac;
using Microsoft.Extensions.DependencyInjection;
using Autofac.Extensions.DependencyInjection;
using Jambo.Consumer.IoC;
using System.Threading;
using Jambo.Consumer.Application.DomainEventHandlers.Blogs;
using Jambo.Domain.Model;
namespace Jambo.Consumer.Console
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
IServiceProvider serviceProvider;
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMediatR(typeof(BlogCreatedEventHandler).GetTypeInfo().Assembly);
ContainerBuilder container = new ContainerBuilder();
container.Populate(services);
container.RegisterModule(new ApplicationModule(
Configuration.GetSection("MongoDB").GetValue<string>("ConnectionString"),
Configuration.GetSection("MongoDB").GetValue<string>("Database")));
container.RegisterModule(new BusModule(
Configuration.GetSection("ServiceBus").GetValue<string>("ConnectionString"),
Configuration.GetSection("ServiceBus").GetValue<string>("Topic")));
serviceProvider = new AutofacServiceProvider(container.Build());
return serviceProvider;
}
public void Run()
{
IMediator mediator = serviceProvider.GetService<IMediator>();
ISubscriber subscriber = serviceProvider.GetService<ISubscriber>();
subscriber.Listen(mediator);
while (true)
{
System.Console.WriteLine(DateTime.Now.ToString());
Thread.Sleep(1000 * 60);
}
}
}
}

View File

@@ -1,11 +0,0 @@
{
"MongoDB": {
"ConnectionString": "mongodb://10.0.75.1:27017",
"Database": "jambov32"
},
"ServiceBus": {
"ConnectionString": "10.0.75.1:9092",
"Topic": "jambov32"
}
}

View File

@@ -1,26 +0,0 @@
using Autofac;
using Jambo.ServiceBus;
using Jambo.Infrastructure;
using Jambo.ServiceBus.Kafka;
using MediatR;
namespace Jambo.Consumer.IoC
{
public class BusModule : Module
{
private readonly string connectionString;
private readonly string topic;
public BusModule(string connectionString, string topic)
{
this.connectionString = connectionString;
this.topic = topic;
}
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new Config(connectionString, topic)).As<Config>().SingleInstance();
builder.RegisterType<Bus>().As<ISubscriber>().SingleInstance();
}
}
}

View File

@@ -1,25 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.6.0" />
<PackageReference Include="MediatR" Version="3.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Consumer.Application\Jambo.Consumer.Application.csproj" />
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
<ProjectReference Include="..\Jambo.Infrastructure\Jambo.Infrastructure.csproj" />
<ProjectReference Include="..\Jambo.ServiceBus.Kafka\Jambo.ServiceBus.Kafka.csproj" />
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,14 +0,0 @@
using System;
using Xunit;
namespace Jambo.Domain.UnitTests
{
public class UnitTest1
{
[Fact]
public void Test1()
{
}
}
}

View File

@@ -1,31 +0,0 @@
using Jambo.Producer.Application.Commands;
using Jambo.Producer.Application.Commands.Blogs;
using Jambo.Domain.Model.Blogs;
using MediatR;
using System;
using System.Threading.Tasks;
using Jambo.ServiceBus;
namespace Jambo.Producer.Application.CommandHandlers.Blogs
{
public class CreatePostCommandHandler : IAsyncRequestHandler<CreateBlogCommand, Guid>
{
private readonly IPublisher bus;
public CreatePostCommandHandler(IPublisher bus)
{
this.bus = bus ?? throw new ArgumentNullException(nameof(bus));
}
public async Task<Guid> Handle(CreateBlogCommand command)
{
Blog blog = Blog.Create();
blog.Start();
blog.UpdateUrl(Url.Create(command.Url));
await bus.Publish(blog.GetEvents(), command.Header);
return blog.Id;
}
}
}

View File

@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MediatR" Version="3.0.1" />
<PackageReference Include="MongoDB.Driver" Version="2.4.4" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.4.0" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,43 +0,0 @@
using Autofac;
using Jambo.Producer.Application.Queries;
using Jambo.Domain.Model.Blogs;
using Jambo.Domain.Model.Posts;
using Jambo.Infrastructure;
using Jambo.Infrastructure.Repositories;
using Jambo.Infrastructure.Repositories.Blogs;
using Jambo.Infrastructure.Repositories.Posts;
namespace Jambo.Producer.IoC
{
public class ApplicationModule : Module
{
public readonly string connectionString;
public readonly string database;
public ApplicationModule(string connectionString, string database)
{
this.connectionString = connectionString;
this.database = database;
}
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new BlogQueries(connectionString, database))
.As<IBlogQueries>();
builder.Register(c => new PostQueries(connectionString, database))
.As<IPostQueries>();
builder.Register(c => new MongoContext(connectionString, database))
.As<MongoContext>().SingleInstance();
builder.RegisterType<BlogReadOnlyRepository>()
.As<IBlogReadOnlyRepository>()
.InstancePerLifetimeScope();
builder.RegisterType<PostReadOnlyRepository>()
.As<IPostReadOnlyRepository>()
.InstancePerLifetimeScope();
}
}
}

View File

@@ -1,24 +0,0 @@
using Autofac;
using Jambo.ServiceBus;
using Jambo.ServiceBus.Kafka;
namespace Jambo.Producer.IoC
{
public class BusModule : Module
{
private readonly string connectionString;
private readonly string topic;
public BusModule(string connectionString, string topic)
{
this.connectionString = connectionString;
this.topic = topic;
}
protected override void Load(ContainerBuilder builder)
{
builder.Register(c => new Config(connectionString, topic)).As<Config>().SingleInstance();
builder.RegisterType<Bus>().As<IPublisher>().SingleInstance();
}
}
}

View File

@@ -1,25 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="4.6.0" />
<PackageReference Include="MediatR" Version="3.0.1" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jambo.Domain\Jambo.Domain.csproj" />
<ProjectReference Include="..\Jambo.Infrastructure\Jambo.Infrastructure.csproj" />
<ProjectReference Include="..\Jambo.Producer.Application\Jambo.Producer.Application.csproj" />
<ProjectReference Include="..\Jambo.ServiceBus.Kafka\Jambo.ServiceBus.Kafka.csproj" />
<ProjectReference Include="..\Jambo.ServiceBus\Jambo.ServiceBus.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,27 +0,0 @@
using Jambo.Domain.Exceptions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Jambo.Producer.WebAPI.Filters
{
public class DomainExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
BlogDomainException blogDomainException = context.Exception as BlogDomainException;
if (blogDomainException != null)
{
string json = JsonConvert.SerializeObject(blogDomainException.BusinessMessage);
context.Result = new BadRequestObjectResult(json);
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
}
}
}
}

View File

@@ -1,13 +0,0 @@
@page
@inject Jambo.Producer.Application.Queries.IBlogQueries blogQueries
@using Microsoft.AspNetCore.Mvc.RazorPages
@using System.Dynamic
<div>
<h1><a href="/">Blogs</a></h1>
@foreach (dynamic blog in blogQueries.GetBlogsAsync().Result)
{
<h2><a href="/Posts?blogId=@blog._id">@blog.url (@blog.rating)</a></h2>
}
</div>

View File

@@ -1,43 +0,0 @@
@page
@model IndexModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using System.Dynamic
@functions {
public class IndexModel : PageModel
{
public readonly Jambo.Producer.Application.Queries.IBlogQueries _blogQueries;
public readonly Jambo.Producer.Application.Queries.IPostQueries _postQueries;
public IndexModel(Jambo.Producer.Application.Queries.IBlogQueries blogQueries,
Jambo.Producer.Application.Queries.IPostQueries postQueries)
{
_blogQueries = blogQueries;
_postQueries = postQueries;
}
public void OnGet()
{
Blog = _blogQueries.GetBlogAsync(Guid.Parse(Request.Query["blogId"])).Result;
Posts = _postQueries.GetPostsAsync(Guid.Parse(Request.Query["blogId"])).Result;
}
public dynamic Blog { get; private set; }
public IEnumerable<dynamic> Posts { get; private set; }
}
}
<div>
<h1><a href="/">Blogs</a></h1>
@*<h2><a href="/Posts?blogId=@Model.Blog._Id">@Model.Blog.Url</a></h2>*@
</div>
<div>
@foreach (dynamic post in Model.Posts)
{
<div>
<h3><a href="/Posts/Single?id=@post._id">@post.title</a></h3>
<p>@post.Content</p>
</div>
}
</div>

View File

@@ -1,40 +0,0 @@
@page
@model IndexModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@using System.Dynamic
@functions {
public class IndexModel : PageModel
{
public readonly Jambo.Producer.Application.Queries.IBlogQueries _blogQueries;
public readonly Jambo.Producer.Application.Queries.IPostQueries _postQueries;
public IndexModel(Jambo.Producer.Application.Queries.IBlogQueries blogQueries,
Jambo.Producer.Application.Queries.IPostQueries postQueries)
{
_blogQueries = blogQueries;
_postQueries = postQueries;
}
public void OnGet()
{
Post = _postQueries.GetPostAsync(Guid.Parse(Request.Query["Id"])).Result;
Blog = _blogQueries.GetBlogAsync(Post.BlogId).Result;
}
public dynamic Blog { get; private set; }
public dynamic Post { get; private set; }
}
}
<div>
<h1><a href="/">Blogs</a></h1>
<h2><a href="/Posts?blogId=@Model.Blog.Id">@Model.Blog.Url</a></h2>
</div>
<div>
<h3><a href="/Posts?id=@Model.Post.Id">@Model.Post.Title</a></h3>
<p>@Model.Post.Content</p>
</div>

View File

@@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Jambo.Producer.WebAPI
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}

View File

@@ -1,43 +0,0 @@

ASP.NET MVC core dependencies have been added to the project.
(These dependencies include packages required to enable scaffolding)
However you may still need to do make changes to your project.
1. Suggested changes to Startup class:
1.1 Add a constructor:
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
1.2 Add MVC services:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
1.3 Configure web app to use use Configuration and use MVC routing:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}

View File

@@ -1,10 +0,0 @@
{
"name": "asp.net",
"private": true,
"dependencies": {
"bootstrap": "3.3.6",
"jquery": "2.2.0",
"jquery-validation": "1.14.0",
"jquery-validation-unobtrusive": "3.2.6"
}
}

View File

@@ -1,44 +0,0 @@
{
"name": "bootstrap",
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
"keywords": [
"css",
"js",
"less",
"mobile-first",
"responsive",
"front-end",
"framework",
"web"
],
"homepage": "http://getbootstrap.com",
"license": "MIT",
"moduleType": "globals",
"main": [
"less/bootstrap.less",
"dist/js/bootstrap.js"
],
"ignore": [
"/.*",
"_config.yml",
"CNAME",
"composer.json",
"CONTRIBUTING.md",
"docs",
"js/tests",
"test-infra"
],
"dependencies": {
"jquery": "1.9.1 - 2"
},
"version": "3.3.6",
"_release": "3.3.6",
"_resolution": {
"type": "version",
"tag": "v3.3.6",
"commit": "81df608a40bf0629a1dc08e584849bb1e43e0b7a"
},
"_source": "https://github.com/twbs/bootstrap.git",
"_target": "3.3.6",
"_originalSource": "bootstrap"
}

View File

@@ -1,5 +0,0 @@
Bootstrap uses [GitHub's Releases feature](https://github.com/blog/1547-release-your-software) for its changelogs.
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap.
Release announcement posts on [the official Bootstrap blog](http://blog.getbootstrap.com) contain summaries of the most noteworthy changes made in each release.

View File

@@ -1,533 +0,0 @@
/*!
* Bootstrap's Gruntfile
* http://getbootstrap.com
* Copyright 2013-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
module.exports = function (grunt) {
'use strict';
// Force use of Unix newlines
grunt.util.linefeed = '\n';
RegExp.quote = function (string) {
return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
};
var fs = require('fs');
var path = require('path');
var npmShrinkwrap = require('npm-shrinkwrap');
var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');
var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');
var getLessVarsData = function () {
var filePath = path.join(__dirname, 'less/variables.less');
var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
var parser = new BsLessdocParser(fileContent);
return { sections: parser.parseFile() };
};
var generateRawFiles = require('./grunt/bs-raw-files-generator.js');
var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');
var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });
Object.keys(configBridge.paths).forEach(function (key) {
configBridge.paths[key].forEach(function (val, i, arr) {
arr[i] = path.join('./docs/assets', val);
});
});
// Project configuration.
grunt.initConfig({
// Metadata.
pkg: grunt.file.readJSON('package.json'),
banner: '/*!\n' +
' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
' * Licensed under the <%= pkg.license %> license\n' +
' */\n',
jqueryCheck: configBridge.config.jqueryCheck.join('\n'),
jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),
// Task configuration.
clean: {
dist: 'dist',
docs: 'docs/dist'
},
jshint: {
options: {
jshintrc: 'js/.jshintrc'
},
grunt: {
options: {
jshintrc: 'grunt/.jshintrc'
},
src: ['Gruntfile.js', 'package.js', 'grunt/*.js']
},
core: {
src: 'js/*.js'
},
test: {
options: {
jshintrc: 'js/tests/unit/.jshintrc'
},
src: 'js/tests/unit/*.js'
},
assets: {
src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']
}
},
jscs: {
options: {
config: 'js/.jscsrc'
},
grunt: {
src: '<%= jshint.grunt.src %>'
},
core: {
src: '<%= jshint.core.src %>'
},
test: {
src: '<%= jshint.test.src %>'
},
assets: {
options: {
requireCamelCaseOrUpperCaseIdentifiers: null
},
src: '<%= jshint.assets.src %>'
}
},
concat: {
options: {
banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',
stripBanners: false
},
bootstrap: {
src: [
'js/transition.js',
'js/alert.js',
'js/button.js',
'js/carousel.js',
'js/collapse.js',
'js/dropdown.js',
'js/modal.js',
'js/tooltip.js',
'js/popover.js',
'js/scrollspy.js',
'js/tab.js',
'js/affix.js'
],
dest: 'dist/js/<%= pkg.name %>.js'
}
},
uglify: {
options: {
compress: {
warnings: false
},
mangle: true,
preserveComments: 'some'
},
core: {
src: '<%= concat.bootstrap.dest %>',
dest: 'dist/js/<%= pkg.name %>.min.js'
},
customize: {
src: configBridge.paths.customizerJs,
dest: 'docs/assets/js/customize.min.js'
},
docsJs: {
src: configBridge.paths.docsJs,
dest: 'docs/assets/js/docs.min.js'
}
},
qunit: {
options: {
inject: 'js/tests/unit/phantom.js'
},
files: 'js/tests/index.html'
},
less: {
compileCore: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'
},
src: 'less/bootstrap.less',
dest: 'dist/css/<%= pkg.name %>.css'
},
compileTheme: {
options: {
strictMath: true,
sourceMap: true,
outputSourceFiles: true,
sourceMapURL: '<%= pkg.name %>-theme.css.map',
sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'
},
src: 'less/theme.less',
dest: 'dist/css/<%= pkg.name %>-theme.css'
}
},
autoprefixer: {
options: {
browsers: configBridge.config.autoprefixerBrowsers
},
core: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>.css'
},
theme: {
options: {
map: true
},
src: 'dist/css/<%= pkg.name %>-theme.css'
},
docs: {
src: ['docs/assets/css/src/docs.css']
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: ['**/*.css'],
dest: 'docs/examples/'
}
},
csslint: {
options: {
csslintrc: 'less/.csslintrc'
},
dist: [
'dist/css/bootstrap.css',
'dist/css/bootstrap-theme.css'
],
examples: [
'docs/examples/**/*.css'
],
docs: {
options: {
ids: false,
'overqualified-elements': false
},
src: 'docs/assets/css/src/docs.css'
}
},
cssmin: {
options: {
// TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
// and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
compatibility: 'ie8',
keepSpecialComments: '*',
sourceMap: true,
advanced: false
},
minifyCore: {
src: 'dist/css/<%= pkg.name %>.css',
dest: 'dist/css/<%= pkg.name %>.min.css'
},
minifyTheme: {
src: 'dist/css/<%= pkg.name %>-theme.css',
dest: 'dist/css/<%= pkg.name %>-theme.min.css'
},
docs: {
src: [
'docs/assets/css/ie10-viewport-bug-workaround.css',
'docs/assets/css/src/pygments-manni.css',
'docs/assets/css/src/docs.css'
],
dest: 'docs/assets/css/docs.min.css'
}
},
csscomb: {
options: {
config: 'less/.csscomb.json'
},
dist: {
expand: true,
cwd: 'dist/css/',
src: ['*.css', '!*.min.css'],
dest: 'dist/css/'
},
examples: {
expand: true,
cwd: 'docs/examples/',
src: '**/*.css',
dest: 'docs/examples/'
},
docs: {
src: 'docs/assets/css/src/docs.css',
dest: 'docs/assets/css/src/docs.css'
}
},
copy: {
fonts: {
expand: true,
src: 'fonts/*',
dest: 'dist/'
},
docs: {
expand: true,
cwd: 'dist/',
src: [
'**/*'
],
dest: 'docs/dist/'
}
},
connect: {
server: {
options: {
port: 3000,
base: '.'
}
}
},
jekyll: {
options: {
config: '_config.yml'
},
docs: {},
github: {
options: {
raw: 'github: true'
}
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
conservativeCollapse: true,
minifyCSS: true,
minifyJS: true,
removeAttributeQuotes: true,
removeComments: true
},
expand: true,
cwd: '_gh_pages',
dest: '_gh_pages',
src: [
'**/*.html',
'!examples/**/*.html'
]
}
},
jade: {
options: {
pretty: true,
data: getLessVarsData
},
customizerVars: {
src: 'docs/_jade/customizer-variables.jade',
dest: 'docs/_includes/customizer-variables.html'
},
customizerNav: {
src: 'docs/_jade/customizer-nav.jade',
dest: 'docs/_includes/nav/customize.html'
}
},
htmllint: {
options: {
ignore: [
'Attribute "autocomplete" not allowed on element "button" at this point.',
'Attribute "autocomplete" is only allowed when the input type is "color", "date", "datetime", "datetime-local", "email", "month", "number", "password", "range", "search", "tel", "text", "time", "url", or "week".',
'Element "img" is missing required attribute "src".'
]
},
src: '_gh_pages/**/*.html'
},
watch: {
src: {
files: '<%= jshint.core.src %>',
tasks: ['jshint:core', 'qunit', 'concat']
},
test: {
files: '<%= jshint.test.src %>',
tasks: ['jshint:test', 'qunit']
},
less: {
files: 'less/**/*.less',
tasks: 'less'
}
},
sed: {
versionNumber: {
pattern: (function () {
var old = grunt.option('oldver');
return old ? RegExp.quote(old) : old;
})(),
replacement: grunt.option('newver'),
exclude: [
'dist/fonts',
'docs/assets',
'fonts',
'js/tests/vendor',
'node_modules',
'test-infra'
],
recursive: true
}
},
'saucelabs-qunit': {
all: {
options: {
build: process.env.TRAVIS_JOB_ID,
throttled: 10,
maxRetries: 3,
maxPollRetries: 4,
urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
}
}
},
exec: {
npmUpdate: {
command: 'npm update'
}
},
compress: {
main: {
options: {
archive: 'bootstrap-<%= pkg.version %>-dist.zip',
mode: 'zip',
level: 9,
pretty: true
},
files: [
{
expand: true,
cwd: 'dist/',
src: ['**'],
dest: 'bootstrap-<%= pkg.version %>-dist'
}
]
}
}
});
// These plugins provide necessary tasks.
require('load-grunt-tasks')(grunt, { scope: 'devDependencies' });
require('time-grunt')(grunt);
// Docs HTML validation task
grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);
var runSubset = function (subset) {
return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
};
var isUndefOrNonZero = function (val) {
return val === undefined || val !== '0';
};
// Test task.
var testSubtasks = [];
// Skip core tests if running a different subset of the test suite
if (runSubset('core') &&
// Skip core tests if this is a Savage build
process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']);
}
// Skip HTML validation if running a different subset of the test suite
if (runSubset('validate-html') &&
// Skip HTML5 validator on Travis when [skip validator] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
testSubtasks.push('validate-html');
}
// Only run Sauce Labs tests if there's a Sauce access key
if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
// Skip Sauce if running a different subset of the test suite
runSubset('sauce-js-unit') &&
// Skip Sauce on Travis when [skip sauce] is in the commit message
isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
testSubtasks.push('connect');
testSubtasks.push('saucelabs-qunit');
}
grunt.registerTask('test', testSubtasks);
grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);
// JS distribution task.
grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);
// CSS distribution task.
grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);
grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']);
// Full distribution task.
grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);
// Default task.
grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']);
// Version numbering task.
// grunt change-version-number --oldver=A.B.C --newver=X.Y.Z
// This can be overzealous, so its changes should always be manually reviewed!
grunt.registerTask('change-version-number', 'sed');
grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); });
// task for building customizer
grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);
grunt.registerTask('build-customizer-html', 'jade');
grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {
var banner = grunt.template.process('<%= banner %>');
generateRawFiles(grunt, banner);
});
grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {
var srcFiles = grunt.config.get('concat.bootstrap.src');
var destFilepath = 'dist/js/npm.js';
generateCommonJSModule(grunt, srcFiles, destFilepath);
});
// Docs task.
grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);
grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);
grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);
grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);
grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']);
grunt.registerTask('prep-release', ['dist', 'docs', 'jekyll:github', 'htmlmin', 'compress']);
// Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json).
// This task should be run and the updated file should be committed whenever Bootstrap's dependencies change.
grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']);
grunt.registerTask('_update-shrinkwrap', function () {
var done = this.async();
npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) {
if (err) {
grunt.fail.warn(err);
}
var dest = 'test-infra/npm-shrinkwrap.json';
fs.renameSync('npm-shrinkwrap.json', dest);
grunt.log.writeln('File ' + dest.cyan + ' updated.');
done();
});
});
};

View File

@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2011-2015 Twitter, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,139 +0,0 @@
# [Bootstrap](http://getbootstrap.com)
[![Slack](https://bootstrap-slack.herokuapp.com/badge.svg)](https://bootstrap-slack.herokuapp.com)
![Bower version](https://img.shields.io/bower/v/bootstrap.svg)
[![npm version](https://img.shields.io/npm/v/bootstrap.svg)](https://www.npmjs.com/package/bootstrap)
[![Build Status](https://img.shields.io/travis/twbs/bootstrap/master.svg)](https://travis-ci.org/twbs/bootstrap)
[![devDependency Status](https://img.shields.io/david/dev/twbs/bootstrap.svg)](https://david-dm.org/twbs/bootstrap#info=devDependencies)
[![NuGet](https://img.shields.io/nuget/v/bootstrap.svg)](https://www.nuget.org/packages/Bootstrap)
[![Selenium Test Status](https://saucelabs.com/browser-matrix/bootstrap.svg)](https://saucelabs.com/u/bootstrap)
Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thornton](https://twitter.com/fat), and maintained by the [core team](https://github.com/orgs/twbs/people) with the massive support and involvement of the community.
To get started, check out <http://getbootstrap.com>!
## Table of contents
* [Quick start](#quick-start)
* [Bugs and feature requests](#bugs-and-feature-requests)
* [Documentation](#documentation)
* [Contributing](#contributing)
* [Community](#community)
* [Versioning](#versioning)
* [Creators](#creators)
* [Copyright and license](#copyright-and-license)
## Quick start
Several quick start options are available:
* [Download the latest release](https://github.com/twbs/bootstrap/archive/v3.3.6.zip).
* Clone the repo: `git clone https://github.com/twbs/bootstrap.git`.
* Install with [Bower](http://bower.io): `bower install bootstrap`.
* Install with [npm](https://www.npmjs.com): `npm install bootstrap`.
* Install with [Meteor](https://www.meteor.com): `meteor add twbs:bootstrap`.
* Install with [Composer](https://getcomposer.org): `composer require twbs/bootstrap`.
Read the [Getting started page](http://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more.
### What's included
Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this:
```
bootstrap/
├── css/
│ ├── bootstrap.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── bootstrap-theme.css
│ ├── bootstrap-theme.css.map
│ ├── bootstrap-theme.min.css
│ └── bootstrap-theme.min.css.map
├── js/
│ ├── bootstrap.js
│ └── bootstrap.min.js
└── fonts/
├── glyphicons-halflings-regular.eot
├── glyphicons-halflings-regular.svg
├── glyphicons-halflings-regular.ttf
├── glyphicons-halflings-regular.woff
└── glyphicons-halflings-regular.woff2
```
We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). CSS [source maps](https://developer.chrome.com/devtools/docs/css-preprocessors) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Fonts from Glyphicons are included, as is the optional Bootstrap theme.
## Bugs and feature requests
Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new).
## Documentation
Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at <http://getbootstrap.com>. The docs may also be run locally.
### Running documentation locally
1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v3.0.x).
**Note for Windows users:** Read [this unofficial guide](http://jekyll-windows.juthilo.com/) to get Jekyll up and running without problems.
2. Install the Ruby-based syntax highlighter, [Rouge](https://github.com/jneen/rouge), with `gem install rouge`.
3. From the root `/bootstrap` directory, run `jekyll serve` in the command line.
4. Open `http://localhost:9001` in your browser, and voilà.
Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/).
### Documentation for previous releases
Documentation for v2.3.2 has been made available for the time being at <http://getbootstrap.com/2.3.2/> while folks transition to Bootstrap 3.
[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download.
## Contributing
Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development.
Moreover, if your pull request contains JavaScript patches or features, you must include [relevant unit tests](https://github.com/twbs/bootstrap/tree/master/js/tests). All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo).
Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>.
## Community
Get updates on Bootstrap's development and chat with the project maintainers and community members.
* Follow [@getbootstrap on Twitter](https://twitter.com/getbootstrap).
* Read and subscribe to [The Official Bootstrap Blog](http://blog.getbootstrap.com).
* Join [the official Slack room](https://bootstrap-slack.herokuapp.com).
* Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel.
* Implementation help may be found at Stack Overflow (tagged [`twitter-bootstrap-3`](https://stackoverflow.com/questions/tagged/twitter-bootstrap-3)).
* Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability.
## Versioning
For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](http://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible.
See [the Releases section of our GitHub project](https://github.com/twbs/bootstrap/releases) for changelogs for each release version of Bootstrap. Release announcement posts on [the official Bootstrap blog](http://blog.getbootstrap.com) contain summaries of the most noteworthy changes made in each release.
## Creators
**Mark Otto**
* <https://twitter.com/mdo>
* <https://github.com/mdo>
**Jacob Thornton**
* <https://twitter.com/fat>
* <https://github.com/fat>
## Copyright and license
Code and documentation copyright 2011-2015 Twitter, Inc. Code released under [the MIT license](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE).

View File

@@ -1,34 +0,0 @@
{
"name": "bootstrap",
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
"keywords": [
"css",
"js",
"less",
"mobile-first",
"responsive",
"front-end",
"framework",
"web"
],
"homepage": "http://getbootstrap.com",
"license": "MIT",
"moduleType": "globals",
"main": [
"less/bootstrap.less",
"dist/js/bootstrap.js"
],
"ignore": [
"/.*",
"_config.yml",
"CNAME",
"composer.json",
"CONTRIBUTING.md",
"docs",
"js/tests",
"test-infra"
],
"dependencies": {
"jquery": "1.9.1 - 2"
}
}

View File

@@ -1,587 +0,0 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #e8e8e8;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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