- buildTerminalPhaseMap: Schritt 1b - Leitungen mit expliziter Farbe als Startpunkte (nur Gerät→Gerät, keine Abgänge) - buildTerminalPhaseMap: Block-Durchreichung (Top↔Bottom) entfernt - buildTerminalPhaseMap: Junction-Verbindungen (Terminal→Leitung) bidirektional verarbeitet via _connectionById Index - PWA: Abgangs-Rendering mit Index-Fallback wenn source_terminal_id fehlt - PWA: Abgangs-Labels max-height 130px, min-height 30px - Auto-Naming: EquipmentCarrier create/update → 'R' + count - Auto-Naming: EquipmentPanel update → 'Feld ' + count - pwa_api.php: Hardcoded Fallbacks 'Feld'/'Hutschiene' entfernt - pwa.js: Hutschiene Auto-Naming dynamisch aus Panel-Carrier-Anzahl - kundenkarte.js: Carrier-Dialog Placeholder 'z.B. R1 (automatisch)' - SW Cache auf v12.5 hochgezählt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
364 lines
11 KiB
PHP
Executable file
364 lines
11 KiB
PHP
Executable file
<?php
|
|
/* Copyright (C) 2026 Alles Watt lauft
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
/**
|
|
* \file kundenkarte/admin/backup.php
|
|
* \ingroup kundenkarte
|
|
* \brief Backup and restore page for KundenKarte data
|
|
*/
|
|
|
|
// Load Dolibarr environment
|
|
$res = 0;
|
|
if (!$res && file_exists("../../main.inc.php")) {
|
|
$res = @include "../../main.inc.php";
|
|
}
|
|
if (!$res && file_exists("../../../main.inc.php")) {
|
|
$res = @include "../../../main.inc.php";
|
|
}
|
|
if (!$res) {
|
|
die("Include of main fails");
|
|
}
|
|
|
|
// Libraries
|
|
require_once DOL_DOCUMENT_ROOT."/core/lib/admin.lib.php";
|
|
require_once DOL_DOCUMENT_ROOT."/core/class/html.form.class.php";
|
|
require_once '../lib/kundenkarte.lib.php';
|
|
dol_include_once('/kundenkarte/class/anlagebackup.class.php');
|
|
|
|
// Translations
|
|
$langs->loadLangs(array("admin", "kundenkarte@kundenkarte"));
|
|
|
|
// Access control
|
|
if (!$user->admin && !$user->hasRight('kundenkarte', 'admin')) {
|
|
accessforbidden();
|
|
}
|
|
|
|
// Parameters
|
|
$action = GETPOST('action', 'aZ09');
|
|
$confirm = GETPOST('confirm', 'alpha');
|
|
|
|
$form = new Form($db);
|
|
$backup = new AnlageBackup($db);
|
|
|
|
/*
|
|
* Actions
|
|
*/
|
|
|
|
// Create backup
|
|
if ($action == 'create_backup') {
|
|
$includeFiles = GETPOSTINT('include_files');
|
|
|
|
$result = $backup->createBackup($includeFiles);
|
|
if ($result) {
|
|
setEventMessages($langs->trans('BackupCreatedSuccess', basename($result)), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans('BackupCreatedError').': '.$backup->error, null, 'errors');
|
|
}
|
|
|
|
header('Location: '.$_SERVER['PHP_SELF']);
|
|
exit;
|
|
}
|
|
|
|
// Download backup
|
|
if ($action == 'download') {
|
|
$filename = GETPOST('file', 'alpha');
|
|
$filepath = $conf->kundenkarte->dir_output.'/backups/'.basename($filename);
|
|
|
|
if (file_exists($filepath) && strpos($filename, 'kundenkarte_backup_') === 0) {
|
|
header('Content-Type: application/zip');
|
|
header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
|
|
header('Content-Length: '.filesize($filepath));
|
|
readfile($filepath);
|
|
exit;
|
|
} else {
|
|
setEventMessages($langs->trans('FileNotFound'), null, 'errors');
|
|
}
|
|
}
|
|
|
|
// Delete backup
|
|
if ($action == 'confirm_delete' && $confirm == 'yes') {
|
|
$filename = GETPOST('file', 'alpha');
|
|
if ($backup->deleteBackup($filename)) {
|
|
setEventMessages($langs->trans('BackupDeleted'), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans('Error'), null, 'errors');
|
|
}
|
|
|
|
header('Location: '.$_SERVER['PHP_SELF']);
|
|
exit;
|
|
}
|
|
|
|
// Restore backup
|
|
if ($action == 'confirm_restore' && $confirm == 'yes') {
|
|
$filename = GETPOST('file', 'alpha');
|
|
$clearExisting = GETPOSTINT('clear_existing');
|
|
|
|
$filepath = $conf->kundenkarte->dir_output.'/backups/'.basename($filename);
|
|
|
|
if ($backup->restoreBackup($filepath, $clearExisting)) {
|
|
setEventMessages($langs->trans('RestoreSuccess'), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans('RestoreError').': '.$backup->error, null, 'errors');
|
|
}
|
|
|
|
header('Location: '.$_SERVER['PHP_SELF']);
|
|
exit;
|
|
}
|
|
|
|
// Upload backup
|
|
if ($action == 'upload_backup' && !empty($_FILES['backupfile']['name'])) {
|
|
$backupDir = $conf->kundenkarte->dir_output.'/backups';
|
|
if (!is_dir($backupDir)) {
|
|
dol_mkdir($backupDir);
|
|
}
|
|
|
|
$filename = $_FILES['backupfile']['name'];
|
|
|
|
// Validate filename format
|
|
if (!preg_match('/^kundenkarte_backup_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\.zip$/', $filename)) {
|
|
setEventMessages($langs->trans('InvalidBackupFile'), null, 'errors');
|
|
} elseif ($_FILES['backupfile']['error'] !== UPLOAD_ERR_OK) {
|
|
setEventMessages($langs->trans('ErrorUploadFailed'), null, 'errors');
|
|
} else {
|
|
$targetPath = $backupDir.'/'.$filename;
|
|
if (move_uploaded_file($_FILES['backupfile']['tmp_name'], $targetPath)) {
|
|
setEventMessages($langs->trans('BackupUploaded'), null, 'mesgs');
|
|
} else {
|
|
setEventMessages($langs->trans('ErrorUploadFailed'), null, 'errors');
|
|
}
|
|
}
|
|
|
|
header('Location: '.$_SERVER['PHP_SELF']);
|
|
exit;
|
|
}
|
|
|
|
/*
|
|
* View
|
|
*/
|
|
|
|
$title = $langs->trans("BackupRestore");
|
|
llxHeader('', $title);
|
|
|
|
// Subheader
|
|
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
|
|
print load_fiche_titre($title, $linkback, 'title_setup');
|
|
|
|
// Configuration header
|
|
$head = kundenkarteAdminPrepareHead();
|
|
print dol_get_fiche_head($head, 'backup', $langs->trans('ModuleKundenKarteName'), -1, "fa-address-card");
|
|
|
|
// Confirmation dialogs
|
|
if ($action == 'delete') {
|
|
$filename = GETPOST('file', 'alpha');
|
|
print $form->formconfirm(
|
|
$_SERVER['PHP_SELF'].'?file='.urlencode($filename),
|
|
$langs->trans('DeleteBackup'),
|
|
$langs->trans('ConfirmDeleteBackup', $filename),
|
|
'confirm_delete',
|
|
'',
|
|
'yes',
|
|
1
|
|
);
|
|
}
|
|
|
|
if ($action == 'restore') {
|
|
$filename = GETPOST('file', 'alpha');
|
|
|
|
$formquestion = array(
|
|
array(
|
|
'type' => 'checkbox',
|
|
'name' => 'clear_existing',
|
|
'label' => $langs->trans('ClearExistingData'),
|
|
'value' => 0
|
|
)
|
|
);
|
|
|
|
print $form->formconfirm(
|
|
$_SERVER['PHP_SELF'].'?file='.urlencode($filename),
|
|
$langs->trans('RestoreBackup'),
|
|
$langs->trans('ConfirmRestoreBackup', $filename),
|
|
'confirm_restore',
|
|
$formquestion,
|
|
'yes',
|
|
1
|
|
);
|
|
}
|
|
|
|
// Statistics
|
|
$stats = $backup->getStatistics();
|
|
|
|
print '<div class="fichecenter">';
|
|
|
|
// Stats cards
|
|
print '<div style="display:flex;gap:20px;flex-wrap:wrap;margin-bottom:30px;">';
|
|
|
|
print '<div class="info-box" style="min-width:200px;">';
|
|
print '<span class="info-box-icon bg-primary"><i class="fa fa-sitemap"></i></span>';
|
|
print '<div class="info-box-content">';
|
|
print '<span class="info-box-text">'.$langs->trans('TotalElements').'</span>';
|
|
print '<span class="info-box-number">'.$stats['total_anlagen'].'</span>';
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print '<div class="info-box" style="min-width:200px;">';
|
|
print '<span class="info-box-icon bg-success"><i class="fa fa-file"></i></span>';
|
|
print '<div class="info-box-content">';
|
|
print '<span class="info-box-text">'.$langs->trans('TotalFiles').'</span>';
|
|
print '<span class="info-box-number">'.$stats['total_files'].'</span>';
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print '<div class="info-box" style="min-width:200px;">';
|
|
print '<span class="info-box-icon bg-warning"><i class="fa fa-plug"></i></span>';
|
|
print '<div class="info-box-content">';
|
|
print '<span class="info-box-text">'.$langs->trans('TotalConnections').'</span>';
|
|
print '<span class="info-box-number">'.$stats['total_connections'].'</span>';
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print '<div class="info-box" style="min-width:200px;">';
|
|
print '<span class="info-box-icon bg-info"><i class="fa fa-building"></i></span>';
|
|
print '<div class="info-box-content">';
|
|
print '<span class="info-box-text">'.$langs->trans('CustomersWithAnlagen').'</span>';
|
|
print '<span class="info-box-number">'.$stats['total_customers'].'</span>';
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print '<div class="info-box" style="min-width:200px;">';
|
|
print '<span class="info-box-icon bg-secondary"><i class="fa fa-hdd-o"></i></span>';
|
|
print '<div class="info-box-content">';
|
|
print '<span class="info-box-text">'.$langs->trans('FilesStorageSize').'</span>';
|
|
print '<span class="info-box-number">'.dol_print_size($stats['files_size']).'</span>';
|
|
print '</div>';
|
|
print '</div>';
|
|
|
|
print '</div>';
|
|
|
|
// Create Backup Section
|
|
print '<div class="titre inline-block">'.$langs->trans("CreateBackup").'</div>';
|
|
print '<br><br>';
|
|
|
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
|
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
|
print '<input type="hidden" name="action" value="create_backup">';
|
|
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="2">'.$langs->trans("BackupOptions").'</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="oddeven">';
|
|
print '<td style="width:300px;">'.$langs->trans("IncludeUploadedFiles").'</td>';
|
|
print '<td>';
|
|
print '<input type="checkbox" name="include_files" value="1" checked> ';
|
|
print '<span class="opacitymedium">'.$langs->trans("IncludeFilesHelp").'</span>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
|
|
print '</table>';
|
|
|
|
print '<br>';
|
|
print '<div class="center">';
|
|
print '<button type="submit" class="button button-primary">';
|
|
print '<i class="fa fa-download"></i> '.$langs->trans("CreateBackupNow");
|
|
print '</button>';
|
|
print '</div>';
|
|
|
|
print '</form>';
|
|
|
|
// Upload Backup Section
|
|
print '<br><br>';
|
|
print '<div class="titre inline-block">'.$langs->trans("UploadBackup").'</div>';
|
|
print '<br><br>';
|
|
|
|
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'" enctype="multipart/form-data">';
|
|
print '<input type="hidden" name="token" value="'.newToken().'">';
|
|
print '<input type="hidden" name="action" value="upload_backup">';
|
|
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<td colspan="2">'.$langs->trans("UploadBackupFile").'</td>';
|
|
print '</tr>';
|
|
|
|
print '<tr class="oddeven">';
|
|
print '<td style="width:300px;">'.$langs->trans("SelectBackupFile").'</td>';
|
|
print '<td>';
|
|
print '<input type="file" name="backupfile" accept=".zip" class="flat">';
|
|
print ' <button type="submit" class="button buttongen smallpaddingimp">'.$langs->trans("Upload").'</button>';
|
|
print '</td>';
|
|
print '</tr>';
|
|
|
|
print '</table>';
|
|
|
|
print '</form>';
|
|
|
|
// Existing Backups Section
|
|
print '<br><br>';
|
|
print '<div class="titre inline-block">'.$langs->trans("ExistingBackups").'</div>';
|
|
print '<br><br>';
|
|
|
|
$backups = $backup->getBackupList();
|
|
|
|
print '<table class="noborder centpercent">';
|
|
print '<tr class="liste_titre">';
|
|
print '<td>'.$langs->trans("Filename").'</td>';
|
|
print '<td>'.$langs->trans("Date").'</td>';
|
|
print '<td class="right">'.$langs->trans("Size").'</td>';
|
|
print '<td class="center">'.$langs->trans("Actions").'</td>';
|
|
print '</tr>';
|
|
|
|
if (empty($backups)) {
|
|
print '<tr class="oddeven">';
|
|
print '<td colspan="4" class="opacitymedium center">'.$langs->trans("NoBackupsFound").'</td>';
|
|
print '</tr>';
|
|
} else {
|
|
foreach ($backups as $bk) {
|
|
print '<tr class="oddeven">';
|
|
print '<td><i class="fa fa-file-archive-o"></i> '.dol_escape_htmltag($bk['filename']).'</td>';
|
|
print '<td>'.dol_print_date(strtotime($bk['date']), 'dayhour').'</td>';
|
|
print '<td class="right">'.dol_print_size($bk['size']).'</td>';
|
|
print '<td class="center nowraponall">';
|
|
|
|
// Download button
|
|
print '<a href="'.$_SERVER['PHP_SELF'].'?action=download&file='.urlencode($bk['filename']).'&token='.newToken().'" class="button buttongen smallpaddingimp" title="'.$langs->trans('Download').'">';
|
|
print '<i class="fa fa-download"></i>';
|
|
print '</a> ';
|
|
|
|
// Restore button
|
|
print '<a href="'.$_SERVER['PHP_SELF'].'?action=restore&file='.urlencode($bk['filename']).'&token='.newToken().'" class="button buttongen smallpaddingimp" title="'.$langs->trans('Restore').'">';
|
|
print '<i class="fa fa-undo"></i>';
|
|
print '</a> ';
|
|
|
|
// Delete button
|
|
print '<a href="'.$_SERVER['PHP_SELF'].'?action=delete&file='.urlencode($bk['filename']).'&token='.newToken().'" class="button buttongen smallpaddingimp" title="'.$langs->trans('Delete').'">';
|
|
print '<i class="fa fa-trash"></i>';
|
|
print '</a>';
|
|
|
|
print '</td>';
|
|
print '</tr>';
|
|
}
|
|
}
|
|
|
|
print '</table>';
|
|
|
|
// Info section
|
|
print '<br>';
|
|
print '<div class="info">';
|
|
print '<strong><i class="fa fa-info-circle"></i> '.$langs->trans("BackupInfo").':</strong><br>';
|
|
print '• '.$langs->trans("BackupInfoContent").'<br>';
|
|
print '• '.$langs->trans("BackupInfoFiles").'<br>';
|
|
print '• '.$langs->trans("BackupInfoRestore").'<br>';
|
|
print '</div>';
|
|
|
|
print '</div>';
|
|
|
|
print dol_get_fiche_end();
|
|
|
|
llxFooter();
|
|
$db->close();
|