admin update, und upload

This commit is contained in:
Troy grunt
2026-02-13 21:59:45 +01:00
parent b9d662b8b9
commit ae21c3c5d8
6 changed files with 358 additions and 18 deletions

View File

@@ -81,6 +81,7 @@ button {
<div class="err"><?= htmlspecialchars($error) ?></div>
<?php endif; ?>
</form>
</body>
</html>
<?php
@@ -221,7 +222,37 @@ exit;
EDIT IDENTITY
───────────────────────────── */
if ($action === 'identity_edit') {
//TODO je nach typ des feldes soll auch das datenfeld getauscht werden, einzeilit input mehrzeilig textarea usw
function renderValueFieldElement(string $type, string $value, string $name = 'value', string $id = null, array $extraAttrs = [], array $filesForIdentity = []): string
{
$safeValue = htmlspecialchars($value);
$idAttr = $id ? ' id="' . htmlspecialchars($id) . '"' : '';
$extraAttrString = '';
foreach ($extraAttrs as $attr => $attrVal) {
$extraAttrString .= ' ' . htmlspecialchars($attr) . '="' . htmlspecialchars($attrVal) . '"';
}
if ($type === 'file') {
$options = '<option value="">-- Datei wählen --</option>';
foreach ($filesForIdentity as $file) {
$fileId = (string)(int)$file['id'];
$selected = $fileId === $value ? ' selected' : '';
$options .= sprintf(
'<option value="%s"%s>%s</option>',
htmlspecialchars($fileId),
$selected,
htmlspecialchars($file['filename'])
);
}
return "<select name=\"" . htmlspecialchars($name) . "\" style=\"width:100%\"{$idAttr}{$extraAttrString}>{$options}</select>";
}
if ($type === 'multi') {
return "<textarea name=\"" . htmlspecialchars($name) . "\" rows=\"3\" style=\"width:100%\"{$idAttr}{$extraAttrString}>{$safeValue}</textarea>";
}
$inputType = $type === 'url' ? 'url' : 'text';
return "<input type=\"{$inputType}\" name=\"" . htmlspecialchars($name) . "\" value=\"{$safeValue}\" style=\"width:100%\"{$idAttr}{$extraAttrString}>";
}
$id = (int)($_GET['id'] ?? 0);
$identity = $sql->single(
@@ -234,8 +265,94 @@ if ($action === 'identity_edit') {
exit('Identität nicht gefunden');
}
$fileUploadErrors = [];
$fileUploadSuccess = 0;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['upload_files'])) {
$filesInput = $_FILES['files'] ?? null;
$hasSelection = false;
if ($filesInput) {
if (is_array($filesInput['name'])) {
foreach ($filesInput['name'] as $fileName) {
if (trim((string)$fileName) !== '') {
$hasSelection = true;
break;
}
}
} else {
$hasSelection = trim((string)$filesInput['name']) !== '';
}
}
if ($filesInput && $hasSelection) {
$uploadDir = __DIR__ . '/../files/';
if (!is_dir($uploadDir) && !mkdir($uploadDir, 0755, true) && !is_dir($uploadDir)) {
$fileUploadErrors[] = 'Upload-Verzeichnis kann nicht erstellt werden.';
} else {
$total = is_array($filesInput['name']) ? count($filesInput['name']) : 1;
$uploaded = 0;
$finfo = finfo_open(FILEINFO_MIME_TYPE);
for ($i = 0; $i < $total; $i++) {
$originalName = is_array($filesInput['name']) ? $filesInput['name'][$i] : $filesInput['name'];
$error = is_array($filesInput['error']) ? $filesInput['error'][$i] : $filesInput['error'];
$tmpName = is_array($filesInput['tmp_name']) ? $filesInput['tmp_name'][$i] : $filesInput['tmp_name'];
if ($error === UPLOAD_ERR_NO_FILE) {
continue;
}
$originalName = trim((string)$originalName);
if ($originalName === '') {
continue;
}
if ($error !== UPLOAD_ERR_OK) {
$fileUploadErrors[] = sprintf('Fehler beim Hochladen von %s.', $originalName);
continue;
}
if (!is_uploaded_file($tmpName)) {
$fileUploadErrors[] = sprintf('Datei %s konnte nicht verarbeitet werden.', $originalName);
continue;
}
$safeName = preg_replace('/[^A-Za-z0-9._-]/', '_', basename($originalName));
if ($safeName === '') {
$safeName = 'file';
}
$storedName = bin2hex(random_bytes(12)) . '_' . $safeName;
$destination = $uploadDir . $storedName;
if (!move_uploaded_file($tmpName, $destination)) {
$fileUploadErrors[] = sprintf('Speichern von %s fehlgeschlagen.', $originalName);
continue;
}
$mimeType = $finfo ? finfo_file($finfo, $destination) : 'application/octet-stream';
$sql->set(
"INSERT INTO files (identity_id, filename, stored_name, mime_type)
VALUES (?, ?, ?, ?)",
"isss",
[$id, $originalName, $storedName, $mimeType]
);
$uploaded++;
}
if ($finfo) {
finfo_close($finfo);
}
if ($uploaded > 0) {
$fileUploadSuccess = $uploaded;
}
}
} else {
$fileUploadErrors[] = 'Bitte wählen Sie mindestens eine Datei aus.';
}
}
// Identität umbenennen
if (isset($_POST['rename'])) {
$sql->set(
@@ -296,6 +413,15 @@ if ($action === 'identity_edit') {
"i",
[$id]
);
$identityFiles = $sql->get(
"SELECT id, filename FROM files WHERE identity_id = ? ORDER BY uploaded_at DESC",
"i",
[$id]
);
if ($identityFiles === false) {
$identityFiles = [];
}
?>
<!doctype html>
<html>
@@ -334,17 +460,13 @@ if ($action === 'identity_edit') {
<input name="key"
value="<?= htmlspecialchars($f['field_key']) ?>">
</td>
<td>
<?php if ($f['typ'] === 'multi'): ?>
<textarea name="value" rows="3" style="width:100%"><?= htmlspecialchars($f['field_value']) ?></textarea>
<?php else: ?>
<input name="value"
value="<?= htmlspecialchars($f['field_value']) ?>"
style="width:100%">
<?php endif; ?>
<td class="value-cell">
<div data-value-wrapper class="value-field-wrapper">
<?= renderValueFieldElement($f['typ'], $f['field_value'], 'value', null, [], $identityFiles) ?>
</div>
</td>
<td>
<select name="typ">
<select name="typ" data-typ-switch>
<option value="single" <?= $f['typ']==='single'?'selected':'' ?>>einzeilig</option>
<option value="multi" <?= $f['typ']==='multi'?'selected':'' ?>>mehrzeilig</option>
<option value="file" <?= $f['typ']==='file'?'selected':'' ?>>Datei</option>
@@ -367,8 +489,10 @@ if ($action === 'identity_edit') {
<h3>Neues Feld</h3>
<form method="post">
<input name="key" placeholder="Feldname" required>
<input name="value" placeholder="Wert">
<select name="typ">
<div data-value-wrapper class="value-field-wrapper" data-placeholder="Wert">
<?= renderValueFieldElement('single', '', 'value', 'new-field-value', ['placeholder' => 'Wert'], $identityFiles) ?>
</div>
<select name="typ" data-typ-switch>
<option value="single">einzeilig</option>
<option value="multi">mehrzeilig</option>
<option value="file">Datei</option>
@@ -377,6 +501,125 @@ if ($action === 'identity_edit') {
<button name="add_field"> Feld hinzufügen</button>
</form>
<h3>Dateien hochladen</h3>
<form method="post" enctype="multipart/form-data">
<input type="file" name="files[]" multiple>
<button name="upload_files">Hochladen</button>
</form>
<?php if ($fileUploadSuccess > 0): ?>
<p style="color:#22c55e; margin-top:.5rem;">
<?= $fileUploadSuccess ?> Datei<?= $fileUploadSuccess === 1 ? '' : 'en' ?> hochgeladen.
</p>
<?php endif; ?>
<?php if (!empty($fileUploadErrors)): ?>
<ul style="color:#f87171; margin-top:.5rem;">
<?php foreach ($fileUploadErrors as $error): ?>
<li><?= htmlspecialchars($error) ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if (!empty($identityFiles)): ?>
<h3>Vorhandene Dateien</h3>
<ul>
<?php foreach ($identityFiles as $file): ?>
<li><?= htmlspecialchars($file['filename']) ?> (ID <?= (int)$file['id'] ?>)</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<script>
document.addEventListener('DOMContentLoaded', function () {
const identityFiles = <?= json_encode(array_map(function ($file) {
return [
'id' => (int)$file['id'],
'filename' => $file['filename'],
];
}, $identityFiles), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG) ?: '[]' ?>;
function createValueFieldElement(type, opts) {
opts = opts || {};
var element;
if (type === 'multi') {
element = document.createElement('textarea');
element.rows = 3;
} else if (type === 'file') {
element = document.createElement('select');
} else {
element = document.createElement('input');
element.type = type === 'url' ? 'url' : 'text';
}
element.name = opts.name || 'value';
element.style.width = '100%';
if (opts.id) {
element.id = opts.id;
}
if (type === 'file') {
var placeholder = opts.placeholder || '-- Datei wählen --';
var defaultOption = document.createElement('option');
defaultOption.value = '';
defaultOption.textContent = placeholder;
element.appendChild(defaultOption);
identityFiles.forEach(function (file) {
var option = document.createElement('option');
option.value = file.id;
option.textContent = file.filename;
if (opts.value && String(opts.value) === String(file.id)) {
option.selected = true;
}
element.appendChild(option);
});
return element;
}
if (opts.placeholder) {
element.setAttribute('placeholder', opts.placeholder);
}
element.value = opts.value || '';
return element;
}
function updateValueField(select) {
var form = select.closest('form');
if (!form) {
return;
}
var wrapper = form.querySelector('[data-value-wrapper]');
if (!wrapper) {
return;
}
var existing = wrapper.querySelector('[name="value"]');
var opts = {
name: existing ? existing.name : 'value',
id: existing ? existing.id : '',
placeholder: existing ? existing.getAttribute('placeholder') : wrapper.getAttribute('data-placeholder') || '',
value: existing ? existing.value : ''
};
if (select.value === 'file') {
delete opts.value;
}
wrapper.textContent = '';
wrapper.appendChild(createValueFieldElement(select.value, opts));
}
var switchers = document.querySelectorAll('select[data-typ-switch]');
for (var i = 0; i < switchers.length; i++) {
switchers[i].addEventListener('change', function () {
updateValueField(this);
});
}
});
</script>
<p><a href="admin.php">← zurück</a></p>
</body>
@@ -409,7 +652,6 @@ if ($action === 'uuid_create') {
EDIT UUID
───────────────────────────── */
if ($action === 'uuid_edit') {
//TODO es fehlt die auswahl welche felder sichtbar sein sollen.
$uuid = $_GET['uuid'] ?? '';
$token = $sql->single(

View File

@@ -170,6 +170,30 @@ function label(string $key): string {
text-align: center;
font-size: .75rem;
color: var(--muted);
display: flex;
flex-direction: column;
gap: .25rem;
align-items: center;
}
.footer-meta {
display: flex;
gap: .65rem;
font-size: .7rem;
flex-wrap: wrap;
justify-content: center;
color: var(--muted);
}
.footer-meta span {
padding: .15rem .4rem;
border-radius: 999px;
background: rgba(148, 163, 184, .18);
}
.footer-note {
font-size: .65rem;
color: rgba(229, 231, 235, .7);
}
</style>
</head>
@@ -203,10 +227,22 @@ function label(string $key): string {
<?php endif; ?>
<footer>
Zugriff über sicheren Link
<?php
//TODO ordentlichen footer
$expiresAt = $token['expires_at'] ?? null;
$note = trim($token['notes'] ?? '');
$expiresLabel = $expiresAt ? date('d.m.Y H:i', strtotime($expiresAt)) : null;
?>
<div class="footer-meta">
<span>
<?= $expiresLabel ? 'gültig bis ' . htmlspecialchars($expiresLabel) : 'unbegrenzt gültig' ?>
</span>
<?php if ($note !== ''): ?>
<span><?= htmlspecialchars($note) ?></span>
<?php endif; ?>
</div>
<div class="footer-note">
<?= 'UUID • ' . htmlspecialchars(substr($token['uuid'], 0, 8)) . '…' ?>
</div>
</footer>
</div>
</body>

1
www/files/.gitkeep Normal file
View File

@@ -0,0 +1 @@
This directory stores uploaded files outside the document root.