$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'; $bt = $step['bead_type'] ?? null; 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)); if ($bt !== null && !in_array($bt, $valid_bead_types)) json_err("Invalid bead type 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 { $bt = in_array($step['bead_type'] ?? null, ['small','large','crucifix']) ? $step['bead_type'] : null; $step_stmt->execute([$session_id, 'prayer', $bt, $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()); }