final - speedup code for api
This commit is contained in:
@@ -1,218 +1,43 @@
|
|||||||
# UrBackup Plugin - Memoria di Sviluppo
|
# MEMORY.md - Stato del Plugin UrBackup
|
||||||
|
|
||||||
## Informazioni Plugin
|
## Ultima modifica: 22/05/2026
|
||||||
- **Nome**: UrBackup
|
|
||||||
- **Versione**: 0.5.0
|
|
||||||
- **Compatibilità**: GLPI 11.0.6+, PHP 8.3+
|
|
||||||
- **Namespace**: `GlpiPlugin\Urbackup`
|
|
||||||
|
|
||||||
## Struttura File
|
## Performance - Asset Definition vs Computer
|
||||||
```
|
|
||||||
plugin_urbackup/
|
|
||||||
├── setup.php # Hook init, menu, tab, versione, prerequisiti
|
|
||||||
├── hook.php # Install/uninstall, massive actions, class list
|
|
||||||
├── composer.json # PSR-4 autoloading
|
|
||||||
├── src/
|
|
||||||
│ ├── Config.php # Configurazione plugin (Computer sempre attivo, capacità)
|
|
||||||
│ ├── Profile.php # Diritti utente
|
|
||||||
│ ├── Server.php # CRUD server UrBackup
|
|
||||||
│ ├── ServerAsset.php # Linking asset-server
|
|
||||||
│ ├── AssetTab.php # Tab UrBackup su asset GLPI
|
|
||||||
│ ├── MassiveAction.php # Azioni massive connect/disconnect
|
|
||||||
│ ├── UrbackupApiClient.php # Client API UrBackup
|
|
||||||
│ ├── LocationHelper.php # Helper assegnazione server per location
|
|
||||||
│ ├── Capacity/
|
|
||||||
│ │ └── UrBackupCapacity.php # Capacity per Asset Definition GLPI 11
|
|
||||||
│ ├── Controller/
|
|
||||||
│ │ ├── ServerController.php
|
|
||||||
│ │ ├── AssetController.php
|
|
||||||
│ │ └── ConfigController.php
|
|
||||||
│ └── Command/
|
|
||||||
│ └── TestApiCommand.php
|
|
||||||
├── install/
|
|
||||||
│ ├── install.php # Schema DB, migrazioni, capacity migration
|
|
||||||
│ ├── uninstall.php # Drop tabelle, pulizia diritti
|
|
||||||
│ └── mysql/
|
|
||||||
│ └── plugin_urbackup-empty.sql # Schema iniziale
|
|
||||||
├── front/
|
|
||||||
│ ├── config.form.php # Config plugin (Computer info)
|
|
||||||
│ ├── server.php # Lista server
|
|
||||||
│ ├── server.form.php # Form server con tab laterali
|
|
||||||
│ └── asset.form.php # Azioni asset (connect/disconnect/backup)
|
|
||||||
├── ajax/
|
|
||||||
│ └── server_test.php # Test connessione API
|
|
||||||
├── templates/
|
|
||||||
│ └── config/
|
|
||||||
│ └── config.html.twig # Config page Twig
|
|
||||||
├── locales/ # Traduzioni (it, en, de)
|
|
||||||
├── css/ & js/ # Asset frontend
|
|
||||||
└── MEMORY.md # Questo file
|
|
||||||
```
|
|
||||||
|
|
||||||
## Classi Principali
|
### Problema
|
||||||
| Classe | Descrizione |
|
Gli Asset Definition custom (GLPI 11) sono 2-5x più lenti dei Computer nativi nel caricamento del tab UrBackup.
|
||||||
|--------|-------------|
|
|
||||||
| `Config` | Configurazione plugin, isItemtypeEnabled |
|
|
||||||
| `Profile` | Diritti utente, permessi |
|
|
||||||
| `Server` | CRUD server UrBackup |
|
|
||||||
| `ServerAsset` | Linking asset-server |
|
|
||||||
| `AssetTab` | Tab UrBackup su asset |
|
|
||||||
| `MassiveAction` | Azioni massive connect/disconnect |
|
|
||||||
| `UrbackupApiClient` | Client API UrBackup (PBKDF2 auth) |
|
|
||||||
| `LocationHelper` | Assegnazione server per location |
|
|
||||||
| `Capacity/UrBackupCapacity` | Capacity per Asset Definition (cleanup + tracking) |
|
|
||||||
| `Controller/ServerController` | Route Symfony per server |
|
|
||||||
| `Controller/AssetController` | Route Symfony per azioni asset |
|
|
||||||
| `Command/TestApiCommand` | CLI test API |
|
|
||||||
|
|
||||||
## Interfacce GLPI
|
### Causa
|
||||||
- Menu: Amministrazione → Server UrBackup
|
L'overhead è in GLPI 11 core, non nel plugin:
|
||||||
- Tab UrBackup su Computer (sempre) + tutti gli Asset Definition
|
1. **`Asset::__construct()`** — itera 30+ Capacity classi per ogni istanza
|
||||||
- Config: Computer sempre attivo; Asset Definition gestiti via native Capacities UI
|
2. **`Asset::post_getFromDB()`** — decodifica JSON custom fields e li processa
|
||||||
|
3. **`eval()` autoloading** — le classi concrete sono definite via `eval()` a runtime
|
||||||
|
|
||||||
## Architettura Capacities (v0.5.0)
|
### Ottimizzazioni applicate
|
||||||
|
1. **`AssetTab.php::loadApiData()`** — letto `$item->fields['name']` direttamente invece di chiamare `ServerAsset::getAssetName()` che faceva una seconda `getFromDB()` ridondante
|
||||||
|
2. **`AssetTab.php::startBackup()`, `saveInternetMode()`, `saveDefaultDirs()`, `showServerLinkedBlock()`** — stesso pattern, `$item->fields['name']` al posto di `getAssetName()`
|
||||||
|
3. **`Server.php::showMissingClientsTab()`** — batch loading IP e gruppi: `batchLoadIps()` (1 query per itemtype vs 1 per riga) + `batchLoadGroups()` (1 query per itemtype vs 1 per riga)
|
||||||
|
|
||||||
### Flusso
|
## Architettura
|
||||||
```
|
- `src/Capacity/UrBackupCapacity.php` — registra `AssetTab` via `CommonGLPI::registerStandardTab()` in `onClassBootstrap()`
|
||||||
Kernel boot: PostBootEvent
|
- `setup.php` — registra capacità, CSS, JS, hook
|
||||||
├── (110) InitializePlugins → plugin_init_urbackup()
|
- `src/AssetTab.php` — display tab content + tab interni (Stato/Azioni/Info-Log)
|
||||||
│ ├── Plugin::registerClass(AssetTab, ['Computer']) ← Computer legacy
|
- `src/ServerAsset.php` — gestione collegamenti asset-server
|
||||||
│ └── AssetDefinitionManager::registerCapacity() ← UrBackupCapacity registrata
|
- `src/Config.php` — itemtype enabled check
|
||||||
│
|
- `src/UrbackupApiClient.php` — client API con caching in-memory (per istanza) e sessione
|
||||||
├── (100) CustomObjectsBoot → bootDefinitions()
|
- `src/LocationHelper.php` — risoluzione location radice
|
||||||
│ └── foreach definition con UrBackupCapacity abilitata:
|
- `src/Server.php` — CRUD server, tab missing clients, form
|
||||||
│ └── UrBackupCapacity::onClassBootstrap($classname)
|
|
||||||
│ └── CommonGLPI::registerStandardTab($classname, AssetTab::class)
|
|
||||||
│ ↑ Tab registrato QUI, dopo che le definizioni sono caricate dal DB
|
|
||||||
│
|
|
||||||
Config::isItemtypeEnabled($itemtype)
|
|
||||||
├── '' → false
|
|
||||||
├── 'Computer' → true
|
|
||||||
├── Asset subclass → true ← SEMPRE visibile
|
|
||||||
└── altro → false
|
|
||||||
│
|
|
||||||
UrBackupCapacity
|
|
||||||
├── onClassBootstrap() → registra AssetTab sul definition classname
|
|
||||||
├── onCapacityDisabled() → pulisce ServerAsset entries
|
|
||||||
├── isUsed() / getCapacityUsageDescription() → UI Capacities
|
|
||||||
```
|
|
||||||
|
|
||||||
### Separazione responsabilità
|
## Asset Tab Interni
|
||||||
- **Registrazione tab**: `UrBackupCapacity::onClassBootstrap()` — chiamata da `bootDefinitions()` per ogni definizione con capacità abilitata
|
- 3 sub-tab: Stato, Azioni (solo UPDATE/CREATE), Info/Log
|
||||||
- **Visibilità tab**: `Config::isItemtypeEnabled()` — sempre true per Asset subclasses
|
- CSS: tab con tonalità di grigio differenti (scuro/medio/chiaro)
|
||||||
- **Pulizia**: `UrBackupCapacity::onCapacityDisabled()` — quando admin disabilita la capacità
|
- Caricamento dati API con caching sessione 30s
|
||||||
- **Config**: Computer sempre attivo; Capacities gestite dalla UI nativa GLPI
|
- Dati caricati: status, settings, authkey, backup recenti (10), log (50)
|
||||||
|
|
||||||
### Bug risolto: tab non registrato su Asset Definition nuovi
|
## Cache
|
||||||
- **Causa**: `setup.php` iterava `getDefinitions()` durante `plugin_init_urbackup()`, ma `bootDefinitions()` (che carica le definizioni dal DB) gira DOPO (priority 100 vs 110), quindi `getDefinitions()` tornava sempre array vuoto
|
- `UrbackupApiClient`: cache in-memory per `getStatus()` e `getClientSettings()`
|
||||||
- **Fix**: La registrazione del tab è stata spostata in `UrBackupCapacity::onClassBootstrap()`, chiamata da `bootstrapDefinition()` durante `bootDefinitions()`, quando le definizioni sono già caricate e la classe dinamica è disponibile
|
- `AssetTab::loadApiData()`: cache sessione 30s (chiave: server_id + client_name)
|
||||||
- **Rimosso**: loop `foreach ($defs ...)` e debug logging da `setup.php`
|
- API timeout: 30s, connect timeout: 5s
|
||||||
- **Rimosso**: debug logging da `AssetTab.php`
|
|
||||||
|
|
||||||
## Tabella glpi_plugin_urbackup_assettypes
|
## Versione
|
||||||
- Colonna `is_default` rimossa (non serve più con capacities)
|
- 0.6.0
|
||||||
- Tabella vestigiale — non più usata per la logica applicativa
|
|
||||||
- Migrazione: `plugin_urbackup_install_convert_assettypes_to_capacities()` abilita capacità su tutte le definizioni
|
|
||||||
|
|
||||||
## Comandi Utili
|
|
||||||
```bash
|
|
||||||
# Installare il plugin
|
|
||||||
php bin/console glpi:plugin:install urbackup
|
|
||||||
|
|
||||||
# Attivare
|
|
||||||
php bin/console glpi:plugin:activate urbackup
|
|
||||||
|
|
||||||
# Disinstallare
|
|
||||||
echo "yes" | php bin/console glpi:plugin:uninstall urbackup
|
|
||||||
|
|
||||||
# Verificare syntax PHP
|
|
||||||
php -l plugins/urbackup/src/*.php plugins/urbackup/front/*.php plugins/urbackup/*.php
|
|
||||||
```
|
|
||||||
|
|
||||||
## Checklist Pre-Consegna
|
|
||||||
- [x] `declare(strict_types=1);` in ogni file PHP
|
|
||||||
- [x] Namespace `GlpiPlugin\Urbackup\` e PSR-4 corretto
|
|
||||||
- [x] Check versione GLPI 11.0.6+ in `setup.php`
|
|
||||||
- [x] CSRF e permessi su ogni POST/AJAX
|
|
||||||
- [x] Query parametrizzate o `$DB->request()`
|
|
||||||
- [x] Output escaped e loggato
|
|
||||||
- [x] Nessun uso di API deprecate GLPI 11
|
|
||||||
- [x] Compatibilità PHP 8.3 verificata
|
|
||||||
|
|
||||||
## Da Completare
|
|
||||||
1. Test funzionalità lista server e form
|
|
||||||
2. AJAX endpoint comunicazione API
|
|
||||||
|
|
||||||
### Verifiche Post-Migrazione Capacity
|
|
||||||
- [x] Tab UrBackup appare su Asset Definition nuovo (Computer + Asset)
|
|
||||||
- [ ] Upgrade da v0.4.x converte righe attive
|
|
||||||
- [ ] Colonna `is_default` droppata
|
|
||||||
- [ ] Disabilitazione capacità pulisce ServerAsset (onCapacityDisabled)
|
|
||||||
|
|
||||||
## API UrBackup - Riferimenti
|
|
||||||
|
|
||||||
### Endpoint API
|
|
||||||
| Metodo | Descrizione |
|
|
||||||
|--------|-------------|
|
|
||||||
| `login` | Autenticazione username/password |
|
|
||||||
| `get_status` | Lista client con stato backup |
|
|
||||||
| `get_backups` | Lista backup per client |
|
|
||||||
| `start_backup` | Avvia backup (file/image) |
|
|
||||||
| `get_progress` | Monitora progresso backup |
|
|
||||||
| `get_clients` | Lista clienti |
|
|
||||||
| `get_groups` | Lista gruppi |
|
|
||||||
|
|
||||||
### Problemi Noti
|
|
||||||
- JSON Parse Error: API restituisce HTML invece di JSON con credenziali errate
|
|
||||||
- Timeout: Verificare raggiungibilità server UrBackup
|
|
||||||
- SSL: Se `ignore_ssl` attivo, accettare certificati self-signed
|
|
||||||
- AJAX 403: Funziona solo da browser con sessione GLPI attiva
|
|
||||||
|
|
||||||
## Cronologia Modifiche
|
|
||||||
|
|
||||||
### v0.4.2 — Refactoring server.form.php e tab laterali
|
|
||||||
- Side tabs a sinistra con nav-pills
|
|
||||||
- 4 tab: Server, Linked, Unlinked, Missing clients
|
|
||||||
- Missing clients con search/sort, location/AIP/group/state
|
|
||||||
- Navigazione server sopra i tab, indipendente
|
|
||||||
- Breadcrumb admin
|
|
||||||
- Fix namespace `getCachedName()`, Gruppo da `glpi_groups_items`
|
|
||||||
- Permessi: READ per accesso form, Connect/API nascosti per READ
|
|
||||||
- i18n: 17 stringhe nuove (it, en, de)
|
|
||||||
- Bug fix: ServerAsset colonne, asset.form.php disconnectAsset(), strict_types in 11 file
|
|
||||||
|
|
||||||
### v0.4.6 — Funzionalità API completate
|
|
||||||
- Salvataggio Internet Mode su server UrBackup
|
|
||||||
- Salvataggio Default Dirs
|
|
||||||
- Backup file (incremental/full)
|
|
||||||
- Backup immagine (incremental/full)
|
|
||||||
- Recupero version client da API
|
|
||||||
|
|
||||||
### v0.4.5 — Tabella semplificata serverassets
|
|
||||||
- Rimossi campi: client_name, client_ip, client_version, is_active, last_file_backup, last_image_backup, last_sync, date_creation, date_mod, urbackup_client_id
|
|
||||||
- Solo: id, plugin_urbackup_servers_id, itemtype, items_id
|
|
||||||
|
|
||||||
### v0.4.4 — Tab Linked/Unlinked Clients
|
|
||||||
- Dati da API UrBackup invece che da DB
|
|
||||||
- Nome tabella corretto: `glpi_plugin_urbackup_serverassets`
|
|
||||||
- Campo: `plugin_urbackup_servers_id`
|
|
||||||
|
|
||||||
### v0.4.3 — Test API Connection automatico
|
|
||||||
- Rimosso pulsante manuale
|
|
||||||
- Rimosse righe last status/message/check
|
|
||||||
- Stati: verde OK, rosso fallito, giallo irraggiungibile
|
|
||||||
|
|
||||||
### v0.5.0 — Capacity System per Asset Definition GLPI 11
|
|
||||||
- Rimpiazzata tabella custom `glpi_plugin_urbackup_assettypes` con Capacities native
|
|
||||||
- Computer: legacy (sempre attivo, fuori capacities)
|
|
||||||
- Asset Definition: tab sempre registrato, capacità gestisce cleanup
|
|
||||||
- `isItemtypeEnabled()` = true per tutti gli Asset
|
|
||||||
- `onClassBootstrap()` rimosso (poi reintrodotto in v0.5.1)
|
|
||||||
- File: `src/Capacity/UrBackupCapacity.php`
|
|
||||||
- Migration: auto-enable capacità su tutte le definizioni + drop is_default
|
|
||||||
|
|
||||||
### v0.5.1 — Fix registrazione tab su Asset Definition nuovi
|
|
||||||
- **Bug**: Tab UrBackup non appariva su Asset Definition nuovi (es. `testnas`)
|
|
||||||
- **Causa**: `getDefinitions()` tornava 0 in `plugin_init_urbackup()` perché `bootDefinitions()` gira dopo (priority 100 vs 110)
|
|
||||||
- **Fix**: Spostata registrazione tab in `UrBackupCapacity::onClassBootstrap()` — chiamata durante `bootDefinitions()` per ogni definizione con capacità abilitata
|
|
||||||
- Rimossi debug logging superflui da `setup.php` e `AssetTab.php`
|
|
||||||
|
|||||||
@@ -10,3 +10,46 @@
|
|||||||
color: #b00;
|
color: #b00;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link {
|
||||||
|
font-weight: 600;
|
||||||
|
border-width: 2px;
|
||||||
|
padding: 8px 18px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link#state-tab {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #4a4a4a;
|
||||||
|
border-color: #4a4a4a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link#state-tab.active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
border-color: #2d2d2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link#actions-tab {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #6c757d;
|
||||||
|
border-color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link#actions-tab.active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #495057;
|
||||||
|
border-color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link#logs-tab {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #adb5bd;
|
||||||
|
border-color: #adb5bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#urbackupTabs .nav-link#logs-tab.active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #6c757d;
|
||||||
|
border-color: #6c757d;
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use GlpiPlugin\Urbackup\Server;
|
|||||||
use GlpiPlugin\Urbackup\ServerAsset;
|
use GlpiPlugin\Urbackup\ServerAsset;
|
||||||
use GlpiPlugin\Urbackup\MassiveAction as PluginUrbackupMassiveAction;
|
use GlpiPlugin\Urbackup\MassiveAction as PluginUrbackupMassiveAction;
|
||||||
|
|
||||||
define('PLUGIN_URBACKUP_VERSION', '0.5.0');
|
define('PLUGIN_URBACKUP_VERSION', '0.6.0');
|
||||||
define('PLUGIN_URBACKUP_MIN_GLPI', '11.0.0');
|
define('PLUGIN_URBACKUP_MIN_GLPI', '11.0.0');
|
||||||
define('PLUGIN_URBACKUP_MAX_GLPI', '11.99.99');
|
define('PLUGIN_URBACKUP_MAX_GLPI', '11.99.99');
|
||||||
|
|
||||||
|
|||||||
+22
-12
@@ -232,7 +232,7 @@ class AssetTab extends CommonDBTM
|
|||||||
echo "<td>" . htmlspecialchars(__('UrBackup server version', 'urbackup')) . "</td>";
|
echo "<td>" . htmlspecialchars(__('UrBackup server version', 'urbackup')) . "</td>";
|
||||||
echo "<td>" . htmlspecialchars((string) ($server->fields['server_version'] ?? '')) . "</td>";
|
echo "<td>" . htmlspecialchars((string) ($server->fields['server_version'] ?? '')) . "</td>";
|
||||||
echo "<td>" . htmlspecialchars(__('Client name', 'urbackup')) . "</td>";
|
echo "<td>" . htmlspecialchars(__('Client name', 'urbackup')) . "</td>";
|
||||||
echo "<td>" . htmlspecialchars(ServerAsset::getAssetName($item::class, (int) $item->fields['id'])) . "</td>";
|
echo "<td>" . htmlspecialchars((string) ($item->fields['name'] ?? '')) . "</td>";
|
||||||
echo "</tr>";
|
echo "</tr>";
|
||||||
|
|
||||||
echo "</table>";
|
echo "</table>";
|
||||||
@@ -263,9 +263,14 @@ class AssetTab extends CommonDBTM
|
|||||||
*/
|
*/
|
||||||
private static function loadApiData(CommonDBTM $item, Server $server, array $link): array
|
private static function loadApiData(CommonDBTM $item, Server $server, array $link): array
|
||||||
{
|
{
|
||||||
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
|
$client_name = (string) ($item->fields['name'] ?? '');
|
||||||
$asset_ip = ServerAsset::extractAssetIp($item);
|
$asset_ip = ServerAsset::extractAssetIp($item);
|
||||||
|
|
||||||
|
$cache_key = 'urbackup_data_' . $server->fields['id'] . '_' . $client_name;
|
||||||
|
if (isset($_SESSION[$cache_key]) && $_SESSION[$cache_key]['time'] > time() - 30) {
|
||||||
|
return $_SESSION[$cache_key]['data'];
|
||||||
|
}
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'client_found' => false,
|
'client_found' => false,
|
||||||
'client_status' => [],
|
'client_status' => [],
|
||||||
@@ -309,6 +314,11 @@ class AssetTab extends CommonDBTM
|
|||||||
$data['error'] = $e->getMessage();
|
$data['error'] = $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_SESSION[$cache_key] = [
|
||||||
|
'time' => time(),
|
||||||
|
'data' => $data,
|
||||||
|
];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,19 +342,19 @@ class AssetTab extends CommonDBTM
|
|||||||
|
|
||||||
$canWrite = Session::haveRight(self::$rightname, UPDATE) || Session::haveRight(self::$rightname, CREATE);
|
$canWrite = Session::haveRight(self::$rightname, UPDATE) || Session::haveRight(self::$rightname, CREATE);
|
||||||
|
|
||||||
echo '<ul class="nav nav-tabs" id="urbackupTabs">';
|
echo '<ul class="nav nav-tabs" id="urbackupTabs" role="tablist">';
|
||||||
echo '<li class="nav-item">';
|
echo '<li class="nav-item" role="presentation">';
|
||||||
echo '<a class="nav-link active" id="state-tab" data-bs-toggle="tab" href="#state" role="tab">';
|
echo '<a class="nav-link active" id="state-tab" data-bs-toggle="tab" href="#state" role="tab" aria-selected="true">';
|
||||||
echo htmlspecialchars(__('State', 'urbackup'));
|
echo htmlspecialchars(__('State', 'urbackup'));
|
||||||
echo '</a></li>';
|
echo '</a></li>';
|
||||||
if ($canWrite) {
|
if ($canWrite) {
|
||||||
echo '<li class="nav-item">';
|
echo '<li class="nav-item" role="presentation">';
|
||||||
echo '<a class="nav-link" id="actions-tab" data-bs-toggle="tab" href="#actions" role="tab">';
|
echo '<a class="nav-link" id="actions-tab" data-bs-toggle="tab" href="#actions" role="tab" aria-selected="false" tabindex="-1">';
|
||||||
echo htmlspecialchars(__('Actions', 'urbackup'));
|
echo htmlspecialchars(__('Actions', 'urbackup'));
|
||||||
echo '</a></li>';
|
echo '</a></li>';
|
||||||
}
|
}
|
||||||
echo '<li class="nav-item">';
|
echo '<li class="nav-item" role="presentation">';
|
||||||
echo '<a class="nav-link" id="logs-tab" data-bs-toggle="tab" href="#logs" role="tab">';
|
echo '<a class="nav-link" id="logs-tab" data-bs-toggle="tab" href="#logs" role="tab" aria-selected="false" tabindex="-1">';
|
||||||
echo htmlspecialchars(__('Info / Log', 'urbackup'));
|
echo htmlspecialchars(__('Info / Log', 'urbackup'));
|
||||||
echo '</a></li>';
|
echo '</a></li>';
|
||||||
echo '</ul>';
|
echo '</ul>';
|
||||||
@@ -835,7 +845,7 @@ class AssetTab extends CommonDBTM
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
|
$client_name = (string) ($item->fields['name'] ?? '');
|
||||||
if ($client_name === '') {
|
if ($client_name === '') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -873,7 +883,7 @@ class AssetTab extends CommonDBTM
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
|
$client_name = (string) ($item->fields['name'] ?? '');
|
||||||
if ($client_name === '') {
|
if ($client_name === '') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -903,7 +913,7 @@ class AssetTab extends CommonDBTM
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
|
$client_name = (string) ($item->fields['name'] ?? '');
|
||||||
if ($client_name === '') {
|
if ($client_name === '') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-13
@@ -1125,6 +1125,7 @@ class Server extends CommonDBTM
|
|||||||
|
|
||||||
$itemtypes = Config::getEnabledItemtypes();
|
$itemtypes = Config::getEnabledItemtypes();
|
||||||
$missingAssets = [];
|
$missingAssets = [];
|
||||||
|
$candidatesByType = [];
|
||||||
|
|
||||||
foreach ($itemtypes as $itemtype) {
|
foreach ($itemtypes as $itemtype) {
|
||||||
if (!class_exists($itemtype)) {
|
if (!class_exists($itemtype)) {
|
||||||
@@ -1174,25 +1175,34 @@ class Server extends CommonDBTM
|
|||||||
$locationName = self::getCachedLocationName($assetLocationId, $cacheLocation);
|
$locationName = self::getCachedLocationName($assetLocationId, $cacheLocation);
|
||||||
$stateName = self::getCachedName('State', (int) ($assetRow['states_id'] ?? 0), $cacheState);
|
$stateName = self::getCachedName('State', (int) ($assetRow['states_id'] ?? 0), $cacheState);
|
||||||
$userName = self::getCachedName('User', (int) ($assetRow['users_id'] ?? 0), $cacheUser);
|
$userName = self::getCachedName('User', (int) ($assetRow['users_id'] ?? 0), $cacheUser);
|
||||||
$groupName = self::getAssetGroupName($itemtype, $assetId, $cacheGroup);
|
|
||||||
|
|
||||||
$ip = self::getAssetIp($itemtype, $assetId);
|
|
||||||
|
|
||||||
$missingAssets[] = [
|
$missingAssets[] = [
|
||||||
'itemtype' => $itemtype,
|
'itemtype' => $itemtype,
|
||||||
'items_id' => $assetId,
|
'items_id' => $assetId,
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'entity' => $entityName,
|
'entity' => $entityName,
|
||||||
'location' => $locationName,
|
'location' => $locationName,
|
||||||
'otherserial' => (string) ($assetRow['otherserial'] ?? ''),
|
'otherserial' => (string) ($assetRow['otherserial'] ?? ''),
|
||||||
'ip' => $ip,
|
'state' => $stateName,
|
||||||
'state' => $stateName,
|
'user' => $userName,
|
||||||
'user' => $userName,
|
|
||||||
'group' => $groupName,
|
|
||||||
];
|
];
|
||||||
|
$candidatesByType[$itemtype][] = $assetId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$batchIps = self::batchLoadIps($candidatesByType);
|
||||||
|
$batchGroups = self::batchLoadGroups($candidatesByType);
|
||||||
|
|
||||||
|
foreach ($missingAssets as &$asset) {
|
||||||
|
$key = $asset['itemtype'] . ':' . $asset['items_id'];
|
||||||
|
$asset['ip'] = $batchIps[$key] ?? '';
|
||||||
|
$groupId = $batchGroups[$key] ?? 0;
|
||||||
|
$asset['group'] = $groupId > 0
|
||||||
|
? self::getCachedName('Group', $groupId, $cacheGroup)
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
unset($asset);
|
||||||
|
|
||||||
if (count($missingAssets) === 0) {
|
if (count($missingAssets) === 0) {
|
||||||
echo '<div class="alert alert-success">';
|
echo '<div class="alert alert-success">';
|
||||||
echo htmlspecialchars(__('All assets in this location are linked or already on the UrBackup server.', 'urbackup'));
|
echo htmlspecialchars(__('All assets in this location are linked or already on the UrBackup server.', 'urbackup'));
|
||||||
@@ -1325,6 +1335,102 @@ JAVASCRIPT;
|
|||||||
return $cache[$id];
|
return $cache[$id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch-load IPs for multiple assets across itemtypes.
|
||||||
|
*
|
||||||
|
* @param array<string, list<int>> $candidatesByType itemtype => [items_id, ...]
|
||||||
|
*
|
||||||
|
* @return array<string, string> key "itemtype:items_id" => IP
|
||||||
|
*/
|
||||||
|
private static function batchLoadIps(array $candidatesByType): array
|
||||||
|
{
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$ips = [];
|
||||||
|
|
||||||
|
foreach ($candidatesByType as $itemtype => $ids) {
|
||||||
|
if (count($ids) === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterator = $DB->request([
|
||||||
|
'SELECT' => ['np.items_id', 'ipa.name'],
|
||||||
|
'FROM' => 'glpi_ipaddresses AS ipa',
|
||||||
|
'INNER JOIN' => [
|
||||||
|
'glpi_networknames AS nn' => [
|
||||||
|
'ON' => [
|
||||||
|
'nn' => 'items_id',
|
||||||
|
'ipa' => 'id',
|
||||||
|
['AND' => ['ipa.itemtype' => 'NetworkName']],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'glpi_networkports AS np' => [
|
||||||
|
'ON' => [
|
||||||
|
'np' => 'id',
|
||||||
|
'nn' => 'items_id',
|
||||||
|
['AND' => ['nn.itemtype' => 'NetworkPort']],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'WHERE' => [
|
||||||
|
'np.itemtype' => $itemtype,
|
||||||
|
'np.items_id' => $ids,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($iterator as $row) {
|
||||||
|
$ip = (string) ($row['name'] ?? '');
|
||||||
|
if ($ip === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$key = $itemtype . ':' . $row['items_id'];
|
||||||
|
if (!isset($ips[$key])) {
|
||||||
|
$ips[$key] = $ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ips;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Batch-load group IDs for multiple assets across itemtypes.
|
||||||
|
*
|
||||||
|
* @param array<string, list<int>> $candidatesByType itemtype => [items_id, ...]
|
||||||
|
*
|
||||||
|
* @return array<string, int> key "itemtype:items_id" => groups_id
|
||||||
|
*/
|
||||||
|
private static function batchLoadGroups(array $candidatesByType): array
|
||||||
|
{
|
||||||
|
global $DB;
|
||||||
|
|
||||||
|
$groups = [];
|
||||||
|
|
||||||
|
foreach ($candidatesByType as $itemtype => $ids) {
|
||||||
|
if (count($ids) === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterator = $DB->request([
|
||||||
|
'FROM' => 'glpi_groups_items',
|
||||||
|
'WHERE' => [
|
||||||
|
'itemtype' => $itemtype,
|
||||||
|
'items_id' => $ids,
|
||||||
|
'type' => \Group_Item::GROUP_TYPE_NORMAL,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
foreach ($iterator as $row) {
|
||||||
|
$key = $itemtype . ':' . $row['items_id'];
|
||||||
|
if (!isset($groups[$key])) {
|
||||||
|
$groups[$key] = (int) ($row['groups_id'] ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
|
||||||
private static function getAssetGroupName(string $itemtype, int $items_id, array &$cache): string
|
private static function getAssetGroupName(string $itemtype, int $items_id, array &$cache): string
|
||||||
{
|
{
|
||||||
global $DB;
|
global $DB;
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ class UrbackupApiClient
|
|||||||
|
|
||||||
private int $lastlogid = 0;
|
private int $lastlogid = 0;
|
||||||
|
|
||||||
|
private ?array $cached_status = null;
|
||||||
|
|
||||||
|
private array $cached_settings = [];
|
||||||
|
|
||||||
private string $server_version = '';
|
private string $server_version = '';
|
||||||
|
|
||||||
private bool $is_version_2_4_or_higher = false;
|
private bool $is_version_2_4_or_higher = false;
|
||||||
@@ -247,21 +251,29 @@ class UrbackupApiClient
|
|||||||
*/
|
*/
|
||||||
public function getStatus(): array
|
public function getStatus(): array
|
||||||
{
|
{
|
||||||
|
if ($this->cached_status !== null) {
|
||||||
|
return $this->cached_status;
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->apiAction('status');
|
$data = $this->apiAction('status');
|
||||||
|
|
||||||
if (isset($data['status']) && is_array($data['status'])) {
|
if (isset($data['status']) && is_array($data['status'])) {
|
||||||
return array_values($data['status']);
|
$this->cached_status = array_values($data['status']);
|
||||||
|
return $this->cached_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($data['clients']) && is_array($data['clients'])) {
|
if (isset($data['clients']) && is_array($data['clients'])) {
|
||||||
return array_values($data['clients']);
|
$this->cached_status = array_values($data['clients']);
|
||||||
|
return $this->cached_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_is_list($data)) {
|
if (array_is_list($data)) {
|
||||||
return $data;
|
$this->cached_status = $data;
|
||||||
|
return $this->cached_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
$this->cached_status = [];
|
||||||
|
return $this->cached_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -317,16 +329,22 @@ class UrbackupApiClient
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($this->cached_settings[$client_id])) {
|
||||||
|
return $this->cached_settings[$client_id];
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->apiAction('settings', [
|
$data = $this->apiAction('settings', [
|
||||||
'sa' => 'clientsettings',
|
'sa' => 'clientsettings',
|
||||||
't_clientid' => $client_id,
|
't_clientid' => $client_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isset($data['settings']) && is_array($data['settings'])) {
|
if (isset($data['settings']) && is_array($data['settings'])) {
|
||||||
return $data['settings'];
|
$this->cached_settings[$client_id] = $data['settings'];
|
||||||
|
return $this->cached_settings[$client_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
$this->cached_settings[$client_id] = $data;
|
||||||
|
return $this->cached_settings[$client_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -338,6 +356,11 @@ class UrbackupApiClient
|
|||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
|
private function clearSettingsCache(int $client_id): void
|
||||||
|
{
|
||||||
|
unset($this->cached_settings[$client_id]);
|
||||||
|
}
|
||||||
|
|
||||||
public function changeClientSetting(string $client_name, string $key, mixed $value): bool
|
public function changeClientSetting(string $client_name, string $key, mixed $value): bool
|
||||||
{
|
{
|
||||||
$client_id = $this->getClientIdByName($client_name);
|
$client_id = $this->getClientIdByName($client_name);
|
||||||
@@ -353,6 +376,7 @@ class UrbackupApiClient
|
|||||||
$key => (string) $value,
|
$key => (string) $value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->clearSettingsCache($client_id);
|
||||||
return $this->responseIsSuccess($data);
|
return $this->responseIsSuccess($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,6 +395,7 @@ class UrbackupApiClient
|
|||||||
$key => $value,
|
$key => $value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->clearSettingsCache($client_id);
|
||||||
return $this->responseIsSuccess($data);
|
return $this->responseIsSuccess($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,6 +418,7 @@ class UrbackupApiClient
|
|||||||
|
|
||||||
$data = $this->apiAction('settings', $params);
|
$data = $this->apiAction('settings', $params);
|
||||||
|
|
||||||
|
$this->clearSettingsCache($client_id);
|
||||||
return $this->responseIsSuccess($data);
|
return $this->responseIsSuccess($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user