Add per-file captions to admin form

This commit is contained in:
hbrain 2026-05-25 20:45:34 +02:00
parent 89ea870291
commit 251d6b298c

21
new.php
View file

@ -7,20 +7,21 @@ function rrmdir($dir){ if(!is_dir($dir)) return; foreach(scandir($dir) as $f){ i
function input_dirs($base){ $dirs = glob($base.'/*', GLOB_ONLYDIR) ?: []; usort($dirs, fn($a,$b)=>filemtime($b)<=>filemtime($a)); return $dirs; } function input_dirs($base){ $dirs = glob($base.'/*', GLOB_ONLYDIR) ?: []; usort($dirs, fn($a,$b)=>filemtime($b)<=>filemtime($a)); return $dirs; }
function safe_input_dir($base, $name){ $name = basename((string)$name); $path = realpath($base . '/' . $name); $root = realpath($base); if (!$path || !$root || !str_starts_with($path, $root . DIRECTORY_SEPARATOR) || !is_dir($path)) throw new RuntimeException('Invalid input directory.'); return $path; } function safe_input_dir($base, $name){ $name = basename((string)$name); $path = realpath($base . '/' . $name); $root = realpath($base); if (!$path || !$root || !str_starts_with($path, $root . DIRECTORY_SEPARATOR) || !is_dir($path)) throw new RuntimeException('Invalid input directory.'); return $path; }
function read_data($dir){ function read_data($dir){
$data = ['title'=>'','date'=>'','place'=>'','description'=>'']; $file = $dir . '/data.txt'; $data = ['title'=>'','date'=>'','place'=>'','description'=>'','captions'=>'']; $file = $dir . '/data.txt';
if (!is_file($file)) return $data; if (!is_file($file)) return $data;
$header = []; $description = []; $inDescription = false; $header = []; $description = []; $captions = []; $inDescription = false;
foreach (file($file, FILE_IGNORE_NEW_LINES) as $line) { foreach (file($file, FILE_IGNORE_NEW_LINES) as $line) {
$line = trim($line); $line = trim($line);
if ($line === '' && ($header || $inDescription)) { $inDescription = true; continue; } if ($line === '' && ($header || $inDescription)) { $inDescription = true; continue; }
if ($line === '' || str_starts_with($line, '#')) continue; if ($line === '' || str_starts_with($line, '#')) continue;
if (str_contains($line, ':')) continue; // key:value is only for per-file captions if (str_contains($line, ':')) { $captions[] = $line; continue; } // key:value is only for per-file captions
if ($inDescription) $description[] = $line; else $header[] = $line; if ($inDescription) $description[] = $line; else $header[] = $line;
} }
if ($header) $data['title'] = $header[0]; if ($header) $data['title'] = $header[0];
if (count($header) > 1) $data['place'] = $header[1]; if (count($header) > 1) $data['place'] = $header[1];
if (count($header) > 2) $data['date'] = $header[2]; if (count($header) > 2) $data['date'] = $header[2];
if ($description) $data['description'] = implode("\n", $description); if ($description) $data['description'] = implode("\n", $description);
if ($captions) $data['captions'] = implode("\n", $captions);
return $data; return $data;
} }
function write_data($dir, $post){ function write_data($dir, $post){
@ -28,12 +29,20 @@ function write_data($dir, $post){
$place = trim($post['place'] ?? ''); $place = trim($post['place'] ?? '');
$date = trim($post['date'] ?? ''); $date = trim($post['date'] ?? '');
$description = trim($post['description'] ?? ''); $description = trim($post['description'] ?? '');
$captions = trim($post['captions'] ?? '');
if (function_exists('mb_substr')) $description = mb_substr($description, 0, 5000, 'UTF-8'); else $description = substr($description, 0, 5000); if (function_exists('mb_substr')) $description = mb_substr($description, 0, 5000, 'UTF-8'); else $description = substr($description, 0, 5000);
$lines = []; $lines = [];
if ($title !== '') $lines[] = $title; if ($title !== '') $lines[] = $title;
if ($place !== '') $lines[] = $place; if ($place !== '') $lines[] = $place;
if ($place !== '' && $date !== '') $lines[] = $date; if ($place !== '' && $date !== '') $lines[] = $date;
if ($description !== '') { $lines[] = ''; $lines[] = $description; } if ($description !== '') { $lines[] = ''; $lines[] = $description; }
if ($captions !== '') {
if ($description === '') $lines[] = '';
foreach (preg_split('/\R/', $captions) as $captionLine) {
$captionLine = trim($captionLine);
if ($captionLine !== '' && str_contains($captionLine, ':')) $lines[] = $captionLine;
}
}
file_put_contents($dir . '/data.txt', implode("\n", $lines) . "\n"); file_put_contents($dir . '/data.txt', implode("\n", $lines) . "\n");
} }
function list_files($dir){ $files = array_values(array_filter(scandir($dir), fn($f)=>$f!=='.' && $f!=='..' && is_file($dir.'/'.$f) && $f !== 'data.txt')); natcasesort($files); return $files; } function list_files($dir){ $files = array_values(array_filter(scandir($dir), fn($f)=>$f!=='.' && $f!=='..' && is_file($dir.'/'.$f) && $f !== 'data.txt')); natcasesort($files); return $files; }
@ -93,13 +102,13 @@ try {
} catch (Throwable $e) { $err = $e->getMessage(); } } catch (Throwable $e) { $err = $e->getMessage(); }
$dirs = input_dirs($config['uploads_dir']); $dirs = input_dirs($config['uploads_dir']);
$editName = $_GET['edit'] ?? ''; $editName = $_GET['edit'] ?? '';
$editDir = null; $editData = ['title'=>'','date'=>'','place'=>'','description'=>'']; $editFiles = []; $editDir = null; $editData = ['title'=>'','date'=>'','place'=>'','description'=>'','captions'=>'']; $editFiles = [];
if ($editName !== '') { try { $editDir = safe_input_dir($config['uploads_dir'], $editName); $editData = read_data($editDir); $editFiles = list_files($editDir); } catch (Throwable $e) { $err = $e->getMessage(); } } if ($editName !== '') { try { $editDir = safe_input_dir($config['uploads_dir'], $editName); $editData = read_data($editDir); $editFiles = list_files($editDir); } catch (Throwable $e) { $err = $e->getMessage(); } }
?> ?>
<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Admin - <?=h($config['site_name'])?></title><link rel="stylesheet" href="style.css"></head><body> <!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Admin - <?=h($config['site_name'])?></title><link rel="stylesheet" href="style.css"></head><body>
<header><h1>MVLog Admin</h1><nav><a href="index.php">Back</a></nav></header><main> <header><h1>MVLog Admin</h1><nav><a href="index.php">Back</a></nav></header><main>
<?php if($msg): ?><div class="ok"><?=h($msg)?></div><?php endif; ?><?php if($err): ?><div class="err"><?=h($err)?></div><?php endif; ?> <?php if($msg): ?><div class="ok"><?=h($msg)?></div><?php endif; ?><?php if($err): ?><div class="err"><?=h($err)?></div><?php endif; ?>
<section><h2>Existing input dirs</h2><?php if(!$dirs): ?><p>No input directories yet.</p><?php endif; ?><div class="admin-list"><?php foreach($dirs as $d): $data=read_data($d); ?><div class="admin-item"><div><strong><?=h($data['title'] ?: basename($d))?></strong><br><span><?=h(basename($d))?> · <?=h(count(list_files($d)))?> files</span></div><a class="button" href="?edit=<?=rawurlencode(basename($d))?>">Edit</a></div><?php endforeach; ?></div></section> <section><h2>Existing input dirs</h2><?php if(!$dirs): ?><p>No input directories yet.</p><?php endif; ?><div class="admin-list"><?php foreach($dirs as $d): $data=read_data($d); ?><div class="admin-item"><div><strong><?=h($data['title'] ?: basename($d))?></strong><br><span><?=h(basename($d))?> · <?=h(count(list_files($d)))?> files</span></div><a class="button" href="?edit=<?=rawurlencode(basename($d))?>">Edit</a></div><?php endforeach; ?></div></section>
<?php if($editDir): ?><section><h2>Edit input dir</h2><p><code>in-dir/<?=h(basename($editDir))?></code></p><form method="post" enctype="multipart/form-data"><input type="hidden" name="action" value="update"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><label>Title<input name="title" value="<?=h($editData['title'])?>"></label><label>Date<input name="date" value="<?=h($editData['date'])?>"></label><label>Place<input name="place" value="<?=h($editData['place'])?>"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-edit"><?=h($editData['description'])?></textarea><small id="description-counter-edit" class="counter"></small></label><label>Add images / videos<input type="file" name="media[]" multiple accept="image/*,video/*"></label><label>Add audio<input type="file" name="audio[]" multiple accept="audio/*"></label><button type="submit">Save changes</button></form><h3>Files</h3><div class="file-list"><?php foreach($editFiles as $f): ?><form method="post" class="file-row"><input type="hidden" name="action" value="delete_file"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="file" value="<?=h($f)?>"><span><?=h($f)?></span><button type="submit" onclick="return confirm('Remove this file?')">Remove</button></form><?php endforeach; ?></div><form method="post" onsubmit="return confirm('Delete this whole input directory?')"><input type="hidden" name="action" value="delete_dir"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><button type="submit">Delete input dir</button></form></section><?php endif; ?> <?php if($editDir): ?><section><h2>Edit input dir</h2><p><code>in-dir/<?=h(basename($editDir))?></code></p><form method="post" enctype="multipart/form-data"><input type="hidden" name="action" value="update"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><label>Title<input name="title" value="<?=h($editData['title'])?>"></label><label>Date<input name="date" value="<?=h($editData['date'])?>"></label><label>Place<input name="place" value="<?=h($editData['place'])?>"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-edit"><?=h($editData['description'])?></textarea><small id="description-counter-edit" class="counter"></small></label><label>Per-file captions <small>filename: caption</small><textarea name="captions" placeholder="001_photo.jpg: Caption text"><?=h($editData['captions'])?></textarea></label><label>Add images / videos<input type="file" name="media[]" multiple accept="image/*,video/*"></label><label>Add audio<input type="file" name="audio[]" multiple accept="audio/*"></label><button type="submit">Save changes</button></form><h3>Files</h3><div class="file-list"><?php foreach($editFiles as $f): ?><form method="post" class="file-row"><input type="hidden" name="action" value="delete_file"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><input type="hidden" name="file" value="<?=h($f)?>"><span><?=h($f)?></span><button type="submit" onclick="return confirm('Remove this file?')">Remove</button></form><?php endforeach; ?></div><form method="post" onsubmit="return confirm('Delete this whole input directory?')"><input type="hidden" name="action" value="delete_dir"><input type="hidden" name="dir" value="<?=h(basename($editDir))?>"><button type="submit">Delete input dir</button></form></section><?php endif; ?>
<?php if(!$editDir): ?><section><h2>Add new movie input</h2><form method="post" enctype="multipart/form-data"><input type="hidden" name="action" value="create"><label>Title*<input name="title" required></label><label>Date<input name="date" placeholder="2026-05-25"></label><label>Place<input name="place"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-new"></textarea><small id="description-counter-new" class="counter"></small></label><label>Images / videos*<input type="file" name="media[]" multiple required accept="image/*,video/*"></label><label>Audio (optional)<input type="file" name="audio[]" multiple accept="audio/*"></label><button type="submit">Create input-dir</button></form></section><?php endif; ?> <?php if(!$editDir): ?><section><h2>Add new movie input</h2><form method="post" enctype="multipart/form-data"><input type="hidden" name="action" value="create"><label>Title*<input name="title" required></label><label>Date<input name="date" placeholder="2026-05-25"></label><label>Place<input name="place"></label><label>Description<textarea name="description" maxlength="5000" data-counter="description-counter-new"></textarea><small id="description-counter-new" class="counter"></small></label><label>Per-file captions <small>filename: caption</small><textarea name="captions" placeholder="001_photo.jpg: Caption text"></textarea></label><label>Images / videos*<input type="file" name="media[]" multiple required accept="image/*,video/*"></label><label>Audio (optional)<input type="file" name="audio[]" multiple accept="audio/*"></label><button type="submit">Create input-dir</button></form></section><?php endif; ?>
</main><footer>Input dirs are saved under <code>in-dir/</code>.</footer><script>document.querySelectorAll('textarea[data-counter]').forEach(function(t){var c=document.getElementById(t.dataset.counter);function u(){c.textContent=t.value.length+' / '+t.maxLength+' characters';}t.addEventListener('input',u);u();});</script></body></html> </main><footer>Input dirs are saved under <code>in-dir/</code>.</footer><script>document.querySelectorAll('textarea[data-counter]').forEach(function(t){var c=document.getElementById(t.dataset.counter);function u(){c.textContent=t.value.length+' / '+t.maxLength+' characters';}t.addEventListener('input',u);u();});</script></body></html>