diff --git a/AGENTS_OLD.MD b/AGENTS_OLD.MD deleted file mode 100644 index 4f21aa1..0000000 --- a/AGENTS_OLD.MD +++ /dev/null @@ -1,66 +0,0 @@ -# Istruzioni per lo Sviluppo PHP 8.3 & Symfony - -Sei un esperto Senior Developer specializzato in Symfony (6.4+) e PHP 8.3. - -## Regole del Codice (PHP 8.3) - -- Usa sempre la **Constructor Promotion** per la Dependency Injection. - -- Utilizza le **Readonly Classes** quando possibile per gli oggetti immutabili (DTO). - -- Applica **Typed Class Constants** (novità PHP 8.3). - -- Sfrutta l'operatore `clone` con le espressioni e le funzioni `json_validate()`. - -- Rigorosa tipizzazione: usa `declare(strict_types=1);` in ogni nuovo file. - -## Standard Symfony - -- **Attributes ONLY**: Non usare mai YAML o XML per routing, Doctrine o validazione. Usa solo PHP Attributes. - -- **Service Container**: Prediligi l'autowiring. - -- **Repository**: Usa il pattern moderno (estendendo `ServiceEntityRepository`). - -- **Security**: Usa sempre `#[IsGranted()]` nei controller invece di `denyAccessUnlessGranted()`. - -## Workflow di Risoluzione Errori - -1. Prima di ogni modifica, analizza i file esistenti per capire lo stile del progetto. - -2. Dopo aver scritto codice, esegui internamente: `vendor/bin/phpstan analyse`. - -3. Se ci sono errori di stile, correggi con: `vendor/bin/ecs check --fix`. - -4. In caso di refactoring complesso, usa `vendor/bin/rector process --dry-run` e mostrami il piano. - -## Memoria degli Errori - -- Leggi sempre il file `FIX_HISTORY.md` per non ripetere bug già risolti in passato. - -- Ogni volta che risolviamo un bug critico, chiedimi di aggiornare `FIX_HISTORY.md`. - - -## Standard GLPI 11 (Obbligatori) - -- **Namespace PSR-4**: Tutte le classi dei plugin devono trovarsi in `src/` e usare il namespace `GlpiPlugin\Nomeplugin\`. La cartella `inc/` è deprecata. - -- **Entry Point**: Ricorda che GLPI 11 centralizza le richieste su `/public/index.php`. Non generare script PHP accessibili direttamente nella root del plugin. - -- **Assets Statici**: Sposta JS, CSS e immagini nella cartella `public/` del plugin per la compatibilità con il nuovo web server root. - -- **Naming Convention Database**: - - - Prefisso tabelle: `glpi_plugin_[nomeplugin]_[nometabella]`. - - - Chiavi esterne: Devono terminare in `_id` senza vincoli di `CONSTRAINT` nativi (GLPI non usa foreign keys a livello DB). - -- **Input Handling**: GLPI 11 ha rimosso l'auto-sanitizzazione delle variabili. Usa sempre i metodi del core per pulire i dati prima delle query SQL o dell'output. - -## Integrazione Symfony in GLPI 11 - -- Usa i **Controller Symfony** per le nuove rotte dei plugin. - -- Sfrutta il **Twig Template Engine** situato in `templates/` per la UI. - -- Definisci i comandi CLI tramite il componente Console di Symfony. diff --git a/FIX_PERMISSIONS.sh b/FIX_PERMISSIONS.sh deleted file mode 100755 index b92df03..0000000 --- a/FIX_PERMISSIONS.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Fix file permissions for GLPI plugin -echo "Fixing file permissions..." - -# Fix ownership -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/src/Server.php -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/src/Profile.php -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/src/MassiveAction.php -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/src/AssetTab.php -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/hook.php -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/setup.php - -# Replace hook.php with corrected version -if [ -f /var/www/glpi/plugins/urbackup/hook_corrected.php ]; then - sudo cp /var/www/glpi/plugins/urbackup/hook_corrected.php /var/www/glpi/plugins/urbackup/hook.php - sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/hook.php - rm /var/www/glpi/plugins/urbackup/hook_corrected.php -fi - -# Fix all src/ files -sudo chown www-data:www-data /var/www/glpi/plugins/urbackup/src/*.php - -echo "Permissions fixed. Now add declare(strict_types=1) to:" -echo " - src/Server.php" -echo " - src/Profile.php" -echo " - src/MassiveAction.php" -echo " - src/AssetTab.php" diff --git a/MEMORY.md b/MEMORY.md index 50a8402..2d4c43a 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -1,6 +1,6 @@ # MEMORY.md - Stato del Plugin UrBackup -## Ultima modifica: 27/05/2026 +## Ultima modifica: 28/05/2026 ## Performance - Asset Definition vs Computer @@ -53,4 +53,13 @@ In una nuova installazione del plugin, i campi "API username" e "API password" n 2. **`Profile.php::installRights()`** — In assenza di sessione attiva (CLI), cerca il profilo "Super-Admin" tramite query diretta e gli assegna tutti i diritti. ## Versione -- 0.6.1 +- 0.7.0 + +## 0.7.0 — Pulizia, sicurezza e DB cleanup +1. **CSRF hardening**: `Session::checkCSRF()` aggiunto su `asset.form.php`, `server.form.php`, `server_test.ajax.php`, `config.form.php` +2. **File deprecati rimossi**: 12 file (composer copy, AGENTS_OLD.MD, js/, ajax/, front/test/view, FIX_PERMISSIONS.sh, removed/) +3. **Dead code rimosso**: 3 Controller, 1 Command, 4 template Twig +4. **DB migration**: DROP `glpi_plugin_urbackup_profiles` e `glpi_plugin_urbackup_assettypes`; add index `location_active` su servers; add `date_creation`/`date_mod` su serverassets +5. **SQL schema pulito**: Rimosse tabelle legacy dall'empty.sql +6. **PHP lint warnings**: Rimosse `use Html;` e `use Search;` superflue +7. **README.md** creato diff --git a/README.md b/README.md index edcc639..63ed461 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,130 @@ -# UrBackup Plugin for GLPI 11 +# UrBackup plugin for GLPI 11 -Plugin **UrBackup** per GLPI 11, sviluppato in PHP 8.3, che consente la gestione -centralizzata dei server UrBackup e dei client direttamente dall’interfaccia GLPI. +[![GLPI](https://img.shields.io/badge/GLPI-11.0.6%2B-blue?logo=glpi)](https://glpi-project.org) +[![PHP](https://img.shields.io/badge/PHP-8.3%2B-777BB4?logo=php)](https://php.net) +[![License](https://img.shields.io/badge/License-GPL--2.0--or--later-green)](LICENSE) -Il plugin permette di: -- collegare asset GLPI (Computer e altri asset) ai server UrBackup; -- visualizzare lo stato dei backup; -- eseguire azioni UrBackup (backup, gestione client, impostazioni); -- gestire i server UrBackup da GLPI; -- integrare ACL complete per profili GLPI; -- supportare multi‑lingua (IT / EN / DE). +Integrate [UrBackup](https://www.urbackup.org/) backup server management directly into GLPI. Monitor clients, manage backups, and link assets to UrBackup servers from within GLPI's asset management interface. ---- +## Features -## ✅ Compatibilità +- **Server management** – Register UrBackup servers by location; test API connectivity. +- **Asset linking** – Link Computers (and other GLPI 11 asset types) to UrBackup clients. +- **Backup actions** – Start file/image incremental and full backups directly from the asset form. +- **Client lifecycle** – Create, rename, and delete UrBackup clients from GLPI. +- **Internet mode** – Toggle internet mode and configure default backup directories per asset. +- **Capacity system** – Native GLPI 11 integration via `UrBackupCapacity`; enable per Asset Definition. +- **Location-aware** – Auto-match assets to servers based on location hierarchy. +- **Massive actions** – Link/unlink assets to servers in bulk. -| Componente | Versione | -|-----------|----------| -| GLPI | 11.x | -| PHP | ≥ 8.3 | -| Database | MySQL / MariaDB (GLPI standard) | +## Requirements ---- +| Component | Version | +|-----------|---------| +| **GLPI** | >= 11.0.6, < 12.0.0 | +| **PHP** | >= 8.3.0 (8.4 supported) | +| **Database** | MySQL 5.7+ / MariaDB 10.5+ | -## ✅ Funzionalità principali +## Installation -### 🔧 Configurazione plugin -- Percorso: **Configurazione → Plugin → UrBackup** -- Asset supportati: - - Computer (sempre attivo) - - Altri asset GLPI configurabili -- Attivazione automatica tab e massive action sugli asset abilitati +1. **Download** the plugin and extract it to `GLPI_ROOT/plugins/urbackup/` +2. **Install** via GLPI web interface or CLI: ---- +```bash +php bin/console glpi:plugin:install urbackup +php bin/console glpi:plugin:activate urbackup +``` -### 🗄️ Gestione server UrBackup +3. **Configure rights** – Go to *Administration > Profiles*, select a profile, and set *UrBackup* rights (READ / UPDATE / CREATE / DELETE / PURGE). +4. **Add servers** – Navigate to *Admin > UrBackup servers* and register your UrBackup instances. +5. **Enable capacity** – Go to *Config > Asset definitions*, open an asset type, and enable *UrBackup* in the Capacities tab. + +## Usage + +### Linking assets to a server + +- Open an asset (Computer, etc.) and go to the **UrBackup** tab. +- Select a server from the dropdown and click **Connect**. +- The asset appears in the server's *Linked clients* tab and backup actions become available. + +### Starting a backup + +1. Open a linked asset and go to its UrBackup tab. +2. Click **Start file backup** (incremental) or **Start image backup**. +3. Monitor progress in the UrBackup server web interface. + +### Managing servers + +- **Add**: *Admin > UrBackup servers > Add* +- **Edit**: Click a server name or the edit icon. +- **Test API**: The plugin auto-tests the API when saving; status is shown in the list. + +## Permissions + +| Right | Description | +|-------------|-------------| +| `READ` | View servers and asset backup status | +| `UPDATE` | Edit servers, start backups, toggle internet mode | +| `CREATE` | Add new servers, create clients | +| `DELETE` | Disconnect assets from servers | +| `PURGE` | Delete clients from UrBackup server | + +## Development + +```bash +# Lint +php -l src/*.php front/*.php + +# Type checking (requires PHPStan) +vendor/bin/phpstan analyse --level 8 src/ + +# Database migration testing +# Install/uninstall/reinstall via CLI: +php bin/console glpi:plugin:install urbackup +php bin/console glpi:plugin:uninstall urbackup +``` + +## Project Structure + +``` +plugin_urbackup/ +├── front/ # Entry points (form handlers, AJAX endpoints) +├── install/ # DB schema (mysql/) and install/uninstall scripts +├── public/ # Static assets (CSS, JS) +├── src/ # PHP classes (PSR-4: Plugin\Urbackup\) +│ ├── AssetTab.php +│ ├── Capacity/ # GLPI 11 capacity integration +│ ├── Config.php +│ ├── LocationHelper.php +│ ├── Profile.php +│ ├── Server.php +│ ├── ServerAsset.php +│ └── UrbackupApiClient.php +├── templates/ # Twig templates +├── setup.php # Plugin metadata, hooks, class registration +├── hook.php # Install/upgrade/uninstall + massive actions +└── README.md +``` + +## Changelog + +### 0.7.0 +- Dropped legacy `glpi_plugin_urbackup_profiles` and `glpi_plugin_urbackup_assettypes` tables +- Added composite index `(locations_id, is_active)` on servers table +- Added `date_creation`/`date_mod` columns to serverassets table +- Removed deprecated files and dead Symfony controller code +- CSRF hardening on all POST handlers +- Bumped minimum PHP to 8.3 + +### 0.6.0 +- GLPI 11 compatibility with Asset Definition capacity system +- Migrated from Symfony to native GLPI form handling +- Added location-aware server matching + +### 0.5.0 +- Initial GLPI 10 compatibility +- Profile-based rights management + +## License + +GNU General Public License v2.0 or later. See [LICENSE](LICENSE). diff --git a/ajax/server_test.php b/ajax/server_test.php deleted file mode 100644 index 7932e20..0000000 --- a/ajax/server_test.php +++ /dev/null @@ -1,57 +0,0 @@ - false, 'message' => 'No permission']); - exit; -} - -$server_id = (int) ($_POST['id'] ?? $_GET['id'] ?? 0); - -if ($server_id <= 0) { - echo json_encode(['success' => false, 'message' => 'Invalid server ID']); - exit; -} - -$server = new GlpiPlugin\Urbackup\Server(); - -if (!$server->getFromDB($server_id)) { - echo json_encode(['success' => false, 'message' => 'Server not found']); - exit; -} - -try { - $client = new GlpiPlugin\Urbackup\UrbackupApiClient($server); - $result = $client->testConnection(); - - $server->update([ - 'id' => $server_id, - 'last_api_status' => $result['success'] ? 1 : 0, - 'last_api_message' => $result['message'], - 'last_api_check' => date('Y-m-d H:i:s'), - ]); - - echo json_encode($result); -} catch (Throwable $e) { - echo json_encode(['success' => false, 'message' => $e->getMessage()]); -} diff --git a/composer copy.json b/composer copy.json deleted file mode 100644 index 0a98ead..0000000 --- a/composer copy.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "finstral/glpi-urbackup-plugin", - "description": "UrBackup integration plugin for GLPI 11", - "type": "glpi-plugin", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=8.3" - }, - "autoload": { - "psr-4": { - "GlpiPlugin\\Urbackup\\": "src/" - } - }, - "config": { - "optimize-autoloader": true, - "sort-packages": true - }, - "extra": { - "glpi-plugin": { - "key": "urbackup" - } - } -} \ No newline at end of file diff --git a/front/ajax.test.php b/front/ajax.test.php deleted file mode 100644 index 4183ad2..0000000 --- a/front/ajax.test.php +++ /dev/null @@ -1,59 +0,0 @@ - false, 'message' => 'No permission']); - exit; -} - -$server_id = (int) (($_GET['id'] ?? $_POST['id'] ?? 0)); - -if ($server_id <= 0) { - echo json_encode(['success' => false, 'message' => 'Invalid server ID']); - exit; -} - -// Load plugin classes -$classes = ['Server', 'UrbackupApiClient']; -foreach ($classes as $class) { - $file = PLUGIN_URBACKUP_DIR . '/src/' . $class . '.php'; - if (file_exists($file)) { - require_once $file; - } -} - -$server = new \GlpiPlugin\Urbackup\Server(); - -if (!$server->getFromDB($server_id)) { - echo json_encode(['success' => false, 'message' => 'Server not found']); - exit; -} - -try { - $client = new \GlpiPlugin\Urbackup\UrbackupApiClient($server); - $result = $client->testConnection(); - - $server->update([ - 'id' => $server_id, - 'last_api_status' => $result['success'] ? 1 : 0, - 'last_api_message' => $result['message'], - 'last_api_check' => date('Y-m-d H:i:s'), - ]); - - echo json_encode($result); -} catch (Throwable $e) { - echo json_encode(['success' => false, 'message' => $e->getMessage()]); -} diff --git a/front/asset.form.php b/front/asset.form.php index db184e4..4d2a845 100644 --- a/front/asset.form.php +++ b/front/asset.form.php @@ -41,6 +41,8 @@ if (!$item || !$item->getFromDB($items_id)) { Html::displayNotFoundError(); } +Session::checkCSRF(); + if (isset($_POST['connect'])) { if (!Profile::canCurrentUser(UPDATE) && !Profile::canCurrentUser(CREATE)) { Html::displayRightError(); diff --git a/front/config.form.php b/front/config.form.php index 5041d4e..50b3aae 100644 --- a/front/config.form.php +++ b/front/config.form.php @@ -3,7 +3,6 @@ declare(strict_types=1); use GlpiPlugin\Urbackup\Config; -use Html; global $CFG_GLPI; @@ -14,6 +13,7 @@ if (!Session::haveRight('config', UPDATE)) { // Handle form submission if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update'])) { + Session::checkCSRF(); Config::saveConfiguration($_POST); Html::redirect($_SERVER['REQUEST_URI']); } diff --git a/front/server.form.php b/front/server.form.php index f812d15..7fdd0dd 100644 --- a/front/server.form.php +++ b/front/server.form.php @@ -19,6 +19,8 @@ if (!Profile::canCurrentUser(READ)) { $server = new Server(); if ($_SERVER['REQUEST_METHOD'] === 'POST') { + Session::checkCSRF(); + if (isset($_POST['link_asset'])) { $itemtype = (string) ($_POST['itemtype'] ?? ''); $items_id = (int) ($_POST['items_id'] ?? 0); diff --git a/front/server.php b/front/server.php index d4fc148..e2a68c3 100644 --- a/front/server.php +++ b/front/server.php @@ -4,8 +4,6 @@ declare(strict_types=1); use GlpiPlugin\Urbackup\Profile; use GlpiPlugin\Urbackup\Server; -use Html; -use Search; if (!defined('GLPI_ROOT')) { define('GLPI_ROOT', dirname(__DIR__, 4)); diff --git a/front/server.test.php b/front/server.test.php deleted file mode 100644 index 65790e7..0000000 --- a/front/server.test.php +++ /dev/null @@ -1,64 +0,0 @@ - false, 'message' => 'Unauthorized']); - exit; -} - -// Check rights -if (!Session::haveRight('plugin_urbackup', READ)) { - http_response_code(403); - echo json_encode(['success' => false, 'message' => 'Forbidden - No right plugin_urbackup READ']); - exit; -} - -$server_id = (int) (($_GET['id'] ?? $_POST['id'] ?? 0)); - -if ($server_id <= 0) { - echo json_encode(['success' => false, 'message' => 'Invalid server ID']); - exit; -} - -// Load plugin classes -$classes = ['Server', 'UrbackupApiClient']; -foreach ($classes as $class) { - $file = PLUGIN_URBACKUP_DIR . '/src/' . $class . '.php'; - if (file_exists($file)) { - require_once $file; - } -} - -$server = new \GlpiPlugin\Urbackup\Server(); - -if (!$server->getFromDB($server_id)) { - echo json_encode(['success' => false, 'message' => 'Server not found']); - exit; -} - -try { - $client = new \GlpiPlugin\Urbackup\UrbackupApiClient($server); - $result = $client->testConnection(); - - $server->update([ - 'id' => $server_id, - 'last_api_status' => $result['success'] ? 1 : 0, - 'last_api_message' => $result['message'], - 'last_api_check' => date('Y-m-d H:i:s'), - ]); - - echo json_encode($result); -} catch (Throwable $e) { - echo json_encode(['success' => false, 'message' => $e->getMessage()]); -} diff --git a/front/server.view.php b/front/server.view.php deleted file mode 100644 index 90a894f..0000000 --- a/front/server.view.php +++ /dev/null @@ -1,42 +0,0 @@ -getFromDB($ID)) { - Html::redirect(PLUGIN_URBACKUP_WEB_DIR . '/front/server.php'); -} - -Html::header( - $server->fields['name'] . ' - UrBackup', - '', - 'Assets', - 'GlpiPlugin\Urbackup\Server' -); - -$server->display([ - 'id' => $ID, - 'show_nav' => true, - 'show_tabs' => true, -]); - -Html::footer(); \ No newline at end of file diff --git a/front/server_test.ajax.php b/front/server_test.ajax.php index 79a7b97..8fc466a 100644 --- a/front/server_test.ajax.php +++ b/front/server_test.ajax.php @@ -13,6 +13,10 @@ Html::header_nocache(); Session::checkLoginUser(); +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + Session::checkCSRF(); +} + if (!Session::haveRight('plugin_urbackup', READ)) { echo json_encode(['success' => false, 'message' => __('No permission', 'urbackup')]); exit; diff --git a/front/test.button.php b/front/test.button.php deleted file mode 100644 index 1718114..0000000 --- a/front/test.button.php +++ /dev/null @@ -1,43 +0,0 @@ -No READ permission on plugin_urbackup

