diff --git a/README.md b/README.md
index 747fb2c..726d148 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ Dieses Projekt stellt eine einfache Verwaltungsoberfläche für digitale Visiten
* Login schützt das Dashboard; nach drei gescheiterten Versuchen wird eine IP für eine Stunde gesperrt (admin_login_attempts).
* Das Dashboard zeigt alle Identitäten sowie UUID-Token. Neue Identitäten erstellt man über ?action=identity_create.
* Jedes Identity kann beliebige Felder (identity_fields) enthalten. Der Typ kann single, multi, file oder url sein.
-* Dateien werden pro Identität über das neue Upload-Formular hinzugefügt und landen in www/_files/. Der Upload speichert Dateiname, generierten Zwischennamen und MIME-Type in files.
+* Dateien werden pro Identität über das neue Upload-Formular hinzugefügt und landen in www/_files/. Der Upload speichert Dateiname, generierten Zwischennamen und MIME-Type in files. Unter dem Formular zeigt das Admin-UI eine Liste der bereits hochgeladenen Dateien inkl. einer Löschoption.
* Wenn ein Feld auf Typ file gesetzt ist, zeigt die Auswahl nur Uploads der aktuellen Identität (per files.identity_id). Die ausgewählte Datei-ID wird im Feldwert gespeichert, damit später nur diese Datei angezeigt oder heruntergeladen wird.
* UUID-Token (access_tokens) verbinden eine Identität mit einer eindeutigen Zeichenfolge, können optional Auslaufdaten und Notizen erhalten und dürfen **nur einmal** existieren. Tokenrechte (token_permissions) bestimmen, welche Feldschlüssel Besucher:innen sehen.
diff --git a/docker-compose.yml b/docker-compose.yml
index 1af2243..0b4ed12 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -10,6 +10,7 @@ services:
- ./www:/var/www/html
depends_on:
- db
+ command: ["sh", "-c", "mkdir -p /var/www/html/_files && chown -R www-data:www-data /var/www/html/_files && apache2-foreground"]
db:
image: mariadb:11
diff --git a/www/admin.php b/www/admin.php
index a248594..b7ed9c5 100644
--- a/www/admin.php
+++ b/www/admin.php
@@ -265,10 +265,30 @@ if ($action === 'identity_edit') {
exit('Identität nicht gefunden');
}
- $fileUploadErrors = [];
- $fileUploadSuccess = 0;
+ $uploadErrorLabel = function (int $error): string {
+ return match ($error) {
+ UPLOAD_ERR_INI_SIZE => 'Datei überschreitet upload_max_filesize',
+ UPLOAD_ERR_FORM_SIZE => 'Datei überschreitet MAX_FILE_SIZE',
+ UPLOAD_ERR_PARTIAL => 'Datei wurde nur teilweise hochgeladen',
+ UPLOAD_ERR_NO_FILE => 'keine Datei ausgewählt',
+ UPLOAD_ERR_NO_TMP_DIR => 'temporäres Verzeichnis fehlt',
+ UPLOAD_ERR_CANT_WRITE => 'Datei konnte nicht geschrieben werden',
+ UPLOAD_ERR_EXTENSION => 'Upload durch eine Erweiterung abgebrochen',
+ default => 'unbekannter Upload-Fehler',
+ };
+ };
+
+ $fileUploadErrors = $_SESSION['fileUploadErrors'] ?? [];
+ unset($_SESSION['fileUploadErrors']);
+ $fileUploadSuccess = (int)($_SESSION['fileUploadSuccess'] ?? 0);
+ unset($_SESSION['fileUploadSuccess']);
+ $fileDeleteMessage = $_SESSION['fileDeleteMessage'] ?? '';
+ unset($_SESSION['fileDeleteMessage']);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $fileUploadErrors = [];
+ $fileUploadSuccess = 0;
+ $fileDeleteMessage = '';
if (isset($_POST['upload_files'])) {
$filesInput = $_FILES['files'] ?? null;
@@ -309,7 +329,11 @@ if ($action === 'identity_edit') {
}
if ($error !== UPLOAD_ERR_OK) {
- $fileUploadErrors[] = sprintf('Fehler beim Hochladen von %s.', $originalName);
+ $fileUploadErrors[] = sprintf(
+ 'Fehler beim Hochladen von %s: %s.',
+ $originalName,
+ $uploadErrorLabel($error)
+ );
continue;
}
@@ -353,6 +377,33 @@ if ($action === 'identity_edit') {
}
}
+ if (isset($_POST['delete_file'])) {
+ $fileId = (int)($_POST['file_id'] ?? 0);
+ if ($fileId > 0) {
+ $file = $sql->single(
+ "SELECT stored_name, filename FROM files WHERE id = ? AND identity_id = ?",
+ "ii",
+ [$fileId, $id]
+ );
+ if ($file) {
+ $diskPath = __DIR__ . '/_files/' . $file['stored_name'];
+ if (is_file($diskPath)) {
+ @unlink($diskPath);
+ }
+ $sql->set(
+ "DELETE FROM files WHERE id = ?",
+ "i",
+ [$fileId]
+ );
+ $fileDeleteMessage = sprintf('Datei "%s" gelöscht.', $file['filename']);
+ } else {
+ $fileUploadErrors[] = 'Datei nicht gefunden oder gehört nicht zu dieser Identität.';
+ }
+ } else {
+ $fileUploadErrors[] = 'Ungültige Datei.';
+ }
+ }
+
// Identität umbenennen
if (isset($_POST['rename'])) {
$sql->set(
@@ -404,6 +455,10 @@ if ($action === 'identity_edit') {
);
}
+ $_SESSION['fileUploadErrors'] = $fileUploadErrors;
+ $_SESSION['fileUploadSuccess'] = $fileUploadSuccess;
+ $_SESSION['fileDeleteMessage'] = $fileDeleteMessage;
+
header("Location: admin.php?action=identity_edit&id=$id");
exit;
}
@@ -523,9 +578,19 @@ if ($action === 'identity_edit') {
= htmlspecialchars($fileDeleteMessage) ?>
+