Browse Source

Added backup feature and restructure user action button to partial

pull/3/head
Nafies Luthfi 8 years ago
parent
commit
32409ce1d0
  1. 31
      app/Helpers/functions.php
  2. 85
      app/Http/Controllers/BackupsController.php
  3. 2
      app/Providers/AppServiceProvider.php
  4. 1
      composer.json
  5. 124
      composer.lock
  6. 1
      config/app.php
  7. 65
      resources/views/backups/forms.blade.php
  8. 54
      resources/views/backups/index.blade.php
  9. 4
      resources/views/users/chart.blade.php
  10. 6
      resources/views/users/edit.blade.php
  11. 8
      resources/views/users/partials/action-buttons.blade.php
  12. 2
      resources/views/users/partials/childs.blade.php
  13. 8
      resources/views/users/show.blade.php
  14. 4
      resources/views/users/tree.blade.php
  15. 8
      routes/web.php

31
app/Helpers/functions.php

@ -0,0 +1,31 @@
<?php
function formatSizeUnits($bytes)
{
if ($bytes >= 1073741824)
{
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
}
elseif ($bytes >= 1048576)
{
$bytes = number_format($bytes / 1048576, 2) . ' MB';
}
elseif ($bytes >= 1024)
{
$bytes = number_format($bytes / 1024, 2) . ' KB';
}
elseif ($bytes > 1)
{
$bytes = $bytes . ' bytes';
}
elseif ($bytes == 1)
{
$bytes = $bytes . ' byte';
}
else
{
$bytes = '0 bytes';
}
return $bytes;
}

85
app/Http/Controllers/BackupsController.php

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Http\Requests\BackupUploadRequest;
use BackupManager\Filesystems\Destination;
use BackupManager\Manager;
use Illuminate\Http\Request;
use League\Flysystem\FileExistsException;
use League\Flysystem\FileNotFoundException;
class BackupsController extends Controller
{
public function index(Request $request)
{
if (!file_exists(storage_path('app/backup/db'))) {
$backups = [];
} else {
$backups = \File::allFiles(storage_path('app/backup/db'));
// Sort files by modified time DESC
usort($backups, function($a, $b) {
return -1 * strcmp($a->getMTime(), $b->getMTime());
});
}
return view('backups.index',compact('backups'));
}
public function store(Request $request)
{
$this->validate($request, [
'file_name' => 'nullable|max:30|regex:/^[\w._-]+$/'
]);
try {
$manager = app()->make(Manager::class);
$fileName = $request->get('file_name') ?: date('Y-m-d_Hi');
$manager->makeBackup()->run('mysql', [
new Destination('local', 'backup/db/' . $fileName)
], 'gzip');
return redirect()->route('backups.index');
} catch (FileExistsException $e) {
return redirect()->route('backups.index');
}
}
public function destroy($fileName)
{
if (file_exists(storage_path('app/backup/db/') . $fileName)) {
unlink(storage_path('app/backup/db/') . $fileName);
}
return redirect()->route('backups.index');
}
public function download($fileName)
{
return response()->download(storage_path('app/backup/db/') . $fileName);
}
public function restore($fileName)
{
try {
$manager = app()->make(Manager::class);
$manager->makeRestore()->run('local', 'backup/db/' . $fileName, 'mysql', 'gzip');
} catch (FileNotFoundException $e) {}
return redirect()->route('backups.index');
}
public function upload(BackupUploadRequest $request)
{
$file = $request->file('backup_file');
if (file_exists(storage_path('app/backup/db/') . $file->getClientOriginalName()) == false) {
$file->storeAs('backup/db', $file->getClientOriginalName());
}
return redirect()->route('backups.index');
}
}

2
app/Providers/AppServiceProvider.php

