FEATURE #1431: add template edit

This commit is contained in:
Michal Kváček
2016-02-19 15:10:39 +01:00
parent 53395bc47a
commit 57e08b76a8
12 changed files with 413 additions and 14 deletions
+179
View File
@@ -0,0 +1,179 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests;
use App\Http\Requests\EditTemplateRequest;
use App\Models\Template;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
class EditTemplate extends Controller
{
/**
* Render form for editing template
*
* @param $template
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function getEdit($template)
{
$template = Template::findOrFail($template);
$path = $template->getPath();
// load template content
$absolutePath = $template->getPathToTemplate();
$content = file_get_contents($absolutePath);
return view('admin.templates.edit', [
'template' => $template,
'templateContent' => $content,
'isWritable' => is_writable(dirname($absolutePath)),
'path' => $path,
]);
}
/**
* Save meta information about template and edited content
*
* @param EditTemplateRequest $request
* @param $templateId
*
* @return \Illuminate\Http\RedirectResponse
*/
public function postEdit(EditTemplateRequest $request, $templateId)
{
// find template
$template = Template::findOrFail($templateId);
// setup meta information
$template->name = $request->name;
if ($request->description) {
$template->description = $request->description;
}
// save meta information into database
$template->save();
// try to save content
return $this->saveContent($template, $request);
}
/**
* Save physical content of given template
*
* @param Template $template
* @param EditTemplateRequest $request
*
* @return \Illuminate\Http\RedirectResponse
*/
private function saveContent(Template $template, EditTemplateRequest $request)
{
// get templates with paths
$originalTemplate = $template->getPathToTemplate(Template::ORIGINAL_DIRECTORY);
$customTemplate = $template->getPathToTemplate(Template::CUSTOM_EDIT_DIRECTORY);
// get paths
$originalDir = dirname($originalTemplate);
$customDir = dirname($customTemplate);
// try to save content
if (!is_writable($customDir) && !is_writable($originalDir)) {
return redirect()
->route('templates.edit', ['id' => $request->id])
->withInput()
->with('error', trans('admin/templates.cannotWriteContent'));
}
// save new content only if something was changed
if ($this->isContentDifferent($originalTemplate, $customTemplate, $request->templateContent)) {
// create directory for custom updates
if (!is_dir($customDir)) {
File::makeDirectory($customDir);
}
// make copy of existing template (if exists)
$this->backupTemplates($customTemplate);
// save content
File::put($customTemplate, $request->templateContent);
}
return redirect()
->route('templates.overview', ['id' => $template->path])
->with('success', trans('admin/templates.editSuccessfull'));
}
/**
* Get last "revision" of edited template.
*
* Template changes are not discarded, however new file is created. Last template is renamed to
* <template>.blade.php.<num>, where <num> is result of this method.
*
* @param $template
*
* @return int
*/
private function getNewRevisionNumber($template)
{
$number = 0;
while (File::exists($template . '.' . $number)) {
$number++;
}
return $number;
}
/**
* If exists previously edited template, rename it to <template>.blade.php.<number>
*
* @param $template
*/
private function backupTemplates($template)
{
// get last sequence number
$number = $this->getNewRevisionNumber($template);
// nothing to backup
if ($number == 0) {
return;
}
// rename template
File::move($template, $template . '.' . $number);
}
/**
* Compare data from previous/original template
*
* @param $originalTemplate
* @param $customTemplate
* @param $currentContent
*
* @return bool
*/
private function isContentDifferent($originalTemplate, $customTemplate, $currentContent)
{
/**
* get last revision of template - we need to substract 1 from result from getNewRevisionNumber(),
* because this method returns number of first non-existing template
*/
$lastRevision = $this->getNewRevisionNumber($customTemplate) - 1;
// select compared file
if ($lastRevision < 0) {
$diffFile = $originalTemplate;
} else {
$diffFile = $customTemplate . '.' . $lastRevision;
}
// get content
$originalContent = file_get_contents($diffFile);
return $originalContent != $currentContent;
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Http\Requests;
class EditTemplateRequest extends Request
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|max:128',
'templateContent' => 'required'
];
}
}
+2 -2
View File
@@ -61,10 +61,10 @@ Route::group([
Route::get('templates/new/{pathId}', 'TemplateOverview@getPath')
->name('templates.new');
Route::get('templates/edit/{id}', 'TemplateOverview@getPath')
Route::get('templates/edit/{id}', 'EditTemplate@getEdit')
->name('templates.edit');
Route::post('templates/edit/{id}', 'TemplateOverview@getPath')
Route::post('templates/edit/{id}', 'EditTemplate@postEdit')
->name('templates.postEdit');
});
+64 -4
View File
@@ -18,18 +18,78 @@ class Template extends Model
'editable'
];
protected $guarded = [];
protected $pathModel = null;
const EDITED_TEMPLATES_DIRECTORY = 'custom';
const TEMPLATE_SUFFIX = '.blade.php';
const CUSTOM_EDIT_DIRECTORY = 1;
const ORIGINAL_DIRECTORY = 2;
/**
* In one path may be many templates
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function template()
public function getPath()
{
return $this->belongsTo('App\Models\TemplatePath', 'id');
if (!$this->pathModel) {
$this->pathModel = TemplatePath::where('id', $this->path)
->first();
}
return $this->pathModel;
}
/**
* Getter for absolute path to template
*
* @param null $mode
*
* @return string
*/
public function getPathToTemplate($mode = null)
{
$templateName = $this->template_name;
$path = $this->getPath();
// delete namespace if given
if (strpos($this->template_name, '::')) {
list(, $templateName) = explode('::', $templateName);
}
// get template name, do not care about directories (they are already contained in path)
$parts = explode('.', $templateName);
$templateName = last($parts);
// just a shortcut for this unbelievable long constant name...
$_ = DIRECTORY_SEPARATOR;
// path parts
$absolutePath = base_path($path->path);
$templatePath = str_replace('.', $_, $templateName);
$file = $templatePath . self::TEMPLATE_SUFFIX;
// compose two versions of path - original and with "custom" directory
$original = $absolutePath . $_ . $file;
$edited = $absolutePath . $_ . self::EDITED_TEMPLATES_DIRECTORY . $_ . $file;
// decide which path will be returned
switch ($mode) {
case self::CUSTOM_EDIT_DIRECTORY:
return $edited;
break;
case self::ORIGINAL_DIRECTORY:
return $original;
break;
default:
if (file_exists($absolutePath . $_ . self::EDITED_TEMPLATES_DIRECTORY . $_ . $file)) {
return $edited;
}
return $original;
break;
}
}
}
+14 -8
View File
@@ -1,11 +1,17 @@
<?php
return [
'core' => 'Core templates',
'homepage' => 'Home page',
'overview' => 'Templates',
'noSubdirectories' => 'No directories available',
'parentDirectory' => 'Parent directory',
'noTemplates' => 'No templates available',
'new' => 'New template',
'subdirectories' => 'Subdirectories'
'core' => 'Core templates',
'homepage' => 'Home page',
'overview' => 'Templates',
'noSubdirectories' => 'No directories available',
'parentDirectory' => 'Parent directory',
'noTemplates' => 'No templates available',
'new' => 'New template',
'subdirectories' => 'Subdirectories',
'edit' => 'Edit template :name',
'name' => 'Template name',
'description' => 'Template description',
'editSuccessfull' => 'Template successfully edited.',
'notWritable' => 'Cannot write to :file. Any submited changes in this file will <strong>not</strong> be saved!',
'cannotWriteContent' => 'Cannot save template content, directory has unsufficient permissions. Meta information were saved.'
];
@@ -0,0 +1,14 @@
@extends('admin.layout')
@section('pageName', @trans('admin/templates.edit', ['name' => $template->template_name]))
@section('content')
<div class="row">
<h2>@lang('admin/templates.edit', ['name' => $template->template_name])</h2>
</div>
<div class="row">
@include('admin.templates.editForm')
</div>
@stop
@@ -0,0 +1,54 @@
@if (session('success'))
<div data-alert class="callout success">
<strong>{{ session('success') }}</strong>
</div>
@endif
@if (session('error'))
<div data-alert class="callout alert">
<strong>{{ session('error') }}</strong>
</div>
@endif
@if (!$isWritable)
<div data-alert class="callout warning">
@lang('admin/templates.notWritable', ['file' => $path->path])
</div>
@endif
<form action="{{route('templates.postEdit', ['id' => $template->id])}}" method="POST">
{!! csrf_field() !!}
<div class="row">
<div class="column large-8">
<div style="height: 30em">
@include('admin.snippets.aceEditor', [
'value' => old('templateContent', $templateContent),
'theme' => 'xcode',
'mode' => 'html',
'name' => 'templateContent'
])
</div>
</div>
<div class="column large-4">
<label>
@lang('admin/templates.name')
<input type="text" name="name" value="{{old('name', $template->name)}}" />
</label>
<label>
@lang('admin/templates.description')
<textarea name="description" rows="3">{{old('description', $template->description)}}</textarea>
</label>
<button type="submit" class="button success">
@lang('admin/templates.edit', ['name' => $template->template_name])
</button>
</div>
</div>
</form>
@@ -5,6 +5,18 @@
@section('content')
<h1>@lang('admin/templates.overview')</h1>
@if (session('success'))
<div data-alert class="callout success">
<strong>{{ session('success') }}</strong>
</div>
@endif
@if (session('error'))
<div data-alert class="callout alert">
<strong>{{ session('error') }}</strong>
</div>
@endif
<div class="row">
<div class="column large-9">
@include('admin.templates.templatesTiles')
View File
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@yield('head_meta')
<title>@yield('pageName', @trans('visitor/common.welcome')) | {{$vendorName}}</title>
<link rel="stylesheet" href="{{asset('../resources/assets/css/foundation.min.css')}}" />
<link rel="stylesheet" href="{{asset('../resources/assets/css/font-awesome.min.css')}}" />
<link rel="stylesheet" href="{{asset('../resources/assets/css/admin.css')}}" />
@yield('head_stylesheets')
<link href="https://fonts.googleapis.com/css?family=Lato:100" rel="stylesheet" type="text/css">
<style>
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
color: #B0BEC5;
display: table;
font-weight: 100;
font-family: 'Lato';
}
.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">
@yield('content')
</div>
</div>
</body>
</html>
View File