bericht/admin/setup.php
Eduard Wisch 3069453823
All checks were successful
Deploy bericht / deploy (push) Successful in 1s
fix: PWA-QR-Bereich Dark-Theme — dunkler Frame, nur QR-Code selbst weiß [deploy]
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:13:42 +02:00

238 lines
12 KiB
PHP

<?php
/* Copyright (C) 2026 Eduard Wisch <data@data-it-solution.de>
* GPL v3+
*
* Admin-Setup für das Bericht-Modul.
* Verwaltung von ODT-Templates + globalen Konstanten.
*/
$res = 0;
if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php";
$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; $tmp2 = realpath(__FILE__); $i = strlen($tmp) - 1; $j = strlen($tmp2) - 1;
while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { $i--; $j--; }
if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php";
if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php";
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");
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once __DIR__.'/../lib/bericht.lib.php';
if (!$user->admin && !$user->hasRight('bericht', 'admin')) accessforbidden();
$langs->loadLangs(array("admin", "bericht@bericht"));
$action = GETPOST('action', 'alpha');
$templates_dir = DOL_DATA_ROOT.'/bericht/templates';
if (!is_dir($templates_dir)) {
dol_mkdir($templates_dir);
}
// --- Aktionen ---
if ($action === 'upload_template' && !empty($_FILES['template_file']['name'])) {
$name = dol_sanitizeFileName($_FILES['template_file']['name']);
if (!preg_match('/\.odt$/i', $name)) {
setEventMessages('Nur .odt-Dateien erlaubt', null, 'errors');
} else {
$dest = $templates_dir.'/'.$name;
if (move_uploaded_file($_FILES['template_file']['tmp_name'], $dest)) {
setEventMessages($langs->trans("BerichtSetupTemplateUploaded"), null, 'mesgs');
} else {
setEventMessages('Upload fehlgeschlagen', null, 'errors');
}
}
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
if ($action === 'delete_template') {
$name = dol_sanitizeFileName(GETPOST('name', 'alphanohtml'));
$path = $templates_dir.'/'.$name;
if ($name && file_exists($path)) {
@unlink($path);
setEventMessages($langs->trans("BerichtSetupTemplateDeleted"), null, 'mesgs');
}
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
if ($action === 'save_const') {
$consts = array(
'BERICHT_DEFAULT_TEMPLATE' => GETPOST('default_template', 'alphanohtml'),
'BERICHT_TAB_ON_INVOICE' => GETPOST('tab_invoice', 'int') ? '1' : '0',
'BERICHT_TAB_ON_ORDER' => GETPOST('tab_order', 'int') ? '1' : '0',
'BERICHT_TAB_ON_PROPAL' => GETPOST('tab_propal', 'int') ? '1' : '0',
'BERICHT_BURN_ANNOTATIONS' => GETPOST('burn', 'int') ? '1' : '0',
'BERICHT_LIBREOFFICE_BIN' => GETPOST('lobin', 'alphanohtml'),
);
foreach ($consts as $k => $v) {
dolibarr_set_const($db, $k, $v, 'chaine', 0, '', $conf->entity);
}
setEventMessages($langs->trans("SetupSaved"), null, 'mesgs');
header("Location: ".$_SERVER['PHP_SELF']);
exit;
}
// --- Anzeige ---
llxHeader('', $langs->trans("BerichtSetup"));
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
print load_fiche_titre($langs->trans("BerichtSetup"), $linkback, 'title_setup');
print '<span class="opacitymedium">'.$langs->trans("BerichtSetupDescription").'</span><br><br>';
// --- PWA-Link prominent oben ---
$pwa_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].'/custom/baustelle/';
print '<div class="bericht-setup-section" style="background:linear-gradient(135deg,#1e3a5f,#2a5080);border:1px solid #4080c0;">';
print '<h3 style="color:#fff;">📱 Baustelle PWA</h3>';
print '<p style="color:#e0e8f0;">Mobile Progressive Web App für die Baustelle. Installierbar auf Handy oder Tablet, funktioniert offline.</p>';
print '<p style="color:#e0e8f0;font-size:13px;"><strong>Funktionen:</strong> Auftragsliste, Foto-Aufnahme direkt aus der Kamera, automatische Synchronisierung bei Verbindung, Multi-User-Filter pro angemeldetem User</p>';
print '<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:12px;">';
print ' <a href="'.dol_escape_htmltag($pwa_url).'" target="_blank" class="butAction" style="background:#5cb85c;color:#fff;border:none;padding:10px 20px;border-radius:6px;text-decoration:none;font-weight:600;">🚀 PWA öffnen</a>';
print ' <button type="button" id="btn-show-pwa-qr" style="background:#337ab7;color:#fff;border:1px solid #2868a0;padding:10px 20px;border-radius:6px;font-weight:600;cursor:pointer;font-size:14px;">📱 QR-Code anzeigen</button>';
print ' <code style="background:rgba(0,0,0,0.3);padding:8px 12px;border-radius:4px;color:#e0e8f0;font-size:12px;">'.dol_escape_htmltag($pwa_url).'</code>';
print '</div>';
print '<div id="pwa-qr-area" style="display:none;text-align:center;margin-top:16px;background:rgba(0,0,0,0.4);padding:20px;border-radius:8px;border:1px solid rgba(255,255,255,0.15);">';
print ' <div id="pwa-qr" style="display:inline-block;background:#fff;padding:12px;border-radius:6px;line-height:0;"></div>';
print ' <p style="color:#e0e8f0;font-size:12px;margin:12px 0 0;">Mit dem Handy scannen → „Zum Home-Screen hinzufügen"</p>';
print '</div>';
print '</div>';
// API-Status
print '<div class="bericht-setup-section">';
print '<h3>🔌 REST-API Status</h3>';
$api_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https://' : 'http://').$_SERVER['HTTP_HOST'].'/custom/bericht/api';
print '<p>Endpoint: <code>'.dol_escape_htmltag($api_url).'</code></p>';
print '<table class="noborder">';
print '<tr><td><code>POST '.dol_escape_htmltag($api_url).'/auth.php</code></td><td>Login → JWT</td></tr>';
print '<tr><td><code>GET '.dol_escape_htmltag($api_url).'/orders.php</code></td><td>Aufträge des Users</td></tr>';
print '<tr><td><code>GET '.dol_escape_htmltag($api_url).'/orders.php?id=X</code></td><td>Auftrags-Detail</td></tr>';
print '<tr><td><code>POST '.dol_escape_htmltag($api_url).'/orders.php?id=X&action=upload_photo</code></td><td>Foto hochladen</td></tr>';
print '<tr><td><code>GET '.dol_escape_htmltag($api_url).'/reports.php?id=X</code></td><td>Bericht-Detail</td></tr>';
print '</table>';
print '<p class="opacitymedium small">JWT-Token sind 7 Tage gültig. Multi-User über fk_user_author/valid/modif gefiltert.</p>';
print '</div>';
// --- Templates ---
print '<div class="bericht-setup-section">';
print '<h3>'.$langs->trans("BerichtSetupTemplates").'</h3>';
print '<p class="opacitymedium">'.$langs->trans("BerichtSetupTemplatesDesc").'</p>';
$templates = bericht_list_templates();
if (empty($templates)) {
print '<div class="opacitymedium">'.$langs->trans("BerichtSetupNoTemplates").'</div>';
} else {
print '<table class="noborder centpercent">';
print '<tr class="liste_titre"><th>'.$langs->trans("File").'</th><th>'.$langs->trans("Size").'</th><th class="right">'.$langs->trans("Action").'</th></tr>';
foreach ($templates as $tpl) {
$path = $templates_dir.'/'.$tpl;
print '<tr class="oddeven">';
print '<td>📄 '.dol_escape_htmltag($tpl).'</td>';
print '<td>'.dol_print_size(filesize($path)).'</td>';
print '<td class="right">';
print '<a href="?action=delete_template&name='.urlencode($tpl).'&token='.newToken().'" '
.'onclick="return confirm(\'Vorlage löschen?\')" class="button-small">'.$langs->trans("Delete").'</a>';
print '</td>';
print '</tr>';
}
print '</table>';
}
print '<br>';
print '<form method="post" enctype="multipart/form-data">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="upload_template">';
print '<label class="butAction" for="template_file">📤 '.$langs->trans("BerichtSetupUploadTemplate").'</label>';
print '<input type="file" id="template_file" name="template_file" accept=".odt" onchange="this.form.submit()" style="display:none">';
print '</form>';
print '</div><br>';
// --- Platzhalter-Hinweis ---
print '<div class="bericht-setup-section">';
print '<h3>'.$langs->trans("BerichtPlaceholdersTitle").'</h3>';
print '<table class="noborder centpercent" style="max-width:700px">';
$placeholders = array(
'{auftragsnummer}' => 'Aus extrafield options_auftragsnummer der Rechnung',
'{angebotsnummer}' => 'Aus extrafield options_angebotsnummer',
'{rechnungsnummer}' => 'Rechnungsnummer (ref)',
'{kunde_name}' => 'Name des Kunden (Société)',
'{kunde_adresse}' => 'Adresse des Kunden (mehrzeilig)',
'{datum}' => 'Heutiges Datum',
'{beschreibung}' => 'Auftragsbeschreibung (extrafield)',
'{hinweis}' => 'Hinweis (extrafield)',
'{bericht_titel}' => 'Titel des Berichts',
'{ersteller}' => 'Login-Name des Erstellers',
);
foreach ($placeholders as $k => $desc) {
print '<tr class="oddeven"><td><code>'.$k.'</code></td><td>'.$desc.'</td></tr>';
}
print '</table>';
print '</div><br>';
// --- Konstanten ---
print '<div class="bericht-setup-section">';
print '<h3>'.$langs->trans("BerichtSetupOptions").'</h3>';
print '<form method="post">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="save_const">';
print '<table class="noborder centpercent">';
print '<tr class="oddeven"><td class="titlefield">'.$langs->trans("BerichtSetupDefaultTemplate").'</td><td>';
print '<select name="default_template">';
print '<option value="">— '.$langs->trans("BerichtNoTemplate").' —</option>';
$current_default = getDolGlobalString('BERICHT_DEFAULT_TEMPLATE', '');
foreach ($templates as $tpl) {
$sel = ($tpl === $current_default) ? ' selected' : '';
print '<option value="'.dol_escape_htmltag($tpl).'"'.$sel.'>'.dol_escape_htmltag($tpl).'</option>';
}
print '</select></td></tr>';
$cb = function ($name, $key, $default) {
$v = getDolGlobalString($key, $default) ? 'checked' : '';
return '<input type="checkbox" name="'.$name.'" value="1" '.$v.'>';
};
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupTabInvoice").'</td><td>'.$cb('tab_invoice', 'BERICHT_TAB_ON_INVOICE', '1').'</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupTabOrder").'</td><td>'.$cb('tab_order', 'BERICHT_TAB_ON_ORDER', '1').'</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupTabPropal").'</td><td>'.$cb('tab_propal', 'BERICHT_TAB_ON_PROPAL', '1').'</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupBurnAnnotations").'</td><td>'.$cb('burn', 'BERICHT_BURN_ANNOTATIONS', '1').'</td></tr>';
print '<tr class="oddeven"><td>'.$langs->trans("BerichtSetupLibreOfficeBin").'</td><td>';
print '<input type="text" name="lobin" value="'.dol_escape_htmltag(getDolGlobalString('BERICHT_LIBREOFFICE_BIN', '/usr/bin/libreoffice')).'" size="40">';
print '</td></tr>';
print '</table>';
print '<br><button type="submit" class="butAction">'.$langs->trans("Save").'</button>';
print '</form>';
print '</div>';
// QR-Code-Lib + kleines Init-Script für PWA-QR
print '<script src="'.dol_buildpath('/bericht/js/lib/qrcode.min.js', 1).'"></script>';
print '<script>
document.addEventListener("DOMContentLoaded", function () {
var btn = document.getElementById("btn-show-pwa-qr");
if (!btn) return;
var area = document.getElementById("pwa-qr-area");
var container = document.getElementById("pwa-qr");
var url = '.json_encode($pwa_url).';
var rendered = false;
btn.addEventListener("click", function () {
if (area.style.display === "none") {
area.style.display = "";
if (!rendered && typeof QRCode !== "undefined") {
new QRCode(container, { text: url, width: 220, height: 220, correctLevel: QRCode.CorrectLevel.M });
rendered = true;
}
} else {
area.style.display = "none";
}
});
});
</script>';
llxFooter();
$db->close();