2026-05-26 08:14:29 +02:00
|
|
|
<?php
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
use App\Models\AppSetting;
|
2026-05-26 08:14:29 +02:00
|
|
|
use App\Models\Documento;
|
2026-05-28 09:34:28 +02:00
|
|
|
use App\Models\DocumentoCartella;
|
2026-05-26 08:14:29 +02:00
|
|
|
use App\Models\Individuo;
|
|
|
|
|
use App\Models\Gruppo;
|
|
|
|
|
use App\Models\Evento;
|
2026-05-28 09:34:28 +02:00
|
|
|
use App\Models\StorageRepository;
|
2026-06-09 11:02:01 +02:00
|
|
|
use App\Models\Tag;
|
2026-05-26 08:14:29 +02:00
|
|
|
use App\Models\TipologiaDocumento;
|
2026-05-28 09:34:28 +02:00
|
|
|
use App\Services\StorageRepositoryService;
|
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
|
use Illuminate\Http\RedirectResponse;
|
2026-05-26 08:14:29 +02:00
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
|
|
|
|
|
|
class DocumentoController extends Controller
|
|
|
|
|
{
|
2026-05-28 09:34:28 +02:00
|
|
|
public function __construct(
|
|
|
|
|
private readonly StorageRepositoryService $repoService
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
private function resolveStorageDisk(?Documento $documento = null): string
|
|
|
|
|
{
|
|
|
|
|
if ($documento && $documento->repository_id) {
|
|
|
|
|
return 'local';
|
|
|
|
|
}
|
|
|
|
|
if ($documento && $documento->storage_disk) {
|
|
|
|
|
return $documento->storage_disk;
|
|
|
|
|
}
|
|
|
|
|
return AppSetting::getDocumentiStorageDisk();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
public function index(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeRead('documenti');
|
2026-05-28 09:34:28 +02:00
|
|
|
|
|
|
|
|
$folderId = $request->get('folder_id');
|
|
|
|
|
$repoId = $request->get('repository_id');
|
|
|
|
|
|
2026-06-09 11:02:01 +02:00
|
|
|
$query = Documento::with(['user', 'target', 'cartella', 'repository', 'tags'])
|
2026-05-28 09:34:28 +02:00
|
|
|
->orderByDesc('created_at');
|
|
|
|
|
|
2026-06-09 11:02:01 +02:00
|
|
|
if ($request->filled('tag')) {
|
|
|
|
|
$tagSlugs = (array) $request->input('tag');
|
|
|
|
|
$query->withAnyTags($tagSlugs);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
if ($repoId) {
|
|
|
|
|
$query->where('repository_id', $repoId);
|
|
|
|
|
} elseif ($folderId) {
|
|
|
|
|
$query->where('cartella_id', $folderId);
|
|
|
|
|
} else {
|
2026-06-17 13:50:41 +02:00
|
|
|
$query->whereNull('repository_id')->whereNull('cartella_id');
|
2026-05-28 09:34:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$documenti = $query->paginate(20);
|
2026-06-09 11:02:01 +02:00
|
|
|
$allTags = Tag::orderBy('name')->get();
|
2026-05-28 09:34:28 +02:00
|
|
|
$cartelle = DocumentoCartella::with('children')
|
|
|
|
|
->whereNull('parent_id')
|
|
|
|
|
->orderBy('nome')
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
$currentFolder = $folderId ? DocumentoCartella::find($folderId) : null;
|
|
|
|
|
$breadcrumb = $currentFolder ? $currentFolder->ancestors() : [];
|
|
|
|
|
|
|
|
|
|
$sottoCartelle = $folderId
|
|
|
|
|
? DocumentoCartella::where('parent_id', $currentFolder->id)->orderBy('nome')->get()
|
|
|
|
|
: DocumentoCartella::whereNull('parent_id')->orderBy('nome')->get();
|
|
|
|
|
|
|
|
|
|
$currentRepo = $repoId ? StorageRepository::find($repoId) : null;
|
|
|
|
|
$repositories = StorageRepository::attivi()->get();
|
2026-05-26 08:14:29 +02:00
|
|
|
|
|
|
|
|
$individui = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
|
|
|
|
$gruppi = Gruppo::orderBy('nome')->get();
|
|
|
|
|
$eventi = Evento::orderBy('nome_evento')->get();
|
|
|
|
|
$mailingLists = \App\Models\MailingList::orderBy('nome')->get();
|
|
|
|
|
$tipologie = TipologiaDocumento::attive();
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
$cartelleMoveOptions = DocumentoCartella::whereNull('parent_id')
|
|
|
|
|
->with('children')
|
|
|
|
|
->orderBy('nome')
|
|
|
|
|
->get()
|
|
|
|
|
->map(fn($f) => [
|
|
|
|
|
'id' => $f->id,
|
|
|
|
|
'nome' => $f->nome,
|
|
|
|
|
'children' => $f->children->map(fn($c) => ['id' => $c->id, 'nome' => $c->nome]),
|
|
|
|
|
]);
|
|
|
|
|
|
2026-06-10 14:03:43 +02:00
|
|
|
$vista = \App\Models\VistaReport::where('user_id', auth()->id())
|
|
|
|
|
->where('tipo', 'documenti')
|
|
|
|
|
->where('is_default', true)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
$userVistas = \App\Models\VistaReport::where('user_id', auth()->id())
|
|
|
|
|
->where('tipo', 'documenti')
|
|
|
|
|
->orderBy('is_default', 'desc')
|
|
|
|
|
->orderBy('nome')
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
$allColumnDefs = [
|
|
|
|
|
['key' => 'nome_file', 'label' => 'Nome'],
|
|
|
|
|
['key' => 'tipologia', 'label' => 'Tipologia'],
|
|
|
|
|
['key' => 'dimensione', 'label' => 'Dimensione'],
|
|
|
|
|
['key' => 'visibilita', 'label' => 'Contesto'],
|
|
|
|
|
['key' => 'tag', 'label' => 'Tag'],
|
|
|
|
|
['key' => 'created_at', 'label' => 'Data'],
|
|
|
|
|
];
|
|
|
|
|
$defaultVisible = ['nome_file', 'tipologia', 'dimensione', 'visibilita', 'tag', 'created_at'];
|
|
|
|
|
$visibleColumns = $vista && !empty($vista->colonne_visibili)
|
|
|
|
|
? $vista->colonne_visibili
|
|
|
|
|
: $defaultVisible;
|
|
|
|
|
|
|
|
|
|
$tableColumns = array_map(fn($col) => [
|
|
|
|
|
'key' => $col['key'],
|
|
|
|
|
'label' => $col['label'],
|
|
|
|
|
'visible' => in_array($col['key'], $visibleColumns),
|
|
|
|
|
], $allColumnDefs);
|
|
|
|
|
$entityType = 'documenti';
|
|
|
|
|
$columnWidths = $vista && $vista->colonne_larghezze ? $vista->colonne_larghezze : [];
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
return view('documenti.index', compact(
|
|
|
|
|
'documenti', 'cartelle', 'currentFolder', 'breadcrumb', 'sottoCartelle',
|
|
|
|
|
'currentRepo', 'repositories',
|
|
|
|
|
'individui', 'gruppi', 'eventi', 'mailingLists', 'tipologie',
|
2026-06-10 14:03:43 +02:00
|
|
|
'cartelleMoveOptions', 'allTags',
|
|
|
|
|
'vista', 'visibleColumns', 'tableColumns', 'entityType', 'columnWidths',
|
|
|
|
|
'userVistas'
|
2026-05-28 09:34:28 +02:00
|
|
|
));
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
2026-06-16 21:35:10 +02:00
|
|
|
public function create()
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
$individui = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
|
|
|
|
$gruppi = Gruppo::orderBy('nome')->get();
|
|
|
|
|
$eventi = Evento::orderBy('nome_evento')->get();
|
|
|
|
|
$tipologie = TipologiaDocumento::attive();
|
|
|
|
|
return view('documenti.create', compact('individui', 'gruppi', 'eventi', 'tipologie'));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
public function edit($documento)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
2026-06-09 11:02:01 +02:00
|
|
|
$documento = Documento::with(['target', 'tags'])->findOrFail($documento);
|
2026-05-26 08:14:29 +02:00
|
|
|
$individui = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
|
|
|
|
$gruppi = Gruppo::orderBy('nome')->get();
|
|
|
|
|
$eventi = Evento::orderBy('nome_evento')->get();
|
|
|
|
|
$mailingLists = \App\Models\MailingList::orderBy('nome')->get();
|
|
|
|
|
$tipologie = TipologiaDocumento::attive();
|
2026-06-09 11:02:01 +02:00
|
|
|
$tags = Tag::orderBy('name')->get();
|
|
|
|
|
$selectedTags = $documento->tags->pluck('id')->toArray();
|
|
|
|
|
return view('documenti.edit', compact('documento', 'individui', 'gruppi', 'eventi', 'mailingLists', 'tipologie', 'tags', 'selectedTags'));
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function options(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$tipo = $request->get('tipo');
|
|
|
|
|
$options = '';
|
|
|
|
|
|
|
|
|
|
switch ($tipo) {
|
|
|
|
|
case 'individuo':
|
|
|
|
|
$items = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$options .= '<option value="' . $item->id . '">' . e($item->cognome . ' ' . $item->nome) . '</option>';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'gruppo':
|
|
|
|
|
$items = Gruppo::orderBy('nome')->get();
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$options .= '<option value="' . $item->id . '">' . e($item->nome) . '</option>';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'evento':
|
|
|
|
|
$items = Evento::orderBy('nome_evento')->get();
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$options .= '<option value="' . $item->id . '">' . e($item->nome_evento) . '</option>';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'mailing':
|
|
|
|
|
$items = \App\Models\MailingList::orderBy('nome')->get();
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$options .= '<option value="' . $item->id . '">' . e($item->nome) . '</option>';
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response($options);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function update(Request $request, $documento)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
$documento = Documento::findOrFail($documento);
|
|
|
|
|
|
|
|
|
|
$tipologieValidi = TipologiaDocumento::opzioni();
|
|
|
|
|
$tipologieRule = 'in:' . implode(',', $tipologieValidi);
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'nome_file' => 'required|string|max:255',
|
|
|
|
|
'tipologia' => 'required|' . $tipologieRule,
|
2026-06-17 13:50:41 +02:00
|
|
|
'contesto_tipo' => 'nullable|in:individuo,gruppo,evento,mailing',
|
2026-05-26 08:14:29 +02:00
|
|
|
'visibilita_target_id' => 'nullable|integer',
|
|
|
|
|
'note' => 'nullable|string',
|
2026-06-09 11:02:01 +02:00
|
|
|
'tags' => 'nullable|array',
|
|
|
|
|
'tags.*' => 'exists:tags,id',
|
2026-05-26 08:14:29 +02:00
|
|
|
]);
|
|
|
|
|
|
2026-06-17 13:50:41 +02:00
|
|
|
$contestoTipo = $data['contesto_tipo'] ?? null;
|
|
|
|
|
$data['visibilita'] = $contestoTipo ?: 'pubblico';
|
2026-05-26 08:14:29 +02:00
|
|
|
|
2026-06-17 13:50:41 +02:00
|
|
|
if ($contestoTipo) {
|
2026-05-26 08:14:29 +02:00
|
|
|
$typeMap = [
|
|
|
|
|
'individuo' => Individuo::class,
|
|
|
|
|
'gruppo' => Gruppo::class,
|
|
|
|
|
'evento' => Evento::class,
|
|
|
|
|
'mailing' => \App\Models\MailingList::class,
|
|
|
|
|
];
|
2026-06-17 13:50:41 +02:00
|
|
|
$data['visibilita_target_type'] = $typeMap[$contestoTipo] ?? null;
|
2026-05-26 08:14:29 +02:00
|
|
|
} else {
|
|
|
|
|
$data['visibilita'] = 'pubblico';
|
|
|
|
|
$data['visibilita_target_id'] = null;
|
|
|
|
|
$data['visibilita_target_type'] = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$documento->update($data);
|
|
|
|
|
|
2026-06-09 11:02:01 +02:00
|
|
|
if ($request->has('tags')) {
|
|
|
|
|
$documento->tags()->sync($request->tags);
|
|
|
|
|
} else {
|
|
|
|
|
$documento->tags()->detach();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 13:50:41 +02:00
|
|
|
$redirect = $request->input('folder_id') ? '/documenti?folder_id=' . $request->input('folder_id') : '/documenti';
|
|
|
|
|
return redirect($redirect)->with('success', 'Documento aggiornato.');
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function store(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
2026-06-17 13:50:41 +02:00
|
|
|
$tipologieValidi = TipologiaDocumento::opzioni();
|
|
|
|
|
$tipologieRule = 'in:' . implode(',', $tipologieValidi);
|
|
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
$data = $request->validate([
|
|
|
|
|
'nome_file' => 'required|string|max:255',
|
2026-06-17 13:50:41 +02:00
|
|
|
'tipologia' => 'required|' . $tipologieRule,
|
2026-05-26 08:14:29 +02:00
|
|
|
'file' => 'required|file|max:10240',
|
|
|
|
|
'visibilita' => 'required|in:pubblico,individuo,gruppo',
|
|
|
|
|
'visibilita_target_id' => 'nullable|integer',
|
|
|
|
|
'visibilita_target_type' => 'nullable|string',
|
2026-05-28 09:34:28 +02:00
|
|
|
'cartella_id' => 'nullable|integer|exists:documenti_cartelle,id',
|
|
|
|
|
'repository_id' => 'nullable|integer|exists:storage_repositories,id',
|
2026-06-17 13:50:41 +02:00
|
|
|
'tags' => 'nullable|array',
|
|
|
|
|
'tags.*' => 'exists:tags,id',
|
2026-05-26 08:14:29 +02:00
|
|
|
]);
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
$repositoryId = $data['repository_id'] ?? null;
|
|
|
|
|
|
|
|
|
|
if ($repositoryId) {
|
|
|
|
|
$repo = StorageRepository::findOrFail($repositoryId);
|
|
|
|
|
$filesystem = $this->repoService->buildFilesystem($repo);
|
|
|
|
|
if (!$filesystem) {
|
|
|
|
|
return back()->with('error', 'Impossibile connettersi al repository remoto.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$extension = $request->file('file')->getClientOriginalExtension();
|
|
|
|
|
$filename = $data['nome_file'] . '.' . ($extension ?: $request->file('file')->guessExtension());
|
|
|
|
|
$path = 'documents/' . date('Y/m/d') . '/' . $filename;
|
|
|
|
|
|
|
|
|
|
$stream = fopen($request->file('file')->getRealPath(), 'rb');
|
|
|
|
|
$filesystem->writeStream($path, $stream);
|
|
|
|
|
fclose($stream);
|
|
|
|
|
|
|
|
|
|
$filePath = $path;
|
|
|
|
|
} else {
|
|
|
|
|
$disk = $this->resolveStorageDisk();
|
|
|
|
|
$storagePath = AppSetting::getDocumentiStoragePath();
|
|
|
|
|
$filePath = $request->file('file')->store($storagePath, $disk);
|
|
|
|
|
}
|
2026-05-26 08:14:29 +02:00
|
|
|
|
|
|
|
|
$visibilitaTargetType = null;
|
|
|
|
|
if ($data['visibilita'] === 'individuo' && $data['visibilita_target_id']) {
|
|
|
|
|
$visibilitaTargetType = Individuo::class;
|
|
|
|
|
} elseif ($data['visibilita'] === 'gruppo' && $data['visibilita_target_id']) {
|
|
|
|
|
$visibilitaTargetType = Gruppo::class;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 13:50:41 +02:00
|
|
|
$doc = Documento::create([
|
2026-05-26 08:14:29 +02:00
|
|
|
'nome_file' => $data['nome_file'],
|
2026-05-28 09:34:28 +02:00
|
|
|
'file_path' => $filePath,
|
|
|
|
|
'storage_disk' => $disk ?? null,
|
2026-05-26 08:14:29 +02:00
|
|
|
'tipo' => 'upload',
|
|
|
|
|
'tipologia' => $data['tipologia'],
|
|
|
|
|
'visibilita' => $data['visibilita'],
|
|
|
|
|
'visibilita_target_id' => $data['visibilita_target_id'] ?? null,
|
|
|
|
|
'visibilita_target_type' => $visibilitaTargetType,
|
2026-05-28 09:34:28 +02:00
|
|
|
'mime_type' => $request->file('file')->getMimeType(),
|
|
|
|
|
'dimensione' => $request->file('file')->getSize(),
|
2026-05-26 08:14:29 +02:00
|
|
|
'user_id' => auth()->id(),
|
2026-05-28 09:34:28 +02:00
|
|
|
'cartella_id' => $data['cartella_id'] ?? null,
|
|
|
|
|
'repository_id' => $repositoryId,
|
2026-05-26 08:14:29 +02:00
|
|
|
]);
|
|
|
|
|
|
2026-06-17 13:50:41 +02:00
|
|
|
if ($doc && $request->has('tags')) {
|
|
|
|
|
$doc->tags()->sync($request->tags);
|
|
|
|
|
}
|
2026-05-26 08:14:29 +02:00
|
|
|
$redirect = $request->_redirect ?? url()->previous();
|
2026-06-17 13:50:41 +02:00
|
|
|
if ($request->filled('folder_id')) {
|
|
|
|
|
$redirect = '/documenti?folder_id=' . $request->input('folder_id');
|
|
|
|
|
}
|
2026-05-26 08:14:29 +02:00
|
|
|
return redirect($redirect)->with('success', 'Documento caricato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function download($documento)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeRead('documenti');
|
|
|
|
|
$documento = Documento::findOrFail($documento);
|
|
|
|
|
|
|
|
|
|
$extension = pathinfo($documento->file_path, PATHINFO_EXTENSION);
|
|
|
|
|
$filename = $extension ? $documento->nome_file . '.' . $extension : $documento->nome_file;
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
if ($documento->repository_id) {
|
|
|
|
|
$repo = StorageRepository::find($documento->repository_id);
|
|
|
|
|
if (!$repo) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
$filesystem = $this->repoService->buildFilesystem($repo);
|
|
|
|
|
if (!$filesystem || !$filesystem->fileExists($documento->file_path)) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$stream = $filesystem->readStream($documento->file_path);
|
|
|
|
|
if (!$stream) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->stream(function () use ($stream) {
|
|
|
|
|
fpassthru($stream);
|
|
|
|
|
fclose($stream);
|
|
|
|
|
}, 200, [
|
|
|
|
|
'Content-Type' => $documento->mime_type ?: 'application/octet-stream',
|
|
|
|
|
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
|
|
|
|
'Content-Length' => $documento->dimensione,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$disk = $this->resolveStorageDisk($documento);
|
|
|
|
|
if (!Storage::disk($disk)->exists($documento->file_path)) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Storage::disk($disk)->download(
|
2026-05-26 08:14:29 +02:00
|
|
|
$documento->file_path,
|
|
|
|
|
$filename,
|
|
|
|
|
['Content-Type' => $documento->mime_type]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function preview($documento)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeRead('documenti');
|
|
|
|
|
$documento = Documento::findOrFail($documento);
|
|
|
|
|
|
2026-05-26 11:47:36 +02:00
|
|
|
$previewableMimeTypes = [
|
|
|
|
|
'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
|
|
|
|
|
'application/pdf',
|
|
|
|
|
'text/plain', 'text/html', 'text/csv',
|
|
|
|
|
];
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
if (!in_array($documento->mime_type, $previewableMimeTypes)) {
|
|
|
|
|
return response()->view('documenti.preview-fallback', [
|
|
|
|
|
'documento' => $documento,
|
|
|
|
|
])->header('X-Preview-Fallback', 'true');
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
if ($documento->repository_id) {
|
|
|
|
|
$repo = StorageRepository::find($documento->repository_id);
|
|
|
|
|
if (!$repo) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
$filesystem = $this->repoService->buildFilesystem($repo);
|
|
|
|
|
if (!$filesystem || !$filesystem->fileExists($documento->file_path)) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$stream = $filesystem->readStream($documento->file_path);
|
|
|
|
|
if (!$stream) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->stream(function () use ($stream) {
|
|
|
|
|
fpassthru($stream);
|
|
|
|
|
fclose($stream);
|
|
|
|
|
}, 200, [
|
|
|
|
|
'Content-Type' => $documento->mime_type,
|
|
|
|
|
'Content-Disposition' => 'inline; filename="' . basename($documento->file_path) . '"',
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$disk = $this->resolveStorageDisk($documento);
|
|
|
|
|
if (!Storage::disk($disk)->exists($documento->file_path)) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$fullPath = Storage::disk($disk)->path($documento->file_path);
|
|
|
|
|
return response()->file($fullPath, ['Content-Type' => $documento->mime_type]);
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function destroy($documento)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeDelete('documenti');
|
|
|
|
|
$documento = Documento::findOrFail($documento);
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
if ($documento->repository_id) {
|
|
|
|
|
$repo = StorageRepository::find($documento->repository_id);
|
|
|
|
|
if ($repo) {
|
|
|
|
|
$filesystem = $this->repoService->buildFilesystem($repo);
|
|
|
|
|
if ($filesystem && $filesystem->fileExists($documento->file_path)) {
|
|
|
|
|
$filesystem->delete($documento->file_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$disk = $this->resolveStorageDisk($documento);
|
|
|
|
|
if (Storage::disk($disk)->exists($documento->file_path)) {
|
|
|
|
|
Storage::disk($disk)->delete($documento->file_path);
|
|
|
|
|
}
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$documento->delete();
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
$redirect = request('_redirect', url()->previous());
|
2026-05-26 08:14:29 +02:00
|
|
|
return redirect($redirect)->with('success', 'Documento eliminato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function checkLinks($documento)
|
|
|
|
|
{
|
|
|
|
|
$documento = Documento::findOrFail($documento);
|
|
|
|
|
$links = [];
|
|
|
|
|
|
|
|
|
|
if (in_array($documento->visibilita, ['individuo', 'gruppo', 'evento']) && $documento->visibilita_target_id) {
|
|
|
|
|
if ($documento->visibilita === 'individuo') {
|
|
|
|
|
$target = Individuo::find($documento->visibilita_target_id);
|
|
|
|
|
if ($target) {
|
|
|
|
|
$links[] = [
|
|
|
|
|
'type' => 'individuo',
|
|
|
|
|
'name' => $target->cognome . ' ' . $target->nome,
|
|
|
|
|
'url' => '/individui/' . $target->id,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
} elseif ($documento->visibilita === 'gruppo') {
|
|
|
|
|
$target = Gruppo::find($documento->visibilita_target_id);
|
|
|
|
|
if ($target) {
|
|
|
|
|
$links[] = [
|
|
|
|
|
'type' => 'gruppo',
|
|
|
|
|
'name' => $target->nome,
|
|
|
|
|
'url' => '/gruppi/' . $target->id,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
} elseif ($documento->visibilita === 'evento') {
|
|
|
|
|
$target = Evento::find($documento->visibilita_target_id);
|
|
|
|
|
if ($target) {
|
|
|
|
|
$links[] = [
|
|
|
|
|
'type' => 'evento',
|
|
|
|
|
'name' => $target->nome_evento,
|
|
|
|
|
'url' => '/eventi/' . $target->id,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($documento->tipologia === 'avatar') {
|
|
|
|
|
$linkedToAvatar = Individuo::whereHas('avatar', function($q) use ($documento) {
|
|
|
|
|
$q->where('id', $documento->id);
|
|
|
|
|
})->count();
|
|
|
|
|
if ($linkedToAvatar > 0) {
|
|
|
|
|
$links[] = [
|
|
|
|
|
'type' => 'avatar',
|
|
|
|
|
'name' => 'Avatar di ' . $linkedToAvatar . ' individuo(i)',
|
|
|
|
|
'url' => null,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($documento->eventi()->count() > 0) {
|
|
|
|
|
foreach ($documento->eventi as $evento) {
|
|
|
|
|
$links[] = [
|
|
|
|
|
'type' => 'evento',
|
|
|
|
|
'name' => 'Evento: ' . $evento->nome_evento,
|
|
|
|
|
'url' => '/eventi/' . $evento->id,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'has_links' => count($links) > 0,
|
|
|
|
|
'links' => $links,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function checkMassLinks(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
2026-06-23 08:48:11 +02:00
|
|
|
$ids = array_values(array_filter($ids, fn($v) => is_numeric($v)));
|
2026-05-26 08:14:29 +02:00
|
|
|
|
|
|
|
|
if (empty($ids)) {
|
2026-06-23 08:48:11 +02:00
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$documenti = Documento::whereIn('id', $ids)->get();
|
|
|
|
|
$allLinks = [];
|
|
|
|
|
$linkedCount = 0;
|
|
|
|
|
|
|
|
|
|
foreach ($documenti as $documento) {
|
|
|
|
|
$isLinked = false;
|
|
|
|
|
$linkName = '';
|
|
|
|
|
$linkUrl = null;
|
|
|
|
|
|
|
|
|
|
if (in_array($documento->visibilita, ['individuo', 'gruppo', 'evento']) && $documento->visibilita_target_id) {
|
|
|
|
|
$isLinked = true;
|
|
|
|
|
$linkName = $documento->nome_file . ' → ';
|
|
|
|
|
|
|
|
|
|
if ($documento->visibilita === 'individuo') {
|
|
|
|
|
$target = Individuo::find($documento->visibilita_target_id);
|
|
|
|
|
$linkName .= $target ? $target->cognome . ' ' . $target->nome : 'Individuo #' . $documento->visibilita_target_id;
|
|
|
|
|
$linkUrl = $target ? '/individui/' . $target->id : null;
|
|
|
|
|
} elseif ($documento->visibilita === 'gruppo') {
|
|
|
|
|
$target = Gruppo::find($documento->visibilita_target_id);
|
|
|
|
|
$linkName .= $target ? $target->nome : 'Gruppo #' . $documento->visibilita_target_id;
|
|
|
|
|
$linkUrl = $target ? '/gruppi/' . $target->id : null;
|
|
|
|
|
} elseif ($documento->visibilita === 'evento') {
|
|
|
|
|
$target = Evento::find($documento->visibilita_target_id);
|
|
|
|
|
$linkName .= $target ? $target->nome_evento : 'Evento #' . $documento->visibilita_target_id;
|
|
|
|
|
$linkUrl = $target ? '/eventi/' . $target->id : null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($documento->tipologia === 'avatar') {
|
|
|
|
|
$linkedToAvatar = Individuo::whereHas('avatar', function($q) use ($documento) {
|
|
|
|
|
$q->where('id', $documento->id);
|
|
|
|
|
})->count();
|
|
|
|
|
if ($linkedToAvatar > 0) {
|
|
|
|
|
$isLinked = true;
|
|
|
|
|
$allLinks[] = [
|
|
|
|
|
'name' => $documento->nome_file . ' → Avatar di ' . $linkedToAvatar . ' individuo(i)',
|
|
|
|
|
'url' => null,
|
|
|
|
|
];
|
|
|
|
|
$linkedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($documento->eventi()->count() > 0) {
|
|
|
|
|
$eventi = $documento->eventi;
|
|
|
|
|
foreach ($eventi as $evento) {
|
|
|
|
|
$allLinks[] = [
|
|
|
|
|
'name' => $documento->nome_file . ' → Evento: ' . $evento->nome_evento,
|
|
|
|
|
'url' => '/eventi/' . $evento->id,
|
|
|
|
|
];
|
|
|
|
|
$linkedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($isLinked && $documento->tipologia !== 'avatar' && $linkName) {
|
|
|
|
|
$allLinks[] = [
|
|
|
|
|
'name' => $linkName,
|
|
|
|
|
'url' => $linkUrl,
|
|
|
|
|
];
|
|
|
|
|
$linkedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'linked_count' => $linkedCount,
|
|
|
|
|
'links' => $allLinks,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
public function massDownload(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeRead('documenti');
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
2026-06-23 08:48:11 +02:00
|
|
|
$ids = array_values(array_filter($ids, fn($v) => is_numeric($v)));
|
2026-05-28 09:34:28 +02:00
|
|
|
|
|
|
|
|
if (empty($ids)) {
|
|
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$documenti = Documento::whereIn('id', $ids)->get();
|
|
|
|
|
|
|
|
|
|
if ($documenti->isEmpty()) {
|
|
|
|
|
return back()->with('error', 'Nessun documento trovato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$zip = new \ZipArchive();
|
|
|
|
|
$zipPath = tempnam(sys_get_temp_dir(), 'docs_') . '.zip';
|
|
|
|
|
|
|
|
|
|
if ($zip->open($zipPath, \ZipArchive::CREATE) !== true) {
|
|
|
|
|
return back()->with('error', 'Impossibile creare l\'archivio ZIP.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$addedCount = 0;
|
|
|
|
|
foreach ($documenti as $documento) {
|
|
|
|
|
$extension = pathinfo($documento->file_path, PATHINFO_EXTENSION);
|
|
|
|
|
$filename = $extension ? $documento->nome_file . '.' . $extension : $documento->nome_file;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if ($documento->repository_id) {
|
|
|
|
|
$repo = StorageRepository::find($documento->repository_id);
|
|
|
|
|
if ($repo) {
|
|
|
|
|
$filesystem = $this->repoService->buildFilesystem($repo);
|
|
|
|
|
if ($filesystem && $filesystem->fileExists($documento->file_path)) {
|
|
|
|
|
$stream = $filesystem->readStream($documento->file_path);
|
|
|
|
|
if ($stream) {
|
|
|
|
|
$zip->addFromString($filename, stream_get_contents($stream));
|
|
|
|
|
fclose($stream);
|
|
|
|
|
$addedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$disk = $this->resolveStorageDisk($documento);
|
|
|
|
|
if (Storage::disk($disk)->exists($documento->file_path)) {
|
|
|
|
|
$zip->addFile(Storage::disk($disk)->path($documento->file_path), $filename);
|
|
|
|
|
$addedCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
\Illuminate\Support\Facades\Log::warning('ZIP download skipped file: ' . $documento->id . ' - ' . $e->getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$zip->close();
|
|
|
|
|
|
|
|
|
|
if ($addedCount === 0) {
|
|
|
|
|
unlink($zipPath);
|
|
|
|
|
return back()->with('error', 'Nessun file disponibile per il download.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->download($zipPath, 'documenti.zip')->deleteFileAfterSend(true);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
public function massDestroy(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeDelete('documenti');
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
2026-06-23 08:48:11 +02:00
|
|
|
$ids = array_values(array_filter($ids, fn($v) => is_numeric($v)));
|
2026-05-28 09:34:28 +02:00
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
if (empty($ids)) {
|
|
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$documenti = Documento::whereIn('id', $ids)->get();
|
2026-05-28 09:34:28 +02:00
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
foreach ($documenti as $documento) {
|
2026-05-28 09:34:28 +02:00
|
|
|
if ($documento->repository_id) {
|
|
|
|
|
$repo = StorageRepository::find($documento->repository_id);
|
|
|
|
|
if ($repo) {
|
|
|
|
|
$filesystem = $this->repoService->buildFilesystem($repo);
|
|
|
|
|
if ($filesystem && $filesystem->fileExists($documento->file_path)) {
|
|
|
|
|
$filesystem->delete($documento->file_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$disk = $this->resolveStorageDisk($documento);
|
|
|
|
|
if (Storage::disk($disk)->exists($documento->file_path)) {
|
|
|
|
|
Storage::disk($disk)->delete($documento->file_path);
|
|
|
|
|
}
|
2026-05-26 08:14:29 +02:00
|
|
|
}
|
|
|
|
|
$documento->delete();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return back()->with('success', count($ids) . ' documenti eliminati.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function massUpdate(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
2026-05-28 09:34:28 +02:00
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
if (empty($ids)) {
|
|
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'tipologia' => 'nullable|string',
|
|
|
|
|
'visibilita' => 'nullable|string',
|
2026-05-28 09:34:28 +02:00
|
|
|
'visibilita_target_id' => 'nullable|integer',
|
|
|
|
|
'visibilita_target_type' => 'nullable|string',
|
2026-05-26 08:14:29 +02:00
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$tipologia = $data['tipologia'] ?? null;
|
|
|
|
|
$visibilita = $data['visibilita'] ?? null;
|
|
|
|
|
|
|
|
|
|
if (empty($tipologia) && empty($visibilita)) {
|
|
|
|
|
return back()->with('error', 'Specificare almeno un campo da aggiornare.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$updateData = [];
|
|
|
|
|
if (!empty($tipologia)) {
|
|
|
|
|
$updateData['tipologia'] = $tipologia;
|
|
|
|
|
}
|
|
|
|
|
if (!empty($visibilita)) {
|
|
|
|
|
$updateData['visibilita'] = $visibilita;
|
|
|
|
|
$typeMap = [
|
|
|
|
|
'individuo' => Individuo::class,
|
|
|
|
|
'gruppo' => Gruppo::class,
|
|
|
|
|
'evento' => Evento::class,
|
|
|
|
|
'mailing' => \App\Models\MailingList::class,
|
|
|
|
|
];
|
|
|
|
|
if (isset($typeMap[$visibilita])) {
|
|
|
|
|
$updateData['visibilita_target_type'] = $typeMap[$visibilita];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
if (!empty($data['visibilita_target_id']) && !empty($data['visibilita_target_type'])) {
|
|
|
|
|
$typeMap = [
|
|
|
|
|
'individuo' => Individuo::class,
|
|
|
|
|
'gruppo' => Gruppo::class,
|
|
|
|
|
'evento' => Evento::class,
|
|
|
|
|
'mailing' => \App\Models\MailingList::class,
|
|
|
|
|
];
|
2026-06-02 20:29:47 +02:00
|
|
|
$targetType = $data['visibilita_target_type'];
|
|
|
|
|
if (!isset($typeMap[$targetType])) {
|
|
|
|
|
return back()->with('error', 'Tipo destinazione non valido.');
|
|
|
|
|
}
|
|
|
|
|
$updateData['visibilita'] = $targetType;
|
2026-05-28 09:34:28 +02:00
|
|
|
$updateData['visibilita_target_id'] = (int) $data['visibilita_target_id'];
|
2026-06-02 20:29:47 +02:00
|
|
|
$updateData['visibilita_target_type'] = $typeMap[$targetType];
|
2026-05-28 09:34:28 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
Documento::whereIn('id', $ids)->update($updateData);
|
|
|
|
|
|
|
|
|
|
return back()->with('success', count($ids) . ' documenti aggiornati.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function massAssociate(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
2026-06-23 08:48:11 +02:00
|
|
|
$ids = array_values(array_filter($ids, fn($v) => is_numeric($v)));
|
2026-05-28 09:34:28 +02:00
|
|
|
|
2026-05-26 08:14:29 +02:00
|
|
|
if (empty($ids)) {
|
|
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'target_type' => 'required|in:individuo,gruppo,evento,mailing',
|
|
|
|
|
'target_id' => 'required|integer',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$targetType = match($data['target_type']) {
|
|
|
|
|
'individuo' => Individuo::class,
|
|
|
|
|
'gruppo' => Gruppo::class,
|
|
|
|
|
'evento' => Evento::class,
|
|
|
|
|
'mailing' => \App\Models\MailingList::class,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Documento::whereIn('id', $ids)->update([
|
|
|
|
|
'visibilita' => $data['target_type'],
|
|
|
|
|
'visibilita_target_id' => $data['target_id'],
|
|
|
|
|
'visibilita_target_type' => $targetType,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return back()->with('success', count($ids) . ' documenti associati.');
|
|
|
|
|
}
|
2026-05-28 09:34:28 +02:00
|
|
|
|
2026-06-09 11:02:01 +02:00
|
|
|
public function massTag(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
2026-06-23 08:48:11 +02:00
|
|
|
$ids = array_values(array_filter($ids, fn($v) => is_numeric($v)));
|
2026-06-09 11:02:01 +02:00
|
|
|
|
|
|
|
|
if (empty($ids)) {
|
|
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'tags' => 'required|array',
|
|
|
|
|
'tags.*' => 'exists:tags,id',
|
|
|
|
|
'mode' => 'required|in:assign,remove',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$tagIds = $data['tags'];
|
|
|
|
|
$mode = $data['mode'];
|
|
|
|
|
$count = 0;
|
|
|
|
|
|
|
|
|
|
Documento::whereIn('id', $ids)->chunk(100, function ($documenti) use ($tagIds, $mode, &$count) {
|
|
|
|
|
foreach ($documenti as $documento) {
|
|
|
|
|
if ($mode === 'assign') {
|
|
|
|
|
$documento->tags()->syncWithoutDetaching($tagIds);
|
|
|
|
|
} else {
|
|
|
|
|
$documento->tags()->detach($tagIds);
|
|
|
|
|
}
|
|
|
|
|
$count++;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$actionLabel = $mode === 'assign' ? 'assegnati' : 'rimossi';
|
|
|
|
|
return back()->with('success', "Tag $actionLabel per $count documenti.");
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 09:34:28 +02:00
|
|
|
public function massMove(Request $request): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
$idsInput = $request->input('ids', '');
|
|
|
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
|
|
|
|
|
|
|
|
|
if (empty($ids)) {
|
|
|
|
|
return back()->with('error', 'Nessun documento selezionato.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'cartella_id' => 'nullable|integer|exists:documenti_cartelle,id',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
Documento::whereIn('id', $ids)->update([
|
|
|
|
|
'cartella_id' => $data['cartella_id'] ?? null,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return back()->with('success', count($ids) . ' documenti spostati.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function moveDocumento(Request $request, int $id): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$this->authorizeWrite('documenti');
|
|
|
|
|
|
|
|
|
|
$documento = Documento::findOrFail($id);
|
|
|
|
|
|
|
|
|
|
$data = $request->validate([
|
|
|
|
|
'cartella_id' => 'nullable|integer|exists:documenti_cartelle,id',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$documento->update(['cartella_id' => $data['cartella_id'] ?? null]);
|
|
|
|
|
|
|
|
|
|
return response()->json(['success' => true]);
|
|
|
|
|
}
|
|
|
|
|
}
|