Update + add in dashboard functionality, and document how to setup the dashboard in the readme

This commit is contained in:
Kevin MacMartin 2016-01-26 23:20:08 -05:00
parent 9bff17c34f
commit 79824835de
64 changed files with 3705 additions and 427 deletions

View file

@ -4,7 +4,7 @@ SITE_DESC="A website template"
APP_ENV=local
APP_DEBUG=true
APP_KEY=random_string
APP_URL=http://template.hypothetic.al
APP_URL=http://localhost
DB_HOST=localhost
DB_DATABASE=hypothetical
@ -17,6 +17,9 @@ CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync
MAILCHIMP_APIKEY=
MAILCHIMP_LISTID=
MAIL_SENDTO=null
MAIL_DRIVER=smtp
@ -26,3 +29,5 @@ MAIL_ADDRESS=null
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=tls
REGISTRATION=true

5
.gitignore vendored
View file

@ -4,4 +4,9 @@
/public/build
/public/css
/public/js
/public/fonts/FontAwesome.otf
/public/fonts/fontawesome-webfont.*
/public/fonts/glyphicons-halflings-regular.*
/public/uploads
/storage/exports
.env

View file

@ -40,6 +40,20 @@ class AuthController extends Controller
$this->middleware('guest', ['except' => 'logout']);
}
/**
* Show the application registration form.
*
* @return \Illuminate\Http\Response
*/
public function showRegistrationForm()
{
if (env('REGISTRATION', false)) {
return view('auth.register');
} else {
header('Location: /login');
}
}
/**
* Get a validator for an incoming registration request.
*
@ -49,8 +63,8 @@ class AuthController extends Controller
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
@ -63,10 +77,12 @@ class AuthController extends Controller
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
if (env('REGISTRATION', false)) {
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
}

View file

@ -14,12 +14,11 @@ class ContactController extends Controller
'message' => 'required'
]);
$contact = new Contact;
$name = $request['name'];
$email = $request['email'];
$message = $request['message'];
$contact = new Contact;
$contact->name = $name;
$contact->email = $email;
$contact->message = $message;

View file

@ -0,0 +1,216 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use Image;
use Excel;
use App\Models\Contact;
use App\Models\Subscriptions;
class DashboardController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard.
*
* @return Response
*/
public function index()
{
return view('dashboard.home', [
'heading' => 'Dashboard Home'
]);
}
/**
* Dashboard View
*/
public function getContact()
{
return view('dashboard.view', [
'heading' => 'Contact Form Submissions',
'model' => 'contact',
'rows' => Contact::getContactSubmissions(),
'cols' => [
[ 'Date', 'created_at' ],
[ 'Name', 'name' ],
[ 'Email', 'email' ],
[ 'Message', 'message' ]
]
]);
}
public function getSubscriptions()
{
return view('dashboard.view', [
'heading' => 'Subscriptions',
'model' => 'subscriptions',
'rows' => Subscriptions::getSubscriptions(),
'cols' => [
[ 'Date', 'created_at' ],
[ 'Email', 'email' ],
[ 'Name', 'name' ],
[ 'Postal / Zip', 'location' ]
]
]);
}
/**
* Dashboard Edit
*/
/**
* Dashboard Export: Export data as a spreadsheet
*/
public function getExport($model)
{
// set the name of the spreadsheet
$sheet_name = ucfirst($model);
// set the model using the 'model' request argument
switch ($model) {
case 'contact':
$headings = [ 'Date', 'Name', 'Email', 'Message' ];
$items = Contact::select('created_at', 'name', 'email', 'message')->get();
break;
case 'subscriptions':
$headings = [ 'Date', 'Email', 'Name', 'Postal / Zip' ];
$items = Subscriptions::select('created_at', 'email', 'name', 'location')->get();
break;
default:
abort(404);
}
Excel::create($sheet_name, function($excel) use($sheet_name, $headings, $items) {
$excel->sheet($sheet_name, function($sheet) use($sheet_name, $headings, $items) {
$sheet->fromArray($items);
$sheet->row(1, $headings);
});
})->store('xls')->export('xls');
}
/**
* Dashboard Image Upload: Upload images
*/
public function postImageUpload(Request $request)
{
if ($request->hasFile('file')) {
$file = base_path() . '/public/uploads/' . $request['model'] . '/' . $request['id'] . '.jpg';
$image = Image::make($request->file('file'));
$image->save($file);
} else {
return 'file-upload-fail';
}
return 'success';
}
/**
* Dashboard Edit: Create and edit rows
*/
public function postEdit(Request $request)
{
$this->validate($request, [
'id' => 'required',
'model' => 'required',
'columns' => 'required'
]);
// store the id request variable for easy access
$id = $request['id'];
// set the model using the 'model' request argument
switch ($request['model']) {
default:
return 'model-access-fail';
}
// populate the eloquent object with the remaining items in $request
foreach ($request['columns'] as $column) {
$item->$column = $request[$column];
}
// save the new or updated item
$item->save();
// return the id number in the format '^id:[0-9][0-9]*$' on success
return 'id:' . $item->id;
}
/**
* Dashboard Reorder: Reorder rows
*/
public function postReorder(Request $request)
{
$this->validate($request, [
'order' => 'required',
'column' => 'required',
'model' => 'required'
]);
$order = $request['order'];
$column = $request['column'];
// set the model using the 'model' request argument
switch ($request['model']) {
default:
return 'model-access-fail';
}
// update each row with the new order
foreach (array_keys($order) as $order_id) {
$item = $items::find($order_id);
$item->$column = $order[$order_id];
$item->save();
}
return 'success';
}
/**
* Dashboard Delete: Delete rows
*/
public function deleteDelete(Request $request)
{
$this->validate($request, [
'id' => 'required',
'model' => 'required'
]);
// set the model using the 'model' request argument
switch ($request['model']) {
default:
return 'model-access-fail';
}
// delete the row with the id using the 'id' request argument
if ($items::where('id', $request['id'])->exists()) {
$items::where('id', $request['id'])->delete();
} else {
return 'row-delete-fail';
}
// delete the associated image if one exists
$image_file = base_path() . '/public/uploads/' . $request['model'] . '/' . $request['id'] . '.jpg';
if (file_exists($image_file)) {
if (!unlink($image_file)) {
return 'image-delete-fail';
}
}
// Return a success
return 'success';
}
}

View file

@ -0,0 +1,39 @@
<?php namespace App\Http\Controllers;
use Newsletter;
use App\Models\Subscriptions;
use Illuminate\Http\Request;
class SubscriptionController extends Controller
{
public function postSubscriptionSubmit(Request $request)
{
$this->validate($request, [
'name' => 'required',
'email' => 'required|email',
'address' => array('required', 'regex:/^([A-Za-z][0-9][A-Za-z] *[0-9][A-Za-z][0-9]|[0-9][0-9][0-9][0-9][0-9])$/')
]);
$name = $request['name'];
$fname = preg_replace('/ .*$/', '', $name);
$lname = preg_match('/. ./', $name) === 1 ? preg_replace('/^[^ ][^ ]* /', '', $name) : '';
$email = $request['email'];
$address = $request['address'];
// Submit the subscription request
Newsletter::subscribe($email, [
'FNAME' => $fname,
'LNAME' => $lname,
'ADDRESS' => $address
]);
// Save to the database on success
$subscriptions = new Subscriptions;
$subscriptions->name = $name;
$subscriptions->email = $email;
$subscriptions->location = $address;
$subscriptions->save();
return 'success';
}
}

View file

@ -44,9 +44,9 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
}

View file

@ -21,7 +21,7 @@ class Authenticate
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
return redirect()->guest('/login');
}
}

View file

@ -18,7 +18,7 @@ class RedirectIfAuthenticated
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/');
return redirect('/dashboard');
}
return $next($request);

View file

