From 7dcfdba4de2a0e230399ff35e7284bb204c91a92 Mon Sep 17 00:00:00 2001 From: DariusIII Date: Thu, 27 Nov 2025 11:27:48 +0100 Subject: [PATCH] Use ContentController exclusively for content --- Blacklight/Contents.php | 265 ------------------ .../Admin/AdminContentController.php | 129 +++++++-- app/Http/Controllers/ContentController.php | 89 ++++-- app/Models/Content.php | 114 ++++++++ app/Models/User.php | 4 + resources/views/content/index.blade.php | 4 +- 6 files changed, 299 insertions(+), 306 deletions(-) delete mode 100755 Blacklight/Contents.php diff --git a/Blacklight/Contents.php b/Blacklight/Contents.php deleted file mode 100755 index 355df5328..000000000 --- a/Blacklight/Contents.php +++ /dev/null @@ -1,265 +0,0 @@ -data_get(); - if ($rows === null) { - return false; - } - - foreach ($rows as $row) { - $arr[] = $row; - } - - return $arr; - } - - /** - * @return array|false - */ - public function getAll(): bool|array - { - $arr = []; - $rows = $this->data_getAll(); - if ($rows === null) { - return false; - } - - foreach ($rows as $row) { - $arr[] = $row; - } - - return $arr; - } - - /** - * Convert get all but from to object. - * - * @return array|false - */ - public function getAllButFront(): bool|array - { - $arr = []; - $rows = $this->data_getAllButFront(); - if ($rows === null) { - return false; - } - - foreach ($rows as $row) { - $arr[] = $row; - } - - return $arr; - } - - /** - * @return array|false - */ - public function getFrontPage(): bool|array - { - $arr = []; - $rows = $this->data_getFrontPage(); - if ($rows === null) { - return false; - } - - foreach ($rows as $row) { - $arr[] = $row; - } - - return $arr; - } - - public function getIndex() - { - $row = $this->data_getIndex(); - - return $row ?? false; - } - - /** - * @return false|mixed - */ - public function getByID($id, $role): mixed - { - $row = $this->data_getByID($id, $role); - - if ($row === null) { - return false; - } - - return Arr::first($row); - } - - public function validate($content): mixed - { - if ($content['url'] !== '/') { - $content['url'] = '/'.$content['url']; - } - - if (! str_ends_with($content['url'], '/')) { - $content['url'] .= '/'; - } - - return $content; - } - - public function add($form): int - { - if ($form['ordinal'] === 1) { - Content::query()->where('ordinal', '>', 0)->increment('ordinal'); - } - - return $this->data_add($form); - } - - public function delete($id): mixed - { - return Content::query()->where('id', $id)->delete(); - } - - /** - * @return mixed|Content - */ - public function update($form): mixed - { - $this->data_update($form); - - return $form; - } - - public function data_update($content): int - { - return Content::query() - ->where('id', $content['id']) - ->update( - [ - 'role' => $content['role'], - 'title' => $content['title'], - 'url' => $content['url'], - 'body' => $content['body'], - 'metadescription' => $content['metadescription'], - 'metakeywords' => $content['metakeywords'], - 'contenttype' => $content['contenttype'], - 'status' => $content['status'], - 'ordinal' => $content['ordinal'], - 'updated_at' => now(), - ] - ); - } - - public function data_add($content): int - { - return Content::query() - ->insertGetId( - [ - 'role' => $content['role'], - 'title' => $content['title'], - 'url' => $content['url'], - 'body' => $content['body'], - 'metadescription' => $content['metadescription'], - 'metakeywords' => $content['metakeywords'], - 'contenttype' => $content['contenttype'], - 'status' => $content['status'], - 'ordinal' => $content['ordinal'], - 'created_at' => now(), - 'updated_at' => now(), - ] - ); - } - - /** - * @return Content[]|\Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Query\Builder[]|\Illuminate\Support\Collection - */ - public function data_get(): array|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection - { - return Content::query() - ->where('status', '=', 1) - ->orderByRaw('contenttype, COALESCE(ordinal, 1000000)') - ->get(); - } - - /** - * @return \Illuminate\Database\Eloquent\Collection|static[] - */ - public function data_getAll(): \Illuminate\Database\Eloquent\Collection|static - { - return Content::query()->select()->orderByRaw('contenttype, COALESCE(ordinal, 1000000)')->get(); - } - - /** - * @return \Illuminate\Database\Eloquent\Collection|static[] - */ - public function data_getAllButFront(): \Illuminate\Database\Eloquent\Collection|static - { - return Content::query() - ->where('id', '<>', 1) - ->orderByRaw('contenttype, COALESCE(ordinal, 1000000)') - ->get(); - } - - /** - * @return \Illuminate\Database\Eloquent\Collection|static[] - */ - public function data_getByID($id, $role): \Illuminate\Database\Eloquent\Collection|static - { - $query = Content::query()->where('id', $id); - if ($role !== User::ROLE_ADMIN) { - $query->where('role', $role)->orWhere('role', '=', 0); - } - - return $query->get(); - } - - /** - * @return \Illuminate\Database\Eloquent\Collection|static[] - */ - public function data_getFrontPage(): \Illuminate\Database\Eloquent\Collection|static - { - return Content::query() - ->where( - [ - 'status' => 1, - 'contenttype' => self::TYPEINDEX, - ] - ) - ->orderByRaw('ordinal ASC, COALESCE(ordinal, 1000000), id') - ->get(); - } - - public function data_getIndex(): ?Content - { - return Content::query()->where( - [ - 'status' => 1, - 'contenttype' => self::TYPEINDEX, - ] - )->first(); - } -} diff --git a/app/Http/Controllers/Admin/AdminContentController.php b/app/Http/Controllers/Admin/AdminContentController.php index 9f6902bbd..d2709961d 100644 --- a/app/Http/Controllers/Admin/AdminContentController.php +++ b/app/Http/Controllers/Admin/AdminContentController.php @@ -3,20 +3,25 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\BasePageController; +use App\Models\Content; use App\Models\User; -use Blacklight\Contents; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class AdminContentController extends BasePageController { /** + * Display list of all content. + * * @throws \Exception */ public function index() { $this->setAdminPrefs(); - $contentList = (new Contents)->getAll(); + + $contentList = Content::query() + ->orderByRaw('contenttype, COALESCE(ordinal, 1000000)') + ->get(); $this->viewData = array_merge($this->viewData, [ 'contentlist' => $contentList, @@ -28,14 +33,14 @@ class AdminContentController extends BasePageController } /** - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View + * Show form to create or edit content. * + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View * @throws \Exception */ public function create(Request $request) { $this->setAdminPrefs(); - $contents = new Contents; $meta_title = 'Content Add'; // Set the current action. @@ -58,17 +63,17 @@ class AdminContentController extends BasePageController switch ($action) { case 'add': $meta_title = 'Content Add'; - $content['status'] = '1'; - $content['contenttype'] = '2'; + $content['status'] = Content::STATUS_ENABLED; + $content['contenttype'] = Content::TYPE_ARTICLE; break; case 'submit': - // Validate and add or update. + // Validate and add or update content if ($request->missing('id') || empty($request->input('id'))) { - $returnid = $contents->add($request->all()); + $returnid = $this->addContent($request->all()); } else { - $content = $contents->update($request->all()); - $returnid = $content['id']; + $this->updateContent($request->all()); + $returnid = $request->input('id'); } return redirect('admin/content-add?id='.$returnid); @@ -78,17 +83,25 @@ class AdminContentController extends BasePageController if ($request->has('id')) { $meta_title = 'Content Edit'; $id = $request->input('id'); - - $content = $contents->getByID($id, User::ROLE_ADMIN); + $content = $this->getContentById($id); } break; } - $contenttypelist = [1 => 'Useful Link', 2 => 'Article', 3 => 'Homepage']; - $rolelist = [1 => 'Everyone', 2 => 'Logged in Users', 3 => 'Admins']; + $contenttypelist = [ + Content::TYPE_USEFUL => 'Useful Link', + Content::TYPE_ARTICLE => 'Article', + Content::TYPE_INDEX => 'Homepage' + ]; + + $rolelist = [ + Content::ROLE_EVERYONE => 'Everyone', + Content::ROLE_LOGGED_IN => 'Logged in Users', + Content::ROLE_ADMIN => 'Admins' + ]; $this->viewData = array_merge($this->viewData, [ - 'status_ids' => [1, 0], + 'status_ids' => [Content::STATUS_ENABLED, Content::STATUS_DISABLED], 'status_names' => ['Enabled', 'Disabled'], 'yesno_ids' => [1, 0], 'yesno_names' => ['Yes', 'No'], @@ -102,15 +115,97 @@ class AdminContentController extends BasePageController return view('admin.content.add', $this->viewData); } + /** + * Delete content by ID. + */ public function destroy(Request $request): \Illuminate\Routing\Redirector|RedirectResponse|\Illuminate\Contracts\Foundation\Application { if ($request->has('id')) { - $contents = new Contents; - $contents->delete($request->input('id')); + Content::query()->where('id', $request->input('id'))->delete(); } $referrer = $request->server('HTTP_REFERER'); return redirect()->to($referrer); } + + /** + * Add new content. + */ + protected function addContent(array $data): int + { + // Normalize URL + $data = $this->normalizeContentUrl($data); + + // If ordinal is 1, increment all existing ordinals + if (($data['ordinal'] ?? 0) === 1) { + Content::query()->where('ordinal', '>', 0)->increment('ordinal'); + } + + return Content::query()->insertGetId([ + 'role' => $data['role'] ?? Content::ROLE_EVERYONE, + 'title' => $data['title'] ?? '', + 'url' => $data['url'] ?? '/', + 'body' => $data['body'] ?? '', + 'metadescription' => $data['metadescription'] ?? '', + 'metakeywords' => $data['metakeywords'] ?? '', + 'contenttype' => $data['contenttype'] ?? Content::TYPE_ARTICLE, + 'status' => $data['status'] ?? Content::STATUS_ENABLED, + 'ordinal' => $data['ordinal'] ?? 0, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + /** + * Update existing content. + */ + protected function updateContent(array $data): int + { + // Normalize URL + $data = $this->normalizeContentUrl($data); + + return Content::query() + ->where('id', $data['id']) + ->update([ + 'role' => $data['role'] ?? Content::ROLE_EVERYONE, + 'title' => $data['title'] ?? '', + 'url' => $data['url'] ?? '/', + 'body' => $data['body'] ?? '', + 'metadescription' => $data['metadescription'] ?? '', + 'metakeywords' => $data['metakeywords'] ?? '', + 'contenttype' => $data['contenttype'] ?? Content::TYPE_ARTICLE, + 'status' => $data['status'] ?? Content::STATUS_ENABLED, + 'ordinal' => $data['ordinal'] ?? 0, + 'updated_at' => now(), + ]); + } + + /** + * Get content by ID for admin viewing. + */ + protected function getContentById(int $id): ?Content + { + return Content::query()->find($id); + } + + /** + * Normalize content URL to ensure proper formatting. + */ + protected function normalizeContentUrl(array $data): array + { + if (isset($data['url'])) { + // Ensure URL starts with / + if ($data['url'] !== '/' && !str_starts_with($data['url'], '/')) { + $data['url'] = '/'.$data['url']; + } + + // Ensure URL ends with / + if (!str_ends_with($data['url'], '/')) { + $data['url'] .= '/'; + } + } + + return $data; + } } diff --git a/app/Http/Controllers/ContentController.php b/app/Http/Controllers/ContentController.php index 1079dd9a6..55847514f 100644 --- a/app/Http/Controllers/ContentController.php +++ b/app/Http/Controllers/ContentController.php @@ -2,23 +2,23 @@ namespace App\Http\Controllers; -use Blacklight\Contents; +use App\Models\Content; +use App\Models\User; use Illuminate\Http\Request; class ContentController extends BasePageController { /** - * @return \Illuminate\Http\JsonResponse|\Illuminate\View\View + * Display content page(s). * + * @return \Illuminate\Http\JsonResponse|\Illuminate\View\View * @throws \Exception */ public function show(Request $request) { - $contents = new Contents; - $role = $this->userdata->role ?? 0; - /* The role column in the content table values are : + /* The role column in the content table values are: * 1 = logged in users * 2 = admins * @@ -30,37 +30,34 @@ class ContentController extends BasePageController * * Admins and mods should be the only ones to see admin content. */ - $isAdmin = ($role === 2 || $role === 4); + $isAdmin = \in_array($role, [User::ROLE_ADMIN, User::ROLE_MODERATOR], true); - $contentId = 0; - if ($request->has('id')) { - $contentId = $request->input('id'); - } - - $contentPage = false; - if ($request->has('page')) { - $contentPage = $request->input('page'); - } + $contentId = $request->input('id', 0); + $contentPage = $request->input('page', false); if ($contentId === 0 && $contentPage === 'content') { - $content = $contents->getAllButFront(); + // Show all content except front page + $content = $this->getAllButFront()->all(); $isFront = false; $meta_title = 'Contents page'; $meta_keywords = 'contents'; $meta_description = 'This is the contents page.'; } elseif ($contentId !== 0 && $contentPage !== false) { - $content = [$contents->getByID($contentId, $role)]; + // Show specific content by ID + $contentItem = $this->getContentById($contentId, $role); + $content = $contentItem ? [$contentItem] : []; $isFront = false; $meta_title = 'Contents page'; $meta_keywords = 'contents'; $meta_description = 'This is the contents page.'; } else { - $content = $contents->getFrontPage(); - $index = $contents->getIndex(); + // Show front page content + $content = $this->getFrontPageContent()->all(); + $index = $this->getIndexContent(); $isFront = true; - $meta_title = $index->title ?? 'Contents page'; - $meta_keywords = $index->metakeyword ?? 'contents'; - $meta_description = $index->metadescription ?? 'This is the contents page.'; + $meta_title = $index?->title ?? 'Contents page'; + $meta_keywords = $index?->metakeywords ?? 'contents'; + $meta_description = $index?->metadescription ?? 'This is the contents page.'; } if (empty($content)) { @@ -78,4 +75,52 @@ class ContentController extends BasePageController return view('content.index', $this->viewData); } + + /** + * Get all active content ordered by type and ordinal. + */ + protected function getActiveContent(): \Illuminate\Database\Eloquent\Collection + { + return Content::active() + ->orderByRaw('contenttype, COALESCE(ordinal, 1000000)') + ->get(); + } + + /** + * Get all content except the front page. + */ + protected function getAllButFront(): \Illuminate\Database\Eloquent\Collection + { + return Content::query() + ->where('id', '<>', 1) + ->orderByRaw('contenttype, COALESCE(ordinal, 1000000)') + ->get(); + } + + /** + * Get content by ID with role-based access control. + */ + protected function getContentById(int $id, int $role): ?Content + { + return Content::query() + ->where('id', $id) + ->forRole($role) + ->first(); + } + + /** + * Get front page content. + */ + protected function getFrontPageContent(): \Illuminate\Database\Eloquent\Collection + { + return Content::frontPage()->get(); + } + + /** + * Get index content metadata. + */ + protected function getIndexContent(): ?Content + { + return Content::active()->ofType(Content::TYPE_INDEX)->first(); + } } diff --git a/app/Models/Content.php b/app/Models/Content.php index 85f530d6a..f76ed83e3 100644 --- a/app/Models/Content.php +++ b/app/Models/Content.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; /** @@ -28,6 +29,10 @@ use Illuminate\Database\Eloquent\Model; * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content whereStatus($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content whereUrl($value) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content active() + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content ofType($type) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content forRole($role) + * @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Content frontPage() * * @mixin \Eloquent * @@ -37,6 +42,20 @@ use Illuminate\Database\Eloquent\Model; */ class Content extends Model { + // Content type constants + public const TYPE_USEFUL = 1; + public const TYPE_ARTICLE = 2; + public const TYPE_INDEX = 3; + + // Status constants + public const STATUS_ENABLED = 1; + public const STATUS_DISABLED = 0; + + // Role constants (aligned with User roles) + public const ROLE_EVERYONE = 0; + public const ROLE_LOGGED_IN = 1; + public const ROLE_ADMIN = 2; + /** * @var string */ @@ -56,4 +75,99 @@ class Content extends Model * @var array */ protected $guarded = []; + + /** + * @var array + */ + protected $casts = [ + 'contenttype' => 'integer', + 'status' => 'integer', + 'ordinal' => 'integer', + 'role' => 'integer', + ]; + + /** + * Scope: Get only active content. + */ + public function scopeActive(Builder $query): Builder + { + return $query->where('status', self::STATUS_ENABLED); + } + + /** + * Scope: Get content of a specific type. + */ + public function scopeOfType(Builder $query, int $type): Builder + { + return $query->where('contenttype', $type); + } + + /** + * Scope: Get content accessible by a specific role. + */ + public function scopeForRole(Builder $query, int $role): Builder + { + // Admins and moderators can see everything + if (\in_array($role, [User::ROLE_ADMIN, User::ROLE_MODERATOR], true)) { + return $query; + } + + // Others can only see content for their role or everyone + return $query->where(function ($q) use ($role) { + $q->where('role', self::ROLE_EVERYONE) + ->orWhere('role', $role); + }); + } + + /** + * Scope: Get front page content. + */ + public function scopeFrontPage(Builder $query): Builder + { + return $query->active() + ->ofType(self::TYPE_INDEX) + ->orderByRaw('ordinal ASC, COALESCE(ordinal, 1000000), id'); + } + + /** + * Get content type label. + */ + public function getContentTypeLabel(): string + { + return match ($this->contenttype) { + self::TYPE_USEFUL => 'Useful Link', + self::TYPE_ARTICLE => 'Article', + self::TYPE_INDEX => 'Homepage', + default => 'Unknown', + }; + } + + /** + * Get role label. + */ + public function getRoleLabel(): string + { + return match ($this->role) { + self::ROLE_EVERYONE => 'Everyone', + self::ROLE_LOGGED_IN => 'Logged in Users', + self::ROLE_ADMIN => 'Admins', + default => 'Unknown', + }; + } + + /** + * Check if content is active. + */ + public function isActive(): bool + { + return $this->status === self::STATUS_ENABLED; + } + + /** + * Check if content is a homepage type. + */ + public function isHomepage(): bool + { + return $this->contenttype === self::TYPE_INDEX; + } } diff --git a/app/Models/User.php b/app/Models/User.php index 4a78839f9..1e62ddadb 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -165,6 +165,10 @@ class User extends Authenticatable public const ROLE_ADMIN = 2; + public const ROLE_DISABLED = 3; + + public const ROLE_MODERATOR = 4; + /** * Users SELECT queue type. */ diff --git a/resources/views/content/index.blade.php b/resources/views/content/index.blade.php index 49ff92d76..c55862f54 100644 --- a/resources/views/content/index.blade.php +++ b/resources/views/content/index.blade.php @@ -5,7 +5,7 @@ @if($front)
- @if(is_array($content) && count($content) > 0) + @if(!empty($content) && count($content) > 0)
@foreach($content as $item)
@@ -43,7 +43,7 @@

Browse our content pages

- @if(is_array($content) && count($content) > 0) + @if(!empty($content) && count($content) > 0)
@foreach($content as $item) @if($item)