Files
dasposchi-de/admin/navigation.php
Claude 3c97192386 Initiales CMS: Deutschsprachiges Blog-System mit Admin-Bereich
Vollständiges, schlankes PHP/SQLite-CMS für IT-, KI- und Gaming-Inhalte:

- Core: DB-Singleton, Auth mit Passwort-Hashing, Session-Cookies,
  CSRF-Schutz, Login-Rate-Limit, Bild-Upload mit serverseitiger Validierung
- Admin: Dashboard, Artikel/Seiten-Verwaltung mit Quill WYSIWYG-Editor,
  Kategorien, Navigation (Drag & Drop), Medienbibliothek, Profil
- Frontend: Responsive Dark-Theme, Artikel-Grid, Kategorie-Filter,
  Archiv, Paginierung, SEO-Meta-Tags
- Sicherheit: Prepared Statements, HTML-Sanitizer, .htaccess-Schutz
  für sensible Verzeichnisse, PHP-Ausführungsschutz im Upload-Ordner
- Installation: install.php erstellt DB-Schema und Admin-Account

https://claude.ai/code/session_01Xsg4j2t4S9goMuWVpF3ezG
2026-04-05 20:59:52 +00:00

213 lines
9.2 KiB
PHP
Raw 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
require_once __DIR__ . '/../core/auth.php';
auth_start_session();
auth_require_login();
$pdo = db();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
csrf_verify();
// Reihenfolge per AJAX aktualisieren
if (isset($_POST['reorder'])) {
$order = json_decode($_POST['reorder'], true);
if (is_array($order)) {
$stmt = $pdo->prepare('UPDATE navigation SET sort_order = ? WHERE id = ?');
foreach ($order as $pos => $navId) {
$stmt->execute([$pos, (int) $navId]);
}
}
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) {
header('Content-Type: application/json');
echo json_encode(['success' => true]);
exit;
}
flash('success', 'Reihenfolge aktualisiert.');
redirect('/admin/navigation.php');
}
if (isset($_POST['delete_id'])) {
$stmt = $pdo->prepare('DELETE FROM navigation WHERE id = ?');
$stmt->execute([(int) $_POST['delete_id']]);
flash('success', 'Navigationspunkt gelöscht.');
redirect('/admin/navigation.php');
}
if (isset($_POST['save'])) {
$navId = !empty($_POST['nav_id']) ? (int) $_POST['nav_id'] : null;
$label = trim($_POST['label'] ?? '');
$type = $_POST['type'] ?? 'url';
$target = trim($_POST['target'] ?? '');
$sortOrder = (int) ($_POST['sort_order'] ?? 0);
if (!in_array($type, ['url', 'page', 'category', 'home'])) {
$type = 'url';
}
if ($label === '') {
flash('error', 'Bezeichnung ist erforderlich.');
} else {
if ($navId) {
$stmt = $pdo->prepare('UPDATE navigation SET label=?, type=?, target=?, sort_order=? WHERE id=?');
$stmt->execute([$label, $type, $target, $sortOrder, $navId]);
flash('success', 'Navigationspunkt aktualisiert.');
} else {
$maxOrder = (int) $pdo->query('SELECT COALESCE(MAX(sort_order),0) FROM navigation')->fetchColumn();
$stmt = $pdo->prepare('INSERT INTO navigation (label, type, target, sort_order) VALUES (?, ?, ?, ?)');
$stmt->execute([$label, $type, $target, $maxOrder + 1]);
flash('success', 'Navigationspunkt erstellt.');
}
}
redirect('/admin/navigation.php');
}
}
$navItems = $pdo->query('SELECT * FROM navigation ORDER BY sort_order')->fetchAll();
$allPages = $pdo->query("SELECT id, title FROM pages WHERE status='published' ORDER BY title")->fetchAll();
$allCategories = $pdo->query('SELECT id, name FROM categories ORDER BY sort_order, name')->fetchAll();
$editNav = null;
if (isset($_GET['edit'])) {
$stmt = $pdo->prepare('SELECT * FROM navigation WHERE id = ?');
$stmt->execute([(int) $_GET['edit']]);
$editNav = $stmt->fetch();
}
$pageTitle = 'Navigation';
$currentPage = 'navigation';
ob_start();
?>
<div class="card">
<h3><?= $editNav ? 'Navigationspunkt bearbeiten' : 'Neuer Navigationspunkt' ?></h3>
<form method="post" class="inline-edit-form">
<?= csrf_field() ?>
<?php if ($editNav): ?>
<input type="hidden" name="nav_id" value="<?= $editNav['id'] ?>">
<?php endif; ?>
<div class="form-row-inline">
<div class="form-group">
<label for="label">Bezeichnung</label>
<input type="text" id="label" name="label" required
value="<?= e($editNav['label'] ?? '') ?>">
</div>
<div class="form-group">
<label for="type">Typ</label>
<select id="type" name="type" onchange="updateTargetField(this.value)">
<option value="home" <?= ($editNav['type'] ?? '') === 'home' ? 'selected' : '' ?>>Startseite</option>
<option value="page" <?= ($editNav['type'] ?? '') === 'page' ? 'selected' : '' ?>>Seite</option>
<option value="category" <?= ($editNav['type'] ?? '') === 'category' ? 'selected' : '' ?>>Kategorie</option>
<option value="url" <?= ($editNav['type'] ?? '') === 'url' ? 'selected' : '' ?>>URL</option>
</select>
</div>
<div class="form-group" id="targetGroup">
<label for="target">Ziel</label>
<input type="text" id="targetInput" name="target"
value="<?= e($editNav['target'] ?? '') ?>" placeholder="URL eingeben">
<select id="targetPageSelect" name="target_page" style="display:none">
<option value=""> Seite wählen </option>
<?php foreach ($allPages as $p): ?>
<option value="<?= $p['id'] ?>" <?= ($editNav['target'] ?? '') == $p['id'] ? 'selected' : '' ?>>
<?= e($p['title']) ?>
</option>
<?php endforeach; ?>
</select>
<select id="targetCatSelect" name="target_category" style="display:none">
<option value=""> Kategorie wählen </option>
<?php foreach ($allCategories as $c): ?>
<option value="<?= $c['id'] ?>" <?= ($editNav['target'] ?? '') == $c['id'] ? 'selected' : '' ?>>
<?= e($c['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>&nbsp;</label>
<button type="submit" name="save" value="1" class="btn btn-primary">Speichern</button>
<?php if ($editNav): ?>
<a href="/admin/navigation.php" class="btn">Abbrechen</a>
<?php endif; ?>
</div>
</div>
</form>
</div>
<div class="card">
<h3>Navigationspunkte</h3>
<?php if (empty($navItems)): ?>
<p class="empty-state">Keine Navigationspunkte vorhanden.</p>
<?php else: ?>
<table class="table" id="navTable">
<thead>
<tr><th>Reihenfolge</th><th>Bezeichnung</th><th>Typ</th><th>Ziel</th><th>Aktionen</th></tr>
</thead>
<tbody id="navBody">
<?php foreach ($navItems as $nav): ?>
<tr data-id="<?= $nav['id'] ?>">
<td class="drag-handle" style="cursor:grab">&#9776; <?= $nav['sort_order'] ?></td>
<td><?= e($nav['label']) ?></td>
<td><?= e($nav['type']) ?></td>
<td><?= e($nav['target']) ?></td>
<td class="actions">
<a href="?edit=<?= $nav['id'] ?>" class="btn btn-sm">Bearbeiten</a>
<form method="post" class="inline-form" onsubmit="return confirm('Wirklich löschen?')">
<?= csrf_field() ?>
<input type="hidden" name="delete_id" value="<?= $nav['id'] ?>">
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
<?php
$content = ob_get_clean();
$extraScripts = '
<script>
function updateTargetField(type) {
var input = document.getElementById("targetInput");
var pageSelect = document.getElementById("targetPageSelect");
var catSelect = document.getElementById("targetCatSelect");
input.style.display = "none"; pageSelect.style.display = "none"; catSelect.style.display = "none";
input.name = ""; pageSelect.name = ""; catSelect.name = "";
if (type === "url") { input.style.display = ""; input.name = "target"; }
else if (type === "page") { pageSelect.style.display = ""; pageSelect.name = "target"; }
else if (type === "category") { catSelect.style.display = ""; catSelect.name = "target"; }
else { input.value = ""; input.name = "target"; }
}
document.addEventListener("DOMContentLoaded", function() {
updateTargetField(document.getElementById("type").value);
// Drag & Drop Sortierung
var tbody = document.getElementById("navBody");
if (!tbody) return;
var dragEl = null;
tbody.querySelectorAll("tr").forEach(function(row) {
row.draggable = true;
row.addEventListener("dragstart", function(e) { dragEl = row; row.style.opacity = "0.4"; });
row.addEventListener("dragend", function() { row.style.opacity = "1"; });
row.addEventListener("dragover", function(e) { e.preventDefault(); });
row.addEventListener("drop", function(e) {
e.preventDefault();
if (dragEl !== row) { tbody.insertBefore(dragEl, row); saveOrder(); }
});
});
function saveOrder() {
var ids = [];
tbody.querySelectorAll("tr").forEach(function(r) { ids.push(r.dataset.id); });
var formData = new FormData();
formData.append("reorder", JSON.stringify(ids));
formData.append("csrf_token", "<?= csrf_token() ?>");
fetch("/admin/navigation.php", {
method: "POST", body: formData,
headers: {"X-Requested-With": "XMLHttpRequest"}
});
}
});
</script>
';
include __DIR__ . '/templates/layout.php';