Merge pull request #562 from bludit/v2.1-beta

V2.1 beta
This commit is contained in:
Diego Najar 2017-11-08 22:47:20 +01:00 committed by GitHub
commit 390864a9b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 147 additions and 424 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "bl-themes/clean-blog"]
path = bl-themes/clean-blog
url = git@github.com:bludit-themes/clean-blog.git

View File

@ -20,6 +20,9 @@ function checkLogin($args)
} }
if ($Login->verifyUser($_POST['username'], $_POST['password'])) { if ($Login->verifyUser($_POST['username'], $_POST['password'])) {
if (isset($_POST['remember'])) {
$Login->setRememberMe($_POST['username']);
}
// Renew the token. This token will be the same inside the session for multiple forms. // Renew the token. This token will be the same inside the session for multiple forms.
$Security->generateTokenCSRF(); $Security->generateTokenCSRF();
Redirect::page('dashboard'); Redirect::page('dashboard');
@ -35,16 +38,38 @@ function checkLogin($args)
return false; return false;
} }
function checkRememberMe()
{
global $Security;
global $Login;
if ($Security->isBlocked()) {
return false;
}
if ($Login->verifyUserByRemember()) {
$Security->generateTokenCSRF();
Redirect::page('dashboard');
return true;
}
$Security->addToBlacklist();
return false;
}
// ============================================================================ // ============================================================================
// Main before POST // Main before POST
// ============================================================================ // ============================================================================
if ($_SERVER['REQUEST_METHOD']!=='POST') {
checkRememberMe();
}
// ============================================================================ // ============================================================================
// POST Method // POST Method
// ============================================================================ // ============================================================================
if ($_SERVER['REQUEST_METHOD']=='POST') if ($_SERVER['REQUEST_METHOD']=='POST') {
{
checkLogin($_POST); checkLogin($_POST);
} }

View File

@ -12,6 +12,10 @@
<input name="password" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Password') ?>" type="password"> <input name="password" class="uk-width-1-1 uk-form-large" placeholder="<?php $L->p('Password') ?>" type="password">
</div> </div>
<div class="uk-form-row">
<label><input type="checkbox" name="remember"> Remember me</label>
</div>
<div class="uk-form-row"> <div class="uk-form-row">
<button type="submit" class="uk-width-1-1 uk-button uk-button-primary uk-button-large"><?php $Language->p('Login') ?></button> <button type="submit" class="uk-width-1-1 uk-button uk-button-primary uk-button-large"><?php $Language->p('Login') ?></button>
</div> </div>

View File

