3cc002a6da
- Bead Markers section in library panel with Small, Large, Crucifix cards - Bead steps render as amber-tinted cards in the sequence (no attribution) - migrate_v5.php: adds step_type/bead_type columns, makes prayer_id nullable - Bead steps produce slides with proper bead+bead_index so the ring advances during presentation — allows participants to follow along on physical beads - Prayer steps between beads still show bead_index=null (ring stays on last bead) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
157 lines
5.8 KiB
PHP
157 lines
5.8 KiB
PHP
<?php
|
|
/**
|
|
* api/builder_session.php — Save or update a custom builder session.
|
|
* Requires superuser+.
|
|
*
|
|
* POST — create or update session + steps
|
|
* Body JSON: {
|
|
* id? : int (existing session ID for update)
|
|
* name : string
|
|
* is_public : bool
|
|
* subject_name? : string
|
|
* subject_pronoun? : 'he'|'she'
|
|
* subject_dates? : string
|
|
* steps : [{prayer_id, attribution}, ...]
|
|
* }
|
|
*/
|
|
require_once __DIR__ . '/../config/db.php';
|
|
require_once __DIR__ . '/../includes/auth.php';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
function json_err(string $msg, int $code = 400): never {
|
|
http_response_code($code);
|
|
echo json_encode(['error' => $msg]);
|
|
exit;
|
|
}
|
|
|
|
require_auth();
|
|
if (!has_role('superuser')) json_err('Access denied', 403);
|
|
|
|
$pdo = get_pdo();
|
|
$user = current_user();
|
|
$uid = (int)$user['id'];
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') json_err('Method not allowed', 405);
|
|
|
|
$body = json_decode(file_get_contents('php://input'), true);
|
|
if (!$body) json_err('Invalid JSON');
|
|
|
|
$name = trim($body['name'] ?? '');
|
|
$is_public = (int)!empty($body['is_public']);
|
|
$subject_name = trim($body['subject_name'] ?? '');
|
|
$subject_pronoun = in_array($body['subject_pronoun'] ?? '', ['he','she']) ? $body['subject_pronoun'] : 'he';
|
|
$subject_dates = trim($body['subject_dates'] ?? '');
|
|
$steps = $body['steps'] ?? [];
|
|
$session_id = (int)($body['id'] ?? 0);
|
|
|
|
if ($name === '') json_err('Session name is required');
|
|
if (empty($steps)) json_err('Add at least one prayer to your sequence');
|
|
|
|
$valid_attributions = ['leader_all', 'leader_only', 'all_only', 'none'];
|
|
$valid_bead_types = ['small', 'large', 'crucifix'];
|
|
|
|
// Validate steps and collect prayer IDs to verify
|
|
$prayer_ids = [];
|
|
foreach ($steps as $i => $step) {
|
|
$type = $step['step_type'] ?? 'prayer';
|
|
if ($type === 'bead') {
|
|
if (!in_array($step['bead_type'] ?? '', $valid_bead_types)) {
|
|
json_err("Invalid bead type on step " . ($i + 1));
|
|
}
|
|
} else {
|
|
$pid = (int)($step['prayer_id'] ?? 0);
|
|
$att = $step['attribution'] ?? 'leader_all';
|
|
if (!$pid) json_err("Step " . ($i + 1) . " is missing a prayer");
|
|
if (!in_array($att, $valid_attributions)) json_err("Invalid attribution on step " . ($i + 1));
|
|
$prayer_ids[] = $pid;
|
|
}
|
|
}
|
|
|
|
// Verify all prayer_ids exist and are accessible
|
|
if (!empty($prayer_ids)) {
|
|
$prayer_ids = array_unique($prayer_ids);
|
|
$in_placeholders = implode(',', array_fill(0, count($prayer_ids), '?'));
|
|
$valid_stmt = $pdo->prepare("
|
|
SELECT id FROM custom_prayers
|
|
WHERE id IN ($in_placeholders)
|
|
AND (is_global = 1 OR created_by = ?)
|
|
");
|
|
$valid_stmt->execute([...$prayer_ids, $uid]);
|
|
$valid_ids = array_column($valid_stmt->fetchAll(), 'id');
|
|
foreach ($prayer_ids as $pid) {
|
|
if (!in_array((string)$pid, array_map('strval', $valid_ids))) {
|
|
json_err("Prayer ID {$pid} not found or not accessible");
|
|
}
|
|
}
|
|
}
|
|
|
|
$pdo->beginTransaction();
|
|
try {
|
|
if ($session_id > 0) {
|
|
// Update existing session — verify ownership
|
|
$owner_stmt = $pdo->prepare("SELECT user_id FROM sessions WHERE id = ? AND occasion = 'custom'");
|
|
$owner_stmt->execute([$session_id]);
|
|
$existing = $owner_stmt->fetch();
|
|
if (!$existing) json_err('Session not found', 404);
|
|
if (!has_role('admin') && (int)$existing['user_id'] !== $uid) json_err('Access denied', 403);
|
|
|
|
$slug = unique_slug($name, $uid, 'sessions', $session_id);
|
|
$pdo->prepare("
|
|
UPDATE sessions
|
|
SET name=?, is_public=?, subject_name=?, subject_pronoun=?, subject_dates=?,
|
|
slug=?, updated_at=NOW()
|
|
WHERE id=?
|
|
")->execute([$name, $is_public, $subject_name ?: null, $subject_pronoun, $subject_dates ?: null, $slug, $session_id]);
|
|
|
|
// Replace all steps
|
|
$pdo->prepare("DELETE FROM builder_steps WHERE session_id = ?")->execute([$session_id]);
|
|
} else {
|
|
// Check rosary limit
|
|
if (!can_create_rosary($uid, (int)$user['rosary_limit'])) {
|
|
json_err('You have reached your rosary limit. Contact an administrator.');
|
|
}
|
|
$slug = unique_slug($name, $uid);
|
|
$pdo->prepare("
|
|
INSERT INTO sessions
|
|
(user_id, is_public, slug, name, occasion, mystery_set,
|
|
subject_name, subject_pronoun, subject_dates)
|
|
VALUES (?, ?, ?, ?, 'custom', 'custom', ?, ?, ?)
|
|
")->execute([$uid, $is_public, $slug, $name, $subject_name ?: null, $subject_pronoun, $subject_dates ?: null]);
|
|
$session_id = (int)$pdo->lastInsertId();
|
|
}
|
|
|
|
// Insert steps
|
|
$step_stmt = $pdo->prepare(
|
|
"INSERT INTO builder_steps (session_id, step_type, bead_type, step_order, prayer_id, attribution)
|
|
VALUES (?, ?, ?, ?, ?, ?)"
|
|
);
|
|
foreach ($steps as $order => $step) {
|
|
$type = $step['step_type'] ?? 'prayer';
|
|
if ($type === 'bead') {
|
|
$step_stmt->execute([$session_id, 'bead', $step['bead_type'], $order, null, 'none']);
|
|
} else {
|
|
$step_stmt->execute([$session_id, 'prayer', null, $order, (int)$step['prayer_id'], $step['attribution']]);
|
|
}
|
|
}
|
|
|
|
$pdo->commit();
|
|
|
|
// Return session URL
|
|
$user_row = $pdo->prepare("SELECT username FROM users WHERE id = ?");
|
|
$user_row->execute([$uid]);
|
|
$username = $user_row->fetchColumn();
|
|
$present_url = BASE_URL . '/' . rawurlencode($username) . '/' . rawurlencode($slug);
|
|
|
|
echo json_encode([
|
|
'saved' => true,
|
|
'session_id' => $session_id,
|
|
'slug' => $slug,
|
|
'present_url' => $present_url,
|
|
'edit_url' => BASE_URL . '/admin/builder.php?id=' . $session_id,
|
|
]);
|
|
} catch (Throwable $e) {
|
|
$pdo->rollBack();
|
|
json_err('Save failed: ' . $e->getMessage());
|
|
}
|