@ -1,34 +1,47 @@
<?php
Route::group(['middleware' => ['web']], function () {
Route::group(['middleware' => 'web'], function () {
/*
|--------------------------------------------------------------------------
| Public Routes
|--------------------------------------------------------------------------
*/
Route::get('/', function () {
return view('website.home');
Route::get('/', function() {
Head::setTitle('Home');
return view('website.index');
});
Route::get('/contact', function() {
Head::setTitle('Contact');
return view('website.contact');
});
Route::post('/contact-submit', 'ContactController@postContactSubmit');
/*
|--------------------------------------------------------------------------
| Content Management Routes
| Post Routes
|--------------------------------------------------------------------------
*/
// Authentication
Route::get('auth/login', 'Auth\AuthController@getLogin');
Route::post('auth/login', 'Auth\AuthController@postLogin');
Route::get('auth/logout', 'Auth\AuthController@getLogout');
Route::post('/contact-submit', 'ContactController@postContactSubmit');
Route::post('/subscription-submit', 'SubscriptionController@postSubscriptionSubmit');
// Registration
Route::get('auth/register', 'Auth\AuthController@getRegister');
Route::post('auth/register', 'Auth\AuthController@postRegister');
/*
|--------------------------------------------------------------------------
| Authentication Routes
|--------------------------------------------------------------------------
*/
Route::auth();
/*
|--------------------------------------------------------------------------
| Dashboard Routes
|--------------------------------------------------------------------------
*/
Route::group(['prefix' => 'dashboard'], function() {
Route::get('/', 'DashboardController@index');
Route::controller('', DashboardController::class);
});
});

View file

@ -1,6 +1,4 @@
<?php
namespace App\Models;
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
@ -8,4 +6,10 @@ class Contact extends Model
{
// the contact table
protected $table = 'contact';
// returns the list of all contact submissions
public static function getContactSubmissions()
{
return self::orderBy('created_at', 'desc')->get();
}
}

View file

@ -0,0 +1,15 @@
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Subscriptions extends Model
{
// the subscriptions table
protected $table = 'subscriptions';
// returns the list of all subscriptions
public static function getSubscriptions()
{
return self::orderBy('created_at', 'desc')->get();
}
}

View file

@ -7,6 +7,12 @@
"description": "A hypothetical website template",
"license": "MIT",
"dependencies": {
"bootstrap": "~3.3.6"
"bootstrap": "~3.3.6",
"font-awesome": "~4.5.0",
"awesome-bootstrap-checkbox": "~0.3.5",
"Sortable": "~1.4.2",
"simplemde": "~1.9.0",
"datetimepicker": "~2.4.5",
"SpinKit": "~1.2.5"
}
}

View file

@ -9,7 +9,10 @@
"laravel/framework": "5.2.*",
"radic/blade-extensions": "~6.0",
"erusev/parsedown": "~1.5",
"gwnobots/laravel-head": "dev-master"
"gwnobots/laravel-head": "dev-master",
"spatie/laravel-newsletter": "^2.2",
"intervention/image": "^2.3",
"maatwebsite/excel": "~2.0.0"
},
"require-dev": {
"fzaninotto/faker": "~1.4",

704
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "909b8a65ce3c9081674735483be15837",
"content-hash": "a72eccf8b74633ef449571150228ba0e",
"hash": "1c79203a020edce6fbdbf7c0f5ce5905",
"content-hash": "b4d80c4f47f54b01adf0017122832a48",
"packages": [
{
"name": "anahkiasen/underscore-php",
@ -357,6 +357,64 @@
],
"time": "2015-03-11 15:46:37"
},
{
"name": "guzzlehttp/psr7",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/4d0bdbe1206df7440219ce14c972aa57cc5e4982",
"reference": "4d0bdbe1206df7440219ce14c972aa57cc5e4982",
"shasum": ""
},
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"GuzzleHttp\\Psr7\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "PSR-7 message implementation",
"keywords": [
"http",
"message",
"stream",
"uri"
],
"time": "2015-11-03 01:34:55"
},
{
"name": "gwnobots/laravel-head",
"version": "dev-master",
@ -405,6 +463,68 @@
],
"time": "2015-03-09 05:14:57"
},
{
"name": "intervention/image",
"version": "2.3.5",
"source": {
"type": "git",
"url": "https://github.com/Intervention/image.git",
"reference": "9f29360b8ab94585cb9e80cf9045abd5b85feb89"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Intervention/image/zipball/9f29360b8ab94585cb9e80cf9045abd5b85feb89",
"reference": "9f29360b8ab94585cb9e80cf9045abd5b85feb89",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
"guzzlehttp/psr7": "~1.1",
"php": ">=5.4.0"
},
"require-dev": {
"mockery/mockery": "~0.9.2",
"phpunit/phpunit": "3.*"
},
"suggest": {
"ext-gd": "to use GD library based image processing.",
"ext-imagick": "to use Imagick based image processing.",
"intervention/imagecache": "Caching extension for the Intervention Image library"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-4": {
"Intervention\\Image\\": "src/Intervention/Image"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Oliver Vogel",
"email": "oliver@olivervogel.net",
"homepage": "http://olivervogel.net/"
}
],
"description": "Image handling and manipulation library with support for Laravel integration",
"homepage": "http://image.intervention.io/",
"keywords": [
"gd",
"image",
"imagick",
"laravel",
"thumbnail",
"watermark"
],
"time": "2016-01-02 19:15:13"
},
{
"name": "jakub-onderka/php-console-color",
"version": "0.1",
@ -552,16 +672,16 @@
},
{
"name": "laravel/framework",
"version": "v5.2.6",
"version": "v5.2.10",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "b774c304ee81fcf5402fa06b261b364ba8f29cf0"
"reference": "93dc5b0089eef468157fd7200e575c3861ec59a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/b774c304ee81fcf5402fa06b261b364ba8f29cf0",
"reference": "b774c304ee81fcf5402fa06b261b364ba8f29cf0",
"url": "https://api.github.com/repos/laravel/framework/zipball/93dc5b0089eef468157fd7200e575c3861ec59a5",
"reference": "93dc5b0089eef468157fd7200e575c3861ec59a5",
"shasum": ""
},
"require": {
@ -676,7 +796,61 @@
"framework",
"laravel"
],
"time": "2015-12-31 17:41:58"
"time": "2016-01-13 20:29:10"
},
{
"name": "laravelcollective/html",
"version": "v5.2.1",
"source": {
"type": "git",
"url": "https://github.com/LaravelCollective/html.git",
"reference": "add2c74b144d0a25ba27a1506b7ce84e6bcadd64"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/LaravelCollective/html/zipball/add2c74b144d0a25ba27a1506b7ce84e6bcadd64",
"reference": "add2c74b144d0a25ba27a1506b7ce84e6bcadd64",
"shasum": ""
},
"require": {
"illuminate/http": "5.2.*",
"illuminate/routing": "5.2.*",
"illuminate/session": "5.2.*",
"illuminate/support": "5.2.*",
"illuminate/view": "5.2.*",
"php": ">=5.5.9"
},
"require-dev": {
"illuminate/database": "5.2.*",
"mockery/mockery": "~0.9",
"phpunit/phpunit": "~4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Collective\\Html\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylorotwell@gmail.com"
},
{
"name": "Adam Engebretson",
"email": "adam@laravelcollective.com"
}
],
"description": "HTML and Form Builders for the Laravel Framework",
"homepage": "http://laravelcollective.com",
"time": "2016-01-15 21:59:03"
},
{
"name": "league/flysystem",
@ -762,6 +936,119 @@
],
"time": "2015-12-19 20:16:43"
},
{
"name": "maatwebsite/excel",
"version": "v2.0.9",
"source": {
"type": "git",
"url": "https://github.com/Maatwebsite/Laravel-Excel.git",
"reference": "bd9428da19fb3de9bbdd80f18f31b744485dc250"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Maatwebsite/Laravel-Excel/zipball/bd9428da19fb3de9bbdd80f18f31b744485dc250",
"reference": "bd9428da19fb3de9bbdd80f18f31b744485dc250",
"shasum": ""
},
"require": {
"illuminate/cache": "~5.0|~5.1",
"illuminate/config": "~5.0|~5.1",
"illuminate/filesystem": "~5.0|~5.1",
"illuminate/support": "~5.0|~5.1",
"nesbot/carbon": "~1.0",
"php": ">=5.3.0",
"phpoffice/phpexcel": "~1.8.0",
"tijsverkoyen/css-to-inline-styles": "~1.5"
},
"require-dev": {
"mockery/mockery": "~0.9",
"orchestra/testbench": "~3.0.0",
"phpseclib/phpseclib": ">=0.3.7",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"illuminate/http": "~5.0|~5.1",
"illuminate/routing": "~5.0|~5.1",
"illuminate/view": "~5.0|~5.1"
},
"type": "library",
"autoload": {
"classmap": [
"src/Maatwebsite/Excel",
"tests/TestCase.php"
],
"psr-0": {
"Maatwebsite\\Excel\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Maatwebsite.nl",
"email": "patrick@maatwebsite.nl"
}
],
"description": "An eloquent way of importing and exporting Excel and CSV in Laravel 4 with the power of PHPExcel",
"keywords": [
"PHPExcel",
"batch",
"csv",
"excel",
"export",
"import",
"laravel"
],
"time": "2015-10-26 10:15:37"
},
{
"name": "mailchimp/mailchimp",
"version": "2.0.6",
"source": {
"type": "git",
"url": "https://bitbucket.org/mailchimp/mailchimp-api-php.git",
"reference": "7ac99b5ac746d5875c5c350ad7e3b83674c83ec1"
},
"dist": {
"type": "zip",
"url": "https://bitbucket.org/mailchimp/mailchimp-api-php/get/7ac99b5ac746d5875c5c350ad7e3b83674c83ec1.zip",
"reference": "7ac99b5ac746d5875c5c350ad7e3b83674c83ec1",
"shasum": ""
},
"require": {
"php": ">=5.2.0"
},
"require-dev": {
"apigen/apigen": "dev-master"
},
"type": "library",
"autoload": {
"psr-0": {
"Mailchimp": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "MailChimp Devs",
"email": "api@mailchimp.com",
"homepage": "http://mailchimp.com",
"role": "Developer"
}
],
"description": "API client library for the MailChimp bulk email as a service platform",
"homepage": "https://bitbucket.org/mailchimp/mailchimp-api-php",
"keywords": [
"api",
"email"
],
"time": "2014-10-30 20:38:12"
},
{
"name": "monolog/monolog",
"version": "1.17.2",
@ -983,16 +1270,16 @@
},
{
"name": "paragonie/random_compat",
"version": "1.1.4",
"version": "1.1.5",
"source": {
"type": "git",
"url": "https://github.com/paragonie/random_compat.git",
"reference": "d762ee5b099a29044603cd4649851e81aa66cb47"
"reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/d762ee5b099a29044603cd4649851e81aa66cb47",
"reference": "d762ee5b099a29044603cd4649851e81aa66cb47",
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/dd8998b7c846f6909f4e7a5f67fabebfc412a4f7",
"reference": "dd8998b7c846f6909f4e7a5f67fabebfc412a4f7",
"shasum": ""
},
"require": {
@ -1027,7 +1314,7 @@
"pseudorandom",
"random"
],
"time": "2015-12-10 14:48:13"
"time": "2016-01-06 13:31:20"
},
{
"name": "patchwork/utf8",
@ -1088,6 +1375,112 @@
],
"time": "2015-12-15 15:34:15"
},
{
"name": "phpoffice/phpexcel",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PHPExcel.git",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"reference": "372c7cbb695a6f6f1e62649381aeaa37e7e70b32",
"shasum": ""
},
"require": {
"ext-xml": "*",
"ext-xmlwriter": "*",
"php": ">=5.2.0"
},
"type": "library",
"autoload": {
"psr-0": {
"PHPExcel": "Classes/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL"
],
"authors": [
{
"name": "Maarten Balliauw",
"homepage": "http://blog.maartenballiauw.be"
},
{
"name": "Mark Baker"
},
{
"name": "Franck Lefevre",
"homepage": "http://blog.rootslabs.net"
},
{
"name": "Erik Tilt"
}
],
"description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
"homepage": "http://phpexcel.codeplex.com",
"keywords": [
"OpenXML",
"excel",
"php",
"spreadsheet",
"xls",
"xlsx"
],
"time": "2015-05-01 07:00:55"
},
{
"name": "psr/http-message",
"version": "1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"reference": "85d63699f0dbedb190bbd4b0d2b9dc707ea4c298",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"time": "2015-05-04 20:22:00"
},
{
"name": "psr/log",
"version": "1.0.0",
@ -1257,16 +1650,16 @@
},
{
"name": "sebwite/support",
"version": "1.0.5",
"version": "1.0.10",
"source": {
"type": "git",
"url": "https://bitbucket.org/sebwitepackages/sebwite-support.git",
"reference": "f731cbdf8f7c608fd79c6373b5e50c4c9efdfd9e"
"reference": "e93caad0a5a1c89847d2c0c02e49f986945fce45"
},
"dist": {
"type": "zip",
"url": "https://bitbucket.org/sebwitepackages/sebwite-support/get/f731cbdf8f7c608fd79c6373b5e50c4c9efdfd9e.zip",
"reference": "f731cbdf8f7c608fd79c6373b5e50c4c9efdfd9e",
"url": "https://bitbucket.org/sebwitepackages/sebwite-support/get/e93caad0a5a1c89847d2c0c02e49f986945fce45.zip",
"reference": "e93caad0a5a1c89847d2c0c02e49f986945fce45",
"shasum": ""
},
"require": {
@ -1277,8 +1670,11 @@
"illuminate/console": "~5.1",
"illuminate/filesystem": "~5.1",
"illuminate/support": "~5.1",
"laravelcollective/html": "~5.1",
"php": ">=5.5.9",
"symfony/filesystem": "~2.7",
"radic/blade-extensions": "~6.1",
"symfony/filesystem": "~2.7|~3.0",
"symfony/options-resolver": "~2.0|~3.0",
"webmozart/path-util": "~1.0"
},
"require-dev": {
@ -1313,7 +1709,58 @@
"structure",
"support"
],
"time": "2015-12-28 19:20:17"
"time": "2016-01-13 11:02:15"
},
{
"name": "spatie/laravel-newsletter",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-newsletter.git",
"reference": "cdfe4de92f4c822904bfaee5f85a98d495ca56d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-newsletter/zipball/cdfe4de92f4c822904bfaee5f85a98d495ca56d3",
"reference": "cdfe4de92f4c822904bfaee5f85a98d495ca56d3",
"shasum": ""
},
"require": {
"illuminate/support": "~5.0",
"mailchimp/mailchimp": "~2.0",
"php": ">=5.4.0"
},
"require-dev": {
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "4.*",
"scrutinizer/ocular": "~1.1"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\Newsletter\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://murze.be",
"role": "Developer"
}
],
"description": "Manage newsletters in Laravel 5",
"homepage": "https://github.com/spatie/laravel-newsletter",
"keywords": [
"laravel",
"mailchimp",
"newsletter"
],
"time": "2015-12-21 22:48:10"
},
{
"name": "swiftmailer/swiftmailer",
@ -1428,6 +1875,59 @@
"homepage": "https://symfony.com",
"time": "2015-12-22 10:39:06"
},
{
"name": "symfony/css-selector",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3",
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\CssSelector\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jean-François Simon",
"email": "jeanfrancois.simon@sensiolabs.com"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2015-12-05 17:45:07"
},
{
"name": "symfony/debug",
"version": "v3.0.1",
@ -1547,25 +2047,25 @@
},
{
"name": "symfony/filesystem",
"version": "v2.8.1",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc"
"reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/a7ad724530a764d70c168d321ac226ba3d2f10fc",
"reference": "a7ad724530a764d70c168d321ac226ba3d2f10fc",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/c2e59d11dccd135dc8f00ee97f34fe1de842e70c",
"reference": "c2e59d11dccd135dc8f00ee97f34fe1de842e70c",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
"dev-master": "3.0-dev"
}
},
"autoload": {
@ -1592,7 +2092,7 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2015-12-22 10:25:57"
"time": "2015-12-22 10:39:06"
},
{
"name": "symfony/finder",
@ -1777,6 +2277,60 @@
"homepage": "https://symfony.com",
"time": "2015-12-26 16:46:13"
},
{
"name": "symfony/options-resolver",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "8e68c053a39e26559357cc742f01a7182ce40785"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/8e68c053a39e26559357cc742f01a7182ce40785",
"reference": "8e68c053a39e26559357cc742f01a7182ce40785",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\OptionsResolver\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony OptionsResolver Component",
"homepage": "https://symfony.com",
"keywords": [
"config",
"configuration",
"options"
],
"time": "2015-11-18 13:48:51"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.0.1",
@ -2194,6 +2748,53 @@
],
"time": "2015-12-05 11:13:14"
},
{
"name": "tijsverkoyen/css-to-inline-styles",
"version": "1.5.5",
"source": {
"type": "git",
"url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
"reference": "9753fc340726e327e4d48b7c0604f85475ae0bc3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/9753fc340726e327e4d48b7c0604f85475ae0bc3",
"reference": "9753fc340726e327e4d48b7c0604f85475ae0bc3",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/css-selector": "~2.1|~3.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5.x-dev"
}
},
"autoload": {
"psr-4": {
"TijsVerkoyen\\CssToInlineStyles\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"authors": [
{
"name": "Tijs Verkoyen",
"email": "css_to_inline_styles@verkoyen.eu",
"role": "Developer"
}
],
"description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
"homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
"time": "2015-12-08 16:14:14"
},
{
"name": "vlucas/phpdotenv",
"version": "v2.2.0",
@ -3352,59 +3953,6 @@
"homepage": "https://github.com/sebastianbergmann/version",
"time": "2015-06-21 13:59:46"
},
{
"name": "symfony/css-selector",
"version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4613311fd46e146f506403ce2f8a0c71d402d2a3",
"reference": "4613311fd46e146f506403ce2f8a0c71d402d2a3",
"shasum": ""
},
"require": {
"php": ">=5.5.9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\CssSelector\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jean-François Simon",
"email": "jeanfrancois.simon@sensiolabs.com"
},
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2015-12-05 17:45:07"
},
{
"name": "symfony/dom-crawler",
"version": "v3.0.1",

View file

@ -160,6 +160,9 @@ return [
*/
Radic\BladeExtensions\BladeExtensionsServiceProvider::class,
Gwnobots\LaravelHead\LaravelHeadServiceProvider::class,
Spatie\Newsletter\NewsletterServiceProvider::class,
Intervention\Image\ImageServiceProvider::class,
Maatwebsite\Excel\ExcelServiceProvider::class,
],
@ -206,6 +209,13 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
],
/*
* Custom Class Aliases...
*/
'Newsletter' => Spatie\Newsletter\NewsletterFacade::class,
'Image' => Intervention\Image\Facades\Image::class,
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
]
];

