Files
glastree/MEMORY.md
T
2026-05-27 07:29:29 +02:00

43 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_table added luogo_indirizzo and luogo_url_maps columns
  • 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.php changed responsabile_id to responsabile_ids JSON
  • Gruppo model updated with getResponsabiliIds() and getResponsabili() 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_evento e note mancanti
  • 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/calendar e GET /eventi/calendar/events
  • Metodo calendar() e calendarEvents() 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') con url('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-4 con caratteri ‹ e › invece di SVG (problema scalabilità risolto)

16 Maggio 2026 - Logo Upload System

  • Model: App\Models\AppSetting con metodi getLogoUrl(), getLogoSmallUrl(), getAppName()
  • Migration: 2026_05_16_000001_add_logo_path_to_app_settings - aggiunge logo_path e logo_small_path
  • Controller: ImpostazioniController - metodi uploadLogo() e removeLogo()
  • Routes: POST /impostazioni/logo, POST /impostazioni/logo/remove
  • Views: Sezione Logo in /impostazioni per 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 colonna signature (longText)
  • Migration: 2026_05_16_000004_add_signature_enabled_to_email_settings - aggiunge colonna signature_enabled (boolean, default true)
  • Model: EmailSetting aggiornato con signature e signature_enabled in fillable, cast boolean, metodo getSignature()
  • View: admin/email-settings/index.blade.php aggiornato 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: EmailController aggiornato con metodo appendSignature(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_settings con criptatura
  • Password decriptata con getDecryptedPassword() o getDecryptedSmtpPassword()
  • Display name validato con filter_var($fromName, FILTER_VALIDATE_EMAIL) - se è un'email usa "Glastree"

Email Signature

  • Campo signature in email_settings (longText, nullable)
  • WYSIWYG editor Quill.js via CDN
  • Metodo appendSignature() in EmailController aggiunge "---\n" + signature al body
  • Applicato sia in sendViaImap() che in storeSentMessage()

Key Decisions

Database

  • Location uses separate columns: luogo_indirizzo (varchar 500) and luogo_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

Email

  • 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 sorting
  • app/Http/Controllers/EmailController.php: email system completo, signature auto-append
  • app/Http/Controllers/Admin/EmailSettingsController.php: email settings + test SMTP

Models

  • app/Models/EmailSetting.php: getDecryptedPassword(), getDecryptedSmtpPassword(), getSignature()
  • app/Models/Evento.php: fillable with location fields
  • app/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 columns
  • resources/views/eventi/edit.blade.php: fixed nested form, added tipo_evento and note
  • resources/views/eventi/show.blade.php: removed duplicate "Dove", added "Luogo" section
  • resources/views/email/index.blade.php: mailbox view con AdminLTE
  • resources/views/email/compose.blade.php: compose senza sidebar sinistra
  • resources/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

  1. SMTP authentication con Infomaniak - verificare credenziali corrette
  2. 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://localhost invece del server remoto
  • Causa: Uso di url('/path') in tutte le view che generava URL basati su APP_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 calls
    • individui/show.blade.php: link gruppi, documenti, form, fetch API calls
    • gruppi/edit.blade.php: form action, link individui/documenti, fetch API calls
    • gruppi/show.blade.php: link individui, eventi
    • eventi/edit.blade.php: form action, link annulla, documenti
    • eventi/show.blade.php: breadcrumbs, link gruppi/responsabili, documenti
    • eventi/create.blade.php: form action, link annulla
    • eventi/index.blade.php: link create, reset, eventi, gruppi
    • documenti/index.blade.php: link targets, edit, delete, mass actions, fetch API
    • documenti/edit.blade.php: form action, download, back link
    • mailing-liste/edit.blade.php: fetch API calls
    • eventi/calendar.blade.php: eventSource URL
  • Layout fix: Aggiunto meta tag csrf-token in adminlte.blade.php head

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" a type="button" con onclick="document.getElementById('main-form').submit()"
    • Path relativi per tutti i form action
    • CSRF token in meta tag del layout

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() e toggleStar() 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:58 ma metodo elimina() non implementato nel controller
  • Soluzione: Aggiunto metodo elimina() in IndividuoController che:
    • Verifica autorizzazione con authorizeDelete('individui')
    • Riceve parametri elimina_contatti e elimina_documenti dal 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
  • Files modificati:
    • app/Http/Controllers/IndividuoController.php: aggiunto metodo elimina() dopo destroy()

25 Maggio 2026 - Fix Eliminazione Singola e Massiva

  • Problema: L'eliminazione non veniva eseguita né per il singolo né per la mass action
  • Cause:
    1. $request->boolean() interpretava male i valori 'true'/'false' come stringhe
    2. deleteSelected() faceva submit multipli di form separati causando redirect concorrenti
  • Soluzione:
    1. Sostituito $request->boolean() con filter_var(..., FILTER_VALIDATE_BOOLEAN) per parsing corretto
    2. Aggiunto metodo massElimina() in IndividuoController per gestire eliminazione multipla con singolo form
    3. Aggiunta route POST /individui/mass-elimina in routes/web.php
    4. Rifattorizzata funzione JS deleteSelected() per usare singolo form con array ids[]
  • Files modificati:
    • app/Http/Controllers/IndividuoController.php: corretto elimina(), aggiunto massElimina()
    • routes/web.php: aggiunta route individui/mass-elimina
    • resources/views/individui/index.blade.php: rifattorizzata deleteSelected()

25 Maggio 2026 - Fix Export CSV Individui

  • Problema: "Esporta Tutti CSV" e "Esporta Selezionati CSV" non funzionavano
  • Cause:
    1. exportCSV() usava :visible (selettore jQuery) con querySelectorAll (JS nativo) che non lo supporta
    2. Export client-side esportava solo righe nel DOM (pagina corrente con pagination), non tutti i record
    3. Dati limitati a 5 colonne senza contatti email/telefono
  • Soluzione:
    1. Creato metodo exportCSV() server-side in IndividuoController con StreamedResponse
    2. Export completo con: Codice, Cognome, Nome, Data Nascita, Genere, Email, Telefono, Cellulare, Indirizzo, CAP, Città, Provincia, Note
    3. Email/Telefono/Cellulare recuperati dai contatti con preferenza per quelli primari
    4. Supporto per export tutti o solo selezionati tramite parametro ids[]
    5. Funzioni JS exportCSV() e exportSelectedCSV() ora puntano a /individui/export
  • Files modificati:
    • app/Http/Controllers/IndividuoController.php: aggiunto metodo exportCSV() con StreamedResponse
    • routes/web.php: aggiunta route GET /individui/export
    • resources/views/individui/index.blade.php: semplificate funzioni exportCSV() e exportSelectedCSV()

(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:
    1. Mancato detach delle tabelle pivot (gruppo_individuo, eventi_gruppi) prima della cancellazione
    2. Nessun controllo per limitare l'eliminazione di gruppi con sottogruppi al superadmin
  • Soluzione:
    1. Rifattorizzato destroy() in GruppoController: aggiunta verifica isSuperAdmin() per gruppi con figli
    2. Creato metodo destroyGruppo() helper che esegue detach pivot, cancella documenti fisici e poi cancella il gruppo
    3. Aggiornata vista tree-item.blade.php: pulsante elimina disabilitato per non-superadmin su gruppi con figli
  • Files modificati:
    • app/Http/Controllers/GruppoController.php: rifattorizzato destroy(), aggiunto destroyGruppo()
    • 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:
    1. Aggiunti checkbox di selezione, pulsante "Elimina Selezionati" e modal di conferma in eventi/index.blade.php
    2. Aggiunti checkbox di selezione, pulsante "Elimina Selezionati" e modal di conferma in mailing-liste/index.blade.php
    3. Aggiunto metodo massElimina() in EventoController con detach di gruppi e responsabili
    4. Aggiunto metodo massElimina() in MailingListController con cancellazione contatti associati
    5. Aggiunte route POST /eventi/mass-elimina e POST /mailing-liste/mass-elimina
  • Files modificati:
    • app/Http/Controllers/EventoController.php: aggiunto massElimina()
    • app/Http/Controllers/MailingListController.php: aggiunto massElimina()
    • routes/web.php: aggiunte route mass-elimina per eventi e mailing-liste
    • resources/views/eventi/index.blade.php: aggiunta selezione massiva, modal conferma, JS
    • resources/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:
    1. Panoramica database: statistiche rapide (individui, gruppi, eventi, documenti, mailing list, contatti)
    2. 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
    3. Report personalizzati: creazione di report custom con selezione tipo dato, colonne, ordinamento e limite
    4. Export CSV: ogni report può essere esportato in CSV
  • Files creati:
    • app/Http/Controllers/ReportController.php: controller completo con 12 report + custom
    • app/Models/ReportCustom.php: modello per report personalizzati
    • database/migrations/2026_05_25_000001_create_report_custom_table.php: migration tabella report_custom
    • resources/views/report/index.blade.php: pagina principale report con card e form creazione
    • resources/views/report/result.blade.php: pagina visualizzazione risultati report
  • Routes aggiunte:
    • GET /report → report.index
    • GET /report/run → report.run
    • GET /report/export → report.export
    • GET /report/custom/{id} → report.run-custom
    • POST /report/custom → report.store-custom
    • DELETE /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 @endif orfano (linea 237) e un </li> extra (linea 239)
  • Soluzione:
    1. Rimosso @endif orfano
    2. Rimosso </li> extra
    3. Aggiunto nuovo @if(Auth::user()->isSuperAdmin()) per proteggere i menu Impostazioni e Admin
  • Verifica completa: Scansionati tutti i blade file del progetto - tutte le direttive @if/@endif, @foreach/@endforeach, @php/@endphp sono correttamente bilanciate. Compilazione viste OK.

25 Maggio 2026 - Accesso Report Basato su Permessi

  • Modifica: Il modulo 'report' è stato aggiunto a User::MODULES come 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' a MODULES = ['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste', 'report']
    • app/Http/Controllers/ReportController.php: sostituiti tutti i check isSuperAdmin() con canAccess('report')
    • resources/views/layouts/adminlte.blade.php: sidebar mostra "Report" se canAccess('report') invece di isSuperAdmin()
  • 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 $allColumns e $visibleColumns alla vista
    • Le colonne visibili sono determinate dalla VistaReport predefinita 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 VistaReport predefinita per tipo 'gruppi'
    • Salvataggio vista dedicato con route POST /gruppi/save-vista
    • Eliminazione vista con route DELETE /gruppi/vista/{id}
  • Model utilizzato: VistaReport (tabella viste_report) con campi: user_id, nome, tipo, colonne_visibili (JSON), is_default
  • Files modificati:
    • app/Http/Controllers/IndividuoController.php: index() passa $allColumns e $visibleColumns
    • app/Http/Controllers/GruppoController.php: index() rifattorizzato per vista tabella/albero, aggiunti saveVista() e deleteVista()
    • routes/web.php: aggiunte route gruppi/save-vista e gruppi/delete-vista
    • resources/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 $tableColumns non definito risolto usando $allColumns
  • Eventi: Checkbox righe senza id impedivano il corretto funzionamento del label; aggiunto id="row-checkbox-{{ $evento->id }}"
  • Mailing List: Stesso problema checkbox, corretto con id univoci
  • 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_type nel database conteneva nomi brevi ('individuo', 'gruppo') invece del namespace completo
  • Soluzione:
    1. Registrata Relation::morphMap() in AppServiceProvider@boot() per mappare nomi brevi a classi complete
    2. Aggiornato DocumentoController@store() per impostare correttamente visibilita_target_type in base alla visibilità
  • Files modificati:
    • app/Providers/AppServiceProvider.php: aggiunta morphMap per individuo, gruppo, evento, mailing, associazione, federazione
    • app/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 Gruppo aveva una relazione responsabili() che puntava a tabella pivot inesistente (la migration 2026_05_12_000003 aveva migrato a JSON responsabile_ids)
  • Soluzione:
    1. Rimossa relazione responsabili() da Gruppo.php
    2. Rimossa eager loading responsabili da GruppoController@index()
    3. Mantenuti metodi helper getResponsabiliIds() e getResponsabili() che leggono dal campo JSON
    4. Corretti riferimenti in gruppi/show.blade.php e individui/show.blade.php per usare getResponsabili()
  • Files modificati:
    • app/Models/Gruppo.php: rimossa relazione responsabili(), mantenuti helper
    • app/Http/Controllers/GruppoController.php: rimossa eager loading responsabili
    • resources/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/show per 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
  • Files modificati:
    • resources/views/gruppi/show.blade.php: aggiunto modale upload documento, alert success/error
    • resources/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_inizio che non esiste nella tabella eventi. La colonna corretta è data_specifica
  • Soluzione: Sostituito data_inizio con data_specifica in reportEventiCalendario() e reportEventiPerGruppo()
  • 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() in GruppoController per ordinare i gruppi in modo gerarchico
    • La tabella ora mostra prima i gruppi root, poi i loro figli in ordine annidato
    • Aggiunta eager loading avatar per supportare il futuro avatar dei gruppi
  • Files modificati:
    • app/Http/Controllers/GruppoController.php: aggiunto sortGruppiHierarchically(), 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() in Gruppo.php (relazione HasOne a Documento con tipologia 'avatar')
    • Aggiunto accessor getAvatarUrlAttribute() per ottenere l'URL dell'avatar
    • Eager loading avatar aggiunto in GruppoController@index()
    • View gruppi/index.blade.php aggiornata per mostrare l'avatar nella tabella
    • View gruppi/show.blade.php aggiornata per mostrare l'avatar del gruppo
    • View gruppi/create.blade.php e edit.blade.php aggiornate per permettere upload avatar
  • Files modificati:
    • app/Models/Gruppo.php: aggiunto avatar(), getAvatarUrlAttribute()
    • app/Http/Controllers/GruppoController.php: aggiunto eager loading avatar
    • resources/views/gruppi/index.blade.php: colonna avatar nella tabella
    • resources/views/gruppi/show.blade.php: avatar nella header card
    • resources/views/gruppi/create.blade.php e edit.blade.php: upload avatar

26 Maggio 2026 - Event Types System (Tipologie Eventi)

  • Nuova tabella: tipologie_eventi con migration + seed dati default (catechesi, liturgia, animazione, formazione, incontro, riunione, festa, altro)
  • Nuovo modello: App\Models\TipologiaEvento — identico pattern a TipologiaDocumento e Ruolo:
    • $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-eventi con 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:100 a nullable|exists:tipologie_eventi,nome in store() e update(); create/edit passano $tipologieEventi alla view
  • Views eventi:
    • create.blade.php: Dropdown "Tipo Evento" dopo Descrizione Breve
    • edit.blade.php: Text libero → dropdown; fallback per vecchi valori non presenti
    • show.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.php
    • app/Models/TipologiaEvento.php
  • Files modificati:
    • app/Http/Controllers/ImpostazioniController.php
    • app/Http/Controllers/EventoController.php
    • app/Models/Evento.php
    • routes/web.php
    • resources/views/impostazioni/index.blade.php
    • resources/views/layouts/adminlte.blade.php
    • resources/views/eventi/create.blade.php
    • resources/views/eventi/edit.blade.php
    • resources/views/eventi/show.blade.php
    • resources/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 permesso reportis_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 costante User::MODULES
  • Fix user 4:
    • Aggiunto 'report' => 2 alle permissions (full access)
    • Settato is_admin = trueisSuperAdmin() = true, bypass totale ACL
    • canAccess('report') = true, tutti i menu sidebar ora visibili
  • Nuovo comando: app/Console/Commands/SyncUserPermissions.phpphp 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
  • Files modificati:
    • resources/views/admin/utenti/edit.blade.php: linea 76, hardcoded → User::MODULES
    • resources/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'ImpostazioniController usava authorizeWrite('viste') come piggyback scorretto.
  • Soluzione: Nuovo modulo settings separato, slegato da viste e da isSuperAdmin().
  • Modifiche:
    • User.php: aggiunto 'settings' a MODULES (costante linea 36)
    • adminlte.blade.php sidebar: @if(Auth::user()->isSuperAdmin())@if(Auth::user()->canManage('settings')) per menu Impostazioni e Admin (linee 205-226)
    • AdminOnly.php middleware: isSuperAdmin()isSuperAdmin() || canManage('settings') — permette accesso a /admin/* anche a non-superadmin con permesso settings:2
    • ImpostazioniController.php: rimpiazzate tutte le 16 occorrenze di authorizeWrite('viste') e authorizeDelete('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 esplicita
    • SyncUserPermissions.php aggiornato: ora include anche superadmin nella sync (per consistenza dati)
    • Eseguito php artisan users:sync-permissions → user 1 e 4 aggiornati con settings: 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é richiede canManage)
  • 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 calls
    • app/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):
    1. url() assoluto nell'action del form edit (url('/admin/utenti/' . $user->id)) → da accesso remoto generava http://localhost/admin/utenti/4, il POST falliva silenziosamente
    2. Nessun hidden field per unchecked checkbox → quando la checkbox veniva deselezionata, il campo is_admin non veniva inviato nella richiesta. Anche se il controller aveva ?? false, il combinato col bug #1 impediva il salvataggio
    3. 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_id
    • create.blade.php: aggiunto hidden field come sopra; default per settings = '0' (Nessuno) invece di '1' (Lettura) per nuovi utenti
    • UtenteController.php (store + update): rimosso ?? false da $user->is_admin — ora usa direttamente $validated['is_admin'] (sempre presente grazie all'hidden field)
    • AuthController.php: aggiunti 'report' => 0 e 'settings' => 0 ai 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 0
    • app/Http/Controllers/Admin/UtenteController.php: removed ?? false
    • app/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_accounts con migration 2026_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 fallback
    • sanitizeName() — pulisce il nome visualizzato
    • getReplyTo() — priorità: reply_toverify_email → null
    • buildDsn() — costruisce DSN Symfony Mailer
    • buildSymfonyEmail(to, subject, body) — costruisce email con From, Reply-To
    • sendEmail(to, subject, body, attachmentPaths) — invia via Symfony Mailer
    • sendReport(subject, body) — invia report riepilogo a verify_email
  • Logica Reply-To: se impostato reply_to (es. no-reply@parrocchia.it) → quello; fallback verify_email; nessun Reply-To se entrambi null
  • Report invio: dopo ogni invio massivo via sender, una email di riepilogo viene inviata a verify_email con: mittente, oggetto, totale, ok, falliti, dettaglio errori
  • Migration 2026_05_26_000003: aggiunte colonne log_falliti (JSON), mittente_nome, mittente_email a mailing_messaggi
  • Model MailingMessaggio: aggiornato $fillable e $casts per log_falliti (array)
  • CRUD in EmailSettingsController: 4 nuovi metodi protetti da authorizeWrite('settings'):
    • senderStore() — crea nuovo sender con password criptata
    • senderUpdate() — modifica sender, password opzionale
    • senderDestroy() — elimina sender
    • senderTestSmtp() — test invio email via sender
  • Routes (6 nuove):
    • POST /impostazioni/sendersenderStore
    • PUT /impostazioni/sender/{id}senderUpdate
    • DELETE /impostazioni/sender/{id}senderDestroy
    • POST /impostazioni/sender/{id}/testsenderTestSmtp
    • GET /mailing/invioMailingController@invio (prima mancante)
    • POST /mailing/invio/elaboraMailingController@invioElabora (prima mancante)
  • EmailController@compose: passa $senderAccounts alla view; nuova view mostra dropdown "Da (mittente)" prima dei destinatari
  • EmailController@send modificato:
    • Se mittente_id > 0 → usa SenderAccount::sendEmail() con allegati; salta store sent message
    • Se mittente_id = 0 o assente → comportamento attuale con EmailSetting
    • Processa allegati allegati[] e documenti_selezionati
    • Nuovo helper: sendWithSender(), processAttachments(), resolveAttachmentPaths()
    • sendViaImap() ora accetta $attachmentPaths e li attacha all'email
  • MailingController riscritto con invio SMTP reale:
    • invia() — invio 1:1 a ogni destinatario, log su mailing_messaggi
    • invioElabora() — invio massivo 1:1 per ogni email da liste multiple, log su mailing_messaggi
    • Tracking: stato in_codainviato/parziale/fallito, conteggi ok/fail, log_falliti JSON
    • Summary email a verify_email dopo 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.php
    • database/migrations/2026_05_26_000003_add_log_falliti_to_mailing_messaggi.php
    • app/Models/SenderAccount.php
  • Files modificati:
    • app/Models/MailingMessaggio.php
    • app/Http/Controllers/Admin/EmailSettingsController.php
    • app/Http/Controllers/EmailController.php
    • app/Http/Controllers/MailingController.php
    • routes/web.php
    • resources/views/admin/email-settings/index.blade.php
    • resources/views/email/compose.blade.php
    • resources/views/mailing/nuovo.blade.php
    • resources/views/mailing/invio.blade.php

26 Maggio 2026 - Export ICS/iCal Eventi

  • Nuovo Service: app/Services/IcsExportService.php — generazione file ICS RFC 5545
    • generateSingle(Evento) — esporta un singolo evento
    • generateCollection(iterable) — esporta multipli eventi in un unico file ICS
    • Supporto RRULE completo per eventi ricorrenti:
      • settimanaleFREQ=WEEKLY;BYDAY=MO
      • mensileFREQ=MONTHLY;BYSETPOS=1;BYDAY=SU (con opzionale BYMONTH)
      • annualeFREQ=YEARLY;BYMONTH=6;BYMONTHDAY=15
      • altroFREQ=WEEKLY;BYDAY=MO
    • Timezone Europe/Rome per 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 evento
    • exportSelectedIcs(Request $request, IcsExportService $ics) — StreamedResponse con filtro per ID selezionati o ricerca corrente
  • Routes:
    • GET /eventi/{evento}/export-icseventi.export-ics
    • POST /eventi/export-icseventi.export-selected-ics
  • View updates:
    • show.blade.php: pulsante "Esporta ICS" nella toolbar azioni
    • index.blade.php: pulsante "Esporta ICS" nella card-tools; se ci sono righe selezionate esporta solo quelle, altrimenti esporta tutti i risultati della ricerca corrente
    • calendar.blade.php: pulsante "Esporta ICS" che esporta tutti gli eventi
  • Files creati:
    • app/Services/IcsExportService.php
  • Files modificati:
    • app/Http/Controllers/EventoController.php
    • routes/web.php
    • resources/views/eventi/show.blade.php
    • resources/views/eventi/index.blade.php
    • resources/views/eventi/calendar.blade.php