363 lines
13 KiB
PHP
363 lines
13 KiB
PHP
<?php
|
|
namespace App\Http\Controllers\Filament;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Documento;
|
|
use App\Models\DocumentoArchivioFisicoItem;
|
|
use App\Models\User;
|
|
use App\Services\Documenti\DocumentoArchivioVisibilityService;
|
|
use App\Support\StabileContext;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
class DocumentiPrintController extends Controller
|
|
{
|
|
public function download(Request $request, Documento $documento, DocumentoArchivioVisibilityService $visibilityService)
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User || ! $visibilityService->canAccessDocumento($user, $documento)) {
|
|
abort(403);
|
|
}
|
|
|
|
$pathCandidates = [];
|
|
if (is_string($documento->percorso_file) && trim($documento->percorso_file) !== '') {
|
|
$pathCandidates[] = trim($documento->percorso_file);
|
|
}
|
|
if (is_string($documento->path_file) && trim($documento->path_file) !== '') {
|
|
$pathCandidates[] = trim($documento->path_file);
|
|
}
|
|
|
|
$selectedDisk = null;
|
|
$selectedPath = null;
|
|
foreach ($pathCandidates as $candidate) {
|
|
if (Storage::disk('local')->exists($candidate)) {
|
|
$selectedDisk = 'local';
|
|
$selectedPath = $candidate;
|
|
break;
|
|
}
|
|
if (Storage::disk('public')->exists($candidate)) {
|
|
$selectedDisk = 'public';
|
|
$selectedPath = $candidate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (! $selectedDisk || ! $selectedPath) {
|
|
abort(404);
|
|
}
|
|
|
|
$filename = is_string($documento->nome_file) && trim($documento->nome_file) !== ''
|
|
? trim($documento->nome_file)
|
|
: ('documento-' . (int) $documento->id . '.pdf');
|
|
|
|
$filename = str_replace(["\r", "\n", '"'], '', $filename);
|
|
|
|
$absolutePath = Storage::disk($selectedDisk)->path($selectedPath);
|
|
|
|
if ($request->boolean('watermark')) {
|
|
$watermarkedPath = $this->buildWatermarkedPdf($absolutePath, $documento);
|
|
if (is_string($watermarkedPath) && is_file($watermarkedPath)) {
|
|
$response = response()->download($watermarkedPath, $filename);
|
|
if ($request->boolean('inline')) {
|
|
$response->setContentDisposition('inline', $filename);
|
|
}
|
|
|
|
return $response->deleteFileAfterSend(true);
|
|
}
|
|
}
|
|
|
|
if ($request->boolean('inline')) {
|
|
return response()->file($absolutePath, [
|
|
'Content-Disposition' => 'inline; filename="' . $filename . '"',
|
|
]);
|
|
}
|
|
|
|
return Storage::disk($selectedDisk)->download($selectedPath, $filename);
|
|
}
|
|
|
|
public function etichetta(Request $request, Documento $documento, DocumentoArchivioVisibilityService $visibilityService)
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User || ! $visibilityService->canAccessDocumento($user, $documento)) {
|
|
abort(403);
|
|
}
|
|
|
|
$format = $request->query('format', '11354');
|
|
$format = in_array($format, ['11354', '99014'], true) ? $format : '11354';
|
|
|
|
$defaults = $this->getDymoDefaultsForUser($format);
|
|
|
|
// Alcuni driver DYMO ruotano automaticamente la 11354: di default attiviamo un fix.
|
|
$dymoFix = $request->query('dymo_fix');
|
|
if ($dymoFix === null) {
|
|
// Default: stampa orizzontale corretta (57x32). Abilita fix solo se necessario.
|
|
$dymoFix = (bool) ($defaults['fix'] ?? false);
|
|
} else {
|
|
$dymoFix = filter_var($dymoFix, FILTER_VALIDATE_BOOL);
|
|
}
|
|
|
|
$dymoRot = $request->has('dymo_rot')
|
|
? (int) $request->query('dymo_rot')
|
|
: (int) ($defaults['rot'] ?? 0);
|
|
|
|
// rot serve solo per la modalità fix (workaround DYMO 11354). In modalità normale lo ignoriamo.
|
|
if ($format === '11354' && $dymoFix) {
|
|
if (! in_array($dymoRot, [-90, 90], true)) {
|
|
$dymoRot = -90;
|
|
}
|
|
} else {
|
|
// Normalizza a valori “sensati” (multipli di 90) per evitare input casuali.
|
|
if (! in_array($dymoRot, [-180, -90, 0, 90, 180], true)) {
|
|
$dymoRot = 0;
|
|
}
|
|
}
|
|
|
|
$dymoDx = $request->has('dymo_dx')
|
|
? (float) $request->query('dymo_dx')
|
|
: (float) ($defaults['dx'] ?? 0);
|
|
$dymoDy = $request->has('dymo_dy')
|
|
? (float) $request->query('dymo_dy')
|
|
: (float) ($defaults['dy'] ?? 0);
|
|
// Clamp: evita valori assurdi che fanno “sparire” l'etichetta.
|
|
$dymoDx = max(-15.0, min(15.0, $dymoDx));
|
|
$dymoDy = max(-15.0, min(15.0, $dymoDy));
|
|
|
|
$faldone = trim((string) $request->query('faldone', ''));
|
|
if ($faldone === '') {
|
|
$faldone = $this->extractFaldoneFromNote((string) ($documento->note ?? ''));
|
|
}
|
|
|
|
$anno = $documento->data_documento?->format('Y') ?: now()->format('Y');
|
|
$protocollo = (string) ($documento->numero_protocollo ?: ('DOC-' . $anno . '-' . str_pad((string) $documento->id, 3, '0', STR_PAD_LEFT)));
|
|
|
|
$stabileId = (int) ($documento->stabile_id ?: 0);
|
|
$codiceUnico = $stabileId > 0 ? ('STB-' . $stabileId . '-' . $protocollo) : $protocollo;
|
|
if ($faldone !== '') {
|
|
$codiceUnico .= '-F' . Str::upper($faldone);
|
|
}
|
|
|
|
$openUrl = route('filament.documenti.download', $documento);
|
|
|
|
return view('filament.print.etichetta-documento', [
|
|
'documento' => $documento,
|
|
'format' => $format,
|
|
'dymoFix' => $dymoFix,
|
|
'dymoRot' => $dymoRot,
|
|
'dymoDx' => $dymoDx,
|
|
'dymoDy' => $dymoDy,
|
|
'faldone' => $faldone,
|
|
'codiceUnico' => $codiceUnico,
|
|
'openUrl' => $openUrl,
|
|
'anno' => $anno,
|
|
'protocollo' => $protocollo,
|
|
]);
|
|
}
|
|
|
|
public function etichettaArchivioFisico(Request $request, DocumentoArchivioFisicoItem $item)
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User || ! $this->canAccessPhysicalArchiveItem($user, $item)) {
|
|
abort(403);
|
|
}
|
|
|
|
$format = $request->query('format', '11354');
|
|
$format = in_array($format, ['11354', '99014'], true) ? $format : '11354';
|
|
$defaults = $this->getDymoDefaultsForUser($format);
|
|
|
|
$dymoFix = $request->query('dymo_fix');
|
|
if ($dymoFix === null) {
|
|
$dymoFix = (bool) ($defaults['fix'] ?? false);
|
|
} else {
|
|
$dymoFix = filter_var($dymoFix, FILTER_VALIDATE_BOOL);
|
|
}
|
|
|
|
$dymoRot = $request->has('dymo_rot')
|
|
? (int) $request->query('dymo_rot')
|
|
: (int) ($defaults['rot'] ?? 0);
|
|
$dymoDx = $request->has('dymo_dx') ? (float) $request->query('dymo_dx') : (float) ($defaults['dx'] ?? 0);
|
|
$dymoDy = $request->has('dymo_dy') ? (float) $request->query('dymo_dy') : (float) ($defaults['dy'] ?? 0);
|
|
|
|
return view('filament.print.etichetta-archivio-fisico', [
|
|
'item' => $item->load(['parent', 'children', 'stabile', 'documento']),
|
|
'format' => $format,
|
|
'dymoFix' => $dymoFix,
|
|
'dymoRot' => $dymoRot,
|
|
'dymoDx' => $dymoDx,
|
|
'dymoDy' => $dymoDy,
|
|
'publicUrl' => $item->qr_code_data,
|
|
]);
|
|
}
|
|
|
|
public function showPublicArchivioFisicoItem(Request $request, DocumentoArchivioFisicoItem $item)
|
|
{
|
|
if (! $request->hasValidSignature()) {
|
|
abort(403);
|
|
}
|
|
|
|
$chain = collect();
|
|
$current = $item;
|
|
while ($current instanceof DocumentoArchivioFisicoItem) {
|
|
$chain->prepend($current);
|
|
$current = $current->parent;
|
|
}
|
|
|
|
return view('filament.pages.strumenti.archivio-fisico-show', [
|
|
'item' => $item->load(['children.children', 'stabile', 'documento', 'parent']),
|
|
'chain' => $chain,
|
|
]);
|
|
}
|
|
|
|
private function getDymoDefaultsForUser(string $format): array
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return [];
|
|
}
|
|
|
|
$imp = is_array($user->amministratore?->impostazioni ?? null)
|
|
? $user->amministratore->impostazioni
|
|
: [];
|
|
|
|
$key = 'stampe.dymo.' . $format;
|
|
$cfg = Arr::get($imp, $key);
|
|
if (! is_array($cfg)) {
|
|
// Default operativi (senza configurazione salvata):
|
|
// - 11354 (57x32): nel setup corrente risulta stabile con rot=0, dx=0, dy=3, fix=0
|
|
// - 99014: nessun offset di default
|
|
if ($format === '11354') {
|
|
return [
|
|
'fix' => false,
|
|
'rot' => 0,
|
|
'dx' => 0.0,
|
|
'dy' => 3.0,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'fix' => false,
|
|
'rot' => 0,
|
|
'dx' => 0.0,
|
|
'dy' => 0.0,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'fix' => (bool) ($cfg['fix'] ?? false),
|
|
'rot' => isset($cfg['rot']) ? (int) $cfg['rot'] : null,
|
|
'dx' => isset($cfg['dx']) ? (float) $cfg['dx'] : 0,
|
|
'dy' => isset($cfg['dy']) ? (float) $cfg['dy'] : 0,
|
|
];
|
|
}
|
|
|
|
private function extractFaldoneFromNote(string $note): string
|
|
{
|
|
$note = trim($note);
|
|
if ($note === '') {
|
|
return '';
|
|
}
|
|
|
|
// Supporta "Faldone: XYZ"
|
|
if (preg_match('/faldone\s*:\s*([^\n\r]+)/i', $note, $m)) {
|
|
return trim((string) ($m[1] ?? ''));
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
private function canAccessPhysicalArchiveItem(User $user, DocumentoArchivioFisicoItem $item): bool
|
|
{
|
|
if ($user->hasAnyRole(['super-admin', 'admin'])) {
|
|
return true;
|
|
}
|
|
|
|
return StabileContext::accessibleStabili($user)
|
|
->pluck('id')
|
|
->map(fn($value): int => (int) $value)
|
|
->contains((int) $item->stabile_id);
|
|
}
|
|
|
|
private function buildWatermarkedPdf(string $inputPath, Documento $documento): ?string
|
|
{
|
|
$ghostscript = trim((string) shell_exec('command -v gs 2>/dev/null'));
|
|
if ($ghostscript === '' || ! is_file($inputPath)) {
|
|
return null;
|
|
}
|
|
|
|
$watermarkText = $this->buildWatermarkText($documento);
|
|
if ($watermarkText === '') {
|
|
return null;
|
|
}
|
|
|
|
$outputPath = tempnam(sys_get_temp_dir(), 'doc-wm-');
|
|
if (! is_string($outputPath) || $outputPath === '') {
|
|
return null;
|
|
}
|
|
|
|
$pdfOutputPath = $outputPath . '.pdf';
|
|
@unlink($outputPath);
|
|
|
|
$postScript = '<< /BeginPage { gsave 0.90 setgray /Helvetica-Bold findfont 14 scalefont setfont 140 90 translate 38 rotate 0 0 moveto ('
|
|
. $this->escapePostScriptString($watermarkText)
|
|
. ') show grestore } >> setpagedevice';
|
|
|
|
$process = new Process([
|
|
$ghostscript,
|
|
'-q',
|
|
'-dNOPAUSE',
|
|
'-dBATCH',
|
|
'-sDEVICE=pdfwrite',
|
|
'-dCompatibilityLevel=1.4',
|
|
'-o',
|
|
$pdfOutputPath,
|
|
'-c',
|
|
$postScript,
|
|
'-f',
|
|
$inputPath,
|
|
]);
|
|
|
|
$process->run();
|
|
|
|
if (! $process->isSuccessful() || ! is_file($pdfOutputPath)) {
|
|
@unlink($pdfOutputPath);
|
|
return null;
|
|
}
|
|
|
|
return $pdfOutputPath;
|
|
}
|
|
|
|
private function buildWatermarkText(Documento $documento): string
|
|
{
|
|
$archiveReference = trim((string) ($documento->archive_reference ?? ''));
|
|
$meta = is_array($documento->metadati_archivio ?? null) ? $documento->metadati_archivio : [];
|
|
|
|
$physicalParts = array_filter([
|
|
trim((string) ($meta['supporto_fisico'] ?? '')),
|
|
trim((string) ($meta['contenitore_numero'] ?? '')),
|
|
trim((string) ($meta['busta_numero'] ?? '')) !== '' ? 'B' . trim((string) $meta['busta_numero']) : null,
|
|
trim((string) ($meta['fascicolo_numero'] ?? '')) !== '' ? 'F' . trim((string) $meta['fascicolo_numero']) : null,
|
|
]);
|
|
|
|
$physicalReference = implode(' ', $physicalParts);
|
|
|
|
return trim(implode(' | ', array_filter([
|
|
$archiveReference !== '' ? 'DIG ' . $archiveReference : null,
|
|
$physicalReference !== '' ? 'FIS ' . $physicalReference : null,
|
|
])));
|
|
}
|
|
|
|
private function escapePostScriptString(string $value): string
|
|
{
|
|
$ascii = (string) Str::of($value)->ascii()->replaceMatches('/\s+/', ' ')->trim();
|
|
|
|
return str_replace(
|
|
['\\', '(', ')'],
|
|
['\\\\', '\\(', '\\)'],
|
|
$ascii,
|
|
);
|
|
}
|
|
}
|