Files
glastree/MEMORY.md
T
2026-06-08 20:10:33 +02:00

163 lines
11 KiB
Markdown

# 🧠 MEMORY.md — Stato Progetto
## Obiettivo
App gestionale Laravel 13 con AdminLTE 4 per gestione Persone e Gruppi.
## Funzionalità Implementate
### Page-length selector
- **4 pagine**: Individui, Eventi, Email, Gruppi
- **Valori**: 10, 20, 25, 50, 100 (default 20)
- Dropdown nel card-footer con campi hidden per preservare query params
- GruppoController: usa `LengthAwarePaginator` per paginare la collezione ordinata gerarchicamente
### CSV Import Gruppi
- Stesso pattern di `IndividuoController` (import GET, importStore POST, downloadTemplate GET)
- Colonne: nome, descrizione, parent_id, diocesi_id, indirizzo_incontro, cap_incontro, città_incontro, sigla_provincia_incontro
- Fault-tolerant: skip righe vuote, log errori, try/catch su ogni riga
### ICS Import Eventi
- Usa `Sabre\VObject\Reader::read()` già in vendor/
- Mapping: SUMMARY→nome_evento, DESCRIPTION→descrizione, DTSTART→data_specifica+ora_inizio, DTEND→durata_minuti, LOCATION→luogo_indirizzo, UID→uid_esterno
- Dedup via uid_esterno
- RRULE: import come evento singolo con prima occorrenza
### build-dist.sh (script di distribuzione)
- Genera `glastree-YYYYMMDD_HHMM.tar.gz`
- Include: sorgenti, vendor (production)
- Esclude: .git, node_modules, tests, cache, storage content, vendor/docs/tests, Docker
- **Istruzioni post-estrazione complete**: mkdir, permessi, configurazione, cache clear
## Bug Fix Recenti
### 2026-06-07 — Colonne mancanti in eventi e gruppo_individuo
- `descrizione_evento` e `is_incontro_gruppo` mancanti in install.sql → creata migration + ALTER TABLE
- `ruolo_nel_gruppo` mancante in install.sql per pivot `gruppo_individuo`
- `VistaReport.php`: aggiunto `is_default` a `$fillable`
### 2026-06-08 — build-dist.sh: "provide valid cache path" su server remoto
**Problema**: Le esclusioni in `build-dist.sh` usavano `--exclude='storage/framework/cache'` (e simili per sessions, views, logs), che escludevano **l'intera directory** dall'archivio. Dopo l'estrazione, le directory necessarie a Laravel non esistevano.
**Fix**:
1. Cambiati exclude patterns per **contenuti soltanto** (non la directory):
- `--exclude='storage/framework/cache/*'` (non `/cache`)
- `--exclude='storage/framework/sessions/*'`
- `--exclude='storage/framework/views/*'`
- `--exclude='storage/logs/*'`
- `--exclude='storage/debugbar/*'`
2. Aggiunte esclusioni per upload utente:
- `--exclude='storage/app/public'`
- `--exclude='storage/app/private'`
3. Creato `.gitignore` in `storage/framework/views/` (mancante)
4. Istruzioni post-estrazione riscritte con:
- `mkdir -p` per tutte le directory necessarie (cache, sessions, views, logs, public, backups, documenti, bootstrap/cache)
- `chmod -R 775` e `chown -R www-data:www-data` per permessi
- `php artisan config:clear`, `route:clear`, `view:clear`
## Problema 419 (Page Expired) su Produzione
Il codice NON ha errori di CSRF — tutte le form hanno `@csrf`. Il problema è configurazione sessione del server:
1. `storage/framework/sessions/` non scrivibile dal web server
2. `APP_URL` in `.env` non corrisponde all'URL reale
3. `SESSION_SECURE_COOKIE=true` ma sito in HTTP
Soluzione:
```bash
chmod -R 775 storage/framework/sessions
php artisan config:clear
```
### 2026-06-08 — build-dist.sh: impossibilità salvare logo su server remoto
**Problema**:
- `public/storage` era un symlink **assoluto** (`/var/www/html/glastree/storage/app/public`) incluso nell'archivio → all'estrazione su altro server puntava a path inesistente.
- `php artisan storage:link` falliva perché il symlink rotto esisteva già.
- Sottodirectory `logos/`, `documenti/eventi/`, `avatars/gruppi/` non create dal `mkdir -p`.
**Fix**:
1. Aggiunto `--exclude='public/storage'` al tar — il symlink va ricreato sul target.
2. Aggiunto `rm -f public/storage` prima di `php artisan storage:link` per evitare conflitto.
3. Usato `ln -sf ../storage/app/public public/storage` (invece di `php artisan storage:link --relative` che richiede `symfony/filesystem` non sempre presente).
4. Aggiunte al `mkdir -p` le sottodirectory mancanti:
- `storage/app/public/logos` (logo upload)
- `storage/app/public/documenti/eventi` (event documenti)
- `storage/app/private/avatars/gruppi` (avatar gruppi)
5. Aggiunta creazione `.gitignore` in `storage/app/public/` (placeholder Laravel).
### Fix form annidato salvataggio email
**2026-06-08 — Problema**: Il form di DELETE era **annidato dentro** il form di SAVE in entrambe le pagine:
- `resources/views/impostazioni/index.blade.php`
- `resources/views/admin/email-settings/index.blade.php`
- HTML invalido: il browser chiudeva implicitamente il form SAVE all'apertura del form DELETE, causando l'invio alla route sbagliata.
**Fix**: Spostato il form DELETE fuori dal form SAVE, separato in un `div` autonomo.
### Fix campi mancanti in validazione EmailSettingsController@save
**2026-06-08**: `signature`, `signature_enabled`, `smtp_username`, `smtp_password` non erano nelle regole di validazione di `save()`. Laravel scarta i campi non validati, quindi non venivano mai scritti nel DB.
**Fix**: Aggiunte le regole mancanti a `$request->validate()`.
### DiagnoseEmail Command
- `php artisan diagnose:email` — controlla APP_KEY, tabella email_settings, record, decrittazione password, sender_accounts, log errori, estensioni PHP.
### 2026-06-08 — Firma email ancora non salvata (secondo tentativo)
**Problema**: La firma (campo `signature`) non veniva salvata nonostante le regole di validazione fossero state aggiunte.
**Root cause analysis**:
1. HTML checkbox `signature_enabled` senza hidden fallback: quando non spuntato, il campo non veniva inviato → non presente in `$validated``updateOrCreate` non lo aggiornava mai a `false`.
2. La vecchia pagina `/impostazioni/` aveva l'handler `shown.bs.tab` per il tab Firma (`#email-firma`) in un solo punto su due, causando possibile mancata inizializzazione dell'editor Quill su alcuni flussi di navigazione.
3. La verifica nel controller controllava solo `imap_host` e `email_address` — anche se signature/signature_enabled fallivano, l'utente vedeva "salvato con successo".
**Fix**:
1. `resources/views/admin/email-settings/index.blade.php:268` — Aggiunto `<input type="hidden" name="signature_enabled" value="0">` prima della checkbox
2. `resources/views/impostazioni/index.blade.php:603` — Stesso hidden fallback
3. `resources/views/impostazioni/index.blade.php:1633` — Aggiunto `#email-firma` al primo handler `shown.bs.tab` (il secondo handler esisteva già ma il primo no)
4. `app/Http/Controllers/Admin/EmailSettingsController.php:70-82` — Aggiunto log di warning se signature o signature_enabled non corrispondono dopo il salvataggio
### 2026-06-08 — Sync email: "Nessuna configurazione email attiva" nonostante test IMAP funzioni
**Problema**: Cliccando "Sincronizza Ora" nelle impostazioni email o "Ricevi/Invia" nella pagina email, viene mostrato "Nessuna configurazione email attiva". Il test IMAP funziona correttamente.
**Root cause**: Stesso bug dei checkbox senza hidden fallback. Il campo `is_active` nel tab Sincronizzazione non veniva inviato quando non spuntato. Alla prima configurazione, se l'utente non visitava esplicitamente il tab Sync e attivava lo switch, `is_active` rimaneva `false` (default DB). Il test IMAP usa `EmailSetting::first()` (ignora `is_active`), mentre `syncEmails()` e `syncNow()` usano `EmailSetting::getActive()` che filtra per `is_active = true`.
**Fix**:
1. `resources/views/admin/email-settings/index.blade.php:251` — Aggiunto `<input type="hidden" name="is_active" value="0">` prima della checkbox
2. `resources/views/impostazioni/index.blade.php:586` — Stesso hidden fallback
3. `EmailSetting::getActive()` ora riceve sempre `is_active` dal form (0 o 1) grazie all'hidden fallback
## 2026-06-08 — Help page: aggiunto tab "Calendario" con documentazione OAuth
- **`resources/views/help/index.blade.php`**: Aggiunto tab "Calendario" nel menu laterale (dopo Google Drive) e relativo tab-pane content con:
- Requisiti (account Google, API Calendar, OAuth credentials)
- Passo 1: Google Cloud Console — abilitare Google Calendar API, creare OAuth Client ID (Web Application), redirect URI `https://developers.google.com/oauthplayground`
- Passo 2: Ottenere Refresh Token via Google OAuth Playground (Use your own OAuth credentials, scope `calendar`)
- Passo 3: Configurazione nell'app (Impostazioni → Calendario, nuova connessione Google Calendar, inserire Client ID/Secret/Refresh Token)
- Test connessione e sincronizzazione
- Nota: refresh token permanente finché non revocato
- **`resources/views/help/pdf.blade.php`**: Aggiunta sezione "3. Google Calendar" nel TOC e contenuto, rinumerazione da 4 a 8 per sezioni successive
### Differenza chiave Google Calendar vs Google Drive
- **Drive**: endpoint server `/auth/google-drive/callback` che gestisce automaticamente il callback OAuth e salva refresh token in `storage_repositories`
- **Calendar**: **NON** ha endpoint server dedicato. Refresh token ottenuto manualmente via Google OAuth Playground e incollato nel campo "Refresh Token" del form
## 2026-06-08 — Pulsante Sync nella pagina calendario eventi
- **`app/Http/Controllers/CalendarioConnessioneController.php`**: Aggiunto metodo `syncAll()` che itera tutte le connessioni attive (`is_active = true`), esegue `syncEvents()` per ognuna, e restituisce JSON con risultati aggregati.
- **`routes/web.php`**: Aggiunta route `POST /calendario-connessioni/sync-all``syncAll` (name: `calendario-connessioni.sync-all`).
- **`resources/views/eventi/calendar.blade.php`**: Aggiunto pulsante "Sync" nel card-tools che:
- Invia POST AJAX a `sync-all`
- Mostra spinner durante l'operazione
- Al completamento mostra alert con riepilogo (connessioni, importati, esportati)
- Richiama `window.calendar.refetchEvents()` per aggiornare la vista FullCalendar
- Esposto `window.calendar` globalmente per accesso dall'handler sync
## 2026-06-08 — Fix logo non visibile su installazione remota
**Problema**: Il logo uploadato non veniva visualizzato su installazioni remote, pur salvandosi correttamente su disco.
**Root cause**: `getLogoPath()` e `getLogoSmallPath()` in `AppSetting.php` usavano `file_exists(public_path('storage/' . $path))` per verificare l'esistenza del file prima di restituire l'URL. Questo controllo dipendeva dal symlink `public/storage → storage/app/public`. Su installazioni remote dove il symlink era assente o mal configurato, `file_exists` restituiva `false` → metodo restituiva `null` → logo non mostrato.
**Fix**:
1. **`app/Models/AppSetting.php`**: Sostituito `file_exists(public_path('storage/' . $path))` con `Storage::disk('public')->exists($path)` e `asset('storage/' . $path)` con `Storage::disk('public')->url($path)`. Il controllo ora avviene direttamente su `storage/app/public/` (disk root), bypassando il symlink.
2. **`app/Http/Controllers/ImpostazioniController.php`**: In `uploadLogo()`, aggiunta creazione automatica del symlink `public/storage → storage/app/public` se mancante, per garantire che il browser possa servire l'immagine.
## Prossimi Passi
- *(nessuno — in attesa di nuove richieste)*