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
87 lines
3.3 KiB
PHP
87 lines
3.3 KiB
PHP
<?php
|
||
require_once __DIR__ . '/../core/auth.php';
|
||
auth_start_session();
|
||
auth_require_login();
|
||
|
||
$pdo = db();
|
||
|
||
// Artikel löschen
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_id'])) {
|
||
csrf_verify();
|
||
$stmt = $pdo->prepare('DELETE FROM articles WHERE id = ?');
|
||
$stmt->execute([(int) $_POST['delete_id']]);
|
||
flash('success', 'Artikel gelöscht.');
|
||
redirect('/admin/articles.php');
|
||
}
|
||
|
||
$page = max(1, (int) ($_GET['page'] ?? 1));
|
||
$total = (int) $pdo->query('SELECT COUNT(*) FROM articles')->fetchColumn();
|
||
$pag = paginate($total, $page, ITEMS_PER_PAGE);
|
||
|
||
$stmt = $pdo->prepare(
|
||
"SELECT a.id, a.title, a.slug, a.status, a.published_at, a.created_at, c.name as category_name
|
||
FROM articles a LEFT JOIN categories c ON a.category_id = c.id
|
||
ORDER BY a.created_at DESC LIMIT ? OFFSET ?"
|
||
);
|
||
$stmt->execute([$pag['limit'], $pag['offset']]);
|
||
$articles = $stmt->fetchAll();
|
||
|
||
$pageTitle = 'Artikel';
|
||
$currentPage = 'articles';
|
||
ob_start();
|
||
?>
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<h3>Alle Artikel (<?= $total ?>)</h3>
|
||
<a href="/admin/article-edit.php" class="btn btn-primary btn-sm">Neuer Artikel</a>
|
||
</div>
|
||
<?php if (empty($articles)): ?>
|
||
<p class="empty-state">Noch keine Artikel vorhanden.</p>
|
||
<?php else: ?>
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>Titel</th>
|
||
<th>Kategorie</th>
|
||
<th>Status</th>
|
||
<th>Datum</th>
|
||
<th>Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($articles as $article): ?>
|
||
<tr>
|
||
<td><a href="/admin/article-edit.php?id=<?= $article['id'] ?>"><?= e($article['title']) ?></a></td>
|
||
<td><?= e($article['category_name'] ?? '–') ?></td>
|
||
<td>
|
||
<span class="badge badge-<?= $article['status'] === 'published' ? 'success' : 'warning' ?>">
|
||
<?= $article['status'] === 'published' ? 'Veröffentlicht' : 'Entwurf' ?>
|
||
</span>
|
||
</td>
|
||
<td><?= format_date($article['published_at'] ?? $article['created_at']) ?></td>
|
||
<td class="actions">
|
||
<a href="/admin/article-edit.php?id=<?= $article['id'] ?>" class="btn btn-sm">Bearbeiten</a>
|
||
<form method="post" class="inline-form" onsubmit="return confirm('Artikel wirklich löschen?')">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="delete_id" value="<?= $article['id'] ?>">
|
||
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
|
||
<?php if ($pag['total_pages'] > 1): ?>
|
||
<div class="pagination">
|
||
<?php for ($i = 1; $i <= $pag['total_pages']; $i++): ?>
|
||
<a href="?page=<?= $i ?>" class="<?= $i === $pag['current_page'] ? 'active' : '' ?>"><?= $i ?></a>
|
||
<?php endfor; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
$content = ob_get_clean();
|
||
include __DIR__ . '/templates/layout.php';
|