683
config/excel.php Normal file
View file

@ -0,0 +1,683 @@
<?php
return array(
'cache' => array(
/*
|--------------------------------------------------------------------------
| Enable/Disable cell caching
|--------------------------------------------------------------------------
*/
'enable' => true,
/*
|--------------------------------------------------------------------------
| Caching driver
|--------------------------------------------------------------------------
|
| Set the caching driver
|
| Available methods:
| memory|gzip|serialized|igbinary|discISAM|apc|memcache|temp|wincache|sqlite|sqlite3
|
*/
'driver' => 'memory',
/*
|--------------------------------------------------------------------------
| Cache settings
|--------------------------------------------------------------------------
*/
'settings' => array(
'memoryCacheSize' => '32MB',
'cacheTime' => 600
),
/*
|--------------------------------------------------------------------------
| Memcache settings
|--------------------------------------------------------------------------
*/
'memcache' => array(
'host' => 'localhost',
'port' => 11211,
),
/*
|--------------------------------------------------------------------------
| Cache dir (for discISAM)
|--------------------------------------------------------------------------
*/
'dir' => storage_path('cache')
),
'properties' => array(
'creator' => env('SITE_NAME', 'Dashboard'),
'lastModifiedBy' => env('SITE_NAME', 'Dashboard'),
'title' => 'Spreadsheet Export',
'description' => 'Exported Spreadsheet Data',
'subject' => 'Spreadsheet export',
'keywords' => 'excel, export',
'category' => 'Excel',
'manager' => env('SITE_NAME', 'Dashboard'),
'company' => env('SITE_NAME', 'Dashboard'),
),
/*
|--------------------------------------------------------------------------
| Sheets settings
|--------------------------------------------------------------------------
*/
'sheets' => array(
/*
|--------------------------------------------------------------------------
| Default page setup
|--------------------------------------------------------------------------
*/
'pageSetup' => array(
'orientation' => 'portrait',
'paperSize' => '9',
'scale' => '100',
'fitToPage' => false,
'fitToHeight' => true,
'fitToWidth' => true,
'columnsToRepeatAtLeft' => array('', ''),
'rowsToRepeatAtTop' => array(0, 0),
'horizontalCentered' => false,
'verticalCentered' => false,
'printArea' => null,
'firstPageNumber' => null,
),
),
/*
|--------------------------------------------------------------------------
| Creator
|--------------------------------------------------------------------------
|
| The default creator of a new Excel file
|
*/
'creator' => env('SITE_NAME', 'Dashboard'),
'csv' => array(
/*
|--------------------------------------------------------------------------
| Delimiter
|--------------------------------------------------------------------------
|
| The default delimiter which will be used to read out a CSV file
|
*/
'delimiter' => ',',
/*
|--------------------------------------------------------------------------
| Enclosure
|--------------------------------------------------------------------------
*/
'enclosure' => '"',
/*
|--------------------------------------------------------------------------
| Line endings
|--------------------------------------------------------------------------
*/
'line_ending' => "\r\n"
),
'export' => array(
/*
|--------------------------------------------------------------------------
| Autosize columns
|--------------------------------------------------------------------------
|
| Disable/enable column autosize or set the autosizing for
| an array of columns ( array('A', 'B') )
|
*/
'autosize' => true,
/*
|--------------------------------------------------------------------------
| Autosize method
|--------------------------------------------------------------------------
|
| --> PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX
| The default is based on an estimate, which does its calculation based
| on the number of characters in the cell value (applying any calculation
| and format mask, and allowing for wordwrap and rotation) and with an
| "arbitrary" adjustment based on the font (Arial, Calibri or Verdana,
| defaulting to Calibri if any other font is used) and a proportional
| adjustment for the font size.
|
| --> PHPExcel_Shared_Font::AUTOSIZE_METHOD_EXACT
| The second method is more accurate, based on actual style formatting as
| well (bold, italic, etc), and is calculated by generating a gd2 imagettf
| bounding box and using its dimensions to determine the size; but this
| method is significantly slower, and its accuracy is still dependent on
| having the appropriate fonts installed.
|
*/
'autosize-method' => PHPExcel_Shared_Font::AUTOSIZE_METHOD_APPROX,
/*
|--------------------------------------------------------------------------
| Auto generate table heading
|--------------------------------------------------------------------------
|
| If set to true, the array indices (or model attribute names)
| will automatically be used as first row (table heading)
|
*/
'generate_heading_by_indices' => true,
/*
|--------------------------------------------------------------------------
| Auto set alignment on merged cells
|--------------------------------------------------------------------------
*/
'merged_cell_alignment' => 'left',
/*
|--------------------------------------------------------------------------
| Pre-calculate formulas during export
|--------------------------------------------------------------------------
*/
'calculate' => false,
/*
|--------------------------------------------------------------------------
| Include Charts during export
|--------------------------------------------------------------------------
*/
'includeCharts' => false,
/*
|--------------------------------------------------------------------------
| Default sheet settings
|--------------------------------------------------------------------------
*/
'sheets' => array(
/*
|--------------------------------------------------------------------------
| Default page margin
|--------------------------------------------------------------------------
|
| 1) When set to false, default margins will be used
| 2) It's possible to enter a single margin which will
| be used for all margins.
| 3) Alternatively you can pass an array with 4 margins
| Default order: array(top, right, bottom, left)
|
*/
'page_margin' => false,
/*
|--------------------------------------------------------------------------
| Value in source array that stands for blank cell
|--------------------------------------------------------------------------
*/
'nullValue' => null,
/*
|--------------------------------------------------------------------------
| Insert array starting from this cell address as the top left coordinate
|--------------------------------------------------------------------------
*/
'startCell' => 'A1',
/*
|--------------------------------------------------------------------------
| Apply strict comparison when testing for null values in the array
|--------------------------------------------------------------------------
*/
'strictNullComparison' => false
),
/*
|--------------------------------------------------------------------------
| Store settings
|--------------------------------------------------------------------------
*/
'store' => array(
/*
|--------------------------------------------------------------------------
| Path
|--------------------------------------------------------------------------
|
| The path we want to save excel file to
|
*/
'path' => storage_path('exports'),
/*
|--------------------------------------------------------------------------
| Return info
|--------------------------------------------------------------------------
|
| Whether we want to return information about the stored file or not
|
*/
'returnInfo' => false
),
/*
|--------------------------------------------------------------------------
| PDF Settings
|--------------------------------------------------------------------------
*/
'pdf' => array(
/*
|--------------------------------------------------------------------------
| PDF Drivers
|--------------------------------------------------------------------------
| Supported: DomPDF, tcPDF, mPDF
*/
'driver' => 'DomPDF',
/*
|--------------------------------------------------------------------------
| PDF Driver settings
|--------------------------------------------------------------------------
*/
'drivers' => array(
/*
|--------------------------------------------------------------------------
| DomPDF settings
|--------------------------------------------------------------------------
*/
'DomPDF' => array(
'path' => base_path('vendor/dompdf/dompdf/')
),
/*
|--------------------------------------------------------------------------
| tcPDF settings
|--------------------------------------------------------------------------
*/
'tcPDF' => array(
'path' => base_path('vendor/tecnick.com/tcpdf/')
),
/*
|--------------------------------------------------------------------------
| mPDF settings
|--------------------------------------------------------------------------
*/
'mPDF' => array(
'path' => base_path('vendor/mpdf/mpdf/')
),
)
)
),
'filters' => array(
/*
|--------------------------------------------------------------------------
| Register read filters
|--------------------------------------------------------------------------
*/
'registered' => array(
'chunk' => 'Maatwebsite\Excel\Filters\ChunkReadFilter'
),
/*
|--------------------------------------------------------------------------
| Enable certain filters for every file read
|--------------------------------------------------------------------------
*/
'enabled' => array()
),
'import' => array(
/*
|--------------------------------------------------------------------------
| Has heading
|--------------------------------------------------------------------------
|
| The sheet has a heading (first) row which we can use as attribute names
|
| Options: true|false|slugged|slugged_with_count|ascii|numeric|hashed|trans|original
|
*/
'heading' => 'slugged',
/*
|--------------------------------------------------------------------------
| First Row with data or heading of data
|--------------------------------------------------------------------------
|
| If the heading row is not the first row, or the data doesn't start
| on the first row, here you can change the start row.
|
*/
'startRow' => 1,
/*
|--------------------------------------------------------------------------
| Cell name word separator
|--------------------------------------------------------------------------
|
| The default separator which is used for the cell names
| Note: only applies to 'heading' settings 'true' && 'slugged'
|
*/
'separator' => '_',
/*
|--------------------------------------------------------------------------
| Include Charts during import
|--------------------------------------------------------------------------
*/
'includeCharts' => false,
/*
|--------------------------------------------------------------------------
| Sheet heading conversion
|--------------------------------------------------------------------------
|
| Convert headings to ASCII
| Note: only applies to 'heading' settings 'true' && 'slugged'
|
*/
'to_ascii' => true,
/*
|--------------------------------------------------------------------------
| Import encoding
|--------------------------------------------------------------------------
*/
'encoding' => array(
'input' => 'UTF-8',
'output' => 'UTF-8'
),
/*
|--------------------------------------------------------------------------
| Calculate
|--------------------------------------------------------------------------
|
| By default cells with formulas will be calculated.
|
*/
'calculate' => true,
/*
|--------------------------------------------------------------------------
| Ignore empty cells
|--------------------------------------------------------------------------
|
| By default empty cells are not ignored
|
*/
'ignoreEmpty' => false,
/*
|--------------------------------------------------------------------------
| Force sheet collection
|--------------------------------------------------------------------------
|
| For a sheet collection even when there is only 1 sheets.
| When set to false and only 1 sheet found, the parsed file will return
| a row collection instead of a sheet collection.
| When set to true, it will return a sheet collection instead.
|
*/
'force_sheets_collection' => false,
/*
|--------------------------------------------------------------------------
| Date format
|--------------------------------------------------------------------------
|
| The format dates will be parsed to
|
*/
'dates' => array(
/*
|--------------------------------------------------------------------------
| Enable/disable date formatting
|--------------------------------------------------------------------------
*/
'enabled' => true,
/*
|--------------------------------------------------------------------------
| Default date format
|--------------------------------------------------------------------------
|
| If set to false, a carbon object will return
|
*/
'format' => false,
/*
|--------------------------------------------------------------------------
| Date columns
|--------------------------------------------------------------------------
*/
'columns' => array()
),
/*
|--------------------------------------------------------------------------
| Import sheets by config
|--------------------------------------------------------------------------
*/
'sheets' => array(
/*
|--------------------------------------------------------------------------
| Example sheet
|--------------------------------------------------------------------------
|
| Example sheet "test" will grab the firstname at cell A2
|
*/
'test' => array(
'firstname' => 'A2'
)
)
),
'views' => array(
/*
|--------------------------------------------------------------------------
| Styles
|--------------------------------------------------------------------------
|
| The default styles which will be used when parsing a view
|
*/
'styles' => array(
/*
|--------------------------------------------------------------------------
| Table headings
|--------------------------------------------------------------------------
*/
'th' => array(
'font' => array(
'bold' => true,
'size' => 12,
)
),
/*
|--------------------------------------------------------------------------
| Strong tags
|--------------------------------------------------------------------------
*/
'strong' => array(
'font' => array(
'bold' => true,
'size' => 12,
)
),
/*
|--------------------------------------------------------------------------
| Bold tags
|--------------------------------------------------------------------------
*/
'b' => array(
'font' => array(
'bold' => true,
'size' => 12,
)
),
/*
|--------------------------------------------------------------------------
| Italic tags
|--------------------------------------------------------------------------
*/
'i' => array(
'font' => array(
'italic' => true,
'size' => 12,
)
),
/*
|--------------------------------------------------------------------------
| Heading 1
|--------------------------------------------------------------------------
*/
'h1' => array(
'font' => array(
'bold' => true,
'size' => 24,
)
),
/*
|--------------------------------------------------------------------------
| Heading 2
|--------------------------------------------------------------------------
*/
'h2' => array(
'font' => array(
'bold' => true,
'size' => 18,
)
),
/*
|--------------------------------------------------------------------------
| Heading 2
|--------------------------------------------------------------------------
*/
'h3' => array(
'font' => array(
'bold' => true,
'size' => 13.5,
)
),
/*
|--------------------------------------------------------------------------
| Heading 4
|--------------------------------------------------------------------------
*/
'h4' => array(
'font' => array(
'bold' => true,
'size' => 12,
)
),
/*
|--------------------------------------------------------------------------
| Heading 5
|--------------------------------------------------------------------------
*/
'h5' => array(
'font' => array(
'bold' => true,
'size' => 10,
)
),
/*
|--------------------------------------------------------------------------
| Heading 6
|--------------------------------------------------------------------------
*/
'h6' => array(
'font' => array(
'bold' => true,
'size' => 7.5,
)
),
/*
|--------------------------------------------------------------------------
| Hyperlinks
|--------------------------------------------------------------------------
*/
'a' => array(
'font' => array(
'underline' => true,
'color' => array('argb' => 'FF0000FF'),
)
),
/*
|--------------------------------------------------------------------------
| Horizontal rules
|--------------------------------------------------------------------------
*/
'hr' => array(
'borders' => array(
'bottom' => array(
'style' => 'thin',
'color' => array('FF000000')
),
)
)
)
)
);

