Files
urbackup/GLPIDEV.md_netbackup
T
2026-08-04 11:24:39 +02:00

343 lines
18 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GLPIDEV.md — API GLPI 11.0.8 Reference (per plugin netbackup)
> **DA LEGGERE ALL'INIZIO DI OGNI SESSIONE DI LAVORO**, insieme a SKILL.md e MEMORY.md.
> File riassuntivo dell'API GLPI 11 usata dal plugin, generato analizzando il core reale in `/var/www/glpi` (versione **11.0.8**).
> Verificato su: `/var/www/glpi/src`, `/var/www/glpi/inc`, `/var/www/glpi/plugins/netbackup`.
---
## 1. Ambiente
| Voce | Valore |
|------|--------|
| GLPI | 11.0.8 (`/var/www/glpi`) |
| PHP | 8.2+ |
| Struttura | tutto il codice core in `src/` (PSR-4, namespace `Glpi\`); `inc/includes.php` bootstrap; `front/`, `ajax/`, `routes/` (Symfony routing), `templates/` Twig, `var/` cache/log |
| Plugin | `GlpiPlugin\Netbackup\` (PSR-4 via composer.json) |
**Regola chiave**: in GLPI 11 il codice procedurale e le classi legacy di GLPI 9/10 in `inc/` NON esistono più — le classi core sono in `src/` (es. `Glpi\...`). L'unica classe globale restante in `inc/` è `includes.php`.
---
## 2. Database Layer
### 2.1 `$DB` globale
- `global $DB;` — istanza `class DB extends DBmysql` (generata in `config/` da `DBConnection`, vedi `src/DBConnection.php:164`).
- Sotto: mysqli. `$DB->update()` ritorna **sempre `true`** → per verificare l'esito usare `$DB->affectedRows()` (legge `mysqli::$affected_rows`). Verificato: pattern `claimJob` in `src/BackupJob.php` usa `$DB->affectedRows() === 1` su UPDATE condizionale `WHERE status='pending'`.
### 2.2 Query builder (lettura)
```php
$iterator = $DB->request([
'FROM' => self::getTable(),
'WHERE' => [
'networkequipments_id' => $id,
'is_active' => 1,
'OR' => [
['field' => ['LIKE', '%x%']],
['field' => null],
],
],
'ORDER' => ['date DESC', 'id DESC'],
'LIMIT' => 10,
'OFFSET' => 0,
'LEFT JOIN' => [
'glpi_networkequipments' => [
'FKEY' => ['glpi_plugin_netbackup_equipments' => 'networkequipments_id', 'glpi_networkequipments' => 'id'],
],
],
'COUNT' => 'cpt', // SELECT COUNT(*) AS cpt
]);
foreach ($iterator as $row) { ... } // iterazione diretta
$iterator->count(); // numero righe
$iterator->numrows(); // alias
$iterator->fetchFields();
$iterator->current(); // riga corrente
```
- `DBmysqlIterator` (`src/DBmysqlIterator.php`) implementa `SeekableIterator, Countable`.
- **Mai concatenare variabili nelle query**: i criteri vengono parametrizzati dal builder.
- `COUNT` con `$DB->request()` è il pattern per i controlli di esistenza idempotenti (vedi MEMORY.md, `insert_missing_vendors()`).
### 2.3 Scrittura
```php
$DB->insert($table, $params); // INSERT
$DB->update($table, $params, $where); // UPDATE (ritorna true sempre → affectedRows)
$DB->delete($table, $where); // DELETE
$DB->updateOrInsert($table, $params, $where, $onlyone = true);
```
### 2.4 DDL e introspezione
```php
$DB->doQuery("ALTER TABLE ... ADD COLUMN ..."); // DDL — query() è DEPRECATO in GLPI 11
$DB->doQueryOrDie($query, $message);
$DB->tableExists($tablename); // introspezione (cache)
$DB->fieldExists($table, $field);
$DB->getField(string $table, string $field, $usecache = true): ?array; // ritorna l'array dei campi della tabella
$DB->insertId();
$DB->affectedRows();
```
- **`$DB->query()` deprecato** → usare `$DB->doQuery()`.
- **`$DB->runFile()` deprecato** → MAI usare (MEMORY.md: uninstall stabile con TRUNCATE + doQuery, mai runFile).
### 2.5 Pattern di migrazione
- `migrate_tables()` idempotente: ogni `ADD COLUMN` guardato da `tableExists()`/`fieldExists()`.
- Ogni `update_X_Y_Z()` chiama `plugin_netbackup_migrate_tables()` + funzioni di inserimento dati idempotenti (check `COUNT` prima di INSERT).
- MAI inserire dati nel DB con SQL manuale / `mysql` CLI: solo logica di migrazione PHP.
---
## 3. Session & Sicurezza
### 3.1 Diritti
```php
Session::checkRight($module, $right); // muore con errore 403 se senza diritto (protezione front/*.php)
Session::checkRightsOr($module, $rights = []);
Session::checkLoginUser(); // solo login richiesto
Session::haveRight($module, $right); // booleano (senza morte)
Session::haveRightsAnd($module, $rights);
Session::haveRightsOr($module, $rights);
Session::getLoginUserID(); // id utente corrente
Session::getPluralNumber(); // per stringhe pluralizzate
Session::addMessageAfterRedirect(...); // messaggi UI post-redirect
Session::getNewCSRFToken(bool $standalone = false);
```
- **IMPORTANTE**: nei file con namespace plugin (`GlpiPlugin\Netbackup\...`) importare `use Session;` (e `Html`, `Toolbox`, `GLPIKey`, ecc.), altrimenti PHP risolve `GlpiPlugin\Netbackup\Session` che non esiste.
### 3.2 CSRF
- Hook `Hooks::CSRF_COMPLIANT = 'csrf_compliant'` registrato in `setup.php` → GLPI gestisce i token automaticamente per i form del plugin.
- Nei POST manuali: campo hidden `_glpi_csrf_token` con `Session::getNewCSRFToken()`.
- Tutti i form POST del plugin validano il token (pattern: `Html::hidden('_glpi_csrf_token', Session::getNewCSRFToken())`).
### 3.3 Profili e diritti (pattern verificato v1.6.x)
- `Profile::registerRights()` → `ProfileRight::addProfileRights()` → **bump `last_rights_update`** per tutti i profili, altrimenti la sessione non si aggiorna (`Session::haveRight` torna false — bug sessione stale, MEMORY.md "Fix Session Rights").
- `Profile::initProfile()` (hook `Hooks::CHANGE_PROFILE = 'change_profile'`) sincronizza i diritti di sessione dopo il cambio profilo.
- `plugin_init_netbackup()` confronta DB vs sessione a ogni page load e aggiorna se diverso.
- Right costanti: `READ`, `UPDATE`, `CREATE`, `DELETE`, `PURGE`, `ALLSTANDARDRIGHT`.
---
## 4. Criptazione Segreti
**⚠️ `Toolbox::encrypt()/decrypt()` NON ESISTE in GLPI 11** (verificato: nessuna funzione encrypt/decrypt in `src/Toolbox.php`). API corretta:
```php
use GLPIKey;
GLPIKey::getInstance()->encrypt(string $string, ?string $key = null): string;
GLPIKey::getInstance()->decrypt(?string $string, ?string $key = null): ?string;
```
- `src/GLPIKey.php:432` / `:463`. Per compatibilità: `decryptUsingLegacyKey()` (`:526`).
- Il plugin usa già `GLPIKey` (6 occorrenze in src/).
- Mai segreti in chiaro nel DB; mai chiavi hardcoded nel codice.
---
## 5. Html & Escaping
```php
Html::header(...); // header pagina (con titolo)
Html::footer(...);
Html::back(); // pulsante indietro
Html::redirect($url);
Html::hidden($name, $value); // campo hidden
Html::submit($name, $value); // pulsante submit
Html::scriptBlock($js); // blocco script (polling JS del plugin)
Html::convDate($date);
Html::convDateTime($date);
Html::displayRightError(); // errore diritti insufficienti
```
### Escaping (XSS)
- `Html::cleanInputText($value)` — input testuali.
- `Html::entities_deep($array)` — sanitizzazione array di input.
- `htmlescape($string)` (global helper GLPI) / `htmlspecialchars()` — output.
- Twig: auto-escaping (vedi §11).
---
## 6. Dropdown & Search
### 6.1 Dropdown
```php
Dropdown::showFromArray($name, $values, $options); // dropdown generico da array
Dropdown::showYesNo($name, $value);
Dropdown::getDropdownName($table, $id); // nome dropdown da id
```
### 6.2 Search (colonne custom NetworkEquipment)
Hook di GLPI in `hook.php`:
- `plugin_netbackup_getAddSearchOptions(string $itemtype): array` — registra le search options 82008204 con `'jointype' => 'child'` su `glpi_plugin_netbackup_equipments` (`alias.networkequipments_id = glpi_networkequipments.id`).
- `plugin_netbackup_giveItem(string $type, int $ID, array $data, string $num): string` — rendering colonne (`giveItem`).
- `plugin_netbackup_searchOptionsValues(array $PARAM): bool` — hook `Hooks::AUTO_SEARCH_OPTION_VALUES = 'searchOptionsValues'`.
**⚠️ CAVEAT verificato (MEMORY.md)**: l'hook `searchOptionsValues` NON viene mai chiamato da GLPI per i dropdown `datatype => 'specific'` — il core usa sempre l'output di `getValueToSelect()` (input). **Il pattern funzionante è l'override del metodo `Equipment::getSpecificValueToSelect()`** che ritorna `Dropdown::showFromArray(...)`. Non rimuovere l'override!
- datatype usati: `'bool'` (8200), `'specific'` nosearch (8201), `'varchar'` (8202), `'datetime'` (8203), `'specific'` con `searchtype => ['equals','empty']` (8204).
- `Search::getOptions($itemtype)` / `Search::show($itemtype, $params)` disponibili nel core (`src/Search.php`).
---
## 7. CommonDBTM / CommonDBChild / CommonGLPI
### 7.1 Classi base
- `CommonDBTM` — tabella + CRUD generico.
- `CommonDBChild` — riga figlia di un item (pattern: `glpi_plugin_netbackup_equipments.networkequipments_id`).
- `CommonDropdown` — dropdown.
- `CommonGLPI` — item senza tabella (tab, UI).
### 7.2 Metodi lifecycle sovrascritti nel plugin
```php
getTypeName($nb = 0); // nome tipo (traducibile, _n())
getIcon(): string; // icona (ti ti-*)
getEmpty(); // riga vuota con default
prepareInputForAdd($input); // sanitizzazione/validazione pre-add
prepareInputForUpdate($input);
post_addItem() / post_updateItem() / post_purgeItem();
getField($field); // valore campo dalla riga caricata
getAdditionalFields();
showForm($ID, $options = []); // form edit
```
### 7.3 Tabs (integrazione su NetworkEquipment e Profile)
```php
getTabNameForItem(CommonGLPI $item, $withtemplate = 0): string; // ritorna ['1' => 'Backup settings']
displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0): bool;
```
Pattern verificato in `src/Equipment.php:48-55` (tab "Backup settings" su NetworkEquipment) e `src/Profile.php` (tab diritti).
### 7.4 CRUD istanza
```php
$obj = new MyClass();
$obj->getEmpty();
$obj->add($input);
$obj->update($input);
$obj->delete($input, $force = false);
$obj->getFromDB($id);
$obj->getField('colonna');
$obj->getTable(); // nome tabella
```
### 7.5 Massive Actions
Hook `Hooks::USE_MASSIVE_ACTION = 'use_massive_action'` in `setup.php` + hook `'massiveaction'` in hook.php.
- `getSpecificMassiveActions()` → array `['key' => __('label'), 'sep' => ..., 'classe' => ...]` (separatore `MassiveAction::CLASS_ACTION_SEPARATOR`).
- `showMassiveActionsSubForm(MassiveAction $ma)` — form parametri.
- `processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids)` — esecuzione.
- Ritorni: `MassiveAction::ACTION_OK` / `MassiveAction::ACTION_KO` (classe `src/MassiveAction.php`).
- Azioni del plugin: `activate_netbackup`, `deactivate_netbackup`, `enable_custom`, `disable_custom`, `bulk_set_custom`, `manual_backup`, `kill_stuck_workers`.
---
## 8. Bootstrap Plugin
### 8.1 setup.php
```php
function plugin_init_netbackup(): void {
global $PLUGIN_HOOKS;
$PLUGIN_HOOKS[Hooks::CSRF_COMPLIANT]['netbackup'] = true;
$PLUGIN_HOOKS[Hooks::CHANGE_PROFILE]['netbackup'] = [Profile::class, 'initProfile'];
$PLUGIN_HOOKS[Hooks::USE_MASSIVE_ACTION]['netbackup'] = 1;
Plugin::registerClass(BackupJob::class, ['notificationtemplates_types' => true]);
// ...
}
function plugin_version_netbackup(): array { /* name, version, requires, author, license, homepage */ }
```
- `set_time_limit(0)` a top-level di setup.php (non in hook) — evita timeout durante uninstall su DB grandi.
- Versionamento: `PLUGIN_NETBACKUP_VERSION` in setup.php, aggiornato a ogni release.
### 8.2 hook.php — funzioni standard
| Funzione | Ruolo |
|----------|-------|
| `plugin_netbackup_init()` | ogni page load: ensureDefaults vendor, warning configs/ non scrivibile, check patch vendor phpseclib |
| `plugin_netbackup_install()` | install: schema, defaults, notifiche, cron |
| `plugin_netbackup_uninstall()` | TRUNCATE prima di DROP, CronTask::unregister, cancellazione notifiche |
| `plugin_netbackup_migrate_tables()` | schema idempotente (tableExists/fieldExists guard) |
| `plugin_netbackup_update_X_Y_Z()` | migrazioni di versione (ogni step singolo, chiama migrate_tables) |
| `plugin_netbackup_getAddSearchOptions()` | search options NetworkEquipment |
| `plugin_netbackup_giveItem()` | rendering colonne lista |
| `plugin_netbackup_searchOptionsValues()` | hook AUTO_SEARCH_OPTION_VALUES (non scatta per 'specific' — vedi §6.2) |
| `plugin_netbackup_getDropdown()` | hook `AUTO_GET_DROPDOWN = 'getDropdown'` |
---
## 9. Cron
```php
CronTask::register(string $itemtype, string $name, int $frequency, array $options = []): bool;
CronTask::unregister(string $plugin);
```
- Core: `src/CronTask.php:966` (`register`, lowercase; PHP è case-insensitive, `CronTask::Register` funziona).
- `$itemtype` = classe del plugin con i metodi cron (`'GlpiPlugin\Netbackup\Cron'`), `$name` = nome task.
- `$options`: `allowmode` (`MODE_INTERNAL | MODE_EXTERNAL`), `mode`, `state`, `param`, `hourmin`, `hourmax`, `comment`.
- Se `GLPI_SYSTEM_CRON` è definito e allowmode ha MODE_EXTERNAL → `mode = MODE_EXTERNAL`.
- Callback (in `src/Cron.php`):
```php
public static function cronInfo($name): array; // ['name' => ..., 'description' => ..., 'state' => 0|1]
public static function cronNetbackup($task): int; // ritorna numero job enqueueati (0 = ok)
```
- **REGOLA ARCHITETTURALE (MEMORY.md)**: il cron GLPI NON esegue MAI SSH — solo enqueue in `glpi_plugin_netbackup_backupjobs`. Il worker CLI (`front/worker.php`, crontab) esegue materialmente i backup.
- Task registrati: `Netbackup` (60s), `NetbackupReport` (86400s).
---
## 10. Notifiche
- `Plugin::registerClass(BackupJob::class, ['notificationtemplates_types' => true])` → `NotificationTargetBackupJob` auto-scoperto dall'itemtype.
- Classe: `src/NotificationTargetBackupJob.php` estende `NotificationTarget`:
- `getEvents()` → `['backup_success' => ..., 'backup_failed' => ..., 'backup_warning' => ..., 'backup_report' => ...]`
- `addDataForTemplate()` → placeholders `##device.name_html##`, `##device.status_html##`, ecc. (devono corrispondere ESATTAMENTE al template DB)
- `getTags()` → lista tag
- `addAdditionalTargets()` → registra `GLOBAL_ADMINISTRATOR`, `ENTITY_ADMINISTRATOR`
- `getEventsToSendImmediately()` → override: `backup_success`, `backup_failed`, `backup_warning`, `backup_report` → invio immediato, NON in coda `glpi_queuednotifications`
- Emissione: `NotificationEvent::raiseEvent('backup_success', $backup, ['entities_id' => ...])` — solo per backup manuali (`users_id > 0`); i backup schedulati dal cron NON emettono notifiche per-device (fix v1.4.1).
- Template in DB: `glpi_notificationtemplates`, `glpi_notificationtemplatetranslations` (EN/IT/DE).
- **REGOLA UPGRADE**: mai sovrascrivere notifiche personalizzate — `plugin_netbackup_backup_notifications_to_sql()` (backup SQL in `backups/notifications/`) PRIMA di qualsiasi operazione; `install_notifications_netbackup()` skippa se esistono; `plugin_netbackup_regenerate_notifications()` solo per forzatura esplicita.
---
## 11. Twig / TemplateRenderer
```php
use Glpi\Application\View\TemplateRenderer;
$twig = TemplateRenderer::getInstance();
$twig->display('@netbackup/profile.html.twig', ['key' => $value]);
```
- Namespace template plugin: `@netbackup/` (directory `templates/` del plugin).
- Auto-escaping Twig attivo: `{{ var }}` escapato; mai logica PHP nei template.
- Pattern verificato in `src/Profile.php:41-42`.
---
## 12. Integrazione NetworkEquipment & IP
- Tabelle custom legate a `glpi_networkequipments.id` (FKEY).
- **⚠️ `glpi_networkequipments.ip` NON esiste in GLPI 11** — gli IP vivono in `glpi_ipaddresses` (IPAM, `src/IPAddress.php`). Il plugin risolve l'IP dal tab IPAM del device.
- Tab "Backup settings" via `getTabNameForItem`/`displayTabContentForItem` (§7.3).
- Search options con `'jointype' => 'child'` + nome tabella corretto (con 's') — un custom `condition` da solo causa join auto-FK sbagliato (MEMORY.md).
- Massive actions integrate nella lista NetworkEquipment (§7.5).
---
## 13. Caveat GLPI 11 verificati (raccolti da MEMORY.md + core)
1. `$DB->query()` **deprecato** → `$DB->doQuery()` per SQL raw.
2. `$DB->runFile()` **deprecato** → mai usare (uninstall: TRUNCATE prima di DROP).
3. `Toolbox::encrypt/decrypt` **non esiste** → `GLPIKey::getInstance()->encrypt()/decrypt()`.
4. `CronTask::Register/Unregister` → `CronTask::register/unregister` (case-insensitive).
5. `Session`/`Html`/`Toolbox` ecc. sono classi globali → `use Session;` nei namespace plugin.
6. `$DB->update()` ritorna sempre `true` → verificare con `$DB->affectedRows()`.
7. GLPI environment enum: `production`, `development`, `testing`, `staging`, `e2e_testing` — MAI `prod`.
8. CSRF: hook `Hooks::CSRF_COMPLIANT` + token `_glpi_csrf_token` nei POST.
9. Hook `searchOptionsValues` (AUTO_SEARCH_OPTION_VALUES) non scatta per datatype `specific` → override `getSpecificValueToSelect()`.
10. `set_time_limit(0)` in setup.php top-level (non in hook) — uninstall su DB grandi.
11. Notifiche: se cancellate e ricreate si perdono i destinatari → backup SQL prima.
12. Heredoc e `__()`: NON chiamare `__()` dentro heredoc — pre-calcolare le stringhe tradotte.
13. phpseclib patchate (`vendor/phpseclib/.../Net/SSH2.php`): riapplicare dopo ogni `composer update` (applicatore in `update_1_6_7`, verifica via `scripts/run_tests.php`).
14. `status` nei DB del plugin: Backup usa `success`/`failed`/`warning`; BackupJob usa `pending`/`running`/`success`/`failed`.
15. `config_data` è `LONGTEXT` (da 1.6.10) — nessun rischio troncamento; il flusso è file-first (`files/_plugins/netbackup/configs/*.cfg`).
16. Permessi `configs/`: directory deve essere scrivibile da `www-data` (`sudo chown -R www-data:www-data files/_plugins/netbackup/`); warning in `plugin_netbackup_init()` se non scrivibile.
---
## 14. Conclusione
Questo file è la mappa dell'API GLPI 11 usata dal plugin. Se una modifica del core GLPI richiede nuove funzioni, aggiornare questo file e verificare la firma reale in `/var/www/glpi/src/` prima di scrivere codice.