├── database ├── migrations │ ├── .gitkeep │ ├── 2015_09_21_230239_create_failed_jobs_table.php │ └── 2015_09_21_230253_create_jobs_table.php └── seeds │ └── DatabaseSeeder.php ├── resources ├── views │ ├── .gitkeep │ ├── app.blade.php │ └── slack │ │ └── index.blade.php └── lang │ ├── en │ ├── messages.php │ ├── slackin.php │ └── validation.php │ ├── fr_FR │ ├── messages.php │ ├── slackin.php │ └── validation.php │ ├── pt_BR │ ├── messages.php │ ├── slackin.php │ └── validation.php │ └── de_DE │ ├── messages.php │ ├── slackin.php │ └── validation.php ├── app ├── Console │ ├── Commands │ │ ├── .gitkeep │ │ ├── SlackTeamInfoCommand.php │ │ ├── SlackStatusCommand.php │ │ └── Command.php │ └── Kernel.php ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── BadgeController.php │ │ └── IndexController.php │ └── routes.php ├── Providers │ ├── AppServiceProvider.php │ ├── TranslatorServiceProvider.php │ └── ValidationServiceProvider.php ├── Jobs │ ├── Job.php │ └── SlackInvitationJob.php ├── Exceptions │ └── Handler.php └── Services │ ├── SlackTeamService.php │ ├── SlackBadgeService.php │ ├── SlackChannelsService.php │ └── SlackStatusService.php ├── storage ├── logs │ └── .gitignore ├── app │ └── .gitignore └── framework │ ├── cache │ └── .gitignore │ ├── views │ └── .gitignore │ └── sessions │ └── .gitignore ├── config ├── slack-badge.php └── services.php ├── public ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── build │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── rev-manifest.json │ └── js │ │ ├── app.min-165f27c9.js │ │ └── app.min.js.map ├── css │ ├── app.css │ ├── bootstrap-theme.css │ └── bootstrap-theme.css.map ├── .htaccess ├── index.php └── js │ ├── app.min.js │ ├── app.js │ └── app.min.js.map ├── .gitignore ├── tests ├── ExampleTest.php └── TestCase.php ├── server.php ├── package.json ├── phpunit.xml ├── gulpfile.js ├── composer.json ├── compat_l5.php ├── artisan ├── .env.example ├── bootstrap └── app.php └── readme.md /database/migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/Console/Commands/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /config/slack-badge.php: -------------------------------------------------------------------------------- 1 | 'F1504F', 5 | 'format' => 'flat', // allowed 'flat' and 'plastic' 6 | ]; 7 | -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lumen.log 2 | /.idea 3 | /.idea/workspace.xml 4 | /vendor 5 | /workbench 6 | /node_modules 7 | /studio.json 8 | /.rocketeer 9 | .env 10 | -------------------------------------------------------------------------------- /public/build/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/build/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/build/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/build/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/build/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/build/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/build/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vluzrmos/lumen-slackin/HEAD/public/build/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /public/build/rev-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "css/all.css": "css/all-72a7a3ba.css", 3 | "js/all.js": "js/all-2c032cc6.js", 4 | "js/app.min.js": "js/app.min-165f27c9.js" 5 | } -------------------------------------------------------------------------------- /app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'token' => env('SLACK_TOKEN'), 6 | 'ssl_verify' => false, 7 | 8 | 'channels' => 'all', 9 | 'resend' => env('SLACK_RESEND_INVITATION'), 10 | ], 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/ExampleTest.php: -------------------------------------------------------------------------------- 1 | call('GET', '/'); 11 | 12 | $this->assertResponseOk(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | call('UserTableSeeder'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /resources/lang/en/messages.php: -------------------------------------------------------------------------------- 1 | 'Made with by Vluzrmos + Lumen Framework, fork me on Github.', 5 | ]; 6 | -------------------------------------------------------------------------------- /resources/lang/fr_FR/messages.php: -------------------------------------------------------------------------------- 1 | 'Créé avec par Vluzrmos + Lumen Framework, forkez moi sur Github.', 5 | ]; 6 | -------------------------------------------------------------------------------- /resources/lang/pt_BR/messages.php: -------------------------------------------------------------------------------- 1 | 'Produzido com por Vluzrmos + Lumen Framework, contribua no Github.', 5 | ]; 6 | -------------------------------------------------------------------------------- /resources/lang/de_DE/messages.php: -------------------------------------------------------------------------------- 1 | 'Mit Liebe gemacht von Vluzrmos + Lumen Framework - Fork auf Github.', 5 | ]; 6 | -------------------------------------------------------------------------------- /app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews 4 | 5 | 6 | AddType image/svg+xml svg 7 | AddType image/svg+xml svgz 8 | 9 | RewriteEngine On 10 | 11 | # Redirect Trailing Slashes... 12 | RewriteRule ^(.*)/$ /$1 [L,R=301] 13 | 14 | # Handle Front Controller... 15 | RewriteCond %{REQUEST_FILENAME} !-d 16 | RewriteCond %{REQUEST_FILENAME} !-f 17 | RewriteRule ^ index.php [L] 18 | 19 | -------------------------------------------------------------------------------- /app/Providers/TranslatorServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton( 17 | 'Symfony\Component\Translation\TranslatorInterface', function () { 18 | return $this->app['translator']; 19 | } 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/Http/routes.php: -------------------------------------------------------------------------------- 1 | get('/', 'IndexController@getIndex'); 15 | $app->post('/invite', 'IndexController@postInvite'); 16 | $app->get('/badge.svg', 'BadgeController@generate'); 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lumen-slackin", 3 | "version": "0.0.0", 4 | "description": "A Lumen Slack Invitator", 5 | "directories": { 6 | "test": "tests" 7 | }, 8 | "dependencies": { 9 | "dotenv": "^1.1.0", 10 | "gulp": "^3.8.8", 11 | "laravel-elixir": "^1.0.0" 12 | }, 13 | "devDependencies": {}, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git@gitlab.vluzrmos.com.br:vluzrmos/lumen-slackin.git" 20 | }, 21 | "keywords": [ 22 | "Lumen", 23 | "Slack" 24 | ], 25 | "author": "Vagner do Carmo", 26 | "license": "DBAD" 27 | } 28 | -------------------------------------------------------------------------------- /app/Http/Controllers/BadgeController.php: -------------------------------------------------------------------------------- 1 | generate($request->get('format', 'flat')); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/Jobs/SlackInvitationJob.php: -------------------------------------------------------------------------------- 1 | email = $email; 16 | $this->username = $username; 17 | } 18 | 19 | /** 20 | * Event Self-Handled handle. 21 | * 22 | * @param SlackTeamService $slack 23 | */ 24 | public function handle(SlackTeamService $slack) 25 | { 26 | $slack->inviteMember($this->email, $this->username); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /database/migrations/2015_09_21_230239_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 17 | $table->text('connection'); 18 | $table->text('queue'); 19 | $table->longText('payload'); 20 | $table->timestamp('failed_at'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::drop('failed_jobs'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /resources/lang/en/slackin.php: -------------------------------------------------------------------------------- 1 | 'Done!', 5 | 6 | 'invited' => 'Invitation sent, please verify your inbox mail!', 7 | 8 | 'join' => 'Join on :team on Slack.', 9 | 10 | 'placeholders' => [ 11 | 'username' => 'Enter your name and last name', 12 | 'email' => 'example@example.com', 13 | ], 14 | 15 | 'submit' => 'Join', 16 | 17 | 'updating_status' => 'Checking for new status...', 18 | 19 | 'updating_team_info' => 'Checking slack team info...', 20 | 21 | 'users_online' => '{0} There is no online users of :total registred.|{1} There is :active online user of :total registred.|[2,Inf] There are :active online users of :total registred.', 22 | 23 | ]; 24 | -------------------------------------------------------------------------------- /resources/lang/pt_BR/slackin.php: -------------------------------------------------------------------------------- 1 | 'Pronto!', 5 | 6 | 'invited' => 'Convite enviado, por favor verifique sua caixa de email!', 7 | 8 | 'join' => 'Entre no Slack do :team.', 9 | 10 | 'placeholders' => [ 11 | 'username' => 'Digite seu nome e sobrenome', 12 | 'email' => 'exemplo@exemplo.com', 13 | ], 14 | 15 | 'submit' => 'Entrar', 16 | 17 | 'updating_status' => 'Verificando novo status...', 18 | 19 | 'updating_team_info' => 'Verificando informações do Slack Team...', 20 | 21 | 'users_online' => '{0} Nenhum usuário online de :total registrados.|{1} Há :active usuário online de :total registrados.|[2,Inf] Há :active usuários online de :total registrados.', 22 | ]; 23 | -------------------------------------------------------------------------------- /resources/lang/de_DE/slackin.php: -------------------------------------------------------------------------------- 1 | 'Erledigt!', 5 | 6 | 'invited' => 'Einladung gesendet, bitte Überprüfe deinen Posteingang!', 7 | 8 | 'join' => 'Trete :team auf Slack bei.', 9 | 10 | 'placeholders' => [ 11 | 'username' => 'Gib deinen Vor- und Nachnamen ein.', 12 | 'email' => 'beispiel@example.com', 13 | ], 14 | 15 | 'submit' => 'Beitreten', 16 | 17 | 'updating_status' => 'Prüfe auf neuen Status...', 18 | 19 | 'updating_team_info' => 'Prüfe Slack Teaminformation...', 20 | 21 | 'users_online' => '{0} Zur Zeit ist von :total registrierten Nutzern keiner online.|{1} Es ist zur Zeit :active Nutzer von :total registrierten Nutzern online.|[2,Inf] Es sind zur Zeit :active von :total registrierten Nuztern online.', 22 | 23 | ]; 24 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | run(); 29 | -------------------------------------------------------------------------------- /resources/lang/fr_FR/slackin.php: -------------------------------------------------------------------------------- 1 | 'Effectué !', 5 | 6 | 'invited' => 'Invitation envoyée, veuillez consulter votre boite de réception email !', 7 | 8 | 'join' => 'Rejoignez :team on Slack.', 9 | 10 | 'placeholders' => [ 11 | 'username' => 'Entrez votre prénom et nom', 12 | 'email' => 'exemple@exemple.com', 13 | ], 14 | 15 | 'submit' => 'Rejoindre', 16 | 17 | 'updating_status' => 'Récupération des status à jour...', 18 | 19 | 'updating_team_info' => 'Vérification des information de la team Slack...', 20 | 21 | 'users_online' => "{0} Aucun des :total utilisateurs enregistrés n'est en ligne.|{1} :active des :total utilisateurs enregistrés est connecté.|[2,Inf] :active des :total utilisateurs enregistrés sont connectés.", 22 | 23 | ]; 24 | -------------------------------------------------------------------------------- /public/js/app.min.js: -------------------------------------------------------------------------------- 1 | !function(e){window.app=window.app||{},app.config=app.config||{debug:!1},e.fn.serializeObject=function(){var a={},s=this.serializeArray();return e.each(s,function(){a[this.name]?(a[this.name].push||(a[this.name]=[a[this.name]]),a[this.name].push(this.value||"")):a[this.name]=this.value||""}),a},e(document).ready(function(){e("#form-invite").find("form").submit(function(a){a.preventDefault();var s=e(this),i=s.serializeObject();e.ajax({url:e(this).attr("action"),type:"post",data:i,dataType:"json",success:function(a){s.hide(),e("#validation-message").removeClass("text-danger").addClass("text-success").html(a.message)},error:function(a){var i=a.responseJSON;if(void 0!=i){if(i.email){var t=e('[name="email"]',s);t.parents(".form-group").addClass("has-error"),t.siblings(".help-block").html(i.email)}if(i.username){var n=e('[name="username"]',s);n.parents(".form-group").addClass("has-error"),n.siblings(".help-block").html(i.username)}}e("#validation-message").removeClass("text-success").addClass("text-danger").html(Lang.get("validation.wrong"))}})})})}(jQuery); -------------------------------------------------------------------------------- /public/build/js/app.min-165f27c9.js: -------------------------------------------------------------------------------- 1 | !function(e){window.app=window.app||{},app.config=app.config||{debug:!1},e.fn.serializeObject=function(){var a={},s=this.serializeArray();return e.each(s,function(){a[this.name]?(a[this.name].push||(a[this.name]=[a[this.name]]),a[this.name].push(this.value||"")):a[this.name]=this.value||""}),a},e(document).ready(function(){e("#form-invite").find("form").submit(function(a){a.preventDefault();var s=e(this),i=s.serializeObject();e.ajax({url:e(this).attr("action"),type:"post",data:i,dataType:"json",success:function(a){s.hide(),e("#validation-message").removeClass("text-danger").addClass("text-success").html(a.message)},error:function(a){var i=a.responseJSON;if(void 0!=i){if(i.email){var t=e('[name="email"]',s);t.parents(".form-group").addClass("has-error"),t.siblings(".help-block").html(i.email)}if(i.username){var n=e('[name="username"]',s);n.parents(".form-group").addClass("has-error"),n.siblings(".help-block").html(i.username)}}e("#validation-message").removeClass("text-success").addClass("text-danger").html(Lang.get("validation.wrong"))}})})})}(jQuery); -------------------------------------------------------------------------------- /app/Console/Commands/SlackTeamInfoCommand.php: -------------------------------------------------------------------------------- 1 | slack = $slack; 34 | } 35 | 36 | /** 37 | * Performs the event. 38 | */ 39 | public function fire() 40 | { 41 | $this->info(trans('slackin.updating_team_info')); 42 | 43 | $info = $this->slack->refreshTeamInfo(); 44 | 45 | $this->horizontalTable($info); 46 | 47 | $this->info(trans('slackin.command_done')); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /database/migrations/2015_09_21_230253_create_jobs_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 17 | $table->string('queue'); 18 | $table->longText('payload'); 19 | $table->tinyInteger('attempts')->unsigned(); 20 | $table->tinyInteger('reserved')->unsigned(); 21 | $table->unsignedInteger('reserved_at')->nullable(); 22 | $table->unsignedInteger('available_at'); 23 | $table->unsignedInteger('created_at'); 24 | $table->index(['queue', 'reserved', 'reserved_at']); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::drop('jobs'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"); 2 | var elixir = require('laravel-elixir'); 3 | 4 | /* 5 | |-------------------------------------------------------------------------- 6 | | Elixir Asset Management 7 | |-------------------------------------------------------------------------- 8 | | 9 | | Elixir provides a clean, fluent API for defining some basic Gulp tasks 10 | | for your Laravel application. By default, we are compiling the Less 11 | | file for our application, as well as publishing vendor resources. 12 | | 13 | */ 14 | 15 | elixir(function(mix) { 16 | mix.scripts([ 17 | 'jquery.js', 18 | 'bootstrap.js' 19 | ], 'public/js/all.js', 'public/js'); 20 | 21 | mix.scripts(['app.js'], 'public/js/app.min.js', 'public/js'); 22 | 23 | mix.styles([ 24 | 'bootstrap.css', 25 | 'bootstrap-theme.css', 26 | 'app.css' 27 | ], 'public/css/all.css', 'public/css'); 28 | 29 | mix.version([ 30 | 'public/css/all.css', 31 | 'public/js/all.js', 32 | 'public/js/app.min.js' 33 | ]); 34 | 35 | mix.copy('public/fonts', 'public/build/fonts'); 36 | }); 37 | 38 | 39 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vluzrmos/lumen-slackin", 3 | "description": "Lumen Slack Invitator.", 4 | "keywords": ["Slack", "lumen", "laravel"], 5 | "license": "DBAD", 6 | "type": "project", 7 | "require": { 8 | "ext-gd": "*", 9 | "illuminate/redis": "5.1.*", 10 | "laravel/lumen-framework": "5.1.*", 11 | "predis/predis": "1.0.*", 12 | "vlucas/phpdotenv": "1.0.*", 13 | "vluzrmos/badge-poser": "1.0.*", 14 | "vluzrmos/language-detector": "1.0.*", 15 | "vluzrmos/slack-api": "0.4.*", 16 | "vluzrmos/tinker":"1.1.*" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "~4.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "App\\": "app/" 24 | }, 25 | "classmap": [ 26 | "database/" 27 | ], 28 | "files":[ 29 | "compat_l5.php" 30 | ] 31 | }, 32 | "autoload-dev": { 33 | "classmap": [ 34 | "tests/" 35 | ] 36 | }, 37 | "config": { 38 | "preferred-install": "dist" 39 | }, 40 | "minimum-stability": "stable" 41 | } 42 | -------------------------------------------------------------------------------- /compat_l5.php: -------------------------------------------------------------------------------- 1 | register('Vluzrmos\Tinker\TinkerServiceProvider'); 22 | } 23 | 24 | /* 25 | |-------------------------------------------------------------------------- 26 | | Run The Artisan Application 27 | |-------------------------------------------------------------------------- 28 | | 29 | | When we run the console application, the current CLI command will be 30 | | executed in this console and the response sent back to a terminal 31 | | or another output device for the developers. Here goes nothing! 32 | | 33 | */ 34 | 35 | $kernel = $app->make( 36 | 'Illuminate\Contracts\Console\Kernel' 37 | ); 38 | 39 | exit($kernel->handle(new ArgvInput, new ConsoleOutput)); 40 | -------------------------------------------------------------------------------- /app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('slack:status')->everyMinute()->withoutOverlapping(); 31 | 32 | // Remove previous cache of "withoutOverlapping" 33 | $schedule->call(function () { 34 | $files = Finder::create()->files() 35 | ->in(storage_path('framework')) 36 | ->depth(0) 37 | ->date('< today') 38 | ->name('schedule-*'); 39 | 40 | foreach ($files as $file) { 41 | @unlink($file->getRealPath()); 42 | } 43 | })->daily(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/Console/Commands/SlackStatusCommand.php: -------------------------------------------------------------------------------- 1 | slack = $slack; 34 | } 35 | 36 | /** 37 | * Performs the event. 38 | */ 39 | public function fire() 40 | { 41 | $this->info(trans('slackin.updating_status')); 42 | 43 | $status = $this->slack->refreshUsersStatus(); 44 | 45 | $this->infoStatusUser($status); 46 | 47 | $this->info(trans('slackin.command_done')); 48 | } 49 | 50 | /** 51 | * Show info message about status of users 52 | * 53 | * @param array $status 54 | */ 55 | public function infoStatusUser(array $status) 56 | { 57 | $message = strip_tags(trans_choice('slackin.users_online', $status['active'], $status)); 58 | 59 | $this->info($message); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/Services/SlackTeamService.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 34 | 35 | $this->slack = $slack; 36 | 37 | $this->channels = $channels; 38 | } 39 | 40 | /** 41 | * Invite a new member to slack team. 42 | * 43 | * @param string $email 44 | * @param string $username 45 | */ 46 | public function inviteMember($email, $username = '') 47 | { 48 | $options = [ 49 | 'first_name' => $username, 50 | 'channels' => $this->channels->getConfigChannelsString(), 51 | ]; 52 | $resend = config('services.slack.resend'); 53 | if ($resend === true) { 54 | $options['resend'] = 'true'; 55 | } 56 | $this->slack->invite($email, $options); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /resources/views/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @if($team['name']){{$team['name']}}@else{{'Lumen Slackin'}}@endif 7 | 8 | 9 | 10 | @if($team['icon']) 11 | 12 | @endif 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 |
36 | @yield('content') 37 |
38 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | APP_ENV=production 2 | APP_DEBUG=false 3 | APP_KEY=SomethingRandom!!! 4 | 5 | APP_LOCALE=en 6 | APP_FALLBACK_LOCALE=en 7 | 8 | CACHE_DRIVER=file 9 | SESSION_DRIVER=file 10 | QUEUE_DRIVER=sync 11 | 12 | # (REQUIRED) Your Team Slack Token 13 | SLACK_TOKEN=YOUR-SLACK-TOKEN-WITH-ADMIN-PRIVILEGIES 14 | 15 | # (OPTIONAL) Should the invitation email be sent again if the invitation is already pending for this email address? 16 | # SLACK_RESEND_INVITATION=true 17 | 18 | # (OPTIONAL) Indicates if should show the slack status message with users ative/total registered. 19 | SLACK_STATUS_ENABLED=true 20 | 21 | ##################################################################################### 22 | ## Language Detector Configurations ## 23 | ## see https://github.com/vluzrmos/laravel-language-detector ## 24 | ##################################################################################### 25 | 26 | #Indicates whenever should autodetect the language (it could be removed) 27 | LANG_DETECTOR_AUTODETECT=true 28 | 29 | #The driver to use, default is browser 30 | LANG_DETECTOR_DRIVER="browser" 31 | 32 | #The segment to use in uri or subdomain driver, default 0 (it could be removed) 33 | LANG_DETECTOR_SEGMENT=0 34 | 35 | #A comma-separated list of available languages on application 36 | LANG_DETECTOR_LANGUAGES="en,fr_FR,pt_BR,de_DE" 37 | 38 | #To aliase the language use the notation ":", "=", ":=" or "=>" to separate the alias and its value. 39 | # LANG_DETECTOR_LANGUAGES="en, en-us:en, de:de_DE, fr:fr_FR, pt-br:pt_BR" 40 | -------------------------------------------------------------------------------- /app/Providers/ValidationServiceProvider.php: -------------------------------------------------------------------------------- 1 | app['validator']->extend( 26 | 'min_words', function ($attribute, $value, $parameters) { 27 | return str_word_count($value) >= array_get($parameters, 0, 1); 28 | } 29 | ); 30 | 31 | /* 32 | * Validate a maximun words required 33 | * @example max_words:10 34 | */ 35 | $this->app['validator']->extend( 36 | 'max_words', function ($attribute, $value, $parameters) { 37 | return str_word_count($value) <= array_get($parameters, 0, 1); 38 | } 39 | ); 40 | 41 | /* 42 | * Replace the :words in messages for min_words rules. 43 | */ 44 | $this->app['validator']->replacer( 45 | 'min_words', function ($message, $attribute, $rule, $parameters) { 46 | return str_replace(':words', $parameters[0], $message); 47 | } 48 | ); 49 | 50 | /* 51 | * Replace the :words in messages for max_words rules. 52 | */ 53 | $this->app['validator']->replacer( 54 | 'max_words', function ($message, $attribute, $rule, $parameters) { 55 | return str_replace(':words', $parameters[0], $message); 56 | } 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resources/views/slack/index.blade.php: -------------------------------------------------------------------------------- 1 | @extends('app') 2 | 3 | @section('content') 4 |
5 |
6 | 11 | 12 |
13 | {!! trans('slackin.join', ['team' => $team['name'], 'domain' => $team['domain']]) !!} 14 |
15 | 16 | @if(isset($totals, $totals['active'], $totals['total'])) 17 |
18 | {!! trans_choice('slackin.users_online', $totals['active'], $totals) !!} 19 |
20 | @endif 21 | 22 |
23 |
24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 | 32 |
33 |
34 | 35 |
36 | 37 |
38 | 39 |
40 |
41 | 42 |
43 | 44 |
45 |
46 |
47 |
48 |
49 | @endsection 50 | -------------------------------------------------------------------------------- /app/Http/Controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 33 | $this->slack = $slack; 34 | } 35 | 36 | /** 37 | * @return \Illuminate\View\View 38 | */ 39 | public function getIndex() 40 | { 41 | $data = [ 42 | 'team' => $this->slack->getTeamInfo(), 43 | ]; 44 | 45 | if (env('SLACK_STATUS_ENABLED', true)) { 46 | $data['totals'] = $this->slack->getUsersStatus(); 47 | } 48 | 49 | return view('slack.index', $data); 50 | } 51 | 52 | /** 53 | * @param Request $request 54 | * 55 | * @return array 56 | */ 57 | public function postInvite(Request $request) 58 | { 59 | $this->validate( 60 | $request, 61 | [ 62 | 'username' => 'required|min_words:2', 63 | 'email' => 'required|email', 64 | ] 65 | ); 66 | 67 | $this->dispatch( 68 | new SlackInvitationJob( 69 | $request->get('email'), 70 | $request->get('username') 71 | ) 72 | ); 73 | 74 | return [ 75 | 'message' => trans('slackin.invited'), 76 | ]; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/Console/Commands/Command.php: -------------------------------------------------------------------------------- 1 | $value) { 22 | $lines[] = $this->getTableLine($key, $value); 23 | } 24 | 25 | $linesMaxLength = $this->getStringsMaxLength($lines); 26 | 27 | $this->writeTableLine($linesMaxLength); 28 | 29 | foreach ($lines as $line) { 30 | $this->output->writeln($line); 31 | } 32 | 33 | $this->writeTableLine($linesMaxLength); 34 | } 35 | 36 | /** 37 | * 38 | * Get a string for a given row (header, content) 39 | * 40 | * @param string $header Title of the header 41 | * @param string $content content of the header 42 | * 43 | * @return string 44 | */ 45 | private function getTableLine($header, $content) 46 | { 47 | return sprintf("%s: %s", $header, $content); 48 | } 49 | 50 | /** 51 | * Get max length of strings on a given array of headers 52 | * 53 | * @param array $strings 54 | * 55 | * @return int 56 | */ 57 | private function getStringsMaxLength(array $strings) 58 | { 59 | $size = 0; 60 | 61 | foreach ($strings as $string) { 62 | $size = max($size, strlen($string)); 63 | } 64 | 65 | return $size; 66 | } 67 | 68 | /** 69 | * Writes a line with a given separator 70 | * @param int $length Length of the line 71 | * @param string $separator Chacactere used for the line 72 | */ 73 | private function writeTableLine($length, $separator = '-') 74 | { 75 | $this->output->writeln(substr(str_repeat($separator, $length), 0, $length)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/Services/SlackBadgeService.php: -------------------------------------------------------------------------------- 1 | slack = $slack; 30 | 31 | $this->poser = $poser; 32 | 33 | app()->configure('slack-badge'); 34 | } 35 | 36 | /** 37 | * Generate a badge poser 38 | * @param string $format 39 | * @return \Illuminate\Http\Response 40 | */ 41 | public function generate($format = 'flat') 42 | { 43 | $totals = $this->slack->getUsersStatus(); 44 | $team = $this->slack->getTeamInfo(); 45 | 46 | $subject = $team['name']; 47 | $status = $totals['active'].'/'.$totals['total']; 48 | $color = config('slack-badge.color', 'F1504'); 49 | $format = config('slack-badge.format', $format); 50 | 51 | if (!$this->isValidFormat($format)) { 52 | $format = array_get($this->getAllowedBadgesFormat(), 0, 'flat'); 53 | } 54 | 55 | $response = $this->poser->generate($subject, $status, $color, $format); 56 | 57 | return $response; 58 | } 59 | 60 | /** 61 | * Check if the badge format is valid 62 | * @param $format 63 | * @return bool 64 | */ 65 | protected function isValidFormat($format) 66 | { 67 | return in_array($format, $this->getAllowedBadgesFormat()); 68 | } 69 | 70 | /** 71 | * Return the allowed badges format 72 | * @return array 73 | */ 74 | protected function getAllowedBadgesFormat() 75 | { 76 | return $this->allowedBadgeFormats; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | window.app = window.app || {}; 3 | 4 | app.config = app.config || {debug: false}; 5 | 6 | $.fn.serializeObject = function () { 7 | var o = {}; 8 | var a = this.serializeArray(); 9 | $.each(a, function () { 10 | if (o[this.name]) { 11 | if (!o[this.name].push) { 12 | o[this.name] = [o[this.name]]; 13 | } 14 | o[this.name].push(this.value || ''); 15 | } else { 16 | o[this.name] = this.value || ''; 17 | } 18 | }); 19 | return o; 20 | }; 21 | 22 | $(document).ready(function () { 23 | $('#form-invite').find('form').submit(function (e) { 24 | e.preventDefault(); 25 | 26 | var $form = $(this); 27 | 28 | var data = $form.serializeObject(); 29 | 30 | $.ajax({ 31 | "url": $(this).attr('action'), 32 | "type": 'post', 33 | "data": data, 34 | "dataType": 'json', 35 | 36 | "success": function (data) { 37 | $form.hide(); 38 | $('#validation-message').removeClass('text-danger').addClass('text-success').html(data.message); 39 | }, 40 | 41 | "error": function (data) { 42 | var validation = data.responseJSON; 43 | 44 | if (validation != undefined) { 45 | if (validation.email) { 46 | var emailField = $('[name="email"]', $form); 47 | 48 | emailField.parents('.form-group').addClass('has-error'); 49 | emailField.siblings('.help-block').html(validation.email); 50 | } 51 | 52 | if (validation.username) { 53 | var usernameField = $('[name="username"]', $form); 54 | 55 | usernameField.parents('.form-group').addClass('has-error'); 56 | usernameField.siblings('.help-block').html(validation.username); 57 | } 58 | } 59 | 60 | $('#validation-message').removeClass('text-success').addClass('text-danger').html(Lang.get('validation.wrong')); 61 | } 62 | }); 63 | 64 | }); 65 | }); 66 | })(jQuery); 67 | -------------------------------------------------------------------------------- /public/js/app.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["app.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"app.min.js","sourcesContent":["(function ($) {\n window.app = window.app || {};\n\n app.config = app.config || {debug: false};\n\n $.fn.serializeObject = function () {\n var o = {};\n var a = this.serializeArray();\n $.each(a, function () {\n if (o[this.name]) {\n if (!o[this.name].push) {\n o[this.name] = [o[this.name]];\n }\n o[this.name].push(this.value || '');\n } else {\n o[this.name] = this.value || '';\n }\n });\n return o;\n };\n\n $(document).ready(function () {\n $('#form-invite').find('form').submit(function (e) {\n e.preventDefault();\n\n var $form = $(this);\n\n var data = $form.serializeObject();\n\n $.ajax({\n \"url\": $(this).attr('action'),\n \"type\": 'post',\n \"data\": data,\n \"dataType\": 'json',\n\n \"success\": function (data) {\n $form.hide();\n $('#validation-message').removeClass('text-danger').addClass('text-success').html(data.message);\n },\n\n \"error\": function (data) {\n var validation = data.responseJSON;\n\n if (validation != undefined) {\n if (validation.email) {\n var emailField = $('[name=\"email\"]', $form);\n\n emailField.parents('.form-group').addClass('has-error');\n emailField.siblings('.help-block').html(validation.email);\n }\n\n if (validation.username) {\n var usernameField = $('[name=\"username\"]', $form);\n\n usernameField.parents('.form-group').addClass('has-error');\n usernameField.siblings('.help-block').html(validation.username);\n }\n }\n\n $('#validation-message').removeClass('text-success').addClass('text-danger').html(Lang.get('validation.wrong'));\n }\n });\n\n });\n });\n})(jQuery);\n"],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /public/build/js/app.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["app.js"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"app.min.js","sourcesContent":["(function ($) {\n window.app = window.app || {};\n\n app.config = app.config || {debug: false};\n\n $.fn.serializeObject = function () {\n var o = {};\n var a = this.serializeArray();\n $.each(a, function () {\n if (o[this.name]) {\n if (!o[this.name].push) {\n o[this.name] = [o[this.name]];\n }\n o[this.name].push(this.value || '');\n } else {\n o[this.name] = this.value || '';\n }\n });\n return o;\n };\n\n $(document).ready(function () {\n $('#form-invite').find('form').submit(function (e) {\n e.preventDefault();\n\n var $form = $(this);\n\n var data = $form.serializeObject();\n\n $.ajax({\n \"url\": $(this).attr('action'),\n \"type\": 'post',\n \"data\": data,\n \"dataType\": 'json',\n\n \"success\": function (data) {\n $form.hide();\n $('#validation-message').removeClass('text-danger').addClass('text-success').html(data.message);\n },\n\n \"error\": function (data) {\n var validation = data.responseJSON;\n\n if (validation != undefined) {\n if (validation.email) {\n var emailField = $('[name=\"email\"]', $form);\n\n emailField.parents('.form-group').addClass('has-error');\n emailField.siblings('.help-block').html(validation.email);\n }\n\n if (validation.username) {\n var usernameField = $('[name=\"username\"]', $form);\n\n usernameField.parents('.form-group').addClass('has-error');\n usernameField.siblings('.help-block').html(validation.username);\n }\n }\n\n $('#validation-message').removeClass('text-success').addClass('text-danger').html(Lang.get('validation.wrong'));\n }\n });\n\n });\n });\n})(jQuery);\n"],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /app/Services/SlackChannelsService.php: -------------------------------------------------------------------------------- 1 | slack = $slack; 27 | } 28 | 29 | /** 30 | * Get api configurated channels and parse to string to be used in a api request. 31 | * 32 | * @return string 33 | */ 34 | public function getConfigChannelsString() 35 | { 36 | $channels = $this->getConfig('channels', ''); 37 | 38 | if (in_array($channels, ['all', '*'])) { 39 | $apiChannels = $this->getApiChannels(); 40 | } else { 41 | $apiChannels = $this->getApiChannelsById($channels); 42 | } 43 | 44 | return $this->parseChannelsToString($apiChannels); 45 | } 46 | 47 | /** 48 | * Get Configuration for a key with a default (if that doesn't exists). 49 | * 50 | * @param $key 51 | * @param null $default 52 | * 53 | * @return mixed 54 | */ 55 | protected function getConfig($key, $default = null) 56 | { 57 | return config($this->configSpace.'.'.$key, $default); 58 | } 59 | 60 | /** 61 | * Get array of channels. 62 | * 63 | * @return array 64 | */ 65 | public function getApiChannels() 66 | { 67 | $all = $this->slack->lists(); 68 | 69 | $channels = array_filter( 70 | $all['channels'], function ($channel) { 71 | return !$channel['is_archived']; 72 | } 73 | ); 74 | 75 | return $channels; 76 | } 77 | 78 | /** 79 | * Get channels by name or ID. 80 | * 81 | * @param array $ids 82 | * 83 | * @return array 84 | */ 85 | public function getApiChannelsById($ids = []) 86 | { 87 | if (is_string($ids)) { 88 | $ids = preg_split('/, ?/', $ids); 89 | } 90 | 91 | $ids = (array)$ids; 92 | 93 | return array_filter( 94 | $this->getApiChannels(), function ($channel) use (&$ids) { 95 | return (in_array($channel['id'], $ids) or in_array($channel['name'], $ids)); 96 | } 97 | ); 98 | } 99 | 100 | /** 101 | * Parse channels IDs to string to be used in a api request. 102 | * 103 | * @param array $channels 104 | * 105 | * @return string 106 | */ 107 | public function parseChannelsToString($channels = []) 108 | { 109 | $channels = array_map( 110 | function ($channel) { 111 | return $channel['id']; 112 | }, $channels 113 | ); 114 | 115 | return implode(',', $channels); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /bootstrap/app.php: -------------------------------------------------------------------------------- 1 | withFacades(); 23 | 24 | // $app->withEloquent(); 25 | 26 | /* 27 | |-------------------------------------------------------------------------- 28 | | Register Container Bindings 29 | |-------------------------------------------------------------------------- 30 | | 31 | | Now we will register a few bindings in the service container. We will 32 | | register the exception handler and the console kernel. You may add 33 | | your own bindings here if you like or you can make another file. 34 | | 35 | */ 36 | 37 | $app->singleton( 38 | 'Illuminate\Contracts\Debug\ExceptionHandler', 39 | 'App\Exceptions\Handler' 40 | ); 41 | 42 | $app->singleton( 43 | 'Illuminate\Contracts\Console\Kernel', 44 | 'App\Console\Kernel' 45 | ); 46 | 47 | /* 48 | |-------------------------------------------------------------------------- 49 | | Register Middleware 50 | |-------------------------------------------------------------------------- 51 | | 52 | | Next, we will register the middleware with the application. These can 53 | | be global middleware that run before and after each request into a 54 | | route or middleware that'll be assigned to some specific routes. 55 | | 56 | */ 57 | 58 | $app->middleware([ 59 | 'Illuminate\Cookie\Middleware\EncryptCookies', 60 | 'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse', 61 | 'Illuminate\Session\Middleware\StartSession', 62 | 'Illuminate\View\Middleware\ShareErrorsFromSession', 63 | 'Laravel\Lumen\Http\Middleware\VerifyCsrfToken', 64 | ]); 65 | 66 | // $app->routeMiddleware([ 67 | 68 | // ]); 69 | 70 | /* 71 | |-------------------------------------------------------------------------- 72 | | Register Service Providers 73 | |-------------------------------------------------------------------------- 74 | | 75 | | Here we will register all of the application's service providers which 76 | | are used to bind services into the container. Service providers are 77 | | totally optional, so you are not required to uncomment this line. 78 | | 79 | */ 80 | 81 | $app->register('App\Providers\AppServiceProvider'); 82 | $app->register('App\Providers\ValidationServiceProvider'); 83 | $app->register('App\Providers\TranslatorServiceProvider'); 84 | $app->register('Vluzrmos\SlackApi\SlackApiServiceProvider'); 85 | $app->register('Vluzrmos\BadgePoser\BadgePoserServiceProvider'); 86 | $app->register('Vluzrmos\LanguageDetector\Providers\LanguageDetectorServiceProvider'); 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Load The Application Routes 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Next we will include the routes file so that they can all be added to 94 | | the application. This will provide all of the URLs the application 95 | | can respond to, as well as the controllers that may handle them. 96 | | 97 | */ 98 | 99 | // if you are using URI language detector driver, uncomment this line 100 | // $options['prefix'] = app('language.routePrefix'); 101 | $options['namespace'] = 'App\Http\Controllers'; 102 | 103 | $app->group($options, function () use ($app) { 104 | require __DIR__.'/../app/Http/routes.php'; 105 | }); 106 | 107 | return $app; 108 | -------------------------------------------------------------------------------- /app/Services/SlackStatusService.php: -------------------------------------------------------------------------------- 1 | cache = $cache; 39 | 40 | $this->slack = $slack; 41 | 42 | $this->slackRtm = $slackRtm; 43 | } 44 | 45 | /** 46 | * Return the previus cached users status or generate a new one. 47 | * 48 | * @return array 49 | */ 50 | public function getUsersStatus() 51 | { 52 | $cached = $this->cache->get(self::SLACK_TOTALS_KEY); 53 | 54 | if (!$cached) { 55 | $cached = $this->refreshUsersStatus(); 56 | } 57 | 58 | return $cached; 59 | } 60 | 61 | /** 62 | * @return array 63 | */ 64 | public function getTeamInfo() 65 | { 66 | /** 67 | * @var array|null $cached 68 | */ 69 | $cached = $this->cache->get(self::SLACK_TEAM_INFO_KEY); 70 | 71 | if (!$cached) { 72 | $cached = $this->refreshTeamInfo(); 73 | } 74 | 75 | return $cached; 76 | } 77 | 78 | /** 79 | * Refresh Team info by Api data. 80 | * 81 | * @return array|null 82 | */ 83 | public function refreshTeamInfo() 84 | { 85 | $info = $this->slack->info(); 86 | 87 | $this->cache->forever(self::SLACK_TEAM_INFO_KEY, $info['team']); 88 | 89 | return $info['team']; 90 | } 91 | 92 | /** 93 | * Return the total users and logged users. 94 | * 95 | * @return array 96 | */ 97 | public function refreshUsersStatus() 98 | { 99 | $rtm = $this->slackRtm->start(); 100 | 101 | $totals = $this->getEmptyStatus(); 102 | 103 | foreach ($rtm['users'] as $user) { 104 | if ($this->isRealUser($user)) { 105 | $totals['total']++; 106 | 107 | if ($this->isActiveUser($user)) { 108 | $totals['active']++; 109 | } 110 | } 111 | } 112 | 113 | $this->cache->forever(self::SLACK_TOTALS_KEY, $totals); 114 | 115 | return $totals; 116 | } 117 | 118 | /** 119 | * Get an empty array of user status. 120 | * 121 | * @return array 122 | */ 123 | protected function getEmptyStatus() 124 | { 125 | return [ 126 | 'active' => 0, 127 | 'total' => 0, 128 | ]; 129 | } 130 | 131 | /** 132 | * Tell whenever a user is active. 133 | * 134 | * @param $user 135 | * 136 | * @return bool 137 | */ 138 | protected function isActiveUser($user) 139 | { 140 | return $user['presence'] == 'active'; 141 | } 142 | 143 | /** 144 | * Check if a user is a real, and not a bot. 145 | * 146 | * @param $user 147 | * 148 | * @return bool 149 | */ 150 | protected function isRealUser($user) 151 | { 152 | return $this->isNotBot($user) and $this->isNotDeletedUser($user); 153 | } 154 | 155 | /** 156 | * Check if the user is not a bot or Slackbot 157 | * @param $user 158 | * @return bool 159 | */ 160 | protected function isNotBot($user) 161 | { 162 | return (isset($user['is_bot']) and !$user['is_bot']) and $user['id'] != 'USLACKBOT'; 163 | } 164 | 165 | /** 166 | * Check if the user is not a deleted user 167 | * @param $user 168 | * @return bool 169 | */ 170 | protected function isNotDeletedUser($user) 171 | { 172 | return $user['deleted'] == false; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## Lumen - Slackin 2 | 3 | [![Join the chat at https://gitter.im/vluzrmos/lumen-slackin](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/vluzrmos/lumen-slackin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/vluzrmos/lumen-slackin/v/stable)](https://packagist.org/packages/vluzrmos/lumen-slackin) [![Total Downloads](https://poser.pugx.org/vluzrmos/lumen-slackin/downloads)](https://packagist.org/packages/vluzrmos/lumen-slackin) [![License](https://poser.pugx.org/vluzrmos/lumen-slackin/license)](https://packagist.org/packages/vluzrmos/lumen-slackin) 6 | 7 | A Slack Invitator made with Lumen Framework and inspired by [rauchg/slackin](https://github.com/rauchg/slackin). 8 | 9 | That application uses some of my awesome packages: 10 | 11 | * [Badge Poser](https://github.com/vluzrmos/laravel-badge-poser) - Badges generator to Laravel. 12 | * [Slack API](https://github.com/vluzrmos/laravel-slack-api) - Laravel easy Slack API. 13 | * [Language Detector](https://github.com/vluzrmos/laravel-language-detector) - Automatic set the application language based on user browser preferences. 14 | * [Lumen Tinker](https://github.com/vluzrmos/lumen-tinker) - An interactive shell to Lumen. 15 | 16 | ## Download the source 17 | 18 | ```bash 19 | composer create-project vluzrmos/lumen-slackin 20 | ``` 21 | 22 | ## Installation 23 | 24 | Copy .env.example to .env and: 25 | 26 | Change the APP_KEY to something random string with max 32 characters. 27 | 28 | Change the SLACK_TOKEN to the token of your user on slack team, with admin privilegies, you can get it on [Slack Web API](https://api.slack.com/web#authentication). 29 | 30 | ## Run 31 | 32 | ## Queue 33 | Start the queue listener: 34 | 35 | ```bash 36 | php artisan queue:listen --timeout=240 1>> /dev/null 2>&1 & 37 | ``` 38 | 39 | > That will start the queue listener in background on \*nix computers, to stop that you need to know 40 | how to kill a job on your system. 41 | 42 | > Its hight recomended run the queue on system startup, on linux you should add the following lines to your crontab: 43 | 44 | ```bash 45 | @reboot php /path/to/that/project/artisan queue:listen --timeout=240 1>> /dev/null 2>&1 46 | ``` 47 | 48 | ### Scheduled Tasks (Optional) 49 | 50 | You may also need to add that command to your cronjob, that will update the users status on every minute: 51 | 52 | ```bash 53 | * * * * * php /path/to/that/project/artisan schedule:run 1>> /dev/null 2>&1 54 | ``` 55 | 56 | That will make your queue run in background and ignoring error messages. 57 | 58 | **Note:** If you do not want to use that feature, you just need to set the environment 59 | variable `SLACK_STATUS_ENABLED` to `false` on your `.env` file, that will hide the message 60 | about users active (online/total) of your team on the homepage: 61 | 62 | SLACK_STATUS_ENABLED=false 63 | 64 | ### HTTP Server 65 | 66 | Start the http server: 67 | 68 | ```bash 69 | php artisan serve 70 | ``` 71 | 72 | By default, artisan serve starts on port 8000, if you want to modify it, just starts it by passing --port=NUMBER or 73 | just make a VirtualHost on your server (Apache or Nginx) with DocumentRoot on /path/to/that/project/public/ path. 74 | 75 | ## Badge is available 76 | 77 | If your need a badge to your slack invitator, just use: 78 | 79 | ```html 80 | 81 | ``` 82 | 83 | Example: 84 | [![Laravel Brasil](https://slack.laravel.com.br/badge.svg)](https://slack.laravel.com.br) 85 | 86 | ## Multi-Language Support 87 | 88 | By default the system will try to detect if the browser language is available on resources/lang, 89 | if available will setup. Available languages: 90 | 91 | * English - en 92 | * French - fr_FR 93 | * Portuguese Brazil - pt_BR 94 | * German - de_DE 95 | 96 | ## Mobile Devices 97 | 98 | That project uses [Twitter Bootstrap 3](http://getbootstrap.com), and it is compatible on small devices. 99 | 100 | ## Using Lumen Slackin 101 | 102 | Your team are using this project? Put your link here: 103 | 104 | - [CakePHP Brasil](http://slack.cakephpbrasil.com.br) 105 | - [Laravel Brasil](https://slack.laravel.com.br) 106 | - [VueSlack](http://vueslack.com) 107 | - [Sencha Brasil](http://sencha-br.wemersonjanuario.com.br) 108 | - [PHP Mexico](http://chat.phpmexico.mx) 109 | - [ScotlandPHP](https://slack.scotlandphp.co.uk) 110 | 111 | > Note: Consider to send a PR to master branch. 112 | 113 | ## License 114 | 115 | [DBAD](http://www.dbad-license.org/). 116 | -------------------------------------------------------------------------------- /resources/lang/en/validation.php: -------------------------------------------------------------------------------- 1 | 'The :attribute must be accepted.', 17 | 'active_url' => 'The :attribute is not a valid URL.', 18 | 'after' => 'The :attribute must be a date after :date.', 19 | 'alpha' => 'The :attribute may only contain letters.', 20 | 'alpha_dash' => 'The :attribute may only contain letters, numbers, and dashes.', 21 | 'alpha_num' => 'The :attribute may only contain letters and numbers.', 22 | 'array' => 'The :attribute must be an array.', 23 | 'before' => 'The :attribute must be a date before :date.', 24 | 'between' => [ 25 | 'numeric' => 'The :attribute must be between :min and :max.', 26 | 'file' => 'The :attribute must be between :min and :max kilobytes.', 27 | 'string' => 'The :attribute must be between :min and :max characters.', 28 | 'array' => 'The :attribute must have between :min and :max items.', 29 | ], 30 | 'boolean' => 'The :attribute field must be true or false.', 31 | 'confirmed' => 'The :attribute confirmation does not match.', 32 | 'date' => 'The :attribute is not a valid date.', 33 | 'date_format' => 'The :attribute does not match the format :format.', 34 | 'different' => 'The :attribute and :other must be different.', 35 | 'digits' => 'The :attribute must be :digits digits.', 36 | 'digits_between' => 'The :attribute must be between :min and :max digits.', 37 | 'email' => 'The :attribute must be a valid email address.', 38 | 'filled' => 'The :attribute field is required.', 39 | 'exists' => 'The selected :attribute is invalid.', 40 | 'image' => 'The :attribute must be an image.', 41 | 'in' => 'The selected :attribute is invalid.', 42 | 'integer' => 'The :attribute must be an integer.', 43 | 'ip' => 'The :attribute must be a valid IP address.', 44 | 'max' => [ 45 | 'numeric' => 'The :attribute may not be greater than :max.', 46 | 'file' => 'The :attribute may not be greater than :max kilobytes.', 47 | 'string' => 'The :attribute may not be greater than :max characters.', 48 | 'array' => 'The :attribute may not have more than :max items.', 49 | ], 50 | 'mimes' => 'The :attribute must be a file of type: :values.', 51 | 'min' => [ 52 | 'numeric' => 'The :attribute must be at least :min.', 53 | 'file' => 'The :attribute must be at least :min kilobytes.', 54 | 'string' => 'The :attribute must be at least :min characters.', 55 | 'array' => 'The :attribute must have at least :min items.', 56 | ], 57 | 'not_in' => 'The selected :attribute is invalid.', 58 | 'numeric' => 'The :attribute must be a number.', 59 | 'regex' => 'The :attribute format is invalid.', 60 | 'required' => 'The :attribute field is required.', 61 | 'required_if' => 'The :attribute field is required when :other is :value.', 62 | 'required_with' => 'The :attribute field is required when :values is present.', 63 | 'required_with_all' => 'The :attribute field is required when :values is present.', 64 | 'required_without' => 'The :attribute field is required when :values is not present.', 65 | 'required_without_all' => 'The :attribute field is required when none of :values are present.', 66 | 'same' => 'The :attribute and :other must match.', 67 | 'size' => [ 68 | 'numeric' => 'The :attribute must be :size.', 69 | 'file' => 'The :attribute must be :size kilobytes.', 70 | 'string' => 'The :attribute must be :size characters.', 71 | 'array' => 'The :attribute must contain :size items.', 72 | ], 73 | 'unique' => 'The :attribute has already been taken.', 74 | 'url' => 'The :attribute format is invalid.', 75 | 'timezone' => 'The :attribute must be a valid zone.', 76 | 'wrong' => 'Something went wrong.', 77 | 'min_words' => 'Type at least :words words.', 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Custom Validation Language Lines 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Here you may specify custom validation messages for attributes using the 85 | | convention "attribute.rule" to name the lines. This makes it quick to 86 | | specify a specific custom language line for a given attribute rule. 87 | | 88 | */ 89 | 90 | 'custom' => [ 91 | 'username' => [ 92 | 'min_words' => 'Type your first and last name', 93 | ], 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Custom Validation Attributes 99 | |-------------------------------------------------------------------------- 100 | | 101 | | The following language lines are used to swap attribute place-holders 102 | | with something more reader friendly such as E-Mail Address instead 103 | | of "email". This simply helps us make messages a little cleaner. 104 | | 105 | */ 106 | 107 | 'attributes' => [ 108 | 'username' => 'name', 109 | 'email' => 'e-mail', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /resources/lang/fr_FR/validation.php: -------------------------------------------------------------------------------- 1 | 'La valeur :attribute doit être accepté.', 17 | 'active_url' => 'La valeur :attribute n\'est pas une URL valide.', 18 | 'after' => 'La valeur :attribute doit être postérieure à :date.', 19 | 'alpha' => 'La valeur :attribute ne peut contenir que des lettres.', 20 | 'alpha_dash' => 'La valeur :attribute ne peut contenir que des lettres, chiffres, et tirets.', 21 | 'alpha_num' => 'La valeur :attribute ne peut contenir que des lettres et chiffres.', 22 | 'array' => 'La valeur :attribute doit être un tableau.', 23 | 'before' => 'La valeur :attribute doit être une date antérieure à :date.', 24 | 'between' => [ 25 | 'numeric' => 'La valeur :attribute doit être comprise entre :min et :max.', 26 | 'file' => 'La valeur :attribute doit être comprise entre :min et :max kilobytes.', 27 | 'string' => 'La valeur :attribute doit comprendre de :min à :max caractères.', 28 | 'array' => 'La valeur :attribute doit comprendre de :min à :max articles.', 29 | ], 30 | 'boolean' => 'La valeur :attribute doit être vrai ou faux.', 31 | 'confirmed' => 'La valeur de confirmation de :attribute ne correspond pas.', 32 | 'date' => 'La valeur :attribute n\'est pas une date valide.', 33 | 'date_format' => 'La valeur :attribute ne respecte pas le format :format.', 34 | 'different' => 'Les valeurs :attribute et :other doivent être differentes.', 35 | 'digits' => 'La valeur :attribute doit comporter :digits chiffres.', 36 | 'digits_between' => 'La valeur :attribute doit comporter entre :min et :max chiffres.', 37 | 'email' => 'La valeur :attribute doit être une adresse email valide.', 38 | 'filled' => 'Le champ :attribute est obligatoire.', 39 | 'exists' => 'La valeur :attribute sélectionnée est invalide.', 40 | 'image' => ':attribute doit être une image.', 41 | 'in' => 'La selection :attribute est invalide.', 42 | 'integer' => ':attribute doit être un chiffre.', 43 | 'ip' => ':attribute doit être une adresse IP valide.', 44 | 'max' => [ 45 | 'numeric' => 'The :attribute may not be greater than :max.', 46 | 'file' => 'The :attribute may not be greater than :max kilobytes.', 47 | 'string' => 'The :attribute may not be greater than :max characters.', 48 | 'array' => 'The :attribute may not have more than :max items.', 49 | ], 50 | 'mimes' => 'The :attribute must be a file of type: :values.', 51 | 'min' => [ 52 | 'numeric' => 'The :attribute must be at least :min.', 53 | 'file' => 'The :attribute must be at least :min kilobytes.', 54 | 'string' => 'The :attribute must be at least :min characters.', 55 | 'array' => 'The :attribute must have at least :min items.', 56 | ], 57 | 'not_in' => 'The selected :attribute is invalid.', 58 | 'numeric' => 'The :attribute must be a number.', 59 | 'regex' => 'The :attribute format is invalid.', 60 | 'required' => 'La valeur de :attribute est requise.', 61 | 'required_if' => 'Le champ :attribute est requis lorsque :other est :value.', 62 | 'required_with' => 'Le champ :attribute est requis lorsque :values est présent.', 63 | 'required_with_all' => 'Le champ :attribute est requis lorsque :values est présent.', 64 | 'required_without' => 'Le champ :attribute est requis lorsque :values n\'est pas présent.', 65 | 'required_without_all' => 'Le champ :attribute est requis lorsqu\'aucunes des valeurs :values ne sont présentes.', 66 | 'same' => ':attribute et :other doivent être identiques.', 67 | 'size' => [ 68 | 'numeric' => 'La valeur :attribute doit être :size.', 69 | 'file' => 'La valeur :attribute doit faire :size kilobytes.', 70 | 'string' => 'La valeur :attribute doit comporter :size caractères.', 71 | 'array' => 'La valeur :attribute doit contenir :size articles.', 72 | ], 73 | 'unique' => 'La valeur :attribute a déjà été utilisée.', 74 | 'url' => 'La valeur :attribute n\'a pas un format valide.', 75 | 'timezone' => 'La valeur :attribute doit être une zone valide.', 76 | 'wrong' => 'Un problème est survenu.', 77 | 'min_words' => 'Entrez au moins :words mots.', 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Custom Validation Language Lines 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Here you may specify custom validation messages for attributes using the 85 | | convention "attribute.rule" to name the lines. This makes it quick to 86 | | specify a specific custom language line for a given attribute rule. 87 | | 88 | */ 89 | 90 | 'custom' => [ 91 | 'username' => [ 92 | 'min_words' => 'Entrez votre prénom et nom', 93 | ], 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Custom Validation Attributes 99 | |-------------------------------------------------------------------------- 100 | | 101 | | The following language lines are used to swap attribute place-holders 102 | | with something more reader friendly such as E-Mail Address instead 103 | | of "email". This simply helps us make messages a little cleaner. 104 | | 105 | */ 106 | 107 | 'attributes' => [ 108 | 'username' => 'nom', 109 | 'email' => 'Adresse email', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /resources/lang/de_DE/validation.php: -------------------------------------------------------------------------------- 1 | 'Das Feld :attribute musss akzeptiert werden.', 17 | 'active_url' => 'Das Feld :attribute ist keine gültige URL.', 18 | 'after' => 'Das Feld :attribute muss ein Datum nach :date sein.', 19 | 'alpha' => 'Das Feld :attribute darf nur Buchstaben enthalten.', 20 | 'alpha_dash' => 'Das Feld :attribute darf nur Buchstaben, Zahlen und Bindestriche enthalten.', 21 | 'alpha_num' => 'Das Feld :attribute darf nur Buchstaben und Zahlen enthalten.', 22 | 'array' => 'Das Feld :attribute muss ein Array sein.', 23 | 'before' => 'Das Feld :attribute muss ein Datum vor :date sein.', 24 | 'between' => [ 25 | 'numeric' => 'Der Wert von :attribute muss zwischen :min und :max liegen.', 26 | 'file' => 'Die Datei :attribute muss zwischen :min und :max Kilobyte groß sein.', 27 | 'string' => 'Die Länge des Feldes :attribute muss zwischen :min und :max Zeichen liegen.', 28 | 'array' => 'Die Länge des Array :attribute muss zwischen :min und :max liegen.', 29 | ], 30 | 'boolean' => 'Das Feld :attribute muss "wahr" oder "falsch" sein.', 31 | 'confirmed' => 'Die :attribute Bestätigung stimmt nicht überein.', 32 | 'date' => 'Das Feld :attribute ist kein gültiges Datum.', 33 | 'date_format' => 'Das Feld :attribute entspricht nicht dem Format :format.', 34 | 'different' => 'Die Felder :attribute und :other müssen sich unterscheiden.', 35 | 'digits' => 'Das Feld :attribute muss :digits Zeichen haben.', 36 | 'digits_between' => 'Das Feld muss eine Länge :attribute zwischen :min und :max Zeichen haben.', 37 | 'email' => 'Das Feld :attribute muss eine gültige E-Mail-Adresse sein.', 38 | 'filled' => 'Das Feld :attribute wird benötigt.', 39 | 'exists' => 'Das ausgewählte Feld :attribute ist ungültig.', 40 | 'image' => 'Das Feld :attribute muss ein Bild sein.', 41 | 'in' => 'Das ausgewählte Feld :attribute ist ungültig.', 42 | 'integer' => 'Das Feld :attribute muss eine ganze Zahl sein.', 43 | 'ip' => 'Das Feld :attribute muss eine gültige IP-Adresse sein.', 44 | 'max' => [ 45 | 'numeric' => 'Der Wert von :attribute darf nicht größer als :max sein.', 46 | 'file' => 'Die Datei :attribute darf nicht größer als :max Kilobyte sein.', 47 | 'string' => 'Die Länge des Feldes :attribute darf nicht größer als :max Zeichen sein.', 48 | 'array' => 'Die Länge des Arrays :attribute darf nicht größer als :max sein.', 49 | ], 50 | 'mimes' => 'Das Feld :attribute muss eine Datei vom Typ :values sein.', 51 | 'min' => [ 52 | 'numeric' => 'Der Wert von :attribute muss mindestens :min sein.', 53 | 'file' => 'Die Datei :attribute muss mindestens :min Kilobyte groß sein.', 54 | 'string' => 'Die Länge des Feldes :attribute muss mindestens :min Zeichen lang sein.', 55 | 'array' => 'Die Länge des Array :attribute muss mindestens :min betragen.', 56 | ], 57 | 'not_in' => 'Das ausgewählte Feld :attribute ist ungültig.', 58 | 'numeric' => 'Das Feld :attribute muss eine Zahl sein.', 59 | 'regex' => 'Das Format des Felds :attribute ist ungültig.', 60 | 'required' => 'Das Feld :attribute muss angegeben werden.', 61 | 'required_if' => 'Das Feld :attribute muss angegeben werden, wenn :other :value ist.', 62 | 'required_with' => 'Das Feld :attribute muss angegeben werden :values angegeben ist.', 63 | 'required_with_all' => 'Das Feld :attribute muss angegeben werden, wenn :values angegeben ist.', 64 | 'required_without' => 'Das Feld :attribute muss angegeben werden :values nicht angegeben ist.', 65 | 'required_without_all' => 'Das Feld :attribute muss angegeben werden, wenn keines von :values angegeben sind.', 66 | 'same' => 'Die Felder :attribute und :other müssen übereinstimmen.', 67 | 'size' => [ 68 | 'numeric' => 'Das Feld :attribute muss :size groß sein.', 69 | 'file' => 'Die Datei :attribute muss :size Kilobyte groß sein.', 70 | 'string' => 'Die Länge des Feldes :attribute muss :size Zeichen lang sein.', 71 | 'array' => 'Die Länge des Array :attribute muss :size betragen.', 72 | ], 73 | 'unique' => 'Der Wert für das Feld :attribute wurde bereits verwendet.', 74 | 'url' => 'Das Format des Felder :attribute ist ungültig.', 75 | 'timezone' => 'Das Feld :attribute muss eine gültige Zeitzone sein.', 76 | 'wrong' => 'Etwas ist schief gelaufen.', 77 | 'min_words' => 'Gib mindestens :words Wort ein.', 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Custom Validation Language Lines 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Here you may specify custom validation messages for attributes using the 85 | | convention "attribute.rule" to name the lines. This makes it quick to 86 | | specify a specific custom language line for a given attribute rule. 87 | | 88 | */ 89 | 90 | 'custom' => [ 91 | 'username' => [ 92 | 'min_words' => 'Gib bitte deinen Vor- und Nachnamen ein', 93 | ], 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Custom Validation Attributes 99 | |-------------------------------------------------------------------------- 100 | | 101 | | The following language lines are used to swap attribute place-holders 102 | | with something more reader friendly such as E-Mail Address instead 103 | | of "email". This simply helps us make messages a little cleaner. 104 | | 105 | */ 106 | 107 | 'attributes' => [ 108 | 'username' => 'Benutzername', 109 | 'email' => 'E-Mail-Adresse', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /resources/lang/pt_BR/validation.php: -------------------------------------------------------------------------------- 1 | 'O campo :attribute deve ser aceito.', 17 | 'active_url' => 'O campo :attribute não contém um URL válido.', 18 | 'after' => 'O campo :attribute deverá conter uma data posterior a :date.', 19 | 'alpha' => 'O campo :attribute deverá conter apenas letras.', 20 | 'alpha_dash' => 'O campo :attribute deverá conter apenas letras, números e traços.', 21 | 'alpha_num' => 'O campo :attribute deverá conter apenas letras e números .', 22 | 'array' => 'O campo :attribute precisa ser um conjunto.', 23 | 'before' => 'O campo :attribute deverá conter uma data anterior a :date.', 24 | 'between' => [ 25 | 'numeric' => 'O campo :attribute deverá ter um valor entre :min - :max.', 26 | 'file' => 'O campo :attribute deverá ter um tamanho entre :min - :max kilobytes.', 27 | 'string' => 'O campo :attribute deverá conter entre :min - :max caracteres.', 28 | 'array' => 'O campo :attribute precisar ter entre :min - :max itens.', 29 | ], 30 | 'boolean' => 'O campo :attribute deverá ter o valor verdadeiro ou falso.', 31 | 'confirmed' => 'A confirmação para o campo :attribute não coincide.', 32 | 'date' => 'O campo :attribute não contém uma data válida.', 33 | 'date_format' => 'A data indicada para o campo :attribute não respeita o formato :format.', 34 | 'different' => 'Os campos :attribute e :other deverão conter valores diferentes.', 35 | 'digits' => 'O campo :attribute deverá conter :digits dígitos.', 36 | 'digits_between' => 'O campo :attribute deverá conter entre :min a :max dígitos.', 37 | 'email' => 'O campo :attribute não contém um endereço de email válido.', 38 | 'exists' => 'O valor selecionado para o campo :attribute é inválido.', 39 | 'filled' => 'É obrigatória a indicação de um valor para o campo :attribute.', 40 | 'image' => 'O campo :attribute deverá conter uma imagem.', 41 | 'in' => 'O campo :attribute não contém um valor válido.', 42 | 'integer' => 'O campo :attribute deverá conter um número inteiro.', 43 | 'ip' => 'O campo :attribute deverá conter um IP válido.', 44 | 'max' => [ 45 | 'numeric' => 'O campo :attribute não deverá conter um valor superior a :max.', 46 | 'file' => 'O campo :attribute não deverá ter um tamanho superior a :max kilobytes.', 47 | 'string' => 'O campo :attribute não deverá conter mais de :max caracteres.', 48 | 'array' => 'O campo :attribute deve ter no máximo :max itens.', 49 | ], 50 | 'mimes' => 'O campo :attribute deverá conter um arquivo do tipo: :values.', 51 | 'min' => [ 52 | 'numeric' => 'O campo :attribute deverá ter um valor superior ou igual a :min.', 53 | 'file' => 'O campo :attribute deverá ter no mínimo :min kilobytes.', 54 | 'string' => 'O campo :attribute deverá conter no mínimo :min caracteres.', 55 | 'array' => 'O campo :attribute deve ter no mínimo :min itens.', 56 | ], 57 | 'not_in' => 'O campo :attribute contém um valor inválido.', 58 | 'numeric' => 'O campo :attribute deverá conter um valor numérico.', 59 | 'regex' => 'O formato do valor para o campo :attribute é inválido.', 60 | 'required' => 'É obrigatória a indicação de um valor para o campo :attribute.', 61 | 'required_if' => 'É obrigatória a indicação de um valor para o campo :attribute quando o valor do campo :other é igual a :value.', 62 | 'required_with' => 'É obrigatória a indicação de um valor para o campo :attribute quando :values está presente.', 63 | 'required_with_all' => 'É obrigatória a indicação de um valor para o campo :attribute quando um dos :values está presente.', 64 | 'required_without' => 'É obrigatória a indicação de um valor para o campo :attribute quanto :values não está presente.', 65 | 'required_without_all' => 'É obrigatória a indicação de um valor para o campo :attribute quando nenhum dos :values está presente.', 66 | 'same' => 'Os campos :attribute e :other deverão conter valores iguais.', 67 | 'size' => [ 68 | 'numeric' => 'O campo :attribute deverá conter o valor :size.', 69 | 'file' => 'O campo :attribute deverá ter o tamanho de :size kilobytes.', 70 | 'string' => 'O campo :attribute deverá conter :size caracteres.', 71 | 'array' => 'O campo :attribute deve ter :size itens.', 72 | ], 73 | 'timezone' => 'O campo :attribute deverá ter um fuso horário válido.', 74 | 'unique' => 'O valor indicado para o campo :attribute já se encontra registrado.', 75 | 'url' => 'O formato do URL indicado para o campo :attribute é inválido.', 76 | 'wrong' => 'Alguma coisa deu errado.', 77 | 'min_words' => 'Digite ao menos :words palavras.', 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Custom Validation Language Lines 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Here you may specify custom validation messages for attributes using the 85 | | convention "attribute.rule" to name the lines. This makes it quick to 86 | | specify a specific custom language line for a given attribute rule. 87 | | 88 | */ 89 | 90 | 'custom' => [ 91 | 'username' => [ 92 | 'min_words' => 'Digite seu nome e sobrenome', 93 | ], 94 | ], 95 | 96 | /* 97 | |-------------------------------------------------------------------------- 98 | | Custom Validation Attributes 99 | |-------------------------------------------------------------------------- 100 | | 101 | | The following language lines are used to swap attribute place-holders 102 | | with something more reader friendly such as E-Mail Address instead 103 | | of "email". This simply helps us make messages a little cleaner. 104 | | 105 | */ 106 | 107 | 'attributes' => [ 108 | 'username' => 'nome', 109 | 'email' => 'e-mail', 110 | ], 111 | 112 | ]; 113 | -------------------------------------------------------------------------------- /public/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.4 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn-default .badge, 33 | .btn-primary .badge, 34 | .btn-success .badge, 35 | .btn-info .badge, 36 | .btn-warning .badge, 37 | .btn-danger .badge { 38 | text-shadow: none; 39 | } 40 | .btn:active, 41 | .btn.active { 42 | background-image: none; 43 | } 44 | .btn-default { 45 | text-shadow: 0 1px 0 #fff; 46 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 47 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 48 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 49 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 50 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 51 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 52 | background-repeat: repeat-x; 53 | border-color: #dbdbdb; 54 | border-color: #ccc; 55 | } 56 | .btn-default:hover, 57 | .btn-default:focus { 58 | background-color: #e0e0e0; 59 | background-position: 0 -15px; 60 | } 61 | .btn-default:active, 62 | .btn-default.active { 63 | background-color: #e0e0e0; 64 | border-color: #dbdbdb; 65 | } 66 | .btn-default.disabled, 67 | .btn-default:disabled, 68 | .btn-default[disabled] { 69 | background-color: #e0e0e0; 70 | background-image: none; 71 | } 72 | .btn-primary { 73 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 74 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 75 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 76 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 77 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 78 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 79 | background-repeat: repeat-x; 80 | border-color: #245580; 81 | } 82 | .btn-primary:hover, 83 | .btn-primary:focus { 84 | background-color: #265a88; 85 | background-position: 0 -15px; 86 | } 87 | .btn-primary:active, 88 | .btn-primary.active { 89 | background-color: #265a88; 90 | border-color: #245580; 91 | } 92 | .btn-primary.disabled, 93 | .btn-primary:disabled, 94 | .btn-primary[disabled] { 95 | background-color: #265a88; 96 | background-image: none; 97 | } 98 | .btn-success { 99 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 100 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 101 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 102 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 103 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 104 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 105 | background-repeat: repeat-x; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:hover, 109 | .btn-success:focus { 110 | background-color: #419641; 111 | background-position: 0 -15px; 112 | } 113 | .btn-success:active, 114 | .btn-success.active { 115 | background-color: #419641; 116 | border-color: #3e8f3e; 117 | } 118 | .btn-success.disabled, 119 | .btn-success:disabled, 120 | .btn-success[disabled] { 121 | background-color: #419641; 122 | background-image: none; 123 | } 124 | .btn-info { 125 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 126 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 127 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 128 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 129 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 130 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 131 | background-repeat: repeat-x; 132 | border-color: #28a4c9; 133 | } 134 | .btn-info:hover, 135 | .btn-info:focus { 136 | background-color: #2aabd2; 137 | background-position: 0 -15px; 138 | } 139 | .btn-info:active, 140 | .btn-info.active { 141 | background-color: #2aabd2; 142 | border-color: #28a4c9; 143 | } 144 | .btn-info.disabled, 145 | .btn-info:disabled, 146 | .btn-info[disabled] { 147 | background-color: #2aabd2; 148 | background-image: none; 149 | } 150 | .btn-warning { 151 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 152 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 153 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 154 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 155 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 156 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 157 | background-repeat: repeat-x; 158 | border-color: #e38d13; 159 | } 160 | .btn-warning:hover, 161 | .btn-warning:focus { 162 | background-color: #eb9316; 163 | background-position: 0 -15px; 164 | } 165 | .btn-warning:active, 166 | .btn-warning.active { 167 | background-color: #eb9316; 168 | border-color: #e38d13; 169 | } 170 | .btn-warning.disabled, 171 | .btn-warning:disabled, 172 | .btn-warning[disabled] { 173 | background-color: #eb9316; 174 | background-image: none; 175 | } 176 | .btn-danger { 177 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 178 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 179 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 180 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 181 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 182 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 183 | background-repeat: repeat-x; 184 | border-color: #b92c28; 185 | } 186 | .btn-danger:hover, 187 | .btn-danger:focus { 188 | background-color: #c12e2a; 189 | background-position: 0 -15px; 190 | } 191 | .btn-danger:active, 192 | .btn-danger.active { 193 | background-color: #c12e2a; 194 | border-color: #b92c28; 195 | } 196 | .btn-danger.disabled, 197 | .btn-danger:disabled, 198 | .btn-danger[disabled] { 199 | background-color: #c12e2a; 200 | background-image: none; 201 | } 202 | .thumbnail, 203 | .img-thumbnail { 204 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 205 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 206 | } 207 | .dropdown-menu > li > a:hover, 208 | .dropdown-menu > li > a:focus { 209 | background-color: #e8e8e8; 210 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 211 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 212 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 213 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 214 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 215 | background-repeat: repeat-x; 216 | } 217 | .dropdown-menu > .active > a, 218 | .dropdown-menu > .active > a:hover, 219 | .dropdown-menu > .active > a:focus { 220 | background-color: #2e6da4; 221 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 222 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 223 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 224 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 225 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 226 | background-repeat: repeat-x; 227 | } 228 | .navbar-default { 229 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 230 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 231 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 232 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 233 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 234 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 235 | background-repeat: repeat-x; 236 | border-radius: 4px; 237 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 238 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 239 | } 240 | .navbar-default .navbar-nav > .open > a, 241 | .navbar-default .navbar-nav > .active > a { 242 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 243 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 244 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 245 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 246 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 247 | background-repeat: repeat-x; 248 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 249 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 250 | } 251 | .navbar-brand, 252 | .navbar-nav > li > a { 253 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 254 | } 255 | .navbar-inverse { 256 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 257 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 258 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 259 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 260 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 261 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 262 | background-repeat: repeat-x; 263 | } 264 | .navbar-inverse .navbar-nav > .open > a, 265 | .navbar-inverse .navbar-nav > .active > a { 266 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 267 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 268 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 269 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 270 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 271 | background-repeat: repeat-x; 272 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 273 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 274 | } 275 | .navbar-inverse .navbar-brand, 276 | .navbar-inverse .navbar-nav > li > a { 277 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 278 | } 279 | .navbar-static-top, 280 | .navbar-fixed-top, 281 | .navbar-fixed-bottom { 282 | border-radius: 0; 283 | } 284 | @media (max-width: 767px) { 285 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 286 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 287 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 288 | color: #fff; 289 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 290 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 291 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 292 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 293 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 294 | background-repeat: repeat-x; 295 | } 296 | } 297 | .alert { 298 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 299 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 300 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 301 | } 302 | .alert-success { 303 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 304 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 305 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 306 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 307 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 308 | background-repeat: repeat-x; 309 | border-color: #b2dba1; 310 | } 311 | .alert-info { 312 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 313 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 314 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 315 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 316 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 317 | background-repeat: repeat-x; 318 | border-color: #9acfea; 319 | } 320 | .alert-warning { 321 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 322 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 323 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 324 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 325 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 326 | background-repeat: repeat-x; 327 | border-color: #f5e79e; 328 | } 329 | .alert-danger { 330 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 331 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 332 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 333 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 334 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 335 | background-repeat: repeat-x; 336 | border-color: #dca7a7; 337 | } 338 | .progress { 339 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 340 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 341 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 342 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 343 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 344 | background-repeat: repeat-x; 345 | } 346 | .progress-bar { 347 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 348 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 349 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 350 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 351 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 352 | background-repeat: repeat-x; 353 | } 354 | .progress-bar-success { 355 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 356 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 357 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 358 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 359 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 360 | background-repeat: repeat-x; 361 | } 362 | .progress-bar-info { 363 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 364 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 365 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 366 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 367 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 368 | background-repeat: repeat-x; 369 | } 370 | .progress-bar-warning { 371 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 372 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 374 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 376 | background-repeat: repeat-x; 377 | } 378 | .progress-bar-danger { 379 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 380 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 381 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 382 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 383 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 384 | background-repeat: repeat-x; 385 | } 386 | .progress-bar-striped { 387 | 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); 388 | 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); 389 | 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); 390 | } 391 | .list-group { 392 | border-radius: 4px; 393 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 394 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 395 | } 396 | .list-group-item.active, 397 | .list-group-item.active:hover, 398 | .list-group-item.active:focus { 399 | text-shadow: 0 -1px 0 #286090; 400 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 401 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 403 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 405 | background-repeat: repeat-x; 406 | border-color: #2b669a; 407 | } 408 | .list-group-item.active .badge, 409 | .list-group-item.active:hover .badge, 410 | .list-group-item.active:focus .badge { 411 | text-shadow: none; 412 | } 413 | .panel { 414 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 415 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 416 | } 417 | .panel-default > .panel-heading { 418 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 419 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 420 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 421 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 422 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 423 | background-repeat: repeat-x; 424 | } 425 | .panel-primary > .panel-heading { 426 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 427 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 428 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 429 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 430 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 431 | background-repeat: repeat-x; 432 | } 433 | .panel-success > .panel-heading { 434 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 435 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 436 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 437 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 438 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 439 | background-repeat: repeat-x; 440 | } 441 | .panel-info > .panel-heading { 442 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 443 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 444 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 445 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 446 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 447 | background-repeat: repeat-x; 448 | } 449 | .panel-warning > .panel-heading { 450 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 451 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 452 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 453 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 454 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 455 | background-repeat: repeat-x; 456 | } 457 | .panel-danger > .panel-heading { 458 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 459 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 460 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 461 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 462 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 463 | background-repeat: repeat-x; 464 | } 465 | .well { 466 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 467 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 468 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 469 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 470 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 471 | background-repeat: repeat-x; 472 | border-color: #dcdcdc; 473 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 474 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 475 | } 476 | /*# sourceMappingURL=bootstrap-theme.css.map */ 477 | -------------------------------------------------------------------------------- /public/css/bootstrap-theme.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["less/theme.less","less/mixins/vendor-prefixes.less","bootstrap-theme.css","less/mixins/gradients.less","less/mixins/reset-filter.less"],"names":[],"mappings":"AAcA;;;;;;EAME,0CAAA;ECgDA,6FAAA;EACQ,qFAAA;EC5DT;AFgBC;;;;;;;;;;;;EC2CA,0DAAA;EACQ,kDAAA;EC7CT;AFVD;;;;;;EAiBI,mBAAA;EECH;AFiCC;;EAEE,wBAAA;EE/BH;AFoCD;EGnDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EAgC2C,2BAAA;EAA2B,oBAAA;EEzBvE;AFLC;;EAEE,2BAAA;EACA,8BAAA;EEOH;AFJC;;EAEE,2BAAA;EACA,uBAAA;EEMH;AFHC;;;EAGE,2BAAA;EACA,wBAAA;EEKH;AFUD;EGpDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEgCD;AF9BC;;EAEE,2BAAA;EACA,8BAAA;EEgCH;AF7BC;;EAEE,2BAAA;EACA,uBAAA;EE+BH;AF5BC;;;EAGE,2BAAA;EACA,wBAAA;EE8BH;AFdD;EGrDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEyDD;AFvDC;;EAEE,2BAAA;EACA,8BAAA;EEyDH;AFtDC;;EAEE,2BAAA;EACA,uBAAA;EEwDH;AFrDC;;;EAGE,2BAAA;EACA,wBAAA;EEuDH;AFtCD;EGtDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEkFD;AFhFC;;EAEE,2BAAA;EACA,8BAAA;EEkFH;AF/EC;;EAEE,2BAAA;EACA,uBAAA;EEiFH;AF9EC;;;EAGE,2BAAA;EACA,wBAAA;EEgFH;AF9DD;EGvDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EE2GD;AFzGC;;EAEE,2BAAA;EACA,8BAAA;EE2GH;AFxGC;;EAEE,2BAAA;EACA,uBAAA;EE0GH;AFvGC;;;EAGE,2BAAA;EACA,wBAAA;EEyGH;AFtFD;EGxDI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EAEA,wHAAA;ECnBF,qEAAA;EJiCA,6BAAA;EACA,uBAAA;EEoID;AFlIC;;EAEE,2BAAA;EACA,8BAAA;EEoIH;AFjIC;;EAEE,2BAAA;EACA,uBAAA;EEmIH;AFhIC;;;EAGE,2BAAA;EACA,wBAAA;EEkIH;AFxGD;;EChBE,oDAAA;EACQ,4CAAA;EC4HT;AFnGD;;EGzEI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHwEF,2BAAA;EEyGD;AFvGD;;;EG9EI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH8EF,2BAAA;EE6GD;AFpGD;EG3FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EJ6GA,oBAAA;EC/CA,6FAAA;EACQ,qFAAA;EC0JT;AF/GD;;EG3FI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,0DAAA;EACQ,kDAAA;ECoKT;AF5GD;;EAEE,gDAAA;EE8GD;AF1GD;EG9GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ECnBF,qEAAA;EF+OD;AFlHD;;EG9GI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EF2CF,yDAAA;EACQ,iDAAA;EC0LT;AF5HD;;EAYI,2CAAA;EEoHH;AF/GD;;;EAGE,kBAAA;EEiHD;AF5FD;EAfI;;;IAGE,aAAA;IG3IF,0EAAA;IACA,qEAAA;IACA,+FAAA;IAAA,wEAAA;IACA,6BAAA;IACA,wHAAA;ID0PD;EACF;AFxGD;EACE,+CAAA;ECzGA,4FAAA;EACQ,oFAAA;ECoNT;AFhGD;EGpKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EE4GD;AFvGD;EGrKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EEoHD;AF9GD;EGtKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EE4HD;AFrHD;EGvKI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH4JF,uBAAA;EEoID;AFrHD;EG/KI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDuSH;AFlHD;EGzLI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED8SH;AFxHD;EG1LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDqTH;AF9HD;EG3LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED4TH;AFpID;EG5LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDmUH;AF1ID;EG7LI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED0UH;AF7ID;EGhKI,+MAAA;EACA,0MAAA;EACA,uMAAA;EDgTH;AFzID;EACE,oBAAA;EC5JA,oDAAA;EACQ,4CAAA;ECwST;AF1ID;;;EAGE,+BAAA;EGjNE,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EH+MF,uBAAA;EEgJD;AFrJD;;;EAQI,mBAAA;EEkJH;AFxID;ECjLE,mDAAA;EACQ,2CAAA;EC4TT;AFlID;EG1OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED+WH;AFxID;EG3OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDsXH;AF9ID;EG5OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED6XH;AFpJD;EG7OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDoYH;AF1JD;EG9OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;ED2YH;AFhKD;EG/OI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EDkZH;AFhKD;EGtPI,0EAAA;EACA,qEAAA;EACA,+FAAA;EAAA,wEAAA;EACA,6BAAA;EACA,wHAAA;EHoPF,uBAAA;ECzMA,2FAAA;EACQ,mFAAA;ECgXT","file":"bootstrap-theme.css","sourcesContent":["\n//\n// Load core variables and mixins\n// --------------------------------------------------\n\n@import \"variables.less\";\n@import \"mixins.less\";\n\n\n//\n// Buttons\n// --------------------------------------------------\n\n// Common styles\n.btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0,0,0,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n // Reset the shadow\n &:active,\n &.active {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n .badge {\n text-shadow: none;\n }\n}\n\n// Mixin for generating new styles\n.btn-styles(@btn-color: #555) {\n #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%));\n .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620\n background-repeat: repeat-x;\n border-color: darken(@btn-color, 14%);\n\n &:hover,\n &:focus {\n background-color: darken(@btn-color, 12%);\n background-position: 0 -15px;\n }\n\n &:active,\n &.active {\n background-color: darken(@btn-color, 12%);\n border-color: darken(@btn-color, 14%);\n }\n\n &.disabled,\n &:disabled,\n &[disabled] {\n background-color: darken(@btn-color, 12%);\n background-image: none;\n }\n}\n\n// Common styles\n.btn {\n // Remove the gradient for the pressed/active state\n &:active,\n &.active {\n background-image: none;\n }\n}\n\n// Apply the mixin to the buttons\n.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; }\n.btn-primary { .btn-styles(@btn-primary-bg); }\n.btn-success { .btn-styles(@btn-success-bg); }\n.btn-info { .btn-styles(@btn-info-bg); }\n.btn-warning { .btn-styles(@btn-warning-bg); }\n.btn-danger { .btn-styles(@btn-danger-bg); }\n\n\n//\n// Images\n// --------------------------------------------------\n\n.thumbnail,\n.img-thumbnail {\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n\n\n//\n// Dropdowns\n// --------------------------------------------------\n\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%));\n background-color: darken(@dropdown-link-hover-bg, 5%);\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n background-color: darken(@dropdown-link-active-bg, 5%);\n}\n\n\n//\n// Navbar\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered\n border-radius: @navbar-border-radius;\n @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075);\n .box-shadow(@shadow);\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.075));\n }\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255,255,255,.25);\n}\n\n// Inverted navbar\n.navbar-inverse {\n #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg);\n .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257\n\n .navbar-nav > .open > a,\n .navbar-nav > .active > a {\n #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%));\n .box-shadow(inset 0 3px 9px rgba(0,0,0,.25));\n }\n\n .navbar-brand,\n .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0,0,0,.25);\n }\n}\n\n// Undo rounded corners in static and fixed navbars\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n\n// Fix active state of dropdown items in collapsed mode\n@media (max-width: @grid-float-breakpoint-max) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: #fff;\n #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%));\n }\n }\n}\n\n\n//\n// Alerts\n// --------------------------------------------------\n\n// Common styles\n.alert {\n text-shadow: 0 1px 0 rgba(255,255,255,.2);\n @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05);\n .box-shadow(@shadow);\n}\n\n// Mixin for generating new styles\n.alert-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%));\n border-color: darken(@color, 15%);\n}\n\n// Apply the mixin to the alerts\n.alert-success { .alert-styles(@alert-success-bg); }\n.alert-info { .alert-styles(@alert-info-bg); }\n.alert-warning { .alert-styles(@alert-warning-bg); }\n.alert-danger { .alert-styles(@alert-danger-bg); }\n\n\n//\n// Progress bars\n// --------------------------------------------------\n\n// Give the progress background some depth\n.progress {\n #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg)\n}\n\n// Mixin for generating new styles\n.progress-bar-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%));\n}\n\n// Apply the mixin to the progress bars\n.progress-bar { .progress-bar-styles(@progress-bar-bg); }\n.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); }\n.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); }\n.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); }\n.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); }\n\n// Reset the striped class because our mixins don't do multiple gradients and\n// the above custom styles override the new `.progress-bar-striped` in v3.2.0.\n.progress-bar-striped {\n #gradient > .striped();\n}\n\n\n//\n// List groups\n// --------------------------------------------------\n\n.list-group {\n border-radius: @border-radius-base;\n .box-shadow(0 1px 2px rgba(0,0,0,.075));\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%);\n #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%));\n border-color: darken(@list-group-active-border, 7.5%);\n\n .badge {\n text-shadow: none;\n }\n}\n\n\n//\n// Panels\n// --------------------------------------------------\n\n// Common styles\n.panel {\n .box-shadow(0 1px 2px rgba(0,0,0,.05));\n}\n\n// Mixin for generating new styles\n.panel-heading-styles(@color) {\n #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%));\n}\n\n// Apply the mixin to the panel headings only\n.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); }\n.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); }\n.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); }\n.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); }\n.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); }\n.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); }\n\n\n//\n// Wells\n// --------------------------------------------------\n\n.well {\n #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg);\n border-color: darken(@well-bg, 10%);\n @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n}\n","// Vendor Prefixes\n//\n// All vendor mixins are deprecated as of v3.2.0 due to the introduction of\n// Autoprefixer in our Gruntfile. They will be removed in v4.\n\n// - Animations\n// - Backface visibility\n// - Box shadow\n// - Box sizing\n// - Content columns\n// - Hyphens\n// - Placeholder text\n// - Transformations\n// - Transitions\n// - User Select\n\n\n// Animations\n.animation(@animation) {\n -webkit-animation: @animation;\n -o-animation: @animation;\n animation: @animation;\n}\n.animation-name(@name) {\n -webkit-animation-name: @name;\n animation-name: @name;\n}\n.animation-duration(@duration) {\n -webkit-animation-duration: @duration;\n animation-duration: @duration;\n}\n.animation-timing-function(@timing-function) {\n -webkit-animation-timing-function: @timing-function;\n animation-timing-function: @timing-function;\n}\n.animation-delay(@delay) {\n -webkit-animation-delay: @delay;\n animation-delay: @delay;\n}\n.animation-iteration-count(@iteration-count) {\n -webkit-animation-iteration-count: @iteration-count;\n animation-iteration-count: @iteration-count;\n}\n.animation-direction(@direction) {\n -webkit-animation-direction: @direction;\n animation-direction: @direction;\n}\n.animation-fill-mode(@fill-mode) {\n -webkit-animation-fill-mode: @fill-mode;\n animation-fill-mode: @fill-mode;\n}\n\n// Backface visibility\n// Prevent browsers from flickering when using CSS 3D transforms.\n// Default value is `visible`, but can be changed to `hidden`\n\n.backface-visibility(@visibility){\n -webkit-backface-visibility: @visibility;\n -moz-backface-visibility: @visibility;\n backface-visibility: @visibility;\n}\n\n// Drop shadows\n//\n// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's\n// supported browsers that have box shadow capabilities now support it.\n\n.box-shadow(@shadow) {\n -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1\n box-shadow: @shadow;\n}\n\n// Box sizing\n.box-sizing(@boxmodel) {\n -webkit-box-sizing: @boxmodel;\n -moz-box-sizing: @boxmodel;\n box-sizing: @boxmodel;\n}\n\n// CSS3 Content Columns\n.content-columns(@column-count; @column-gap: @grid-gutter-width) {\n -webkit-column-count: @column-count;\n -moz-column-count: @column-count;\n column-count: @column-count;\n -webkit-column-gap: @column-gap;\n -moz-column-gap: @column-gap;\n column-gap: @column-gap;\n}\n\n// Optional hyphenation\n.hyphens(@mode: auto) {\n word-wrap: break-word;\n -webkit-hyphens: @mode;\n -moz-hyphens: @mode;\n -ms-hyphens: @mode; // IE10+\n -o-hyphens: @mode;\n hyphens: @mode;\n}\n\n// Placeholder text\n.placeholder(@color: @input-color-placeholder) {\n // Firefox\n &::-moz-placeholder {\n color: @color;\n opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526\n }\n &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+\n &::-webkit-input-placeholder { color: @color; } // Safari and Chrome\n}\n\n// Transformations\n.scale(@ratio) {\n -webkit-transform: scale(@ratio);\n -ms-transform: scale(@ratio); // IE9 only\n -o-transform: scale(@ratio);\n transform: scale(@ratio);\n}\n.scale(@ratioX; @ratioY) {\n -webkit-transform: scale(@ratioX, @ratioY);\n -ms-transform: scale(@ratioX, @ratioY); // IE9 only\n -o-transform: scale(@ratioX, @ratioY);\n transform: scale(@ratioX, @ratioY);\n}\n.scaleX(@ratio) {\n -webkit-transform: scaleX(@ratio);\n -ms-transform: scaleX(@ratio); // IE9 only\n -o-transform: scaleX(@ratio);\n transform: scaleX(@ratio);\n}\n.scaleY(@ratio) {\n -webkit-transform: scaleY(@ratio);\n -ms-transform: scaleY(@ratio); // IE9 only\n -o-transform: scaleY(@ratio);\n transform: scaleY(@ratio);\n}\n.skew(@x; @y) {\n -webkit-transform: skewX(@x) skewY(@y);\n -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+\n -o-transform: skewX(@x) skewY(@y);\n transform: skewX(@x) skewY(@y);\n}\n.translate(@x; @y) {\n -webkit-transform: translate(@x, @y);\n -ms-transform: translate(@x, @y); // IE9 only\n -o-transform: translate(@x, @y);\n transform: translate(@x, @y);\n}\n.translate3d(@x; @y; @z) {\n -webkit-transform: translate3d(@x, @y, @z);\n transform: translate3d(@x, @y, @z);\n}\n.rotate(@degrees) {\n -webkit-transform: rotate(@degrees);\n -ms-transform: rotate(@degrees); // IE9 only\n -o-transform: rotate(@degrees);\n transform: rotate(@degrees);\n}\n.rotateX(@degrees) {\n -webkit-transform: rotateX(@degrees);\n -ms-transform: rotateX(@degrees); // IE9 only\n -o-transform: rotateX(@degrees);\n transform: rotateX(@degrees);\n}\n.rotateY(@degrees) {\n -webkit-transform: rotateY(@degrees);\n -ms-transform: rotateY(@degrees); // IE9 only\n -o-transform: rotateY(@degrees);\n transform: rotateY(@degrees);\n}\n.perspective(@perspective) {\n -webkit-perspective: @perspective;\n -moz-perspective: @perspective;\n perspective: @perspective;\n}\n.perspective-origin(@perspective) {\n -webkit-perspective-origin: @perspective;\n -moz-perspective-origin: @perspective;\n perspective-origin: @perspective;\n}\n.transform-origin(@origin) {\n -webkit-transform-origin: @origin;\n -moz-transform-origin: @origin;\n -ms-transform-origin: @origin; // IE9 only\n transform-origin: @origin;\n}\n\n\n// Transitions\n\n.transition(@transition) {\n -webkit-transition: @transition;\n -o-transition: @transition;\n transition: @transition;\n}\n.transition-property(@transition-property) {\n -webkit-transition-property: @transition-property;\n transition-property: @transition-property;\n}\n.transition-delay(@transition-delay) {\n -webkit-transition-delay: @transition-delay;\n transition-delay: @transition-delay;\n}\n.transition-duration(@transition-duration) {\n -webkit-transition-duration: @transition-duration;\n transition-duration: @transition-duration;\n}\n.transition-timing-function(@timing-function) {\n -webkit-transition-timing-function: @timing-function;\n transition-timing-function: @timing-function;\n}\n.transition-transform(@transition) {\n -webkit-transition: -webkit-transform @transition;\n -moz-transition: -moz-transform @transition;\n -o-transition: -o-transform @transition;\n transition: transform @transition;\n}\n\n\n// User select\n// For selecting text on the page\n\n.user-select(@select) {\n -webkit-user-select: @select;\n -moz-user-select: @select;\n -ms-user-select: @select; // IE10+\n user-select: @select;\n}\n",".btn-default,\n.btn-primary,\n.btn-success,\n.btn-info,\n.btn-warning,\n.btn-danger {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n}\n.btn-default:active,\n.btn-primary:active,\n.btn-success:active,\n.btn-info:active,\n.btn-warning:active,\n.btn-danger:active,\n.btn-default.active,\n.btn-primary.active,\n.btn-success.active,\n.btn-info.active,\n.btn-warning.active,\n.btn-danger.active {\n -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n}\n.btn-default .badge,\n.btn-primary .badge,\n.btn-success .badge,\n.btn-info .badge,\n.btn-warning .badge,\n.btn-danger .badge {\n text-shadow: none;\n}\n.btn:active,\n.btn.active {\n background-image: none;\n}\n.btn-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #dbdbdb;\n text-shadow: 0 1px 0 #fff;\n border-color: #ccc;\n}\n.btn-default:hover,\n.btn-default:focus {\n background-color: #e0e0e0;\n background-position: 0 -15px;\n}\n.btn-default:active,\n.btn-default.active {\n background-color: #e0e0e0;\n border-color: #dbdbdb;\n}\n.btn-default.disabled,\n.btn-default:disabled,\n.btn-default[disabled] {\n background-color: #e0e0e0;\n background-image: none;\n}\n.btn-primary {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #245580;\n}\n.btn-primary:hover,\n.btn-primary:focus {\n background-color: #265a88;\n background-position: 0 -15px;\n}\n.btn-primary:active,\n.btn-primary.active {\n background-color: #265a88;\n border-color: #245580;\n}\n.btn-primary.disabled,\n.btn-primary:disabled,\n.btn-primary[disabled] {\n background-color: #265a88;\n background-image: none;\n}\n.btn-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #3e8f3e;\n}\n.btn-success:hover,\n.btn-success:focus {\n background-color: #419641;\n background-position: 0 -15px;\n}\n.btn-success:active,\n.btn-success.active {\n background-color: #419641;\n border-color: #3e8f3e;\n}\n.btn-success.disabled,\n.btn-success:disabled,\n.btn-success[disabled] {\n background-color: #419641;\n background-image: none;\n}\n.btn-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #28a4c9;\n}\n.btn-info:hover,\n.btn-info:focus {\n background-color: #2aabd2;\n background-position: 0 -15px;\n}\n.btn-info:active,\n.btn-info.active {\n background-color: #2aabd2;\n border-color: #28a4c9;\n}\n.btn-info.disabled,\n.btn-info:disabled,\n.btn-info[disabled] {\n background-color: #2aabd2;\n background-image: none;\n}\n.btn-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #e38d13;\n}\n.btn-warning:hover,\n.btn-warning:focus {\n background-color: #eb9316;\n background-position: 0 -15px;\n}\n.btn-warning:active,\n.btn-warning.active {\n background-color: #eb9316;\n border-color: #e38d13;\n}\n.btn-warning.disabled,\n.btn-warning:disabled,\n.btn-warning[disabled] {\n background-color: #eb9316;\n background-image: none;\n}\n.btn-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n background-repeat: repeat-x;\n border-color: #b92c28;\n}\n.btn-danger:hover,\n.btn-danger:focus {\n background-color: #c12e2a;\n background-position: 0 -15px;\n}\n.btn-danger:active,\n.btn-danger.active {\n background-color: #c12e2a;\n border-color: #b92c28;\n}\n.btn-danger.disabled,\n.btn-danger:disabled,\n.btn-danger[disabled] {\n background-color: #c12e2a;\n background-image: none;\n}\n.thumbnail,\n.img-thumbnail {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.dropdown-menu > li > a:hover,\n.dropdown-menu > li > a:focus {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n background-color: #e8e8e8;\n}\n.dropdown-menu > .active > a,\n.dropdown-menu > .active > a:hover,\n.dropdown-menu > .active > a:focus {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n background-color: #2e6da4;\n}\n.navbar-default {\n background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);\n background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n border-radius: 4px;\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);\n}\n.navbar-default .navbar-nav > .open > a,\n.navbar-default .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);\n background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);\n}\n.navbar-brand,\n.navbar-nav > li > a {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);\n}\n.navbar-inverse {\n background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);\n background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);\n filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\n}\n.navbar-inverse .navbar-nav > .open > a,\n.navbar-inverse .navbar-nav > .active > a {\n background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);\n background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);\n -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);\n}\n.navbar-inverse .navbar-brand,\n.navbar-inverse .navbar-nav > li > a {\n text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);\n}\n.navbar-static-top,\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n border-radius: 0;\n}\n@media (max-width: 767px) {\n .navbar .navbar-nav .open .dropdown-menu > .active > a,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,\n .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {\n color: #fff;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n }\n}\n.alert {\n text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);\n -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.alert-success {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);\n border-color: #b2dba1;\n}\n.alert-info {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);\n border-color: #9acfea;\n}\n.alert-warning {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);\n border-color: #f5e79e;\n}\n.alert-danger {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);\n border-color: #dca7a7;\n}\n.progress {\n background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);\n}\n.progress-bar {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);\n}\n.progress-bar-success {\n background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);\n background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);\n}\n.progress-bar-info {\n background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);\n background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);\n}\n.progress-bar-warning {\n background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);\n background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);\n}\n.progress-bar-danger {\n background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);\n background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);\n}\n.progress-bar-striped {\n background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n}\n.list-group {\n border-radius: 4px;\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);\n}\n.list-group-item.active,\n.list-group-item.active:hover,\n.list-group-item.active:focus {\n text-shadow: 0 -1px 0 #286090;\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);\n border-color: #2b669a;\n}\n.list-group-item.active .badge,\n.list-group-item.active:hover .badge,\n.list-group-item.active:focus .badge {\n text-shadow: none;\n}\n.panel {\n -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n}\n.panel-default > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);\n background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);\n}\n.panel-primary > .panel-heading {\n background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);\n background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);\n}\n.panel-success > .panel-heading {\n background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);\n background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);\n}\n.panel-info > .panel-heading {\n background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);\n background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);\n}\n.panel-warning > .panel-heading {\n background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);\n background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);\n}\n.panel-danger > .panel-heading {\n background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);\n background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);\n}\n.well {\n background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);\n background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);\n background-repeat: repeat-x;\n filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);\n border-color: #dcdcdc;\n -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);\n}\n/*# sourceMappingURL=bootstrap-theme.css.map */","// Gradients\n\n#gradient {\n\n // Horizontal gradient, from left to right\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n // Vertical gradient, from top to bottom\n //\n // Creates two color stops, start and end, by specifying a color and position for each color stop.\n // Color stops are not available in IE9 and below.\n .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {\n background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12\n background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n background-repeat: repeat-x;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down\n }\n\n .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {\n background-repeat: repeat-x;\n background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+\n background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12\n background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+\n }\n .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {\n background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);\n background-repeat: no-repeat;\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)\",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback\n }\n .radial(@inner-color: #555; @outer-color: #333) {\n background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);\n background-image: radial-gradient(circle, @inner-color, @outer-color);\n background-repeat: no-repeat;\n }\n .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {\n background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);\n }\n}\n","// Reset filters for IE\n//\n// When you need to remove a gradient background, do not forget to use this to reset\n// the IE filter for IE9 and below.\n\n.reset-filter() {\n filter: e(%(\"progid:DXImageTransform.Microsoft.gradient(enabled = false)\"));\n}\n"]} --------------------------------------------------------------------------------