256 lines
8.2 KiB
PHP
256 lines
8.2 KiB
PHP
|
|
<?php
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
namespace Modules\Pages;
|
||
|
|
|
||
|
|
use Core\Http\Response;
|
||
|
|
use Core\Services\Auth;
|
||
|
|
use Core\Services\Database;
|
||
|
|
use Core\Services\Shortcodes;
|
||
|
|
use Core\Views\View;
|
||
|
|
use PDO;
|
||
|
|
use Throwable;
|
||
|
|
|
||
|
|
class PagesController
|
||
|
|
{
|
||
|
|
private View $view;
|
||
|
|
|
||
|
|
public function __construct()
|
||
|
|
{
|
||
|
|
$this->view = new View(__DIR__ . '/views');
|
||
|
|
}
|
||
|
|
|
||
|
|
public function show(): Response
|
||
|
|
{
|
||
|
|
$slug = trim((string)($_GET['slug'] ?? ''));
|
||
|
|
if ($slug === '') {
|
||
|
|
return $this->notFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
$db = Database::get();
|
||
|
|
if (!$db instanceof PDO) {
|
||
|
|
return $this->notFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
$stmt = $db->prepare("SELECT title, content_html FROM ac_pages WHERE slug = :slug AND is_published = 1 LIMIT 1");
|
||
|
|
$stmt->execute([':slug' => $slug]);
|
||
|
|
$page = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
|
if (!$page) {
|
||
|
|
return $this->notFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
return new Response($this->view->render('site/show.php', [
|
||
|
|
'title' => (string)$page['title'],
|
||
|
|
'content_html' => Shortcodes::render((string)$page['content_html'], [
|
||
|
|
'page_slug' => $slug,
|
||
|
|
'page_title' => (string)$page['title'],
|
||
|
|
]),
|
||
|
|
]));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function adminIndex(): Response
|
||
|
|
{
|
||
|
|
if ($guard = $this->guard(['admin', 'manager', 'editor'])) {
|
||
|
|
return $guard;
|
||
|
|
}
|
||
|
|
|
||
|
|
$db = Database::get();
|
||
|
|
$pages = [];
|
||
|
|
if ($db instanceof PDO) {
|
||
|
|
try {
|
||
|
|
$stmt = $db->query("SELECT id, title, slug, is_published, is_home, is_blog_index, updated_at FROM ac_pages ORDER BY updated_at DESC");
|
||
|
|
$pages = $stmt ? $stmt->fetchAll(PDO::FETCH_ASSOC) : [];
|
||
|
|
} catch (Throwable $e) {
|
||
|
|
$pages = [];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return new Response($this->view->render('admin/index.php', [
|
||
|
|
'title' => 'Pages',
|
||
|
|
'pages' => $pages,
|
||
|
|
]));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function adminEdit(): Response
|
||
|
|
{
|
||
|
|
if ($guard = $this->guard(['admin', 'manager', 'editor'])) {
|
||
|
|
return $guard;
|
||
|
|
}
|
||
|
|
|
||
|
|
$id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
|
||
|
|
$page = [
|
||
|
|
'id' => 0,
|
||
|
|
'title' => '',
|
||
|
|
'slug' => '',
|
||
|
|
'content_html' => '',
|
||
|
|
'is_published' => 0,
|
||
|
|
'is_home' => 0,
|
||
|
|
'is_blog_index' => 0,
|
||
|
|
];
|
||
|
|
|
||
|
|
$db = Database::get();
|
||
|
|
if ($id > 0 && $db instanceof PDO) {
|
||
|
|
$stmt = $db->prepare("SELECT id, title, slug, content_html, is_published, is_home, is_blog_index FROM ac_pages WHERE id = :id LIMIT 1");
|
||
|
|
$stmt->execute([':id' => $id]);
|
||
|
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
|
if ($row) {
|
||
|
|
$page = $row;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return new Response($this->view->render('admin/edit.php', [
|
||
|
|
'title' => $id > 0 ? 'Edit Page' : 'New Page',
|
||
|
|
'page' => $page,
|
||
|
|
'error' => '',
|
||
|
|
]));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function adminSave(): Response
|
||
|
|
{
|
||
|
|
if ($guard = $this->guard(['admin', 'manager', 'editor'])) {
|
||
|
|
return $guard;
|
||
|
|
}
|
||
|
|
|
||
|
|
$db = Database::get();
|
||
|
|
if (!$db instanceof PDO) {
|
||
|
|
return new Response('', 302, ['Location' => '/admin/pages']);
|
||
|
|
}
|
||
|
|
|
||
|
|
$id = (int)($_POST['id'] ?? 0);
|
||
|
|
$title = trim((string)($_POST['title'] ?? ''));
|
||
|
|
$slug = trim((string)($_POST['slug'] ?? ''));
|
||
|
|
$content = (string)($_POST['content_html'] ?? '');
|
||
|
|
$isPublished = isset($_POST['is_published']) ? 1 : 0;
|
||
|
|
$isHome = isset($_POST['is_home']) ? 1 : 0;
|
||
|
|
$isBlogIndex = isset($_POST['is_blog_index']) ? 1 : 0;
|
||
|
|
|
||
|
|
if ($title === '') {
|
||
|
|
return $this->renderEditError($id, $title, $slug, $content, $isPublished, $isHome, $isBlogIndex, 'Title is required.');
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($slug === '') {
|
||
|
|
$slug = $this->slugify($title);
|
||
|
|
} else {
|
||
|
|
$slug = $this->slugify($slug);
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
if ($id > 0) {
|
||
|
|
$chk = $db->prepare("SELECT id FROM ac_pages WHERE slug = :slug AND id != :id LIMIT 1");
|
||
|
|
$chk->execute([':slug' => $slug, ':id' => $id]);
|
||
|
|
} else {
|
||
|
|
$chk = $db->prepare("SELECT id FROM ac_pages WHERE slug = :slug LIMIT 1");
|
||
|
|
$chk->execute([':slug' => $slug]);
|
||
|
|
}
|
||
|
|
if ($chk->fetch()) {
|
||
|
|
return $this->renderEditError($id, $title, $slug, $content, $isPublished, $isHome, $isBlogIndex, 'Slug already exists.');
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($isHome === 1) {
|
||
|
|
$db->exec("UPDATE ac_pages SET is_home = 0");
|
||
|
|
}
|
||
|
|
if ($isBlogIndex === 1) {
|
||
|
|
$db->exec("UPDATE ac_pages SET is_blog_index = 0");
|
||
|
|
}
|
||
|
|
|
||
|
|
if ($id > 0) {
|
||
|
|
$stmt = $db->prepare("
|
||
|
|
UPDATE ac_pages
|
||
|
|
SET title = :title, slug = :slug, content_html = :content,
|
||
|
|
is_published = :published, is_home = :is_home, is_blog_index = :is_blog_index
|
||
|
|
WHERE id = :id
|
||
|
|
");
|
||
|
|
$stmt->execute([
|
||
|
|
':title' => $title,
|
||
|
|
':slug' => $slug,
|
||
|
|
':content' => $content,
|
||
|
|
':published' => $isPublished,
|
||
|
|
':is_home' => $isHome,
|
||
|
|
':is_blog_index' => $isBlogIndex,
|
||
|
|
':id' => $id,
|
||
|
|
]);
|
||
|
|
} else {
|
||
|
|
$stmt = $db->prepare("
|
||
|
|
INSERT INTO ac_pages (title, slug, content_html, is_published, is_home, is_blog_index)
|
||
|
|
VALUES (:title, :slug, :content, :published, :is_home, :is_blog_index)
|
||
|
|
");
|
||
|
|
$stmt->execute([
|
||
|
|
':title' => $title,
|
||
|
|
':slug' => $slug,
|
||
|
|
':content' => $content,
|
||
|
|
':published' => $isPublished,
|
||
|
|
':is_home' => $isHome,
|
||
|
|
':is_blog_index' => $isBlogIndex,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
} catch (Throwable $e) {
|
||
|
|
return $this->renderEditError($id, $title, $slug, $content, $isPublished, $isHome, $isBlogIndex, 'Unable to save page.');
|
||
|
|
}
|
||
|
|
|
||
|
|
return new Response('', 302, ['Location' => '/admin/pages']);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function adminDelete(): Response
|
||
|
|
{
|
||
|
|
if ($guard = $this->guard(['admin', 'manager'])) {
|
||
|
|
return $guard;
|
||
|
|
}
|
||
|
|
$db = Database::get();
|
||
|
|
if (!$db instanceof PDO) {
|
||
|
|
return new Response('', 302, ['Location' => '/admin/pages']);
|
||
|
|
}
|
||
|
|
$id = (int)($_POST['id'] ?? 0);
|
||
|
|
if ($id > 0) {
|
||
|
|
$stmt = $db->prepare("DELETE FROM ac_pages WHERE id = :id");
|
||
|
|
$stmt->execute([':id' => $id]);
|
||
|
|
}
|
||
|
|
return new Response('', 302, ['Location' => '/admin/pages']);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function renderEditError(int $id, string $title, string $slug, string $content, int $isPublished, int $isHome, int $isBlogIndex, string $error): Response
|
||
|
|
{
|
||
|
|
$page = [
|
||
|
|
'id' => $id,
|
||
|
|
'title' => $title,
|
||
|
|
'slug' => $slug,
|
||
|
|
'content_html' => $content,
|
||
|
|
'is_published' => $isPublished,
|
||
|
|
'is_home' => $isHome,
|
||
|
|
'is_blog_index' => $isBlogIndex,
|
||
|
|
];
|
||
|
|
return new Response($this->view->render('admin/edit.php', [
|
||
|
|
'title' => $id > 0 ? 'Edit Page' : 'New Page',
|
||
|
|
'page' => $page,
|
||
|
|
'error' => $error,
|
||
|
|
]));
|
||
|
|
}
|
||
|
|
|
||
|
|
private function notFound(): Response
|
||
|
|
{
|
||
|
|
$view = new View();
|
||
|
|
return new Response($view->render('site/404.php', [
|
||
|
|
'title' => 'Not Found',
|
||
|
|
'message' => 'Page not found.',
|
||
|
|
]), 404);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function slugify(string $value): string
|
||
|
|
{
|
||
|
|
$value = strtolower(trim($value));
|
||
|
|
$value = preg_replace('~[^a-z0-9]+~', '-', $value) ?? $value;
|
||
|
|
$value = trim($value, '-');
|
||
|
|
return $value !== '' ? $value : 'page';
|
||
|
|
}
|
||
|
|
|
||
|
|
private function guard(array $roles): ?Response
|
||
|
|
{
|
||
|
|
if (!Auth::check()) {
|
||
|
|
return new Response('', 302, ['Location' => '/admin/login']);
|
||
|
|
}
|
||
|
|
if (!Auth::hasRole($roles)) {
|
||
|
|
return new Response('', 302, ['Location' => '/admin']);
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|