Files
urbackup/src/AssetTab.php
T
mariano 1dc84aa5eb Stable
2026-05-20 09:20:27 +02:00

913 lines
33 KiB
PHP

<?php
/**
* -------------------------------------------------------------------------
* UrBackup plugin for GLPI
* -------------------------------------------------------------------------
*/
namespace GlpiPlugin\Urbackup;
use CommonDBTM;
use CommonGLPI;
use Dropdown;
use Html;
use Session;
use Throwable;
class AssetTab extends CommonDBTM
{
public static $rightname = 'plugin_urbackup';
/**
* Extract actual value from a UrBackup setting struct.
*
* API returns settings as: {"use":N, "value":..., "value_client":..., "value_group":...}
*/
private static function extractSettingValue(mixed $setting, mixed $default = null): mixed
{
if (is_array($setting) && array_key_exists('value', $setting)) {
return $setting['value'];
}
return $setting ?? $default;
}
/**
* Get type name.
*
* @param int $nb Number
*
* @return string
*/
public static function getTypeName($nb = 0): string
{
return __('UrBackup', 'urbackup');
}
/**
* Get tab name for item.
*
* @param CommonGLPI $item Item
* @param int $withtemplate With template
*
* @return string
*/
public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0): string
{
if (!Config::isItemtypeEnabled($item::class)) {
return '';
}
if (!Profile::canCurrentUser(READ)) {
return '';
}
return self::createTabEntry(__('UrBackup', 'urbackup'), 0, null, 'ti ti-cloud-upload');
}
/**
* Display tab content for item.
*
* @param CommonGLPI $item Item
* @param int $tabnum Tab number
* @param int $withtemplate With template
*
* @return bool
*/
public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0): bool
{
if (!Config::isItemtypeEnabled($item::class)) {
return false;
}
if (!Profile::canCurrentUser(READ)) {
echo "<div class='alert alert-warning'>";
echo htmlspecialchars(__('You do not have permission to view UrBackup information.', 'urbackup'));
echo "</div>";
return true;
}
$itemtype = $item::class;
$items_id = (int) ($item->fields['id'] ?? 0);
$link = ServerAsset::getLinkForAsset($itemtype, $items_id, true);
echo "<div class='plugin-urbackup-asset-tab'>";
if ($link === null) {
self::showNoServerLinkedBlock($item);
} else {
self::showServerLinkedBlock($item, $link);
}
echo "</div>";
return true;
}
/**
* Show block when no server is linked.
*
* @param CommonDBTM $item Asset item
*
* @return void
*/
private static function showNoServerLinkedBlock(CommonDBTM $item): void
{
$asset_location_id = LocationHelper::getAssetLocationId($item);
$root_location_id = LocationHelper::getRootLocationIdForAsset($item);
$is_sub_location = LocationHelper::assetIsInSubLocation($item);
$servers = LocationHelper::getAvailableServersForAsset($item);
echo "<div class='alert alert-info'>";
echo htmlspecialchars(__('No UrBackup server linked.', 'urbackup'));
echo "</div>";
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='2'>" . htmlspecialchars(__('UrBackup server selection', 'urbackup')) . "</th></tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Asset location ID', 'urbackup')) . "</td>";
echo "<td>" . htmlspecialchars((string) $asset_location_id) . "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Root location ID', 'urbackup')) . "</td>";
echo "<td>" . htmlspecialchars((string) $root_location_id) . "</td>";
echo "</tr>";
if ($is_sub_location) {
echo "<tr class='tab_bg_1'>";
echo "<td colspan='2'><em>";
echo htmlspecialchars(
__('The asset is in a sub-location. The plugin will use the server assigned to the root location.', 'urbackup')
);
echo "</em></td>";
echo "</tr>";
}
if (count($servers) === 0) {
echo "<tr class='tab_bg_1'>";
echo "<td colspan='2'>";
echo "<div class='alert alert-warning'>";
echo htmlspecialchars(__('No UrBackup server available for the root location of this asset.', 'urbackup'));
echo "</div>";
echo "</td>";
echo "</tr>";
echo "</table>";
return;
}
if (!Profile::canCurrentUser(UPDATE) && !Profile::canCurrentUser(CREATE)) {
echo "<tr class='tab_bg_1'>";
echo "<td colspan='2'>";
echo htmlspecialchars(__('A server is available, but you do not have permission to link this asset.', 'urbackup'));
echo "</td>";
echo "</tr>";
echo "</table>";
return;
}
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Available servers for root location', 'urbackup')) . "</td>";
echo "<td>";
echo "<form method='post' action='" . htmlspecialchars(PLUGIN_URBACKUP_WEB_DIR . "/front/asset.form.php") . "'>";
echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]);
echo Html::hidden('itemtype', ['value' => $item::class]);
echo Html::hidden('items_id', ['value' => (int) $item->fields['id']]);
Dropdown::showFromArray(
'plugin_urbackup_servers_id',
$servers,
[
'value' => 0,
]
);
echo Html::submit(__('Connect', 'urbackup'), [
'name' => 'connect',
'class' => 'btn btn-primary',
]);
Html::closeForm();
echo "</td>";
echo "</tr>";
echo "</table>";
}
/**
* Show block when server is linked.
*
* @param CommonDBTM $item Asset item
* @param array<string, mixed> $link Link row
*
* @return void
*/
private static function showServerLinkedBlock(CommonDBTM $item, array $link): void
{
$server = new Server();
$server_id = (int) ($link['plugin_urbackup_servers_id'] ?? 0);
if ($server_id <= 0 || !$server->getFromDB($server_id)) {
echo "<div class='alert alert-danger'>";
echo htmlspecialchars(__('The linked UrBackup server no longer exists.', 'urbackup'));
echo "</div>";
return;
}
$api_data = self::loadApiData($item, $server, $link);
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='4'>" . htmlspecialchars(__('UrBackup status', 'urbackup')) . "</th></tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Linked server', 'urbackup')) . "</td>";
echo "<td>" . $server->getLink() . "</td>";
echo "<td>" . htmlspecialchars(__('IP address', 'urbackup')) . "</td>";
echo "<td>" . htmlspecialchars((string) $server->fields['ip_address']) . "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('UrBackup server version', 'urbackup')) . "</td>";
echo "<td>" . htmlspecialchars((string) ($server->fields['server_version'] ?? '')) . "</td>";
echo "<td>" . htmlspecialchars(__('Client name', 'urbackup')) . "</td>";
echo "<td>" . htmlspecialchars(ServerAsset::getAssetName($item::class, (int) $item->fields['id'])) . "</td>";
echo "</tr>";
echo "</table>";
if ($api_data['error'] !== '') {
echo "<div class='alert alert-warning'>";
echo htmlspecialchars($api_data['error']);
echo "</div>";
}
if ($api_data['ip_warning'] !== '') {
echo "<div class='alert alert-warning'>";
echo htmlspecialchars($api_data['ip_warning']);
echo "</div>";
}
self::showInternalTabs($item, $server, $link, $api_data);
}
/**
* Load API data for asset tab.
*
* @param CommonDBTM $item Asset
* @param Server $server Server
* @param array<string, mixed> $link Link
*
* @return array<string, mixed>
*/
private static function loadApiData(CommonDBTM $item, Server $server, array $link): array
{
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
$asset_ip = ServerAsset::extractAssetIp($item);
$data = [
'client_found' => false,
'client_status' => [],
'client_settings' => [],
'authkey' => '',
'recent_backups' => [],
'logs' => [],
'error' => '',
'ip_warning' => '',
];
try {
$api = new UrbackupApiClient($server);
$client_status = $api->getClientStatusByName($client_name);
if ($client_status !== null) {
$data['client_found'] = true;
$data['client_status'] = $client_status;
$urbackup_ip = (string) (
$client_status['ip'] ??
$client_status['ip_address'] ??
$client_status['addr'] ??
''
);
if ($asset_ip !== '' && $urbackup_ip !== '' && $asset_ip !== $urbackup_ip) {
$data['ip_warning'] = sprintf(
__('Client name matches, but GLPI IP "%s" differs from UrBackup IP "%s".', 'urbackup'),
$asset_ip,
$urbackup_ip
);
}
$data['client_settings'] = $api->getClientSettings($client_name);
$data['authkey'] = $api->getClientAuthKey($client_name);
$data['recent_backups'] = $api->getRecentBackups($client_name, 10);
$data['logs'] = $api->getClientLogs($client_name, 50);
}
} catch (Throwable $e) {
$data['error'] = $e->getMessage();
}
return $data;
}
/**
* Show internal sections.
*
* @param CommonDBTM $item Asset
* @param Server $server Server
* @param array<string, mixed> $link Link
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function showInternalTabs(
CommonDBTM $item,
Server $server,
array $link,
array $api_data
): void {
echo "<div class='plugin-urbackup-inner-tabs'>";
echo "<h3>" . htmlspecialchars(__('State', 'urbackup')) . "</h3>";
self::showStateSection($server, $link, $api_data);
echo "<h3>" . htmlspecialchars(__('Actions', 'urbackup')) . "</h3>";
self::showActionsSection($item, $server, $link, $api_data);
echo "<h3>" . htmlspecialchars(__('Info / Log', 'urbackup')) . "</h3>";
self::showInfoLogSection($api_data);
echo "</div>";
}
/**
* Show state section.
*
* @param Server $server Server
* @param array<string, mixed> $link Link
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function showStateSection(Server $server, array $link, array $api_data): void
{
$status = $api_data['client_status'];
$settings = $api_data['client_settings'];
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='2'>" . htmlspecialchars(__('Client state', 'urbackup')) . "</th></tr>";
if (!$api_data['client_found']) {
echo "<tr class='tab_bg_1'>";
echo "<td colspan='2'>";
echo htmlspecialchars(__('Client not found on UrBackup server.', 'urbackup'));
echo "</td>";
echo "</tr>";
echo "</table>";
return;
}
$internetMode = self::extractSettingValue($settings['internet_mode_enabled'] ?? $settings['internet_mode'] ?? null, 0);
$internetModeDisplay = ((int) $internetMode === 1)
? '<span class="badge bg-success">' . __('Yes', 'urbackup') . '</span>'
: '<span class="badge bg-secondary">' . __('No', 'urbackup') . '</span>';
$rows = [
__('Client version', 'urbackup') => $status['client_version_string'] ?? $status['client_version'] ?? $status['version'] ?? '-',
__('Online / Offline', 'urbackup') => self::formatOnlineStatus($status),
__('Last file backup', 'urbackup') => self::formatTimestamp($status['file_lastbackup'] ?? $status['lastbackup'] ?? $status['last_file_backup'] ?? ''),
__('Last image backup', 'urbackup') => self::formatTimestamp($status['image_lastbackup'] ?? $status['lastbackup_image'] ?? $status['last_image_backup'] ?? ''),
__('Last file backup result', 'urbackup') => self::formatBoolStatus($status['file_ok'] ?? null),
__('Last image backup result', 'urbackup') => self::formatBoolStatus($status['image_ok'] ?? null),
__('Current activities', 'urbackup') => $status['status'] ?? $status['activity'] ?? '-',
__('Internet mode', 'urbackup') => $internetModeDisplay,
];
foreach ($rows as $label => $value) {
$displayValue = is_array($value) ? json_encode($value) : (string) $value;
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars((string) $label) . "</td>";
echo "<td>" . $displayValue . "</td>";
echo "</tr>";
}
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Internet authentication key', 'urbackup')) . "</td>";
echo "<td>";
$authKey = (string) $api_data['authkey'];
if ($authKey === '') {
echo '<span class="text-muted">-</span>';
} else {
echo '<input type="password" class="form-control" value="' . htmlspecialchars($authKey) . '" readonly id="urbackup-authkey">';
echo '<div class="form-check mt-2">';
echo '<input type="checkbox" class="form-check-input" id="urbackup-show-key" onchange="document.getElementById(\'urbackup-authkey\').type = this.checked ? \'text\' : \'password\'">';
echo '<label class="form-check-label" for="urbackup-show-key">' . __('Show', 'urbackup') . '</label>';
echo '</div>';
}
echo "</td>";
echo "</tr>";
echo "</table>";
echo '<details class="mt-2" style="cursor:pointer;color:#666;font-size:0.85em">';
echo '<summary>' . htmlspecialchars(__('API raw data (debug)', 'urbackup')) . '</summary>';
echo '<pre style="max-height:300px;overflow:auto;background:#f5f5f5;padding:8px;border:1px solid #ddd;font-size:0.8em">';
echo "--- client_settings ---\n\n";
echo htmlspecialchars(json_encode($settings, JSON_PRETTY_PRINT));
echo "\n\n--- client_status ---\n\n";
echo htmlspecialchars(json_encode($status, JSON_PRETTY_PRINT));
echo "\n\n--- recent_backups ---\n\n";
echo htmlspecialchars(json_encode($api_data['recent_backups'] ?? [], JSON_PRETTY_PRINT));
echo '</pre>';
echo '</details>';
}
/**
* Show actions section.
*
* @param CommonDBTM $item Asset
* @param Server $server Server
* @param array<string, mixed> $link Link
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function showActionsSection(
CommonDBTM $item,
Server $server,
array $link,
array $api_data
): void {
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='2'>" . htmlspecialchars(__('Available actions', 'urbackup')) . "</th></tr>";
if (!Profile::canCurrentUser(UPDATE) && !Profile::canCurrentUser(CREATE)) {
echo "<tr class='tab_bg_1'>";
echo "<td colspan='2'>";
echo htmlspecialchars(__('You do not have permission for UrBackup actions.', 'urbackup'));
echo "</td>";
echo "</tr>";
echo "</table>";
return;
}
if (!$api_data['client_found'] && Profile::canCurrentUser(CREATE)) {
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Create client in UrBackup', 'urbackup')) . "</td>";
echo "<td>";
self::showActionButton($item, 'create_client', __('Create client in UrBackup', 'urbackup'), 'btn btn-primary');
echo "</td>";
echo "</tr>";
}
if (Profile::canCurrentUser(UPDATE)) {
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Internet mode', 'urbackup')) . "</td>";
echo "<td>";
self::showInternetModeForm($item, $api_data);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Default directories', 'urbackup')) . "</td>";
echo "<td>";
self::showDefaultDirsForm($item, $api_data);
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Backup commands', 'urbackup')) . "</td>";
echo "<td>";
self::showActionButton($item, 'incremental_file_backup', __('Incremental file backup', 'urbackup'), 'btn btn-secondary');
self::showActionButton($item, 'full_file_backup', __('Full file backup', 'urbackup'), 'btn btn-secondary');
self::showActionButton($item, 'incremental_image_backup', __('Incremental image backup', 'urbackup'), 'btn btn-secondary');
self::showActionButton($item, 'full_image_backup', __('Full image backup', 'urbackup'), 'btn btn-secondary');
echo "</td>";
echo "</tr>";
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Disconnect client', 'urbackup')) . "</td>";
echo "<td>";
self::showDisconnectButton($item);
echo "</td>";
echo "</tr>";
}
if (Profile::canCurrentUser(PURGE)) {
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(__('Delete client from UrBackup server', 'urbackup')) . "</td>";
echo "<td>";
echo "<div class='alert alert-warning'>";
echo htmlspecialchars(
__('The client deletion will be queued on the UrBackup server and may require up to 24 hours.', 'urbackup')
);
echo "</div>";
self::showActionButton($item, 'delete_client', __('Delete client from UrBackup server', 'urbackup'), 'btn btn-danger');
echo "</td>";
echo "</tr>";
}
echo "</table>";
}
/**
* Show info/log section.
*
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function formatBytes(mixed $bytes): string
{
$bytes = (float) ($bytes ?? 0);
if ($bytes <= 0) {
return '-';
}
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = floor(log($bytes, 1024));
$i = min((int) $i, count($units) - 1);
return sprintf('%.1f %s', $bytes / pow(1024, $i), $units[$i]);
}
/**
* Show info/log section.
*
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function showInfoLogSection(array $api_data): void
{
$recent_backups = $api_data['recent_backups'] ?? [];
usort($recent_backups, function ($a, $b) {
$tsA = (int) ($a['time'] ?? $a['backuptime'] ?? $a['backup_time'] ?? $a['created'] ?? $a['start_time'] ?? 0);
$tsB = (int) ($b['time'] ?? $b['backuptime'] ?? $b['backup_time'] ?? $b['created'] ?? $b['start_time'] ?? 0);
return $tsB - $tsA;
});
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='6'>" . htmlspecialchars(__('Recent backups', 'urbackup')) . "</th></tr>";
echo "<tr>";
echo "<th>" . htmlspecialchars(__('Type')) . "</th>";
echo "<th>" . htmlspecialchars(__('Date')) . "</th>";
echo "<th>" . htmlspecialchars(__('Result')) . "</th>";
echo "<th>" . htmlspecialchars(__('Size')) . "</th>";
echo "<th>" . htmlspecialchars(__('Incremental', 'urbackup')) . "</th>";
echo "<th>" . htmlspecialchars(__('Backup ID', 'urbackup')) . "</th>";
echo "</tr>";
foreach ($recent_backups as $backup) {
$incremental = (int) ($backup['incremental'] ?? 0);
$timestamp = $backup['time'] ?? $backup['backuptime'] ?? $backup['backup_time'] ?? $backup['created'] ?? $backup['start_time'] ?? 0;
$dateFormatted = self::formatTimestamp($timestamp);
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars((string) ($backup['backup_type'] ?? '')) . "</td>";
echo "<td>" . htmlspecialchars($dateFormatted) . "</td>";
echo "<td><span class='text-success'><i class='ti ti-circle-check'></i> " . htmlspecialchars(__('Success', 'urbackup')) . "</span></td>";
echo "<td>" . htmlspecialchars(self::formatBytes($backup['size'] ?? $backup['size_bytes'] ?? 0)) . "</td>";
echo "<td>" . htmlspecialchars($incremental === 1 ? __('Yes', 'urbackup') : __('No', 'urbackup')) . "</td>";
echo "<td><code>" . htmlspecialchars((string) ($backup['backupid'] ?? $backup['id'] ?? '-')) . "</code></td>";
echo "</tr>";
}
if (count($api_data['recent_backups']) === 0) {
echo "<tr class='tab_bg_1'><td colspan='6'>";
echo htmlspecialchars(__('No recent backup information available.', 'urbackup'));
echo "</td></tr>";
}
echo "</table>";
echo "<br>";
$logs = array_reverse($api_data['logs'] ?? []);
echo "<table class='tab_cadre_fixe'>";
echo "<tr><th colspan='3'>" . htmlspecialchars(__('Client logs', 'urbackup')) . "</th></tr>";
echo "<tr>";
echo "<th>" . htmlspecialchars(__('Date')) . "</th>";
echo "<th>" . htmlspecialchars(__('Level')) . "</th>";
echo "<th>" . htmlspecialchars(__('Message')) . "</th>";
echo "</tr>";
foreach ($logs as $log) {
echo "<tr class='tab_bg_1'>";
echo "<td>" . htmlspecialchars(self::formatTimestamp($log['time'] ?? $log['created'] ?? '')) . "</td>";
echo "<td>" . htmlspecialchars((string) ($log['level'] ?? $log['severity'] ?? '')) . "</td>";
echo "<td>" . htmlspecialchars((string) ($log['message'] ?? $log['msg'] ?? $log['text'] ?? '')) . "</td>";
echo "</tr>";
}
if (count($api_data['logs']) === 0) {
echo "<tr class='tab_bg_1'><td colspan='3'>";
echo htmlspecialchars(__('No client logs available.', 'urbackup'));
echo "</td></tr>";
}
echo "</table>";
}
/**
* Show generic UrBackup action button.
*
* @param CommonDBTM $item Asset
* @param string $action Action
* @param string $label Button label
* @param string $class CSS class
*
* @return void
*/
private static function showActionButton(CommonDBTM $item, string $action, string $label, string $class): void
{
echo "<form method='post' action='" . htmlspecialchars(PLUGIN_URBACKUP_WEB_DIR . "/front/asset.form.php") . "' class='plugin-urbackup-inline-form'>";
echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]);
echo Html::hidden('itemtype', ['value' => $item::class]);
echo Html::hidden('items_id', ['value' => (int) $item->fields['id']]);
echo Html::hidden('urbackup_action', ['value' => $action]);
echo Html::submit($label, [
'name' => 'execute',
'class' => $class,
]);
Html::closeForm();
echo " ";
}
/**
* Show internet mode form.
*
* @param CommonDBTM $item Asset
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function showInternetModeForm(CommonDBTM $item, array $api_data): void
{
$settings = $api_data['client_settings'];
$current = (int) self::extractSettingValue(
$settings['internet_mode_enabled'] ?? $settings['internet_mode'] ?? null,
0
);
echo "<form method='post' action='" . htmlspecialchars(PLUGIN_URBACKUP_WEB_DIR . "/front/asset.form.php") . "' class='plugin-urbackup-inline-form'>";
echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]);
echo Html::hidden('itemtype', ['value' => $item::class]);
echo Html::hidden('items_id', ['value' => (int) $item->fields['id']]);
echo Html::hidden('urbackup_action', ['value' => 'set_internet_mode']);
echo Html::hidden('execute', ['value' => '1']);
echo Html::hidden('internet_mode', ['value' => '0']);
echo '<div class="form-check form-switch">';
echo '<input class="form-check-input" type="checkbox" name="internet_mode" value="1" id="internet-mode-toggle" role="switch"'
. ($current ? ' checked' : '')
. ' onchange="this.form.submit()">';
echo '<label class="form-check-label" for="internet-mode-toggle">'
. htmlspecialchars(__('Enable Internet mode', 'urbackup'))
. '</label>';
echo '</div>';
Html::closeForm();
}
/**
* Show default directories form.
*
* @param CommonDBTM $item Asset
* @param array<string, mixed> $api_data API data
*
* @return void
*/
private static function showDefaultDirsForm(CommonDBTM $item, array $api_data): void
{
$settings = $api_data['client_settings'];
$raw = $settings['default_dirs'] ?? '';
$display = '';
if (is_array($raw)) {
// API returns struct: {"use":N,"value":"paths","value_client":"","value_group":""}
$display = (string) ($raw['value'] ?? '');
} else {
$display = (string) $raw;
}
echo "<form method='post' action='" . htmlspecialchars(PLUGIN_URBACKUP_WEB_DIR . "/front/asset.form.php") . "'>";
echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]);
echo Html::hidden('itemtype', ['value' => $item::class]);
echo Html::hidden('items_id', ['value' => (int) $item->fields['id']]);
echo Html::hidden('urbackup_action', ['value' => 'set_default_dirs']);
echo "<textarea name='default_dirs' rows='4' cols='80'>" . htmlspecialchars($display) . "</textarea><br>";
echo '<div style="color:#999;font-size:0.8em">';
echo 'raw: ' . htmlspecialchars(json_encode($raw));
echo '</div>';
echo Html::submit(__('Save'), [
'name' => 'execute',
'class' => 'btn btn-primary',
]);
Html::closeForm();
}
/**
* Show disconnect button.
*
* @param CommonDBTM $item Asset
*
* @return void
*/
private static function showDisconnectButton(CommonDBTM $item): void
{
echo "<form method='post' action='" . htmlspecialchars(PLUGIN_URBACKUP_WEB_DIR . "/front/asset.form.php") . "'>";
echo Html::hidden('_glpi_csrf_token', ['value' => Session::getNewCSRFToken()]);
echo Html::hidden('itemtype', ['value' => $item::class]);
echo Html::hidden('items_id', ['value' => (int) $item->fields['id']]);
echo Html::submit(__('Disconnect', 'urbackup'), [
'name' => 'disconnect',
'class' => 'btn btn-warning',
]);
Html::closeForm();
}
/**
* Format online status.
*
* @param array<string, mixed> $status Status
*
* @return string
*/
private static function formatOnlineStatus(array $status): string
{
$online = $status['online'] ?? $status['is_online'] ?? null;
return match (true) {
$online === true || $online === 1 || $online === '1' || $online === 'true' => __('Online', 'urbackup'),
$online === false || $online === 0 || $online === '0' || $online === 'false' => __('Offline', 'urbackup'),
default => '',
};
}
/**
* Format bool status.
*
* @param mixed $value Value
*
* @return string
*/
private static function formatBoolStatus(mixed $value): string
{
return match (true) {
$value === true || $value === 1 || $value === '1' || $value === 'true' => __('OK', 'urbackup'),
$value === false || $value === 0 || $value === '0' || $value === 'false' => __('Failed', 'urbackup'),
default => '',
};
}
/**
* Format timestamp-like value.
*
* @param mixed $value Timestamp
*
* @return string
*/
private static function formatTimestamp(mixed $value): string
{
if ($value === null || $value === '' || $value === 0 || $value === '0') {
return '';
}
$ts = (int) $value;
if ($ts > 0 && $ts < 2000000000) {
// Unix timestamp
return Html::convDateTime(date('Y-m-d H:i:s', $ts));
}
$str = trim((string) $value);
if ($str === '') {
return '';
}
// ISO date string like "2024-01-15T10:30:00" or "2024-01-15 10:30:00"
$normalized = str_replace('T', ' ', $str);
if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/', $normalized)) {
return Html::convDateTime($normalized);
}
// Plain date like "2024-01-15"
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $str)) {
return Html::convDate($str);
}
return $str;
}
public static function startBackup(CommonDBTM $item, string $type): bool
{
$link = ServerAsset::getLinkForAsset($item::class, (int) $item->fields['id'], false);
if ($link === null) {
return false;
}
$server_id = (int) ($link['plugin_urbackup_servers_id'] ?? 0);
if ($server_id <= 0) {
return false;
}
$server = new Server();
if (!$server->getFromDB($server_id)) {
return false;
}
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
if ($client_name === '') {
return false;
}
try {
$api = new UrbackupApiClient($server);
switch ($type) {
case 'file':
return $api->startIncrementalFileBackup($client_name);
case 'image':
return $api->startIncrementalImageBackup($client_name);
}
} catch (\Throwable $e) {
return false;
}
return false;
}
public static function saveInternetMode(CommonDBTM $item, bool $enabled): bool
{
$link = ServerAsset::getLinkForAsset($item::class, (int) $item->fields['id'], false);
if ($link === null) {
return false;
}
$server_id = (int) ($link['plugin_urbackup_servers_id'] ?? 0);
if ($server_id <= 0) {
return false;
}
$server = new Server();
if (!$server->getFromDB($server_id)) {
return false;
}
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
if ($client_name === '') {
return false;
}
try {
$api = new UrbackupApiClient($server);
return $api->saveInternetMode($client_name, $enabled);
} catch (\Throwable $e) {
return false;
}
}
public static function saveDefaultDirs(CommonDBTM $item, string $dirs): bool
{
$link = ServerAsset::getLinkForAsset($item::class, (int) $item->fields['id'], false);
if ($link === null) {
return false;
}
$server_id = (int) ($link['plugin_urbackup_servers_id'] ?? 0);
if ($server_id <= 0) {
return false;
}
$server = new Server();
if (!$server->getFromDB($server_id)) {
return false;
}
$client_name = ServerAsset::getAssetName($item::class, (int) $item->fields['id']);
if ($client_name === '') {
return false;
}
try {
$api = new UrbackupApiClient($server);
return $api->updateClientSettings($client_name, 'default_dirs', trim($dirs));
} catch (\Throwable $e) {
return false;
}
}
}