galleries.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. <?php
  2. /**
  3. * Gallery overview: create new galleries, list and delete existing ones.
  4. */
  5. require dirname(__DIR__) . '/app/bootstrap.php';
  6. auth_require();
  7. if ($_SERVER['REQUEST_METHOD'] === 'POST') {
  8. csrf_verify();
  9. $action = $_POST['action'] ?? '';
  10. if ($action === 'create') {
  11. $title = trim((string)($_POST['title'] ?? ''));
  12. if ($title === '') {
  13. flash_set('Please enter a gallery title.', 'error');
  14. redirect('galleries.php');
  15. }
  16. $slug = slugify($title) . '-' . random_token(6);
  17. $password = (string)($_POST['password'] ?? '');
  18. $gallery = [
  19. 'slug' => $slug,
  20. 'title' => $title,
  21. 'created_at' => date('Y-m-d H:i:s'),
  22. 'password_hash' => $password !== '' ? password_hash($password, PASSWORD_DEFAULT) : null,
  23. 'expires_at' => trim((string)($_POST['expires_at'] ?? '')) ?: null,
  24. 'images' => [],
  25. ];
  26. // A guest upload link is just a per-gallery secret in the URL; presence
  27. // of upload_key = guest uploads enabled (revocable from the edit page).
  28. if (!empty($_POST['allow_uploads'])) {
  29. $gallery['upload_key'] = random_token(24);
  30. }
  31. gallery_save($gallery);
  32. flash_set('Gallery created. Now add images.');
  33. redirect('gallery-edit.php?g=' . rawurlencode($slug));
  34. }
  35. if ($action === 'delete') {
  36. $gallery = gallery_load((string)($_POST['slug'] ?? ''));
  37. if ($gallery !== null) {
  38. s3_delete_gallery_objects($gallery);
  39. gallery_delete($gallery['slug']);
  40. flash_set('Gallery and its S3 images deleted.');
  41. }
  42. redirect('galleries.php');
  43. }
  44. }
  45. $galleries = galleries_all();
  46. admin_header('Galleries', 'galleries');
  47. flash_render();
  48. ?>
  49. <h1>Galleries</h1>
  50. <form method="post" class="card">
  51. <?= csrf_field() ?>
  52. <input type="hidden" name="action" value="create">
  53. <h2 style="margin-top:0">New gallery</h2>
  54. <label for="t">Title</label>
  55. <input type="text" id="t" name="title" placeholder="Wedding Miller — June 2026" required>
  56. <label for="p">Password <span style="text-transform:none;letter-spacing:0">(optional — leave blank for a public link)</span></label>
  57. <input type="text" id="p" name="password" autocomplete="off">
  58. <label for="ex">Expiry date <span style="text-transform:none;letter-spacing:0">(optional — gallery is hidden after this day)</span></label>
  59. <input type="date" id="ex" name="expires_at">
  60. <p class="help"><label style="display:inline;text-transform:none;letter-spacing:0">
  61. <input type="checkbox" name="allow_uploads" value="1"> Allow guest uploads via a shared link
  62. </label></p>
  63. <button type="submit">Create gallery</button>
  64. </form>
  65. <h2>Existing galleries (<?= count($galleries) ?>)</h2>
  66. <?php if (!$galleries): ?>
  67. <p class="help">No galleries yet.</p>
  68. <?php else: ?>
  69. <div class="card"><table>
  70. <tr><th>Title</th><th>Images</th><th>Protection</th><th>Expires</th><th>Created</th><th style="width:200px">Actions</th></tr>
  71. <?php foreach ($galleries as $g): $expired = gallery_is_expired($g); ?>
  72. <tr>
  73. <td><?= e($g['title']) ?></td>
  74. <td><?= count($g['images'] ?? []) ?></td>
  75. <td>
  76. <?= !empty($g['password_hash']) ? '<span class="tag tag-lock">password</span>' : '<span class="tag">open</span>' ?>
  77. <?= !empty($g['upload_key']) ? ' <span class="tag">uploads</span>' : '' ?>
  78. </td>
  79. <td>
  80. <?= e($g['expires_at'] ?? '—') ?>
  81. <?= $expired ? ' <span class="tag tag-expired">expired</span>' : '' ?>
  82. </td>
  83. <td><?= e(substr($g['created_at'] ?? '', 0, 10)) ?></td>
  84. <td>
  85. <a href="gallery-edit.php?g=<?= e(rawurlencode($g['slug'])) ?>">Edit</a> ·
  86. <a href="../gallery.php?g=<?= e(rawurlencode($g['slug'])) ?>" target="_blank" rel="noopener">View ↗</a>
  87. <form method="post" style="display:inline"
  88. onsubmit="return confirm('Delete this gallery AND all its images on S3? This cannot be undone.')">
  89. <?= csrf_field() ?>
  90. <input type="hidden" name="action" value="delete">
  91. <input type="hidden" name="slug" value="<?= e($g['slug']) ?>">
  92. <button class="btn-danger" style="margin:0;padding:.25rem .7rem">Delete</button>
  93. </form>
  94. </td>
  95. </tr>
  96. <?php endforeach; ?>
  97. </table></div>
  98. <?php endif; ?>
  99. <?php admin_footer(); ?>