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.'; } }