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:
142
admin/media.php
Normal file
142
admin/media.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../core/auth.php';
|
||||
require_once __DIR__ . '/../core/upload.php';
|
||||
auth_start_session();
|
||||
auth_require_login();
|
||||
|
||||
// Bild hochladen
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
csrf_verify();
|
||||
|
||||
if (isset($_POST['delete_file'])) {
|
||||
$file = $_POST['delete_file'];
|
||||
// Sicherstellen, dass der Pfad im Upload-Verzeichnis liegt
|
||||
$realUploadDir = realpath(UPLOAD_DIR);
|
||||
$realFile = realpath(UPLOAD_DIR . $file);
|
||||
if ($realFile && str_starts_with($realFile, $realUploadDir) && is_file($realFile)) {
|
||||
unlink($realFile);
|
||||
flash('success', 'Datei gelöscht.');
|
||||
} else {
|
||||
flash('error', 'Datei nicht gefunden.');
|
||||
}
|
||||
redirect('/admin/media.php');
|
||||
}
|
||||
|
||||
if (!empty($_FILES['images'])) {
|
||||
$files = $_FILES['images'];
|
||||
$uploaded = 0;
|
||||
$count = is_array($files['name']) ? count($files['name']) : 1;
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$file = [
|
||||
'name' => is_array($files['name']) ? $files['name'][$i] : $files['name'],
|
||||
'type' => is_array($files['type']) ? $files['type'][$i] : $files['type'],
|
||||
'tmp_name' => is_array($files['tmp_name']) ? $files['tmp_name'][$i] : $files['tmp_name'],
|
||||
'error' => is_array($files['error']) ? $files['error'][$i] : $files['error'],
|
||||
'size' => is_array($files['size']) ? $files['size'][$i] : $files['size'],
|
||||
];
|
||||
if ($file['error'] === UPLOAD_ERR_OK && handle_upload($file)) {
|
||||
$uploaded++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($uploaded > 0) {
|
||||
flash('success', $uploaded . ' Datei(en) hochgeladen.');
|
||||
} else {
|
||||
flash('error', 'Upload fehlgeschlagen.');
|
||||
}
|
||||
redirect('/admin/media.php');
|
||||
}
|
||||
}
|
||||
|
||||
// Alle Bilder sammeln
|
||||
function scan_uploads(string $dir, string $prefix = ''): array
|
||||
{
|
||||
$files = [];
|
||||
if (!is_dir($dir)) return $files;
|
||||
$items = scandir($dir);
|
||||
foreach ($items as $item) {
|
||||
if ($item === '.' || $item === '..' || $item === '.htaccess' || $item === '.gitkeep') continue;
|
||||
$path = $dir . '/' . $item;
|
||||
if (is_dir($path)) {
|
||||
$files = array_merge($files, scan_uploads($path, $prefix . $item . '/'));
|
||||
} elseif (preg_match('/\.(jpg|jpeg|png|gif|webp)$/i', $item)) {
|
||||
$files[] = [
|
||||
'path' => $prefix . $item,
|
||||
'url' => UPLOAD_URL . $prefix . $item,
|
||||
'size' => filesize($path),
|
||||
'time' => filemtime($path),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
$images = scan_uploads(rtrim(UPLOAD_DIR, '/'));
|
||||
usort($images, fn($a, $b) => $b['time'] - $a['time']);
|
||||
|
||||
$pageTitle = 'Medien';
|
||||
$currentPage = 'media';
|
||||
ob_start();
|
||||
?>
|
||||
<div class="card">
|
||||
<h3>Bilder hochladen</h3>
|
||||
<form method="post" enctype="multipart/form-data" class="upload-form">
|
||||
<?= csrf_field() ?>
|
||||
<div class="upload-zone" id="uploadZone">
|
||||
<p>Bilder hierher ziehen oder klicken</p>
|
||||
<input type="file" name="images[]" multiple accept="image/*" id="fileInput">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="margin-top:10px">Hochladen</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>Medienbibliothek (<?= count($images) ?>)</h3>
|
||||
<?php if (empty($images)): ?>
|
||||
<p class="empty-state">Noch keine Bilder vorhanden.</p>
|
||||
<?php else: ?>
|
||||
<div class="media-grid">
|
||||
<?php foreach ($images as $img): ?>
|
||||
<div class="media-item">
|
||||
<img src="<?= e($img['url']) ?>" alt="" loading="lazy">
|
||||
<div class="media-actions">
|
||||
<button class="btn btn-sm" onclick="copyUrl('<?= e($img['url']) ?>')">URL kopieren</button>
|
||||
<form method="post" class="inline-form" onsubmit="return confirm('Bild wirklich löschen?')">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="delete_file" value="<?= e($img['path']) ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$content = ob_get_clean();
|
||||
|
||||
$extraScripts = '
|
||||
<script>
|
||||
function copyUrl(url) {
|
||||
var fullUrl = window.location.origin + url;
|
||||
navigator.clipboard.writeText(fullUrl).then(function() {
|
||||
alert("URL kopiert: " + fullUrl);
|
||||
});
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
var zone = document.getElementById("uploadZone");
|
||||
var input = document.getElementById("fileInput");
|
||||
zone.addEventListener("click", function() { input.click(); });
|
||||
zone.addEventListener("dragover", function(e) { e.preventDefault(); zone.classList.add("dragover"); });
|
||||
zone.addEventListener("dragleave", function() { zone.classList.remove("dragover"); });
|
||||
zone.addEventListener("drop", function(e) {
|
||||
e.preventDefault(); zone.classList.remove("dragover");
|
||||
input.files = e.dataTransfer.files;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
';
|
||||
|
||||
include __DIR__ . '/templates/layout.php';
|
||||
Reference in New Issue
Block a user