isAjax()) { $this->json(['success' => false, 'message' => 'Unauthorized'], 403); } header('Location: /login'); exit; } $taskId = (int)($_POST['task_id'] ?? 0); if ($taskId <= 0 || empty($_FILES['file'])) { if ($this->isAjax()) { $this->json(['success' => false, 'message' => 'Файл не выбран'], 400); } $_SESSION['error'] = 'Файл не выбран'; header('Location: /tasks'); exit; } $file = $_FILES['file']; if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) { if ($this->isAjax()) { $this->json(['success' => false, 'message' => 'Ошибка загрузки файла'], 400); } $_SESSION['error'] = 'Ошибка загрузки файла'; header('Location: /tasks'); exit; } $uploadDir = __DIR__ . '/../../storage/uploads/tasks/' . $taskId; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0775, true); } $originalName = (string)$file['name']; $tmpName = (string)$file['tmp_name']; $extension = pathinfo($originalName, PATHINFO_EXTENSION); $storedName = uniqid('task_', true) . ($extension ? '.' . $extension : ''); $targetPath = $uploadDir . '/' . $storedName; if (!move_uploaded_file($tmpName, $targetPath)) { if ($this->isAjax()) { $this->json(['success' => false, 'message' => 'Не удалось сохранить файл'], 500); } $_SESSION['error'] = 'Не удалось сохранить файл'; header('Location: /tasks'); exit; } $relativePath = 'storage/uploads/tasks/' . $taskId . '/' . $storedName; $fileSize = (int)filesize($targetPath); $mimeType = mime_content_type($targetPath) ?: null; $pdo = DB::connection(); $stmt = $pdo->prepare(" INSERT INTO task_files ( task_id, uploaded_by, original_name, stored_name, file_path, file_size, mime_type, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, NOW()) "); $stmt->execute([ $taskId, Auth::user()['id'], $originalName, $storedName, $relativePath, $fileSize, $mimeType, ]); if ($this->isAjax()) { $this->json([ 'success' => true, 'task_id' => $taskId, ]); } header('Location: /tasks'); exit; } public function download(): void { if (!Auth::check()) { header('Location: /login'); exit; } $fileId = (int)($_GET['id'] ?? 0); if ($fileId <= 0) { http_response_code(404); echo 'Файл не найден'; return; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT * FROM task_files WHERE id = ? LIMIT 1 "); $stmt->execute([$fileId]); $file = $stmt->fetch(); if (!$file) { http_response_code(404); echo 'Файл не найден'; return; } $absolutePath = __DIR__ . '/../../' . $file['file_path']; if (!is_file($absolutePath)) { http_response_code(404); echo 'Файл не найден на диске'; return; } header('Content-Description: File Transfer'); header('Content-Type: ' . ($file['mime_type'] ?: 'application/octet-stream')); header('Content-Disposition: attachment; filename="' . basename((string)$file['original_name']) . '"'); header('Content-Length: ' . filesize($absolutePath)); header('Pragma: public'); readfile($absolutePath); exit; } public function view(): void { if (!Auth::check()) { header('Location: /login'); exit; } $fileId = (int)($_GET['id'] ?? 0); if ($fileId <= 0) { http_response_code(404); echo 'Файл не найден'; return; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT * FROM task_files WHERE id = ? LIMIT 1 "); $stmt->execute([$fileId]); $file = $stmt->fetch(); if (!$file) { http_response_code(404); echo 'Файл не найден'; return; } $absolutePath = __DIR__ . '/../../' . $file['file_path']; if (!is_file($absolutePath)) { http_response_code(404); echo 'Файл не найден на диске'; return; } $mimeType = $file['mime_type'] ?: mime_content_type($absolutePath) ?: 'application/octet-stream'; header('Content-Type: ' . $mimeType); header('Content-Length: ' . filesize($absolutePath)); header('Content-Disposition: inline; filename="' . basename((string)$file['original_name']) . '"'); readfile($absolutePath); exit; } public function delete(): void { if (!Auth::check()) { if ($this->isAjax()) { $this->json(['success' => false, 'message' => 'Unauthorized'], 403); } header('Location: /login'); exit; } $fileId = (int)($_POST['id'] ?? 0); if ($fileId <= 0) { if ($this->isAjax()) { $this->json(['success' => false, 'message' => 'Файл не найден'], 404); } $_SESSION['error'] = 'Файл не найден'; header('Location: /tasks'); exit; } $pdo = DB::connection(); $stmt = $pdo->prepare(" SELECT * FROM task_files WHERE id = ? LIMIT 1 "); $stmt->execute([$fileId]); $file = $stmt->fetch(); if (!$file) { if ($this->isAjax()) { $this->json(['success' => false, 'message' => 'Файл не найден'], 404); } $_SESSION['error'] = 'Файл не найден'; header('Location: /tasks'); exit; } $absolutePath = __DIR__ . '/../../' . $file['file_path']; $stmt = $pdo->prepare("DELETE FROM task_files WHERE id = ?"); $stmt->execute([$fileId]); if (is_file($absolutePath)) { @unlink($absolutePath); } if ($this->isAjax()) { $this->json([ 'success' => true, 'file_id' => $fileId, ]); } header('Location: /tasks'); exit; } private function isAjax(): bool { return strtolower((string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '')) === 'xmlhttprequest'; } private function json(array $data, int $statusCode = 200): void { http_response_code($statusCode); header('Content-Type: application/json; charset=utf-8'); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); exit; } }