20
config/image.php Normal file
View file

@ -0,0 +1,20 @@
<?php
return array(
/*
|--------------------------------------------------------------------------
| Image Driver
|--------------------------------------------------------------------------
|
| Intervention Image supports "GD Library" and "Imagick" to process images
| internally. You may choose one of them according to your PHP
| configuration. By default PHP's "GD Library" implementation is used.
|
| Supported: "gd", "imagick"
|
*/
'driver' => 'gd'
);

View file

@ -102,7 +102,7 @@ return array(
|
*/
'responsive' => true,
'responsive' => false,
/*
|--------------------------------------------------------------------------

View file

@ -0,0 +1,64 @@
<?php
return [
'mailChimp' => [
/*
* The api key of a MailChimp account. You can find yours here:
* https://us10.admin.mailchimp.com/account/api-key-popup/
*/
'apiKey' => env('MAILCHIMP_APIKEY'),
/*
* Here you can define properties of the lists you want to
* send campaigns.
*/
'lists' => [
/*
* This key is used to identify this list. It can be used
* in the various methods provided by this package.
*
* You can set it to any string you want and you can add
* as many lists as you want.
*/
'subscribers' => [
/*
* A mail chimp list id. Check the mailchimp docs if you don't know
* how to get this value:
* http://kb.mailchimp.com/lists/managing-subscribers/find-your-list-id
*/
'id' => env('MAILCHIMP_LISTID'),
/*
* These values will be used when creating a new campaign.
*/
'createCampaign' => [
'fromEmail' => '',
'fromName' => '',
'toName' => '',
],
/*
* These values will be used when subscribing to a list.
*/
'subscribe' => [
'emailType' => 'html',
'requireDoubleOptin' => false,
'updateExistingUser' => false,
],
/*
* These values will be used when unsubscribing from a list.
*/
'unsubscribe' => [
'deletePermanently' => false,
'sendGoodbyeEmail' => false,
'sendUnsubscribeEmail' => false,
],
],
],
],
];

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddSubscriptionTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('subscriptions', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email');
$table->string('location');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('subscriptions');
}
}

View file

@ -11,6 +11,6 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// $this->call(UserTableSeeder::class);
}
}

View file

