219 lines
8.1 KiB
PHP
219 lines
8.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Modules\Media;
|
|
|
|
use Core\Http\Response;
|
|
use Core\Services\Auth;
|
|
use Core\Services\Database;
|
|
use Core\Views\View;
|
|
use PDO;
|
|
use Throwable;
|
|
|
|
class MediaController
|
|
{
|
|
private View $view;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->view = new View(__DIR__ . '/views');
|
|
}
|
|
|
|
public function index(): Response
|
|
{
|
|
if (!Auth::check()) {
|
|
return new Response('', 302, ['Location' => '/admin/login']);
|
|
}
|
|
if (!Auth::hasRole(['admin', 'manager'])) {
|
|
return new Response('', 302, ['Location' => '/admin']);
|
|
}
|
|
|
|
$db = Database::get();
|
|
$items = [];
|
|
$folders = [];
|
|
$folderId = isset($_GET['folder']) ? (int)$_GET['folder'] : 0;
|
|
if ($db instanceof PDO) {
|
|
$folderStmt = $db->query("SELECT id, name FROM ac_media_folders ORDER BY name ASC");
|
|
$folders = $folderStmt ? $folderStmt->fetchAll(PDO::FETCH_ASSOC) : [];
|
|
if ($folderId > 0) {
|
|
$stmt = $db->prepare("SELECT id, file_name, file_url, file_type, file_size, created_at FROM ac_media WHERE folder_id = :folder_id ORDER BY created_at DESC");
|
|
$stmt->execute([':folder_id' => $folderId]);
|
|
} else {
|
|
$stmt = $db->query("SELECT id, file_name, file_url, file_type, file_size, created_at FROM ac_media WHERE folder_id IS NULL ORDER BY created_at DESC");
|
|
}
|
|
$items = $stmt ? $stmt->fetchAll(PDO::FETCH_ASSOC) : [];
|
|
}
|
|
|
|
$error = (string)($_GET['error'] ?? '');
|
|
$success = (string)($_GET['success'] ?? '');
|
|
return new Response($this->view->render('admin/index.php', [
|
|
'title' => 'Media',
|
|
'items' => $items,
|
|
'folders' => $folders,
|
|
'active_folder' => $folderId,
|
|
'error' => $error,
|
|
'success' => $success,
|
|
]));
|
|
}
|
|
|
|
public function picker(): Response
|
|
{
|
|
if (!Auth::check()) {
|
|
return new Response('', 302, ['Location' => '/admin/login']);
|
|
}
|
|
if (!Auth::hasRole(['admin', 'manager', 'editor'])) {
|
|
return new Response('', 302, ['Location' => '/admin']);
|
|
}
|
|
|
|
$db = Database::get();
|
|
$items = [];
|
|
if ($db instanceof PDO) {
|
|
$stmt = $db->query("SELECT id, file_name, file_url, file_type FROM ac_media ORDER BY created_at DESC");
|
|
$items = $stmt ? $stmt->fetchAll(PDO::FETCH_ASSOC) : [];
|
|
}
|
|
|
|
return new Response(json_encode(['items' => $items]), 200, ['Content-Type' => 'application/json']);
|
|
}
|
|
|
|
public function upload(): Response
|
|
{
|
|
if (!Auth::check()) {
|
|
return new Response('', 302, ['Location' => '/admin/login']);
|
|
}
|
|
if (!Auth::hasRole(['admin', 'manager'])) {
|
|
return new Response('', 302, ['Location' => '/admin']);
|
|
}
|
|
|
|
$file = $_FILES['media_file'] ?? null;
|
|
$folderId = isset($_POST['folder_id']) ? (int)$_POST['folder_id'] : 0;
|
|
if (!$file || !isset($file['tmp_name'])) {
|
|
return $this->uploadError('No file uploaded.', $folderId);
|
|
}
|
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
|
return $this->uploadError($this->uploadErrorMessage((int)$file['error']), $folderId);
|
|
}
|
|
|
|
$tmp = (string)$file['tmp_name'];
|
|
if ($tmp === '' || !is_uploaded_file($tmp)) {
|
|
return new Response('', 302, ['Location' => '/admin/media']);
|
|
}
|
|
|
|
$ext = strtolower(pathinfo((string)$file['name'], PATHINFO_EXTENSION));
|
|
if ($ext === '') {
|
|
$ext = 'bin';
|
|
}
|
|
|
|
$uploadDir = __DIR__ . '/../../uploads/media';
|
|
if (!is_dir($uploadDir)) {
|
|
if (!mkdir($uploadDir, 0755, true)) {
|
|
return $this->uploadError('Upload directory could not be created.', $folderId);
|
|
}
|
|
}
|
|
if (!is_writable($uploadDir)) {
|
|
return $this->uploadError('Upload directory is not writable.', $folderId);
|
|
}
|
|
|
|
$baseName = preg_replace('~[^a-z0-9]+~', '-', strtolower((string)$file['name'])) ?? 'file';
|
|
$baseName = trim($baseName, '-');
|
|
$fileName = ($baseName !== '' ? $baseName : 'file') . '-' . date('YmdHis') . '.' . $ext;
|
|
$dest = $uploadDir . '/' . $fileName;
|
|
if (!move_uploaded_file($tmp, $dest)) {
|
|
return $this->uploadError('Upload failed. Check server permissions.', $folderId);
|
|
}
|
|
|
|
$fileUrl = '/uploads/media/' . $fileName;
|
|
$fileType = (string)($file['type'] ?? '');
|
|
$fileSize = (int)($file['size'] ?? 0);
|
|
|
|
$db = Database::get();
|
|
if ($db instanceof PDO) {
|
|
try {
|
|
$stmt = $db->prepare("
|
|
INSERT INTO ac_media (file_name, file_url, file_type, file_size, folder_id)
|
|
VALUES (:name, :url, :type, :size, :folder_id)
|
|
");
|
|
$stmt->execute([
|
|
':name' => (string)$file['name'],
|
|
':url' => $fileUrl,
|
|
':type' => $fileType,
|
|
':size' => $fileSize,
|
|
':folder_id' => $folderId > 0 ? $folderId : null,
|
|
]);
|
|
} catch (Throwable $e) {
|
|
return $this->uploadError('Database insert failed.', $folderId);
|
|
}
|
|
}
|
|
|
|
$redirect = $folderId > 0 ? '/admin/media?folder=' . $folderId . '&success=1' : '/admin/media?success=1';
|
|
return new Response('', 302, ['Location' => $redirect]);
|
|
}
|
|
|
|
public function delete(): Response
|
|
{
|
|
if (!Auth::check()) {
|
|
return new Response('', 302, ['Location' => '/admin/login']);
|
|
}
|
|
if (!Auth::hasRole(['admin', 'manager'])) {
|
|
return new Response('', 302, ['Location' => '/admin']);
|
|
}
|
|
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
$db = Database::get();
|
|
if ($db instanceof PDO && $id > 0) {
|
|
$stmt = $db->prepare("SELECT file_url FROM ac_media WHERE id = :id");
|
|
$stmt->execute([':id' => $id]);
|
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
if ($row && !empty($row['file_url'])) {
|
|
$path = __DIR__ . '/../../..' . (string)$row['file_url'];
|
|
if (is_file($path)) {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
$db->prepare("DELETE FROM ac_media WHERE id = :id")->execute([':id' => $id]);
|
|
}
|
|
return new Response('', 302, ['Location' => '/admin/media']);
|
|
}
|
|
|
|
public function createFolder(): Response
|
|
{
|
|
if (!Auth::check()) {
|
|
return new Response('', 302, ['Location' => '/admin/login']);
|
|
}
|
|
if (!Auth::hasRole(['admin', 'manager'])) {
|
|
return new Response('', 302, ['Location' => '/admin']);
|
|
}
|
|
$name = trim((string)($_POST['name'] ?? ''));
|
|
if ($name === '') {
|
|
return new Response('', 302, ['Location' => '/admin/media']);
|
|
}
|
|
$db = Database::get();
|
|
if ($db instanceof PDO) {
|
|
$stmt = $db->prepare("INSERT INTO ac_media_folders (name) VALUES (:name)");
|
|
$stmt->execute([':name' => $name]);
|
|
}
|
|
return new Response('', 302, ['Location' => '/admin/media']);
|
|
}
|
|
|
|
private function uploadError(string $message, int $folderId): Response
|
|
{
|
|
$target = $folderId > 0 ? '/admin/media?folder=' . $folderId : '/admin/media';
|
|
$target .= '&error=' . rawurlencode($message);
|
|
return new Response('', 302, ['Location' => $target]);
|
|
}
|
|
|
|
private function uploadErrorMessage(int $code): string
|
|
{
|
|
$max = (string)ini_get('upload_max_filesize');
|
|
$map = [
|
|
UPLOAD_ERR_INI_SIZE => "File exceeds upload_max_filesize ({$max}).",
|
|
UPLOAD_ERR_FORM_SIZE => 'File exceeds form limit.',
|
|
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded.',
|
|
UPLOAD_ERR_NO_TMP_DIR => 'Missing temp upload directory.',
|
|
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
|
|
UPLOAD_ERR_EXTENSION => 'Upload stopped by a PHP extension.',
|
|
UPLOAD_ERR_NO_FILE => 'No file uploaded.',
|
|
];
|
|
return $map[$code] ?? 'Upload failed.';
|
|
}
|
|
}
|