@ -110,6 +110,11 @@ define('CLI_STATUS', 'published');
// Cli mode, username for new pages // Cli mode, username for new pages
define('CLI_USERNAME', 'admin'); define('CLI_USERNAME', 'admin');
// Remember me
define('REMEMBER_COOKIE_USERNAME', 'BLUDITREMEMBERUSERNAME');
define('REMEMBER_COOKIE_TOKEN', 'BLUDITREMEMBERTOKEN');
define('REMEMBER_COOKIE_EXPIRE_IN_DAYS', 30);
// Filename // Filename
define('FILENAME', 'index.txt'); define('FILENAME', 'index.txt');
@ -202,6 +207,7 @@ include(PATH_HELPERS.'paginator.class.php');
include(PATH_HELPERS.'image.class.php'); include(PATH_HELPERS.'image.class.php');
include(PATH_HELPERS.'tcp.class.php'); include(PATH_HELPERS.'tcp.class.php');
include(PATH_HELPERS.'dom.class.php'); include(PATH_HELPERS.'dom.class.php');
include(PATH_HELPERS.'cookie.class.php');
if (file_exists(PATH_KERNEL.'bludit.pro.php')) { if (file_exists(PATH_KERNEL.'bludit.pro.php')) {
include(PATH_KERNEL.'bludit.pro.php'); include(PATH_KERNEL.'bludit.pro.php');

View File

@ -11,8 +11,7 @@ class dbUsers extends dbJSON
'salt'=> array('inFile'=>false, 'value'=>'!Pink Floyd!Welcome to the machine!'), 'salt'=> array('inFile'=>false, 'value'=>'!Pink Floyd!Welcome to the machine!'),
'email'=> array('inFile'=>false, 'value'=>''), 'email'=> array('inFile'=>false, 'value'=>''),
'registered'=> array('inFile'=>false, 'value'=>'1985-03-15 10:00'), 'registered'=> array('inFile'=>false, 'value'=>'1985-03-15 10:00'),
'tokenEmail'=> array('inFile'=>false, 'value'=>''), 'tokenRemember'=> array('inFile'=>false, 'value'=>''),
'tokenEmailTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'),
'tokenAuth'=> array('inFile'=>false, 'value'=>''), 'tokenAuth'=> array('inFile'=>false, 'value'=>''),
'tokenAuthTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'), 'tokenAuthTTL'=> array('inFile'=>false, 'value'=>'2009-03-15 14:00'),
'twitter'=> array('inFile'=>false, 'value'=>''), 'twitter'=> array('inFile'=>false, 'value'=>''),
@ -119,7 +118,7 @@ class dbUsers extends dbJSON
return md5( uniqid().time().DOMAIN ); return md5( uniqid().time().DOMAIN );
} }
public function generateEmailToken() public function generateRememberToken()
{ {
return $this->generateAuthToken(); return $this->generateAuthToken();
} }
@ -134,6 +133,13 @@ class dbUsers extends dbJSON
return sha1($password.$salt); return sha1($password.$salt);
} }
public function setRememberToken($username, $token)
{
$args['username'] = $username;
$args['tokenRemember'] = $token;
return $this->set($args);
}
public function setPassword($username, $password) public function setPassword($username, $password)
{ {
$salt = $this->generateSalt(); $salt = $this->generateSalt();
@ -170,18 +176,25 @@ class dbUsers extends dbJSON
return false; return false;
} }
public function setTokenEmail($username) // Returns the username with the remember token assigned, FALSE otherwise
public function getByRememberToken($token)
{ {
// Random hash foreach ($this->db as $username=>$fields) {
$token = $this->generateEmailToken(); if ($fields['tokenRemember']==$token) {
$this->db[$username]['tokenEmail'] = $token; return $username;
}
}
return false;
}
// Token time to live, defined by TOKEN_EMAIL_TTL // This function clean all tokens for Remember me
$this->db[$username]['tokenEmailTTL'] = Date::currentOffset(DB_DATE_FORMAT, TOKEN_EMAIL_TTL); // This function is used when some hacker try to use an invalid remember token
public function invalidateAllRememberTokens()
// Save the database {
$this->save(); foreach ($this->db as $username=>$values) {
return $token; $this->db[$username]['tokenRemember'] = '';
}
return $this->save();
} }
// Returns array with the username databases filtered by username, FALSE otherwise // Returns array with the username databases filtered by username, FALSE otherwise

View File

@ -2,24 +2,28 @@
class Cookie { class Cookie {
public static function get($name) public static function get($key)
{ {
if(isset($_COOKIE[$name])) if (isset($_COOKIE[$key])) {
{ return $_COOKIE[$key];
return($_COOKIE[$name]); }
return false;
} }
return(false); public static function set($key, $value, $daysToExpire=30)
{
// The time the cookie expires.
// This is a Unix timestamp so is in number of seconds since the epoch.
// In other words, you'll most likely set this with the time() function plus the number of seconds before you want it to expire.
// Or you might use mktime(). time()+60*60*24*30 will set the cookie to expire in 30 days.
// If set to 0, or omitted, the cookie will expire at the end of the session (when the browser closes).
$expire = time()+60*60*24*$daysToExpire;
setcookie($key, $value, $expire);
} }
public static function add($name, $value, $expire = 525600) public static function isset($key)
{ {
setcookie($name, $value, time() + ($expire * 60)); return isset($_COOKIE[$key]);
}
public static function isSet($name)
{
return(isset($_COOKIE[$name]));
} }
} }

View File