@ -1,6 +1,6 @@
process.env.DISABLE_NOTIFIER = true;
var gulp = require("gulp"),
var gulp = require('gulp'),
elixir = require('laravel-elixir'),
lessglob = require('less-plugin-glob');
@ -12,37 +12,55 @@ if (!elixir.config.production)
elixir.config.autoprefix = {
remove: false,
cascade: false,
browsers: ['last 2 versions']
browsers: [ 'last 2 versions' ]
};
// javascript files in resources/assets/js/
// javascript files for the dashboard in resources/assets/js/
var jsDashboard = [
'dashboard.js'
];
// javascript files for the public site in resources/assets/js/
var jsLocal = [
'site-vars.js',
'contact.js',
'subscription.js',
'app.js'
];
// javascript files in bower_components/
// javascript files in bower_components/ for libraries
var jsBower = [
'jquery/dist/jquery.min.js',
'bootstrap/dist/js/bootstrap.min.js'
'bootstrap/dist/js/bootstrap.min.js',
];
var jsDashboardBower = [
'Sortable/Sortable.js',
'datetimepicker/jquery.datetimepicker.js',
'simplemde/dist/simplemde.min.js'
];
// less import path locations other than resources/assets/less/
var lessPaths = [
'bower_components/bootstrap/less'
];
var lessPaths = [ 'bower_components' ];
elixir(function(mix) {
// elixir mix functions
mix.copy('bower_components/bootstrap/dist/fonts/bootstrap/**', 'public/fonts')
.less('app.less', 'public/css/app.css', {
paths: lessPaths,
plugins: [lessglob]
})
.scripts(jsLocal, 'public/js/app.js', 'resources/assets/js/')
.scripts(jsBower, 'public/js/lib.js', 'bower_components/')
.version(['css/app.css', 'js/app.js', 'js/lib.js']);
// compile the project
mix
.copy('bower_components/bootstrap/dist/fonts/**', 'public/fonts')
.copy('bower_components/font-awesome/fonts/**', 'public/fonts')
.less('dashboard.less', 'public/css/dashboard.css', {
paths: lessPaths,
plugins: [ lessglob ]
})
.less('app.less', 'public/css/app.css', {
paths: lessPaths,
plugins: [ lessglob ]
})
.scripts(jsLocal, 'public/js/app.js', 'resources/assets/js/')
.scripts(jsDashboard, 'public/js/dashboard.js', 'resources/assets/js/')
.scripts(jsBower, 'public/js/lib.js', 'bower_components/')
.scripts(jsDashboardBower, 'public/js/lib-dashboard.js', 'bower_components/')
.version([ 'css/dashboard.css', 'css/app.css', 'js/dashboard.js', 'js/app.js', 'js/lib.js', 'js/lib-dashboard.js' ]);
// start livereload when not production
if (!elixir.config.production)

237
readme.md
View file

@ -2,3 +2,240 @@
A hypothetical website template
## Dashboard
Unless otherwise stated all examples in this section are to be added to `app/Http/Controllers/DashboardController.php`.
### Adding a Viewable Model to the Dashboard
#### Viewable List of Rows
First add a function to generate the page:
```php
public function getContact()
{
return view('dashboard.view', [
'heading' => 'Contact Form Submissions',
'model' => 'contact',
'rows' => Contact::getContactSubmissions(),
'cols' => [
[ 'Date', 'created_at' ],
[ 'Name', 'name' ],
[ 'Email', 'email' ],
[ 'Message', 'message' ]
]
]);
}
```
* `heading`: The title that will appear for this page
* `model`: The model that will be accessed on this page
* `rows`: A function returning an array containing the data to be shown on this page
* `cols`: An array containing a set of arrays where the first element of each is the visible column name and the second is the column name in the array
#### Export Functionality
Viewable models must have an entry in the switch statement of the `getExport` function to make the export button work:
```php
switch ($model) {
case 'contact':
$headings = [ 'Date', 'Name', 'Email', 'Message' ];
$items = Contact::select('created_at', 'name', 'email', 'message')->get();
break;
default:
abort(404);
}
```
* `$headings`: The visible column names in the same order as the array containing the items to be exported
* `$items`: A function returning an array containing the data to be exported
### Adding an Editable Model to the Dashboard
#### Editable List of Rows
##### Editable List for Unsortable Model
```php
public function getShows()
{
return view('dashboard.edit-list', [
'heading' => 'Shows',
'model' => 'shows',
'rows' => Shows::getShowsList(),
'column' => 'title',
'sortcol' => 'false'
]);
}
```
##### Editable List for Sortable Model
**NOTE**: Sortable models must have an entry configured in the `postReorder` function (details below)
```php
public function getNews()
{
return view('dashboard.edit-list', [
'heading' => 'News',
'model' => 'news',
'rows' => News::getNewsList(),
'column' => 'title',
'sortcol' => 'order'
]);
}
```
* `heading`: The title that will appear for this page
* `model`: The model that will be accessed on this page
* `rows`: A function returning an array containing the data to be shown on this page
* `column`: The column name in the array that contains the data to display in each row
* `sortcol`: The name of the column containing the sort order or `'false'` to disable
#### Delete Functionality
Editable models must have an entry in the switch statement of the `deleteDelete` function to make deletion functionality work:
```php
switch ($request['model']) {
case 'shows':
$items = new Shows();
break;
case 'news':
$items = new News();
break;
default:
return 'model-access-fail';
}
```
#### Editable Item
This function should be named the same as the one above except with `Edit` at the end
##### Editable Item for Unsortable Model
```php
public function getShowsEdit($id = 'new')
{
if ($id != 'new') {
if (Shows::where('id', $id)->exists()) {
$item = Shows::where('id', $id)->first();
} else {
return view('errors.no-such-record');
}
} else {
$item = null;
}
return view('dashboard.edit-item', [
'heading' => 'Shows',
'model' => 'shows',
'id' => $id,
'item' => $item,
'help_text' => '<strong>NOTE:</strong> This is some help text for the current page',
'columns' => [
[ 'name' => 'venue', 'type' => 'text' ],
[ 'name' => 'date', 'type' => 'date' ],
[ 'name' => 'address', 'type' => 'text' ],
[ 'name' => 'phone', 'type' => 'text' ],
[ 'name' => 'website', 'type' => 'text' ],
[ 'name' => 'cover', 'type' => 'text' ],
[ 'name' => 'description', 'type' => 'mkd' ]
]
]);
}
```
##### Editable Item for Sortable Model
```php
public function getNewsEdit($id = 'new')
{
if ($id != 'new') {
if (News::where('id', $id)->exists()) {
$item = News::where('id', $id)->first();
} else {
return view('errors.no-such-record');
}
} else {
$item = new News();
$item['order'] = News::count();
}
return view('dashboard.edit-item', [
'heading' => 'News',
'model' => 'news',
'id' => $id,
'item' => $item,
'imgup' => true,
'columns' => [
[ 'name' => 'title', 'type' => 'text', 'label' => 'The Title' ],
[ 'name' => 'iframe', 'type' => 'text' ],
[ 'name' => 'story', 'type' => 'mkd' ],
[ 'name' => 'order', 'type' => 'hidden' ]
]
]);
}
```
* `heading`: The title that will appear for this page
* `model`: The model that will be accessed on this page
* `id`: Always set this to `$id`
* `item`: Always set this to `$item`
* `imgup`: Set this to `true` to enable image upload, otherwise set to `false`
* `help_text`: An optional value that will add a box containing help text above the form if set
* `columns`: An array containing a set of arrays where:
* `name` is the name of the column to be edited
* `type` is the type of column (details below)
* `label` is an optional value that overrides the visible column name
###### Editable Column Types
The following is a list of possible `types` in the `columns` array for Editable Items:
* `text`: Text input field for text data
* `mkd`: Markdown editor for text data containing markdown
* `date`: Date and time selection tool for date/time data
* `hidden`: Fields that will contain values to pass to the update function but won't appear on the page (this must be used for the sort column)
#### Edit Item Functionality
Editable models must have an entry in the switch statement of the `postEdit` function to make create and edit functionality work:
```php
switch ($request['model']) {
case 'shows':
$item = $id == 'new' ? new Shows : Shows::find($id);
break;
case 'news':
$item = $id == 'new' ? new News : News::find($id);
break;
default:
return 'model-access-fail';
}
```
#### Additional Requirement for Sortable Models
Sortable models must have an entry in the switch statement of the `postReorder` function to make sorting functionality work:
```php
switch ($request['model']) {
case 'news':
$items = new News();
break;
default:
return 'model-access-fail';
}
```
#### Additional Requirements for Image Upload
If the value of `imgup` has been set to `true`, ensure `public/uploads/model_name` exists (where `model_name` is the name of the given model) and contains a `.gitkeep` that exists in version control.
By default, uploaded images are saved in JPEG format with the value of the `id` column of the respective row as its name and `.jpg` as its file extension.
When a row is deleted, its respective image will be deleted as well if it exists.

View file

@ -1,6 +1,11 @@
// run once the document is ready
$(document).ready(function() {
// initialize the contact form
if ($('#contact-form').length)
contactFormInit();
switch (SiteVars.page) {
case '':
subscriptionFormInit();
break;
case 'contact':
contactFormInit();
break;
}
});

View file

@ -1,6 +1,7 @@
// contact page functionality
// contact form functionality
function contactFormInit() {
var $form = $('#contact-form'),
$input = $form.find(':input'),
$notify = $('#notification'),
contact = {},
submitting = false;
@ -16,6 +17,7 @@ function contactFormInit() {
$('#submit').on('click', function(e) {
e.preventDefault();
var $submit = $(this);
if (!submitting) {
submitting = true;
@ -30,6 +32,8 @@ function contactFormInit() {
$notify.removeClass('visible');
if (response === 'success') {
$input.attr('disabled', true);
$submit.addClass('disabled');
$notify.text('Thanks for your message!').addClass('success').addClass('visible');
} else {
var responseJSON = response.responseJSON,

View file

@ -0,0 +1,415 @@
// declare a reverse function for jquery
jQuery.fn.reverse = [].reverse;
// show the confirmation modal and run the supplied command if confirm is pressed
function askConfirmation(message, command) {
var $confirmationModal = $('#confirmation-modal'),
$heading = $confirmationModal.find('.panel-heading'),
$cancelButton = $confirmationModal.find('.btn.cancel-button'),
$confirmButton = $confirmationModal.find('.btn.confirm-button'),
fadeTime = 250;
// close the confirmation modal and unbind its events
var closeConfirmationModal = function() {
// unbind events
$(document).off('keyup', escapeModal);
$cancelButton.off('click', closeConfirmationModal);
$confirmButton.off('click', confirmModal);
// clear the heading
$heading.empty();
// hide the modal
$confirmationModal.css({ opacity: 0 });
setTimeout(function() { $confirmationModal.css({ visibility: 'hidden' }); }, fadeTime);
};
// close the modal if the escape button is pressed
var escapeModal = function(e) {
if (e.keyCode == 27)
closeConfirmationModal();
};
// functionality to run when clicking the confirm button
var confirmModal = function() {
command();
closeConfirmationModal();
};
// hide the modal when the cancel button is pressed
$cancelButton.on('click', closeConfirmationModal);
// hide the modal when the escape key is pressed
$(document).on('keyup', escapeModal);
// run the command and hide the modal when the confirm button is pressed
$confirmButton.on('click', confirmModal);
// set the heading with the supplied message
$heading.text(message);
// show the confirmation modal
$confirmationModal.css({
visibility: 'visible',
opacity: 1
});
}
// show the alert modal and display the provided message until accept is pressed
function showAlert(message, command) {
var $alertModal = $('#alert-modal'),
$message = $alertModal.find('.message'),
$acceptButton = $alertModal.find('.btn.accept-button'),
fadeTime = 250;
// close the alert modal and unbind its events
var closeAlertModal = function() {
// unbind events
$(document).off('keyup', escapeModal);
$acceptButton.off('click', closeAlertModal);
// clear the message
$message.empty();
// hide the modal
$alertModal.css({ opacity: 0 });
setTimeout(function() { $alertModal.css({ visibility: 'hidden' }); }, fadeTime);
// if a command was passed run it now
if (command !== undefined)
command();
};
// close the modal if the escape button is pressed
var escapeModal = function(e) {
if (e.keyCode == 27)
closeAlertModal();
};
// hide the modal when the escape key is pressed
$(document).on('keyup', escapeModal);
// hide the modal when the accept button is pressed
$acceptButton.on('click', closeAlertModal);
// set the message with the supplied message
$message.text(message);
// show the alert modal
$alertModal.css({
visibility: 'visible',
opacity: 1
});
}
// initialize edit list functionality
function editListInit() {
var editList = document.getElementById('edit-list'),
$editList = $(editList),
model = $editList.data('model');
// initialize new button functionality
var newButtonInit = function() {
var $newButton = $('.btn.new-button');
$newButton.on('click', function() {
window.location.href = '/dashboard/' + model + '-edit/new';
});
};
// initialize edit button functionality
var editButtonInit = function() {
var $editButtons = $('.btn.edit-button');
$editButtons.on('click', function() {
var $this = $(this),
$listItem = $this.closest('.list-group-item'),
itemId = $listItem.data('id');
// go to the edit page
window.location.href = '/dashboard/' + model + '-edit/' + itemId;
});
};
// initialize delete button functionality
var deleteButtonInit = function() {
var $deleteButtons = $('.btn.delete-button');
$deleteButtons.on('click', function() {
var $this = $(this),
$listItem = $this.closest('.list-group-item'),
itemId = $listItem.data('id');
askConfirmation('Are you sure you want to delete this?', function() {
$.ajax({
type: 'DELETE',
url: '/dashboard/delete',
data: {
model: model,
id: itemId,
_token: $('#token').val()
}
}).always(function(response) {
if (response === 'success') {
$listItem.slideUp(150, function() { $listItem.remove(); });
} else {
showAlert('ERROR: Failed to delete record');
}
});
});
});
};
// initialize sort functionality if data-sort is set
var sortRowInit = function() {
if ($editList.attr('data-sort')) {
var sortCol = $editList.data('sort'),
sortOrder = {};
var sortable = Sortable.create(editList, {
handle: '.sort-icon',
onUpdate: function() {
// update the sortOrder object based on the updated order
$editList.find('.list-group-item').reverse().each(function(index) {
var thisId = $(this).data('id');
sortOrder[thisId] = index;
});
$.ajax({
type: 'POST',
url: '/dashboard/reorder',
data: {
model: model,
order: sortOrder,
column: sortCol,
_token: $('#token').val()
}
}).always(function(response) {
if (response !== 'success') {
showAlert('ERROR: Sorting failed', function() {
document.location.reload(true);
});
}
});
}
});
}
};
newButtonInit();
editButtonInit();
deleteButtonInit();
sortRowInit();
}
function editItemInit() {
var $editItem = $('#edit-item'),
$submit = $('#submit'),
$backButton = $('#back'),
$textInputs = $('.text-input'),
$dateTimePickers = $('.date-time-picker'),
$mkdEditors = $('.mkd-editor'),
$imgUpload = $('#image-upload'),
$token = $('#_token'),
$spinner = $('#loading-modal'),
fadeTime = 250,
model = $editItem.data('model'),
id = $editItem.data('id'),
allowTimes = [],
simplemde = [],
formData = {},
submitting = false;
// show the loading modal
var showLoadingModal = function() {
$spinner.css({
visibility: 'visible',
opacity: 1
});
};
// hide the loading modal
var hideLoadingModal = function() {
$spinner.css({ opacity: 0 });
setTimeout(function() { $spinner.css({ visibility: 'hidden' }); }, fadeTime);
};
// fill the formData object with data from all the form fields
var getFormData = function() {
// function to add a column and value to the formData object
var addFormData = function(column, value) {
// add the value to a key with the column name
formData[column] = value;
// add the column to the array of columns
formData.columns.push(column);
};
// reset the formData object
formData = {};
// add the database model row id and _token
formData.model = model;
formData.id = id;
formData._token = $token.val();
// create an empty array to contain the list of columns
formData.columns = [];
// add values from the contents of text-input class elements
$textInputs.each(function() {
var $this = $(this),
column = $this.attr('id'),
value = $this.val();
addFormData(column, value);
});
// add values from the contents of date-time-picker class elements
$dateTimePickers.each(function() {
var $this = $(this),
column = $this.attr('id'),
value = $this.val() + ':00';
addFormData(column, value);
});
// add values from the contents of the markdown editor for mkd-editor class elements
$mkdEditors.each(function() {
var $this = $(this),
column = $this.attr('id'),
value = simplemde[column].value();
addFormData(column, value);
});
};
var uploadImage = function(row_id) {
// functionality to run on success
var returnSuccess = function() {
hideLoadingModal();
window.location.href = '/dashboard/' + model;
};
// add the image from the image upload box for image-upload class elements
if ($imgUpload.length && $imgUpload.val() !== '') {
var file = new FormData();
// add the file, id and model to the formData variable
file.append('file', $imgUpload[0].files[0]);
file.append('id', row_id);
file.append('model', model);
$.ajax({
type: 'POST',
url: '/dashboard/image-upload',
data: file,
processData: false,
contentType: false,
beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRF-TOKEN', $token.val()); }
}).always(function(response) {
if (response === 'success') {
returnSuccess();
} else {
hideLoadingModal();
showAlert('ERROR: Failed to upload image');
submitting = false;
}
});
} else {
returnSuccess();
}
};
// allow start time selection to start on the hour and every 15 minutes after
for (var hours=0; hours<=23; hours++) {
for (var minutes=0; minutes<=3; minutes++) {
allowTimes.push(hours + ':' + (minutes === 0 ? '00' : minutes * 15));
}
}
// enable the datetimepicker for each element with the date-time-picker class
$dateTimePickers.each(function() {
$(this).datetimepicker({
format: 'Y-m-d H:i',
allowTimes: allowTimes,
step: 15
});
});
// enable the markdown editor for each element with the mkd-editor class
$mkdEditors.each(function() {
var $this = $(this),
column = $this.attr('id');
simplemde[column] = new SimpleMDE({
element: this,
toolbar: [
'bold',
'italic',
'|',
'heading-1',
'heading-2',
'heading-3',
'|',
'quote',
'unordered-list',
'ordered-list',
'link'
],
blockStyles: { italic: '_' },
autoDownloadFontAwesome: false,
tabSize: 4
});
setTimeout(function() {
simplemde[column].value($this.attr('value'));
}, 100);
});
// initialize back button
$backButton.on('click', function() {
if (!submitting) {
askConfirmation('Cancel and return to the ' + model + ' list?', function() {
window.location.href = '/dashboard/' + model;
});
}
});
// initialize submit button
$submit.on('click', function() {
if (!submitting) {
submitting = true;
// show the loading modal
showLoadingModal();
// populate the formData object
getFormData();
// submit the update
$.ajax({
type: 'POST',
url: '/dashboard/edit',
data: formData
}).always(function(response) {
if (/^id:[0-9][0-9]*$/.test(response)) {
uploadImage(response.replace(/^id:/, ''));
} else {
hideLoadingModal();
showAlert('ERROR: Failed to update record');
submitting = false;
}
});
}
});
}
// run once the document is ready
$(document).ready(function() {
if ($('#edit-list').length) {
editListInit();
} else if ($('#edit-item').length) {
editItemInit();
}
});

View file

@ -0,0 +1,63 @@
// subscription form functionality
function subscriptionFormInit() {
var $form = $('#subscription-form'),
$input = $form.find(':input'),
$notify = $('#notification'),
subscribe = {},
submitting = false;
var getSubscribeData = function() {
subscribe = {
name: $('#name').val(),
email: $('#email').val(),
address: $('#address').val(),
_token: $('#token').val()
};
};
$('#submit').on('click', function(e) {
e.preventDefault();
var $submit = $(this);
if (!submitting) {
submitting = true;
getSubscribeData();
$.ajax({
type: 'POST',
url: '/subscription-submit',
data: subscribe
}).always(function(response) {
$form.find('.error').removeClass('error');
$notify.removeClass('visible').removeClass('error');
if (response === 'success') {
$form.addClass('success');
setTimeout(function() {
$notify.text('Thanks for subscribing!').addClass('success').addClass('visible');
$input.fadeOut(150);
}, 1000);
} else {
var responseJSON = response.responseJSON,
errors = 0;
// add the error class to fields that haven't been filled out
for (var prop in responseJSON) {
if (responseJSON.hasOwnProperty(prop)) {
$('#' + prop).addClass('error');
errors++;
}
}
// if there are no errors with form fields then there must have been an API error
if (errors === 0)
$notify.text('An error occurred. Are you already subscribed?').addClass('error').addClass('visible');
// re-enable submitting
submitting = false;
}
});
}
});
}

View file

@ -1,11 +1,8 @@
// Core
@import "bootstrap";
@import "bootstrap/less/bootstrap";
@import "var";
@import "fonts";
// Base
@import "auth";
// Supplementary
@import "elements/**";
@import "pages/**";
@ -21,6 +18,8 @@ html, body {
height: 100%;
}
html.no-scroll { overflow-y: hidden; }
body {
min-width: 300px;
margin: 0;

View file

@ -1,33 +0,0 @@
.auth-container {
form {
margin-top: 50px;
padding: 25px 25px 50px 25px;
border-radius: 5px;
border: 1px solid darken(@c_accent, 5%);
background-color: @c_accent;
.form-field {
label {
float: left;
min-width: 125px;
}
.input {
display: block;
overflow: hidden;
margin-bottom: 10px;
input {
width: 100%;
height: 24px;
}
}
}
button {
position: absolute;
right: 41px;
bottom: 25px;
}
}
}

356
resources/assets/less/dashboard.less vendored Normal file
View file

@ -0,0 +1,356 @@
// Core
@import "bootstrap/less/bootstrap";
@import "font-awesome/less/font-awesome";
@import "awesome-bootstrap-checkbox/awesome-bootstrap-checkbox";
@import (inline) "datetimepicker/jquery.datetimepicker.css";
@import (inline) "simplemde/dist/simplemde.min.css";
@import (inline) "SpinKit/css/spinners/11-folding-cube.css";
@import "var";
@import "fonts";
@import "classes";
@fa-font-path: "../../fonts";
* {
outline: none !important;
.font_sans;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body { min-width: 350px; }
nav.navbar {
background-color: @c_dashboard_dark;
.navbar-brand { font-weight: bold; }
#spark-navbar-collapse {
box-shadow: none;
border: none;
.dropdown {
.dropdown-toggle .caret { margin-left: 4px; }
&.open .dropdown-toggle {
background-color: darken(@c_dashboard_dark, 5%);
color: @c_dashboard_light;
}
.dropdown-menu {
top: calc(100% ~"-" 1px);
right: -1px;
background-color: @c_dashboard_dark;
& > li > a:hover, & > li > a:focus { background-color: transparent; }
& > li > a { &, &:hover, &:focus { color: #fff; } }
}
}
}
.navbar-brand, .navbar-default .navbar-nav > li > a, .navbar-nav > li > a {
&, &:hover, &:focus { color: @c_dashboard_light; }
}
.navbar-toggle {
position: relative;
bottom: 0px;
left: 10px;
border: none;
&, &:hover, &:focus { background-color: transparent; }
.icon-bar {
width: 27px;
height: 4px;
background-color: @c_dashboard_light;
transition: background-color 100ms;
}
&:hover .icon-bar { background-color: darken(@c_dashboard_dark, 15%); }
}
}
.panel-default {
margin-top: 20px;
margin-bottom: 40px;
.panel-body {
padding-bottom: 0px;
background-color: lighten(@c_dashboard_light, 1%);
.help-text {
padding: 5px 10px;
margin-top: 10px;
margin-bottom: 10px;
border-radius: 5px;
border: 1px solid darken(@c_dashboard_dark, 5%);
background-color: @c_dashboard_dark;
color: @c_text_light;
}
}
& > .panel-heading {
border-top: 1px solid darken(@c_dashboard_dark, 5%);
border-right: 1px solid darken(@c_dashboard_dark, 5%);
border-left: 1px solid darken(@c_dashboard_dark, 5%);
background-color: @c_dashboard_dark;
color: @c_dashboard_light;
font-weight: bold;
.btn {
float: right;
position: relative;
bottom: 3px;
min-width: 70px;
height: 26px;
padding-top: 1px;
padding-bottom: 2px;
}
a {
text-decoration: none;
color: @c_text;
}
}
}
.form-control {
&:focus {
border-color: @c_dashboard_dark;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px fade(@c_dashboard_dark, 60%);
}
}
.btn {
border-color: darken(@c_dashboard_dark, 5%);
background-color: @c_dashboard_dark;
transition: background-color 100ms;
&:hover { background-color: darken(@c_dashboard_dark, 5%); }
&:focus { background-color: lighten(@c_dashboard_dark, 5%); }
&.btn-warning {
border-color: darken(@c_dashboard_edit, 10%);
background-color: @c_dashboard_edit;
&:hover { background-color: darken(@c_dashboard_edit, 5%); }
&:focus { background-color: lighten(@c_dashboard_edit, 5%); }
}
&.btn-danger {
border-color: darken(@c_dashboard_delete, 10%);
background-color: @c_dashboard_delete;
&:hover { background-color: darken(@c_dashboard_delete, 5%); }
&:focus { background-color: lighten(@c_dashboard_delete, 5%); }
}
&.btn-default {
border-color: darken(@c_dashboard_light, 10%);
background-color: @c_dashboard_light;
color: @c_text;
&:hover { background-color: darken(@c_dashboard_light, 5%); }
&:focus { background-color: lighten(@c_dashboard_light, 5%); }
}
&.btn-link { color: @c_dashboard_light; }
&, &:hover, &:focus { text-decoration: none; }
}
.table {
& > thead > tr > th { border-bottom: 1px solid #666; }
@media (max-width: (@screen-sm - 1)) {
tr.heading-row { display: none; }
tr:not(:first-child) { border-top: 1px solid #ddd; }
& > tbody > tr > td {
&:first-child { padding-top: 20px; }
&:last-child { padding-bottom: 20px; }
}
& > tbody > tr:first-child > td:first-child { padding-top: 0px; }
& > tbody > tr:last-child > td:last-child { padding-bottom: 0px; }
& > tbody > tr > td {
display: block;
border-top: none;
}
}
@media (min-width: @screen-sm) {
& > tbody > tr > td { padding: 20px 8px; }
& > tbody > tr:last-child > td { padding-bottom: 0px; }
.mobile-heading { display: none; }
}
}
.list-group {
margin-bottom: 0px;
padding-bottom: 15px;
&.linked-list {
margin-bottom: 0px;
.list-group-item {
padding: 0px;
a {
display: block;
padding: 10px 15px;
color: @c_dashboard_dark;
background-color: #fff;
transition: background-color 100ms, color 100ms;
&:focus, &:hover { text-decoration: none; }
&:hover {
color: @c_dashboard_light;
background-color: @c_dashboard_dark;
}
}
&:first-child a {
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
&:last-child a {
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}
}
}
&.edit-list {
.list-group-item {
position: relative;
padding-top: 4px;
padding-bottom: 4px;
.sort-icon {
display: inline-block;
margin-right: 10px;
cursor: grab;
cursor: -webkit-grabbing;
}
.title-column {
padding-top: 6px;
padding-bottom: 6px;
padding-left: 0px;
}
.button-column {
text-align: right;
padding-right: 0px;
padding-left: 0px;
.btn {
min-width: 70px;
height: 26px;
margin: 3px;
padding-top: 1px;
padding-bottom: 2px;
}
}
}
}
}
.edit-item {
margin-top: 10px;
.CodeMirror, .CodeMirror-scroll { min-height: 100px; }
.date-time-picker { cursor: pointer; }
label {
height: 32px;
line-height: 32px;
}
input {
display: block;
width: 100%;
margin-bottom: 15px;
}
.current-image {
width: 125px;
height: 125px;
background-size: contain;
background-position: left center;
background-repeat: no-repeat;
}
.back-button { float: left; }
.submit-button { float: right; }
.back-button, .submit-button {
margin: 25px 15px 15px 15px;
@media (max-width: (@screen-sm - 1)) {
float: none;
width: calc(100% ~"-" 30px);
&:first-child { margin: 25px 15px 5px 15px; }
&:last-child { margin: 5px 15px 25px 15px; }
}
}
}
#loading-modal {
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: fade(lighten(@c_dashboard_light, 1%), 40%);
transition: opacity 250ms;
opacity: 0;
visibility: hidden;
z-index: 1000;
.spinner-container {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
}
.modal {
display: table;
position: fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: fade(lighten(@c_dashboard_light, 1%), 40%);
transition: opacity 250ms;
opacity: 0;
visibility: hidden;
z-index: 1000;
.modal-container {
display: table-cell;
vertical-align: middle;
text-align: center;
.panel { margin: 0px; }
.btn {
display: inline-block;
margin: 20px 15px;
}
}
&#alert-modal {
.modal-container {
.panel { position: relative; }
.message {
min-height: 50px;
padding: 15px 106px 10px 20px;
text-align: left;
}
.btn {
position: absolute;
right: 0px;
top: 50%;
transform: translateY(-50%);
}
}
}
}

View file

@ -1,3 +1,9 @@
footer {
width: 100%;
height: 32px;
line-height: 32px;
padding-left: 10px;
background-color: #000;
color: @c_text_light;
font-size: 11px;
}

View file

@ -1,18 +1,55 @@
nav.navbar {
background-color: @c_base;
height: @nav_height;
margin-bottom: 0px;
padding-right: 10px;
background: linear-gradient(to right, transparent 0%, fade(@c_base, 40%) 25%, fade(@c_base, 50%) 50%, @c_base 100%);
border: none;
border-radius: 0px;
z-index: 1;
@media (max-width: @grid-float-breakpoint-max) {
height: @mobile_nav_height;
background: fade(@c_base, 55%);
}
.navbar-logo {
top: 25px;
left: 15px;
width: 150px;
height: 150px;
position: fixed;
top: 45px;
left: 63px;
width: 145px;
height: 145px;
background-image: url('/img/logo.png');
background-size: contain;
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
transition: transform 200ms;
&:hover { transform: scale(1.03); }
&:active {
transition: transform 100ms;
transform: scale(1.01);
}
@media (max-width: @grid-float-breakpoint-max) {
top: 5px;
left: 10px;
width: 40px;
height: 40px;
}
@media (min-width: @grid-float-breakpoint) {
@logoAnimSpeed: 400ms;
transition: transform 200ms, top @logoAnimSpeed, left @logoAnimSpeed, width @logoAnimSpeed, height @logoAnimSpeed;
&:active { transition: transform 100ms, top @logoAnimSpeed, left @logoAnimSpeed, width @logoAnimSpeed, height @logoAnimSpeed; }
&.scaled {
top: 10px;
left: 15px;
width: @nav_height - 20;
height: @nav_height - 20;
}
}
}
#navbar {
@ -22,20 +59,69 @@ nav.navbar {
ul.nav {
li {
a {
padding: 0px 15px;
user-select: none;
color: @c_text_light;
font-weight: bold;
text-decoration: none;
text-transform: uppercase;
font-size: 36px;
font-weight: 500;
font-stretch: ultra-condensed;
line-height: @nav_height;
transition: color 100ms;
@media (max-width: 1250px) { font-size: 28px; }
@media (max-width: 1050px) { font-size: 24px; }
&:after {
content: "";
display: block;
position: absolute;
bottom: calc(50% ~"-" 0.8em);
left: 50%;
transform: translateX(-50%);
width: calc(100% ~"-" 20px);
height: 3px;
background-color: @c_text_light;
opacity: 0;
transition: opacity 200ms;
@media (max-width: @grid-float-breakpoint-max) { bottom: 0px; }
}
}
&:hover a { color: @c_text; }
&.active a { color: @c_text; }
&.active a, &.active:hover a, &:hover a, &:focus a { background-color: transparent; }
&:hover a { color: darken(@c_text_light, 25%); }
&.active a:after { opacity: 1; }
}
}
@media (max-width: @grid-float-breakpoint-max) {
position: relative;
bottom: 4px;
width: 100vw;
padding-bottom: 5px;
background-color: fade(@c_base, 55%);
ul.nav li a {
line-height: 40px;
font-size: 18px;
}
}
}
.navbar-toggle {
position: relative;
bottom: 2px;
left: 20px;
border: none;
&, &:hover, &:focus { background-color: transparent; }
.icon-bar {
width: 27px;
height: 4px;
background-color: @c_text_light;
transition: background-color 150ms;
}
&:hover .icon-bar { background-color: darken(@c_text_light, 25%); }
}
}

View file

@ -0,0 +1,53 @@
body.contact {
#contact-form {
@trans-speed: 100ms;
margin-top: 35px;
margin-bottom: 20px;
input, textarea {
width: 100%;
margin-bottom: 20px;
padding: 5px 10px;
font-size: 14px;
background-color: rgba(255, 255, 255, 0.8);
border: 2px solid fade(@c_text, 25%);
transition: border @trans-speed;
&:focus { border: 2px solid fade(@c_base, 60%); }
&.error { border: 2px solid @c_error; }
}
textarea {
resize: none;
height: 150px;
}
#submit {
text-align: center;
font-weight: bold;
color: @c_text_light;
background-color: lighten(@c_base, 5%);
transition: background-color @trans-speed;
&:hover { background-color: @c_base; }
&.disabled { background-color: @c_base; }
}
#notification {
margin: 0px auto 15px auto;
padding: 5px 10px;
color: @c_text_light;
text-align: center;
font-size: 14px;
opacity: 0;
background-color: lighten(@c_error, 15%);
transition: opacity @trans-speed;
span { font-weight: bold; }
&.visible { opacity: 1; }
&.success {
background-color: transparent;
color: @c_text;
font-weight: bold;
}
}
}
}

View file

@ -1,3 +1,12 @@
/*
|
| Overrides
|
*/
@grid-float-breakpoint: @screen-sm;
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
/*
|
| Custom Variables
@ -8,5 +17,14 @@
@c_text: #111; // text
@c_text_light: #fff; // light text
@c_base: #0088cc; // base
@c_accent: #f5f5f5; // accent
@c_accent: #000; // accent
@c_error: #ff0000; // error
@c_dashboard_dark: #3e6087;
@c_dashboard_light: #f1f1f1;
@c_dashboard_edit: #87823e;
@c_dashboard_delete: #87483e;
// Sizes
@nav_height: 96px;
@mobile_nav_height: 50px;

View file

@ -1,49 +0,0 @@
#contact-form {
margin-top: 35px;
margin-bottom: 20px;
input, textarea {
width: 100%;
margin-bottom: 20px;
padding: 5px 10px;
font-size: 14px;
background-color: rgba(255, 255, 255, 0.8);
border: 2px solid fade(@c_text, 25%);
transition: border 100ms;
&:focus { border: 2px solid fade(@c_base, 60%); }
&.error { border: 2px solid @c_error; }
}
textarea {
resize: none;
height: 150px;
}
#submit {
text-align: center;
font-weight: bold;
color: @c_text_light;
background-color: lighten(@c_base, 5%);
transition: background-color 100ms;
&:hover { background-color: @c_base; }
}
#notification {
margin: 0px auto 15px auto;
padding: 5px 10px;
color: @c_text_light;
text-align: center;
font-size: 14px;
opacity: 0;
background-color: lighten(@c_error, 15%);
transition: opacity 100ms;
&.visible { opacity: 1; }
span { font-weight: bold; }
&.success {
background-color: transparent;
color: @c_text;
font-weight: bold;
}
}
}

View file

@ -1,11 +0,0 @@
@extends('base')
@section('page-content')
<div class="container auth-container">
<div class="row">
<div class="col-xs-12 col-md-6 col-md-offset-3">
@yield('auth-form')
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1 @@
Click here to reset your password: {{ url('password/reset', $token).'?email='.urlencode($user->getEmailForPasswordReset()) }}

View file

@ -1,24 +1,65 @@
@extends('auth')
@extends('layouts.dashboard')
@section('auth-form')
<form method="POST" action="/auth/login">
{!! csrf_field() !!}
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Login</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/login') }}">
{!! csrf_field() !!}
<div class="form-field">
<label for="email">Email</label>
<div class="input"><input type="email" name="email" value="{{ old('email') }}" /></div>
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" value="{{ old('email') }}" />
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" />
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<div class="checkbox">
<input type="checkbox" class="styled" id="remember" name="remember" />
<label>Remember Me</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Login
</button>
<a class="btn btn-link" href="{{ url('/password/reset') }}">Forgot Your Password?</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="form-field">
<label for="password">Password</label>
<div class="input"><input type="password" name="password" id="password" /></div>
</div>
<div class="form-field">
<label for="remember">Remember Me</label>
<input type="checkbox" name="remember" />
</div>
<button type="submit">Login</button>
</form>
</div>
@endsection

View file

@ -0,0 +1,46 @@
@extends('layouts.dashboard')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Reset Password</div>
<div class="panel-body">
@if (session('status'))
<div class="alert alert-success">
{{ session('status') }}
</div>
@endif
<form class="form-horizontal" role="form" method="POST" action="{{ url('/password/email') }}">
{!! csrf_field() !!}
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" value="{{ old('email') }}" />
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Send Password Reset Link
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,70 @@
@extends('layouts.dashboard')
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Reset Password</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/password/reset') }}">
{!! csrf_field() !!}
<input type="hidden" name="token" value="{{ $token }}" />
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" value="{{ $email or old('email') }}" />
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" />
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password_confirmation" />
@if ($errors->has('password_confirmation'))
<span class="help-block">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Reset Password
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -1,29 +1,82 @@
@extends('auth')
@extends('layouts.dashboard')
@section('auth-form')
<form method="POST" action="/auth/register">
{!! csrf_field() !!}
@section('content')
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Register</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" action="{{ url('/register') }}">
{!! csrf_field() !!}
<div class="form-field">
<label for="name">Name</label>
<div class="input"><input type="text" name="name" value="{{ old('name') }}" /></div>
<div class="form-group{{ $errors->has('name') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Name</label>
<div class="col-md-6">
<input type="text" class="form-control" name="name" value="{{ old('name') }}" />
@if ($errors->has('name'))
<span class="help-block">
<strong>{{ $errors->first('name') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('email') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">E-Mail Address</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" value="{{ old('email') }}" />
@if ($errors->has('email'))
<span class="help-block">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" />
@if ($errors->has('password'))
<span class="help-block">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group{{ $errors->has('password_confirmation') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Confirm Password</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password_confirmation" />
@if ($errors->has('password_confirmation'))
<span class="help-block">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button type="submit" class="btn btn-primary">
Register
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="form-field">
<label for="email">Email</label>
<div class="input"><input type="email" name="email" value="{{ old('email') }}" /></div>
</div>
<div class="form-field">
<label for="password">Password</label>
<div class="input"><input type="password" name="password" id="password" /></div>
</div>
<div class="form-field">
<label for="password_confirmation">Confirm</label>
<div class="input"><input type="password" name="password_confirmation" id="password_confirmation" /></div>
</div>
<button type="submit">Register</button>
</form>
</div>
@endsection

View file

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<p>Name: {{ $contact['name'] }}</p>
<p>Email: {{ $contact['email'] }}</p>
<p>{{ $contact['message'] }}</p>
</body>
</html>

View file

@ -0,0 +1,52 @@
@extends('layouts.dashboard')
@section('content')
<div class="container spark-screen">
<div class="row">
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
{{ $heading }}
@yield('dashboard-heading')
</div>
<div class="panel-body">
@yield('dashboard-body')
</div>
</div>
</div>
</div>
</div>
<div id="confirmation-modal" class="modal">
<div class="modal-container">
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading"></div>
<button type="button" class="cancel-button btn btn-primary">Cancel</button>
<button type="button" class="confirm-button btn btn-danger">Confirm</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="alert-modal" class="modal">
<div class="modal-container">
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
<div class="panel panel-default">
<div class="panel-heading">ALERT</div>
<div class="message"></div>
<button type="button" class="accept-button btn btn-primary">OK</button>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,78 @@
@extends('dashboard.core')
@section('dashboard-body')
@if(!empty($help_text))
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<div class="help-text">
{!! $help_text !!}
</div>
</div>
</div>
</div>
@endif
<form id="edit-item" class="edit-item" data-id="{{ $id }}" data-model="{{ $model }}">
<input type="hidden" name="_token" id="_token" value="{{ csrf_token() }}" />
<div class="container-fluid">
<div class="row">
@foreach($columns as $column)
@set('value', empty($item->$column['name']) ? '' : $item->$column['name'])
@if($column['type'] == 'hidden')
<input class="text-input" type="hidden" name="{{ $column['name'] }}" id="{{ $column['name'] }}" value="{{ $value }}" />
@else
<div class="col-xs-12 col-md-2">
<label for="{{ $column['name'] }}">{{ empty($column['label']) ? ucfirst($column['name']) : $column['label'] }}:</label>
</div>
<div class="col-xs-12 col-md-10">
@if($column['type'] == 'text')
<input class="text-input" type="text" name="{{ $column['name'] }}" id="{{ $column['name'] }}" value="{{ $value }}" />
@elseif($column['type'] == 'date')
<input class="date-time-picker" type="text" name="{{ $column['name'] }}" id="{{ $column['name'] }}" value="{{ preg_replace('/:[0-9][0-9]$/', '', $value) }}" />
@elseif($column['type'] == 'mkd')
<textarea class="mkd-editor" name="{{ $column['name'] }}" id="{{ $column['name'] }}" value="{{ $value }}"></textarea>
@endif
</div>
@endif
@endforeach
@if(!empty($imgup) && $imgup)
<div class="col-xs-12 col-md-2">
<label for="{{ $column['name'] }}">Picture:</label>
</div>
<div class="col-xs-12 col-md-10">
<input class="image-upload" type="file" name="image-upload" id="image-upload" />
@set('current_image', "/uploads/$model/$id.jpg")
@if(file_exists(base_path() . '/public' . $current_image))
<div class="current-image" style="background-image: url({{ $current_image }});" />
@else
<div>(No Image Set)</div>
@endif
</div>
@endif
</div>
<div class="row">
<button id="back" type="button" class="back-button btn btn-default">Back</button>
<button id="submit" type="button" class="submit-button btn btn-primary">{{ $id == 'new' ? 'Create' : 'Update' }} {{ $heading }} Item</button>
</div>
</div>
</form>
<div id="loading-modal">
<div class="spinner-container">
<div class="sk-folding-cube">
<div class="sk-cube1 sk-cube"></div>
<div class="sk-cube2 sk-cube"></div>
<div class="sk-cube4 sk-cube"></div>
<div class="sk-cube3 sk-cube"></div>
</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,32 @@
@extends('dashboard.core')
@section('dashboard-heading')
<button type="button" class="new-button btn btn-default">New</button>
@endsection
@section('dashboard-body')
@set('sort_data', $sortcol != 'false' ? "data-sort=$sortcol" : '')
@set('sort_icon', $sortcol != 'false' ? '<i class="fa fa-bars sort-icon" title="Click and drag to reorder"></i>' : '')
<ul id="edit-list" class="list-group edit-list" data-model="{{ $model }}" {{ $sort_data }}>
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}" />
@foreach($rows as $row)
<li class="list-group-item" data-id="{{ $row['id'] }}">
<div class="container-fluid">
<div class="row">
<div class="col-xs-9 title-column">
{!! $sort_icon !!}
{{ $row[$column] }}
</div>
<div class="col-xs-3 button-column">
<button type="button" class="edit-button btn btn-warning">Edit</button>
<button type="button" class="delete-button btn btn-danger">Delete</button>
</div>
</div>
</div>
</li>
@endforeach
</ul>
@endsection

View file

@ -0,0 +1,11 @@
@set('menu', [
[ 'Contact', 'contact' ],
[ 'Subscriptions', 'subscriptions' ],
[ 'Shows', 'shows' ],
[ 'News', 'news' ],
[ 'Videos', 'videos' ]
])
@foreach($menu as $menu_item)
<li class="{{ $menu_class }}"><a href="{{ url('/dashboard/' . $menu_item[1]) }}">{{ $menu_item[0] }}</a></li>
@endforeach

View file

@ -0,0 +1,39 @@
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#spark-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>
<a class="navbar-brand" href="{{ url('/dashboard') }}">
{{ env('SITE_NAME') }} Dashboard
</a>
</div>
<div id="spark-navbar-collapse" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
@if (Auth::guest())
<li><a href="{{ url('/login') }}">Login</a></li>
@if(env('REGISTRATION', false))
<li><a href="{{ url('/register') }}">Register</a></li>
@endif
@else
@set('menu_class', 'nav-item')
@include('dashboard.elements.menu')
<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="{{ url('/logout') }}">Logout</a></li>
</ul>
</li>
@endif
</ul>
</div>
</div>
</nav>

View file

@ -0,0 +1,6 @@
@extends('dashboard.core')
@section('dashboard-body')
@set('menu_class', 'list-group-item')
<ul class="list-group linked-list">@include('dashboard.elements.menu')</ul>
@endsection

View file

@ -0,0 +1,27 @@
@extends('dashboard.core')
@section('dashboard-heading')
<a href="/dashboard/export/{{ $model }}"><button type="button" class="btn btn-default">Export</button></a>
@endsection
@section('dashboard-body')
<table class="table">
<thead>
<tr class="heading-row">
@foreach($cols as $column)
<th>{{ $column[0] }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($rows as $row)
<tr>
@foreach($cols as $column)
<td><strong class="mobile-heading">{{ $column[0] }}: </strong>{{ $row[$column[1]] }}</td>
@endforeach
</tr>
@endforeach
</tbody>
</table>
@endsection

View file

@ -1,3 +1 @@
<footer>
</footer>
<footer>Copyright &copy; {{ date('Y') }} {{ env('SITE_NAME') }}</footer>

View file

@ -1,19 +1,17 @@
<nav class="navbar navbar-default">
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<button type="button" id="navbar-toggle" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle Navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-logo"></div>
<a class="navbar-logo" href="/"></a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
<li class="navlink navlink-section-contact"><a href="/contact" title="Contact">Contact</a></li>
</ul>
</div>
</div>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta charset="UTF-8">
</head>
<body>
<p><strong>Name:</strong> {{ $contact['name'] }}</p>

View file

@ -1,45 +1,5 @@
<!DOCTYPE html>
<html>
<head>
<title>Page Not Found - {{ env('SITE_NAME') }}</title>
@extends('layouts.error')
<style>
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
color: #B0BEC5;
display: table;
font-weight: 100;
font-family: Impact, Charcoal, sans-serif;
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 72px;
margin-bottom: 40px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<div class="title">Page Not Found</div>
</div>
</div>
</body>
</html>
@section('error-title')
Page Not Found
@endsection

View file

@ -1,45 +1,5 @@
<!DOCTYPE html>
<html>
<head>
<title>Be Right Back - {{ env('SITE_NAME') }}</title>
@extends('layouts.error')
<style>
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
color: #B0BEC5;
display: table;
font-weight: 100;
font-family: Impact, Charcoal, sans-serif;
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 72px;
margin-bottom: 40px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<div class="title">Be Right Back</div>
</div>
</div>
</body>
</html>
@section('error-title')
Be Right Back
@endsection

View file

@ -0,0 +1,5 @@
@extends('layouts.error')
@section('error-title')
No Such Record
@endsection

View file

@ -2,9 +2,9 @@
<html lang="en">
<head>
{!! Head::render() !!}
<link rel="stylesheet" href="{{ elixir('css/app.css') }}">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<script src="{{ elixir('js/lib.js') }}"></script>
<script src="{{ elixir('js/app.js') }}"></script>
@yield('page-includes')
@if (Config::get('app.debug'))
<script type="text/javascript">
@ -12,9 +12,13 @@
</script>
@endif
</head>
<body class="{{ Request::path() == "/" ? "index" : preg_replace('/\//', '-', Request::path()) }}">
<body class="{{ Request::path() == '/' ? 'index' : preg_replace('/\/.*/', '', Request::path()) }}">
@yield('page-top')
@yield('page-content')
<div id="page-content">
@yield('content')
</div>
@yield('page-bottom')
</body>
</html>

View file

@ -0,0 +1,11 @@
@extends('layouts.base')
@section('page-includes')
<script src="{{ elixir('js/lib-dashboard.js') }}"></script>
<script src="{{ elixir('js/dashboard.js') }}"></script>
<link rel="stylesheet" href="{{ elixir('css/dashboard.css') }}" />
@endsection
@section('page-top')
@include('dashboard.elements.nav')
@endsection

View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>@yield('error-title') - {{ env('SITE_NAME') }}</title>
<style>
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
color: #B0BEC5;
display: table;
font-weight: 100;
font-family: Impact, Charcoal, sans-serif;
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 72px;
margin-bottom: 40px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<div class="title">@yield('error-title')</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,14 @@
@extends('layouts.base')
@section('page-includes')
<script src="{{ elixir('js/app.js') }}"></script>
<link rel="stylesheet" href="{{ elixir('css/app.css') }}" />
@endsection
@section('page-top')
@include('elements.nav')
@endsection
@section('page-bottom')
@include('elements.footer')
@endsection

View file

@ -1,9 +0,0 @@
@extends('base')
@section('page-top')
@include('elements.nav')
@endsection
@section('page-bottom')
@include('elements.footer')
@endsection

View file

@ -1,7 +1,6 @@
@extends('public')
@section('page-content')
@extends('layouts.public')
@section('content')
<div class="container">
<div class="row">
<div class="col-xs-12">
@ -10,12 +9,12 @@
</div>
<div class="row">
<div class="col-xs-12 col-md-8 col-md-push-2">
<div class="col-xs-12 col-md-8 col-md-offset-2">
<div id="contact-form">
<form action="#" method="POST" accept-charset="UTF-8">
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}" />
<input type="text" name="name" id="name" placeholder="Name" /><br />
<input type="text" name="email" id="email" placeholder="Email" /><br />
<input type="text" name="name" id="name" placeholder="Name" />
<input type="text" name="email" id="email" placeholder="Email" />
<textarea name="message" id="message" placeholder="Message"></textarea>
<input id="submit" type="submit" value="Submit" />
@ -26,5 +25,4 @@
</div>
</div>
</div>
@endsection

View file

@ -1,11 +0,0 @@
@extends('public')
@section('page-content')
<div class="container">
<div class="content">
<div class="title">Hypothetical Template</div>
</div>
</div>
@endsection

View file

@ -0,0 +1,14 @@
@extends('layouts.public')
@section('content')
<div id="subscription-form">
<form action="#" method="POST" accept-charset="UTF-8">
<input type="hidden" name="_token" id="token" value="{{ csrf_token() }}" />
<input type="text" name="email" id="email" placeholder="Email" />
<input type="text" name="name" id="name" placeholder="Name" />
<input type="text" name="address" id="address" placeholder="Postal / ZIP" />
<div id="notification"></div>
<input id="submit" type="submit" value="Subscribe" />
</form>
</div>
@endsection