1090 lines
77 KiB
Markdown
1090 lines
77 KiB
Markdown
# MEMORY.md - Glastree Project
|
|
|
|
## Project Context
|
|
Laravel 13 / PHP 8.4 project for parish management (Glastree). Full codebase exists and is operational.
|
|
|
|
---
|
|
|
|
## Issues Open
|
|
*Nessuna issue aperta.*
|
|
|
|
---
|
|
|
|
### 01 Giugno 2026 - Guida Docker in Help Page
|
|
- **Aggiunto**: Nuovo tab "Docker" nella pagina `/help` con guida completa per creare un'immagine Docker
|
|
- **Contenuto**:
|
|
- Due opzioni affiancate: SQLite (leggero/demo) e MySQL (standard/produzione)
|
|
- Dockerfile basato su `php:8.4-apache` con pdo_mysql + pdo_sqlite, composer, asset compilati
|
|
- Entrypoint script `docker-entrypoint.sh` che rileva automaticamente `DB_CONNECTION` e configura .env di conseguenza; attende MySQL via PDO poll se necessario
|
|
- Seeder `DockerBaseDataSeeder` con tipologie documenti (7), tipologie eventi (8) e ruoli (8)
|
|
- Docker Compose in due varianti (Opzione A — SQLite, Opzione B — MySQL con healthcheck e volume separato)
|
|
- Istruzioni build, avvio con `-f` per scegliere il compose, e creazione primo utente amministratore via CLI
|
|
- **Files modificati**:
|
|
- `resources/views/help/index.blade.php`: tab-pane `#help-docker` riscritto con doppia opzione SQLite/MySQL
|
|
|
|
|
|
|
|
## 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
|
|
```bash
|
|
# Clear all caches
|
|
php artisan optimize:clear
|
|
|
|
# Recompile views
|
|
php artisan view:cache
|
|
|
|
# Check routes
|
|
php artisan route:list --name=email
|
|
```
|
|
|
|
---
|
|
|
|
## Known Issues / TODO
|
|
*Nessuna issue nota.*
|
|
|
|
---
|
|
|
|
### 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 `report` né `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 costante `User::MODULES`
|
|
- **Fix user 4**:
|
|
- Aggiunto `'report' => 2` alle permissions (full access)
|
|
- Settato `is_admin = true` → `isSuperAdmin()` = true, bypass totale ACL
|
|
- `canAccess('report')` = true, tutti i menu sidebar ora visibili
|
|
- **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
|
|
- **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_to` → `verify_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/sender` → `senderStore`
|
|
- `PUT /impostazioni/sender/{id}` → `senderUpdate`
|
|
- `DELETE /impostazioni/sender/{id}` → `senderDestroy`
|
|
- `POST /impostazioni/sender/{id}/test` → `senderTestSmtp`
|
|
- `GET /mailing/invio` → `MailingController@invio` (prima mancante)
|
|
- `POST /mailing/invio/elabora` → `MailingController@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_coda` → `inviato`/`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:
|
|
- `settimanale` → `FREQ=WEEKLY;BYDAY=MO`
|
|
- `mensile` → `FREQ=MONTHLY;BYSETPOS=1;BYDAY=SU` (con opzionale `BYMONTH`)
|
|
- `annuale` → `FREQ=YEARLY;BYMONTH=6;BYMONTHDAY=15`
|
|
- `altro` → `FREQ=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-ics` → `eventi.export-ics`
|
|
- `POST /eventi/export-ics` → `eventi.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`
|
|
|
|
### 27 Maggio 2026 - Mittenti Aggiuntivi in Sezione Email di /impostazioni
|
|
|
|
- **Modifica**: Spostata gestione sender accounts (Mittenti Aggiuntivi) da pagina dedicata `/impostazioni/email` a **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@index` ora passa `$senderAccounts` alla view
|
|
- **`senderDestroy()`** in `EmailSettingsController`: 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 SenderAccount
|
|
- `resources/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()` e `previewRemote()` 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 al `browse` endpoint — 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 se `currentRepoBrowse` è 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@update` ora accetta `parent_id` (con validazione anti-ciclo) — preparato per drag-and-drop futuro
|
|
- **CSS**: `.folder-actions` visibili su hover del `.list-group-item`
|
|
- **Google Drive fix**: Aggiunto `normalizePath()` in `StorageRepositoryService` — converte `/` → `''` per compatibilità cross-adapter (Google Drive non accetta `/` come root path). Usato in `testConnection()` e `listContents()`
|
|
- **testRepo() fix**: Passato `this` esplicitamente invece di `event.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
|
|
- `User` model: 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) e `updatePassword()` con validazione `current_password` e `Password::min(8)`
|
|
- **Layout fix**: Link "Profilo" in adminlte.blade.php ora punta a `route('profile')` invece di `#`
|
|
- **Profile view**: Già esistente `auth/profile.blade.php` con 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 tipo `contatti.email`, `gruppi.nome`). Metodo `resolveRelationValue()` per risolvere valori su relazioni nidificate.
|
|
- **ReportController fix**:
|
|
- `storeCustom()`: colonne salvate correttamente come array in config['columns']; supporto per array da Select2 o comma-separated
|
|
- `runCustomReport()`: eager loading automatico per relazioni in colonne dot notation; risoluzione valori via `ReportColumnRegistry::resolveRelationValue()`
|
|
- `exportCSV()`: supporto per custom_id con filename da slug del nome report; BOM UTF-8 per Excel
|
|
- `resolveReport()`: metodo centralizzato per match report type + tipologia filter
|
|
- `run()`: 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_completo` e `scadenze_documenti` hanno 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` — tabella `documenti_cartelle` con `parent_id` FK self, `nome`, timestamps
|
|
- `2026_05_27_000002_add_cartella_id_to_documenti.php` — colonna `cartella_id` FK → `documenti_cartelle.id` su `documenti`
|
|
- `2026_05_27_000003_add_documenti_storage_to_app_settings.php` — colonne `documenti_storage_disk` (default `local`) e `documenti_storage_path` (default `documenti`) in `app_settings`
|
|
- **Model `DocumentoCartella`**: relazioni `parent()`, `children()`, `documenti()`, helper `ancestors()`, `fullPath()`
|
|
- **Model `Documento`**: aggiunto `cartella_id` in `$fillable`, nuova relazione `cartella()` (BelongsTo → DocumentoCartella). Tutte le relazioni esistenti (eventi/gruppi/individui/target/user/tenant) INTATTE
|
|
- **Model `AppSetting`**: aggiunti metodi `getDocumentiStorageDisk()`, `getDocumentiStoragePath()`, `getDocumentiStorageAbsolutePath()`
|
|
- **`DocumentoCartellaController`**: CRUD store/update/destroy + endpoint JSON `breadcrumb(id)` per breadcrumb navigazione
|
|
- **`DocumentoController` riscritto**:
|
|
- `index()`: filtra per `folder_id` GET param, passa `$cartelle` (struttura nidificata), `$currentFolder`, `$breadcrumb` alla view
|
|
- `store()`: accetta `cartella_id` opzionale, storage su disco dinamico da AppSetting
|
|
- `download/preview/destroy/massDestroy`: storage disk risolto dinamicamente via `resolveStorageDisk()`
|
|
- Tutti gli altri metodi (update, checkLinks, massUpdate, massAssociate) INALTERATI
|
|
- **`ImpostazioniController`**: `saveAppSettings()` valida e salva `documenti_storage_disk` (local|public) e `documenti_storage_path`
|
|
- **View `documenti/index.blade.php`**: riscritto come file manager a 3 colonne:
|
|
- Sidebar sinistra (col-md-3): folder tree con `list-group` e 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
|
|
- **View `documenti/_folder_tree.blade.php`**: partial ricorsivo con `@include` per 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@store`
|
|
- `PUT /documenti/cartelle/{id}` → `DocumentoCartellaController@update`
|
|
- `DELETE /documenti/cartelle/{id}` → `DocumentoCartellaController@destroy`
|
|
- `GET /documenti/cartelle/{id}/breadcrumb` → `DocumentoCartellaController@breadcrumb`
|
|
- `POST /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 aggiorna `cartella_id` su `Documento::whereIn('id', $ids)`, JS handler submit form `/documenti/mass-move` con 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 per `massForm` (linea 391) prima dei modali. Il form `massForm` avvolgeva tutto il contenuto incluse le modali, causando `<form>` nidificati (HTML invalido) — browser ignorava i form interni come `newFolderForm`, 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.php`
|
|
- `database/migrations/2026_05_27_000002_add_cartella_id_to_documenti.php`
|
|
- `database/migrations/2026_05_27_000003_add_documenti_storage_to_app_settings.php`
|
|
- `app/Models/DocumentoCartella.php`
|
|
- `app/Http/Controllers/DocumentoCartellaController.php`
|
|
- `resources/views/documenti/_folder_tree.blade.php`
|
|
- **Files modificati**:
|
|
- `app/Models/Documento.php`: cartella_id fillable + cartella() relation
|
|
- `app/Models/AppSetting.php`: metodi storage documenti
|
|
- `app/Http/Controllers/DocumentoController.php`: riscritto con filtro folder + storage dinamico
|
|
- `app/Http/Controllers/ImpostazioniController.php`: saveAppSettings validazione storage
|
|
- `resources/views/documenti/index.blade.php`: riscritto (file manager 3 colonne)
|
|
- `resources/views/impostazioni/index.blade.php`: nuova sezione Repository Documenti
|
|
- `routes/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()` in `documenti/index.blade.php` aveva una `}` extra alla riga 1183 che causava `SyntaxError` JS. 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.
|
|
- **Problema 2 — Icone cartelle assenti/non funzionanti**: Le icone rename/delete sono nel partial `_folder_tree.blade.php` con CSS hover (`.list-group-item:hover .folder-actions { display: inline-flex !important; }`). Le icone erano renderizzate nel DOM ma il JS era rotto: `renameFolder()` e `deleteFolder()` mai definite.
|
|
- **Fix**: Il fix del JS (rimozione `}` extra) risolve anche questo problema.
|
|
- **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`→`public` causava 404 sui vecchi file).
|
|
- **Fix**: Creata migration `2026_05_28_000001_add_storage_disk_to_documenti.php` che aggiunge colonna `storage_disk` (nullable string) alla tabella `documenti`.
|
|
- `DocumentoController@store()` ora salva `storage_disk` sul 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.
|
|
- **Stato**: ✅ 18/19 test pass (solo ExampleTest pre-esistente). `php -l` su tutti i file modificati OK. `php artisan view:cache` OK.
|
|
|
|
(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-actions` in `_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-open` nel folder tree → funzione `moveFolder(id, nome)` con SweetAlert2 prompt contenente `<select>` con gerarchia cartelle (esclude se stessa e figli) → `PUT /documenti/cartelle/{id}` con `parent_id`. Aggiunti dati `folders` JSON in linea.
|
|
- **Aggiunto pulsante "Associa" per file singoli**: Bottone `fa-link` in ogni card (grid) e riga (list) → `showAssociateModal(id)` pre-popola il modale `#associateModal` con l'ID del documento singolo.
|
|
- **UpdateModal con target dinamico**: Aggiunto `#updateTargetGroup` nel modal "Cambia tipo/contesto": quando si seleziona `individuo`/`gruppo`/`evento`/`mailing`, compare dropdown con i dati caricati dagli array JS esistenti (`individui`, `gruppi`, `eventi`, `mailingLists`). Funzione `toggleUpdateTarget(value)` show/hide + popolamento.
|
|
- **massUpdate controller esteso**: `massUpdate()` in `DocumentoController` ora accetta `visibilita_target_id` e `visibilita_target_type` come parametri opzionali. Se presenti, aggiorna contesto + target sul DB con `$typeMap`.
|
|
- **Fix saveAppSettings null error**: `ImpostazioniController@saveAppSettings` ora preserva i valori esistenti quando i campi arrivano `null` (dovuto a `ConvertEmptyStringsToNull` middleware): `$settings->nome_applicazione = $validated[...] ?? $settings->nome_applicazione ?? ''`. Applicato a tutti i campi stringa. Risolve `SQLSTATE[23000]: Column 'nome_applicazione' cannot be null`.
|
|
- **Files modificati**:
|
|
- `app/Http/Controllers/ImpostazioniController.php`: saveAppSettings fallback valori esistenti
|
|
- `app/Http/Controllers/DocumentoController.php`: massUpdate esteso con target fields
|
|
- `resources/views/documenti/_folder_tree.blade.php`: always-visible icons + move button
|
|
- `resources/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 -l` OK. `php artisan view:cache` OK.
|
|
|
|
(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-tag` dopo "Associa"
|
|
- List view (colonna azioni): stesso bottone per riga
|
|
- Nuova funzione JS `showUpdateModal(id)` pre-popola `updateIds` col singolo ID
|
|
- **Fix mailing list in updateModal**: `mailingLists` ora 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, funzione `showUpdateModal()`, mapping mailing list corretto
|
|
- **Stato**: ✅ `php -l` OK. `php artisan view:cache` OK.
|
|
|
|
### 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 `ZipArchive` contenente 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
|
|
- **Files modificati**:
|
|
- `routes/web.php`: nuova route `POST /documenti/mass-download`
|
|
- `app/Http/Controllers/DocumentoController.php`: nuovo metodo `massDownload()`
|
|
- `resources/views/documenti/index.blade.php`: icone cartelle in grid+list, bottone "Scarica" mass action, funzione JS `massDownload()`
|
|
- **Stato**: ✅ 18/19 test pass (solo ExampleTest pre-esistente). `php -l` OK. `php artisan view:cache` OK.
|
|
|
|
### 28 Maggio 2026 - Fix pagina bloccata: massaAction() mancante di chiusura }
|
|
|
|
- **Problema**: La funzione `massAction()` nel JS di `documenti/index.blade.php` aveva 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 di `function 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 chiudere `massAction()`.
|
|
- **Verifica**: 182 `{` = 182 `}` nel blocco JS. 18/19 test pass. `php -l` OK. `php artisan view:cache` OK.
|
|
|
|
### 28 Maggio 2026 - Fix: delete redirect, folder azioni non funzionanti, escaping JS
|
|
|
|
- **Fix delete redirect dashboard**: `DocumentoController@destroy()` usava `url('/')` come default redirect → dopo eliminazione si finiva in dashboard. Cambiato a `url()->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 con `json_encode()` che produce stringhe JS correttamente escaped con virgolette.
|
|
- **Fix browseRepo repo names**: Stesso fix `addslashes` → `json_encode` per i nomi repository negli onclick.
|
|
- **Files modificati**:
|
|
- `app/Http/Controllers/DocumentoController.php`: destroy() redirect default `url()->previous()`
|
|
- `resources/views/documenti/index.blade.php`: 10 onclick con `json_encode` invece di `addslashes`
|
|
- `resources/views/documenti/_folder_tree.blade.php`: 3 onclick con `json_encode` invece di `addslashes`
|
|
- **Stato**: ✅ 18/19 test pass. `php -l` OK. `php artisan view:cache` OK. JS braces 182/182.
|
|
|
|
### 28 Maggio 2026 - Fix Google Drive Repository: credenziali mancanti + wipe su update
|
|
|
|
- **Problema**: Drive-Segreteria (repo #1) mostrava 0 file nonostante l'account Google Drive avesse 2 cartelle e 1 file. Il test connessione diceva "Connessione riuscita. 0 elementi trovati nella root." — falso positivo.
|
|
- **Cause** (2 bug):
|
|
1. **Credenziali OAuth mancanti**: `client_secret` e `refresh_token` erano entrambi `null` nel DB. La Google API falliva silenziosamente dentro `fetchDirectory()` (catch vuoto → 0 risultati).
|
|
2. **Update wipe credenziali**: `StorageRepositoryController@update()` chiamava `encryptSensitiveConfig()` che criptava solo campi NON vuoti. Quando il form edit veniva salvato con `client_secret=''` e `refresh_token=''` (placeholder), i vecchi valori criptati venivano sovrascritti con stringhe vuote.
|
|
- **Fix**:
|
|
1. `StorageRepositoryService::encryptSensitiveConfig()`: aggiunto parametro `$existingConfig`. Se un campo sensibile è vuoto nel nuovo config ma presente in `$existingConfig`, lo preserva.
|
|
2. `StorageRepositoryController@update()`: passa `$storageRepository->config` come existing config a `encryptSensitiveConfig()`.
|
|
3. `StorageRepositoryService::buildGoogleDrive()`: validazione esplicita — lancia `InvalidArgumentException` se mancano `client_id`, `client_secret` o `refresh_token`.
|
|
4. `testConnection()` e `listContents()`: check preventivo credenziali Google Drive complete prima di chiamare `buildFilesystem()`, con messaggio chiaro.
|
|
- **Files modificati**:
|
|
- `app/Services/StorageRepositoryService.php`: encryptSensitiveConfig() con existingConfig, buildGoogleDrive() validation, testConnection() credential check, listContents() credential check
|
|
- `app/Http/Controllers/StorageRepositoryController.php`: update() passa existing config
|
|
- **Per risolvere**: l'utente deve reinserire `client_secret` e `refresh_token` nella modale di modifica del repository (Impostazioni → Repository Remoti → Drive-Segreteria → Modifica). Dopo il fix, questi campi non verranno più persi al prossimo salvataggio.
|
|
- **Stato**: ✅ 18/19 test pass (solo ExampleTest pre-esistente). `php -l` OK. `php artisan view:cache` OK.
|
|
|
|
### 28 Maggio 2026 - OAuth Google Drive integrato nell'app
|
|
|
|
- **Implementazione**: Flusso OAuth 2.0 completo senza passare dall'OAuth Playground
|
|
- **Route nuove**:
|
|
- `GET /auth/google-drive/redirect` → reindirizza a Google OAuth
|
|
- `GET /auth/google-drive/callback` → riceve il token, lo salva nel repository
|
|
- **Controller**: `StorageRepositoryController@oauthRedirect()` e `oauthCallback()`
|
|
- **Flusso UX**:
|
|
1. Utente seleziona Google Drive → compila Client ID, Client Secret, Nome
|
|
2. Click "Autorizza con Google" → reindirizzato a Google
|
|
3. Login con account Google e autorizzazione
|
|
4. Callback salva automaticamente refresh_token + campi form nella sessione
|
|
5. Redirect a `/impostazioni#repository` → modale si riapre con tutti i campi preservati
|
|
- **Fix preservazione campi**: `oauthRedirect()` salva in sessione anche `nome`, `is_active`, `root_folder_id`. Il callback li restituisce nel flash `google_drive_new_token`. JS ripristina tutti i campi nel modal (non chiama più `resetRepoForm()` che svuotava tutto).
|
|
- **Status visivo**: ✅ badge verde "Autorizzato" se refresh_token presente, ❌ "Non autorizzato" altrimenti
|
|
- **Files modificati**:
|
|
- `routes/web.php`: 2 nuove route OAuth
|
|
- `app/Http/Controllers/StorageRepositoryController.php`: oauthRedirect(), oauthCallback()
|
|
- `app/Http/Controllers/ImpostazioniController.php`: passa `$googleDriveNewToken` alla view
|
|
- `resources/views/impostazioni/index.blade.php`: pulsante + status + JS auto-ripristino campi
|
|
- **Config necessaria una tantum**: aggiungere `{APP_URL}/auth/google-drive/callback` come Authorized Redirect URI in Google Cloud Console
|
|
- **Stato**: ✅ 18/19 test pass. `php -l` OK. `php artisan view:cache` OK. JS braces 176/176.
|
|
|
|
### 28 Maggio 2026 - Fix Google Drive: normalizePath() root path
|
|
|
|
- **Problema**: Google Drive non visualizzava file e cartelle (repository remoto mostrava 0 elementi).
|
|
- **Causa**: `normalizePath('/')` in `StorageRepositoryService.php` restituiva `'/'` invece di `''`. L'adapter `as247/flysystem-google-drive` non accetta `'/'` come root path, causando fallimento silenzioso di `listContents()` → `[]`.
|
|
- **Contraddizione**: MEMORY.md del 28 Maggio documentava `normalizePath()` come "converte `/` → `''`" ma l'implementazione effettiva a riga 122 faceva `return '/'`.
|
|
- **Fix**: Cambiato `return '/'` in `return ''` in `normalizePath()`.
|
|
- **Files modificati**:
|
|
- `app/Services/StorageRepositoryService.php`: riga 122, `'/'` → `''`
|
|
- **Stato**: ✅ 10/10 StorageRepository test pass. `php -l` OK.
|
|
|
|
### 28 Maggio 2026 - Fix browseRepo ReferenceError: getRepoIcon inesistente
|
|
|
|
- **Problema**: Cliccando "Drive-Segreteria" nella sidebar documenti non succedeva nulla (contenuto remoto non caricato).
|
|
- **Causa**: `updateRemoteBreadcrumb()` chiamava `getRepoIcon(repoIconFromId(...))` ma `getRepoIcon()` non è mai stata definita. Il `ReferenceError` bloccava l'esecuzione di `browseRepo()` prima che `loadRemoteContents()` venisse chiamato — nessuna fetch AJAX, nessun contenuto caricato.
|
|
- **Fix**: Rimossa chiamata a `getRepoIcon()`, `repoIconFromId()` già restituisce il `className` completo (es. `"fab fa-google-drive text-danger"`). Rimosso anche `fab` hardcoded dal template (duplicato).
|
|
- **Files modificati**:
|
|
- `resources/views/documenti/index.blade.php`: riga 1383, `getRepoIcon(...)` → diretto
|
|
- **Stato**: ✅ JS braces 206/206 OK. `php -l` OK. `php artisan view:cache` OK.
|
|
|
|
### 28 Maggio 2026 - CAUSA RADICE: SweetAlert2 mancante in layout
|
|
|
|
- **Problema**: Tutte le icone azioni cartelle (Rinomina, Sposta, Elimina) non funzionavano — click senza effetto. Anche il pulsante "+ Nuova cartella" nel modal upload non funzionava.
|
|
- **Causa**: SweetAlert2 (`Swal`) **non era incluso nel layout** `adminlte.blade.php`. Le funzioni `renameFolder()`, `moveFolder()`, `deleteFolder()` e il listener `massMoveNewFolderBtn` chiamano `Swal.fire()`, che lanciava `ReferenceError: Swal is not defined`.
|
|
- **SweetAlert2 assente**: Non presente né via CDN, né via NPM (`package.json` non ha sweetalert2), né via Vite. L'unica dipendenza JS era jQuery + Bootstrap + AdminLTE via CDN.
|
|
- **Perché i fix precedenti sembravano non funzionare**: Anche dopo le correzioni a `getRepoIcon()` e `normalizePath()`, la view cache conteneva ancora il vecchio codice JS. SweetAlert2 mancante bloccava indipendentemente le azioni cartelle.
|
|
- **Fix**: Aggiunto CDN SweetAlert2 11 nel layout `adminlte.blade.php` subito dopo AdminLTE JS, prima di `@yield('scripts')`.
|
|
- **Files modificati**:
|
|
- `resources/views/layouts/adminlte.blade.php`: riga 259, aggiunto `<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>`
|
|
- **Stato**: ✅ view cache pulita (`php artisan view:clear`), tutti i file lint OK. `php artisan view:cache` OK.
|
|
|
|
### 28 Maggio 2026 - Fix authType WebDAV (Nextcloud)
|
|
- **Problema**: Test connessione Nextcloud dava "Unsupported operand types: string & int"
|
|
- **Causa**: `buildWebDAV()` passava `'authType' => 'basic'` (stringa) a `Sabre\DAV\Client`, che esegue bitwise AND (`'basic' & AUTH_BASIC`) — incompatibile
|
|
- **Soluzione**: Convertito stringa → int tramite `match`: `'basic' → WebDAVClient::AUTH_BASIC (1)`, `'digest' → AUTH_DIGEST (2)`, `'ntlm' → AUTH_NTLM (4)`
|
|
- **Stato test Nextcloud**: AuthType non dà più errore. Ora fallisce con "Unauthorized" — le credenziali Nextcloud effettive non sono corrette.
|
|
- **Drive-Segreteria-ADIM**: Credenziali OK, connessione OK, ma 0 elementi nella root. Verificare `root_folder_id`.
|
|
|
|
### 29 Maggio 2026 - Import file remoto come documento locale
|
|
- **Route**: `POST /storage-repositories/{id}/import` → `StorageRepositoryController@importToLocal`
|
|
- **Controller**: `importToLocal()` in `StorageRepositoryController.php`:
|
|
- Autorizza `authorizeWrite('documenti')`
|
|
- Legge file da filesystem remoto via `readStream()`
|
|
- Salva su disco locale (`AppSetting::getDocumentiStorageDisk()`)
|
|
- Crea record `Documento` con nome, tipologia, cartella, visibilità
|
|
- Restituisce JSON con esito
|
|
- **Frontend**:
|
|
- Nuova funzione JS `importRepoFile(repoId, path, basename)` con SweetAlert2 form:
|
|
- Tipologia (select: documento/avatar/galleria/statuto/altro)
|
|
- Cartella (select gerarchica con opzioni da DB)
|
|
- Visibilità (select: pubblico/individuo/gruppo)
|
|
- Pulsante "Salva" (fa-save, btn-success) su ogni file remoto in vista griglia e lista
|
|
- Colonna azioni lista espansa a 150px
|
|
- **Files modificati**:
|
|
- `app/Http/Controllers/StorageRepositoryController.php`: aggiunto `importToLocal()`
|
|
- `routes/web.php`: aggiunta route `storage-repositories.import`
|
|
- `resources/views/documenti/index.blade.php`: pulsanti Salva in grid+list, funzione `importRepoFile()`, variabile JS `cartelle`
|
|
|
|
(Last updated: 01 Giugno 2026 - Sistema Backup Completo + Help Header + Script Migrazione)
|
|
|
|
### 01 Giugno 2026 - Sistema Backup Completo per Migrazione
|
|
|
|
- **Header navbar**: Aggiunta icona `fa-question-circle` che linka a `route('help')` — posizionata nella navbar superiore prima della campanella notifiche
|
|
- **Sidebar**: Aggiunto link "Backup" sotto Admin (protetto da `canManage('settings')`)
|
|
- **BackupController** (`app/Http/Controllers/Admin/BackupController.php`): 8 metodi:
|
|
- `index()` — lista backup + form config
|
|
- `run()` — esegue backup manuale
|
|
- `download(filename)` — scarica ZIP
|
|
- `destroy(filename)` — elimina backup
|
|
- `updateConfig()` — salva impostazioni backup
|
|
- `toggleAuto()` — abilita/disabilita backup automatico
|
|
- `saveAutoConfig()` — salva frequenza e ora backup automatico
|
|
- `downloadMigrationScript()` — (JSON) restituisce script di migrazione
|
|
- **Backup view** (`resources/views/admin/backup/index.blade.php`):
|
|
- Tabella backup con file, data, dimensione, contenuto, azioni (scarica/elimina)
|
|
- Card configurazione: percorso, ritenzione, toggle files/.env
|
|
- Card backup automatico: toggle on/off, frequenza (daily/weekly/monthly), ora
|
|
- Card migrazione: link alla guida
|
|
- Alert informativo sul cron necessario
|
|
- **BackupRunCommand** (`app/Console/Commands/BackupRunCommand.php`): `php artisan backup:run` con opzioni `--filename=`, `--no-files`, `--no-env`
|
|
- **Console scheduler** (`routes/console.php`): backup automatico con `dailyAt()` e `withoutOverlapping()`, controlla `backup_auto_enabled`
|
|
- **Route backup** (7 rotte sotto `admin/`):
|
|
- `GET /admin/backup` → backup.index
|
|
- `POST /admin/backup/run` → backup.run
|
|
- `GET /admin/backup/download/{filename}` → backup.download
|
|
- `DELETE /admin/backup/{filename}` → backup.destroy
|
|
- `POST /admin/backup/config` → backup.updateConfig
|
|
- `POST /admin/backup/toggle-auto` → backup.toggleAuto
|
|
- `POST /admin/backup/save-auto` → backup.saveAutoConfig
|
|
- **Guida help**: Nuovo tab "Backup e Migrazione" con:
|
|
- Spiegazione contenuto backup (tabella)
|
|
- Backup via UI e via CLI
|
|
- Backup automatico e requisiti cron
|
|
- **⚠️ Avviso APP_KEY**: blocco informativo su rigenerazione
|
|
- **Procedura migrazione completa** in 12 passi (LAMP)
|
|
- Riferimento a `installa-glastree.sh`
|
|
- **PHP Web Installer** (`installer/`):
|
|
- Sostituisce il vecchio script bash, ora rimosso
|
|
- **index.php**: entry point — se .env esiste, redirect a login; altrimenti avvia wizard
|
|
- **setup.php**: wizard multi-step con sessione PHP, routing via `?step=N`
|
|
- **functions.php**: 15 helper functions
|
|
- **assets/style.css**: stile AdminLTE-like, responsive
|
|
- **Step 1 — Benvenuto**: scegli Fresh Install o Restore da Backup (option cards)
|
|
- **Step 2 — Requisiti**: verifica PHP 8.2+, estensioni (pdo_mysql, mbstring, xml, curl, zip, gd, fileinfo), permessi storage e bootstrap/cache
|
|
- **Step 3 — Database**: due modalita:
|
|
- A: Crea nuovo (root MySQL → crea DB + utente)
|
|
- B: Usa esistente (test PDO → salva .env)
|
|
- **Step 4 — Installazione**:
|
|
- Fresh: `key:generate`, `migrate`, `db:seed`, `storage:link` via bootstrap Laravel
|
|
- Backup: upload ZIP o percorso server → extract, import SQL, copy files, merge .env
|
|
- **Step 5 — Amministratore**:
|
|
- Fresh: form creazione super-admin (nome, email, password con validazione)
|
|
- Backup: mostra utenti esistenti dal DB, opzione salta o crea nuovo
|
|
- **Step 6 — Finalizzazione**: `config:cache`, `route:cache`, `view:cache`, **self-destruct** (elimina installer/)
|
|
- **Sicurezza**: self-destruct dopo installazione, blocco re-install se .env esiste, CSRF token, HTTPS warning, password mai esposte in HTML dopo invio
|
|
- **Migration backup**: `2026_06_01_100155_add_backup_settings_to_app_settings` estesa con `backup_auto_enabled`, `backup_auto_frequency`, `backup_auto_hour`
|
|
- **.env.example**: aggiornato — default MySQL invece di SQLite, locale it, righe minime essenziali
|
|
- **Guida Installazione**: nuovo tab nella help page con:
|
|
- Requisiti server (tabella)
|
|
- Installazione passo-passo (LAMP, clone, composer, Apache, wizard)
|
|
- Spiegazione delle due modalita (fresh/backup)
|
|
- Cosa fare dopo (SSL, configurazione, cron)
|
|
- Risoluzione problemi comuni (500 error, APP_KEY, mod_rewrite)
|
|
- **Files creati** (installer/):
|
|
- `installer/index.php`
|
|
- `installer/setup.php`
|
|
- `installer/functions.php`
|
|
- `installer/assets/style.css`
|
|
- **Files rimossi**: `installa-glastree.sh`
|
|
- **Files modificati**:
|
|
- `.env.example`: default MySQL, pulizia righe superflue
|
|
- `database/migrations/2026_06_01_100155_add_backup_settings_to_app_settings.php`: +3 colonne auto-backup
|
|
- `resources/views/help/index.blade.php`: nuovo tab Installazione
|
|
- **Stato**: ✅ PHP syntax OK su tutti i file. ✅ view:cache OK. ✅ 7 route backup registrate. ✅ `php artisan backup:run` registrato. ✅ installer/ struttura completa.
|