# MEMORY.md - Stato del Plugin UrBackup ## Ultima modifica: 31/08/2026 ## 31/08/2026 — Audit statico: fix 7 bug (solo UI/codice, nessuna modifica DB) Audit statico (IA) + confronto contro sorgente C++ del backend UrBackup e core GLPI 11. Nessun bug di classe critico (crash/XSS/SQLi), ma diversi output errati corretti. 1. **Bug 1 — Livello log sempre vuoto** (`AssetTab.php:728`): l'API `livelog` restituisce `loglevel` (int 1=ERROR,2=WARNING,3=INFO,4=DEBUG), il codice leggeva `level`/`severity` (mai presenti). Aggiunto `formatLogLevel()` che mappa `loglevel`→stringa. 2. **Bug 2 — "No client logs available" fuorviante** (`AssetTab.php:733`): se `client_found=false` il client non è stato trovato per nome e i log NON vengono mai estratti. Ora mostra avviso "Client not found on UrBackup server. Check that the asset name matches the client name." Il matching resta per NOME client (confermato dall'utente come corretto). 3. **Bug 3 — JOIN IP invertito** (`Server.php` `batchLoadIps` ~1516 e `getAssetIp` ~1614): JOIN glpi_ipaddresses↔glpi_networknames generava `nn.items_id = ipa.id`; corretto a `nn.id = ipa.items_id` (con `ipa.itemtype='NetworkName'`), coerente con core `glpi/src/Report.php`. Colonna IP di Linked/Missing ora corretta. 4. **Bug 4 — Chiave cache sessione non univoca** (`AssetTab.php:374`): `$cache_key` includeva solo server id+nome client; ora `urbackup_data_{serverid}_{itemtype}_{items_id}`, evita cross-contaminazione tra asset omonimi. 5. **Bug 5 — Query Missing/Unlinked non filtrate per server** (`Server.php:1047` e `:1245`): aggiunto `WHERE plugin_urbackup_servers_id = server corrente` in `showUnlinkedClientsTab()` e `showMissingClientsTab()`, così asset collegati ad altri server non interferiscono. 6. **Bug 6 — server_test.ajax.php auth + CSRF** (`front/server_test.ajax.php`): sostituito `Profile::canCurrentUser(UPDATE)` (non entity-aware) con `$server->check($id, UPDATE)` in try/catch → 403 JSON; creato `public/js/urbackup.js` (registrato via `Hooks::ADD_JAVASCRIPT` in setup.php) che legge meta `glpi:csrf_token` e invia header `X-Glpi-Csrf-Token` su ogni POST AJAX del plugin (pre-requisito del listener GLPI 11 `CheckCsrfListener`). 7. **Bug 7 — dropdown_host.ajax.php info-disclosure** (`front/dropdown_host.ajax.php`): aggiunto check `Profile::canCurrentUser(READ)`. 8. **Traduzioni**: aggiunta la nuova stringa "Client not found on UrBackup server. Check that the asset name matches the client name." a it_IT/de_DE/en_GB `.po`, ricompilati i `.mo` (msgfmt). Versione header coerente (0.7.3). Changelog README aggiornato. - Verifica IA: `php -l` OK su tutti i file; `git diff` autorevisione OK; `Hooks::ADD_JAVASCRIPT` verificato in `src/Glpi/Plugin/Hooks.php:60`; meta `glpi:csrf_token` verificato in `templates/layout/parts/head.html.twig:68`. **Verifica UI utente OBBLIGATORIA** (vedi checklist). ## 31/08/2026 — Fix campi API username/password non editabili in prod (solo UI) - **Sintomo utente**: installato il plugin sul server produttivo, nel form Server non si vede il campo API username e il campo API password è bloccato (asterischi fissi non editabili), anche per l'utente che ha installato il plugin. - **Causa**: divergenza nel controllo diritti. I front usavano `Profile::canCurrentUser()` (legge i diritti da `glpi_profilerights` su DB), ma `Server::showFormFields()` (form server) usava `Session::haveRight(self::$rightname, UPDATE)` per calcolare `$canEdit` (e idem `rawSearchOptions()` riga 308 e `showUnlinkedClientsTab()`/`showMissingClientsTab()` righe 1155/1222). `Session::haveRight` dipende dalla **cache dei diritti in sessione** (`$_SESSION['glpiactiveprofile']['rights']`), popolata al login. Dopo l'installazione/aggiornamento del plugin i diritti sono scritti in DB ma la **sessione corrente non viene ricostruita**, quindi `haveRight` resta false → ramo read-only: username stampato come testo (invisibile se vuoto), password `******` fissa. - **Fix**: sostituito `Session::haveRight(self::$rightname, ...)` con `Profile::canCurrentUser(...)` in Server.php (righe ~308, ~447-449, ~1155, ~1222) e AssetTab.php (riga ~446, tab Asset), coerente col resto del plugin, legge dal DB e aggira la cache di sessione stantia. - Nessun bump versione (nessuna modifica DB, regola 6); nessuna nuova stringa i18n. - Verifica IA: `php -l` OK; git diff autorevisione OK. **Verifica UI utente OBBLIGATORIA**: aggiornare il file su prod, poi testare il form Server (API username editabile + API password campo input con placeholder). ## 25/08/2026 — Blocco "This asset hosts the UrBackup server" in evidenza (solo UI) - **Richiesta utente**: mettere in evidenza con riquadro colorato il blocco host + nome del server UrBackup. - **Modifiche**: `AssetTab.php::showHostServerBlock()` — classi dedicate `plugin-urbackup-host-card` / `-card-header` sul card Bootstrap e `plugin-urbackup-host-server-link` sui link server; `public/css/urbackup.css` — card con bordo 2px #1e6091, header blu #1e6091 testo bianco, body #eaf3fa, link server bold 1.05rem. - Nessun bump versione (nessuna modifica DB, regola 6); nessuna nuova stringa i18n. - Verifiche IA: `php -l` OK; git diff autorevisione OK. **Verifica visiva browser: CONFERMATA dall'utente il 25/08/2026** ("ok funziona tutto"). ## 07/08/2026 — Fix dropdown "Hardware host" (0.7.3): elemento non compariva - **Sintomo utente**: scelto il tipo host (es. Computer) nel form server, il dropdown degli elementi non compariva. - **Causa 1 (405)**: `Ajax::updateItemOnSelectEvent` genera `$("#target").load(url, {params})` — jQuery `.load()` con data **oggetto** invia **POST**, mentre `front/dropdown_host.ajax.php` era GET-only (405) → div mai popolato. (Nota: il CSRF non era il blocco per `.load()`: il global `$(document).ajaxSend` in public/js/common.js aggiunge `X-Glpi-Csrf-Token` a OGNI POST AJAX, letto da `getAjaxCsrfToken()` sul ``.) - **Causa 2 (500)**: `Session::getMatchingActiveEntities()` richiede esattamente 1 argomento (è un **filtro** di entity, `@since 10.0.13`) → usare `Session::getActiveEntities()` (Session.php:2194). - **Causa 3 (parametro sbagliato)**: in `Dropdown::show` il parametro per limitare le entità è **`entity`** (default -1, riga 133), NON `entity_restrict` (che è solo l'output serializzato del config, riga 287). - **Causa 4 (scriptBlock NON emesso)**: `Html::scriptBlock()` in GLPI 11 **ritorna** la stringa (`return sprintf(...)`, Html.php:5145) — serve `echo Html::scriptBlock(...)`; senza echo lo script sparisce silenziosamente. - **Fix**: JS inline nel form via `echo Html::scriptBlock(...)`: `$(document).on('change', '#dropdown_host_itemtype$rand', ...)` → `$.get(PLUGIN_URBACKUP_WEB_DIR.'/front/dropdown_host.ajax.php', {itemtype, value:0})` → `.html()` del div `urbackup_host_items$rand` (GET = bodyless → nessun check CSRF; jQuery esegue gli script inline iniettati, incluso il `$(function(){})` del config select2). Rimosso `use Ajax;` (non più usato). - **Lezione GLPI 11 — i dropdown sono LAZY**: `Dropdown::show()` NON renderizza più le `