Files
EpiWebview/dist/editconfig.php

414 lines
16 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* Config-Editor für EPI Webview
* - Liest ../config.php (falls vorhanden) und ../example.config.php
* - Zeigt editierbare Felder für alle Keys aus example.config.php
* - Zeigt veraltete (nur in config.php existierende) Keys rot & disabled
* - Speichert Werte zurück nach ../config.php
*/
error_reporting(E_ALL);
// ---- Dateipfade anpassen falls nötig ----
$CONFIG_FILE = realpath(__DIR__ . '/../config.php') ?: (__DIR__ . '/../config.php');
$EXAMPLE_FILE = realpath(__DIR__ . '/../example.config.php') ?: (__DIR__ . '/../example.config.php');
// ---- Hilfsfunktionen: Parsing ----
function parse_define_file(string $file): array {
if (!is_file($file)) return ['defines' => [], 'order' => [], 'ui' => []];
$lines = file($file, FILE_IGNORE_NEW_LINES);
$defines = [];
$order = [];
$ui = [];
// BOM entfernen, falls vorhanden
if (isset($lines[0])) {
$lines[0] = preg_replace('/^\xEF\xBB\xBF/', '', $lines[0]);
}
// Regex
$reDefine = '/^\s*define\(\s*([\'"])([^\'"]+)\1\s*,\s*(.+?)\s*\)\s*;\s*(?:(?:\/\/|#)\s*(.*))?$/u';
$reSection = '/^\s*\/\/\s*@section\s*:\s*(.+)\s*$/iu';
$reHR = '/^\s*\/\/\s*@hr\s*$/iu';
$reNote = '/^\s*\/\/\s*@note\s*:\s*(.+)\s*$/iu';
foreach ($lines as $ln) {
// Meta-Kommentare zuerst prüfen (UI-Struktur)
if (preg_match($reSection, $ln, $m)) {
$ui[] = ['kind' => 'section', 'title' => trim($m[1])];
continue;
}
if (preg_match($reHR, $ln)) {
$ui[] = ['kind' => 'hr'];
continue;
}
if (preg_match($reNote, $ln, $m)) {
$ui[] = ['kind' => 'note', 'text' => trim($m[1])];
continue;
}
// define(…) Zeilen
if (!preg_match($reDefine, $ln, $m)) {
continue;
}
$name = $m[2];
$rawValueExpr = trim($m[3]);
$inlineC = isset($m[4]) ? trim($m[4]) : '';
// Typ bestimmen
$type = 'string';
$valForForm = '';
$raw = rtrim($rawValueExpr, ',');
if (preg_match('/^(true|false)$/i', $raw)) {
$type = 'bool';
$valForForm = (strtolower($raw) === 'true');
} elseif (preg_match('/^[+-]?\d+$/', $raw)) {
$type = 'int';
$valForForm = (int)$raw;
} elseif (preg_match('/^([\'"])(.*)\1$/s', $raw, $mm)) {
$type = 'string';
$valForForm = stripcslashes($mm[2]);
} else {
$type = 'string';
$valForForm = $raw;
}
$defines[$name] = [
'name' => $name,
'type' => $type,
'value' => $valForForm,
'raw' => $rawValueExpr,
'comment' => $inlineC,
'line' => $ln,
];
$order[] = $name;
// Wichtig: define auch in die UI-Reihenfolge aufnehmen
$ui[] = ['kind' => 'define', 'name' => $name];
}
return ['defines' => $defines, 'order' => $order, 'ui' => $ui];
}
function php_export_define_value(string $type, $value): string {
// Baut den rechten Teil von define('X', <HIER>);
if ($type === 'bool') {
return $value ? 'true' : 'false';
}
if ($type === 'int') {
return (string)intval($value);
}
// String:
// einfache Quotes nehmen und escapen
$escaped = str_replace("'", "\\'", (string)$value);
return "'" . $escaped . "'";
}
function h(?string $s): string { return htmlspecialchars((string)$s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
// ---- Dateien parsen ----
$example = parse_define_file($EXAMPLE_FILE);
$current = parse_define_file($CONFIG_FILE);
// Keys klassifizieren
$exampleKeys = array_keys($example['defines']);
$currentKeys = array_keys($current['defines']);
$deprecatedKeys = array_values(array_diff($currentKeys, $exampleKeys)); // nur in config.php
$newKeys = array_values(array_diff($exampleKeys, $currentKeys)); // nur in example (neu)
$sharedKeys = array_values(array_intersect($exampleKeys, $currentKeys));
// Beim Rendern: Reihenfolge aus example beibehalten
$renderKeys = $example['order'];
// ---- POST: Speichern ----
$message = '';
$err = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['__save_config']) && isset($_POST['cfg']) && is_array($_POST['cfg'])) {
$incoming = $_POST['cfg']; // ['NAME' => valueString/checkbox etc.]
// Build neuer config.php Inhalt:
$out = [];
$out[] = "<?php";
$out[] = "// Diese Datei wurde durch den Epi Webview Config-Editor erzeugt/aktualisiert.";
$out[] = "// Bearbeite Werte bevorzugt über die Weboberfläche.";
$out[] = "";
// --- Inhalt in der Reihenfolge & Struktur der example.config.php ---
foreach ($example['ui'] as $item) {
if ($item['kind'] === 'section') {
$out[] = "// @section: " . $item['title'];
continue;
}
if ($item['kind'] === 'hr') {
$out[] = "// @hr";
continue;
}
if ($item['kind'] === 'note') {
$out[] = "// @note: " . $item['text'];
continue;
}
if ($item['kind'] !== 'define') {
continue;
}
$label = $item['name'];
$ex = $example['defines'][$label] ?? null;
if (!$ex) continue;
$type = $ex['type']; // Typ an example orientieren
$tooltip = $ex['comment'] ?? '';
// Wert aus POST übernehmen (Checkbox-Fix: existiert das Feld?)
if ($type === 'bool') {
$bool = array_key_exists($label, $incoming);
$valueForDefine = php_export_define_value('bool', $bool);
} elseif ($type === 'int') {
$raw = $incoming[$label] ?? '';
$valueForDefine = php_export_define_value('int', $raw);
} else {
$raw = $incoming[$label] ?? '';
$valueForDefine = php_export_define_value('string', $raw);
}
$line = "define('{$label}', {$valueForDefine});";
if (!empty($tooltip)) {
$line .= " // " . $tooltip; // Kommentare als Tooltip auch in config.php mitführen
}
$out[] = $line;
}
// Deprecated Block (nur in aktueller config.php vorhanden, nicht in example)
if (!empty($deprecatedKeys)) {
$out[] = "";
$out[] = "// --- Veraltete/entfernte Parameter (nur Referenz, werden nicht mehr genutzt) ---";
foreach ($deprecatedKeys as $key) {
$c = $current['defines'][$key];
$valueForDefine = php_export_define_value($c['type'], $c['value']);
$line = "// define('{$c['name']}', {$valueForDefine});";
if (!empty($c['comment'])) {
$line .= " // " . $c['comment'];
}
$out[] = $line;
}
}
$out[] = "?>";
$content = implode("\n", $out) . "\n";
try {
if (false === file_put_contents($CONFIG_FILE, $content)) {
$err = "Konnte config.php nicht schreiben: " . h($CONFIG_FILE);
} else {
$message = "Konfiguration gespeichert.";
// Nach dem Speichern neu parsen, damit UI aktuell ist
$current = parse_define_file($CONFIG_FILE);
$currentKeys = array_keys($current['defines']);
$deprecatedKeys = array_values(array_diff($currentKeys, $exampleKeys));
$sharedKeys = array_values(array_intersect($exampleKeys, $currentKeys));
}
} catch (Throwable $e) {
$err = "Fehler beim Schreiben: " . h($e->getMessage());
}
}
// ---- Werte für Formular vorbereiten ----
// Für jeden example-Key: Formularwert = aus aktueller config (falls vorhanden), sonst example default
function valueForForm(array $example, array $current, string $key) {
$ex = $example['defines'][$key] ?? null;
$cu = $current['defines'][$key] ?? null;
$type = $ex ? $ex['type'] : ($cu ? $cu['type'] : 'string');
$comment = $ex && $ex['comment'] !== '' ? $ex['comment'] : ($cu['comment'] ?? '');
if ($cu) {
$val = $cu['value'];
} else {
$val = $ex['value'];
}
return [$type, $val, $comment];
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="Konfiguration bearbeiten" />
<meta name="author" content="" />
<title>Konfiguration Epi Webview</title>
<link href="css/styles.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/js/all.min.js" crossorigin="anonymous"></script>
<script src="js/jquery-3.5.1.min.js"></script>
<style>
.deprecated { color: #c62828; }
.cfg-help { margin-left: .35rem; cursor: help; }
.form-group { margin-bottom: .75rem; }
.small-muted { font-size:.85rem; color:#6c757d; }
.checkbox-inline { display:flex; align-items:center; gap:.5rem; }
.cfg-label { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
</style>
<script>
$(function(){
// Tooltips (Bootstrap 4)
$('[data-toggle="tooltip"]').tooltip();
// Side UI nachladen wie in deinem Template
loadSidenav();
loadFooter();
});
function loadSidenav(){
$('#layoutSidenav_nav').load('../sources/getSidenav.php');
}
function loadFooter(){
$('#footerholder').load('../sources/getFooter.php');
}
</script>
</head>
<body class="sb-nav-fixed">
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
<a class="navbar-brand" href="index.php">Epi Webview</a>
<button class="btn btn-link btn-sm order-1 order-lg-0" id="sidebarToggle" type="button"><i class="fas fa-bars"></i></button>
<ul class="navbar-nav ml-auto ml-md-0"></ul>
</nav>
<div id="layoutSidenav">
<div id="layoutSidenav_nav"></div>
<div id="layoutSidenav_content">
<main>
<div class="container-fluid">
<h1 class="mt-4">Konfiguration</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item active">Dashboard / Konfiguration</li>
</ol>
<?php if ($message): ?>
<div class="alert alert-success"><?= h($message) ?></div>
<?php endif; ?>
<?php if ($err): ?>
<div class="alert alert-danger"><?= h($err) ?></div>
<?php endif; ?>
<div class="card mb-4">
<div class="card-header">
<i class="fas fa-sliders-h mr-1"></i>
Einstellungen
<span class="small-muted">( <code><?=h(basename($CONFIG_FILE))?></code> Source: <code><?=h(basename($EXAMPLE_FILE))?></code>)</span>
</div>
<div class="card-body">
<form method="post">
<input type="hidden" name="__save_config" value="1" />
<?php foreach ($example['ui'] as $item): ?>
<?php
if ($item['kind'] === 'section') {
echo '<h5 class="mt-3">'.h($item['title']).'</h5>';
continue;
}
if ($item['kind'] === 'hr') {
echo '<hr />';
continue;
}
if ($item['kind'] === 'note') {
echo '<p class="small-muted">'.h($item['text']).'</p>';
continue;
}
if ($item['kind'] !== 'define') {
continue;
}
$key = $item['name'];
// Nur Keys rendern, die in example vorkommen
if (!isset($example['defines'][$key])) continue;
[$type, $val, $tooltip] = valueForForm($example, $current, $key);
?>
<div class="form-group">
<label class="cfg-label" for="cfg_<?=h($key)?>">
<?= h($key) ?>
<?php if ($tooltip): ?>
<i class="far fa-question-circle cfg-help"
data-toggle="tooltip" data-placement="right"
title="<?=h($tooltip)?>"></i>
<?php endif; ?>
</label>
<?php if ($type === 'bool'): ?>
<div class="checkbox-inline">
<input type="checkbox"
id="cfg_<?=h($key)?>"
name="cfg[<?=h($key)?>]"
value="1" <?= $val ? 'checked' : '' ?> />
<span><?= $val ? 'Aktiviert' : 'Deaktiviert' ?></span>
</div>
<?php elseif ($type === 'int'): ?>
<input type="number" step="1" class="form-control"
id="cfg_<?=h($key)?>"
name="cfg[<?=h($key)?>]"
value="<?=h((string)$val)?>" />
<?php else: ?>
<input type="text" class="form-control"
id="cfg_<?=h($key)?>"
name="cfg[<?=h($key)?>]"
value="<?=h((string)$val)?>" />
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php if (!empty($deprecatedKeys)): ?>
<hr />
<h5 class="deprecated">Veraltete Parameter (nur in <code><?=h(basename($CONFIG_FILE))?></code>, nicht mehr in <code><?=h(basename($EXAMPLE_FILE))?></code>)</h5>
<?php foreach ($deprecatedKeys as $key): ?>
<?php $c = $current['defines'][$key]; ?>
<div class="form-group">
<label class="cfg-label deprecated" for="dep_<?=h($key)?>">
<?= h($key) ?>
<?php if (!empty($c['comment'])): ?>
<i class="far fa-question-circle cfg-help" data-toggle="tooltip" data-placement="right" title="<?=h($c['comment'])?>"></i>
<?php endif; ?>
</label>
<input type="text" class="form-control is-invalid" id="dep_<?=h($key)?>" value="<?=
h($c['type']==='bool' ? ($c['value']?'true':'false') : (string)$c['value'])
?>" disabled />
<div class="invalid-feedback">Dieser Parameter ist in der aktuellen Version nicht mehr vorgesehen.</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
<div class="mt-4">
<button type="submit" class="btn btn-primary"><i class="fas fa-save mr-1"></i> Speichern</button>
<span class="small-muted ml-2">Speichert nach <code><?=h($CONFIG_FILE)?></code></span>
</div>
</form>
</div>
</div>
</div>
</main>
<div id="footerholder"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="js/scripts.js"></script>
<script>
// Sidebar Toggle (falls benötigt)
$('#sidebarToggle').on('click', function(){ $('body').toggleClass('sb-sidenav-toggled'); });
</script>
</body>
</html>