versione pre-stable
This commit is contained in:
@@ -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.
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
[](https://glpi-project.org)
|
||||
[](https://php.net)
|
||||
[](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).
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* AJAX endpoint for testing UrBackup API
|
||||
*/
|
||||
|
||||
$AJAX_INCLUDE = 1;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
define('GLPI_ROOT', dirname(__DIR__, 3));
|
||||
}
|
||||
|
||||
require_once GLPI_ROOT . "/inc/includes.php";
|
||||
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
Html::header_nocache();
|
||||
|
||||
Session::checkLoginUser();
|
||||
|
||||
// Allow AJAX requests without CSRF token (internal plugin calls)
|
||||
if (!Session::getCSRFToken()) {
|
||||
// Allow for AJAX calls
|
||||
}
|
||||
|
||||
if (!Session::haveRight('plugin_urbackup', READ)) {
|
||||
echo json_encode(['success' => 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()]);
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* AJAX endpoint for API test
|
||||
* Uses standard GLPI front page pattern
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/_check_webserver_config.php');
|
||||
|
||||
global $CFG_GLPI;
|
||||
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
|
||||
// Check login
|
||||
Session::checkLoginUser();
|
||||
|
||||
// Check rights
|
||||
if (!Session::haveRight('plugin_urbackup', READ)) {
|
||||
echo json_encode(['success' => 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()]);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Test API endpoint
|
||||
* Works without GLPI session redirect
|
||||
*/
|
||||
|
||||
define('PLUGIN_URBACKUP_DIR', __DIR__ . '/..');
|
||||
define('GLPI_ROOT', dirname(__DIR__, 4));
|
||||
|
||||
// Load minimal GLPI
|
||||
require_once GLPI_ROOT . '/inc/includes.php';
|
||||
|
||||
// Check session exists
|
||||
if (!isset($_SESSION['glpiID']) || (int) $_SESSION['glpiID'] <= 0) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => 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()]);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
use GlpiPlugin\Urbackup\Profile;
|
||||
use GlpiPlugin\Urbackup\Server;
|
||||
use GlpiPlugin\Urbackup\ServerAsset;
|
||||
use Html;
|
||||
|
||||
if (!defined('GLPI_ROOT')) {
|
||||
define('GLPI_ROOT', dirname(__DIR__, 4));
|
||||
}
|
||||
|
||||
include_once GLPI_ROOT . "/inc/includes.php";
|
||||
|
||||
if (!Profile::canCurrentUser(READ)) {
|
||||
Html::displayRightError();
|
||||
}
|
||||
|
||||
$ID = $_GET['id'] ?? 0;
|
||||
|
||||
if ($ID <= 0) {
|
||||
Html::redirect(PLUGIN_URBACKUP_WEB_DIR . '/front/server.php');
|
||||
}
|
||||
|
||||
$server = new Server();
|
||||
if (!$server->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();
|
||||
@@ -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;
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Test page - will work when accessed from within GLPI session
|
||||
*/
|
||||
$AJAX_INCLUDE = 1;
|
||||
|
||||
define('GLPI_ROOT', dirname(__DIR__, 4));
|
||||
require_once GLPI_ROOT . '/inc/includes.php';
|
||||
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
Html::header_nocache();
|
||||
|
||||
Session::checkLoginUser();
|
||||
|
||||
// Check rights
|
||||
if (!Session::haveRight('plugin_urbackup', READ)) {
|
||||
echo "<p style='color:red'>No READ permission on plugin_urbackup</p>";
|
||||
Html::footer();
|
||||
exit;
|
||||
}
|
||||
|
||||
echo "<p style='color:green'>READ permission OK</p>";
|
||||
|
||||
// Get server ID
|
||||
$server_id = (int) ($_GET['id'] ?? 0);
|
||||
|
||||
if ($server_id > 0) {
|
||||
$server = new GlpiPlugin\Urbackup\Server();
|
||||
if ($server->getFromDB($server_id)) {
|
||||
echo "<p>Server: " . $server->fields['name'] . "</p>";
|
||||
|
||||
$client = new GlpiPlugin\Urbackup\UrbackupApiClient($server);
|
||||
$result = $client->testConnection();
|
||||
|
||||
echo "<pre>" . print_r($result, true) . "</pre>";
|
||||
} else {
|
||||
echo "<p>Server not found</p>";
|
||||
}
|
||||
} else {
|
||||
echo "<p>No server ID provided</p>";
|
||||
}
|
||||
|
||||
Html::footer();
|
||||
+20
-27
@@ -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'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
-- glpi_plugin_urbackup_profiles was dropped in 0.7.0 (replaced by Profile::registerRights())
|
||||
@@ -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 = '<span class="text-success fw-bold"><i class="ti ti-check"></i> ' + 'API connection OK' + '</span>';
|
||||
} 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 = '<span class="' + statusClass + ' fw-bold"><i class="ti ' + icon + '"></i> ' + label + '</span><br><small class="text-muted">' + message + '</small>';
|
||||
}
|
||||
} catch (e) {
|
||||
resultBox.innerHTML = '<span class="text-danger fw-bold"><i class="ti ti-x"></i> Error</span>';
|
||||
}
|
||||
} else {
|
||||
resultBox.innerHTML = '<span class="text-danger fw-bold"><i class="ti ti-x"></i> HTTP ' + xhr.status + '</span>';
|
||||
}
|
||||
};
|
||||
|
||||
xhr.ontimeout = function () {
|
||||
resultBox.innerHTML = '<span class="text-warning fw-bold"><i class="ti ti-wifi-off"></i> Server unreachable</span><br><small class="text-muted">Connection timeout</small>';
|
||||
};
|
||||
|
||||
xhr.onerror = function () {
|
||||
resultBox.innerHTML = '<span class="text-warning fw-bold"><i class="ti ti-wifi-off"></i> Server unreachable</span><br><small class="text-muted">Network error</small>';
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
})();
|
||||
@@ -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."
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------------
|
||||
* UrBackup plugin for GLPI
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
use GlpiPlugin\Urbackup\Profile;
|
||||
|
||||
include('../../../inc/includes.php');
|
||||
|
||||
Session::checkLoginUser();
|
||||
Session::checkRight('profile', UPDATE);
|
||||
Session::checkCSRF($_POST);
|
||||
|
||||
$profiles_id = (int) ($_POST['profiles_id'] ?? 0);
|
||||
|
||||
if ($profiles_id <= 0) {
|
||||
Session::addMessageAfterRedirect(
|
||||
__('Invalid profile.', 'urbackup'),
|
||||
true,
|
||||
ERROR
|
||||
);
|
||||
|
||||
Html::back();
|
||||
}
|
||||
|
||||
if (isset($_POST['update_urbackup_rights'])) {
|
||||
Profile::saveRights($_POST);
|
||||
|
||||
Session::addMessageAfterRedirect(
|
||||
__('UrBackup rights saved successfully.', 'urbackup'),
|
||||
true,
|
||||
INFO
|
||||
);
|
||||
}
|
||||
|
||||
global $CFG_GLPI;
|
||||
|
||||
Html::redirect($CFG_GLPI['root_doc'] . '/front/profile.form.php?id=' . $profiles_id);
|
||||
@@ -25,7 +25,7 @@ use GlpiPlugin\Urbackup\Server;
|
||||
use GlpiPlugin\Urbackup\ServerAsset;
|
||||
use GlpiPlugin\Urbackup\MassiveAction as PluginUrbackupMassiveAction;
|
||||
|
||||
define('PLUGIN_URBACKUP_VERSION', '0.6.0');
|
||||
define('PLUGIN_URBACKUP_VERSION', '0.7.0');
|
||||
define('PLUGIN_URBACKUP_MIN_GLPI', '11.0.0');
|
||||
define('PLUGIN_URBACKUP_MAX_GLPI', '11.99.99');
|
||||
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GlpiPlugin\Urbackup\Command;
|
||||
|
||||
use GlpiPlugin\Urbackup\Server;
|
||||
use GlpiPlugin\Urbackup\UrbackupApiClient;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TestApiCommand extends Command
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->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("<error>Server not found: $server_id</error>");
|
||||
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("<info>SUCCESS: " . $result['message'] . "</info>");
|
||||
$output->writeln("Identity: " . $result['identity']);
|
||||
} else {
|
||||
$output->writeln("<error>FAILED: " . $result['message'] . "</error>");
|
||||
}
|
||||
|
||||
return $result['success'] ? Command::SUCCESS : Command::FAILURE;
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeln("<error>Exception: " . $e->getMessage() . "</error>");
|
||||
return Command::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------------
|
||||
* UrBackup plugin for GLPI
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace GlpiPlugin\Urbackup\Controller;
|
||||
|
||||
use GlpiPlugin\Urbackup\Config;
|
||||
use GlpiPlugin\Urbackup\Profile;
|
||||
use GlpiPlugin\Urbackup\Server;
|
||||
use GlpiPlugin\Urbackup\ServerAsset;
|
||||
use GlpiPlugin\Urbackup\UrbackupApiClient;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use CommonDBTM;
|
||||
use Html;
|
||||
use Session;
|
||||
|
||||
class AssetController
|
||||
{
|
||||
#[Route('/plugin/urbackup/asset/action', name: 'urbackup_asset_action', methods: ['POST'])]
|
||||
public function assetAction(Request $request): void
|
||||
{
|
||||
Session::checkLoginUser();
|
||||
Session::checkCSRF($_POST);
|
||||
|
||||
$itemtype = (string) $request->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);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------------
|
||||
* UrBackup plugin for GLPI
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace GlpiPlugin\Urbackup\Controller;
|
||||
|
||||
use GlpiPlugin\Urbackup\Config;
|
||||
use GlpiPlugin\Urbackup\Profile;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Html;
|
||||
use Session;
|
||||
|
||||
class ConfigController extends AbstractController
|
||||
{
|
||||
#[Route('/plugin/urbackup/config', name: 'urbackup_config', methods: ['GET', 'POST'])]
|
||||
public function configure(Request $request): Response
|
||||
{
|
||||
Session::checkRight('config', UPDATE);
|
||||
|
||||
if ($request->isMethod('POST') && $request->request->has('update')) {
|
||||
Config::saveConfiguration($request->request->all());
|
||||
Html::back();
|
||||
}
|
||||
|
||||
return $this->render('config/config.html.twig', []);
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* -------------------------------------------------------------------------
|
||||
* UrBackup plugin for GLPI
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace GlpiPlugin\Urbackup\Controller;
|
||||
|
||||
use GlpiPlugin\Urbackup\Server;
|
||||
use GlpiPlugin\Urbackup\Profile;
|
||||
use GlpiPlugin\Urbackup\UrbackupApiClient;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Html;
|
||||
use Session;
|
||||
use Search;
|
||||
|
||||
class ServerController extends AbstractController
|
||||
{
|
||||
#[Route('/plugin/urbackup/servers', name: 'urbackup_server_list', methods: ['GET'])]
|
||||
public function listServers(): void
|
||||
{
|
||||
if (!Server::canView()) {
|
||||
Html::displayRightError();
|
||||
}
|
||||
|
||||
Html::header(
|
||||
Server::getTypeName(Session::getPluralNumber()),
|
||||
$_SERVER['PHP_SELF'],
|
||||
'admin',
|
||||
Server::class
|
||||
);
|
||||
|
||||
Search::show(Server::class);
|
||||
|
||||
Html::footer();
|
||||
}
|
||||
|
||||
#[Route('/plugin/urbackup/server/{id}', name: 'urbackup_server_show', methods: ['GET'], requirements: ['id' => '\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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<div class="plugin-urbackup-asset-tab">
|
||||
{% if not linked %}
|
||||
<div class="alert alert-info">
|
||||
{{ __('No UrBackup server linked.', 'urbackup') }}
|
||||
</div>
|
||||
<table class="tab_cadre_fixe">
|
||||
<tr>
|
||||
<th colspan="2">{{ __('UrBackup server selection', 'urbackup') }}</th>
|
||||
</tr>
|
||||
{% if is_sub_location %}
|
||||
<tr class="tab_bg_1">
|
||||
<td colspan="2"><em>
|
||||
{{ __('The asset is in a sub-location. The plugin will use the server assigned to the root location.', 'urbackup') }}
|
||||
</em></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if servers|length == 0 %}
|
||||
<tr class="tab_bg_1">
|
||||
<td colspan="2">
|
||||
<div class="alert alert-warning">
|
||||
{{ __('No UrBackup server available for the root location of this asset.', 'urbackup') }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% elseif can_connect %}
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('Available servers for root location', 'urbackup') }}</td>
|
||||
<td>
|
||||
<form method="post" action="{{ path('urbackup_asset_action') }}">
|
||||
<input type="hidden" name="_glpi_csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="itemtype" value="{{ itemtype }}">
|
||||
<input type="hidden" name="items_id" value="{{ items_id }}">
|
||||
<select name="plugin_urbackup_servers_id">
|
||||
{% for id, name in servers %}
|
||||
<option value="{{ id }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit" name="connect" class="btn btn-primary">{{ __('Connect', 'urbackup') }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="tab_bg_1">
|
||||
<td colspan="2">
|
||||
{{ __('A server is available, but you do not have permission to link this asset.', 'urbackup') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
{% else %}
|
||||
<table class="tab_cadre_fixe">
|
||||
<tr>
|
||||
<th colspan="4">{{ __('UrBackup status', 'urbackup') }}</th>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('Linked server', 'urbackup') }}</td>
|
||||
<td>{{ server_link }}</td>
|
||||
<td>{{ __('IP address', 'urbackup') }}</td>
|
||||
<td>{{ server_ip }}</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('UrBackup server version', 'urbackup') }}</td>
|
||||
<td>{{ server_version }}</td>
|
||||
<td>{{ __('Client name', 'urbackup') }}</td>
|
||||
<td>{{ client_name }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -1,18 +0,0 @@
|
||||
<form method="post" action="{{ path('urbackup_config') }}">
|
||||
<div class="center">
|
||||
<table class="tab_cadre_fixe">
|
||||
<tr>
|
||||
<th colspan="2">{{ __('UrBackup configuration', 'urbackup') }}</th>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td><strong>{{ __('Computer', 'urbackup') }}</strong></td>
|
||||
<td><span class="badge bg-success">{{ __('Always enabled', 'urbackup') }}</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<div class="alert alert-info">
|
||||
{{ __('For Asset Definition types, enable/disable UrBackup via Config > Asset definitions > Capacities.', 'urbackup') }}
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="_glpi_csrf_token" value="{{ csrf_token() }}">
|
||||
</form>
|
||||
@@ -1,118 +0,0 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="center">
|
||||
<form method="post" action="{{ path('urbackup_server_save') }}">
|
||||
<table class="tab_cadre_fixe">
|
||||
<tr>
|
||||
<th colspan="4">{{ __('UrBackup server', 'urbackup') }}</th>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('Name') }}</td>
|
||||
<td>
|
||||
<input type="text" name="name" value="{{ server.name ?? '' }}" size="40">
|
||||
</td>
|
||||
<td>{{ __('Active') }}</td>
|
||||
<td>
|
||||
<select name="is_active">
|
||||
<option value="1"{% if server.is_active ?? true %} selected{% endif %}>{{ __('Yes') }}</option>
|
||||
<option value="0"{% if not (server.is_active ?? true) %} selected{% endif %}>{{ __('No') }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('Entity') }}</td>
|
||||
<td>
|
||||
{{ render_entity_dropdown('entities_id', server.entities_id ?? 0) }}
|
||||
</td>
|
||||
<td>{{ __('Recursive') }}</td>
|
||||
<td>
|
||||
<select name="is_recursive">
|
||||
<option value="1"{% if server.is_recursive ?? false %} selected{% endif %}>{{ __('Yes') }}</option>
|
||||
<option value="0"{% if not (server.is_recursive ?? false) %} selected{% endif %}>{{ __('No') }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('Location') }}</td>
|
||||
<td>
|
||||
{{ render_location_dropdown('locations_id', server.locations_id ?? 0) }}
|
||||
<br><small>{{ __('Associate the server with the main/root location. Assets in sub-locations will use this root location server.', 'urbackup') }}</small>
|
||||
</td>
|
||||
<td>{{ __('Protocol', 'urbackup') }}</td>
|
||||
<td>
|
||||
<select name="protocol">
|
||||
<option value="http"{% if server.protocol == 'http' %} selected{% endif %}>HTTP</option>
|
||||
<option value="https"{% if server.protocol == 'https' %} selected{% endif %}>HTTPS</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('IP address', 'urbackup') }}</td>
|
||||
<td>
|
||||
<input type="text" name="ip_address" value="{{ server.ip_address ?? '' }}" size="40">
|
||||
</td>
|
||||
<td>{{ __('Network port', 'urbackup') }}</td>
|
||||
<td>
|
||||
<input type="number" name="port" value="{{ server.port ?? 55414 }}" min="1" max="65535">
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('UrBackup server version', 'urbackup') }}</td>
|
||||
<td>
|
||||
<input type="text" name="server_version" value="{{ server.server_version ?? '' }}" size="30">
|
||||
</td>
|
||||
<td>{{ __('Ignore SSL verification', 'urbackup') }}</td>
|
||||
<td>
|
||||
<select name="ignore_ssl">
|
||||
<option value="1"{% if server.ignore_ssl ?? false %} selected{% endif %}>{{ __('Yes') }}</option>
|
||||
<option value="0"{% if not (server.ignore_ssl ?? false) %} selected{% endif %}>{{ __('No') }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('API username', 'urbackup') }}</td>
|
||||
<td>
|
||||
<input type="text" name="api_username" value="{{ server.api_username ?? '' }}" size="40" autocomplete="off">
|
||||
</td>
|
||||
<td>{{ __('API password', 'urbackup') }}</td>
|
||||
<td>
|
||||
<input type="password" name="api_password" value="{{ server.api_password ?? '' }}" autocomplete="new-password">
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('Comments') }}</td>
|
||||
<td colspan="3">
|
||||
<textarea name="comment" rows="5" cols="100">{{ server.comment ?? '' }}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
{% if server.id > 0 %}
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('UrBackup web interface', 'urbackup') }}</td>
|
||||
<td colspan="3">
|
||||
{% if server.url %}
|
||||
<a href="{{ server.url }}" target="_blank" rel="noopener" class="btn btn-secondary">
|
||||
{{ __('Open UrBackup interface', 'urbackup') }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ __('No URL available', 'urbackup') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ __('API test', 'urbackup') }}</td>
|
||||
<td colspan="3">
|
||||
<button type="button" class="btn btn-primary plugin-urbackup-test-api" data-server-id="{{ server.id }}">
|
||||
{{ __('Test API connection', 'urbackup') }}
|
||||
</button>
|
||||
<span id="plugin-urbackup-api-test-result"></span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
<input type="hidden" name="id" value="{{ server.id ?? 0 }}">
|
||||
<input type="hidden" name="_glpi_csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit" name="save" class="btn btn-primary">{{ __('Save') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,51 +0,0 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="center">
|
||||
<table class="tab_cadre_fixe">
|
||||
<tr>
|
||||
<th colspan="6">{{ __('UrBackup servers', 'urbackup') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{ __('Name') }}</th>
|
||||
<th>{{ __('IP address', 'urbackup') }}</th>
|
||||
<th>{{ __('Port') }}</th>
|
||||
<th>{{ __('Version') }}</th>
|
||||
<th>{{ __('Status') }}</th>
|
||||
<th>{{ __('Actions') }}</th>
|
||||
</tr>
|
||||
{% for server in servers %}
|
||||
<tr class="tab_bg_1">
|
||||
<td>{{ server.name }}</td>
|
||||
<td>{{ server.ip_address }}</td>
|
||||
<td>{{ server.port }}</td>
|
||||
<td>{{ server.server_version }}</td>
|
||||
<td>
|
||||
{% if server.last_api_status %}
|
||||
<span class="badge bg-success">{{ __('OK', 'urbackup') }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">{{ __('Failed', 'urbackup') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ path('urbackup_server_show', {'id': server.id}) }}" class="btn btn-sm btn-primary">
|
||||
{{ __('View') }}
|
||||
</a>
|
||||
<button class="btn btn-sm btn-secondary plugin-urbackup-test-api" data-server-id="{{ server.id }}">
|
||||
{{ __('Test API', 'urbackup') }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="tab_bg_1">
|
||||
<td colspan="6" class="center">{{ __('No records found', 'urbackup') }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% if can_create %}
|
||||
<a href="{{ path('urbackup_server_show', {'id': 0}) }}" class="btn btn-success">
|
||||
{{ __('Add') }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user