@ -9,11 +9,13 @@ class Login {
$this->dbUsers = $dbUsers; $this->dbUsers = $dbUsers;
} }
// Returns the username of the user logged
public function username() public function username()
{ {
return Session::get('username'); return Session::get('username');
} }
// Returns the role of the user logged
public function role() public function role()
{ {
return Session::get('role'); return Session::get('role');
@ -26,9 +28,8 @@ class Login {
$username = Session::get('username'); $username = Session::get('username');
if (!empty($username)) { if (!empty($username)) {
return true; return true;
} } else {
else { Log::set(__METHOD__.LOG_SEP.'Session username empty, destroying the session.');
Log::set(__METHOD__.LOG_SEP.'Session username empty, destroy the session.');
Session::destroy(); Session::destroy();
return false; return false;
} }
@ -49,6 +50,33 @@ class Login {
Log::set(__METHOD__.LOG_SEP.'User logged, fingerprint: '.$this->fingerPrint()); Log::set(__METHOD__.LOG_SEP.'User logged, fingerprint: '.$this->fingerPrint());
} }
public function setRememberMe($username)
{
$username = Sanitize::html($username);
// Set the token on the users database
$token = $this->dbUsers->generateRememberToken();
$this->dbUsers->setRememberToken($username, $token);
// Set the token on the cookies
Cookie::set(REMEMBER_COOKIE_USERNAME, $username, REMEMBER_COOKIE_EXPIRE_IN_DAYS);
Cookie::set(REMEMBER_COOKIE_TOKEN, $token, REMEMBER_COOKIE_EXPIRE_IN_DAYS);
Log::set(__METHOD__.LOG_SEP.'Cookies seted for Remember Me.');
}
public function invalidateRememberMe()
{
// Invalidate all tokens on the user databases
$this->dbUsers->invalidateAllRememberTokens();
// Destroy the cookies
Cookie::set(REMEMBER_COOKIE_USERNAME, '', -1);
Cookie::set(REMEMBER_COOKIE_TOKEN, '', -1);
unset($_COOKIE[REMEMBER_COOKIE_USERNAME]);
unset($_COOKIE[REMEMBER_COOKIE_TOKEN]);
}
// Check if the username and the password are valid // Check if the username and the password are valid
// Returns TRUE if valid and set the session // Returns TRUE if valid and set the session
// Returns FALSE for invalid username or password // Returns FALSE for invalid username or password
@ -82,15 +110,21 @@ class Login {
Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username); Log::set(__METHOD__.LOG_SEP.'User logged succeeded by username and password - Username: '.$username);
return true; return true;
} }
else {
Log::set(__METHOD__.LOG_SEP.'Password incorrect.');
}
Log::set(__METHOD__.LOG_SEP.'Password incorrect.');
return false; return false;
} }
public function verifyUserByToken($username, $token) // Check if the user has the cookies and the correct token
public function verifyUserByRemember()
{ {
if (!Cookie::isset(REMEMBER_COOKIE_USERNAME) || !Cookie::isset(REMEMBER_COOKIE_TOKEN)) {
return false;
}
$username = Cookie::get(REMEMBER_COOKIE_USERNAME);
$token = Cookie::get(REMEMBER_COOKIE_TOKEN);
$username = Sanitize::html($username); $username = Sanitize::html($username);
$token = Sanitize::html($token); $token = Sanitize::html($token);
@ -98,55 +132,37 @@ class Login {
$token = trim($token); $token = trim($token);
if (empty($username) || empty($token)) { if (empty($username) || empty($token)) {
Log::set(__METHOD__.LOG_SEP.'Username or Token-email empty. Username: '.$username.' - Token-email: '.$token); $this->invalidateRememberMe();
Log::set(__METHOD__.LOG_SEP.'Username or Token empty. Username: '.$username.' - Token: '.$token);
return false; return false;
} }
if ($username !== $this->dbUsers->getByRememberToken($token)) {
$this->invalidateRememberMe();
Log::set(__METHOD__.LOG_SEP.'The user has different token or the token doesn\'t exist.');
return false;
}
// Validate user and login
$user = $this->dbUsers->getDb($username); $user = $this->dbUsers->getDb($username);
if($user==false) {
Log::set(__METHOD__.LOG_SEP.'Username does not exist: '.$username);
return false;
}
$currentTime = Date::current(DB_DATE_FORMAT);
if($user['tokenEmailTTL']<$currentTime) {
Log::set(__METHOD__.LOG_SEP.'Token-email expired: '.$username);
return false;
}
if($token === $user['tokenEmail'])
{
// Set the user loggued.
$this->setLogin($username, $user['role']); $this->setLogin($username, $user['role']);
Log::set(__METHOD__.LOG_SEP.'User authenticated via Remember Me.');
// Invalidate the current token.
$this->dbUsers->setTokenEmail($username);
Log::set(__METHOD__.LOG_SEP.'User logged succeeded by Token-email - Username: '.$username);
return true; return true;
} }
else {
Log::set(__METHOD__.LOG_SEP.'Token-email incorrect.');
}
return false;
}
public function fingerPrint() public function fingerPrint()
{ {
// User agent
$agent = getenv('HTTP_USER_AGENT'); $agent = getenv('HTTP_USER_AGENT');
if (empty($agent)) { if (empty($agent)) {
$agent = 'Bludit/2.0 (Mr Nibbler Protocol)'; $agent = 'Bludit/2.0 (Mr Nibbler Protocol)';
} }
return sha1($agent); return sha1($agent);
} }
public function logout() public function logout()
{ {
return Session::destroy(); $this->invalidateRememberMe();
Session::destroy();
return true;
} }
} }

