FEATURE: move bussiness logic out of models

This commit is contained in:
Michal Kváček
2016-10-17 01:05:20 +02:00
parent 2ded2924a1
commit e26e6fd460
46 changed files with 1069 additions and 1039 deletions
+6 -4
View File
@@ -1,6 +1,7 @@
<?php
namespace App\CMS\Contracts\Widgets;
use App\Http\Requests\Request;
use App\Models\Widgets\Widget;
interface PublicWidget
@@ -27,10 +28,11 @@ interface PublicWidget
public static function getPresenter();
/**
* @param Widget $widget
* @param \stdClass $data
* Get widget settings for saving into database
*
* @return mixed
* @param Request $request
*
* @return array
*/
public static function save(Widget $widget, \stdClass $data);
public static function save(Request $request);
}
+1 -1
View File
@@ -8,7 +8,7 @@
namespace App\CMS\Enumerations;
use App\Models\TemplatePath;
use App\Models\Templates\TemplatePath;
class TemplatePaths extends BaseEnumeration
{
@@ -8,7 +8,7 @@
namespace App\CMS\Enumerations;
use App\Models\TemplatePath;
use App\Models\Templates\TemplatePath;
class TemplatePathsHumanReadable extends BaseEnumeration
{
@@ -8,7 +8,7 @@
namespace App\CMS\Enumerations;
use App\Models\TemplatePath;
use App\Models\Templates\TemplatePath;
class TemplatePathsHumanReadableRootOnly extends BaseEnumeration
{
+1 -1
View File
@@ -8,7 +8,7 @@
namespace App\CMS\Enumerations;
use App\Models\Template;
use App\Models\Templates\Template;
class Templates extends BaseEnumeration
{
@@ -3,8 +3,8 @@ namespace App\CMS\Handlers\Registrators;
use App\CMS\Enumerations\TemplatePaths;
use App\CMS\Enumerations\Templates;
use App\Models\Template;
use App\Models\TemplatePath;
use App\Models\Templates\Template;
use App\Models\Templates\TemplatePath;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Symfony\Component\Process\Exception\InvalidArgumentException;
+5 -13
View File
@@ -2,8 +2,8 @@
namespace App\CMS\Widgets\HtmlCode;
use App\CMS\Contracts\Widgets\PublicWidget;
use App\CMS\Widgets\Contracts\WidgetRenderer;
use App\CMS\Widgets\Exceptions\MissingParameterException;
use App\Http\Requests\Request;
use App\Models\Widgets\Widget;
class HtmlCode implements PublicWidget
@@ -39,25 +39,17 @@ class HtmlCode implements PublicWidget
}
/**
* @param Widget $widget
* @param \stdClass $data
* @param Request $data
*
* @return Widget
* @return array
* @throws MissingParameterException
*/
public static function save(Widget $widget, \stdClass $data)
public static function save(Request $data)
{
if (!isset($data->code) || empty($data->code)) {
throw new MissingParameterException('code');
}
$widgetSettings = [
'code' => $data->code,
'containerClass' => isset($data->containerClass) ? $data->containerClass : ''
];
$widget->settings = json_encode($widgetSettings);
return $widget;
return ['code' => $data->code];
}
}
+3 -10
View File
@@ -4,8 +4,8 @@ namespace App\CMS\Widgets\Menu;
use App\CMS\Contracts\Widgets\PublicWidget;
use App\CMS\Widgets\Contracts\WidgetRenderer;
use App\CMS\Widgets\Exceptions\MissingParameterException;
use App\Http\Requests\Request;
use App\Models\Content\Content;
use App\Models\Widgets\Widget;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class Menu implements PublicWidget
@@ -46,7 +46,7 @@ class Menu implements PublicWidget
*
* @return mixed
*/
public static function save(Widget $widget, \stdClass $data)
public static function save(Request $data)
{
if (!isset($data->settings) || empty($data->settings) || !json_decode($data->settings)) {
throw new MissingParameterException('settings');
@@ -61,14 +61,7 @@ class Menu implements PublicWidget
}
}
$settings = [
'links' => $links,
'containerClass' => $data->containerClass
];
$widget->settings = json_encode($settings);
return $widget;
return ['links' => $links];
}
public static function selectContentPages($ids)
@@ -9,7 +9,7 @@ use App\Http\Requests\CreateNewContentRequest;
use App\Models\Content\Content;
use App\Models\FileManager\File;
use App\Models\Module;
use App\Models\Template;
use App\Models\Templates\Template;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
@@ -16,7 +16,7 @@ use App\Models\Content\Content;
use App\Models\Content\ContentHistory;
use App\Models\FileManager\File;
use App\Models\Module;
use App\Models\Template;
use App\Models\Templates\Template;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Admin\Templates;
use App\Http\Controllers\Admin\Controller;
use App\Http\Requests\CopyTemplateRequest;
use App\Models\Template;
use App\Models\Templates\Template;
class CopyTemplate extends Controller
{
@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Admin\Templates;
use App\Http\Controllers\Controller;
use App\Http\Requests;
use App\Http\Requests\EditTemplateRequest;
use App\Models\Template;
use App\Models\Templates\Template;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
@@ -3,7 +3,7 @@
namespace App\Http\Controllers\Admin\Templates;
use App\Http\Controllers\Admin\Controller;
use App\Models\TemplatePath;
use App\Models\Templates\TemplatePath;
class TemplateOverview extends Controller
{
@@ -4,7 +4,7 @@ namespace App\Http\Controllers\Admin\Widgets\Areas;
use App\Http\Controllers\Admin\Controller;
use App\Http\Requests\NewWidgetAreaRequest;
use App\Models\WidgetArea;
use App\Models\Widgets\WidgetArea;
use Illuminate\Support\Facades\Event;
class CreateNewWidgetArea extends Controller
@@ -5,8 +5,8 @@ namespace App\Http\Controllers\Admin\Widgets\Areas;
use App\Http\Controllers\Admin\Controller;
use App\Http\Requests\EditWidgetAreaRequest;
use App\Models\Widgets\Widget;
use App\Models\WidgetArea;
use App\Models\WidgetInWidgetArea;
use App\Models\Widgets\WidgetArea;
use App\Models\Widgets\WidgetInWidgetArea;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Event;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
@@ -3,8 +3,7 @@
namespace App\Http\Controllers\Admin\Widgets\Areas;
use App\Http\Controllers\Admin\Controller;
use App\Http\Requests;
use App\Models\WidgetArea;
use App\Models\Widgets\WidgetArea;
class WidgetAreaOverview extends Controller
{
@@ -50,7 +50,7 @@ class CreateNewWidget extends Controller
// try to save this widget
try {
$this->widget->createNew($widgetType, $request->all());
$this->widget->createNew($widgetType, $request);
} catch (MissingParameterException $e) {
// user omitted some value
return $this->invalidWidget($type, $e->getMessage());
+2 -68
View File
@@ -3,13 +3,14 @@
namespace App\Models\ActionLog;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
/**
* Class ActionLog
*/
class ActionLog extends Model
{
use ModelTraits\ActionLog;
protected $table = 'action_log';
public $timestamps = false;
protected $fillable = [
@@ -19,71 +20,4 @@ class ActionLog extends Model
'user_agent',
'parameters'
];
protected $guarded = [];
/**
* Query factory for loading action logs with their type (therefore with name and value)
*
* @param bool $joinUser
*
* @return mixed
*/
public function getLogActionsQuery($joinUser = false)
{
$actions = \DB::table('action_log')
->select('action_log.*', 'action_log_types.*')
->join('action_log_types', 'action_log_types.id', '=', 'action_log.type_id')
->orderBy('action_log.created_at', 'DESC');
if ($joinUser) {
$actions = $actions->join('users', 'users.id', '=', 'action_log.user_id')
->select('action_log.*', 'action_log_types.*', 'users.email', 'users.name as user_name');
}
return $actions;
}
/**
* Get last N user actions
*
* @param null $id
* @param int $limit
*
* @return array
*/
public function getUserActions($id = null, $limit = 15)
{
// if user is not specified, use logged one
if (is_null($id)) {
$id = Auth::user()->id;
}
// load last N actions of selected user
$actions = $this->getLogActionsQuery()
->limit($limit)
->where('user_id', $id)
->get();
// translate and return result
$this->translateLogMessages($actions);
return $actions;
}
/**
* Method used for translating and filling parameters into value of log.
*
* @param $messages
*
* @return array
*/
public function translateLogMessages(&$messages)
{
// translate name and value of each record
foreach ($messages as $i => $action) {
$messages[$i]->name = trans($action->name);
$messages[$i]->value = trans($action->value, (array)json_decode($action->parameters));
}
}
}
@@ -0,0 +1,72 @@
<?php
namespace App\Models\ActionLog\ModelTraits;
use Illuminate\Support\Facades\Auth;
trait ActionLog
{
/**
* Query factory for loading action logs with their type (therefore with name and value)
*
* @param bool $joinUser
*
* @return mixed
*/
public function getLogActionsQuery($joinUser = false)
{
$actions = \DB::table('action_log')
->select('action_log.*', 'action_log_types.*')
->join('action_log_types', 'action_log_types.id', '=', 'action_log.type_id')
->orderBy('action_log.created_at', 'DESC');
if ($joinUser) {
$actions = $actions->join('users', 'users.id', '=', 'action_log.user_id')
->select('action_log.*', 'action_log_types.*', 'users.email', 'users.name as user_name');
}
return $actions;
}
/**
* Get last N user actions
*
* @param null $id
* @param int $limit
*
* @return array
*/
public function getUserActions($id = null, $limit = 15)
{
// if user is not specified, use logged one
if (is_null($id)) {
$id = Auth::user()->id;
}
// load last N actions of selected user
$actions = $this->getLogActionsQuery()
->limit($limit)
->where('user_id', $id)
->get();
// translate and return result
$this->translateLogMessages($actions);
return $actions;
}
/**
* Method used for translating and filling parameters into value of log.
*
* @param $messages
*
* @return array
*/
public function translateLogMessages(&$messages)
{
// translate name and value of each record
foreach ($messages as $i => $action) {
$messages[$i]->name = trans($action->name);
$messages[$i]->value = trans($action->value, (array)json_decode($action->parameters));
}
}
}
+1 -356
View File
@@ -2,17 +2,10 @@
namespace App\Models\Content;
use App\CMS\Enumerations\ContentStatuses;
use App\Http\Requests\Request;
use App\Models\FileManager\Directory;
use App\Models\FileManager\File;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use Modules\Categories\Entities\Category;
/**
@@ -20,7 +13,7 @@ use Modules\Categories\Entities\Category;
*/
class Content extends Model
{
use SoftDeletes;
use SoftDeletes, ModelTraits\Content;
public $timestamps = true;
protected $fillable = [
'user_id',
@@ -41,101 +34,9 @@ class Content extends Model
'module_settings',
'status'
];
protected $guarded = [];
protected $foundSubpages = [];
protected $subpagesInit = false;
const STATUS_DRAFT = 'draft';
const STATUS_PROTECTED = 'protected';
const STATUS_PUBLIC = 'public';
/**
* @var File
*/
private $file;
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->file = App::make(File::class);
}
/**
* Getter for query with setup filter for non-deleted and non-draft pages
*/
public function getPageQuery()
{
return Content::where('status', '!=', Content::STATUS_DRAFT)
->select('contents.*')
->with('author')
->join('modules', 'modules.handler', '=', 'contents.module_handler')
->where(function ($query) {
$query->where('published_from', '<', Carbon::now())
->orWhereNull('published_from');
})
->where(function ($query) {
$query->where('published_to', '>', Carbon::now())
->orWhereNull('published_to');
})
->where('language', LaravelLocalization::getCurrentLocale())
->where('modules.enabled', true);
}
/**
* Getter for homepage content page
* @return Content
*/
public function getHomepage()
{
return $this->getPageQuery()
->where('is_homepage', 1)
->first();
}
/**
* Getter for any page specified by its url.
*
* @param $url
*
* @return Content
*/
public function getByURL($url)
{
return $this->getPageQuery()
->where('url', $url)
->first();
}
/**
* Get list of all subpages
*
* @param bool $orderBy
* @param bool $orderDirection
*
* @return array
*/
public function subpages($orderBy = false, $orderDirection = false)
{
if (!$this->subpagesInit) {
$subpages = $this->getPageQuery()
->where('contents.parent_id', $this->id);
if ($orderBy) {
$subpages->orderBy($orderBy, $orderDirection ? $orderDirection : 'ASC');
}
$subpages = $subpages->get();
// convert JSON settings into stdClass
foreach ($subpages as $i => $subpage) {
$subpages[$i]->module_settings = json_decode($subpage->module_settings);
}
$this->subpagesInit = true;
$this->foundSubpages = $subpages;
}
return $this->foundSubpages;
}
/**
* Is current content draft?
@@ -177,231 +78,6 @@ class Content extends Model
return $this->isPublic() || $this->isProtected();
}
/**
* Returns list of all root pages - those pages, which parent_id is null
*
* @return mixed
*/
public function rootPages()
{
$rootPages = $this->getPageQuery()
->where('parent_id', null);
if ($this->id) {
$rootPages = $rootPages->where('id', '!=', $this->id);
}
return $rootPages->get();
}
/**
* Getter for overview of all content pages.
* These content pages may be filtered by using some handler
*
* @param bool $handler
* @param bool $status
* @param bool $joinAuthor
* @param bool $joinParent
*
* @return
*/
public function getOverview($handler = false, $status = false, $joinAuthor = false, $joinParent = false)
{
$query = DB::table('contents')
->select('contents.*', 'modules.name AS module_name', 'modules.id AS module_id')
->join('modules', 'modules.handler', '=', 'contents.module_handler')
->whereNull('contents.deleted_at')
->where('contents.language', LaravelLocalization::getCurrentLocale())
->where('modules.enabled', 1)
->orderBy('created_at', 'DESC');
// filter results using handler
if ($handler) {
$query = $query->where('contents.module_handler', $handler);
}
// join user email and user name to results when needed
if ($joinAuthor) {
$query = $query->join('users', 'users.id', '=', 'contents.user_id')
->addSelect(DB::raw('COALESCE(users.name, users.email) AS author_name'))
->addSelect('users.email AS author_email');
}
if ($joinParent) {
$query = $query->leftJoin('contents as parent', 'parent.id', '=', 'contents.parent_id')
->addSelect('parent.title AS parent_title')
->addSelect('parent.url AS parent_url');
}
// add filtering by status
if (in_array($status, ContentStatuses::getSimple())) {
$query = $query->where('contents.status', $status);
}
return $query;
}
/**
* Updates current model with information about changed columns
*
* @param array $fields
* @param array $request
* @param string $revisionId
*
* @return array revisionID and number of changed columns
*/
public function updateWithHistory(array $fields, array $request, $revisionId = null)
{
if (!$revisionId) {
$revisionId = uniqid();
}
$changed = 0;
foreach ($fields as $fieldDB => $fieldRequest) {
// ignore non-existing fields
if (!isset($request[$fieldRequest]) && !is_null($request[$fieldRequest])) {
continue;
}
// if module settings is still JSON, decode it to normal stdClass
if (is_string($this->module_settings)) {
$this->module_settings = json_decode($this->module_settings);
}
// are we editing common settings, or module-specific settings?
if (!in_array($fieldDB, $this->fillable)) {
// this field is not part of "core" content - probably it will be module settings
$commonField = false;
$fieldValue = isset($this->module_settings->$fieldDB) ? $this->module_settings->$fieldDB : null;
} else {
$commonField = true;
$fieldValue = $this->$fieldDB;
}
// remove any whitespaces around real content
if (!is_null($request[$fieldRequest])) {
$request[$fieldRequest] = trim($request[$fieldRequest]);
}
if (trim($fieldValue) != $request[$fieldRequest]) {
$this->saveHistory($revisionId, $fieldRequest, $fieldValue, $request);
// save change into database
if ($commonField) {
$this->$fieldDB = $request[$fieldRequest];
} else {
$this->module_settings->$fieldDB = $request[$fieldRequest];
}
$changed++;
}
}
// make JSON from stdClass
if (!is_string($this->module_settings)) {
$this->module_settings = json_encode($this->module_settings);
}
$this->save();
return [$revisionId, $changed];
}
/**
* Upload and set some image as title photo
*
* @param Request $request
*/
public function uploadTitlePhoto(Request $request)
{
if ($request->hasFile('titlePhoto')) {
return $this->file->upload($request->file('titlePhoto'), $request);
}
return null;
}
/**
* Upload and assign files to newly created content page
*
* @param Request $request
* @param null $revisionId
*
* @return int
*/
public function uploadRelatedFiles(Request $request, $revisionId = null)
{
$files = $request->all()['files'];
$changed = 0;
// check if any file was removed
if ($revisionId) {
$changed += $this->removeRelatedFiles($revisionId, $request);
}
// allow uploading only on created pages
if (!$this->id || !$files[0]) {
return $changed;
}
// find or create new directory for files assigned to content
$directory = Directory::firstOrCreate([
'name' => '.content_files',
'hidden' => true
]);
$request->directoryId = $directory->id;
foreach ($files as $file) {
if (!$file) {
continue;
}
$uploaded = $this->file->upload($file, $request);
// add assignement
ContentHasFile::create([
'file_id' => $uploaded['id'],
'content_id' => $this->id
]);
if ($revisionId) {
$this->saveHistory($revisionId, 'file', null, ['file' => $uploaded['file']->getClientOriginalName()]);
}
$changed++;
}
return $changed;
}
/**
* Delete files from association
*
* @param $revisionId
* @param $request
*
* @return int
*/
private function removeRelatedFiles($revisionId, $request)
{
$files = array_keys((array)$request->keepAssigned);
$allFiles = $this->files()
->whereNotIn('id', $files)
->get();
foreach ($allFiles as $file) {
$this->saveHistory($revisionId, 'file', $file->original_filename, ['file' => null]);
ContentHasFile::where('content_id', $this->id)
->where('file_id', $file->id)
->delete();
}
return count($allFiles);
}
/**
* Mark relationship with its historical records
*
@@ -436,35 +112,4 @@ class Content extends Model
{
return $this->belongsTo(Category::class);
}
/**
* Save history entry into database
*
* @param $revisionId
* @param $fieldRequest
* @param $fieldValue
* @param $request
*/
private function saveHistory($revisionId, $fieldRequest, $fieldValue, $request)
{ // setup history object
$history = [
'user_id' => \Auth::user()->id,
'content_id' => $this->id,
'edit_batch' => $revisionId,
'column' => $fieldRequest,
'old_value' => $fieldValue,
'new_value' => $request[$fieldRequest]
];
// save history
ContentHistory::create($history);
}
public function view()
{
$this->timestamps = false;
$this->viewed++;
$this->save();
$this->timestamps = true;
}
}
-1
View File
@@ -6,7 +6,6 @@ use Illuminate\Database\Eloquent\Model;
class ContentHasFile extends Model
{
public $timestamps = false;
public $table = 'contents_has_files';
+1 -76
View File
@@ -2,9 +2,6 @@
namespace App\Models\Content;
use App\CMS\Handlers\Contracts\AppHandler;
use App\CMS\Handlers\Contracts\HistoryHandler;
use App\CMS\Helpers\Differ;
use Illuminate\Database\Eloquent\Model;
/**
@@ -12,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
*/
class ContentHistory extends Model
{
use ModelTraits\ContentHistory;
protected $table = 'content_history';
// this table does not have created_at and updated_at fields
public $timestamps = false;
@@ -24,77 +22,4 @@ class ContentHistory extends Model
'old_value'
];
protected $guarded = [];
/**
* Returns history grouped by revisions (edit_batches)
*
* @param Content $content
*
* @return array
*/
public function getStructuredHistoryOf(Content $content)
{
$data = $content->history()
->select('content_history.*', 'contents.module_handler', 'users.email AS user_email', 'users.name AS user_name')
->join('users', 'users.id', '=', 'content_history.user_id')
->join('contents', 'contents.id', '=', 'content_history.content_id')
->orderBy('changed_at', 'DESC')
->get();
$info = [];
$history = [];
foreach ($data as $change) {
$info[$change->edit_batch] = [
'changed_at' => $change->changed_at,
'user' => [
'id' => $change->user_id,
'name' => $change->user_name,
'email' => $change->user_email
]
];
$change = $this->handleChangeset($change);
$history[$change->edit_batch][] = $change;
}
return [
'changesets' => $history,
'info' => $info
];
}
/**
* Getter for history handler of given module
*
* @param $change
*
* @return HistoryHandler
*/
protected function getHistoryHandler($change)
{
/**
* @var $handler AppHandler
*/
// get handler of given content page
$handler = $change->module_handler;
return $handler::getHistoryHandler();
}
public function handleChangeset($change)
{
$historyHandler = $this->getHistoryHandler($change);
$change->column_trans = $historyHandler::parseColumnName($change->column);
$change->diff = $historyHandler::parseChangedValue($change);
return $change;
}
public function getDiff($change)
{
return Differ::htmlDiff(e($change->old_value), e($change->new_value));
}
}
+360
View File
@@ -0,0 +1,360 @@
<?php
namespace App\Models\Content\ModelTraits;
use App\CMS\Enumerations\ContentStatuses;
use App\Http\Requests\Request;
use App\Models\FileManager\Directory;
use App\Models\FileManager\File;
use Carbon\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
use App\Models\Content\Content as ContentModel;
trait Content {
/**
* @var File
*/
private $file;
protected $foundSubpages = [];
protected $subpagesInit = false;
public function __construct()
{
$this->file = App::make(File::class);
}
/**
* Getter for query with setup filter for non-deleted and non-draft pages
*/
public function getPageQuery()
{
return ContentModel::where('status', '!=', ContentModel::STATUS_DRAFT)
->select('contents.*')
->with('author')
->join('modules', 'modules.handler', '=', 'contents.module_handler')
->where(function ($query) {
$query->where('published_from', '<', Carbon::now())
->orWhereNull('published_from');
})
->where(function ($query) {
$query->where('published_to', '>', Carbon::now())
->orWhereNull('published_to');
})
->where('language', LaravelLocalization::getCurrentLocale())
->where('modules.enabled', true);
}
/**
* Getter for homepage content page
* @return Content
*/
public function getHomepage()
{
return $this->getPageQuery()
->where('is_homepage', 1)
->first();
}
/**
* Getter for any page specified by its url.
*
* @param $url
*
* @return Content
*/
public function getByURL($url)
{
return $this->getPageQuery()
->where('url', $url)
->first();
}
/**
* Get list of all subpages
*
* @param bool $orderBy
* @param bool $orderDirection
*
* @return array
*/
public function subpages($orderBy = false, $orderDirection = false)
{
if (!$this->subpagesInit) {
$subpages = $this->getPageQuery()
->where('contents.parent_id', $this->id);
if ($orderBy) {
$subpages->orderBy($orderBy, $orderDirection ? $orderDirection : 'ASC');
}
$subpages = $subpages->get();
// convert JSON settings into stdClass
foreach ($subpages as $i => $subpage) {
$subpages[$i]->module_settings = json_decode($subpage->module_settings);
}
$this->subpagesInit = true;
$this->foundSubpages = $subpages;
}
return $this->foundSubpages;
}
/**
* Returns list of all root pages - those pages, which parent_id is null
*
* @return mixed
*/
public function rootPages()
{
$rootPages = $this->getPageQuery()
->where('parent_id', null);
if ($this->id) {
$rootPages = $rootPages->where('id', '!=', $this->id);
}
return $rootPages->get();
}
/**
* Getter for overview of all content pages.
* These content pages may be filtered by using some handler
*
* @param bool $handler
* @param bool $status
* @param bool $joinAuthor
* @param bool $joinParent
*
* @return
*/
public function getOverview($handler = false, $status = false, $joinAuthor = false, $joinParent = false)
{
$query = DB::table('contents')
->select('contents.*', 'modules.name AS module_name', 'modules.id AS module_id')
->join('modules', 'modules.handler', '=', 'contents.module_handler')
->whereNull('contents.deleted_at')
->where('contents.language', LaravelLocalization::getCurrentLocale())
->where('modules.enabled', 1)
->orderBy('created_at', 'DESC');
// filter results using handler
if ($handler) {
$query = $query->where('contents.module_handler', $handler);
}
// join user email and user name to results when needed
if ($joinAuthor) {
$query = $query->join('users', 'users.id', '=', 'contents.user_id')
->addSelect(DB::raw('COALESCE(users.name, users.email) AS author_name'))
->addSelect('users.email AS author_email');
}
if ($joinParent) {
$query = $query->leftJoin('contents as parent', 'parent.id', '=', 'contents.parent_id')
->addSelect('parent.title AS parent_title')
->addSelect('parent.url AS parent_url');
}
// add filtering by status
if (in_array($status, ContentStatuses::getSimple())) {
$query = $query->where('contents.status', $status);
}
return $query;
}
/**
* Updates current model with information about changed columns
*
* @param array $fields
* @param array $request
* @param string $revisionId
*
* @return array revisionID and number of changed columns
*/
public function updateWithHistory(array $fields, array $request, $revisionId = null)
{
if (!$revisionId) {
$revisionId = uniqid();
}
$changed = 0;
foreach ($fields as $fieldDB => $fieldRequest) {
// ignore non-existing fields
if (!isset($request[$fieldRequest]) && !is_null($request[$fieldRequest])) {
continue;
}
// if module settings is still JSON, decode it to normal stdClass
if (is_string($this->module_settings)) {
$this->module_settings = json_decode($this->module_settings);
}
// are we editing common settings, or module-specific settings?
if (!in_array($fieldDB, $this->fillable)) {
// this field is not part of "core" content - probably it will be module settings
$commonField = false;
$fieldValue = isset($this->module_settings->$fieldDB) ? $this->module_settings->$fieldDB : null;
} else {
$commonField = true;
$fieldValue = $this->$fieldDB;
}
// remove any whitespaces around real content
if (!is_null($request[$fieldRequest])) {
$request[$fieldRequest] = trim($request[$fieldRequest]);
}
if (trim($fieldValue) != $request[$fieldRequest]) {
$this->saveHistory($revisionId, $fieldRequest, $fieldValue, $request);
// save change into database
if ($commonField) {
$this->$fieldDB = $request[$fieldRequest];
} else {
$this->module_settings->$fieldDB = $request[$fieldRequest];
}
$changed++;
}
}
// make JSON from stdClass
if (!is_string($this->module_settings)) {
$this->module_settings = json_encode($this->module_settings);
}
$this->save();
return [$revisionId, $changed];
}
/**
* Upload and set some image as title photo
*
* @param Request $request
*/
public function uploadTitlePhoto(Request $request)
{
if ($request->hasFile('titlePhoto')) {
return $this->file->upload($request->file('titlePhoto'), $request);
}
return null;
}
/**
* Upload and assign files to newly created content page
*
* @param Request $request
* @param null $revisionId
*
* @return int
*/
public function uploadRelatedFiles(Request $request, $revisionId = null)
{
$files = $request->all()['files'];
$changed = 0;
// check if any file was removed
if ($revisionId) {
$changed += $this->removeRelatedFiles($revisionId, $request);
}
// allow uploading only on created pages
if (!$this->id || !$files[0]) {
return $changed;
}
// find or create new directory for files assigned to content
$directory = Directory::firstOrCreate([
'name' => '.content_files',
'hidden' => true
]);
$request->directoryId = $directory->id;
foreach ($files as $file) {
if (!$file) {
continue;
}
$uploaded = $this->file->upload($file, $request);
// add assignement
ContentHasFile::create([
'file_id' => $uploaded['id'],
'content_id' => $this->id
]);
if ($revisionId) {
$this->saveHistory($revisionId, 'file', null, ['file' => $uploaded['file']->getClientOriginalName()]);
}
$changed++;
}
return $changed;
}
/**
* Delete files from association
*
* @param $revisionId
* @param $request
*
* @return int
*/
private function removeRelatedFiles($revisionId, $request)
{
$files = array_keys((array)$request->keepAssigned);
$allFiles = $this->files()
->whereNotIn('id', $files)
->get();
foreach ($allFiles as $file) {
$this->saveHistory($revisionId, 'file', $file->original_filename, ['file' => null]);
ContentHasFile::where('content_id', $this->id)
->where('file_id', $file->id)
->delete();
}
return count($allFiles);
}
/**
* Save history entry into database
*
* @param $revisionId
* @param $fieldRequest
* @param $fieldValue
* @param $request
*/
private function saveHistory($revisionId, $fieldRequest, $fieldValue, $request)
{ // setup history object
$history = [
'user_id' => \Auth::user()->id,
'content_id' => $this->id,
'edit_batch' => $revisionId,
'column' => $fieldRequest,
'old_value' => $fieldValue,
'new_value' => $request[$fieldRequest]
];
// save history
ContentHistory::create($history);
}
public function view()
{
$this->timestamps = false;
$this->viewed++;
$this->save();
$this->timestamps = true;
}
}
@@ -0,0 +1,83 @@
<?php
namespace App\Models\Content\ModelTraits;
use App\CMS\Handlers\Contracts\AppHandler;
use App\CMS\Handlers\Contracts\HistoryHandler;
use App\CMS\Helpers\Differ;
use App\Models\Content\Content;
trait ContentHistory
{
/**
* Returns history grouped by revisions (edit_batches)
*
* @param Content $content
*
* @return array
*/
public function getStructuredHistoryOf(Content $content)
{
$data = $content->history()
->select('content_history.*', 'contents.module_handler', 'users.email AS user_email', 'users.name AS user_name')
->join('users', 'users.id', '=', 'content_history.user_id')
->join('contents', 'contents.id', '=', 'content_history.content_id')
->orderBy('changed_at', 'DESC')
->get();
$info = [];
$history = [];
foreach ($data as $change) {
$info[$change->edit_batch] = [
'changed_at' => $change->changed_at,
'user' => [
'id' => $change->user_id,
'name' => $change->user_name,
'email' => $change->user_email
]
];
$change = $this->handleChangeset($change);
$history[$change->edit_batch][] = $change;
}
return [
'changesets' => $history,
'info' => $info
];
}
/**
* Getter for history handler of given module
*
* @param $change
*
* @return HistoryHandler
*/
protected function getHistoryHandler($change)
{
/**
* @var $handler AppHandler
*/
// get handler of given content page
$handler = $change->module_handler;
return $handler::getHistoryHandler();
}
public function handleChangeset($change)
{
$historyHandler = $this->getHistoryHandler($change);
$change->column_trans = $historyHandler::parseColumnName($change->column);
$change->diff = $historyHandler::parseChangedValue($change);
return $change;
}
public function getDiff($change)
{
return Differ::htmlDiff(e($change->old_value), e($change->new_value));
}
}
-1
View File
@@ -17,7 +17,6 @@ class Directory extends Model
'user_id',
'hidden'
];
protected $guarded = [];
/**
* Relationship with parent/sub directories
+11 -127
View File
@@ -2,23 +2,15 @@
namespace App\Models\FileManager;
use App\Events\UploadFile;
use App\Http\Requests\Request;
use Franzose\ClosureTable\Extensions\Str;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\File as FileFacade;
use Intervention\Image\Constraint;
use Intervention\Image\Facades\Image;
use Symfony\Component\HttpFoundation\File\File as SymphonyFile;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Illuminate\Queue\SerializesModels;
/**
* Class File
*/
class File extends Model
{
use SerializesModels, ModelTraits\File;
/**
* Hash algorithm used for hashing file
*/
@@ -32,125 +24,17 @@ class File extends Model
'original_filename',
'description',
'keywords',
'directory'
'directory_id',
'user_id',
'file_size',
'mime_type',
'hash_algorith',
'name',
'file_size'
];
protected $guarded = [];
/**
* Getter for filename of uploaded file.
*
* This filename is used for localy stored file.
*
* @param UploadedFile $file
*
* @return string
*/
public function getFilename(UploadedFile $file)
public function directory()
{
return uniqid() . '.' . $file->getClientOriginalExtension();
}
/**
* Saves file into FS and creates new record in database
*
* @param UploadedFile $file
* @param Request $request
*
* @return array associative array - key 'file' is type of SymphonyFile and contains info about real file, key 'id'
* is ID of row in files table
*/
public function upload(UploadedFile $file, Request $request)
{
$destination = config('app.upload_path');
$filename = $this->getFilename($file);
// save file into physical location (local FS)
$uploadedFile = $file->move($destination, $filename);
// is uploaded file an image?
if (Str::startsWith($uploadedFile->getMimeType(), 'image')) {
// yes, create thumbnail
$this->createThumbnail($uploadedFile);
}
// insert record into database
$id = $this->saveIntoDatabase($uploadedFile, $request, $file->getClientOriginalName());
// fire event about new file
Event::fire(new UploadFile($uploadedFile, $id));
return [
'filename' => $filename,
'file' => $file,
'id' => $id
];
}
/**
* Save file into database
*
* This file saves new record into database. It also calculates hash of given file, obtains MIME type etc.
*
* @param SymphonyFile $file
* @param Request $request
* @param $originalFilename
*
* @return mixed
*/
protected function saveIntoDatabase(SymphonyFile $file, Request $request, $originalFilename)
{
// get path and hash of uploaded file
$path = config('app.upload_path') . DIRECTORY_SEPARATOR . $file->getFilename();
$hash = hash_file(self::HASH_ALGORITHM, $path);
// define mandatory fields
$newFile = new File();
$newFile->user_id = Auth::user()->id;
$newFile->filename = $file->getFilename();
$newFile->original_filename = $originalFilename;
$newFile->file_hash = $hash;
$newFile->hash_algorithm = self::HASH_ALGORITHM;
$newFile->mime_type = $file->getMimeType();
$newFile->file_size = $file->getSize();
// save optional fields
$optionalFields = ['name', 'description', 'keywords', 'directoryId'];
foreach ($optionalFields as $field) {
if (isset($request->{$field}) && !empty($request->{$field})) {
$snakeField = snake_case($field);
$newFile->$snakeField = $request->$field;
}
}
// create new database row
$newFile->save();
return $newFile->id;
}
/**
* Create thumbnail of given image
*
* @param SymphonyFile $file
*/
private function createThumbnail(SymphonyFile $file)
{
// does thumbnail directory exist?
if (!is_dir(config('app.thumbnail_path'))) {
FileFacade::makeDirectory(config('app.thumbnail_path'));
}
// get image instance
$image = Image::make(config('app.upload_path') . DIRECTORY_SEPARATOR . $file->getFilename());
// resize image and keep aspect ration
$image->resize(env('THUMBNAIL_WIDTH', 300), null, function (Constraint $constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
// save thumbnail with lower quality
$image->save(config('app.thumbnail_path') . DIRECTORY_SEPARATOR . $file->getFilename(), 75);
return $this->belongsTo(Directory::class, 'directory_id');
}
}
+128
View File
@@ -0,0 +1,128 @@
<?php
namespace App\Models\FileManager\ModelTraits;
use App\Events\UploadFile;
use App\Http\Requests\Request;
use App\Models\FileManager\File as FileModel;
use Franzose\ClosureTable\Extensions\Str;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\File as FileFacade;
use Intervention\Image\Constraint;
use Intervention\Image\Facades\Image;
use Symfony\Component\HttpFoundation\File\File as SymphonyFile;
use Symfony\Component\HttpFoundation\File\UploadedFile;
trait File
{
/**
* Getter for filename of uploaded file.
*
* This filename is used for localy stored file.
*
* @param UploadedFile $file
*
* @return string
*/
public function getFilename(UploadedFile $file)
{
return uniqid() . '.' . $file->getClientOriginalExtension();
}
/**
* Saves file into FS and creates new record in database
*
* @param UploadedFile $file
* @param Request $request
*
* @return array associative array - key 'file' is type of SymphonyFile and contains info about real file, key 'id'
* is ID of row in files table
*/
public function upload(UploadedFile $file, Request $request)
{
$destination = config('app.upload_path');
$filename = $this->getFilename($file);
// save file into physical location (local FS)
$uploadedFile = $file->move($destination, $filename);
// is uploaded file an image?
if (Str::startsWith($uploadedFile->getMimeType(), 'image')) {
// yes, create thumbnail
$this->createThumbnail($uploadedFile);
}
// insert record into database
$id = $this->saveIntoDatabase($uploadedFile, $request, $file->getClientOriginalName());
// fire event about new file
Event::fire(new UploadFile($uploadedFile, $id));
return [
'filename' => $filename,
'file' => $file,
'id' => $id
];
}
/**
* Save file into database
*
* This file saves new record into database. It also calculates hash of given file, obtains MIME type etc.
*
* @param SymphonyFile $file
* @param Request $request
* @param $originalFilename
*
* @return mixed
*/
protected function saveIntoDatabase(SymphonyFile $file, Request $request, $originalFilename)
{
// get path and hash of uploaded file
$path = config('app.upload_path') . DIRECTORY_SEPARATOR . $file->getFilename();
$hash = hash_file(FileModel::HASH_ALGORITHM, $path);
// define mandatory fields
$newFile = FileModel::create([
'user_id' => Auth::user()->id,
'filename' => $file->getFilename(),
'original_filename' => $originalFilename,
'file_hash' => $hash,
'hash_algorithm' => FileModel::HASH_ALGORITHM,
'mime_type' => $file->getMimeType(),
'file_size' => $file->getSize(),
'name' => $request->name,
'description' => $request->description,
'directory_id' => $request->directoryId,
'keywords' => $request->keywords
]);
return $newFile->id;
}
/**
* Create thumbnail of given image
*
* @param SymphonyFile $file
*/
private function createThumbnail(SymphonyFile $file)
{
// does thumbnail directory exist?
if (!is_dir(config('app.thumbnail_path'))) {
FileFacade::makeDirectory(config('app.thumbnail_path'));
}
// get image instance
$image = Image::make(config('app.upload_path') . DIRECTORY_SEPARATOR . $file->getFilename());
// resize image and keep aspect ration
$image->resize(env('THUMBNAIL_WIDTH', 300), null, function (Constraint $constraint) {
$constraint->aspectRatio();
$constraint->upsize();
});
// save thumbnail with lower quality
$image->save(config('app.thumbnail_path') . DIRECTORY_SEPARATOR . $file->getFilename(), 75);
}
}
-1
View File
@@ -14,5 +14,4 @@ class Language extends Model
'script',
'native'
];
protected $guarded = [];
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace App\Models\ModelTraits;
use App\CMS\Handlers\Contracts\TemplateHandler;
use App\Models\Templates\TemplatePath;
use Illuminate\Support\Facades\App;
trait Module {
/**
* Create new module in database
*
* @param $name
* @param $alias
* @param string $description
* @param null $handler
* @param $parentModuleId
* @param array $settings
*
* @return Module
* @throws \ErrorException
*/
public static function install($name, $alias, $description, $handler, $parentModuleId, $settings)
{
// check if user does not want to install module in runtime
if (!App::runningInConsole()) {
throw new \ErrorException('Modules can be installed only via Artisan!');
}
$managesContent = $handler ? in_array('App\CMS\Handlers\Contracts\AppHandler', class_implements($handler)) : false;
$isTemplate = $handler ? in_array(TemplateHandler::class, class_implements($handler)) : false;
$supportCategories = $managesContent ? call_user_func([$handler, 'supportCategories']) : false;
if ($parentModuleId) {
$parentModule = Module::findOrFail($parentModuleId);
$parentModule->has_submodules = true;
$parentModule->save();
}
// insert module into database
$module = Module::create([
'name' => $name,
'description' => $description,
'handler' => $handler,
'manages_content' => $managesContent,
'support_categories' => $supportCategories,
'alias' => $alias,
'module_settings' => json_encode($settings),
'parent_id' => $parentModuleId ? $parentModuleId : null,
'enabled' => 1,
'is_template' => $isTemplate
]);
// install something module-specific
if ($handler) {
call_user_func([$handler, 'install']);
}
return $module;
}
/**
* Delete module from database and uninstall its dependencies
*
* @throws \ErrorException
*/
public function uninstall()
{
// check if user does not want to install module in runtime
if (!App::runningInConsole()) {
throw new \ErrorException('Modules can be uninstalled only via Artisan!');
}
// delete everything in /resources/views/visitor
if ($this->is_template) {
$this->deleteDirectory(resource_path('views/visitor'));
$this->deleteDirectory(resource_path('views/modules'));
$this->deleteDirectory(public_path('assets'));
// delete all template paths
TemplatePath::getQuery()->delete();
}
if ($this->handler && is_callable([$this->handler, 'uninstall'])) {
call_user_func([$this->handler, 'uninstall'], $this);
}
$this->delete();
}
/**
* Delete directory when uninstalling module.
*
* @param $directory
*
* @throws \ErrorException
*/
private function deleteDirectory($directory)
{
if (!is_dir($directory))
return;
$success = \File::deleteDirectory($directory, true);
if (!$success) {
throw new \ErrorException('Cannot delete ' . $directory);
}
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\Models\ModelTraits;
use App\Models\Settings as SettingsModel;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
trait Settings
{
/**
* Prepares common query for selecting settings
*
* @return mixed
*/
protected function getSettingsQuery()
{
return SettingsModel::select(['key', 'value', 'language'])
->where('language', LaravelLocalization::getCurrentLocale())
->orWhere('language', null)
->orderBy('language', 'ASC');
}
/**
* Load all settings
*
* @return \stdClass
*/
public function getAll()
{
$settings = $this->getSettingsQuery()
->get();
return $this->returnPairs($settings);
}
/**
* Get all settings with language info
*
* @return \stdClass
*/
public function getAllWithLanguage()
{
$settings = $this->getSettingsQuery()
->get();
$data = new \stdClass();
foreach ($settings as $conf) {
$data->{$conf->key} = [
'val' => $conf->value,
'lang' => $conf->language
];
}
return $data;
}
/**
* Load only config for views
*
* @return \stdClass
*/
public function getForView()
{
$settings = $this->getSettingsQuery()
->where('load_into_views', 1)
->get();
return $this->returnPairs($settings);
}
/**
* From pair <key, value> create object key => value
*
* @param $settings
*
* @return \stdClass
*/
private function returnPairs($settings)
{
$data = new \stdClass();
foreach ($settings as $conf) {
$data->{$conf->key} = $conf->value;
}
return $data;
}
}
+1 -103
View File
@@ -2,15 +2,14 @@
namespace App\Models;
use App\CMS\Handlers\Contracts\TemplateHandler;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
/**
* Class Module
*/
class Module extends Model
{
use ModelTraits\Module;
public $timestamps = false;
protected $fillable = [
'handler',
@@ -24,105 +23,4 @@ class Module extends Model
'manages_content',
'is_template'
];
protected $guarded = [];
/**
* Create new module in database
*
* @param $name
* @param $alias
* @param string $description
* @param null $handler
* @param $parentModuleId
* @param array $settings
*
* @return Module
* @throws \ErrorException
*/
public static function install($name, $alias, $description, $handler, $parentModuleId, $settings)
{
// check if user does not want to install module in runtime
if (!App::runningInConsole()) {
throw new \ErrorException('Modules can be installed only via Artisan!');
}
$managesContent = $handler ? in_array('App\CMS\Handlers\Contracts\AppHandler', class_implements($handler)) : false;
$isTemplate = $handler ? in_array(TemplateHandler::class, class_implements($handler)) : false;
$supportCategories = $managesContent ? call_user_func([$handler, 'supportCategories']) : false;
if ($parentModuleId) {
$parentModule = Module::findOrFail($parentModuleId);
$parentModule->has_submodules = true;
$parentModule->save();
}
// insert module into database
$module = Module::create([
'name' => $name,
'description' => $description,
'handler' => $handler,
'manages_content' => $managesContent,
'support_categories' => $supportCategories,
'alias' => $alias,
'module_settings' => json_encode($settings),
'parent_id' => $parentModuleId ? $parentModuleId : null,
'enabled' => 1,
'is_template' => $isTemplate
]);
// install something module-specific
if ($handler) {
call_user_func([$handler, 'install']);
}
return $module;
}
/**
* Delete module from database and uninstall its dependencies
*
* @throws \ErrorException
*/
public function uninstall()
{
// check if user does not want to install module in runtime
if (!App::runningInConsole()) {
throw new \ErrorException('Modules can be uninstalled only via Artisan!');
}
// delete everything in /resources/views/visitor
if ($this->is_template) {
$this->deleteDirectory(resource_path('views/visitor'));
$this->deleteDirectory(resource_path('views/modules'));
$this->deleteDirectory(public_path('assets'));
// delete all template paths
TemplatePath::getQuery()->delete();
}
if ($this->handler && is_callable([$this->handler, 'uninstall'])) {
call_user_func([$this->handler, 'uninstall'], $this);
}
$this->delete();
}
/**
* Delete directory when uninstalling module.
*
* @param $directory
*
* @throws \ErrorException
*/
private function deleteDirectory($directory)
{
if (!is_dir($directory))
return;
$success = \File::deleteDirectory($directory, true);
if (!$success) {
throw new \ErrorException('Cannot delete ' . $directory);
}
}
}
+2 -78
View File
@@ -7,85 +7,9 @@ use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
class Settings extends Model
{
use ModelTraits\Settings;
protected $table = 'settings';
public $timestamps = false;
protected $fillable = ['key', 'value', 'language'];
/**
* Prepares common query for selecting settings
*
* @return mixed
*/
protected function getSettingsQuery()
{
return Settings::select(['key', 'value', 'language'])
->where('language', LaravelLocalization::getCurrentLocale())
->orWhere('language', null)
->orderBy('language', 'ASC');
}
/**
* Load all settings
*
* @return \stdClass
*/
public function getAll()
{
$settings = $this->getSettingsQuery()
->get();
return $this->returnPairs($settings);
}
/**
* Get all settings with language info
*
* @return \stdClass
*/
public function getAllWithLanguage()
{
$settings = $this->getSettingsQuery()
->get();
$data = new \stdClass();
foreach ($settings as $conf) {
$data->{$conf->key} = [
'val' => $conf->value,
'lang' => $conf->language
];
}
return $data;
}
/**
* Load only config for views
*
* @return \stdClass
*/
public function getForView()
{
$settings = $this->getSettingsQuery()
->where('load_into_views', 1)
->get();
return $this->returnPairs($settings);
}
/**
* From pair <key, value> create object key => value
*
* @param $settings
*
* @return \stdClass
*/
private function returnPairs($settings)
{
$data = new \stdClass();
foreach ($settings as $conf) {
$data->{$conf->key} = $conf->value;
}
return $data;
}
}
@@ -0,0 +1,78 @@
<?php
namespace App\Models\Templates\ModelTraits;
use App\Models\Templates\TemplatePath;
use App\Models\Templates\Template as TemplateModel;
trait Template {
protected $pathModel = null;
/**
* In one path may be many templates
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function getPath()
{
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 . TemplateModel::TEMPLATE_SUFFIX;
// compose two versions of path - original and with "custom" directory
$original = $absolutePath . $_ . $file;
$edited = $absolutePath . $_ . TemplateModel::EDITED_TEMPLATES_DIRECTORY . $_ . $file;
// decide which path will be returned
switch ($mode) {
case TemplateModel::CUSTOM_EDIT_DIRECTORY:
return $edited;
break;
case TemplateModel::ORIGINAL_DIRECTORY:
return $original;
break;
default:
if (file_exists($absolutePath . $_ . TemplateModel::EDITED_TEMPLATES_DIRECTORY . $_ . $file)) {
return $edited;
}
return $original;
break;
}
}
}
+2 -76
View File
@@ -1,14 +1,12 @@
<?php
namespace App\Models;
namespace App\Models\Templates;
use Illuminate\Database\Eloquent\Model;
/**
* Class CustomFieldType
*/
class Template extends Model
{
use ModelTraits\Template;
public $timestamps = true;
protected $fillable = [
'template_name',
@@ -18,82 +16,10 @@ class Template extends Model
'description',
'editable'
];
protected $guarded = [];
protected $pathModel = null;
const EDITED_TEMPLATES_DIRECTORY = 'custom';
const ASSETS_DIRECTORY = 'assets';
const MODULES_DIRECTORY = 'modules';
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 getPath()
{
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;
}
}
}
+9 -5
View File
@@ -1,6 +1,6 @@
<?php
namespace App\Models;
namespace App\Models\Templates;
use Illuminate\Database\Eloquent\Model;
@@ -10,8 +10,12 @@ use Illuminate\Database\Eloquent\Model;
class TemplatePath extends Model
{
public $timestamps = true;
protected $fillable = ['name', 'path', 'parent_id', 'namespace'];
protected $guarded = [];
protected $fillable = [
'name',
'path',
'parent_id',
'namespace'
];
/**
* In one path may be many templates
@@ -20,7 +24,7 @@ class TemplatePath extends Model
*/
public function templates()
{
return $this->hasMany('App\Models\Template', 'path');
return $this->hasMany(Template::class, 'path');
}
/**
@@ -30,6 +34,6 @@ class TemplatePath extends Model
*/
public function subdirectories()
{
return $this->hasMany('App\Models\TemplatePath', 'parent_id');
return $this->hasMany(TemplatePath::class, 'parent_id');
}
}
-15
View File
@@ -22,7 +22,6 @@ class User extends Model implements AuthenticableInterface
'password',
'name',
];
protected $guarded = [];
/**
* The attributes excluded from the model's JSON form.
*
@@ -38,18 +37,4 @@ class User extends Model implements AuthenticableInterface
const ROLE_VISITOR = 'visitor';
const ROLE_EDITOR = 'editor';
const ROLE_ADMIN = 'admin';
/**
* Getter for Gravatar URL
*
* This method returns full path (url) to user's Gravatar. It accepts optional parameter for setting size.
*
* @param int $size size of rendered gravatar
*
* @return string
*/
public function gravatar($size = 50)
{
return Gravatar::img($this->email, $size);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Models\Widgets\ModelTraits;
use App\Events\CreateNewWidget;
use App\Http\Requests\Request;
use App\Models\Widgets\Widget as WidgetModel;
use App\Models\Widgets\WidgetType;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
trait Widget
{
/**
* Create new widget of given type
*
* @param WidgetType $widgetType
* @param Request $request
*
* @return WidgetModel
*/
public function createNew(WidgetType $widgetType, Request $request)
{
$settings = call_user_func([$widgetType->handler, 'save'], $request);
// create new widget
$widget = WidgetModel::create([
'widget_type_handler' => $widgetType->handler,
'language' => LaravelLocalization::getCurrentLocale(),
'user_id' => \Auth::user()->id,
'name' => $request->name,
'container_class' => $request->containerClass,
'container_id' => empty($request->containerId) ? null : $request->containerId,
'description' => $request->description,
'settings' => json_encode($settings),
]);
// fire event with newly created widget
\Event::fire(new CreateNewWidget($widget));
return $widget;
}
}
@@ -0,0 +1,24 @@
<?php
namespace App\Models\Widgets\ModelTraits;
use App\Models\Widgets\Widget;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
trait WidgetArea
{
public function getWidgets($uniqueCode = false)
{
$widgets = Widget::select('widgets.*', 'widgets.widget_type_handler', 'widget_areas.code as widget_area_code', 'widgets.name as widget_name', 'widgets.settings')
->join('widget_in_widget_area', 'widget_in_widget_area.widget_id', '=', 'widgets.id')
->join('widget_areas', 'widget_in_widget_area.widget_area_id', '=', 'widget_areas.id')
->where('language', LaravelLocalization::getCurrentLocale())
->orderBy('widget_in_widget_area.position');
if (!$uniqueCode) {
$widgets = $widgets->where('widget_areas.viewed', '>=', setting('preloadFrequentedWidgetAreasTreshold', 10));
} else {
$widgets = $widgets->where('code', $uniqueCode);
}
return $widgets;
}
}
@@ -0,0 +1,20 @@
<?php
namespace App\Models\Widgets\ModelTraits;
use App\Models\Widgets\WidgetInWidgetArea as WidgetInWidgetAreaModel;
trait WidgetInWidgetArea
{
public static function getWidgetIdsFromArea($widgetAreaId)
{
$models = WidgetInWidgetAreaModel::where('widget_area_id', $widgetAreaId)
->get();
$ids = [];
foreach ($models as $model) {
$ids[] = $model->widget_id;
}
return $ids;
}
}
-41
View File
@@ -22,45 +22,4 @@ class Widget extends Model
'viewed',
'deletable'
];
protected $guarded = [];
/**
* Create new widget of given type
*
* @param WidgetType $widgetType
* @param $data
*
* @return Widget
*/
public function createNew(WidgetType $widgetType, $data)
{
// convert array or whatever into object
if (!is_object($data)) {
$data = (object)$data;
}
// create new widget
$widget = new Widget();
$widget->widget_type_handler = $widgetType->handler;
$widget->language = LaravelLocalization::getCurrentLocale();
$widget->user_id = \Auth::user()->id;
$widget->name = $data->name;
$widget->container_class = $data->containerClass;
$widget->container_id = empty($data->containerId) ? null : $data->containerId;
// save description if available
if (isset($data->description)) {
$widget->description = $data->description;
}
$widget = call_user_func([$widgetType->handler, 'save'], $widget, $data);
// save into database
$widget->save();
// fire event with newly created widget
Event::fire(new \App\Events\CreateNewWidget($widget));
return $widget;
}
}
+2 -18
View File
@@ -3,13 +3,14 @@
namespace App\Models\Widgets;
use Illuminate\Database\Eloquent\Model;
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
/**
* Class WidgetArea
*/
class WidgetArea extends Model
{
use ModelTraits\WidgetArea;
public $timestamps = false;
protected $fillable = [
'name',
@@ -17,21 +18,4 @@ class WidgetArea extends Model
'description',
'deletable'
];
protected $guarded = [];
public function getWidgets($uniqueCode = false)
{
$widgets = Widget::select('widgets.*', 'widgets.widget_type_handler', 'widget_areas.code as widget_area_code', 'widgets.name as widget_name', 'widgets.settings')
->join('widget_in_widget_area', 'widget_in_widget_area.widget_id', '=', 'widgets.id')
->join('widget_areas', 'widget_in_widget_area.widget_area_id', '=', 'widget_areas.id')
->where('language', LaravelLocalization::getCurrentLocale())
->orderBy('widget_in_widget_area.position');
if (!$uniqueCode) {
$widgets = $widgets->where('widget_areas.viewed', '>=', setting('preloadFrequentedWidgetAreasTreshold', 10));
} else {
$widgets = $widgets->where('code', $uniqueCode);
}
return $widgets;
}
}
+2 -14
View File
@@ -9,6 +9,8 @@ use Illuminate\Database\Eloquent\Model;
*/
class WidgetInWidgetArea extends Model
{
use ModelTraits\WidgetInWidgetArea;
protected $table = 'widget_in_widget_area';
public $timestamps = false;
protected $fillable = [
@@ -16,18 +18,4 @@ class WidgetInWidgetArea extends Model
'widget_area_id',
'position'
];
protected $guarded = [];
public static function getWidgetIdsFromArea($widgetAreaId)
{
$models = WidgetInWidgetArea::where('widget_area_id', $widgetAreaId)
->get();
$ids = [];
foreach ($models as $model) {
$ids[] = $model->widget_id;
}
return $ids;
}
}
-5
View File
@@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\Model;
*/
class WidgetType extends Model
{
public $timestamps = false;
protected $fillable = [
@@ -19,8 +18,4 @@ class WidgetType extends Model
'module_id',
'description'
];
protected $guarded = [];
}
+1 -1
View File
@@ -11,7 +11,7 @@ use App\CMS\Handlers\Search\Content;
use App\CMS\Handlers\Search\Files;
use App\CMS\Handlers\Search\Presenters\AdminContent as AdminContentPresenter;
use App\CMS\Handlers\Search\Presenters\AdminFiles;
use App\Models\Template;
use App\Models\Templates\Template;
use App\CMS\Widgets\HtmlCode\HtmlCode;
use App\CMS\Widgets\Menu\Menu;
use App\CMS\Widgets\OrbitSlider\OrbitSlider;
+1 -1
View File
@@ -4,7 +4,7 @@ namespace App\Providers;
use App\CMS\Handlers\Registrators\TemplateRegistrator;
use App\CMS\Helpers\CurrentTemplate;
use App\Models\Template;
use App\Models\Templates\Template;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;