@ -13,6 +13,8 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot() public function boot()
{ {
require_once app_path() . '/Helpers/functions.php';
// Always redirect to https. // Always redirect to https.
if($this->app->environment() === 'production') { if($this->app->environment() === 'production') {
$this->app['request']->server->set('HTTPS', true); $this->app['request']->server->set('HTTPS', true);

1
composer.json

@ -6,6 +6,7 @@
"type": "project", "type": "project",
"require": { "require": {
"php": ">=5.6.4", "php": ">=5.6.4",
"backup-manager/laravel": "^1.2",
"laravel/framework": "5.4.*", "laravel/framework": "5.4.*",
"laravel/tinker": "~1.0", "laravel/tinker": "~1.0",
"luthfi/formfield": "^0.1.6" "luthfi/formfield": "^0.1.6"

124
composer.lock

@ -4,9 +4,131 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "07477f8810af9f131f85a76202d51325",
"content-hash": "bc9e15954b41c3284e71738afec16a04",
"packages": [ "packages": [
{ {
"name": "backup-manager/backup-manager",
"version": "1.1.3",
"source": {
"type": "git",
"url": "https://github.com/backup-manager/backup-manager.git",
"reference": "9e53714a993135f57fe2bff001203b6f75c2387c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/backup-manager/backup-manager/zipball/9e53714a993135f57fe2bff001203b6f75c2387c",
"reference": "9e53714a993135f57fe2bff001203b6f75c2387c",
"shasum": ""
},
"require": {
"league/flysystem": "~1.0",
"php": ">=5.5.9",
"symfony/process": "~2.1|~3.0"
},
"require-dev": {
"aws/aws-sdk-php": "~3.0",
"dropbox/dropbox-sdk": "~1.1",
"league/flysystem-aws-s3-v3": "~1.0",
"league/flysystem-dropbox": "~1.0",
"league/flysystem-rackspace": "~1.0",
"league/flysystem-sftp": "~1.0",
"mockery/mockery": "~0.9",
"phpspec/phpspec": "~2.1",
"satooshi/php-coveralls": "~0.6"
},
"suggest": {
"league/flysystem-aws-s3-v3": "AwsS3 and GoogleCS adapter support.",
"league/flysystem-dropbox": "Dropbox adapter support.",
"league/flysystem-rackspace": "Rackspace adapter support.",
"league/flysystem-sftp": "Sftp adapter support."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"BackupManager\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Shawn McCool",
"email": "shawn@heybigname.com",
"homepage": "http://heybigname.com/"
},
{
"name": "Mitchell van Wijngaarden",
"email": "mitchell@kooding.nl",
"homepage": "http://heybigname.com/"
}
],
"description": "A framework agnostic database backup manager with user-definable procedures and support for S3, Dropbox, FTP, SFTP, and more with drivers for popular frameworks.",
"time": "2017-01-05T12:10:13+00:00"
},
{
"name": "backup-manager/laravel",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/backup-manager/laravel.git",
"reference": "343885742036ff6a20850190c9bfcfa697087a94"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/backup-manager/laravel/zipball/343885742036ff6a20850190c9bfcfa697087a94",
"reference": "343885742036ff6a20850190c9bfcfa697087a94",
"shasum": ""
},
"require": {
"backup-manager/backup-manager": "^1.0",
"illuminate/console": "^4.0||^5.0",
"illuminate/container": "^4.0||^5.0",
"illuminate/support": "^4.0||^5.0",
"php": ">=5.5.0",
"symfony/process": "^2.0||^3.0"
},
"require-dev": {
"mockery/mockery": "dev-master",
"satooshi/php-coveralls": "~0.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-4": {
"BackupManager\\Laravel\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Shawn McCool",
"email": "shawn@heybigname.com",
"homepage": "http://heybigname.com/"
},
{
"name": "Mitchell van Wijngaarden",
"email": "mitchell@kooding.nl",
"homepage": "http://heybigname.com/"
}
],
"description": "Database backup manager seamlessly integrated with Laravel 4 or 5 with user-definable procedures and support for S3, Dropbox, FTP, SFTP, and more.",
"time": "2017-07-31T12:23:01+00:00"
},
{
"name": "dnoegel/php-xdg-base-dir", "name": "dnoegel/php-xdg-base-dir",
"version": "0.1", "version": "0.1",
"source": { "source": {

1
config/app.php

@ -166,6 +166,7 @@ return [
/* /*
* Package Service Providers... * Package Service Providers...
*/ */
BackupManager\Laravel\Laravel5ServiceProvider::class,
Laravel\Tinker\TinkerServiceProvider::class, Laravel\Tinker\TinkerServiceProvider::class,
Luthfi\FormField\FormFieldServiceProvider::class, Luthfi\FormField\FormFieldServiceProvider::class,
Barryvdh\Debugbar\ServiceProvider::class, Barryvdh\Debugbar\ServiceProvider::class,

65
resources/views/backups/forms.blade.php

@ -0,0 +1,65 @@
@if (Request::get('action') == 'delete' && Request::has('file_name'))
<div class="panel panel-danger">
<div class="panel-heading">
<h3 class="panel-title">{{ trans('backup.delete') }}</h3>
</div>
<div class="panel-body">
<p>{!! trans('backup.sure_to_delete_file', ['filename' => Request::get('file_name')]) !!}</p>
</div>
<div class="panel-footer">
<a href="{{ route('backups.index') }}" class="btn btn-default">{{ trans('backup.cancel_delete') }}</a>
<form action="{{ route('backups.destroy', Request::get('file_name')) }}" method="post" class="pull-right">
{{ method_field('delete') }}
{{ csrf_field() }}
<input type="hidden" name="file_name" value="{{ Request::get('file_name') }}">
<input type="submit" class="btn btn-danger" value="{{ trans('backup.confirm_delete') }}">
</form>
</div>
</div>
@endif
@if (Request::get('action') == 'restore' && Request::has('file_name'))
<div class="panel panel-warning">
<div class="panel-heading"><h3 class="panel-title">{{ trans('backup.restore') }}</h3></div>
<div class="panel-body">
<p>{!! trans('backup.sure_to_restore', ['filename' => Request::get('file_name')]) !!}</p>
</div>
<div class="panel-footer">
<a href="{{ route('backups.index') }}" class="btn btn-default">{{ trans('backup.cancel_restore') }}</a>
<form action="{{ route('backups.restore', Request::get('file_name')) }}"
method="post"
class="pull-right"
onsubmit="return confirm('Click OK to Restore.')">
{{ csrf_field() }}
<input type="hidden" name="file_name" value="{{ Request::get('file_name') }}">
<input type="submit" class="btn btn-warning" value="{{ trans('backup.confirm_restore') }}">
</form>
</div>
</div>
@endif
<div class="panel panel-default">
<div class="panel-body">
<form action="{{ route('backups.store') }}" method="post">
{{ csrf_field() }}
<div class="form-group">
<label for="file_name" class="control-label">{{ trans('backup.create') }}</label>
<input type="text" name="file_name" class="form-control" placeholder="{{ date('Y-m-d_Hi') }}">
{!! $errors->first('file_name', '<div class="text-danger text-right">:message</div>') !!}
</div>
<div class="form-group">
<input type="submit" value="{{ trans('backup.create') }}" class="btn btn-success">
</div>
</form>
<hr>
<form action="{{ route('backups.upload') }}" method="post" enctype="multipart/form-data">
{{ csrf_field() }}
<div class="form-group">
<label for="backup_file" class="control-label">{{ trans('backup.upload') }}</label>
<input type="file" name="backup_file" class="form-control">
{!! $errors->first('backup_file', '<div class="text-danger text-right">:message</div>') !!}
</div>
<div class="form-group">
<input type="submit" value="{{ trans('backup.upload') }}" class="btn btn-primary">
</div>
</form>
</div>
</div>

54
resources/views/backups/index.blade.php

@ -0,0 +1,54 @@
@extends('layouts.app')
@section('title',trans('backup.index_title'))
@section('content')
<h1 class="page-header">{{ trans('backup.index_title') }}</h1>
<div class="row">
<div class="col-md-8">
<div class="panel panel-default table-responsive">
<div class="panel-heading"><h3 class="panel-title">{{ trans('backup.list') }}</h3></div>
<table class="table table-condensed">
<thead>
<th>#</th>
<th>{{ trans('backup.file_name') }}</th>
<th>{{ trans('backup.file_size') }}</th>
<th>{{ trans('backup.created_at') }}</th>
<th class="text-center">{{ trans('backup.actions') }}</th>
</thead>
<tbody>
@forelse($backups as $key => $backup)
<tr>
<td>{{ $key + 1 }}</td>
<td>{{ $backup->getFilename() }}</td>
<td>{{ formatSizeUnits($backup->getSize()) }}</td>
<td>{{ date('Y-m-d H:i:s', $backup->getMTime()) }}</td>
<td class="text-center">
<a href="{{ route('backups.index', ['action' => 'restore', 'file_name' => $backup->getFilename()]) }}"
id="restore_{{ str_replace('.gz', '', $backup->getFilename()) }}"
class="btn btn-warning btn-xs"
title="{{ trans('backup.restore') }}">Restore</i></a>
<a href="{{ route('backups.download', [$backup->getFilename()]) }}"
id="download_{{ str_replace('.gz', '', $backup->getFilename()) }}"
class="btn btn-info btn-xs"
title="{{ trans('backup.download') }}">Download</a>
<a href="{{ route('backups.index', ['action' => 'delete', 'file_name' => $backup->getFilename()]) }}"
id="del_{{ str_replace('.gz', '', $backup->getFilename()) }}"
class="btn btn-danger btn-xs"
title="{{ trans('backup.delete') }}">Delete</a>
</td>
</tr>
@empty
<tr>
<td colspan="3">{{ trans('backup.empty') }}</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="col-md-4">
@include('backups.forms')
</div>
</div>
@endsection

4
resources/views/users/chart.blade.php

@ -4,9 +4,7 @@
</div> </div>
<div class="container-fluid"> <div class="container-fluid">
<h1 class="page-header"> <h1 class="page-header">
<div class="pull-right">
{{ link_to_route('users.show', 'Lihat Profil '.$user->name, [$user->id], ['class' => 'btn btn-default']) }}
</div>
@include('users.partials.action-buttons')
{{ $user->name }} <small>Bagan Keluarga</small> {{ $user->name }} <small>Bagan Keluarga</small>
</h1> </h1>

6
resources/views/users/edit.blade.php

@ -7,7 +7,7 @@
</div> </div>
Edit Profil {{ $user->profileLink() }} Edit Profil {{ $user->profileLink() }}
</h2> </h2>
{{ Form::model($user, ['route' => ['users.update', $user->id], 'method' =>'patch']) }}
{{ Form::model($user, ['route' => ['users.update', $user->id], 'method' =>'patch', 'autocomplete' => 'nope']) }}
<div class="row"> <div class="row">
<div class="col-md-4"> <div class="col-md-4">
<div class="panel panel-default"> <div class="panel panel-default">
@ -40,8 +40,8 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">Akun Login</h3></div> <div class="panel-heading"><h3 class="panel-title">Akun Login</h3></div>
<div class="panel-body"> <div class="panel-body">
{!! FormField::email('email', ['label' => 'Email', 'placeholder' => 'Misal: nama@mail.com']) !!}
{!! FormField::password('password', ['label' => 'Password', 'placeholder' => '******']) !!}
{!! FormField::email('email', ['label' => 'Email', 'placeholder' => 'Misal: nama@mail.com', 'autocomplete' => 'off']) !!}
{!! FormField::password('password', ['label' => 'Password', 'placeholder' => '******', 'autocomplete' => 'off']) !!}
</div> </div>
</div> </div>
</div> </div>

8
resources/views/users/partials/action-buttons.blade.php

@ -0,0 +1,8 @@
<div class="pull-right btn-group" role="group">
@can ('edit', $user)
{{ link_to_route('users.edit', 'Edit Data', [$user->id], ['class' => 'btn btn-warning']) }}
@endcan
{{ link_to_route('users.show', 'Lihat Profil '.$user->name, [$user->id], ['class' => Request::segment(3) == null ? 'btn btn-default active' : 'btn btn-default']) }}
{{ link_to_route('users.chart', 'Lihat Bagan Keluarga', [$user->id], ['class' => Request::segment(3) == 'chart' ? 'btn btn-default active' : 'btn btn-default']) }}
{{ link_to_route('users.tree', 'Lihat Pohon Keluarga', [$user->id], ['class' => Request::segment(3) == 'tree' ? 'btn btn-default active' : 'btn btn-default']) }}
</div>

2
resources/views/users/partials/childs.blade.php

@ -5,7 +5,7 @@
{{ link_to_route('users.show', 'Tambah Anak', [$user->id, 'action' => 'add_child'], ['class' => 'btn btn-success btn-xs']) }} {{ link_to_route('users.show', 'Tambah Anak', [$user->id, 'action' => 'add_child'], ['class' => 'btn btn-success btn-xs']) }}
</div> </div>
@endcan @endcan
<h3 class="panel-title">Anak-Anak</h3>
<h3 class="panel-title">Anak-Anak ({{ $user->childs->count() }})</h3>
</div> </div>
<ul class="list-group"> <ul class="list-group">

8
resources/views/users/show.blade.php

@ -2,13 +2,7 @@
@section('content') @section('content')
<h1 class="page-header"> <h1 class="page-header">
<div class="pull-right">
@can ('edit', $currentUser)
{{ link_to_route('users.edit', 'Edit Data', [$currentUser->id], ['class' => 'btn btn-warning']) }}
@endcan
{{ link_to_route('users.chart', 'Lihat Bagan Keluarga', [$currentUser->id], ['class' => 'btn btn-default']) }}
{{ link_to_route('users.tree', 'Lihat Pohon Keluarga', [$currentUser->id], ['class' => 'btn btn-default']) }}
</div>
@include('users.partials.action-buttons', ['user' => $currentUser])
{{ $currentUser->name }} <small>Profil</small> {{ $currentUser->name }} <small>Profil</small>
</h1> </h1>
<div class="row"> <div class="row">

4
resources/views/users/tree.blade.php

@ -4,9 +4,7 @@
</div> </div>
<div class="container-fluid"> <div class="container-fluid">
<h1 class="page-header"> <h1 class="page-header">
<div class="pull-right">
{{ link_to_route('users.show', 'Lihat Profil '.$user->name, [$user->id], ['class' => 'btn btn-default']) }}
</div>
@include('users.partials.action-buttons')
{{ $user->name }} <small>Pohon Keluarga</small> {{ $user->name }} <small>Pohon Keluarga</small>
</h1> </h1>
<?php <?php

8
routes/web.php

@ -30,3 +30,11 @@ Route::get('users/{user}/edit', 'UsersController@edit')->name('users.edit');
Route::patch('users/{user}', 'UsersController@update')->name('users.update'); Route::patch('users/{user}', 'UsersController@update')->name('users.update');
Route::get('users/{user}/chart', 'UsersController@chart')->name('users.chart'); Route::get('users/{user}/chart', 'UsersController@chart')->name('users.chart');
Route::get('users/{user}/tree', 'UsersController@tree')->name('users.tree'); Route::get('users/{user}/tree', 'UsersController@tree')->name('users.tree');
/**
* Backup Restore Database Routes
*/
Route::post('backups/upload', ['as'=>'backups.upload', 'uses'=>'BackupsController@upload']);
Route::post('backups/{fileName}/restore', ['as'=>'backups.restore', 'uses'=>'BackupsController@restore']);
Route::get('backups/{fileName}/dl', ['as'=>'backups.download', 'uses'=>'BackupsController@download']);
Route::resource('backups','BackupsController');
Loading…
Cancel
Save