++ 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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
22
app/Http/Controllers/ShortPdfLinkController.php
Normal file
22
app/Http/Controllers/ShortPdfLinkController.php
Normal 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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -108,12 +108,24 @@ class Home extends Component
|
||||
->count('territorio_id');
|
||||
}
|
||||
|
||||
// Monthly average
|
||||
// Monthly average (territories/month this year)
|
||||
$mediaPercorrenzaMensile = 0;
|
||||
if ($annoCorrente && $annoCorrente->mesi_trascorsi > 0) {
|
||||
$mediaPercorrenzaMensile = round($territoriPercorsi / $annoCorrente->mesi_trascorsi, 1);
|
||||
}
|
||||
|
||||
// Average assignment duration in months (current year only, matches old app "Media Percorrenza Congregazione")
|
||||
$avgGiorni = null;
|
||||
if ($annoCorrente) {
|
||||
$avgGiorni = Assegnazione::where('anno_teocratico_id', $annoCorrente->id)
|
||||
->whereNotNull('returned_at')
|
||||
->whereRaw('YEAR(assigned_at) >= 1900')
|
||||
->whereRaw('DATEDIFF(returned_at, assigned_at) > 0')
|
||||
->selectRaw('AVG(DATEDIFF(returned_at, assigned_at)) as media_giorni')
|
||||
->value('media_giorni');
|
||||
}
|
||||
$mediaDurataPercorrenzaMesi = $avgGiorni ? round($avgGiorni / 30.44, 1) : 0;
|
||||
|
||||
// Campaign stats
|
||||
$campagnaStats = null;
|
||||
if ($campagnaAttiva) {
|
||||
@@ -178,6 +190,7 @@ class Home extends Component
|
||||
'totInReparto' => $totInReparto,
|
||||
'territoriPercorsi' => $territoriPercorsi,
|
||||
'mediaPercorrenzaMensile' => $mediaPercorrenzaMensile,
|
||||
'mediaDurataPercorrenzaMesi' => $mediaDurataPercorrenzaMesi,
|
||||
'campagnaStats' => $campagnaStats,
|
||||
'homeLimit' => $homeLimit,
|
||||
'territoriDaAssegnare' => $territoriDaAssegnare,
|
||||
|
||||
@@ -25,8 +25,6 @@ class XmlExchange extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $sqlDump;
|
||||
public $xmlImport;
|
||||
public array $importStats = [];
|
||||
public array $importIssues = [];
|
||||
public array $pdfFolder = [];
|
||||
@@ -46,46 +44,135 @@ class XmlExchange extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function convertLegacySqlToXml()
|
||||
public function importTerritoryPdfFolder(): void
|
||||
{
|
||||
$this->validate([
|
||||
'sqlDump' => ['required', 'file', 'mimes:sql,txt'],
|
||||
'pdfFolder' => ['required', 'array', 'min:1'],
|
||||
'pdfFolder.*' => ['file', 'mimes:pdf', 'max:10240'],
|
||||
]);
|
||||
|
||||
$content = file_get_contents($this->sqlDump->getRealPath());
|
||||
$dataset = $this->legacySqlToDataset($content ?: '');
|
||||
$xml = $this->datasetToXml($dataset, 'legacy-sql-conversion');
|
||||
$importId = (string) Str::uuid();
|
||||
$storedFiles = [];
|
||||
|
||||
foreach ($this->pdfFolder as $index => $file) {
|
||||
$originalName = $file->getClientOriginalName();
|
||||
$safeName = Str::slug(pathinfo($originalName, PATHINFO_FILENAME));
|
||||
$extension = strtolower($file->getClientOriginalExtension() ?: 'pdf');
|
||||
$storedPath = $file->storeAs(
|
||||
'bulk-territori-imports/' . $importId,
|
||||
str_pad((string) $index, 4, '0', STR_PAD_LEFT) . '-' . $safeName . '.' . $extension,
|
||||
'local'
|
||||
);
|
||||
|
||||
$storedFiles[] = [
|
||||
'original_name' => $originalName,
|
||||
'stored_path' => $storedPath,
|
||||
];
|
||||
}
|
||||
|
||||
$this->pdfFolder = [];
|
||||
|
||||
}
|
||||
|
||||
public function refreshPdfImportStatus(): void
|
||||
{
|
||||
if (! $this->currentPdfImportId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$state = app(TerritorioPdfImportState::class)->get($this->currentPdfImportId);
|
||||
|
||||
if (! $state) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pdfImportStatus = $state['status'] ?? 'idle';
|
||||
$this->pdfImportStats = $state['stats'] ?? [];
|
||||
$this->pdfImportLogs = $state['logs'] ?? [];
|
||||
$this->pdfImportIssues = $state['issues'] ?? [];
|
||||
$this->pdfImportLogText = implode(PHP_EOL, $this->pdfImportLogs);
|
||||
}
|
||||
|
||||
protected function dispatchPdfImport(string $importId, array $storedFiles, string $initialLog): void
|
||||
{
|
||||
$state = app(TerritorioPdfImportDispatcher::class)
|
||||
->dispatchStoredFiles($importId, $storedFiles, auth()->id(), $initialLog);
|
||||
|
||||
$this->currentPdfImportId = $importId;
|
||||
$this->pdfImportStatus = $state['status'] ?? 'queued';
|
||||
$this->pdfImportStats = $state['stats'] ?? [];
|
||||
$this->pdfImportLogs = $state['logs'] ?? [];
|
||||
$this->pdfImportIssues = $state['issues'] ?? [];
|
||||
$this->refreshPdfImportStatus();
|
||||
|
||||
session()->flash('success', 'Import PDF avviato in background. I log si aggiorneranno automaticamente.');
|
||||
}
|
||||
|
||||
public function downloadImportLogPdf()
|
||||
{
|
||||
if (empty($this->importStats)) {
|
||||
session()->flash('error', 'Nessun log da scaricare. Esegui prima un import XML.');
|
||||
return;
|
||||
}
|
||||
|
||||
$stats = $this->importStats;
|
||||
$issues = $this->importIssues;
|
||||
$generatedAt = now()->format('d/m/Y H:i:s');
|
||||
|
||||
$html = view('pdf.import-log', compact('stats', 'issues', 'generatedAt'))->render();
|
||||
|
||||
return response()->streamDownload(function () use ($html) {
|
||||
echo Pdf::loadHTML($html)
|
||||
->setPaper('a4', 'portrait')
|
||||
->output();
|
||||
}, 'import-log-' . now()->format('Ymd-His') . '.pdf', ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
public function exportCurrentAsXml()
|
||||
{
|
||||
$dataset = $this->currentDataset();
|
||||
$xml = $this->datasetToXml($dataset, 'current-app-export');
|
||||
|
||||
return response()->streamDownload(function () use ($xml) {
|
||||
echo $xml;
|
||||
}, 'termanager-conversion.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
|
||||
}, 'termanager-export.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
|
||||
}
|
||||
|
||||
public function importXmlIntoApp(): void
|
||||
public function render()
|
||||
{
|
||||
if (session()->has('importStats')) {
|
||||
$this->importStats = session('importStats');
|
||||
}
|
||||
if (session()->has('importIssues')) {
|
||||
$this->importIssues = session('importIssues');
|
||||
}
|
||||
|
||||
return view('livewire.settings.xml-exchange');
|
||||
}
|
||||
|
||||
public function legacySqlToDatasetPublic(string $sql): array
|
||||
{
|
||||
return $this->legacySqlToDataset($sql);
|
||||
}
|
||||
|
||||
public function datasetToXmlPublic(array $dataset, string $source): string
|
||||
{
|
||||
return $this->datasetToXml($dataset, $source);
|
||||
}
|
||||
|
||||
public function importXmlFromContent(string $content): array
|
||||
{
|
||||
$this->importStats = [];
|
||||
$this->importIssues = [];
|
||||
|
||||
$this->validate([
|
||||
'xmlImport' => ['required', 'file', 'mimes:xml,txt'],
|
||||
]);
|
||||
|
||||
$content = file_get_contents($this->xmlImport->getRealPath());
|
||||
if (! $content) {
|
||||
$this->addError('xmlImport', 'File XML non valido.');
|
||||
return;
|
||||
}
|
||||
|
||||
$xml = @simplexml_load_string($content);
|
||||
if (! $xml) {
|
||||
$this->addError('xmlImport', 'Impossibile leggere il file XML.');
|
||||
return;
|
||||
return ['error' => 'Impossibile leggere il file XML.'];
|
||||
}
|
||||
|
||||
$actorId = auth()->id() ?? User::query()->value('id');
|
||||
if (! $actorId) {
|
||||
$this->addError('xmlImport', 'Serve almeno un utente nel sistema per importare i dati.');
|
||||
return;
|
||||
return ['error' => 'Serve almeno un utente nel sistema per importare i dati.'];
|
||||
}
|
||||
|
||||
$stats = [
|
||||
@@ -268,113 +355,10 @@ class XmlExchange extends Component
|
||||
}
|
||||
});
|
||||
|
||||
$this->importStats = $stats;
|
||||
|
||||
$message = 'Import XML completato con successo.';
|
||||
if ($stats['duplicate_territori'] > 0 || $stats['assegnazioni_saltate'] > 0) {
|
||||
$message .= ' Territori duplicati saltati: ' . $stats['duplicate_territori'] . '. Assegnazioni saltate: ' . $stats['assegnazioni_saltate'] . '.';
|
||||
}
|
||||
|
||||
session()->flash('success', $message);
|
||||
}
|
||||
|
||||
public function importTerritoryPdfFolder(): void
|
||||
{
|
||||
$this->validate([
|
||||
'pdfFolder' => ['required', 'array', 'min:1'],
|
||||
'pdfFolder.*' => ['file', 'mimes:pdf', 'max:10240'],
|
||||
]);
|
||||
|
||||
$importId = (string) Str::uuid();
|
||||
$storedFiles = [];
|
||||
|
||||
foreach ($this->pdfFolder as $index => $file) {
|
||||
$originalName = $file->getClientOriginalName();
|
||||
$safeName = Str::slug(pathinfo($originalName, PATHINFO_FILENAME));
|
||||
$extension = strtolower($file->getClientOriginalExtension() ?: 'pdf');
|
||||
$storedPath = $file->storeAs(
|
||||
'bulk-territori-imports/' . $importId,
|
||||
str_pad((string) $index, 4, '0', STR_PAD_LEFT) . '-' . $safeName . '.' . $extension,
|
||||
'local'
|
||||
);
|
||||
|
||||
$storedFiles[] = [
|
||||
'original_name' => $originalName,
|
||||
'stored_path' => $storedPath,
|
||||
];
|
||||
}
|
||||
|
||||
$this->pdfFolder = [];
|
||||
|
||||
}
|
||||
|
||||
public function refreshPdfImportStatus(): void
|
||||
{
|
||||
if (! $this->currentPdfImportId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$state = app(TerritorioPdfImportState::class)->get($this->currentPdfImportId);
|
||||
|
||||
if (! $state) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->pdfImportStatus = $state['status'] ?? 'idle';
|
||||
$this->pdfImportStats = $state['stats'] ?? [];
|
||||
$this->pdfImportLogs = $state['logs'] ?? [];
|
||||
$this->pdfImportIssues = $state['issues'] ?? [];
|
||||
$this->pdfImportLogText = implode(PHP_EOL, $this->pdfImportLogs);
|
||||
}
|
||||
|
||||
protected function dispatchPdfImport(string $importId, array $storedFiles, string $initialLog): void
|
||||
{
|
||||
$state = app(TerritorioPdfImportDispatcher::class)
|
||||
->dispatchStoredFiles($importId, $storedFiles, auth()->id(), $initialLog);
|
||||
|
||||
$this->currentPdfImportId = $importId;
|
||||
$this->pdfImportStatus = $state['status'] ?? 'queued';
|
||||
$this->pdfImportStats = $state['stats'] ?? [];
|
||||
$this->pdfImportLogs = $state['logs'] ?? [];
|
||||
$this->pdfImportIssues = $state['issues'] ?? [];
|
||||
$this->refreshPdfImportStatus();
|
||||
|
||||
session()->flash('success', 'Import PDF avviato in background. I log si aggiorneranno automaticamente.');
|
||||
}
|
||||
|
||||
public function downloadImportLogPdf()
|
||||
{
|
||||
if (empty($this->importStats)) {
|
||||
session()->flash('error', 'Nessun log da scaricare. Esegui prima un import XML.');
|
||||
return;
|
||||
}
|
||||
|
||||
$stats = $this->importStats;
|
||||
$issues = $this->importIssues;
|
||||
$generatedAt = now()->format('d/m/Y H:i:s');
|
||||
|
||||
$html = view('pdf.import-log', compact('stats', 'issues', 'generatedAt'))->render();
|
||||
|
||||
return response()->streamDownload(function () use ($html) {
|
||||
echo Pdf::loadHTML($html)
|
||||
->setPaper('a4', 'portrait')
|
||||
->output();
|
||||
}, 'import-log-' . now()->format('Ymd-His') . '.pdf', ['Content-Type' => 'application/pdf']);
|
||||
}
|
||||
|
||||
public function exportCurrentAsXml()
|
||||
{
|
||||
$dataset = $this->currentDataset();
|
||||
$xml = $this->datasetToXml($dataset, 'current-app-export');
|
||||
|
||||
return response()->streamDownload(function () use ($xml) {
|
||||
echo $xml;
|
||||
}, 'termanager-export.xml', ['Content-Type' => 'application/xml; charset=UTF-8']);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.settings.xml-exchange');
|
||||
return [
|
||||
'stats' => $stats,
|
||||
'issues' => $this->importIssues,
|
||||
];
|
||||
}
|
||||
|
||||
private function currentDataset(): array
|
||||
@@ -512,7 +496,7 @@ class XmlExchange extends Component
|
||||
private function extractInsertRows(string $sql): array
|
||||
{
|
||||
$result = [];
|
||||
preg_match_all('/INSERT INTO `([^`]+)` VALUES\s*(.+?);/s', $sql, $matches, PREG_SET_ORDER);
|
||||
preg_match_all('/INSERT INTO `([^`]+)` VALUES\s*(.+);/m', $sql, $matches, PREG_SET_ORDER);
|
||||
|
||||
foreach ($matches as $match) {
|
||||
$table = $match[1];
|
||||
@@ -545,7 +529,7 @@ class XmlExchange extends Component
|
||||
$escape = false;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '\\\\') {
|
||||
if ($ch === '\\') {
|
||||
$escape = true;
|
||||
continue;
|
||||
}
|
||||
@@ -603,7 +587,7 @@ class XmlExchange extends Component
|
||||
$escape = false;
|
||||
continue;
|
||||
}
|
||||
if ($ch === '\\\\') {
|
||||
if ($ch === '\\') {
|
||||
$escape = true;
|
||||
continue;
|
||||
}
|
||||
@@ -645,7 +629,8 @@ class XmlExchange extends Component
|
||||
if (str_starts_with($raw, "'") && str_ends_with($raw, "'")) {
|
||||
$v = substr($raw, 1, -1);
|
||||
$v = str_replace(["\\\\", "\\'", '\\"', '\\n', '\\r', '\\t'], ['\\', "'", '"', "\n", "\r", "\t"], $v);
|
||||
return html_entity_decode($v, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$v = html_entity_decode($v, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
return $this->normalizeUnicodeQuotes($v);
|
||||
}
|
||||
|
||||
if (is_numeric($raw)) {
|
||||
@@ -655,6 +640,15 @@ class XmlExchange extends Component
|
||||
return $raw;
|
||||
}
|
||||
|
||||
private function normalizeUnicodeQuotes(string $value): string
|
||||
{
|
||||
return str_replace(
|
||||
["\u{2018}", "\u{2019}", "\u{2032}", "\u{2035}", "\u{201C}", "\u{201D}", "\u{201E}", "\u{2033}", "\u{2036}"],
|
||||
["'", "'", "'", "'", '"', '"', '"', '"', '"'],
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
private function datesFromLegacyAnnoLabel(string $label): array
|
||||
{
|
||||
if (preg_match('/(\d{4})\s*-\s*(\d{4})/', $label, $m)) {
|
||||
@@ -709,7 +703,7 @@ class XmlExchange extends Component
|
||||
|
||||
$settings = $xml->addChild('settings');
|
||||
foreach ($dataset['settings'] as $key => $value) {
|
||||
$settings->addChild($key, htmlspecialchars((string) $value, ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($settings, $key, (string) $value);
|
||||
}
|
||||
|
||||
$zonesNode = $xml->addChild('zones');
|
||||
@@ -718,7 +712,7 @@ class XmlExchange extends Component
|
||||
if (isset($zone['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $zone['legacy_id']);
|
||||
}
|
||||
$node->addChild('nome', htmlspecialchars((string) ($zone['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'nome', (string) ($zone['nome'] ?? ''));
|
||||
$node->addChild('attivo', (string) ((int) ($zone['attivo'] ?? 1)));
|
||||
}
|
||||
|
||||
@@ -728,7 +722,7 @@ class XmlExchange extends Component
|
||||
if (isset($tipologia['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $tipologia['legacy_id']);
|
||||
}
|
||||
$node->addChild('nome', htmlspecialchars((string) ($tipologia['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'nome', (string) ($tipologia['nome'] ?? ''));
|
||||
$node->addChild('attivo', (string) ((int) ($tipologia['attivo'] ?? 1)));
|
||||
}
|
||||
|
||||
@@ -738,8 +732,8 @@ class XmlExchange extends Component
|
||||
if (isset($proclamatore['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $proclamatore['legacy_id']);
|
||||
}
|
||||
$node->addChild('nome', htmlspecialchars((string) ($proclamatore['nome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('cognome', htmlspecialchars((string) ($proclamatore['cognome'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'nome', (string) ($proclamatore['nome'] ?? ''));
|
||||
$this->addXmlText($node, 'cognome', (string) ($proclamatore['cognome'] ?? ''));
|
||||
$node->addChild('attivo', (string) ((int) ($proclamatore['attivo'] ?? 1)));
|
||||
}
|
||||
|
||||
@@ -749,11 +743,11 @@ class XmlExchange extends Component
|
||||
if (isset($territorio['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $territorio['legacy_id']);
|
||||
}
|
||||
$node->addChild('numero', htmlspecialchars((string) ($territorio['numero'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'numero', (string) ($territorio['numero'] ?? ''));
|
||||
$node->addChild('legacy_zona_id', (string) ($territorio['legacy_zona_id'] ?? ($territorio['zona_id'] ?? '')));
|
||||
$node->addChild('legacy_tipologia_id', (string) ($territorio['legacy_tipologia_id'] ?? ($territorio['tipologia_id'] ?? '')));
|
||||
$node->addChild('confini', htmlspecialchars((string) ($territorio['confini'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$node->addChild('note', htmlspecialchars((string) ($territorio['note'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'confini', (string) ($territorio['confini'] ?? ''));
|
||||
$this->addXmlText($node, 'note', (string) ($territorio['note'] ?? ''));
|
||||
$node->addChild('attivo', (string) ((int) ($territorio['attivo'] ?? 1)));
|
||||
$node->addChild('prioritario', (string) ((int) ($territorio['prioritario'] ?? 0)));
|
||||
}
|
||||
@@ -764,7 +758,7 @@ class XmlExchange extends Component
|
||||
if (isset($anno['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $anno['legacy_id']);
|
||||
}
|
||||
$node->addChild('label', htmlspecialchars((string) ($anno['label'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'label', (string) ($anno['label'] ?? ''));
|
||||
$node->addChild('start_date', (string) ($anno['start_date'] ?? ''));
|
||||
$node->addChild('end_date', (string) ($anno['end_date'] ?? ''));
|
||||
}
|
||||
@@ -775,7 +769,7 @@ class XmlExchange extends Component
|
||||
if (isset($campagna['legacy_id'])) {
|
||||
$node->addAttribute('legacy_id', (string) $campagna['legacy_id']);
|
||||
}
|
||||
$node->addChild('descrizione', htmlspecialchars((string) ($campagna['descrizione'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'descrizione', (string) ($campagna['descrizione'] ?? ''));
|
||||
$node->addChild('start_date', (string) ($campagna['start_date'] ?? ''));
|
||||
$node->addChild('end_date', (string) ($campagna['end_date'] ?? ''));
|
||||
}
|
||||
@@ -793,9 +787,18 @@ class XmlExchange extends Component
|
||||
$node->addChild('assigned_at', (string) ($assegnazione['assigned_at'] ?? ''));
|
||||
$node->addChild('returned_at', (string) ($assegnazione['returned_at'] ?? ''));
|
||||
$node->addChild('counted_in_campaign', (string) ((int) ($assegnazione['counted_in_campaign'] ?? 0)));
|
||||
$node->addChild('note', htmlspecialchars((string) ($assegnazione['note'] ?? ''), ENT_QUOTES | ENT_XML1, 'UTF-8'));
|
||||
$this->addXmlText($node, 'note', (string) ($assegnazione['note'] ?? ''));
|
||||
}
|
||||
|
||||
return $xml->asXML() ?: '';
|
||||
}
|
||||
|
||||
private function addXmlText(\SimpleXMLElement $parent, string $name, string $value): \SimpleXMLElement
|
||||
{
|
||||
$child = $parent->addChild($name);
|
||||
$dom = dom_import_simplexml($child);
|
||||
$dom->textContent = $value;
|
||||
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace App\Models;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Assegnazione extends Model
|
||||
@@ -103,18 +102,19 @@ class Assegnazione extends Model
|
||||
return null;
|
||||
}
|
||||
|
||||
$months = max(1, (int) Setting::getValue('assignment_link_ttl_hours', 1));
|
||||
return route('assignments.pdf.viewer', [
|
||||
'assignment' => $this->id,
|
||||
'code' => $this->ensurePdfAccessCode(),
|
||||
]);
|
||||
}
|
||||
|
||||
$url = URL::temporarySignedRoute(
|
||||
'assignments.pdf.viewer',
|
||||
now()->addMonths($months),
|
||||
[
|
||||
'assignment' => $this->id,
|
||||
'code' => $this->ensurePdfAccessCode(),
|
||||
]
|
||||
);
|
||||
public function shortPdfUrl(): ?string
|
||||
{
|
||||
if (! $this->is_aperta || ! $this->territorio?->pdf_path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $url;
|
||||
return route('assignments.pdf.short', ['code' => $this->ensurePdfAccessCode()]);
|
||||
}
|
||||
|
||||
// ─── Scopes ─────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user