'jpg', 'image/png' => 'png', 'image/webp' => 'webp']; function slugify(string $name): string { $slug = strtolower(str_replace(["'", '’'], '', $name)); return trim(preg_replace('/[^a-z0-9]+/', '-', $slug), '-'); } function deleteBackground(?string $filename): void { if ($filename && is_file(BACKGROUND_DIR . basename($filename))) { unlink(BACKGROUND_DIR . basename($filename)); } } $id = (int)($_GET['id'] ?? 0); $show = ['id' => 0, 'name' => '', 'slug' => '', 'background_image' => null]; if ($id) { $stmt = $db->prepare('SELECT id, name, slug, background_image FROM shows WHERE id = ?'); $stmt->execute([$id]); $show = $stmt->fetch(); if (!$show) { http_response_code(404); exit('Show not found.'); } } $errors = []; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // PHP discards the whole form when a request exceeds post_max_size, so catch that before the CSRF check if (empty($_POST) && (int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 0) { $errors[] = 'That upload is too large. Background images can be up to ' . (MAX_BACKGROUND_BYTES / 1024 / 1024) . ' MB.'; } else { verifyCsrf(); if (($_POST['action'] ?? '') === 'delete' && $id) { $db->prepare('DELETE FROM shows WHERE id = ?')->execute([$id]); deleteBackground($show['background_image']); flash("Deleted {$show['name']}."); header('Location: shows.php'); exit; } $show['name'] = trim((string)($_POST['name'] ?? '')); $show['slug'] = trim((string)($_POST['slug'] ?? '')) ?: slugify($show['name']); if ($show['name'] === '' || mb_strlen($show['name']) > 255) { $errors[] = 'Name is required (up to 255 characters).'; } if (!preg_match('/^[a-z0-9]+(-[a-z0-9]+)*$/', $show['slug']) || strlen($show['slug']) > 64) { $errors[] = 'Slug may only contain lowercase letters, numbers and single dashes (up to 64 characters).'; } else { $stmt = $db->prepare('SELECT COUNT(*) FROM shows WHERE slug = ? AND id != ?'); $stmt->execute([$show['slug'], $id]); if ($stmt->fetchColumn() > 0) $errors[] = "Another show already uses the slug '{$show['slug']}'."; } $upload = $_FILES['background'] ?? null; $uploadExt = null; if ($upload && $upload['error'] !== UPLOAD_ERR_NO_FILE) { if (in_array($upload['error'], [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE], true) || $upload['size'] > MAX_BACKGROUND_BYTES) { $errors[] = 'Background images can be up to ' . (MAX_BACKGROUND_BYTES / 1024 / 1024) . ' MB.'; } elseif ($upload['error'] !== UPLOAD_ERR_OK) { $errors[] = 'The background image failed to upload. Please try again.'; } else { $mime = (new finfo(FILEINFO_MIME_TYPE))->file($upload['tmp_name']); $uploadExt = BACKGROUND_TYPES[$mime] ?? null; if ($uploadExt === null || getimagesize($upload['tmp_name']) === false) { $errors[] = 'Background images must be JPEG, PNG or WebP.'; } } } if (!$errors) { $oldBackground = $show['background_image']; if ($uploadExt !== null) { $filename = $show['slug'] . '-' . bin2hex(random_bytes(4)) . '.' . $uploadExt; if (move_uploaded_file($upload['tmp_name'], BACKGROUND_DIR . $filename)) { $show['background_image'] = $filename; } else { $errors[] = 'The background image could not be saved. Check that the web server can write to images/backgrounds/.'; } } elseif (!empty($_POST['remove_background'])) { $show['background_image'] = null; } } if (!$errors) { if ($id) { $db->prepare('UPDATE shows SET name = ?, slug = ?, background_image = ? WHERE id = ?') ->execute([$show['name'], $show['slug'], $show['background_image'], $id]); } else { $db->prepare('INSERT INTO shows (name, slug, background_image) VALUES (?, ?, ?)') ->execute([$show['name'], $show['slug'], $show['background_image']]); } if ($oldBackground !== $show['background_image']) deleteBackground($oldBackground); flash("Saved {$show['name']}."); header('Location: shows.php'); exit; } } } adminHeader($id ? 'Edit Show' : 'Add a Show', $admin); ?>