1
bl-themes/clean-blog Submodule

@ -0,0 +1 @@
Subproject commit 1620e2f4640cf1dfcd04e2018e6e17b81d7b7e62

View File

@ -1 +0,0 @@
node_modules

View File

@ -1,11 +0,0 @@
sudo: false
language: node_js
node_js:
- "node"
install: npm install
script:
- npm test
- gulp
cache:
directories:
- node_modules

View File

@ -1,3 +0,0 @@
img {
max-width: 100%;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,136 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<?php
include(THEME_DIR_PHP.'head.php')
?>
</head>
<body>
<?php
Theme::plugins('siteBodyBegin')
?>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div class="container">
<a class="navbar-brand" href="<?php echo $Site->url() ?>"><?php echo $Site->title() ?></a>
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<?php echo $Language->get('Menu') ?>
<i class="fa fa-bars"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="<?php echo $Site->url() ?>"><?php echo $Language->get('Home') ?></a>
</li>
<?php
foreach ($staticPages as $staticPage) {
echo '<li class="nav-item">';
echo '<a class="nav-link" href="'.$staticPage->permalink().'">'.$staticPage->title().'</a>';
echo '</li>';
}
?>
</ul>
</div>
</div>
</nav>
<!-- Main Content -->
<?php
if ($WHERE_AM_I=='page') {
include(THEME_DIR_PHP.'page.php');
} else {
include(THEME_DIR_PHP.'home.php');
}
?>
<!-- Footer -->
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<ul class="list-inline text-center">
<?php
if ($Site->twitter()) {
echo '
<li class="list-inline-item">
<a href="'.$Site->twitter().'">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-twitter fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
';
}
if ($Site->facebook()) {
echo '
<li class="list-inline-item">
<a href="'.$Site->facebook().'">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-facebook fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
';
}
if ($Site->github()) {
echo '
<li class="list-inline-item">
<a href="'.$Site->github().'">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
';
}
if ($Site->codepen()) {
echo '
<li class="list-inline-item">
<a href="'.$Site->codepen().'">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-codepen fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
';
}
if ($Site->instagram()) {
echo '
<li class="list-inline-item">
<a href="'.$Site->instagram().'">
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-instagram fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
';
}
?>
</ul>
<p class="copyright text-muted"><?php echo $Site->footer() ?> - Powered by <a href="https://www.bludit.com">BLUDIT</a></p>
</div>
</div>
</div>
</footer>
<!-- Scripts -->
<?php
echo Theme::jquery(); // Jquery from Bludit Core
echo Theme::js('vendor/popper/popper.min.js');
echo Theme::js('vendor/bootstrap/js/bootstrap.min.js');
echo Theme::js('js/clean-blog.min.js');
?>
<?php
Theme::plugins('siteBodyEnd')
?>
</body>
</html>

View File

@ -1,16 +0,0 @@
<?php
/*
This file is loaded before the theme
You can add some configuration code in this file
*/
// Background image
$backgroundImage = 'https://source.unsplash.com/1600x900/?nature';
if ($page->coverImage()===false) {
$domImage = DOM::getFirstImage($page->content($fullContent=true));
if ($domImage!==false) {
$backgroundImage = $domImage;
}
} else {
$backgroundImage = $page->coverImage($absolute=true);
}

View File

@ -1,6 +0,0 @@
/*!
* Start Bootstrap - Clean Blog v4.0.0-beta (https://startbootstrap.com/template-overviews/clean-blog)
* Copyright 2013-2017 Start Bootstrap
* Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-clean-blog/blob/master/LICENSE)
*/
!function(i){"use strict";i("body").on("input propertychange",".floating-label-form-group",function(o){i(this).toggleClass("floating-label-form-group-with-value",!!i(o.target).val())}).on("focus",".floating-label-form-group",function(){i(this).addClass("floating-label-form-group-with-focus")}).on("blur",".floating-label-form-group",function(){i(this).removeClass("floating-label-form-group-with-focus")});if(i(window).width()>1170){var o=i("#mainNav").height();i(window).on("scroll",{previousTop:0},function(){var s=i(window).scrollTop();s<this.previousTop?s>0&&i("#mainNav").hasClass("is-fixed")?i("#mainNav").addClass("is-visible"):i("#mainNav").removeClass("is-visible is-fixed"):s>this.previousTop&&(i("#mainNav").removeClass("is-visible"),s>o&&!i("#mainNav").hasClass("is-fixed")&&i("#mainNav").addClass("is-fixed")),this.previousTop=s})}}(jQuery);

View File

@ -1,8 +0,0 @@
{
"theme-data":
{
"name": "Clean Blog",
"description": "Clean Blog ist ein Theme auf der Grundlage von Bootstrap. Es eignet sich insbesondere für Blogs."
},
"posted-by": "Veröffentlicht von"
}

View File

@ -1,8 +0,0 @@
{
"theme-data":
{
"name": "Clean Blog",
"description": "Clean Blog ist ein Theme auf der Grundlage von Bootstrap. Es eignet sich insbesondere für Blogs."
},
"posted-by": "Veröffentlicht von"
}

View File

@ -1,8 +0,0 @@
{
"theme-data":
{
"name": "Clean Blog",
"description": "Clean blog is a carefully styled Bootstrap blog theme that is perfect for personal or company blogs."
},
"posted-by": "Posted by"
}

View File

@ -1,8 +0,0 @@
{
"theme-data":
{
"name": "Clean Blog",
"description": "Tema sencillo y profesional, ideal para blogs personales o para empresas."
},
"posted-by": "Publicado por"
}

View File

@ -1,10 +0,0 @@
{
"author": "Start Bootstrap",
"email": "",
"website": "https://startbootstrap.com/template-overviews/clean-blog/",
"version": "1.0",
"releaseDate": "2017-10-13",
"license": "MIT",
"compatible": "2.0",
"notes": ""
}

View File

@ -1,25 +0,0 @@
<?php
echo Theme::charset('utf-8');
echo Theme::viewport('width=device-width, initial-scale=1, shrink-to-fit=no');
// Title and description
echo Theme::headTitle();
echo Theme::headDescription();
// Favicon
echo Theme::favicon('img/favicon.png');
// CSS
echo Theme::css('vendor/bootstrap/css/bootstrap.min.css');
echo Theme::css('css/clean-blog.min.css');
echo Theme::css('css/bludit.css');
// FontAwesome from Bludit Core
echo Theme::fontAwesome();
// Load plugins
Theme::plugins('siteHead');
?>
<link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>

View File

@ -1,55 +0,0 @@
<!-- Page Header -->
<header class="masthead" style="background-image: url('<?php echo $backgroundImage ?>')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="site-heading">
<h1><?php echo $Site->title() ?></h1>
<span class="subheading"><?php echo $Site->description() ?></span>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<!-- Content -->
<?php
foreach ($content as $page):
?>
<div class="post-preview">
<a href="<?php echo $page->permalink() ?>">
<h2 class="post-title"><?php echo $page->title() ?></h2>
<h3 class="post-subtitle"><?php echo $page->description() ?></h3>
</a>
<p class="post-meta"><?php echo $Language->get('Posted by').' '.$page->user('username').' - '.$page->date() ?></p>
</div>
<hr>
<?php
endforeach
?>
<!-- Pager -->
<div class="clearfix">
<?php
if(Paginator::showPrev()) {
echo '<a class="btn btn-secondary float-left" href="'.Paginator::prevPageUrl().'">&larr; '.$Language->get('Previous page').'</a>';
}
if(Paginator::showNext()) {
echo '<a class="btn btn-secondary float-right" href="'.Paginator::nextPageUrl().'">'.$Language->get('Next page').' &rarr;</a>';
}
?>
</div>
</div>
</div>
</div>
<hr>

View File

@ -1,27 +0,0 @@
<!-- Page Header -->
<header class="masthead" style="background-image: url('<?php echo $backgroundImage ?>')">
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<div class="post-heading">
<h1><?php echo $page->title() ?></h1>
<h2 class="subheading"><?php echo $page->description() ?></h2>
<p class="meta"><?php echo $Language->get('Posted by').' '.$page->user('username').' - '.$page->date() ?></p>
</div>
</div>
</div>
</div>
</header>
<!-- Post Content -->
<article>
<div class="container">
<div class="row">
<div class="col-lg-8 col-md-10 mx-auto">
<?php echo $page->content($fullContent=true) ?>
</div>
</div>
</div>
</article>
<hr>

File diff suppressed because one or more lines are too long

View File

@ -1,2 +0,0 @@
html{box-sizing:border-box;font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}*,::after,::before{box-sizing:inherit}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#868e96;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -37,7 +37,7 @@ h2 {
} }
/* PAGES */ /* PAGES */
article.page:not(:last-child) { article.page {
margin-bottom: 100px; margin-bottom: 100px;
} }