";
echo htmlspecialchars(__('You do not have permission to view UrBackup information.', 'urbackup'));
echo "";
return true;
}
$itemtype = $item::class;
$items_id = (int) ($item->fields['id'] ?? 0);
$link = ServerAsset::getLinkForAsset($itemtype, $items_id, true);
echo "
";
if ($link === null) {
self::showNoServerLinkedBlock($item);
} else {
self::showServerLinkedBlock($item, $link);
}
echo "
";
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 "";
echo htmlspecialchars(__('No UrBackup server linked.', 'urbackup'));
echo "
";
echo "";
echo "| " . htmlspecialchars(__('UrBackup server selection', 'urbackup')) . " |
";
echo "";
echo "| " . htmlspecialchars(__('Asset location ID', 'urbackup')) . " | ";
echo "" . htmlspecialchars((string) $asset_location_id) . " | ";
echo "
";
echo "";
echo "| " . htmlspecialchars(__('Root location ID', 'urbackup')) . " | ";
echo "" . htmlspecialchars((string) $root_location_id) . " | ";
echo "
";
if ($is_sub_location) {
echo "";
echo "| ";
echo htmlspecialchars(
__('The asset is in a sub-location. The plugin will use the server assigned to the root location.', 'urbackup')
);
echo " | ";
echo "
";
}
if (count($servers) === 0) {
echo "";
echo "| ";
echo " ";
echo htmlspecialchars(__('No UrBackup server available for the root location of this asset.', 'urbackup'));
echo " ";
echo " | ";
echo "
";
echo "
";
return;
}
if (!Profile::canCurrentUser(UPDATE) && !Profile::canCurrentUser(CREATE)) {
echo "";
echo "| ";
echo htmlspecialchars(__('A server is available, but you do not have permission to link this asset.', 'urbackup'));
echo " | ";
echo "
";
echo "";
return;
}
echo "";
echo "| " . htmlspecialchars(__('Available servers for root location', 'urbackup')) . " | ";
echo "";
echo " | ";
echo "
";
echo "";
}
/**
* Show block when server is linked.
*
* @param CommonDBTM $item Asset item
* @param array $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 "";
echo htmlspecialchars(__('The linked UrBackup server no longer exists.', 'urbackup'));
echo "
";
return;
}
$api_data = self::loadApiData($item, $server, $link);
echo "";
echo "| " . htmlspecialchars(__('UrBackup status', 'urbackup')) . " |
";
echo "";
echo "| " . htmlspecialchars(__('Linked server', 'urbackup')) . " | ";
echo "" . $server->getLink() . " | ";
echo "" . htmlspecialchars(__('IP address', 'urbackup')) . " | ";
echo "" . htmlspecialchars((string) $server->fields['ip_address']) . " | ";
echo "
";
echo "";
echo "| " . htmlspecialchars(__('UrBackup server version', 'urbackup')) . " | ";
echo "" . htmlspecialchars((string) ($server->fields['server_version'] ?? '')) . " | ";
echo "" . htmlspecialchars(__('Client name', 'urbackup')) . " | ";
echo "" . htmlspecialchars(ServerAsset::getAssetName($item::class, (int) $item->fields['id'])) . " | ";
echo "
";
echo "
";
if ($api_data['error'] !== '') {
echo "";
echo htmlspecialchars($api_data['error']);
echo "
";
}
if ($api_data['ip_warning'] !== '') {
echo "";
echo htmlspecialchars($api_data['ip_warning']);
echo "
";
}
self::showInternalTabs($item, $server, $link, $api_data);
}
/**
* Load API data for asset tab.
*
* @param CommonDBTM $item Asset
* @param Server $server Server
* @param array $link Link
*
* @return array
*/
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 $link Link
* @param array $api_data API data
*
* @return void
*/
private static function showInternalTabs(
CommonDBTM $item,
Server $server,
array $link,
array $api_data
): void {
echo "";
echo "
" . htmlspecialchars(__('State', 'urbackup')) . "
";
self::showStateSection($server, $link, $api_data);
echo "" . htmlspecialchars(__('Actions', 'urbackup')) . "
";
self::showActionsSection($item, $server, $link, $api_data);
echo "" . htmlspecialchars(__('Info / Log', 'urbackup')) . "
";
self::showInfoLogSection($api_data);
echo "";
}
/**
* Show state section.
*
* @param Server $server Server
* @param array $link Link
* @param array $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 "";
echo "| " . htmlspecialchars(__('Client state', 'urbackup')) . " |
";
if (!$api_data['client_found']) {
echo "";
echo "| ";
echo htmlspecialchars(__('Client not found on UrBackup server.', 'urbackup'));
echo " | ";
echo "
";
echo "
";
return;
}
$internetMode = self::extractSettingValue($settings['internet_mode_enabled'] ?? $settings['internet_mode'] ?? null, 0);
$internetModeDisplay = ((int) $internetMode === 1)
? '' . __('Yes', 'urbackup') . ''
: '' . __('No', 'urbackup') . '';
$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 "";
echo "| " . htmlspecialchars((string) $label) . " | ";
echo "" . $displayValue . " | ";
echo "
";
}
echo "";
echo "| " . htmlspecialchars(__('Internet authentication key', 'urbackup')) . " | ";
echo "";
$authKey = (string) $api_data['authkey'];
if ($authKey === '') {
echo '-';
} else {
echo '';
echo ' ';
echo '';
echo '';
echo ' ';
}
echo " | ";
echo "
";
echo "";
echo '';
echo '' . htmlspecialchars(__('API raw data (debug)', 'urbackup')) . '
';
echo '';
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 '';
echo ' ';
}
/**
* Show actions section.
*
* @param CommonDBTM $item Asset
* @param Server $server Server
* @param array $link Link
* @param array $api_data API data
*
* @return void
*/
private static function showActionsSection(
CommonDBTM $item,
Server $server,
array $link,
array $api_data
): void {
echo "";
echo "| " . htmlspecialchars(__('Available actions', 'urbackup')) . " |
";
if (!Profile::canCurrentUser(UPDATE) && !Profile::canCurrentUser(CREATE)) {
echo "";
echo "| ";
echo htmlspecialchars(__('You do not have permission for UrBackup actions.', 'urbackup'));
echo " | ";
echo "
";
echo "
";
return;
}
if (!$api_data['client_found'] && Profile::canCurrentUser(CREATE)) {
echo "";
echo "| " . htmlspecialchars(__('Create client in UrBackup', 'urbackup')) . " | ";
echo "";
self::showActionButton($item, 'create_client', __('Create client in UrBackup', 'urbackup'), 'btn btn-primary');
echo " | ";
echo "
";
}
if (Profile::canCurrentUser(UPDATE)) {
echo "";
echo "| " . htmlspecialchars(__('Internet mode', 'urbackup')) . " | ";
echo "";
self::showInternetModeForm($item, $api_data);
echo " | ";
echo "
";
echo "";
echo "| " . htmlspecialchars(__('Default directories', 'urbackup')) . " | ";
echo "";
self::showDefaultDirsForm($item, $api_data);
echo " | ";
echo "
";
echo "";
echo "| " . htmlspecialchars(__('Backup commands', 'urbackup')) . " | ";
echo "";
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 " | ";
echo "
";
echo "";
echo "| " . htmlspecialchars(__('Disconnect client', 'urbackup')) . " | ";
echo "";
self::showDisconnectButton($item);
echo " | ";
echo "
";
}
if (Profile::canCurrentUser(PURGE)) {
echo "";
echo "| " . htmlspecialchars(__('Delete client from UrBackup server', 'urbackup')) . " | ";
echo "";
echo " ";
echo htmlspecialchars(
__('The client deletion will be queued on the UrBackup server and may require up to 24 hours.', 'urbackup')
);
echo " ";
self::showActionButton($item, 'delete_client', __('Delete client from UrBackup server', 'urbackup'), 'btn btn-danger');
echo " | ";
echo "
";
}
echo "";
}
/**
* Show info/log section.
*
* @param array $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 $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 "";
echo "| " . htmlspecialchars(__('Recent backups', 'urbackup')) . " |
";
echo "";
echo "| " . htmlspecialchars(__('Type')) . " | ";
echo "" . htmlspecialchars(__('Date')) . " | ";
echo "" . htmlspecialchars(__('Result')) . " | ";
echo "" . htmlspecialchars(__('Size')) . " | ";
echo "" . htmlspecialchars(__('Incremental', 'urbackup')) . " | ";
echo "" . htmlspecialchars(__('Backup ID', 'urbackup')) . " | ";
echo "
";
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 "";
echo "| " . htmlspecialchars((string) ($backup['backup_type'] ?? '')) . " | ";
echo "" . htmlspecialchars($dateFormatted) . " | ";
echo " " . htmlspecialchars(__('Success', 'urbackup')) . " | ";
echo "" . htmlspecialchars(self::formatBytes($backup['size'] ?? $backup['size_bytes'] ?? 0)) . " | ";
echo "" . htmlspecialchars($incremental === 1 ? __('Yes', 'urbackup') : __('No', 'urbackup')) . " | ";
echo "" . htmlspecialchars((string) ($backup['backupid'] ?? $backup['id'] ?? '-')) . " | ";
echo "
";
}
if (count($api_data['recent_backups']) === 0) {
echo "| ";
echo htmlspecialchars(__('No recent backup information available.', 'urbackup'));
echo " |
";
}
echo "
";
echo "
";
$logs = array_reverse($api_data['logs'] ?? []);
echo "";
echo "| " . htmlspecialchars(__('Client logs', 'urbackup')) . " |
";
echo "";
echo "| " . htmlspecialchars(__('Date')) . " | ";
echo "" . htmlspecialchars(__('Level')) . " | ";
echo "" . htmlspecialchars(__('Message')) . " | ";
echo "
";
foreach ($logs as $log) {
echo "";
echo "| " . htmlspecialchars(self::formatTimestamp($log['time'] ?? $log['created'] ?? '')) . " | ";
echo "" . htmlspecialchars((string) ($log['level'] ?? $log['severity'] ?? '')) . " | ";
echo "" . htmlspecialchars((string) ($log['message'] ?? $log['msg'] ?? $log['text'] ?? '')) . " | ";
echo "
";
}
if (count($api_data['logs']) === 0) {
echo "| ";
echo htmlspecialchars(__('No client logs available.', 'urbackup'));
echo " |
";
}
echo "
";
}
/**
* 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 "