FEATURE #2047: use laravel's authorization

This commit is contained in:
Michal Kváček
2017-08-02 13:33:26 +02:00
parent 67fb3c6945
commit a4c020daa0
60 changed files with 1357 additions and 50 deletions
+1
View File
@@ -33,3 +33,4 @@
/public/fonts
/bower_components
.sass-cache
!/resources/views/vendor/
@@ -1,8 +1,8 @@
<?php
namespace App\Http\Controllers\Admin\FileManager;
use App\CMS\Helpers\Path;
use App\CMS\Helpers\Template;
use App\Http\Controllers\Admin\Controller;
use App\Http\Requests\Request;
use App\Models\FileManager\File;
@@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}
@@ -0,0 +1,45 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
public function logout() {
Auth::logout();
return redirect()->route('login');
}
}
@@ -0,0 +1,73 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
* @var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
*
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'g-recaptcha-response' => 'required|recaptcha',
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
*
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'role' => User::ROLE_VISITOR,
'state' => User::STATE_ACTIVE,
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
@@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = '/admin';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}
+15 -1
View File
@@ -2,6 +2,7 @@
namespace App\Http\Middleware;
use App\Models\User;
use Closure;
use Illuminate\Support\Facades\Auth;
@@ -18,13 +19,26 @@ class Authenticate
*/
public function handle($request, Closure $next, $guard = null)
{
if (!Auth::guard($guard)->check()) {
if (!Auth::guard($guard)
->check() || Auth::user()->status != User::STATE_ACTIVE || Auth::user()->role == User::ROLE_VISITOR) {
Auth::logout();
if ($request->ajax()) {
return response('Unauthorized.', 401);
}
// select right message
if (Auth::user() && Auth::user()->status != User::STATE_ACTIVE) {
$message = 'auth.userNotActive';
} elseif (Auth::user() && Auth::user()->role == User::ROLE_VISITOR) {
$message = 'auth.visitorInAdmin';
} else {
$message = 'auth.loginExpired';
}
return redirect()
->route('login')
->with('warning', trans($message))
->with('redirectTo', $request->fullUrl());
}
+5 -3
View File
@@ -3,8 +3,6 @@
namespace App\Listeners\Loggers;
use Illuminate\Auth\Events\Logout;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
class LogLogout extends Logger
{
@@ -12,10 +10,15 @@ class LogLogout extends Logger
* Handle the event.
*
* @param Logout $event
*
* @return void
*/
public function handle(Logout $event)
{
if (is_null($event->user)) {
return;
}
$parameters = [
'id' => $event->user->id,
'name' => $event->user->name,
@@ -27,7 +30,6 @@ class LogLogout extends Logger
/**
* Getter for action name. Used only internally
*
* @return mixed
*/
protected function getName()
+32 -9
View File
@@ -2,18 +2,27 @@
namespace App\Models;
use App\Notifications\ResetPasswordNotification;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticableInterface;
use Illuminate\Contracts\Auth\CanResetPassword;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Notifications\Notifiable;
/**
* Class User
*/
class User extends Model implements AuthenticableInterface
class User extends Model implements AuthenticableInterface, CanResetPassword
{
use SoftDeletes, Authenticatable, Authorizable;
use SoftDeletes, Authenticatable, Authorizable, Notifiable;
const STATE_DELETED = 'deleted';
const STATE_BLOCKED = 'blocked';
const STATE_ACTIVE = 'active';
const ROLE_VISITOR = 'visitor';
const ROLE_EDITOR = 'editor';
const ROLE_ADMIN = 'admin';
public $timestamps = true;
protected $fillable = [
'email',
@@ -24,17 +33,31 @@ class User extends Model implements AuthenticableInterface
];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
const STATE_DELETED = 'deleted';
const STATE_BLOCKED = 'blocked';
const STATE_ACTIVE = 'active';
const ROLE_VISITOR = 'visitor';
const ROLE_EDITOR = 'editor';
const ROLE_ADMIN = 'admin';
/**
* Get the e-mail address where password reset links are sent.
* @return string
*/
public function getEmailForPasswordReset()
{
return $this->email;
}
/**
* Send the password reset notification.
*
* @param string $token
*
* @return void
*/
public function sendPasswordResetNotification($token)
{
$this->notify(new ResetPasswordNotification($token));
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Notifications;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class ResetPasswordNotification extends Notification
{
/**
* @var string
*/
private $token;
/**
* Create a new notification instance.
* @return void
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
*
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = route('password.reset', ['token' => $this->token, 'email' => $notifiable->email]);
return (new MailMessage())->subject(trans('notification.forgottenPassword.subject'))
->line(trans('notification.forgottenPassword.firstLine'))
->action(trans('notification.forgottenPassword.button'), $url)
->line(trans('notification.forgottenPassword.lastLine'));
}
}
+1 -10
View File
@@ -14,15 +14,6 @@ class RouteServiceProvider extends ServiceProvider
*/
protected $namespace = 'App\Http\Controllers';
/**
* Define your route model bindings, pattern filters, etc.
* @return void
*/
public function boot()
{
parent::boot();
}
/**
* Define the routes for the application.
* @return void
@@ -41,6 +32,6 @@ class RouteServiceProvider extends ServiceProvider
{
Route::namespace($this->namespace)
->middleware('web')
->group(app_path('Http/routes.php'));
->group(base_path('routes/web.php'));
}
}
+2 -3
View File
@@ -153,10 +153,10 @@ return [
Illuminate\Translation\TranslationServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Illuminate\Notifications\NotificationServiceProvider::class,
Mcamara\LaravelLocalization\LaravelLocalizationServiceProvider::class,
\App\ModuleManager\Providers\ModuleManager::class,
\Nwidart\Menus\MenusServiceProvider::class,
// Pingpong\Menus\MenusServiceProvider::class,
Franzose\ClosureTable\ClosureTableServiceProvider::class,
\Watson\Sitemap\SitemapServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
@@ -185,7 +185,6 @@ return [
*/
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
@@ -221,7 +220,7 @@ return [
'View' => Illuminate\Support\Facades\View::class,
'Recaptcha' => Greggilbert\Recaptcha\Facades\Recaptcha::class,
'Shortcode' => Webwizo\Shortcodes\Facades\Shortcode::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
],
];
+18
View File
@@ -111,4 +111,22 @@ return [
'sendmail' => '/usr/sbin/sendmail -bs',
/*
|--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/
'markdown' => [
'theme' => 'default',
'paths' => [
resource_path('views/vendor/mail'),
],
]
];
+2
View File
@@ -10,4 +10,6 @@ return [
'loginToContinue' => 'Pro vstup je nutné přihlášení',
'yourEmail' => 'Váš e-mail',
'yourPassword' => 'Vaše heslo',
'sendConfirmEmail' => 'Odeslat odkaz pro resetování hesla',
'passwordReset' => 'Nové heslo'
];
+3
View File
@@ -3,4 +3,7 @@ return [
"accessDenied" => "Nelze přistoupit k tomuto zdroji",
"failed" => "Přihlašovací údaje nejsou správné.",
"throttle" => "Příliš mnoho neplatných přihlášení. Zkuste to znovu za :seconds sekund.",
'userNotActive' => 'Uživatel nebyl aktivován, či byl administrátorem zablokován.',
'visitorInAdmin' => 'Návštěvník nemá oprávnění do adminstrace!',
'loginExpired' => 'Vaše přihlášení expirovalo. Přihlašte se prosím znovu.'
];
+9
View File
@@ -0,0 +1,9 @@
<?php
return [
'forgottenPassword' => [
'firstLine' => 'Dostali jste tento e-mail, protože byl zaslán požadavek na reset hesla. Pro nastavení nového hesla klikněte na následující tlačítko:',
'lastLine' => 'V případě, že tento e-mail neočekáváte, můžete ho klidně ignorovat.',
'button' => 'Resetovat heslo',
'subject' => 'Odkaz pro změnu hesla'
]
];
+10
View File
@@ -0,0 +1,10 @@
<?php
return [
'heading' => 'Registrace nového uživatele',
'name' => 'Jméno',
'email' => 'E-mail',
'password' => 'Heslo',
'passwordConfirm' => 'Heslo (znovu)',
'register' => 'Vytvořit účet',
'setNewPassword' => 'Nastavit nové heslo'
];
+7
View File
@@ -26,6 +26,13 @@
<body>
@if (session('status'))
<div class="notification alert alert-info">
<i class="fa fw fa-check"></i>
<strong>{{ session('status') }}</strong>
</div>
@endif
@if (session('success'))
<div class="notification alert alert-success">
<i class="fa fw fa-check"></i>
@@ -8,7 +8,7 @@
<div class="card">
<header class="auth-header">
<h1 class="auth-title">
{{isset($pageName) ? $pageName : ''}}
@lang('admin/loginForm.login')
</h1>
</header>
<div class="auth-content">
@@ -24,6 +24,12 @@
name="email" id="email"
placeholder="@lang('admin/loginForm.yourEmail')"
required>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<label for="password">@lang('admin/loginForm.password')</label>
@@ -32,10 +38,24 @@
name="password" id="password"
placeholder="@lang('admin/loginForm.yourPassword')"
required>
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<div class="form-group text-center">
<button type="submit" class="btn btn-block btn-primary">Login</button>
</div>
<div class="form-group text-center">
<a class="btn btn-link" href="{{ route('password.request') }}">
@lang('admin/loginForm.forgottenPassword')
</a>
</div>
</form>
</div>
</div>
@@ -0,0 +1,42 @@
@extends('admin.layout')
@section('pageHeading', trans('admin/loginForm.forgottenPassword'))
@section('page')
<div class="auth">
<div class="auth-container">
<div class="card">
<header class="auth-header">
<h1 class="auth-title">
@lang('admin/loginForm.forgottenPassword')
</h1>
</header>
<div class="auth-content">
<form class="form-horizontal" method="POST" action="{{ route('password.email') }}">
{!! csrf_field() !!}
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email" class="col-md-4 control-label">@lang('register.email')</label>
<input id="email" type="email" class="form-control underlined" name="email"
value="{{ old('email') }}" required>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">
@lang('admin/loginForm.sendConfirmEmail')
</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,68 @@
@extends('admin.layout')
@section('pageHeading', trans('admin/loginForm.passwordReset'))
@section('page')
<div class="auth">
<div class="auth-container">
<div class="card">
<header class="auth-header">
<h1 class="auth-title">
@lang('admin/loginForm.passwordReset')
</h1>
</header>
<div class="auth-content">
<form class="form-horizontal" method="POST" action="{{ route('password.request') }}">
{!! csrf_field() !!}
<input type="hidden" name="token" value="{{ $token }}">
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email">@lang('register.email')</label>
<input id="email" type="email" class="form-control underlined" name="email"
value="{{ $email or old('email') }}" required autofocus>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password">@lang('register.password')</label>
<input id="password" type="password" class="form-control underlined" name="password"
required>
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
<div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
<label for="password-confirm">@lang('register.passwordConfirm')</label>
<input id="password-confirm" type="password" class="form-control underlined"
name="password_confirmation" required>
@if ($errors->has('password_confirmation'))
<span class="help-block">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">
@lang('register.setNewPassword')
</button>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection
+89
View File
@@ -0,0 +1,89 @@
@extends('admin.layout')
@section('pageHeading', trans('register.heading'))
@section('page')
<div class="auth">
<div class="auth-container">
<div class="card">
<header class="auth-header">
<h1 class="auth-title">
@lang('register.heading')
</h1>
</header>
<div class="auth-content">
<form class="form-horizontal" method="POST" action="{{ route('register') }}">
{!! csrf_field() !!}
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label for="name">@lang('register.name')</label>
<input id="name" type="text" class="form-control underlined" name="name"
value="{{ old('name') }}" placeholder="@lang('register.name')"
required autofocus>
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label for="email">@lang('register.email')</label>
<input id="email" type="email" class="form-control underlined" name="email"
placeholder="@lang('admin/loginForm.yourEmail')"
value="{{ old('email') }}" required>
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label for="password">@lang('register.password')</label>
<input id="password" type="password" class="form-control underlined" name="password"
required>
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<label for="password-confirm">@lang('register.passwordConfirm')</label>
<input id="password-confirm" type="password" class="form-control underlined"
name="password_confirmation" required>
</div>
<div class="form-group">
{!! \Greggilbert\Recaptcha\Facades\Recaptcha::render() !!}
@if ($errors->has('g-recaptcha-response'))
<span class="help-block">
<strong>{{ $errors->first('g-recaptcha-response') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block">
@lang('register.register')
</button>
</div>
<div class="clearfix"></div>
</form>
</div>
</div>
</div>
</div>
@endsection
+80
View File
@@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Styles -->
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
<div id="app">
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
<!-- Collapsed Hamburger -->
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<!-- Branding Image -->
<a class="navbar-brand" href="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a>
</div>
<div class="collapse navbar-collapse" id="app-navbar-collapse">
<!-- Left Side Of Navbar -->
<ul class="nav navbar-nav">
&nbsp;
</ul>
<!-- Right Side Of Navbar -->
<ul class="nav navbar-nav navbar-right">
<!-- Authentication Links -->
@if (Auth::guest())
<li><a href="{{ route('login') }}">Login</a></li>
<li><a href="{{ route('register') }}">Register</a></li>
@else
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ Auth::user()->name }} <span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
Logout
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
</li>
</ul>
</li>
@endif
</ul>
</div>
</div>
</nav>
@yield('content')
</div>
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color or 'blue' }}" target="_blank">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
+11
View File
@@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
+7
View File
@@ -0,0 +1,7 @@
<tr>
<td class="header">
<a href="{{ $url }}">
{{ $slot }}
</a>
</td>
</tr>
+51
View File
@@ -0,0 +1,51 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0">
{!! $header or '' !!}
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0">
<!-- Body content -->
<tr>
<td class="content-cell">
{!! $slot !!}
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{!! $slot !!}
@endcomponent
+13
View File
@@ -0,0 +1,13 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>
+7
View File
@@ -0,0 +1,7 @@
<table class="promotion" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
@@ -0,0 +1,13 @@
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<a href="{{ $url }}" class="button button-green" target="_blank">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
+7
View File
@@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
+3
View File
@@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>
+285
View File
@@ -0,0 +1,285 @@
/* Base */
body, body *:not(html):not(style):not(br):not(tr):not(code) {
font-family: Avenir, Helvetica, sans-serif;
box-sizing: border-box;
}
body {
background-color: #f5f8fa;
color: #74787E;
height: 100%;
hyphens: auto;
line-height: 1.4;
margin: 0;
-moz-hyphens: auto;
-ms-word-break: break-all;
width: 100% !important;
-webkit-hyphens: auto;
-webkit-text-size-adjust: none;
word-break: break-all;
word-break: break-word;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869D4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #2F3133;
font-size: 19px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
color: #2F3133;
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
color: #2F3133;
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
color: #74787E;
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
background-color: #f5f8fa;
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.content {
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #bbbfc3;
font-size: 19px;
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
/* Body */
.body {
background-color: #FFFFFF;
border-bottom: 1px solid #EDEFF2;
border-top: 1px solid #EDEFF2;
margin: 0;
padding: 0;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.inner-body {
background-color: #FFFFFF;
margin: 0 auto;
padding: 0;
width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #EDEFF2;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 12px;
}
/* Footer */
.footer {
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
}
.footer p {
color: #AEAEAE;
font-size: 12px;
text-align: center;
}
/* Tables */
.table table {
margin: 30px auto;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.table th {
border-bottom: 1px solid #EDEFF2;
padding-bottom: 8px;
}
.table td {
color: #74787E;
font-size: 15px;
line-height: 18px;
padding: 10px 0;
}
.content-cell {
padding: 35px;
}
/* Buttons */
.action {
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.button {
border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
color: #FFF;
display: inline-block;
text-decoration: none;
-webkit-text-size-adjust: none;
}
.button-blue {
background-color: #3097D1;
border-top: 10px solid #3097D1;
border-right: 18px solid #3097D1;
border-bottom: 10px solid #3097D1;
border-left: 18px solid #3097D1;
}
.button-green {
background-color: #2ab27b;
border-top: 10px solid #2ab27b;
border-right: 18px solid #2ab27b;
border-bottom: 10px solid #2ab27b;
border-left: 18px solid #2ab27b;
}
.button-red {
background-color: #bf5329;
border-top: 10px solid #bf5329;
border-right: 18px solid #bf5329;
border-bottom: 10px solid #bf5329;
border-left: 18px solid #bf5329;
}
/* Panels */
.panel {
margin: 0 0 21px;
}
.panel-content {
background-color: #EDEFF2;
padding: 16px;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Promotions */
.promotion {
background-color: #FFFFFF;
border: 2px dashed #9BA2AB;
margin: 0;
margin-bottom: 25px;
margin-top: 25px;
padding: 24px;
width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
}
.promotion h1 {
text-align: center;
}
.promotion p {
font-size: 15px;
text-align: center;
}
+36
View File
@@ -0,0 +1,36 @@
@component('mail::message')
{{-- Intro Lines --}}
@foreach ($introLines as $line)
{!!$line!!}
@endforeach
{{-- Action Button --}}
@isset($actionText)
<?php
switch ($level) {
case 'success':
$color = 'green';
break;
case 'error':
$color = 'red';
break;
default:
$color = 'blue';
}
?>
@component('mail::button', ['url' => $actionUrl, 'color' => $color])
{!!$actionText!!}
@endcomponent
@endisset
{{-- Outro Lines --}}
@foreach ($outroLines as $line)
{!!$line!!}
@endforeach
{{-- Salutation --}}
@if (! empty($salutation))
{!!$salutation!!}
@endif
@endcomponent
@@ -0,0 +1,12 @@
<li class="dropdown-submenu {{ $item->hasActiveOnChild() ? 'active' : '' }}">
<a tabindex="-1" href="#">{{ $child->title }}</a>
<ul class="dropdown-menu">
@foreach ($child->childs as $item)
@if ($item->hasChilds())
@include('menus::child.dropdown', ['child' => $item])
@else
@include('menus::child.item', compact('item'))
@endif
@endforeach
</ul>
</li>
@@ -0,0 +1,11 @@
@if ($item->isDivider())
<li class="divider"></li>
@elseif ($item->isHeader())
<li class="dropdown-header">{{ $item->title }}</li>
@else
<li class="{{ $item->isActive() ? 'active' : '' }}">
<a tabindex="-1" href="{{ $item->getUrl() }}">
{{ $item->title }}
</a>
</li>
@endif
@@ -0,0 +1,3 @@
<ul class="nav navbar-nav">
@include('menus::menu')
</ul>
@@ -0,0 +1,15 @@
<li class="dropdown {{ $item->hasActiveOnChild() ? 'active' : '' }}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
{{ $item->title }}
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
@foreach ($item->childs as $child)
@if ($child->hasChilds())
@include('menus::child.dropdown', ['item' => $child])
@else
@include('menus::item.item', ['item' => $child])
@endif
@endforeach
</ul>
</li>
@@ -0,0 +1,12 @@
@if ($item->isDivider())
<li class="divider"></li>
@elseif ($item->isHeader())
<li class="dropdown-header">{{ $item->title }}</li>
@else
<li class="{{ $item->isActive() ? 'active' : '' }}">
<a href="{{ $item->getUrl() }}" {!! $item->getAttributes() !!}>
{!! $item->getIcon() !!}
{{ $item->title }}
</a>
</li>
@endif
+7
View File
@@ -0,0 +1,7 @@
@foreach ($items as $item)
@if ($item->hasChilds())
@include('menus::item.dropdown', compact('item'))
@else
@include('menus::item.item', compact('item'))
@endif
@endforeach
@@ -0,0 +1,3 @@
<ul class="nav nav-pills nav-justified">
@include('menus::menu')
</ul>
@@ -0,0 +1,3 @@
<ul class="nav nav-pills nav-stacked">
@include('menus::menu')
</ul>
@@ -0,0 +1,3 @@
<ul class="nav nav-pills">
@include('menus::menu')
</ul>
@@ -0,0 +1,3 @@
<ul class="nav nav-tabs nav-justified">
@include('menus::menu')
</ul>
@@ -0,0 +1,3 @@
<ul class="nav nav-tabs">
@include('menus::menu')
</ul>
@@ -0,0 +1,3 @@
<ul class="nav navbar-nav navbar-left">
@include('menus::menu')
</ul>
@@ -0,0 +1,3 @@
<ul class="nav navbar-nav navbar-right">
@include('menus::menu')
</ul>
+49
View File
@@ -0,0 +1,49 @@
<style type="text/css">
.dropdown-submenu {
position: relative;
}
.dropdown-submenu>.dropdown-menu {
top: 0;
left: 100%;
margin-top: -6px;
margin-left: -1px;
-webkit-border-radius: 0 6px 6px 6px;
-moz-border-radius: 0 6px 6px;
border-radius: 0 6px 6px 6px;
}
.dropdown-submenu:hover>.dropdown-menu {
display: block;
}
.dropdown-submenu>a:after {
display: block;
content: " ";
float: right;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 5px 0 5px 5px;
border-left-color: #ccc;
margin-top: 5px;
margin-right: -10px;
}
.dropdown-submenu:hover>a:after {
border-left-color: #fff;
}
.dropdown-submenu.pull-left {
float: none;
}
.dropdown-submenu.pull-left>.dropdown-menu {
left: -100%;
margin-left: 10px;
-webkit-border-radius: 6px 0 6px 6px;
-moz-border-radius: 6px 0 6px 6px;
border-radius: 6px 0 6px 6px;
}
</style>
+36
View File
@@ -0,0 +1,36 @@
@if ($paginator->hasPages())
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="page-item disabled"><span class="page-link">&laquo;</span></li>
@else
<li class="page-item"><a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">&laquo;</a></li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="page-item disabled"><span class="page-link">{{ $element }}</span></li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="page-item active"><span class="page-link">{{ $page }}</span></li>
@else
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li class="page-item"><a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">&raquo;</a></li>
@else
<li class="page-item disabled"><span class="page-link">&raquo;</span></li>
@endif
</ul>
@endif
+36
View File
@@ -0,0 +1,36 @@
@if ($paginator->hasPages())
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="disabled"><span>&laquo;</span></li>
@else
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">&laquo;</a></li>
@endif
{{-- Pagination Elements --}}
@foreach ($elements as $element)
{{-- "Three Dots" Separator --}}
@if (is_string($element))
<li class="disabled"><span>{{ $element }}</span></li>
@endif
{{-- Array Of Links --}}
@if (is_array($element))
@foreach ($element as $page => $url)
@if ($page == $paginator->currentPage())
<li class="active"><span>{{ $page }}</span></li>
@else
<li><a href="{{ $url }}">{{ $page }}</a></li>
@endif
@endforeach
@endif
@endforeach
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">&raquo;</a></li>
@else
<li class="disabled"><span>&raquo;</span></li>
@endif
</ul>
@endif
@@ -0,0 +1,17 @@
@if ($paginator->hasPages())
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="page-item disabled"><span class="page-link">@lang('pagination.previous')</span></li>
@else
<li class="page-item"><a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li class="page-item"><a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
@else
<li class="page-item disabled"><span class="page-link">@lang('pagination.next')</span></li>
@endif
</ul>
@endif
@@ -0,0 +1,17 @@
@if ($paginator->hasPages())
<ul class="pagination">
{{-- Previous Page Link --}}
@if ($paginator->onFirstPage())
<li class="disabled"><span>@lang('pagination.previous')</span></li>
@else
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
@endif
{{-- Next Page Link --}}
@if ($paginator->hasMorePages())
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
@else
<li class="disabled"><span>@lang('pagination.next')</span></li>
@endif
</ul>
@endif
+3 -14
View File
@@ -10,6 +10,8 @@ Route::pattern('id', '\d+');
Route::pattern('url', '[0-9\-a-z]+');
/*******************************************************************************/
Auth::routes();
$this->get('logout', 'Auth\LoginController@logout')->name('logout');
Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bindings']], function () {
/**
@@ -24,7 +26,7 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bi
// include all routes in routes/admin subfolder
$files = ['contents', 'fileManager', 'settings', 'templates', 'users', 'widgets'];
foreach ($files as $file) {
require_once __DIR__ . DIRECTORY_SEPARATOR . 'routes' . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . $file . '.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . $file . '.php';
}
// display dashboard with some usefull information
@@ -38,10 +40,6 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bi
// search form with results
Route::get('search', 'Search@getSearch')
->name('search');
// logout method
Route::get('logout', 'Login@logout')
->name('logout');
});
/**************************************************************************************/
@@ -49,21 +47,12 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => ['bi
/**
* This section must be specified as last one
*/
Route::group(['middleware' => ['web']], function () {
// Homepage
Route::get('/', 'Visitor\Homepage@getIndex')
->name('page.index');
// display login form
Route::get('admin/login', 'Admin\Login@getLogin')
->name('loginForm');
// handle login
Route::post('admin/login', 'Admin\Login@postLogin')
->name('login');
// route for sitemap
Route::get('sitemap.xml', 'Visitor\Sitemap@getMap')
->name('sitemap');