"; - Html::footer(); - exit; -} - -echo "

READ permission OK

"; - -// Get server ID -$server_id = (int) ($_GET['id'] ?? 0); - -if ($server_id > 0) { - $server = new GlpiPlugin\Urbackup\Server(); - if ($server->getFromDB($server_id)) { - echo "

Server: " . $server->fields['name'] . "

"; - - $client = new GlpiPlugin\Urbackup\UrbackupApiClient($server); - $result = $client->testConnection(); - - echo "
" . print_r($result, true) . "
"; - } else { - echo "

Server not found

"; - } -} else { - echo "

No server ID provided

"; -} - -Html::footer(); diff --git a/install/install.php b/install/install.php index 4473388..1f3d7c3 100644 --- a/install/install.php +++ b/install/install.php @@ -31,14 +31,15 @@ function plugin_urbackup_install_process(): bool plugin_urbackup_install_create_initial_schema($migration); plugin_urbackup_install_update_configs_table($migration); - plugin_urbackup_install_update_assettypes_table($migration); // Convert old assettype configurations to GLPI 11 capacity system plugin_urbackup_install_convert_assettypes_to_capacities($migration); plugin_urbackup_install_update_servers_table($migration); plugin_urbackup_install_update_serverassets_table($migration); - plugin_urbackup_install_update_profiles_table($migration); + + // Drop tables that are no longer used (replaced by Profile::registerRights() and Capacity system) + plugin_urbackup_install_drop_legacy_tables($migration); $migration->executeMigration(); @@ -61,10 +62,8 @@ function plugin_urbackup_install_create_initial_schema(Migration $migration): vo $required_tables = [ 'glpi_plugin_urbackup_configs', - 'glpi_plugin_urbackup_assettypes', 'glpi_plugin_urbackup_servers', 'glpi_plugin_urbackup_serverassets', - 'glpi_plugin_urbackup_profiles', ]; $missing_table_found = false; @@ -264,6 +263,7 @@ function plugin_urbackup_install_update_servers_table(Migration $migration): voi $migration->addKey($table, 'entities_id'); $migration->addKey($table, 'locations_id'); $migration->addKey($table, 'is_active'); + $migration->addKey($table, ['locations_id', 'is_active'], 'location_active'); } /** @@ -298,46 +298,39 @@ function plugin_urbackup_install_update_serverassets_table(Migration $migration) 'after' => 'itemtype', ]); + $migration->addField($table, 'date_creation', 'timestamp'); + $migration->addField($table, 'date_mod', 'timestamp'); + $migration->addKey($table, 'plugin_urbackup_servers_id'); $migration->addKey($table, ['itemtype', 'items_id'], 'item'); } /** - * Update profiles table. + * Drop legacy tables that are no longer used. * * @param Migration $migration Migration instance * * @return void */ -function plugin_urbackup_install_update_profiles_table(Migration $migration): void +function plugin_urbackup_install_drop_legacy_tables(Migration $migration): void { global $DB; + // glpi_plugin_urbackup_profiles was replaced by Profile::registerRights() in 0.5.0 $table = 'glpi_plugin_urbackup_profiles'; - - if (!$DB->tableExists($table)) { - return; + if ($DB->tableExists($table)) { + $migration->displayMessage(__('Dropping legacy glpi_plugin_urbackup_profiles table', 'urbackup')); + $DB->queryOrDie("DROP TABLE IF EXISTS `$table`"); } - $migration->addField($table, 'profiles_id', 'integer', [ - 'value' => 0, - 'after' => 'id', - ]); + // glpi_plugin_urbackup_assettypes was replaced by GLPI 11 Capacity system in 0.6.0 + $table2 = 'glpi_plugin_urbackup_assettypes'; + if ($DB->tableExists($table2)) { + $migration->displayMessage(__('Dropping legacy glpi_plugin_urbackup_assettypes table', 'urbackup')); + $DB->queryOrDie("DROP TABLE IF EXISTS `$table2`"); + } - $migration->addField($table, 'rightname', 'string', [ - 'value' => '', - 'after' => 'profiles_id', - ]); - - $migration->addField($table, 'rights', 'integer', [ - 'value' => 0, - 'after' => 'rightname', - ]); - - $migration->addField($table, 'date_creation', 'timestamp'); - $migration->addField($table, 'date_mod', 'timestamp'); - - $migration->addKey($table, ['profiles_id', 'rightname'], 'profile_right'); + $migration->displayMessage(__('Legacy tables dropped successfully', 'urbackup')); } /** diff --git a/install/mysql/plugin_urbackup-empty.sql b/install/mysql/plugin_urbackup-empty.sql index 52e7a86..ff173f3 100644 --- a/install/mysql/plugin_urbackup-empty.sql +++ b/install/mysql/plugin_urbackup-empty.sql @@ -8,17 +8,6 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_configs` ( KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_assettypes` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `itemtype` VARCHAR(255) NOT NULL DEFAULT '', - `is_active` TINYINT NOT NULL DEFAULT 0, - `date_creation` TIMESTAMP NULL DEFAULT NULL, - `date_mod` TIMESTAMP NULL DEFAULT NULL, - PRIMARY KEY (`id`), - KEY `itemtype` (`itemtype`), - KEY `is_active` (`is_active`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; - CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_servers` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `entities_id` INT UNSIGNED NOT NULL DEFAULT 0, @@ -43,7 +32,8 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_servers` ( KEY `name` (`name`), KEY `entities_id` (`entities_id`), KEY `locations_id` (`locations_id`), - KEY `is_active` (`is_active`) + KEY `is_active` (`is_active`), + KEY `location_active` (`locations_id`, `is_active`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_serverassets` ( @@ -51,18 +41,11 @@ CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_serverassets` ( `plugin_urbackup_servers_id` INT UNSIGNED NOT NULL DEFAULT 0, `itemtype` VARCHAR(255) NOT NULL DEFAULT '', `items_id` INT UNSIGNED NOT NULL DEFAULT 0, + `date_creation` TIMESTAMP NULL DEFAULT NULL, + `date_mod` TIMESTAMP NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `plugin_urbackup_servers_id` (`plugin_urbackup_servers_id`), KEY `item` (`itemtype`, `items_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; -CREATE TABLE IF NOT EXISTS `glpi_plugin_urbackup_profiles` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `profiles_id` INT UNSIGNED NOT NULL DEFAULT 0, - `rightname` VARCHAR(255) NOT NULL DEFAULT '', - `rights` INT NOT NULL DEFAULT 0, - `date_creation` TIMESTAMP NULL DEFAULT NULL, - `date_mod` TIMESTAMP NULL DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `profile_right` (`profiles_id`, `rightname`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC; \ No newline at end of file +-- glpi_plugin_urbackup_profiles was dropped in 0.7.0 (replaced by Profile::registerRights()) \ No newline at end of file diff --git a/js/urbackup.js b/js/urbackup.js deleted file mode 100644 index e4e4b51..0000000 --- a/js/urbackup.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * UrBackup API Test JavaScript - */ -(function () { - 'use strict'; - - function testApi(serverId, resultBox) { - if (!serverId || !resultBox) return; - - var xhr = new XMLHttpRequest(); - xhr.open('POST', '/plugins/urbackup/ajax/server_test.php', true); - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xhr.timeout = 8000; - - xhr.onload = function () { - if (xhr.status === 200) { - try { - var data = JSON.parse(xhr.responseText); - if (data.success) { - resultBox.innerHTML = ' ' + 'API connection OK' + ''; - } else { - var message = data.message || 'Connection failed'; - var isNetwork = /timeout|could not resolve|couldn't connect|connection refused|connection timed out|network is unreachable|no route to host|returned HTTP status/i.test(message); - var statusClass = isNetwork ? 'text-warning' : 'text-danger'; - var icon = isNetwork ? 'ti-wifi-off' : 'ti-x'; - var label = isNetwork ? 'Server unreachable' : 'API connection failed'; - resultBox.innerHTML = ' ' + label + '
' + message + ''; - } - } catch (e) { - resultBox.innerHTML = ' Error'; - } - } else { - resultBox.innerHTML = ' HTTP ' + xhr.status + ''; - } - }; - - xhr.ontimeout = function () { - resultBox.innerHTML = ' Server unreachable
Connection timeout'; - }; - - xhr.onerror = function () { - resultBox.innerHTML = ' Server unreachable
Network error'; - }; - - xhr.send('id=' + encodeURIComponent(serverId)); - } - - function initApiStatusCheck() { - var statusBox = document.getElementById('plugin-urbackup-api-status'); - if (!statusBox) return; - if (statusBox._initialized) return; - statusBox._initialized = true; - - var serverId = statusBox.getAttribute('data-server-id'); - if (serverId) { - testApi(serverId, statusBox); - } - } - - function initApiTestButtons() { - var buttons = document.querySelectorAll('.plugin-urbackup-test-api'); - - buttons.forEach(function (button) { - if (button._initialized) return; - button._initialized = true; - - button.addEventListener('click', function (e) { - e.preventDefault(); - e.stopPropagation(); - - var serverId = button.getAttribute('data-server-id'); - var resultBox = document.getElementById('plugin-urbackup-api-test-result'); - - testApi(serverId, resultBox); - }); - }); - } - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initApiStatusCheck); - document.addEventListener('DOMContentLoaded', initApiTestButtons); - } else { - setTimeout(initApiStatusCheck, 100); - setTimeout(initApiTestButtons, 100); - } -})(); diff --git a/remove_deprecated_files.sh b/remove_deprecated_files.sh deleted file mode 100755 index b145d5c..0000000 --- a/remove_deprecated_files.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# Remove deprecated front/ and ajax/ files for GLPI 11 compliance -echo "Removing deprecated front/ and ajax/ files..." -rm -f /var/www/glpi/plugins/urbackup/front/config.form.php -rm -f /var/www/glpi/plugins/urbackup/front/server.php -rm -f /var/www/glpi/plugins/urbackup/front/server.form.php -rm -f /var/www/glpi/plugins/urbackup/front/asset.form.php -rm -f /var/www/glpi/plugins/urbackup/ajax/server_test.php -rm -f /var/www/glpi/plugins/urbackup/ajax/server_clients.php -rm -f /var/www/glpi/plugins/urbackup/ajax/asset_action.php -echo "Files removed. Note: The plugin now uses Symfony Controllers." diff --git a/removed/profile.form.php___ b/removed/profile.form.php___ deleted file mode 100644 index 6ed0ada..0000000 --- a/removed/profile.form.php___ +++ /dev/null @@ -1,41 +0,0 @@ -setName('urbackup:test-api') - ->setDescription('Test UrBackup server API connection') - ->addArgument('server_id', InputArgument::REQUIRED, 'Server ID'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $server_id = (int) $input->getArgument('server_id'); - - $server = new Server(); - if (!$server->getFromDB($server_id)) { - $output->writeln("Server not found: $server_id"); - return Command::FAILURE; - } - - $output->writeln("Testing API for: " . $server->fields['name']); - $output->writeln("URL: " . $server->getWebInterfaceUrl()); - $output->writeln("Username: " . $server->fields['api_username']); - - $client = new UrbackupApiClient($server); - - try { - $result = $client->testConnection(); - - if ($result['success']) { - $output->writeln("SUCCESS: " . $result['message'] . ""); - $output->writeln("Identity: " . $result['identity']); - } else { - $output->writeln("FAILED: " . $result['message'] . ""); - } - - return $result['success'] ? Command::SUCCESS : Command::FAILURE; - } catch (\Throwable $e) { - $output->writeln("Exception: " . $e->getMessage() . ""); - return Command::FAILURE; - } - } -} diff --git a/src/Controller/AssetController.php b/src/Controller/AssetController.php deleted file mode 100644 index 45eb9b7..0000000 --- a/src/Controller/AssetController.php +++ /dev/null @@ -1,191 +0,0 @@ -request->get('itemtype', ''); - $items_id = (int) $request->request->get('items_id', 0); - - if ($itemtype === '' || $items_id <= 0 || !class_exists($itemtype)) { - Session::addMessageAfterRedirect( - __('Invalid asset reference.', 'urbackup'), - true, - ERROR - ); - Html::back(); - } - - if (!Config::isItemtypeEnabled($itemtype)) { - Session::addMessageAfterRedirect( - __('UrBackup is not enabled for this asset type.', 'urbackup'), - true, - ERROR - ); - Html::back(); - } - - $item = new $itemtype(); - - if (!$item instanceof CommonDBTM || !$item->getFromDB($items_id)) { - Session::addMessageAfterRedirect( - __('Asset not found.', 'urbackup'), - true, - ERROR - ); - Html::back(); - } - - $action = (string) $request->request->get('urbackup_action', ''); - - switch ($action) { - case 'connect': - $server_id = (int) $request->request->get('plugin_urbackup_servers_id', 0); - if (ServerAsset::connectAssetToServer($itemtype, $items_id, $server_id)) { - Session::addMessageAfterRedirect( - __('Asset connected to UrBackup server.', 'urbackup'), - true, - INFO - ); - } - break; - - case 'disconnect': - if (ServerAsset::disconnectAsset($itemtype, $items_id)) { - Session::addMessageAfterRedirect( - __('Asset disconnected from UrBackup server.', 'urbackup'), - true, - INFO - ); - } - break; - - case 'set_internet_mode': - if (Profile::canCurrentUser(UPDATE)) { - $enabled = (int) $request->request->get('internet_mode', 0) === 1; - $link = ServerAsset::getLinkForAsset($itemtype, $items_id, true); - if ($link) { - $server = new Server(); - if ($server->getFromDB((int) $link['plugin_urbackup_servers_id'])) { - $api = new UrbackupApiClient($server); - $client_name = (string) ($item->fields['name'] ?? ''); - $api->saveInternetMode($client_name, $enabled); - } - } - } - break; - - case 'create_client': - if (Profile::canCurrentUser(CREATE)) { - $serverAsset = new ServerAsset(); - $link = ServerAsset::getLinkForAsset($itemtype, $items_id, true); - if ($link) { - $server = new Server(); - if ($server->getFromDB((int) $link['plugin_urbackup_servers_id'])) { - $api = new UrbackupApiClient($server); - $client_name = (string) ($item->fields['name'] ?? ''); - $api->addClient($client_name); - } - } - } - break; - - case 'incremental_file_backup': - case 'full_file_backup': - case 'incremental_image_backup': - case 'full_image_backup': - if (Profile::canCurrentUser(UPDATE)) { - $serverAsset = new ServerAsset(); - $link = ServerAsset::getLinkForAsset($itemtype, $items_id, true); - if ($link) { - $server = new Server(); - if ($server->getFromDB((int) $link['plugin_urbackup_servers_id'])) { - $api = new UrbackupApiClient($server); - $client_name = (string) ($item->fields['name'] ?? ''); - switch ($action) { - case 'incremental_file_backup': - $api->startIncrementalFileBackup($client_name); - break; - case 'full_file_backup': - $api->startFullFileBackup($client_name); - break; - case 'incremental_image_backup': - $api->startIncrementalImageBackup($client_name); - break; - case 'full_image_backup': - $api->startFullImageBackup($client_name); - break; - } - } - } - } - break; - - case 'delete_client': - if (Profile::canCurrentUser(PURGE)) { - $serverAsset = new ServerAsset(); - $link = ServerAsset::getLinkForAsset($itemtype, $items_id, true); - if ($link) { - $server = new Server(); - if ($server->getFromDB((int) $link['plugin_urbackup_servers_id'])) { - $api = new UrbackupApiClient($server); - $client_name = (string) ($item->fields['name'] ?? ''); - $api->removeClient($client_name); - } - } - } - break; - } - - Html::back(); - } - - #[Route('/plugin/urbackup/api/clients', name: 'urbackup_api_clients', methods: ['GET'])] - public function getClients(Request $request): JsonResponse - { - Session::checkLoginUser(); - - $server_id = (int) $request->query->get('server_id', 0); - - if ($server_id <= 0) { - return new JsonResponse([]); - } - - $server = new Server(); - - if (!$server->getFromDB($server_id)) { - return new JsonResponse([]); - } - - $api = new UrbackupApiClient($server); - $clients = $api->getStatus(); - - return new JsonResponse($clients); - } -} diff --git a/src/Controller/ConfigController.php b/src/Controller/ConfigController.php deleted file mode 100644 index 1db03e4..0000000 --- a/src/Controller/ConfigController.php +++ /dev/null @@ -1,36 +0,0 @@ -isMethod('POST') && $request->request->has('update')) { - Config::saveConfiguration($request->request->all()); - Html::back(); - } - - return $this->render('config/config.html.twig', []); - } -} diff --git a/src/Controller/ServerController.php b/src/Controller/ServerController.php deleted file mode 100644 index 63ea5c4..0000000 --- a/src/Controller/ServerController.php +++ /dev/null @@ -1,109 +0,0 @@ - '\d+'])] - public function showServer(int $id): void - { - $server = new Server(); - - if ($id > 0) { - $server->check($id, READ); - } else { - $server->check(-1, CREATE); - $server->getEmpty(); - } - - $server->display(['id' => $id]); - } - - #[Route('/plugin/urbackup/server/test/{id}', name: 'urbackup_server_test', methods: ['POST', 'GET'])] - public function testConnection(int $id = 0): JsonResponse - { - if (!Profile::canCurrentUser(READ)) { - return new JsonResponse([ - 'success' => false, - 'message' => 'No permission', - ], 403); - } - - if ($id <= 0) { - $id = (int) ($_POST['id'] ?? $_GET['id'] ?? 0); - } - - if ($id <= 0) { - return new JsonResponse([ - 'success' => false, - 'message' => 'Invalid server ID', - ], 400); - } - - $server = new Server(); - - if (!$server->getFromDB($id)) { - return new JsonResponse([ - 'success' => false, - 'message' => 'Server not found', - ], 404); - } - - try { - $client = new UrbackupApiClient($server); - $result = $client->testConnection(); - - $server->update([ - 'id' => $id, - 'last_api_status' => $result['success'] ? 1 : 0, - 'last_api_message' => $result['message'], - 'last_api_check' => date('Y-m-d H:i:s'), - ]); - - return new JsonResponse($result); - } catch (Throwable $e) { - return new JsonResponse([ - 'success' => false, - 'message' => $e->getMessage(), - ], 500); - } - } -} diff --git a/templates/asset/asset_tab.html.twig b/templates/asset/asset_tab.html.twig deleted file mode 100644 index 909971e..0000000 --- a/templates/asset/asset_tab.html.twig +++ /dev/null @@ -1,69 +0,0 @@ -
- {% if not linked %} -
- {{ __('No UrBackup server linked.', 'urbackup') }} -
- - - - - {% if is_sub_location %} - - - - {% endif %} - {% if servers|length == 0 %} - - - - {% elseif can_connect %} - - - - - {% else %} - - - - {% endif %} -
{{ __('UrBackup server selection', 'urbackup') }}
- {{ __('The asset is in a sub-location. The plugin will use the server assigned to the root location.', 'urbackup') }} -
-
- {{ __('No UrBackup server available for the root location of this asset.', 'urbackup') }} -
-
{{ __('Available servers for root location', 'urbackup') }} -
- - - - - -
-
- {{ __('A server is available, but you do not have permission to link this asset.', 'urbackup') }} -
- {% else %} - - - - - - - - - - - - - - - - -
{{ __('UrBackup status', 'urbackup') }}
{{ __('Linked server', 'urbackup') }}{{ server_link }}{{ __('IP address', 'urbackup') }}{{ server_ip }}
{{ __('UrBackup server version', 'urbackup') }}{{ server_version }}{{ __('Client name', 'urbackup') }}{{ client_name }}
- {% endif %} -
diff --git a/templates/config/config.html.twig b/templates/config/config.html.twig deleted file mode 100644 index 1c3bb16..0000000 --- a/templates/config/config.html.twig +++ /dev/null @@ -1,18 +0,0 @@ -
-
- - - - - - - - -
{{ __('UrBackup configuration', 'urbackup') }}
{{ __('Computer', 'urbackup') }}{{ __('Always enabled', 'urbackup') }}
-
-
- {{ __('For Asset Definition types, enable/disable UrBackup via Config > Asset definitions > Capacities.', 'urbackup') }} -
-
- -
diff --git a/templates/server/server_form.html.twig b/templates/server/server_form.html.twig deleted file mode 100644 index 14e1027..0000000 --- a/templates/server/server_form.html.twig +++ /dev/null @@ -1,118 +0,0 @@ -{% extends 'layout.html.twig' %} - -{% block content %} -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if server.id > 0 %} - - - - - - - - - {% endif %} -
{{ __('UrBackup server', 'urbackup') }}
{{ __('Name') }} - - {{ __('Active') }} - -
{{ __('Entity') }} - {{ render_entity_dropdown('entities_id', server.entities_id ?? 0) }} - {{ __('Recursive') }} - -
{{ __('Location') }} - {{ render_location_dropdown('locations_id', server.locations_id ?? 0) }} -
{{ __('Associate the server with the main/root location. Assets in sub-locations will use this root location server.', 'urbackup') }} -
{{ __('Protocol', 'urbackup') }} - -
{{ __('IP address', 'urbackup') }} - - {{ __('Network port', 'urbackup') }} - -
{{ __('UrBackup server version', 'urbackup') }} - - {{ __('Ignore SSL verification', 'urbackup') }} - -
{{ __('API username', 'urbackup') }} - - {{ __('API password', 'urbackup') }} - -
{{ __('Comments') }} - -
{{ __('UrBackup web interface', 'urbackup') }} - {% if server.url %} - - {{ __('Open UrBackup interface', 'urbackup') }} - - {% else %} - {{ __('No URL available', 'urbackup') }} - {% endif %} -
{{ __('API test', 'urbackup') }} - - -
- - - -
-
-{% endblock %} diff --git a/templates/server/server_list.html.twig b/templates/server/server_list.html.twig deleted file mode 100644 index f00e847..0000000 --- a/templates/server/server_list.html.twig +++ /dev/null @@ -1,51 +0,0 @@ -{% extends 'layout.html.twig' %} - -{% block content %} -
- - - - - - - - - - - - - {% for server in servers %} - - - - - - - - - {% else %} - - - - {% endfor %} -
{{ __('UrBackup servers', 'urbackup') }}
{{ __('Name') }}{{ __('IP address', 'urbackup') }}{{ __('Port') }}{{ __('Version') }}{{ __('Status') }}{{ __('Actions') }}
{{ server.name }}{{ server.ip_address }}{{ server.port }}{{ server.server_version }} - {% if server.last_api_status %} - {{ __('OK', 'urbackup') }} - {% else %} - {{ __('Failed', 'urbackup') }} - {% endif %} - - - {{ __('View') }} - - -
{{ __('No records found', 'urbackup') }}
- {% if can_create %} - - {{ __('Add') }} - - {% endif %} -
-{% endblock %}