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
This commit is contained in:
169
admin/page-edit.php
Normal file
169
admin/page-edit.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../core/auth.php';
|
||||
auth_start_session();
|
||||
auth_require_login();
|
||||
|
||||
$pdo = db();
|
||||
$id = isset($_GET['id']) ? (int) $_GET['id'] : null;
|
||||
$page = null;
|
||||
|
||||
if ($id) {
|
||||
$stmt = $pdo->prepare('SELECT * FROM pages WHERE id = ?');
|
||||
$stmt->execute([$id]);
|
||||
$page = $stmt->fetch();
|
||||
if (!$page) {
|
||||
flash('error', 'Seite nicht gefunden.');
|
||||
redirect('/admin/pages.php');
|
||||
}
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
csrf_verify();
|
||||
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$slug = trim($_POST['slug'] ?? '');
|
||||
$body = $_POST['body'] ?? '';
|
||||
$status = in_array($_POST['status'] ?? '', ['draft', 'published']) ? $_POST['status'] : 'draft';
|
||||
|
||||
$errors = [];
|
||||
if ($title === '') {
|
||||
$errors[] = 'Titel ist erforderlich.';
|
||||
}
|
||||
if ($slug === '') {
|
||||
$slug = slugify($title);
|
||||
} else {
|
||||
$slug = slugify($slug);
|
||||
}
|
||||
|
||||
$slugCheck = $pdo->prepare('SELECT id FROM pages WHERE slug = ? AND id != ?');
|
||||
$slugCheck->execute([$slug, $id ?? 0]);
|
||||
if ($slugCheck->fetch()) {
|
||||
$errors[] = 'Dieser Slug wird bereits verwendet.';
|
||||
}
|
||||
|
||||
$body = sanitize_html($body);
|
||||
|
||||
if (empty($errors)) {
|
||||
if ($id) {
|
||||
$stmt = $pdo->prepare("UPDATE pages SET title=?, slug=?, body=?, status=?, updated_at=datetime('now') WHERE id=?");
|
||||
$stmt->execute([$title, $slug, $body, $status, $id]);
|
||||
flash('success', 'Seite aktualisiert.');
|
||||
} else {
|
||||
$stmt = $pdo->prepare('INSERT INTO pages (title, slug, body, status) VALUES (?, ?, ?, ?)');
|
||||
$stmt->execute([$title, $slug, $body, $status]);
|
||||
$id = $pdo->lastInsertId();
|
||||
flash('success', 'Seite erstellt.');
|
||||
}
|
||||
redirect('/admin/page-edit.php?id=' . $id);
|
||||
} else {
|
||||
foreach ($errors as $err) {
|
||||
flash('error', $err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$pageTitle = $page ? 'Seite bearbeiten' : 'Neue Seite';
|
||||
$currentPage = 'pages';
|
||||
|
||||
$extraHead = '<link href="https://cdn.quilljs.com/1.3.7/quill.snow.css" rel="stylesheet">';
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<form method="post" class="edit-form" id="pageForm">
|
||||
<?= csrf_field() ?>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-col-8">
|
||||
<div class="form-group">
|
||||
<label for="title">Titel</label>
|
||||
<input type="text" id="title" name="title" required
|
||||
value="<?= e($page['title'] ?? $_POST['title'] ?? '') ?>">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="slug">Slug</label>
|
||||
<input type="text" id="slug" name="slug"
|
||||
value="<?= e($page['slug'] ?? $_POST['slug'] ?? '') ?>"
|
||||
placeholder="Wird automatisch generiert">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Inhalt</label>
|
||||
<div id="editor"><?= $page['body'] ?? $_POST['body'] ?? '' ?></div>
|
||||
<textarea name="body" id="bodyHidden" style="display:none"><?= e($page['body'] ?? $_POST['body'] ?? '') ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-col-4">
|
||||
<div class="card sidebar-card">
|
||||
<h4>Status</h4>
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="radio" name="status" value="draft"
|
||||
<?= ($page['status'] ?? 'draft') === 'draft' ? 'checked' : '' ?>>
|
||||
Entwurf
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="status" value="published"
|
||||
<?= ($page['status'] ?? '') === 'published' ? 'checked' : '' ?>>
|
||||
Veröffentlicht
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
|
||||
$extraScripts = '
|
||||
<script src="https://cdn.quilljs.com/1.3.7/quill.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var quill = new Quill("#editor", {
|
||||
theme: "snow",
|
||||
modules: {
|
||||
toolbar: {
|
||||
container: [
|
||||
[{"header": [2, 3, 4, false]}],
|
||||
["bold", "italic", "underline"],
|
||||
[{"list": "ordered"}, {"list": "bullet"}],
|
||||
["blockquote", "code-block"],
|
||||
["link", "image"],
|
||||
["clean"]
|
||||
],
|
||||
handlers: {
|
||||
image: function() {
|
||||
var input = document.createElement("input");
|
||||
input.setAttribute("type", "file");
|
||||
input.setAttribute("accept", "image/*");
|
||||
input.click();
|
||||
input.onchange = function() {
|
||||
var file = input.files[0];
|
||||
if (!file) return;
|
||||
var formData = new FormData();
|
||||
formData.append("image", file);
|
||||
formData.append("csrf_token", document.querySelector("[name=csrf_token]").value);
|
||||
fetch("/admin/upload-handler.php", {method:"POST", body:formData})
|
||||
.then(function(r){return r.json();})
|
||||
.then(function(data){
|
||||
if(data.success){var range=quill.getSelection(true);quill.insertEmbed(range.index,"image",data.url);}
|
||||
else{alert(data.error||"Upload fehlgeschlagen");}
|
||||
}).catch(function(){alert("Upload fehlgeschlagen");});
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
document.getElementById("pageForm").addEventListener("submit", function(){
|
||||
document.getElementById("bodyHidden").value = quill.root.innerHTML;
|
||||
});
|
||||
var titleEl=document.getElementById("title"),slugEl=document.getElementById("slug"),slugManual=slugEl.value!=="";
|
||||
slugEl.addEventListener("input",function(){slugManual=slugEl.value!=="";});
|
||||
titleEl.addEventListener("input",function(){
|
||||
if(!slugManual){slugEl.value=titleEl.value.toLowerCase().replace(/[äÄ]/g,"ae").replace(/[öÖ]/g,"oe").replace(/[üÜ]/g,"ue").replace(/ß/g,"ss").replace(/[^a-z0-9\\s-]/g,"").replace(/[\\s-]+/g,"-").replace(/^-|-$/g,"");}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
';
|
||||
|
||||
include __DIR__ . '/templates/layout.php';
|
||||
Reference in New Issue
Block a user