471 lines
13 KiB
PHP
471 lines
13 KiB
PHP
<?php
|
|
|
|
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;
|
|
|
|
/**
|
|
* Class Content
|
|
*/
|
|
class Content extends Model
|
|
{
|
|
use SoftDeletes;
|
|
public $timestamps = true;
|
|
protected $fillable = [
|
|
'user_id',
|
|
'parent_id',
|
|
'category_id',
|
|
'language',
|
|
'module_handler',
|
|
'published_from',
|
|
'published_to',
|
|
'title_photo',
|
|
'template',
|
|
'is_homepage',
|
|
'title',
|
|
'url',
|
|
'meta_keywords',
|
|
'meta_description',
|
|
'content',
|
|
'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?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isDraft()
|
|
{
|
|
return $this->status == self::STATUS_DRAFT;
|
|
}
|
|
|
|
/**
|
|
* Is current content visible for all visitors?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isPublic()
|
|
{
|
|
return $this->status == self::STATUS_PUBLIC;
|
|
}
|
|
|
|
/**
|
|
* Is current content visible only for logged users?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isProtected()
|
|
{
|
|
return $this->status == self::STATUS_PROTECTED;
|
|
}
|
|
|
|
/**
|
|
* Is current content visible for logged user OR for everyone?
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isVisible()
|
|
{
|
|
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
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
*/
|
|
public function history()
|
|
{
|
|
return $this->hasMany(\App\Models\Content\ContentHistory::class);
|
|
}
|
|
|
|
/**
|
|
* Mark relationship with users table
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
*/
|
|
public function author()
|
|
{
|
|
return $this->belongsTo(User::class, 'user_id');
|
|
}
|
|
|
|
/**
|
|
* Related files to content page
|
|
*
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
|
*/
|
|
public function files()
|
|
{
|
|
return $this->belongsToMany(File::class, 'contents_has_files');
|
|
}
|
|
|
|
public function category()
|
|
{
|
|
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;
|
|
}
|
|
}
|