535 lines
18 KiB
PHP
535 lines
18 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Gruppo;
|
|
use App\Models\Individuo;
|
|
use App\Models\Diocesi;
|
|
use App\Models\Tag;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Pagination\Paginator;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class GruppoController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
$this->authorizeRead('gruppi');
|
|
|
|
$viewMode = request('view', 'table');
|
|
$perPage = in_array((int) request('perPage'), [10, 20, 25, 50, 100]) ? (int) request('perPage') : 20;
|
|
$user = auth()->user();
|
|
|
|
$vista = \App\Models\VistaReport::where('user_id', $user->id)
|
|
->where('tipo', 'gruppi')
|
|
->where('is_default', true)
|
|
->first();
|
|
|
|
$userVistas = \App\Models\VistaReport::where('user_id', $user->id)
|
|
->where('tipo', 'gruppi')
|
|
->orderBy('is_default', 'desc')
|
|
->orderBy('nome')
|
|
->get();
|
|
|
|
$allColumnDefs = [
|
|
['key' => 'nome', 'label' => 'Nome'],
|
|
['key' => 'descrizione', 'label' => 'Descrizione'],
|
|
['key' => 'diocesi', 'label' => 'Diocesi'],
|
|
['key' => 'livello', 'label' => 'Livello'],
|
|
['key' => 'padre', 'label' => 'Gruppo Padre'],
|
|
['key' => 'membri', 'label' => 'Membri'],
|
|
['key' => 'responsabili', 'label' => 'Responsabili'],
|
|
['key' => 'indirizzo', 'label' => 'Indirizzo'],
|
|
['key' => 'citta', 'label' => 'Città'],
|
|
['key' => 'telefono', 'label' => 'Telefono'],
|
|
['key' => 'email', 'label' => 'Email'],
|
|
['key' => 'tag', 'label' => 'Tag'],
|
|
];
|
|
$allColumns = array_column($allColumnDefs, 'key');
|
|
$defaultVisible = ['nome', 'livello', 'padre', 'membri', 'responsabili', 'tag'];
|
|
|
|
if ($vista && !empty($vista->colonne_visibili)) {
|
|
$visibleColumns = $vista->colonne_visibili;
|
|
} else {
|
|
$visibleColumns = $defaultVisible;
|
|
}
|
|
|
|
$tableColumns = array_map(fn($col) => [
|
|
'key' => $col['key'],
|
|
'label' => $col['label'],
|
|
'visible' => in_array($col['key'], $visibleColumns),
|
|
], $allColumnDefs);
|
|
$entityType = 'gruppi';
|
|
$columnWidths = $vista && $vista->colonne_larghezze ? $vista->colonne_larghezze : [];
|
|
|
|
$gruppiQuery = Gruppo::with(['diocesi', 'parent', 'children', 'individui', 'avatar', 'tags']);
|
|
|
|
if (request()->filled('tag')) {
|
|
$tagSlugs = (array) request()->input('tag');
|
|
$gruppiQuery->withAnyTags($tagSlugs);
|
|
}
|
|
|
|
$allGruppi = $gruppiQuery->orderBy('nome')->get()
|
|
->map(function ($gruppo) {
|
|
$depth = 0;
|
|
$parent = $gruppo->parent;
|
|
while ($parent) {
|
|
$depth++;
|
|
$parent = $parent->parent;
|
|
}
|
|
$gruppo->depth = $depth;
|
|
return $gruppo;
|
|
});
|
|
|
|
if ($viewMode === 'table') {
|
|
$sorted = $this->sortGruppiHierarchically($allGruppi);
|
|
$page = Paginator::resolveCurrentPage();
|
|
$gruppi = new LengthAwarePaginator(
|
|
$sorted->forPage($page, $perPage)->values(),
|
|
$sorted->count(),
|
|
$perPage,
|
|
$page,
|
|
['path' => Paginator::resolveCurrentPath()]
|
|
);
|
|
$gruppi->appends(request()->except('page'));
|
|
} else {
|
|
$gruppi = $allGruppi;
|
|
}
|
|
|
|
$allTags = Tag::orderBy('name')->get();
|
|
|
|
return view('gruppi.index', compact(
|
|
'gruppi',
|
|
'allColumns',
|
|
'visibleColumns',
|
|
'viewMode',
|
|
'vista',
|
|
'allTags',
|
|
'tableColumns',
|
|
'entityType',
|
|
'columnWidths',
|
|
'userVistas'
|
|
));
|
|
}
|
|
|
|
protected function sortGruppiHierarchically($gruppi)
|
|
{
|
|
$rootGroups = $gruppi->filter(fn($g) => $g->parent_id === null)->sortBy('nome');
|
|
$sorted = collect();
|
|
|
|
foreach ($rootGroups as $root) {
|
|
$sorted->push($root);
|
|
$this->addChildrenRecursive($root, $gruppi, $sorted);
|
|
}
|
|
|
|
return $sorted;
|
|
}
|
|
|
|
protected function addChildrenRecursive($parent, $allGruppi, &$sorted)
|
|
{
|
|
$children = $allGruppi->filter(fn($g) => $g->parent_id === $parent->id)->sortBy('nome');
|
|
|
|
foreach ($children as $child) {
|
|
$sorted->push($child);
|
|
$this->addChildrenRecursive($child, $allGruppi, $sorted);
|
|
}
|
|
}
|
|
|
|
public function create(Request $request)
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
$diocesi = Diocesi::orderBy('nome')->get();
|
|
$allGruppi = Gruppo::orderBy('nome')->get();
|
|
$selectedParent = $request->query('parent_id') ? Gruppo::find($request->query('parent_id')) : null;
|
|
$individui = Individuo::orderBy('cognome')->orderBy('nome')->get();
|
|
$tags = Tag::orderBy('name')->get();
|
|
return view('gruppi.create', compact('diocesi', 'allGruppi', 'selectedParent', 'individui', 'tags'));
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
$data = $request->validate([
|
|
'nome' => 'required|string|max:255',
|
|
'descrizione' => 'nullable|string',
|
|
'parent_id' => 'nullable|exists:gruppi,id',
|
|
'diocesi_id' => 'nullable|exists:diocesi,id',
|
|
'responsabile_ids' => 'nullable|array',
|
|
'responsabile_ids.*' => 'exists:individui,id',
|
|
'indirizzo_incontro' => 'nullable|string|max:500',
|
|
'cap_incontro' => 'nullable|string|max:10',
|
|
'città_incontro' => 'nullable|string|max:255',
|
|
'sigla_provincia_incontro' => 'nullable|string|max:2',
|
|
'mappa_posizione' => 'nullable|string',
|
|
'tags' => 'nullable|array',
|
|
'tags.*' => 'exists:tags,id',
|
|
]);
|
|
|
|
if (!empty($data['responsabile_ids'])) {
|
|
$data['responsabile_ids'] = json_encode(array_values($data['responsabile_ids']));
|
|
}
|
|
|
|
$gruppo = Gruppo::create($data);
|
|
|
|
if ($request->has('tags')) {
|
|
$gruppo->tags()->sync($request->tags);
|
|
}
|
|
|
|
if ($request->has('individui')) {
|
|
foreach ($request->individui as $individuoData) {
|
|
if (!empty($individuoData['individuo_id'])) {
|
|
$gruppo->individui()->attach($individuoData['individuo_id'], [
|
|
'ruolo_nel_gruppo' => $individuoData['ruolo_nel_gruppo'] ?? null,
|
|
'data_adesione' => $individuoData['data_adesione'] ?? null,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return redirect('/gruppi/' . $gruppo->id)->with('success', 'Gruppo creato con successo.');
|
|
}
|
|
|
|
public function show($id)
|
|
{
|
|
$this->authorizeRead('gruppi');
|
|
$gruppo = Gruppo::with(['parent', 'children', 'diocesi', 'individui.contatti', 'individui.documenti', 'individui' => function ($q) {
|
|
$q->withPivot('ruolo_ids', 'ruolo_nel_gruppo', 'data_adesione');
|
|
}, 'avatar', 'tags', 'eventi' => function ($q) {
|
|
$q->where('is_incontro_gruppo', true);
|
|
}, 'eventi.responsabili.contatti'])->findOrFail($id);
|
|
return view('gruppi.show', compact('gruppo'));
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
$gruppo = Gruppo::with(['individui.contatti', 'individui.documenti', 'avatar', 'tags'])->findOrFail($id);
|
|
$diocesi = Diocesi::orderBy('nome')->get();
|
|
$gruppi = Gruppo::where('id', '!=', $gruppo->id)->orderBy('nome')->get();
|
|
$membri = $gruppo->individui()->withPivot('ruolo_ids', 'ruolo_nel_gruppo', 'data_adesione')->orderBy('cognome')->orderBy('nome')->get();
|
|
$tags = Tag::orderBy('name')->get();
|
|
$selectedTags = $gruppo->tags->pluck('id')->toArray();
|
|
return view('gruppi.edit', compact('gruppo', 'diocesi', 'gruppi', 'membri', 'tags', 'selectedTags'));
|
|
}
|
|
|
|
public function update(Request $request, $id)
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
$gruppo = Gruppo::findOrFail($id);
|
|
$data = $request->validate([
|
|
'nome' => 'required|string|max:255',
|
|
'descrizione' => 'nullable|string',
|
|
'parent_id' => 'nullable|exists:gruppi,id',
|
|
'diocesi_id' => 'nullable|exists:diocesi,id',
|
|
'responsabile_ids' => 'nullable|array',
|
|
'responsabile_ids.*' => 'exists:individui,id',
|
|
'indirizzo_incontro' => 'nullable|string|max:500',
|
|
'cap_incontro' => 'nullable|string|max:10',
|
|
'città_incontro' => 'nullable|string|max:255',
|
|
'sigla_provincia_incontro' => 'nullable|string|max:2',
|
|
'mappa_posizione' => 'nullable|string',
|
|
'tags' => 'nullable|array',
|
|
'tags.*' => 'exists:tags,id',
|
|
]);
|
|
|
|
if (!empty($data['responsabile_ids'])) {
|
|
$data['responsabile_ids'] = json_encode(array_values($data['responsabile_ids']));
|
|
} else {
|
|
$data['responsabile_ids'] = null;
|
|
}
|
|
|
|
$gruppo->update($data);
|
|
|
|
if ($request->has('tags')) {
|
|
$gruppo->tags()->sync($request->tags);
|
|
} else {
|
|
$gruppo->tags()->detach();
|
|
}
|
|
|
|
if ($request->has('individui')) {
|
|
$gruppo->individui()->detach();
|
|
foreach ($request->individui as $individuoData) {
|
|
if (!empty($individuoData['individuo_id'])) {
|
|
$gruppo->individui()->attach($individuoData['individuo_id'], [
|
|
'ruolo_ids' => !empty($individuoData['ruolo_ids']) ? json_encode(array_values($individuoData['ruolo_ids'])) : null,
|
|
'data_adesione' => $individuoData['data_adesione'] ?? null,
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return redirect('/gruppi/' . $gruppo->id)->with('success', 'Gruppo aggiornato.');
|
|
}
|
|
|
|
public function destroy($id)
|
|
{
|
|
$this->authorizeDelete('gruppi');
|
|
$gruppo = Gruppo::with('children', 'individui')->findOrFail($id);
|
|
|
|
$hasChildren = $gruppo->children()->count() > 0;
|
|
$isSuperAdmin = auth()->user()->isSuperAdmin();
|
|
|
|
if ($hasChildren && !$isSuperAdmin) {
|
|
return redirect('/gruppi')->with('error', 'Solo il superamministratore può eliminare un gruppo con sottogruppi.');
|
|
}
|
|
|
|
if ($hasChildren && $isSuperAdmin) {
|
|
foreach ($gruppo->children as $child) {
|
|
$this->destroyGruppo($child);
|
|
}
|
|
}
|
|
|
|
$this->destroyGruppo($gruppo);
|
|
|
|
return redirect('/gruppi')->with('success', 'Gruppo eliminato.');
|
|
}
|
|
|
|
protected function destroyGruppo(Gruppo $gruppo): void
|
|
{
|
|
$gruppo->individui()->detach();
|
|
$gruppo->eventi()->detach();
|
|
foreach ($gruppo->documenti as $documento) {
|
|
if ($documento->file_path && file_exists(storage_path('app/public/' . $documento->file_path))) {
|
|
unlink(storage_path('app/public/' . $documento->file_path));
|
|
}
|
|
}
|
|
$gruppo->documenti()->delete();
|
|
$gruppo->delete();
|
|
}
|
|
|
|
public function allGruppi()
|
|
{
|
|
$this->authorizeRead('gruppi');
|
|
$gruppi = Gruppo::select('id', 'nome')
|
|
->orderBy('nome')
|
|
->get()
|
|
->map(function($g) {
|
|
return [
|
|
'id' => $g->id,
|
|
'nome' => $g->nome,
|
|
'full_path' => $g->getFullPathAttribute(),
|
|
];
|
|
});
|
|
return response()->json($gruppi);
|
|
}
|
|
|
|
public function individuiEmail(Gruppo $gruppo)
|
|
{
|
|
$this->authorizeRead('gruppi');
|
|
$individui = $gruppo->individui()->with('contatti')->get();
|
|
|
|
$result = $individui->map(function($ind) {
|
|
$emails = $ind->contatti->where('tipo', 'email')->pluck('valore')->toArray();
|
|
return [
|
|
'id' => $ind->id,
|
|
'codice_id' => $ind->codice_id,
|
|
'cognome' => $ind->cognome,
|
|
'nome' => $ind->nome,
|
|
'emails' => $emails,
|
|
];
|
|
});
|
|
|
|
return response()->json($result);
|
|
}
|
|
|
|
public function saveVista(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'nome' => 'required|string|max:255',
|
|
'colonne_visibili' => 'nullable|array',
|
|
'colonne_visibili.*' => 'string',
|
|
'is_default' => 'nullable|boolean',
|
|
]);
|
|
|
|
if (!empty($data['is_default'])) {
|
|
\App\Models\VistaReport::where('user_id', auth()->id())
|
|
->where('tipo', 'gruppi')
|
|
->where('is_default', true)
|
|
->update(['is_default' => false]);
|
|
}
|
|
|
|
$vista = \App\Models\VistaReport::create([
|
|
'user_id' => auth()->id(),
|
|
'nome' => $data['nome'],
|
|
'tipo' => 'gruppi',
|
|
'colonne_visibili' => $data['colonne_visibili'] ?? [],
|
|
'is_default' => !empty($data['is_default']),
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'vista_id' => $vista->id]);
|
|
}
|
|
|
|
public function deleteVista($id)
|
|
{
|
|
$vista = \App\Models\VistaReport::where('user_id', auth()->id())
|
|
->where('id', $id)
|
|
->firstOrFail();
|
|
$vista->delete();
|
|
|
|
return redirect()->route('gruppi.index')->with('success', 'Vista eliminata.');
|
|
}
|
|
|
|
public function import()
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
return view('gruppi.import');
|
|
}
|
|
|
|
public function importStore(Request $request)
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
$request->validate([
|
|
'file' => 'required|file|mimes:csv,txt',
|
|
]);
|
|
|
|
set_time_limit(0);
|
|
session_write_close();
|
|
|
|
$file = $request->file('file');
|
|
$handle = fopen($file->path(), 'r');
|
|
$header = fgetcsv($handle, 0, ',');
|
|
|
|
if ($header === false) {
|
|
fclose($handle);
|
|
return back()->with('error', 'Il file CSV non è valido.');
|
|
}
|
|
|
|
$header = array_map('trim', $header);
|
|
$imported = 0;
|
|
$skipped = 0;
|
|
$errors = [];
|
|
$rowNumber = 1;
|
|
|
|
DB::beginTransaction();
|
|
|
|
while (($row = fgetcsv($handle, 0, ',')) !== false) {
|
|
$rowNumber++;
|
|
|
|
if (count($row) < count($header)) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$data = array_combine($header, $row);
|
|
|
|
if ($data === false) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$data = array_map('trim', $data);
|
|
$nome = $data['nome'] ?? '';
|
|
|
|
if (empty($nome)) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
Gruppo::create([
|
|
'nome' => $nome,
|
|
'descrizione' => $data['descrizione'] ?? null,
|
|
'parent_id' => !empty($data['parent_id']) ? (int) $data['parent_id'] : null,
|
|
'diocesi_id' => !empty($data['diocesi_id']) ? (int) $data['diocesi_id'] : null,
|
|
'indirizzo_incontro' => $data['indirizzo_incontro'] ?? null,
|
|
'cap_incontro' => $data['cap_incontro'] ?? null,
|
|
'città_incontro' => $data['città_incontro'] ?? null,
|
|
'sigla_provincia_incontro' => $data['sigla_provincia_incontro'] ?? null,
|
|
]);
|
|
|
|
$imported++;
|
|
} catch (\Exception $e) {
|
|
Log::warning('Errore import gruppo riga ' . $rowNumber . ': ' . $e->getMessage());
|
|
$errors[] = 'Errore riga ' . $rowNumber . ': ' . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
fclose($handle);
|
|
|
|
DB::commit();
|
|
|
|
$message = 'Importati ' . $imported . ' gruppi.';
|
|
if ($skipped > 0) {
|
|
$message .= ' Saltate ' . $skipped . ' righe (vuote o incomplete).';
|
|
}
|
|
if (count($errors) > 0) {
|
|
return back()->with('error', $message . ' Errori: ' . implode('; ', $errors));
|
|
}
|
|
|
|
return redirect()->route('gruppi.index')->with('success', $message);
|
|
}
|
|
|
|
public function downloadTemplate()
|
|
{
|
|
$this->authorizeRead('gruppi');
|
|
$headers = ['nome', 'descrizione', 'parent_id', 'diocesi_id', 'indirizzo_incontro', 'cap_incontro', 'città_incontro', 'sigla_provincia_incontro'];
|
|
$example = ['Gruppo Giovani', 'Giovani dai 18 ai 30 anni', '', '', 'Via Roma 10', '00100', 'Roma', 'RM'];
|
|
|
|
$csv = implode(',', $headers) . "\n" . implode(',', $example);
|
|
|
|
return response($csv, 200, [
|
|
'Content-Type' => 'text/csv',
|
|
'Content-Disposition' => 'attachment; filename="template_gruppi.csv"',
|
|
]);
|
|
}
|
|
|
|
public function massTag(Request $request)
|
|
{
|
|
$this->authorizeWrite('gruppi');
|
|
$idsInput = $request->input('ids', '');
|
|
$ids = is_array($idsInput) ? $idsInput : (is_string($idsInput) ? explode(',', $idsInput) : []);
|
|
|
|
if (empty($ids)) {
|
|
return redirect()->route('gruppi.index')->with('error', 'Nessun gruppo selezionato.');
|
|
}
|
|
|
|
$data = $request->validate([
|
|
'tags' => 'required|array',
|
|
'tags.*' => 'exists:tags,id',
|
|
'mode' => 'required|in:assign,remove',
|
|
]);
|
|
|
|
$tagIds = $data['tags'];
|
|
$mode = $data['mode'];
|
|
$count = 0;
|
|
|
|
Gruppo::whereIn('id', $ids)->chunk(100, function ($gruppi) use ($tagIds, $mode, &$count) {
|
|
foreach ($gruppi as $gruppo) {
|
|
if ($mode === 'assign') {
|
|
$gruppo->tags()->syncWithoutDetaching($tagIds);
|
|
} else {
|
|
$gruppo->tags()->detach($tagIds);
|
|
}
|
|
$count++;
|
|
}
|
|
});
|
|
|
|
$actionLabel = $mode === 'assign' ? 'assegnati' : 'rimossi';
|
|
return redirect()->route('gruppi.index')->with('success', "Tag {$actionLabel} per {$count} gruppi.");
|
|
}
|
|
|
|
public function massElimina(Request $request)
|
|
{
|
|
$this->authorizeDelete('gruppi');
|
|
$ids = $request->input('ids', []);
|
|
if (!is_array($ids) || empty($ids)) {
|
|
return redirect()->route('gruppi.index')->with('error', 'Nessun gruppo selezionato.');
|
|
}
|
|
|
|
$gruppi = Gruppo::whereIn('id', $ids)->get();
|
|
$count = 0;
|
|
|
|
foreach ($gruppi as $gruppo) {
|
|
$gruppo->individui()->detach();
|
|
$gruppo->delete();
|
|
$count++;
|
|
}
|
|
|
|
return redirect()->route('gruppi.index')->with('success', "{$count} gruppi eliminati con successo.");
|
|
}
|
|
}
|