getClientOriginalExtension()); if (! in_array($ext, $allowedExtensions, true)) { return null; } if ($file->getSize() > $maxBytes) { return null; } $destination = storage_path('uploads/'.trim($subdir, '/')); if (! is_dir($destination)) { mkdir($destination, 0755, true); } $filename = uniqid('exam_', true).'.'.$ext; try { $file->move($destination, $filename); return [ 'stored' => $filename, 'original' => $file->getClientOriginalName(), 'extension' => $ext, ]; } catch (\Throwable) { return null; } } public function ensurePdfExists(string $finalFilename, ?string $originalExt, string $subdir): ?string { $pdfNeighbor = $this->neighborPdfIfExists($finalFilename, $subdir); if ($pdfNeighbor !== null) { return $pdfNeighbor; } $ext = strtolower((string) $originalExt); if ($ext === 'pdf') { $path = $this->fullUploadPath($subdir, $finalFilename); return is_file($path) ? $finalFilename : null; } return $this->convertDocToPdf($this->fullUploadPath($subdir, $finalFilename), $subdir); } public function copyDraftToFinal(string $draftFilename, string $sourceDir, string $targetDir): ?string { $source = $this->fullUploadPath($sourceDir, $draftFilename); if (! is_file($source)) { return null; } $destinationDir = storage_path('uploads/'.trim($targetDir, '/')); if (! is_dir($destinationDir)) { mkdir($destinationDir, 0755, true); } $ext = pathinfo($draftFilename, PATHINFO_EXTENSION); $destName = uniqid('final_', true).'.'.$ext; $destPath = $destinationDir.'/'.$destName; if (! @copy($source, $destPath)) { return null; } return $destName; } private function convertDocToPdf(string $sourcePath, string $targetSubdir): ?string { $ext = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION)); if (! in_array($ext, ['doc', 'docx'], true)) { return null; } $targetDir = storage_path('uploads/'.trim($targetSubdir, '/')); if (! is_dir($targetDir)) { mkdir($targetDir, 0755, true); } $base = pathinfo($sourcePath, PATHINFO_FILENAME); $targetPath = $targetDir.'/'.$base.'.pdf'; $cmd = 'soffice --headless --convert-to pdf --outdir '.escapeshellarg($targetDir).' '.escapeshellarg($sourcePath).' 2>/dev/null'; @exec($cmd); return is_file($targetPath) ? basename($targetPath) : null; } private function neighborPdfIfExists(string $filename, string $subdir): ?string { if ($filename === '') { return null; } $path = $this->fullUploadPath($subdir, $filename); $base = pathinfo($path, PATHINFO_FILENAME); $dir = pathinfo($path, PATHINFO_DIRNAME); $pdfPath = $dir.'/'.$base.'.pdf'; return is_file($pdfPath) ? basename($pdfPath) : null; } private function fullUploadPath(string $subdir, string $filename): string { return storage_path('uploads/'.trim($subdir, '/').'/'.$filename); } }