62 KiB
MEMORY.md - Glastree Project
Project Context
Laravel 13 / PHP 8.4 project for parish management (Glastree). Full codebase exists and is operational.
System Status
✅ FUNZIONANTE (Stable)
- Dashboard
- Individui (CRUD completo) - funziona da internet (con viste personalizzate per colonna)
- Gruppi (CRUD completo) - funziona da internet (con vista tabella/albero e colonne personalizzate)
- Documenti - funziona da internet
- Eventi (CRUD completo) - funziona da internet (con mass delete)
- Calendario eventi - funziona da internet
- Email (sistema completo)
- Impostazioni (App Settings, Logo, Email Settings)
- Mailing List - funziona da internet (con mass delete)
- Report (predefiniti e personalizzati, accesso tramite permesso 'report')
⚠️ IN COMPLETAMENTO
- Sistema Email: funziona al 95%, SMTP potrebbe necessitare credenziali corrette
- Nuova implementazione: email sync logging
Progress Summary
Eventi Enhancements (Completed)
- Migration
2026_05_12_142009_add_location_to_eventi_tableaddedluogo_indirizzoandluogo_url_mapscolumns - Evento model updated with fillable fields
- EventoController updated: index() with search + sorting, store/update with location fields
- eventi/index.blade.php: search input, sortable columns (nome_evento, created_at), delete button
- eventi/create.blade.php: "Dove" card with address + Google Maps URL fields
- eventi/edit.blade.php: "Dove" card with address + Google Maps URL fields
- eventi/show.blade.php: "Luogo" section with address, link to Google Maps, embedded map preview
Responsabile Multi-select (Completed)
- Migration
2026_05_12_000003_change_responsabile_to_multi.phpchangedresponsabile_idtoresponsabile_idsJSON - Gruppo model updated with
getResponsabiliIds()andgetResponsabili()methods - GruppoController updated: index(), store(), update(), show() for multi-select
- Views updated: show, edit, create blades with multi-select support
13 Maggio 2026 - Fixes
- Ordinamento colonne: Aggiunto ordinamento per Descrizione, Tipo, Gruppi, Responsabili
- Form edit: Aggiunti campi
tipo_eventoenotemancanti - Show view: Corretta struttura HTML della sezione documenti
14 Maggio 2026 - Fixes
- Form annidati: La sezione Documenti spostata FUORI dal form principale
- Tipologia enum: Aggiunti valori 'programma' e 'locandina' alla colonna tipologia in documenti
- Visibilita enum: Aggiunto valore 'evento' alla colonna visibilita in documenti
15 Maggio 2026 - Eventi Show
- Rimossa sezione "Dove" duplicata dalla sidebar
- Aggiunta nuova sezione "Luogo" sotto "Data e Orario" nella colonna centrale
15 Maggio 2026 - Email Settings
- Aggiunto pulsante "Test Invio Email (SMTP)" nella pagina impostazioni email
- Aggiunto metodo
testSmtp()nel controller per verificare la configurazione SMTP - Route:
POST /impostazioni/email/test-smtp - Campo password opzionale (non più required)
- Rimossi attributi required dai campi del form impostazioni email
15 Maggio 2026 - Eventi Calendar
- Nuova vista calendario con FullCalendar 6.1.10
- Routes:
GET /eventi/calendareGET /eventi/calendar/events - Metodo
calendar()ecalendarEvents()in EventoController - Sidebar: menu Eventi con sottovoci Calendario, Elenco, Nuovo Evento
- Click su evento apre la pagina show dell'evento
15 Maggio 2026 - Email UI
- Compose view: Rimossa sidebar sinistra, solo form a schermo intero
- Sidebar menu Email:
- Ordine: Nuova Email, In arrivo, Preferiti, Inviate, Bozze, Archivio, Cestino
- Cliccando "Email" apre direttamente inbox
- Bottone sincronizzazione: "Ricevi/Invia" nella pagina inbox
- Sincronizzazione: Limitata a 100 messaggi per cartella
15 Maggio 2026 - Bug Fixes
- RFC 2822: Usare
Address::create()di Symfony per gestire correttamente il display name - Route email.sync: Sostituito
route('email.sync.quick')conurl('email/sync/quick')nella vista email/index.blade.php
16 Maggio 2026 - Email UI Fixes
- Footer: Aggiunto
min-height: calc(100vh - 120px)al content-wrapper per garantire visibilità footer - Pagination: Creato custom view
vendor.pagination.simple-bootstrap-4con caratteri‹e›invece di SVG (problema scalabilità risolto)
16 Maggio 2026 - Logo Upload System
- Model:
App\Models\AppSettingcon metodigetLogoUrl(),getLogoSmallUrl(),getAppName() - Migration:
2026_05_16_000001_add_logo_path_to_app_settings- aggiungelogo_pathelogo_small_path - Controller:
ImpostazioniController- metodiuploadLogo()eremoveLogo() - Routes:
POST /impostazioni/logo,POST /impostazioni/logo/remove - Views: Sezione Logo in
/impostazioniper caricare logo grande e piccolo - Login page: Logo dinamico da
AppSetting::getLogoUrl() - Sidebar: Logo piccolo da
AppSetting::getLogoSmallUrl()con fallback a icona
16 Maggio 2026 - Email Signature Feature
- Migration:
2026_05_16_000003_add_signature_to_email_settings- aggiunge colonnasignature(longText) - Migration:
2026_05_16_000004_add_signature_enabled_to_email_settings- aggiunge colonnasignature_enabled(boolean, default true) - Model:
EmailSettingaggiornato consignatureesignature_enabledin fillable, cast boolean, metodogetSignature() - View:
admin/email-settings/index.blade.phpaggiornato con:- Titolo cambiato da "Email IMAP" a "Email Settings"
- Nuova sezione "Firma" nel menu laterale
- Toggle switch "Attiva firma automatica" per abilitare/disabilitare la firma
- Editor WYSIWYG Quill.js per la composizione della firma
- Caricamento lazy di Quill.js da CDN
- Controller:
EmailControlleraggiornato con metodoappendSignature(body, signature, enabled)che aggiunge la firma al body solo se abilitata - La firma viene aggiunta sia alle email inviate via SMTP che a quelle salvate in "Invio"
- Sidebar: Voce di menu cambiata da "Email IMAP" a "Impostazione Email"
Sistema Email - Dettagli Tecnici
Routes Eventi
GET /eventi → eventi.index (elenco)
GET /eventi/calendar → eventi.calendar (calendario)
GET /eventi/calendar/events → eventi.calendar.events (eventi per FullCalendar)
GET /eventi/create → eventi.create
POST /eventi → eventi.store
GET /eventi/{id} → eventi.show
GET /eventi/{id}/edit → eventi.edit
PUT /eventi/{id} → eventi.update
DELETE /eventi/{id} → eventi.destroy
Routes Email
POST /email/send → email.send
POST /email/sync/quick → email.sync.quick
POST /email/bulk → email.bulk
GET /email/{folder?} → email.index
POST /email/{id}/star → email.star
DELETE /email/{id} → email.destroy
Folder Types
- inbox, sent, drafts, archive, starred, trash
SMTP Configuration
- Credenziali salvate in
email_settingscon criptatura - Password decriptata con
getDecryptedPassword()ogetDecryptedSmtpPassword() - Display name validato con
filter_var($fromName, FILTER_VALIDATE_EMAIL)- se è un'email usa "Glastree"
Email Signature
- Campo
signatureinemail_settings(longText, nullable) - WYSIWYG editor Quill.js via CDN
- Metodo
appendSignature()in EmailController aggiunge "---\n" + signature al body - Applicato sia in
sendViaImap()che instoreSentMessage()
Key Decisions
Database
- Location uses separate columns:
luogo_indirizzo(varchar 500) andluogo_url_maps(text) - Map preview via Google Maps embed iframe using the address
- Sortable columns: nome_evento, created_at, descrizione_evento, tipo_recorrenza, gruppi, responsabili
- Multi-select uses Ctrl+click pattern with array input names
- SMTP defaults to Gmail if not configured
- Email name sanitized to comply with RFC 2822
- Sincronizzazione IMAP limitata a 100 messaggi per cartella
- Signature appended with separator "---\n" before the signature text
UI
- AdminLTE per tutte le viste
- Sidebar con menu navigazione
- Compose email senza sidebar sinistra
Relevant Files
Core Controllers
app/Http/Controllers/EventoController.php: eventi with extended sortingapp/Http/Controllers/EmailController.php: email system completo, signature auto-appendapp/Http/Controllers/Admin/EmailSettingsController.php: email settings + test SMTP
Models
app/Models/EmailSetting.php: getDecryptedPassword(), getDecryptedSmtpPassword(), getSignature()app/Models/Evento.php: fillable with location fieldsapp/Models/Gruppo.php: getResponsabiliIds(), getResponsabili()
Views
resources/views/admin/email-settings/index.blade.php: Email settings with signature section (Quill.js WYSIWYG)resources/views/eventi/index.blade.php: sortable columnsresources/views/eventi/edit.blade.php: fixed nested form, added tipo_evento and noteresources/views/eventi/show.blade.php: removed duplicate "Dove", added "Luogo" sectionresources/views/email/index.blade.php: mailbox view con AdminLTEresources/views/email/compose.blade.php: compose senza sidebar sinistraresources/views/layouts/adminlte.blade.php: sidebar menu Email con ordine corretto
Migrations
2026_05_16_000003_add_signature_to_email_settings.php: Adds signature column to email_settings table
Commands Utili
# Clear all caches
php artisan optimize:clear
# Recompile views
php artisan view:cache
# Check routes
php artisan route:list --name=email
Known Issues / TODO
- SMTP authentication con Infomaniak - verificare credenziali corrette
- Invio email richiede password SMTP corretta nel form impostazioni
16 Maggio 2026 - URL Hardcoded Fix (Internet Access)
- Problema: Da internet (accesso remoto) molti form e link puntavano a
http://localhostinvece del server remoto - Causa: Uso di
url('/path')in tutte le view che generava URL basati suAPP_URL=http://localhost - Soluzione: Sostituzione di tutte le occorrenze di
url('/path')con path relativi/path - Files modificati:
individui/edit.blade.php: form action, link gruppi/documenti, fetch API callsindividui/show.blade.php: link gruppi, documenti, form, fetch API callsgruppi/edit.blade.php: form action, link individui/documenti, fetch API callsgruppi/show.blade.php: link individui, eventieventi/edit.blade.php: form action, link annulla, documentieventi/show.blade.php: breadcrumbs, link gruppi/responsabili, documentieventi/create.blade.php: form action, link annullaeventi/index.blade.php: link create, reset, eventi, gruppidocumenti/index.blade.php: link targets, edit, delete, mass actions, fetch APIdocumenti/edit.blade.php: form action, download, back linkmailing-liste/edit.blade.php: fetch API callseventi/calendar.blade.php: eventSource URL
- Layout fix: Aggiunto meta tag
csrf-tokeninadminlte.blade.phphead
16 Maggio 2026 - Individui Edit Form Fix
- Problema: Form non salvava da accesso internet, il pulsante submit non funzionava
- Causa: Il button type="submit" non triggherava il form submission
- Soluzione:
- Cambiato button da
type="submit"atype="button"cononclick="document.getElementById('main-form').submit()" - Path relativi per tutti i form action
- CSRF token in meta tag del layout
- Cambiato button da
17 Maggio 2026 - Bulk Action Error Fix
- Problema: Azioni massive sulle email (sposta nel cestino) mostravano popup "errore" generico
- Causa:
authorizeWrite()lancia eccezione 403 HTML invece di JSON per richieste AJAX - Soluzione: Aggiunto try-catch in
bulkAction(),markRead()etoggleStar()per ritornare JSON con messaggio appropriato
17 Maggio 2026 - Nginx Proxy Manager Config
- Problema: Email non funziona da internet (dietro NPM)
- bootstrap/app.php: Aggiunto
$middleware->trustProxies(at: '*') - Configurazione .env:
APP_URL= APP_PROTOCOL=https FORCE_HTTPS=true TRUSTED_PROXIES=* - Middleware rimosso: ForceHttps non necessario - NPM gestisce terminazione HTTPS
- Advanced Tab NPM: Abilitare X-Forwarded-Proto e headers proxy
17 Maggio 2026 - Form Annidati Fix
- Problema: Contatti, gruppi e documenti in individui/edit non venivano salvati
- Causa: Sezioni fuori dal main-form (chiusura prematura)
- Soluzione: Spostata chiusura form dopo tutte le sezioni, aggiunto pulsante "Salva Tutte le Modifiche"
- Contatti: Aggiunto pulsante salva nella riga nuova (icona💾)
17 Maggio 2026 - Bulk Action Email Fix
- Problema: Azioni massive su email mostravano popup "errore"
- Causa: authorizeWrite() lancia eccezione 403 HTML invece di JSON
- Soluzione: try-catch in bulkAction(), markRead(), toggleStar()
(Last updated: 25 Maggio 2026 - Fix export CSV individui)
18 Maggio 2026 - AGENTS.md Update
- Aggiunta regola "Verifica Salvataggio" nelle Regole Operative AI
- Obbligo di verificare SEMPRE l'avvenuta riuscita dopo operazioni di modifica (store, update, delete, upload, sync)
25 Maggio 2026 - IndividuoController::elimina() Method
- Problema: Errore 500 "Call to undefined method App\Http\Controllers\IndividuoController::elimina()" su richiesta
POST /individui/{id}/elimina - Causa: Route definita in
routes/web.php:58ma metodoelimina()non implementato nel controller - Soluzione: Aggiunto metodo
elimina()inIndividuoControllerche:- Verifica autorizzazione con
authorizeDelete('individui') - Riceve parametri
elimina_contattieelimina_documentidal form - Elimina opzionalmente i contatti collegati se richiesto
- Elimina opzionalmente i documenti collegati (con rimozione file fisico) se richiesto
- Elimina l'individuo e redirect alla lista con messaggio di successo
- Verifica autorizzazione con
- Files modificati:
app/Http/Controllers/IndividuoController.php: aggiunto metodoelimina()dopodestroy()
25 Maggio 2026 - Fix Eliminazione Singola e Massiva
- Problema: L'eliminazione non veniva eseguita né per il singolo né per la mass action
- Cause:
$request->boolean()interpretava male i valori'true'/'false'come stringhedeleteSelected()faceva submit multipli di form separati causando redirect concorrenti
- Soluzione:
- Sostituito
$request->boolean()confilter_var(..., FILTER_VALIDATE_BOOLEAN)per parsing corretto - Aggiunto metodo
massElimina()inIndividuoControllerper gestire eliminazione multipla con singolo form - Aggiunta route
POST /individui/mass-eliminainroutes/web.php - Rifattorizzata funzione JS
deleteSelected()per usare singolo form con arrayids[]
- Sostituito
- Files modificati:
app/Http/Controllers/IndividuoController.php: correttoelimina(), aggiuntomassElimina()routes/web.php: aggiunta routeindividui/mass-eliminaresources/views/individui/index.blade.php: rifattorizzatadeleteSelected()
25 Maggio 2026 - Fix Export CSV Individui
- Problema: "Esporta Tutti CSV" e "Esporta Selezionati CSV" non funzionavano
- Cause:
exportCSV()usava:visible(selettore jQuery) conquerySelectorAll(JS nativo) che non lo supporta- Export client-side esportava solo righe nel DOM (pagina corrente con pagination), non tutti i record
- Dati limitati a 5 colonne senza contatti email/telefono
- Soluzione:
- Creato metodo
exportCSV()server-side inIndividuoControllerconStreamedResponse - Export completo con: Codice, Cognome, Nome, Data Nascita, Genere, Email, Telefono, Cellulare, Indirizzo, CAP, Città, Provincia, Note
- Email/Telefono/Cellulare recuperati dai contatti con preferenza per quelli primari
- Supporto per export tutti o solo selezionati tramite parametro
ids[] - Funzioni JS
exportCSV()eexportSelectedCSV()ora puntano a/individui/export
- Creato metodo
- Files modificati:
app/Http/Controllers/IndividuoController.php: aggiunto metodoexportCSV()con StreamedResponseroutes/web.php: aggiunta routeGET /individui/exportresources/views/individui/index.blade.php: semplificate funzioniexportCSV()eexportSelectedCSV()
(Last updated: 25 Maggio 2026 - Report system, mass actions, gruppi deletion fix)
25 Maggio 2026 - Fix Eliminazione Gruppi Padre (Superadmin Only)
- Problema: Eliminando un gruppo padre si otteneva pagina 404. Inoltre l'eliminazione di gruppi con figli era disponibile per tutti
- Cause:
- Mancato detach delle tabelle pivot (
gruppo_individuo,eventi_gruppi) prima della cancellazione - Nessun controllo per limitare l'eliminazione di gruppi con sottogruppi al superadmin
- Mancato detach delle tabelle pivot (
- Soluzione:
- Rifattorizzato
destroy()inGruppoController: aggiunta verificaisSuperAdmin()per gruppi con figli - Creato metodo
destroyGruppo()helper che esegue detach pivot, cancella documenti fisici e poi cancella il gruppo - Aggiornata vista
tree-item.blade.php: pulsante elimina disabilitato per non-superadmin su gruppi con figli
- Rifattorizzato
- Files modificati:
app/Http/Controllers/GruppoController.php: rifattorizzatodestroy(), aggiuntodestroyGruppo()resources/views/gruppi/partials/tree-item.blade.php: pulsante elimina condizionato a superadmin per gruppi padre
25 Maggio 2026 - Mass Actions Eventi e Mailing List
- Problema: Eventi e Mailing List non avevano funzionalità di eliminazione massiva
- Soluzione:
- Aggiunti checkbox di selezione, pulsante "Elimina Selezionati" e modal di conferma in
eventi/index.blade.php - Aggiunti checkbox di selezione, pulsante "Elimina Selezionati" e modal di conferma in
mailing-liste/index.blade.php - Aggiunto metodo
massElimina()inEventoControllercon detach di gruppi e responsabili - Aggiunto metodo
massElimina()inMailingListControllercon cancellazione contatti associati - Aggiunte route
POST /eventi/mass-eliminaePOST /mailing-liste/mass-elimina
- Aggiunti checkbox di selezione, pulsante "Elimina Selezionati" e modal di conferma in
- Files modificati:
app/Http/Controllers/EventoController.php: aggiuntomassElimina()app/Http/Controllers/MailingListController.php: aggiuntomassElimina()routes/web.php: aggiunte route mass-elimina per eventi e mailing-listeresources/views/eventi/index.blade.php: aggiunta selezione massiva, modal conferma, JSresources/views/mailing-liste/index.blade.php: aggiunta selezione massiva, modal conferma, JS
25 Maggio 2026 - Rimozione Menu Utilità Viste
- Modifica: Rimosso dal sidebar menu la voce "Utilità Viste" (
viste.index) - Nota: Le route e i controller per le viste rimangono funzionanti, solo il link sidebar è stato rimosso temporaneamente
25 Maggio 2026 - Sistema Report Completo
- Funzionalità: Nuova pagina Report accessibile solo dal superadmin con:
- Panoramica database: statistiche rapide (individui, gruppi, eventi, documenti, mailing list, contatti)
- 12 Report predefiniti:
- Individui per Genere
- Individui per Fascia d'Età
- Gerarchia Gruppi
- Gruppi e Membri
- Eventi del Mese
- Documenti per Tipologia
- Contatti per Tipo
- Dettaglio Mailing List
- Individui senza Contatti
- Individui senza Gruppo
- Eventi per Gruppo
- Documenti Orfani
- Report personalizzati: creazione di report custom con selezione tipo dato, colonne, ordinamento e limite
- Export CSV: ogni report può essere esportato in CSV
- Files creati:
app/Http/Controllers/ReportController.php: controller completo con 12 report + customapp/Models/ReportCustom.php: modello per report personalizzatidatabase/migrations/2026_05_25_000001_create_report_custom_table.php: migration tabella report_customresources/views/report/index.blade.php: pagina principale report con card e form creazioneresources/views/report/result.blade.php: pagina visualizzazione risultati report
- Routes aggiunte:
GET /report→ report.indexGET /report/run→ report.runGET /report/export→ report.exportGET /report/custom/{id}→ report.run-customPOST /report/custom→ report.store-customDELETE /report/custom/{id}→ report.destroy-custom
- Sidebar aggiornata: link "Report" ora punta a
route('report.index')con evidenziazione active
(Last updated: 25 Maggio 2026 - Fix sidebar @endif orfano, verifica completa sintassi)
25 Maggio 2026 - Fix Sidebar Errore Sintassi
- Problema: Errore "syntax error, unexpected token 'endif'" nella dashboard
- Causa: Dopo aver rimosso il blocco
@if(Auth::user()->isSuperAdmin())dalla sidebar per sostituirlo con@if(Auth::user()->canAccess('report')), erano rimasti un@endiforfano (linea 237) e un</li>extra (linea 239) - Soluzione:
- Rimosso
@endiforfano - Rimosso
</li>extra - Aggiunto nuovo
@if(Auth::user()->isSuperAdmin())per proteggere i menu Impostazioni e Admin
- Rimosso
- Verifica completa: Scansionati tutti i blade file del progetto - tutte le direttive
@if/@endif,@foreach/@endforeach,@php/@endphpsono correttamente bilanciate. Compilazione viste OK.
25 Maggio 2026 - Accesso Report Basato su Permessi
- Modifica: Il modulo 'report' è stato aggiunto a
User::MODULEScome nuovo modulo permissionabile - Funzionalità: Gli utenti con permesso
canAccess('report')(livello lettura o superiore) possono accedere alla pagina Report, non più solo il superadmin - Files modificati:
app/Models/User.php: aggiunto 'report' aMODULES= ['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste', 'report']app/Http/Controllers/ReportController.php: sostituiti tutti i checkisSuperAdmin()concanAccess('report')resources/views/layouts/adminlte.blade.php: sidebar mostra "Report" secanAccess('report')invece diisSuperAdmin()
- Come abilitare un utente: Dal pannello Admin → Utenti, assegnare permesso di livello ≥ 1 (Lettura) per il modulo 'report'
25 Maggio 2026 - Viste Personalizzate per Colonne (Individui e Gruppi)
- Funzionalità: Ogni utente può personalizzare quali colonne visualizzare nelle liste di Individui e Gruppi
- Individui:
- Colonne disponibili: Codice, Cognome, Nome, Email, Telefono
- Il controller
IndividuoController@index()ora passa$allColumnse$visibleColumnsalla vista - Le colonne visibili sono determinate dalla
VistaReportpredefinita dell'utente - Modal "Colonne Visibili" mostra tutte le colonne con checkbox per toggle
- Modal "Salva Vista" permette di salvare una vista con nome, colonne, e opzione predefinita
- Gruppi:
- Nuova vista tabella affiancata alla vista albero esistente
- Toggle "Tabella/Albero" nella card header
- Colonne disponibili: Nome, Descrizione, Diocesi, Livello, Gruppo Padre, Membri, Responsabili, Indirizzo, Città
- Le colonne visibili sono determinate dalla
VistaReportpredefinita per tipo 'gruppi' - Salvataggio vista dedicato con route
POST /gruppi/save-vista - Eliminazione vista con route
DELETE /gruppi/vista/{id}
- Model utilizzato:
VistaReport(tabellaviste_report) con campi:user_id,nome,tipo,colonne_visibili(JSON),is_default - Files modificati:
app/Http/Controllers/IndividuoController.php:index()passa$allColumnse$visibleColumnsapp/Http/Controllers/GruppoController.php:index()rifattorizzato per vista tabella/albero, aggiuntisaveVista()edeleteVista()routes/web.php: aggiunte routegruppi/save-vistaegruppi/delete-vistaresources/views/individui/index.blade.php: colonne condizionali con@if(in_array(..., $visibleColumns))resources/views/gruppi/index.blade.php: riscritta completamente con doppio mode (tabella/albero), modal salvataggio vista, toggle colonne
25 Maggio 2026 - Fix Vari Pagine (Individui, Eventi, Mailing, Documenti Upload)
- Individui/Gruppi: Errore
$tableColumnsnon definito risolto usando$allColumns - Eventi: Checkbox righe senza
idimpedivano il corretto funzionamento del label; aggiuntoid="row-checkbox-{{ $evento->id }}" - Mailing List: Stesso problema checkbox, corretto con
idunivoci - Documenti upload: Funzione
loadUploadTargets()chiamata ma non definita; aggiunta per popolare il dropdown target
25 Maggio 2026 - Fix Documento Avatar e Morph Map
- Problema: Caricando un documento avatar associato a un individuo, errore "Class 'individuo' not found"
- Causa: Il campo
visibilita_target_typenel database conteneva nomi brevi ('individuo', 'gruppo') invece del namespace completo - Soluzione:
- Registrata
Relation::morphMap()inAppServiceProvider@boot()per mappare nomi brevi a classi complete - Aggiornato
DocumentoController@store()per impostare correttamentevisibilita_target_typein base alla visibilità
- Registrata
- Files modificati:
app/Providers/AppServiceProvider.php: aggiunta morphMap per individuo, gruppo, evento, mailing, associazione, federazioneapp/Http/Controllers/DocumentoController.php: logica esplicita per mappare visibilità → target_type
25 Maggio 2026 - Fix Pagina Gruppi (Tabella Pivot Inesistente)
- Problema: Pagina gruppi falliva con "Table 'glastree.gruppo_responsabili' doesn't exist"
- Causa: Il modello
Gruppoaveva una relazioneresponsabili()che puntava a tabella pivot inesistente (la migration2026_05_12_000003aveva migrato a JSONresponsabile_ids) - Soluzione:
- Rimossa relazione
responsabili()daGruppo.php - Rimossa eager loading
responsabilidaGruppoController@index() - Mantenuti metodi helper
getResponsabiliIds()egetResponsabili()che leggono dal campo JSON - Corretti riferimenti in
gruppi/show.blade.phpeindividui/show.blade.phpper usaregetResponsabili()
- Rimossa relazione
- Files modificati:
app/Models/Gruppo.php: rimossa relazioneresponsabili(), mantenuti helperapp/Http/Controllers/GruppoController.php: rimossa eager loadingresponsabiliresources/views/gruppi/show.blade.php: corretto$child->responsabile→$child->getResponsabili()->first()resources/views/individui/show.blade.php: corretto$gruppo->responsabile→$gruppo->getResponsabili()->first()
25 Maggio 2026 - Fix Caricamento Documenti in Pagina Gruppi
- Funzionalità: Aggiunto modale "Carica Documento" nella pagina
gruppi/showper upload diretto - Logica Responsabili: Solo i membri del gruppo possono essere selezionati come responsabili
- In
create.blade.php: il dropdown responsabili si aggiorna dinamicamente quando si aggiungono/rimuovono membri - Funzione JS
updateResponsabiliFromMembri()sincronizza il select responsabili con i membri aggiunti
- In
- Files modificati:
resources/views/gruppi/show.blade.php: aggiunto modale upload documento, alert success/errorresources/views/gruppi/create.blade.php: logica JS per vincolare responsabili ai soli membri
(Last updated: 25 Maggio 2026 - Fix report eventi_calendario, colonna data)
25 Maggio 2026 - Fix Report Eventi Calendario
- Problema: Report "Eventi del Mese" falliva con "Unknown column 'data_inizio' in 'WHERE'"
- Causa: Il report usava
data_inizioche non esiste nella tabellaeventi. La colonna corretta èdata_specifica - Soluzione: Sostituito
data_iniziocondata_specificainreportEventiCalendario()ereportEventiPerGruppo() - Files modificati:
app/Http/Controllers/ReportController.php: righe 389-397 e 532, corretto nome colonna
(Last updated: 25 Maggio 2026 - Fix report eventi_calendario, colonna data)
25 Maggio 2026 - Fix Gruppi Table Hierarchy
- Problema: La visualizzazione a tabella dei gruppi non mostrava correttamente la gerarchia padre-figlio
- Soluzione:
- Aggiunto metodo
sortGruppiHierarchically()inGruppoControllerper ordinare i gruppi in modo gerarchico - La tabella ora mostra prima i gruppi root, poi i loro figli in ordine annidato
- Aggiunta eager loading
avatarper supportare il futuro avatar dei gruppi
- Aggiunto metodo
- Files modificati:
app/Http/Controllers/GruppoController.php: aggiuntosortGruppiHierarchically(),addChildrenRecursive(), aggiunto 'avatar' a eager loading
25 Maggio 2026 - Group Avatar Support
- Funzionalità: I gruppi ora possono avere un avatar/logo come gli individui
- Implementazione:
- Aggiunto metodo
avatar()inGruppo.php(relazione HasOne a Documento con tipologia 'avatar') - Aggiunto accessor
getAvatarUrlAttribute()per ottenere l'URL dell'avatar - Eager loading
avataraggiunto inGruppoController@index() - View
gruppi/index.blade.phpaggiornata per mostrare l'avatar nella tabella - View
gruppi/show.blade.phpaggiornata per mostrare l'avatar del gruppo - View
gruppi/create.blade.phpeedit.blade.phpaggiornate per permettere upload avatar
- Aggiunto metodo
- Files modificati:
app/Models/Gruppo.php: aggiuntoavatar(),getAvatarUrlAttribute()app/Http/Controllers/GruppoController.php: aggiunto eager loadingavatarresources/views/gruppi/index.blade.php: colonna avatar nella tabellaresources/views/gruppi/show.blade.php: avatar nella header cardresources/views/gruppi/create.blade.phpeedit.blade.php: upload avatar
26 Maggio 2026 - Event Types System (Tipologie Eventi)
- Nuova tabella:
tipologie_eventicon migration + seed dati default (catechesi, liturgia, animazione, formazione, incontro, riunione, festa, altro) - Nuovo modello:
App\Models\TipologiaEvento— identico pattern aTipologiaDocumentoeRuolo:$fillable: nome, descrizione, ordine, attiva- Metodi:
eventi()(hasMany),attive(),opzioni() - FK via
nome(string) →eventi.tipo_evento(stesso pattern Documento.tipologia → TipologiaDocumento.nome)
- Admin CRUD: 4 nuovi metodi in
ImpostazioniController:tipologieEventiStore(),tipologieEventiUpdate(),tipologieEventiDestroy(),tipologieEventiReorder() - Routes (5 nuove):
/impostazioni/tipologie-eventi/* - Impostazioni view: Nuovo tab
#tipologie-eventicon tabella sortabile, modali add/edit, stessi pattern esistenti - Sidebar: Nuovo link "Tipologie Eventi" sotto Impostazioni (solo superadmin)
- Evento model: Aggiunta relazione
tipologiaEvento()(BelongsTo via 'nome') - EventoController: Validazione cambiata da
nullable|string|max:100anullable|exists:tipologie_eventi,nomein store() e update(); create/edit passano$tipologieEventialla view - Views eventi:
create.blade.php: Dropdown "Tipo Evento" dopo Descrizione Breveedit.blade.php: Text libero → dropdown; fallback per vecchi valori non presentishow.blade.php: Badge "Tipologia" nella sezione Data e Orario; colonna rinominata "Ricorrenza"index.blade.php: Nuova colonna sortabile "Tipologia" con badge; colonna "Tipo" → "Ricorrenza"; colspan 8→9
- Files creati:
database/migrations/2026_05_26_000001_create_tipologie_eventi_table.phpapp/Models/TipologiaEvento.php
- Files modificati:
app/Http/Controllers/ImpostazioniController.phpapp/Http/Controllers/EventoController.phpapp/Models/Evento.phproutes/web.phpresources/views/impostazioni/index.blade.phpresources/views/layouts/adminlte.blade.phpresources/views/eventi/create.blade.phpresources/views/eventi/edit.blade.phpresources/views/eventi/show.blade.phpresources/views/eventi/index.blade.php
26 Maggio 2026 - ACL Fixes & Form Modules Sync
- Problema: Form admin utenti (create/edit) aveva lista moduli hardcoded e mancante di
'report'; user 4 (fbarachino) non aveva permessoreportnéis_admin - Fix form edit/create: Sostituito
@foreach(['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste'] as $module)con@foreach(\App\Models\User::MODULES as $module)in entrambe le view (edit.blade.php:76,create.blade.php:66) — ora la lista è sempre allineata alla costanteUser::MODULES - Fix user 4:
- Aggiunto
'report' => 2alle permissions (full access) - Settato
is_admin = true→isSuperAdmin()= true, bypass totale ACL canAccess('report')= true, tutti i menu sidebar ora visibili
- Aggiunto
- Nuovo comando:
app/Console/Commands/SyncUserPermissions.php—php artisan users:sync-permissions- Itera tutti gli utenti (inclusi superadmin) e aggiunge con default 0 ogni modulo mancante in
User::MODULES - Previene discrepanze future tra permissions salvate e MODULES costante
- Itera tutti gli utenti (inclusi superadmin) e aggiunge con default 0 ogni modulo mancante in
- Files modificati:
resources/views/admin/utenti/edit.blade.php: linea 76, hardcoded →User::MODULESresources/views/admin/utenti/create.blade.php: linea 66, hardcoded →User::MODULES
- File creato:
app/Console/Commands/SyncUserPermissions.php: nuovo comando artisan
- Stato sidebar user 4:
isSuperAdmin()= true → tutti i menu visibili,canAccess('report')= true
26 Maggio 2026 - ACL 'settings' Module (Impostazioni/Admin Permission)
- Problema: Non esisteva un permesso ACL specifico per accedere al menu Impostazioni e Admin. L'accesso era legato esclusivamente a
isSuperAdmin()bypass totale. Inoltre l'ImpostazioniControllerusavaauthorizeWrite('viste')come piggyback scorretto. - Soluzione: Nuovo modulo
settingsseparato, slegato davistee daisSuperAdmin(). - Modifiche:
User.php: aggiunto'settings'aMODULES(costante linea 36)adminlte.blade.phpsidebar:@if(Auth::user()->isSuperAdmin())→@if(Auth::user()->canManage('settings'))per menu Impostazioni e Admin (linee 205-226)AdminOnly.phpmiddleware:isSuperAdmin()→isSuperAdmin() || canManage('settings')— permette accesso a/admin/*anche a non-superadmin con permesso settings:2ImpostazioniController.php: rimpiazzate tutte le 16 occorrenze diauthorizeWrite('viste')eauthorizeDelete('viste')con'settings'EmailSettingsController.php: aggiunto$this->authorizeWrite('settings')in tutti i 5 metodi pubblici (index, save, testConnection, testSmtp, syncNow) — prima non avevano alcuna autorizzazione esplicitaSyncUserPermissions.phpaggiornato: ora include anche superadmin nella sync (per consistenza dati)- Eseguito
php artisan users:sync-permissions→ user 1 e 4 aggiornati consettings: 0
- Comportamento risultante:
isSuperAdmin()bypassa sempre tutto (base Controller.php)- Utente normale con
settings:2(Completo) → vede menu Impostazioni+Admin, può operare - Utente normale con
settings:0→ menu nascosto, 403 su accesso diretto URL settings:1(Lettura) non basta per operazioni di scrittura (sidebar non mostra il menu perché richiedecanManage)
- Files modificati:
app/Models/User.php: MODULES + 'settings'resources/views/layouts/adminlte.blade.php: isSuperAdmin → canManage('settings')app/Http/Middleware/AdminOnly.php: isSuperAdmin || canManage('settings')app/Http/Controllers/ImpostazioniController.php: viste → settings in 16 authorize callsapp/Http/Controllers/Admin/EmailSettingsController.php: aggiunte 5 authorizeWrite('settings')app/Console/Commands/SyncUserPermissions.php: superadmin inclusi nella sync
26 Maggio 2026 - Fix Salvataggio is_admin (Checkbox Superadmin)
- Problema: Non era possibile togliere il flag "Superadmin (bypass ACL)" dall'interfaccia admin. Il flag rimaneva sempre true dopo il salvataggio.
- Cause (3 bug concatenati):
url()assoluto nell'action del form edit (url('/admin/utenti/' . $user->id)) → da accesso remoto generavahttp://localhost/admin/utenti/4, il POST falliva silenziosamente- Nessun hidden field per unchecked checkbox → quando la checkbox veniva deselezionata, il campo
is_adminnon veniva inviato nella richiesta. Anche se il controller aveva?? false, il combinato col bug #1 impediva il salvataggio - Mancanza
old()per lo stato checkbox → su validation error redirect, la checkbox mostrava il valore DB invece del valore inviato
- Fix applicati:
edit.blade.php:url(...)→ path relativo/admin/utenti/...(linea 20); aggiunto hidden<input type="hidden" name="is_admin" value="0">prima della checkbox;old()per stato checkbox e status/role_preset_idcreate.blade.php: aggiunto hidden field come sopra; default persettings='0'(Nessuno) invece di'1'(Lettura) per nuovi utentiUtenteController.php(store + update): rimosso?? falseda$user->is_admin— ora usa direttamente$validated['is_admin'](sempre presente grazie all'hidden field)AuthController.php: aggiunti'report' => 0e'settings' => 0ai default permissions per nuove registrazioni (sia first-user che non)
- Files modificati:
resources/views/admin/utenti/edit.blade.php: form action, hidden field, old()resources/views/admin/utenti/create.blade.php: hidden field, settings default 0app/Http/Controllers/Admin/UtenteController.php: removed ?? falseapp/Http/Controllers/Auth/AuthController.php: added report/settings defaults
(Last updated: 26 Maggio 2026 - Fix Salvataggio is_admin)
26 Maggio 2026 - Sender Accounts (Mittenti Aggiuntivi solo invio)
- Nuova tabella:
sender_accountscon migration2026_05_26_000002 - Colonne:
id,email_address(UNIQUE),email_name,smtp_host,smtp_port,smtp_encryption,smtp_username,smtp_password(Crypt),reply_to,verify_email,note,is_active, timestamps - Model
SenderAccount:$fillable,$casts, metodi:getDecryptedPassword()— decripta password SMTP con fallbacksanitizeName()— pulisce il nome visualizzatogetReplyTo()— priorità:reply_to→verify_email→ nullbuildDsn()— costruisce DSN Symfony MailerbuildSymfonyEmail(to, subject, body)— costruisce email con From, Reply-TosendEmail(to, subject, body, attachmentPaths)— invia via Symfony MailersendReport(subject, body)— invia report riepilogo averify_email
- Logica Reply-To: se impostato
reply_to(es.no-reply@parrocchia.it) → quello; fallbackverify_email; nessun Reply-To se entrambi null - Report invio: dopo ogni invio massivo via sender, una email di riepilogo viene inviata a
verify_emailcon: mittente, oggetto, totale, ok, falliti, dettaglio errori - Migration
2026_05_26_000003: aggiunte colonnelog_falliti(JSON),mittente_nome,mittente_emailamailing_messaggi - Model
MailingMessaggio: aggiornato$fillablee$castsperlog_falliti(array) - CRUD in
EmailSettingsController: 4 nuovi metodi protetti daauthorizeWrite('settings'):senderStore()— crea nuovo sender con password criptatasenderUpdate()— modifica sender, password opzionalesenderDestroy()— elimina sendersenderTestSmtp()— test invio email via sender
- Routes (6 nuove):
POST /impostazioni/sender→senderStorePUT /impostazioni/sender/{id}→senderUpdateDELETE /impostazioni/sender/{id}→senderDestroyPOST /impostazioni/sender/{id}/test→senderTestSmtpGET /mailing/invio→MailingController@invio(prima mancante)POST /mailing/invio/elabora→MailingController@invioElabora(prima mancante)
EmailController@compose: passa$senderAccountsalla view; nuova view mostra dropdown "Da (mittente)" prima dei destinatariEmailController@sendmodificato:- Se
mittente_id> 0 → usaSenderAccount::sendEmail()con allegati; salta store sent message - Se
mittente_id= 0 o assente → comportamento attuale conEmailSetting - Processa allegati
allegati[]edocumenti_selezionati - Nuovo helper:
sendWithSender(),processAttachments(),resolveAttachmentPaths() sendViaImap()ora accetta$attachmentPathse li attacha all'email
- Se
MailingControllerriscritto con invio SMTP reale:invia()— invio 1:1 a ogni destinatario, log sumailing_messaggiinvioElabora()— invio massivo 1:1 per ogni email da liste multiple, log sumailing_messaggi- Tracking: stato
in_coda→inviato/parziale/fallito, conteggi ok/fail, log_falliti JSON - Summary email a
verify_emaildopo invio massivo sendViaSystem()— invio via EmailSetting (fallback quando nessun sender selezionato)resolveMailingAttachmentPaths()— risolve path file da Documenti selezionati
- View
email-settings/index.blade.php:- Nuovo tab "Mittenti Aggiuntivi" nella sidebar
- Tabella con email, nome, SMTP, Reply-To, Verify, Stato, Azioni (Modifica/Test/Elimina)
- Modal CRUD per creare/modificare sender con tutti i campi
- JS:
resetSenderForm(),editSender(id),testSenderSmtp(id) - Sezione informativa "Come funziona"
- View
email/compose.blade.php: dropdown "Da (mittente)" se ci sono sender attivi (default: Sistema) - View
mailing/nuovo.blade.php: dropdown "Mittente" se ci sono sender attivi - View
mailing/invio.blade.php: dropdown "Mittente" se ci sono sender attivi - Files creati:
database/migrations/2026_05_26_000002_create_sender_accounts_table.phpdatabase/migrations/2026_05_26_000003_add_log_falliti_to_mailing_messaggi.phpapp/Models/SenderAccount.php
- Files modificati:
app/Models/MailingMessaggio.phpapp/Http/Controllers/Admin/EmailSettingsController.phpapp/Http/Controllers/EmailController.phpapp/Http/Controllers/MailingController.phproutes/web.phpresources/views/admin/email-settings/index.blade.phpresources/views/email/compose.blade.phpresources/views/mailing/nuovo.blade.phpresources/views/mailing/invio.blade.php
26 Maggio 2026 - Export ICS/iCal Eventi
- Nuovo Service:
app/Services/IcsExportService.php— generazione file ICS RFC 5545generateSingle(Evento)— esporta un singolo eventogenerateCollection(iterable)— esporta multipli eventi in un unico file ICS- Supporto RRULE completo per eventi ricorrenti:
settimanale→FREQ=WEEKLY;BYDAY=MOmensile→FREQ=MONTHLY;BYSETPOS=1;BYDAY=SU(con opzionaleBYMONTH)annuale→FREQ=YEARLY;BYMONTH=6;BYMONTHDAY=15altro→FREQ=WEEKLY;BYDAY=MO
- Timezone
Europe/Romeper eventi con ora, DATE (all-day) per eventi senza ora - LOCATION, DESCRIPTION, SUMMARY, UID univoci
- Line folding a 75 caratteri RFC 5545
- Escape di
;,,,\,\n
- Nuovi metodi in
EventoController:exportIcs(int $id, IcsExportService $ics)— StreamedResponse download singolo eventoexportSelectedIcs(Request $request, IcsExportService $ics)— StreamedResponse con filtro per ID selezionati o ricerca corrente
- Routes:
GET /eventi/{evento}/export-ics→eventi.export-icsPOST /eventi/export-ics→eventi.export-selected-ics
- View updates:
show.blade.php: pulsante "Esporta ICS" nella toolbar azioniindex.blade.php: pulsante "Esporta ICS" nella card-tools; se ci sono righe selezionate esporta solo quelle, altrimenti esporta tutti i risultati della ricerca correntecalendar.blade.php: pulsante "Esporta ICS" che esporta tutti gli eventi
- Files creati:
app/Services/IcsExportService.php
- Files modificati:
app/Http/Controllers/EventoController.phproutes/web.phpresources/views/eventi/show.blade.phpresources/views/eventi/index.blade.phpresources/views/eventi/calendar.blade.php
27 Maggio 2026 - Mittenti Aggiuntivi in Sezione Email di /impostazioni
- Modifica: Spostata gestione sender accounts (Mittenti Aggiuntivi) da pagina dedicata
/impostazioni/emaila sottosezione della sezione Email nella pagina principale/impostazioni - Sub-tab aggiunto: pill "Mittenti Aggiuntivi" dopo Firma nei sottotab della sezione Email
- Tab-pane
email-mittenti: tabella sender con colonne Email, Nome, SMTP, Reply-To, Verify, Stato, Azioni; stessi campi CRUD della vista dedicata - Form puliti: pulsanti Elimina usano fetch AJAX (
deleteSender()) invece di form annidati per evitare HTML invalido (nested forms dentro il form principale Email) - Controller:
ImpostazioniController@indexora passa$senderAccountsalla view senderDestroy()inEmailSettingsController: ora ritorna JSON per richieste AJAX (expectsJson())- Sidebar: rimossa voce "Impostazioni Email" aggiunta in precedenza sotto Email (non serve — le impostazioni email sono accessibili dalla sezione Email in
/impostazioni) - Files modificati:
app/Http/Controllers/ImpostazioniController.php: import + compact SenderAccountresources/views/impostazioni/index.blade.php: pill Mittenti Aggiuntivi, tab-pane email-mittenti, modale sender modifica/crea, funzioni JS (resetSenderForm, editSender, deleteSender, testSenderSmtp)app/Http/Controllers/Admin/EmailSettingsController.php: senderDestroy() con expectsJson()resources/views/layouts/adminlte.blade.php: rimossa voce Impostazioni Email dal sidebar
(Last updated: 28 Maggio 2026 - AJAX browser, folder actions, Google Drive fix)
28 Maggio 2026 - AJAX Browser, Folder Actions, Google Drive + Bug Fixes
- Download/Preview proxy: Aggiunti
downloadRemote()epreviewRemote()in StorageRepositoryController — stream di file remoti per download e anteprima inline - Routes (2 nuove):
GET /storage-repositories/{id}/download,GET /storage-repositories/{id}/preview - AJAX browser: Repository nel folder tree usano
browseRepo(repoId, nome, path)via AJAX albrowseendpoint — mostra cartelle e file remoti in grid/list view (stesso formato del file manager locale) - Funzioni JS:
browseRepo(),loadRemoteContents(),renderRemoteContents()(grid/list rendering),downloadRepoFile(),previewRepoFile(),getFileIcon()(MIME-type mapping),showLocalContent()— navigazione remota senza ricarica pagina - View toggle remoto:
setView()ora ricarica i contenuti remoti in grid/list securrentRepoBrowseè attivo - Folder tree rename: Pulsante penna su hover → SweetAlert2 prompt →
PUT /documenti/cartelle/{id}con{nome} - Folder tree delete: Pulsante X rossa su hover → SweetAlert2 confirm →
DELETE /documenti/cartelle/{id}(con warning se documenti/sottocartelle presenti nel controller) - Folder move:
DocumentoCartellaController@updateora accettaparent_id(con validazione anti-ciclo) — preparato per drag-and-drop futuro - CSS:
.folder-actionsvisibili su hover del.list-group-item - Google Drive fix: Aggiunto
normalizePath()inStorageRepositoryService— converte/→''per compatibilità cross-adapter (Google Drive non accetta/come root path). Usato intestConnection()elistContents() - testRepo() fix: Passato
thisesplicitamente invece dievent.target(che puntava all'<i>interno causando icona duplicata e disabilitazione errata del bottone) - Stato: ✅ 18/19 test pass (solo ExampleTest pre-esistente). Sintassi PHP OK, view cache OK.
27 Maggio 2026 - Password Reset & Profile
- Rimosso registration: Eliminata view
auth/register.blade.php, rimossi metodi register da AuthController; rimosso link "Non hai un account? Registrati" dalla login - Aggiunto password reset:
- Routes password.request, password.email, password.reset, password.update in routes/web.php
- Metodi showForgotForm(), sendResetLink(), showResetForm(), reset() in AuthController
Usermodel: implementato CanResetPassword con sendPasswordResetNotification() custom via Symfony Mailer su EmailSetting SMTP- Views: auth/forgot-password.blade.php, auth/reset-password.blade.php, auth/emails/reset-password.blade.php
- Login fix: Checkbox "Ricordami" ora usa
$request->has('remember')invece di hardcoded false - Login update: Aggiunto link "Password dimenticata?" che punta a
route('password.request') - ProfileController: Creato con
show()(view auth.profile) eupdatePassword()con validazionecurrent_passwordePassword::min(8) - Layout fix: Link "Profilo" in adminlte.blade.php ora punta a
route('profile')invece di# - Profile view: Già esistente
auth/profile.blade.phpcon form cambio password funzionante
27 Maggio 2026 - Report Enhancements
- ReportColumnRegistry (
app/Helpers/ReportColumnRegistry.php): Registry centralizzato di colonne disponibili per entity type (individui, gruppi, eventi, documenti, contatti) con supporto per colonne dirette, accessor e relazioni (dot notation tipocontatti.email,gruppi.nome). MetodoresolveRelationValue()per risolvere valori su relazioni nidificate. - ReportController fix:
storeCustom(): colonne salvate correttamente come array in config['columns']; supporto per array da Select2 o comma-separatedrunCustomReport(): eager loading automatico per relazioni in colonne dot notation; risoluzione valori viaReportColumnRegistry::resolveRelationValue()exportCSV(): supporto per custom_id con filename da slug del nome report; BOM UTF-8 per ExcelresolveReport(): metodo centralizzato per match report type + tipologia filterrun(): supporto per custom_id per eseguire report personalizzati dalla stessa route
- 6 nuovi report predefiniti (18 totali): documenti_completo, scadenze_documenti, individui_completo, gruppi_completo, eventi_completo, contatti_completo
- Tipologia filter: I report
documenti_completoescadenze_documentihanno dropdown inline Select2 per filtro tipologia nella card dell'index - Report index view: Select2 multiselect per colonne, Select2 tipologia filter, sort direction, nuovi bottoni CSV export per custom reports
27 Maggio 2026 - Document Manager Rework (Cartelle + Storage Settings)
- Migrations (3 nuove):
2026_05_27_000001_create_documenti_cartelle_table.php— tabelladocumenti_cartelleconparent_idFK self,nome, timestamps2026_05_27_000002_add_cartella_id_to_documenti.php— colonnacartella_idFK →documenti_cartelle.idsudocumenti2026_05_27_000003_add_documenti_storage_to_app_settings.php— colonnedocumenti_storage_disk(defaultlocal) edocumenti_storage_path(defaultdocumenti) inapp_settings
- Model
DocumentoCartella: relazioniparent(),children(),documenti(), helperancestors(),fullPath() - Model
Documento: aggiuntocartella_idin$fillable, nuova relazionecartella()(BelongsTo → DocumentoCartella). Tutte le relazioni esistenti (eventi/gruppi/individui/target/user/tenant) INTATTE - Model
AppSetting: aggiunti metodigetDocumentiStorageDisk(),getDocumentiStoragePath(),getDocumentiStorageAbsolutePath() DocumentoCartellaController: CRUD store/update/destroy + endpoint JSONbreadcrumb(id)per breadcrumb navigazioneDocumentoControllerriscritto:index(): filtra perfolder_idGET param, passa$cartelle(struttura nidificata),$currentFolder,$breadcrumballa viewstore(): accettacartella_idopzionale, storage su disco dinamico da AppSettingdownload/preview/destroy/massDestroy: storage disk risolto dinamicamente viaresolveStorageDisk()- Tutti gli altri metodi (update, checkLinks, massUpdate, massAssociate) INALTERATI
ImpostazioniController:saveAppSettings()valida e salvadocumenti_storage_disk(local|public) edocumenti_storage_path- View
documenti/index.blade.php: riscritto come file manager a 3 colonne:- Sidebar sinistra (col-md-3): folder tree con
list-groupe partial ricorsivo, link "Tutti i documenti", pulsante "+" Nuova Cartella - Contenuto principale (col-md-9): breadcrumb navigazione, toolbar azioni massive, griglia card (col-md-4 each) con icona MIME-type, nome, tipologia badge, dimensione, data, autore, pulsanti azioni
- Stessi 6 modali esistenti (upload, associa, cambia tipo, preview, delete confirm, mass delete confirm) + nuovo modale "Nuova Cartella" AJAX
- JS: nuova funzione per submit AJAX del form nuova cartella con reload pagina
- Sidebar sinistra (col-md-3): folder tree con
- View
documenti/_folder_tree.blade.php: partial ricorsivo con@includeper rendering folder tree annidato con indentazione progressiva - View
impostazioni/index.blade.php: nuova sezione "Repository Documenti" (tab-pane#documenti) con select disk, input path, path assoluto calcolato + alert informativo - Routes (5 nuove):
POST /documenti/cartelle→DocumentoCartellaController@storePUT /documenti/cartelle/{id}→DocumentoCartellaController@updateDELETE /documenti/cartelle/{id}→DocumentoCartellaController@destroyGET /documenti/cartelle/{id}/breadcrumb→DocumentoCartellaController@breadcrumbPOST /documenti/mass-move→DocumentoController@massMove
- Mass Move: Nuovo pulsante "Sposta selezionati in..." nella toolbar azioni massive, modal con stesso dropdown cartelle del move singolo,
massMove()controller aggiornacartella_idsuDocumento::whereIn('id', $ids), JS handler submit form/documenti/mass-movecon redirect/reload. Pulsante "+ Nuova cartella" nella modale con SweetAlert2 prompt e AJAX al backend - Cartelle miste a documenti (Opzione A): Controller
index()passa$sottoCartelle(figli della cartella corrente, o root se nessuna filtrata) alla vista; griglia mostra folder card (fas fa-folder, card-warning) prima delle document card; lista mostra righe folder (tr class=table-warning) con colonne Nome/Cartella/-/-/-/Apri prima delle righe documento; empty state "Nessun elemento presente" se entrambi vuoti - Fix nested forms: Aggiunto
</form>di chiusura permassForm(linea 391) prima dei modali. Il formmassFormavvolgeva tutto il contenuto incluse le modali, causando<form>nidificati (HTML invalido) — browser ignorava i form interni comenewFolderForm, impedendo la creazione cartelle dal "+". - Verifica: sintassi PHP ok, view cache ok, 8/9 test pass (solo ExampleTest pre-esistente fallisce)
- Files creati:
database/migrations/2026_05_27_000001_create_documenti_cartelle_table.phpdatabase/migrations/2026_05_27_000002_add_cartella_id_to_documenti.phpdatabase/migrations/2026_05_27_000003_add_documenti_storage_to_app_settings.phpapp/Models/DocumentoCartella.phpapp/Http/Controllers/DocumentoCartellaController.phpresources/views/documenti/_folder_tree.blade.php
- Files modificati:
app/Models/Documento.php: cartella_id fillable + cartella() relationapp/Models/AppSetting.php: metodi storage documentiapp/Http/Controllers/DocumentoController.php: riscritto con filtro folder + storage dinamicoapp/Http/Controllers/ImpostazioniController.php: saveAppSettings validazione storageresources/views/documenti/index.blade.php: riscritto (file manager 3 colonne)resources/views/impostazioni/index.blade.php: nuova sezione Repository Documentiroutes/web.php: 4 nuove rotte cartelle + import DocumentoCartellaController
(Last updated: 28 Maggio 2026 - Bug fix: JS syntax error, storage disk persistence)
28 Maggio 2026 - Bug Fix: JS syntax error, storage disk persistence
- Problema 1 — Vista lista non funzionante:
setView()indocumenti/index.blade.phpaveva una}extra alla riga 1183 che causavaSyntaxErrorJS. L'intero blocco<script>(1507 righe) non veniva parsato, rendendo inutilizzabili: toggle griglia/lista, renameFolder, deleteFolder, AJAX browser remoto, modali AJAX.- Fix: Rimossa la
}extra alla riga 1183.
- Fix: Rimossa la
- Problema 2 — Icone cartelle assenti/non funzionanti: Le icone rename/delete sono nel partial
_folder_tree.blade.phpcon CSS hover (.list-group-item:hover .folder-actions { display: inline-flex !important; }). Le icone erano renderizzate nel DOM ma il JS era rotto:renameFolder()edeleteFolder()mai definite.- Fix: Il fix del JS (rimozione
}extra) risolve anche questo problema.
- Fix: Il fix del JS (rimozione
- Problema 3 — Impostazione percorso locale non funzionante:
AppSetting::getDocumentiStorageDisk()veniva risolto dinamicamente sia in store che in download/preview/destroy. Cambiando il disco nelle impostazioni, i documenti caricati in precedenza non erano più accessibili (es.local→publiccausava 404 sui vecchi file).- Fix: Creata migration
2026_05_28_000001_add_storage_disk_to_documenti.phpche aggiunge colonnastorage_disk(nullable string) alla tabelladocumenti. DocumentoController@store()ora salvastorage_disksul record del documento.resolveStorageDisk($documento)ora restituisce il disco persistito se disponibile, altrimenti il default da AppSetting.- Tutti i metodi (download, preview, destroy, massDestroy) usano
resolveStorageDisk($documento)e beneficiano automaticamente del fix.
- Fix: Creata migration
- Stato: ✅ 18/19 test pass (solo ExampleTest pre-esistente).
php -lsu tutti i file modificati OK.php artisan view:cacheOK.
(Last updated: 28 Maggio 2026 - Folder icons, associa button, updateModal target, saveAppSettings fix)
28 Maggio 2026 - Folder Icons, Associa Button, UpdateModal Target, saveAppSettings Fix
- Icone cartelle sempre visibili: Rimosso
style="display:none;"da.folder-actionsin_folder_tree.blade.php— rename, move e delete sono ora sempre visibili, non solo su hover. CSS aggiornato:.list-group-item .folder-actions { display: inline-flex; }. - Aggiunto pulsante "Sposta" per cartelle: Nuovo bottone
fa-folder-opennel folder tree → funzionemoveFolder(id, nome)con SweetAlert2 prompt contenente<select>con gerarchia cartelle (esclude se stessa e figli) →PUT /documenti/cartelle/{id}conparent_id. Aggiunti datifoldersJSON in linea. - Aggiunto pulsante "Associa" per file singoli: Bottone
fa-linkin ogni card (grid) e riga (list) →showAssociateModal(id)pre-popola il modale#associateModalcon l'ID del documento singolo. - UpdateModal con target dinamico: Aggiunto
#updateTargetGroupnel modal "Cambia tipo/contesto": quando si selezionaindividuo/gruppo/evento/mailing, compare dropdown con i dati caricati dagli array JS esistenti (individui,gruppi,eventi,mailingLists). FunzionetoggleUpdateTarget(value)show/hide + popolamento. - massUpdate controller esteso:
massUpdate()inDocumentoControllerora accettavisibilita_target_idevisibilita_target_typecome parametri opzionali. Se presenti, aggiorna contesto + target sul DB con$typeMap. - Fix saveAppSettings null error:
ImpostazioniController@saveAppSettingsora preserva i valori esistenti quando i campi arrivanonull(dovuto aConvertEmptyStringsToNullmiddleware):$settings->nome_applicazione = $validated[...] ?? $settings->nome_applicazione ?? ''. Applicato a tutti i campi stringa. RisolveSQLSTATE[23000]: Column 'nome_applicazione' cannot be null. - Files modificati:
app/Http/Controllers/ImpostazioniController.php: saveAppSettings fallback valori esistentiapp/Http/Controllers/DocumentoController.php: massUpdate esteso con target fieldsresources/views/documenti/_folder_tree.blade.php: always-visible icons + move buttonresources/views/documenti/index.blade.php: CSS always-visible, updateModal target dropdown, moveFolder() JS, showAssociateModal() JS, associa button su ogni file
- Stato: ✅ 18/19 test pass.
php -lOK.php artisan view:cacheOK.
(Last updated: 28 Maggio 2026 - Single-document "Cambia tipo/contesto" + mailing list fix in updateModal)
28 Maggio 2026 - Single-document "Cambia tipo/contesto" + mailing list fix
- Aggiunto pulsante singolo "Cambia tipo/contesto" su ogni documento:
- Grid view (card footer): bottone
fa-tagdopo "Associa" - List view (colonna azioni): stesso bottone per riga
- Nuova funzione JS
showUpdateModal(id)pre-popolaupdateIdscol singolo ID
- Grid view (card footer): bottone
- Fix mailing list in updateModal:
mailingListsora mappato a{id, label}via.map(fn($m) => ['id' => $m->id, 'label' => $m->nome]) - Files modificati:
resources/views/documenti/index.blade.php: bottoni su grid+list, funzioneshowUpdateModal(), mapping mailing list corretto
- Stato: ✅
php -lOK.php artisan view:cacheOK.
28 Maggio 2026 - Folder action icons (grid/list view) + mass download ZIP
- Aggiunte icone azioni cartelle in griglia e lista (non solo sidebar tree):
- Grid view: card-footer con pulsanti Rinomina, Sposta, Elimina per ogni cartella
- List view: colonna azioni con pulsanti Rinomina, Sposta, Elimina per ogni riga cartella
- Nuova mass action "Scarica": pulsante nella toolbar azioni massive
- Crea un archivio ZIP con
ZipArchivecontenente i file selezionati - Supporta sia documenti locali che da repository remoti
- Download monouso (
response()->download()->deleteFileAfterSend(true)) - In caso di file non accessibili, li salta e include solo quelli disponibili
- Crea un archivio ZIP con
- Files modificati:
routes/web.php: nuova routePOST /documenti/mass-downloadapp/Http/Controllers/DocumentoController.php: nuovo metodomassDownload()resources/views/documenti/index.blade.php: icone cartelle in grid+list, bottone "Scarica" mass action, funzione JSmassDownload()
- Stato: ✅ 18/19 test pass (solo ExampleTest pre-esistente).
php -lOK.php artisan view:cacheOK.
28 Maggio 2026 - Fix pagina bloccata: massaAction() mancante di chiusura }
- Problema: La funzione
massAction()nel JS didocumenti/index.blade.phpaveva un}di chiusura mancante, causando:- Tutto il JS successivo (tutte le funzioni) veniva interpretato come annidato dentro massAction
- Le funzioni non erano accessibili globalmente → icone azioni click non funzionanti (renameFolder, deleteFolder, showUpdateModal, ecc.)
- La pagina risultava bloccata/non reattiva
- Causa: Introdotto nel refactoring file manager (27 Maggio), il
}finale difunction massAction()era stato perso. Il codice originale in git HEAD aveva 3}di chiusura (catch + if + function), il file corrente ne aveva solo 2. - Fix: Aggiunto
}mancante per chiuderemassAction(). - Verifica: 182
{= 182}nel blocco JS. 18/19 test pass.php -lOK.php artisan view:cacheOK.
28 Maggio 2026 - Fix: delete redirect, folder azioni non funzionanti, escaping JS
- Fix delete redirect dashboard:
DocumentoController@destroy()usavaurl('/')come default redirect → dopo eliminazione si finiva in dashboard. Cambiato aurl()->previous(). - Fix folder azioni non funzionanti: Tutti gli onclick handler per renameFolder/moveFolder/deleteFolder usavano
addslashes()che non gestisce correttamente apici nei nomi in contesto HTML → JS error silenzioso, funzioni non eseguite. Sostituito conjson_encode()che produce stringhe JS correttamente escaped con virgolette. - Fix browseRepo repo names: Stesso fix
addslashes→json_encodeper i nomi repository negli onclick. - Files modificati:
app/Http/Controllers/DocumentoController.php: destroy() redirect defaulturl()->previous()resources/views/documenti/index.blade.php: 10 onclick conjson_encodeinvece diaddslashesresources/views/documenti/_folder_tree.blade.php: 3 onclick conjson_encodeinvece diaddslashes
- Stato: ✅ 18/19 test pass.
php -lOK.php artisan view:cacheOK. JS braces 182/182.