validate([ 'files.*' => 'required|file|mimes:pdf,docx,xlsx,jpg,png|max:5120', 'project_id' => 'required|exists:projects,id', 'folder_id' => 'nullable|exists:folders,id' ]); foreach ($request->file('files') as $file) { $document = Document::create([ 'name' => $file->getClientOriginalName(), 'project_id' => $request->project_id, 'folder_id' => $request->folder_id, 'created_by' => auth()->id() ]); $document->addMedia($file)->toMediaCollection('documents'); ProcessDocumentOCR::dispatch($document->currentVersion); } return redirect()->back()->with('success', 'Files uploaded successfully'); } /** * Display the specified resource. */ public function show(Document $document) { $this->authorize('view', $document); // Si usas políticas if (!Storage::exists($document->file_path)) { abort(404); } $document->url = Storage::url($document->file_path); $document->load('user'); return view('documents.show', [ 'document' => $document, 'versions' => $document->versions()->latest()->get(), 'comments' => $this->comments, ]); } /** * Show the form for editing the specified resource. */ public function edit(Document $document) { // } /** * Update the specified resource in storage. */ public function update(Request $request, Document $document) { // } /** * Remove the specified resource from storage. */ public function destroy(Document $document) { // } public function upload(Request $request, Project $project) { $request->validate([ 'files.*' => 'required|mimes:pdf,docx,xlsx,jpeg,png|max:2048' ]); foreach ($request->file('files') as $file) { $document = $project->documents()->create([ 'name' => $file->getClientOriginalName(), 'status' => 0 ]); $this->createVersion($document, $file); } } private function createVersion(Document $document, $file) { $version = $document->versions()->create([ 'file_path' => $file->store("projects/{$document->project_id}/documents"), 'hash' => hash_file('sha256', $file), 'user_id' => auth()->id() ]); $document->update(['current_version_id' => $version->id]); } /** * Actualizar PDF con anotaciones, firmas y sellos */ public function updatePdf(Request $request, Document $document) { $this->authorize('update', $document); $request->validate([ 'pdf_data' => 'required|string', // PDF modificado en base64 'annotations' => 'sometimes|array', 'signatures' => 'sometimes|array', 'stamps' => 'sometimes|array', ]); try { // Procesar el PDF modificado $modifiedPdf = $this->processPdfData($request->pdf_data); // Reemplazar el archivo original (sin crear nueva versión si no quieres) $this->replaceOriginalPdf($document, $modifiedPdf); // Opcional: también crear una nueva versión para historial $newVersion = $this->createNewVersion($document, $modifiedPdf, $request->all()); return response()->json([ 'success' => true, 'message' => 'PDF actualizado correctamente', 'version_id' => $newVersion->id ?? null, ]); } catch (\Exception $e) { \Log::error('Error updating PDF: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'Error al actualizar el PDF: ' . $e->getMessage() ], 500); } } /** * Procesar datos del PDF en base64 */ private function processPdfData($pdfData) { // Eliminar el prefijo data:application/pdf;base64, si existe $pdfData = preg_replace('/^data:application\/pdf;base64,/', '', $pdfData); // Decodificar base64 $pdfContent = base64_decode($pdfData); if (!$pdfContent) { throw new \Exception('Datos PDF inválidos'); } return $pdfContent; } /** * Procesar PDF con anotaciones (método alternativo) */ private function processPdfWithAnnotations($document, $data) { // Aquí integrarías una librería PHP para PDF como spatie/pdf-to-image o setasign/fpdi // Por ahora, devolvemos el contenido del archivo original // En producción, implementarías la lógica de modificación if ($document->currentVersion) { $filePath = $document->currentVersion->file_path; } else { $filePath = $document->getFirstMedia('documents')->getPath(); } if (!Storage::exists($filePath)) { throw new \Exception('Archivo PDF no encontrado'); } return Storage::get($filePath); } /** * Reemplazar el PDF original */ private function replaceOriginalPdf($document, $pdfContent) { // Obtener la ruta del archivo original $filePath = $document->file_path; // Si el documento usa media library if ($document->getFirstMedia('documents')) { $media = $document->getFirstMedia('documents'); $media->update([ 'file_name' => $document->name . '.pdf', 'size' => strlen($pdfContent), ]); // Reemplazar el archivo Storage::put($media->getPath(), $pdfContent); } else { // Si usas file_path directo Storage::put($filePath, $pdfContent); // Actualizar metadata del documento $document->update([ 'file_size' => strlen($pdfContent), 'updated_at' => now(), ]); } } /** * Crear nueva versión del documento */ private function createNewVersion($document, $pdfContent, $data = []) { $versionNumber = $document->versions()->count() + 1; $fileName = "documents/{$document->id}/v{$versionNumber}.pdf"; // Guardar el nuevo PDF Storage::put($fileName, $pdfContent); // Crear registro de versión $version = $document->versions()->create([ 'version_number' => $versionNumber, 'file_path' => $fileName, 'file_size' => strlen($pdfContent), 'hash' => hash('sha256', $pdfContent), 'created_by' => auth()->id(), 'metadata' => [ 'annotations_count' => count($data['annotations'] ?? []), 'signatures_count' => count($data['signatures'] ?? []), 'stamps_count' => count($data['stamps'] ?? []), 'edited_at' => now()->toISOString(), 'edited_by' => auth()->user()->name ] ]); return $version; } /** * Guardar metadatos de anotaciones */ private function saveAnnotationsMetadata($version, $data) { // Guardar anotaciones en la base de datos si es necesario if (!empty($data['annotations'])) { foreach ($data['annotations'] as $annotation) { $version->annotations()->create([ 'type' => $annotation['type'] ?? 'text', 'content' => $annotation['content'] ?? '', 'position' => $annotation['position'] ?? [], 'page' => $annotation['page'] ?? 1, 'created_by' => auth()->id() ]); } } } /** * Subir firma */ public function uploadSignature(Request $request) { $request->validate([ 'signature' => 'required|image|max:2048|mimes:png,jpg,jpeg' ]); try { $user = auth()->user(); $path = $request->file('signature')->store("signatures/{$user->id}", 'public'); // Opcional: Guardar en base de datos $user->signatures()->create([ 'file_path' => $path, 'file_name' => $request->file('signature')->getClientOriginalName() ]); return response()->json([ 'success' => true, 'path' => Storage::url($path), 'filename' => basename($path) ]); } catch (\Exception $e) { \Log::error('Error uploading signature: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'Error al subir la firma' ], 500); } } /** * Subir sello */ public function uploadStamp(Request $request) { $request->validate([ 'stamp' => 'required|image|max:2048|mimes:png,jpg,jpeg' ]); try { $user = auth()->user(); $path = $request->file('stamp')->store("stamps/{$user->id}", 'public'); // Opcional: Guardar en base de datos $user->stamps()->create([ 'file_path' => $path, 'file_name' => $request->file('stamp')->getClientOriginalName(), 'type' => $request->type ?? 'custom' ]); return response()->json([ 'success' => true, 'path' => Storage::url($path), 'filename' => basename($path) ]); } catch (\Exception $e) { \Log::error('Error uploading stamp: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'Error al subir el sello' ], 500); } } /** * Obtener firmas del usuario */ public function getSignatures() { $user = auth()->user(); $signatures = $user->signatures()->get()->map(function($signature) { return [ 'id' => $signature->id, 'url' => Storage::url($signature->file_path), 'name' => $signature->file_name ]; }); return response()->json([ 'success' => true, 'signatures' => $signatures ]); } /** * Obtener sellos del usuario */ public function getStamps() { $user = auth()->user(); $stamps = $user->stamps()->get()->map(function($stamp) { return [ 'id' => $stamp->id, 'url' => Storage::url($stamp->file_path), 'name' => $stamp->file_name, 'type' => $stamp->type ]; }); return response()->json([ 'success' => true, 'stamps' => $stamps ]); } /** * Descargar documento */ public function download(Document $document, $versionId = null) { $this->authorize('view', $document); $version = $versionId ? $document->versions()->findOrFail($versionId) : $document->currentVersion; if (!$version || !Storage::exists($version->file_path)) { abort(404); } return Storage::download($version->file_path, $document->name . '.pdf'); } /** * Obtener el PDF actual para edición */ public function getPdfForEditing(Document $document) { $this->authorize('view', $document); try { if ($document->getFirstMedia('documents')) { $filePath = $document->getFirstMedia('documents')->getPath(); } else { $filePath = $document->file_path; } if (!Storage::exists($filePath)) { abort(404); } $pdfContent = Storage::get($filePath); $base64Pdf = base64_encode($pdfContent); return response()->json([ 'success' => true, 'pdf_data' => $base64Pdf, 'document_name' => $document->name ]); } catch (\Exception $e) { \Log::error('Error getting PDF for editing: ' . $e->getMessage()); return response()->json([ 'success' => false, 'message' => 'Error al cargar el PDF' ], 500); } } }