++ fix temporary PDF viewer URLs, which were causing issues with caching and expiring links. Instead, we now generate short-lived URLs that redirect to the PDF viewer route, ensuring that users can access the PDFs without running into expired links. This change affects the Assegnazione model, the ShortPdfLinkController, and the relevant Blade views for assignments and records. Additionally, I've updated the Home Livewire component to calculate and display the average duration of assignments in months, providing more insight into assignment durations on the dashboard.

This commit is contained in:
2026-04-13 15:05:37 +00:00
parent 465e7cf092
commit 0553d4ef74
23 changed files with 781 additions and 212 deletions

View File

@@ -3,35 +3,31 @@
namespace App\Http\Controllers;
use App\Models\Assegnazione;
use Carbon\Carbon;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\StreamedResponse;
class AssignmentPdfController extends Controller
{
public function viewer(Request $request, Assegnazione $assignment, string $code): View
{
$this->validateAccess($request, $assignment, $code);
$this->validateAccess($assignment, $code);
$expiresAt = Carbon::createFromTimestamp((int) $request->query('expires'));
$pdfUrl = URL::temporarySignedRoute(
'assignments.pdf.file',
$expiresAt,
['assignment' => $assignment->id, 'code' => $code]
);
$pdfUrl = route('assignments.pdf.file', [
'assignment' => $assignment->id,
'code' => $code,
]);
return view('assignments.pdf-viewer', [
'assignment' => $assignment,
'pdfUrl' => $pdfUrl,
'pdfUrl' => $pdfUrl,
]);
}
public function file(Request $request, Assegnazione $assignment, string $code): StreamedResponse
{
$this->validateAccess($request, $assignment, $code);
$this->validateAccess($assignment, $code);
$pdfPath = $assignment->territorio?->pdf_path;
abort_unless($pdfPath && Storage::disk('public')->exists($pdfPath), 404);
@@ -40,17 +36,16 @@ class AssignmentPdfController extends Controller
$pdfPath,
'territorio-' . $assignment->territorio?->numero . '.pdf',
[
'Content-Type' => 'application/pdf',
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="territorio-' . $assignment->territorio?->numero . '.pdf"',
]
);
}
protected function validateAccess(Request $request, Assegnazione $assignment, string $code): void
protected function validateAccess(Assegnazione $assignment, string $code): void
{
abort_unless($request->hasValidSignature(), 403);
abort_unless($assignment->pdf_access_code && hash_equals($assignment->pdf_access_code, $code), 404);
abort_unless($assignment->is_aperta, 403);
abort_unless($assignment->territorio?->pdf_path, 404);
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Http\Controllers\Settings;
use App\Http\Controllers\Controller;
use App\Livewire\Settings\XmlExchange;
use Illuminate\Http\Request;
class XmlExchangeUploadController extends Controller
{
public function convertSqlToXml(Request $request)
{
$request->validate([
'sqlDump' => ['required', 'file', 'max:256000'],
]);
$file = $request->file('sqlDump');
$ext = strtolower($file->getClientOriginalExtension());
if (! in_array($ext, ['sql', 'txt'])) {
return back()->withErrors(['sqlDump' => 'Il file deve essere .sql o .txt']);
}
$content = file_get_contents($file->getRealPath());
if (! $content) {
return back()->withErrors(['sqlDump' => 'File vuoto o non leggibile.']);
}
$exchange = app(XmlExchange::class);
$dataset = $exchange->legacySqlToDatasetPublic($content);
$xml = $exchange->datasetToXmlPublic($dataset, 'legacy-sql-conversion');
return response()->streamDownload(function () use ($xml) {
echo $xml;
}, 'termanager-conversion.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
}
public function importXml(Request $request)
{
$request->validate([
'xmlImport' => ['required', 'file', 'max:256000'],
]);
$file = $request->file('xmlImport');
$ext = strtolower($file->getClientOriginalExtension());
if (! in_array($ext, ['xml', 'txt'])) {
return back()->withErrors(['xmlImport' => 'Il file deve essere .xml o .txt']);
}
$content = file_get_contents($file->getRealPath());
if (! $content) {
return back()->withErrors(['xmlImport' => 'File vuoto o non leggibile.']);
}
$exchange = new XmlExchange();
$result = $exchange->importXmlFromContent($content);
if (isset($result['error'])) {
return back()->withErrors(['xmlImport' => $result['error']]);
}
$stats = $result['stats'];
$issues = $result['issues'];
$message = 'Import XML completato con successo.';
if (($stats['duplicate_territori'] ?? 0) > 0 || ($stats['assegnazioni_saltate'] ?? 0) > 0) {
$message .= ' Territori duplicati saltati: ' . $stats['duplicate_territori'] . '. Assegnazioni saltate: ' . $stats['assegnazioni_saltate'] . '.';
}
return redirect()->route('xml.exchange')
->with('success', $message)
->with('importStats', $stats)
->with('importIssues', $issues);
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Http\Controllers;
use App\Models\Assegnazione;
use Illuminate\Http\RedirectResponse;
class ShortPdfLinkController extends Controller
{
public function __invoke(string $code): RedirectResponse
{
$assignment = Assegnazione::where('pdf_access_code', $code)->firstOrFail();
abort_unless($assignment->is_aperta, 403);
abort_unless($assignment->territorio?->pdf_path, 404);
return redirect()->route('assignments.pdf.viewer', [
'assignment' => $assignment->id,
'code' => $code,
]);
}
}