entityManager->getRepository(TaskDocument::class)->find($id); if (null === $document) { throw new NotFoundHttpException('Document not found.'); } // ROLE_CLIENT can only download documents from their own tickets if (!$this->security->isGranted('ROLE_ADMIN') && !$this->security->isGranted('ROLE_USER')) { $ticket = $document->getClientTicket(); if (null === $ticket || $ticket->getSubmittedBy() !== $this->security->getUser()) { throw new AccessDeniedHttpException('You do not have access to this document.'); } } $filePath = $this->uploadDir.'/'.$document->getFileName(); if (!file_exists($filePath)) { throw new NotFoundHttpException('File not found on disk.'); } $response = new BinaryFileResponse($filePath); $mimeType = $document->getMimeType() ?? 'application/octet-stream'; // Inline for images and PDFs, attachment for everything else // SVG files are always served as attachment to prevent XSS via embedded JavaScript $disposition = 'image/svg+xml' === $mimeType ? ResponseHeaderBag::DISPOSITION_ATTACHMENT : (str_starts_with($mimeType, 'image/') || 'application/pdf' === $mimeType ? ResponseHeaderBag::DISPOSITION_INLINE : ResponseHeaderBag::DISPOSITION_ATTACHMENT); $response->setContentDisposition($disposition, $document->getOriginalName()); $response->headers->set('Content-Type', $mimeType); return $response; } }