202 lines
8.3 KiB
PHP
202 lines
8.3 KiB
PHP
<?php
|
|
|
|
$tokenRec = require_valid_token_web();
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
|
switch ($method) {
|
|
|
|
case 'GET':
|
|
$qID = $_GET['questionID'] ?? '';
|
|
if (!$qID) {
|
|
json_error('MISSING_PARAM', 'questionID query param required', 400);
|
|
}
|
|
try {
|
|
$opened = qdb_open_read_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
$stmt = $pdo->prepare('SELECT answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId FROM answer_option WHERE questionID = :qid ORDER BY orderIndex');
|
|
$stmt->execute([':qid' => $qID]);
|
|
$options = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
foreach ($options as &$o) {
|
|
$o['points'] = (int)$o['points'];
|
|
$o['orderIndex'] = (int)$o['orderIndex'];
|
|
$o['optionKey'] = qdb_option_key($o);
|
|
$o['labelGerman'] = qdb_option_german_label($pdo, $o['answerOptionID'], $o['defaultText']);
|
|
$tr = $pdo->prepare('SELECT languageCode, text FROM answer_option_translation WHERE answerOptionID = :id');
|
|
$tr->execute([':id' => $o['answerOptionID']]);
|
|
$o['translations'] = $tr->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
unset($o);
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_success(['answerOptions' => $options]);
|
|
} catch (Throwable $e) {
|
|
if (isset($tmpDb, $lockFp)) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
}
|
|
if (function_exists('qdb_api_log_note_exception')) {
|
|
qdb_api_log_note_exception($e);
|
|
}
|
|
require_once __DIR__ . '/../lib/errors.php';
|
|
error_log('answer_options GET: ' . $e->getMessage());
|
|
json_error('SERVER_ERROR', qdb_public_error_message($e, 'Load answer options'), 500);
|
|
}
|
|
break;
|
|
|
|
case 'POST':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
if (empty($body['questionID']) || empty($body['optionKey']) || !isset($body['defaultText'])) {
|
|
json_error('MISSING_FIELDS', 'questionID, optionKey, and defaultText (German label) required', 400);
|
|
}
|
|
$id = bin2hex(random_bytes(16));
|
|
$qID = $body['questionID'];
|
|
$optKey = qdb_validate_stable_key((string)$body['optionKey'], 'Option key');
|
|
$text = trim($body['defaultText']);
|
|
qdb_require_non_empty_german($text, 'German label');
|
|
$points = (int)($body['points'] ?? 0);
|
|
$order = (int)($body['orderIndex'] ?? 0);
|
|
$nextQ = trim($body['nextQuestionId'] ?? '');
|
|
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
|
|
try {
|
|
$chk = $pdo->prepare('SELECT 1 FROM question WHERE questionID = :id');
|
|
$chk->execute([':id' => $qID]);
|
|
if (!$chk->fetch()) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('NOT_FOUND', 'Question not found', 404);
|
|
}
|
|
if ($order === 0) {
|
|
$max = $pdo->prepare('SELECT COALESCE(MAX(orderIndex),0)+1 FROM answer_option WHERE questionID = :id');
|
|
$max->execute([':id' => $qID]);
|
|
$order = (int)$max->fetchColumn();
|
|
}
|
|
qdb_assert_unique_option_key($pdo, $qID, $optKey);
|
|
$pdo->prepare('INSERT INTO answer_option (answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId) VALUES (:id, :qid, :t, :p, :o, :nq)')
|
|
->execute([':id' => $id, ':qid' => $qID, ':t' => $optKey, ':p' => $points, ':o' => $order, ':nq' => $nextQ]);
|
|
qdb_upsert_source_translation($pdo, 'answer_option', $id, $text);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success(['answerOption' => [
|
|
'answerOptionID' => $id,
|
|
'questionID' => $qID,
|
|
'optionKey' => $optKey,
|
|
'defaultText' => $optKey,
|
|
'labelGerman' => $text,
|
|
'points' => $points,
|
|
'orderIndex' => $order,
|
|
'nextQuestionId' => $nextQ,
|
|
'translations' => [],
|
|
]]);
|
|
} catch (Throwable $e) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
error_log($e->getMessage());
|
|
json_error('SERVER_ERROR', 'Server error', 500);
|
|
}
|
|
break;
|
|
|
|
case 'PUT':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
if (empty($body['answerOptionID'])) {
|
|
json_error('MISSING_FIELDS', 'answerOptionID is required', 400);
|
|
}
|
|
$id = $body['answerOptionID'];
|
|
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
|
|
try {
|
|
$existing = $pdo->prepare('SELECT * FROM answer_option WHERE answerOptionID = :id');
|
|
$existing->execute([':id' => $id]);
|
|
$row = $existing->fetch(PDO::FETCH_ASSOC);
|
|
if (!$row) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('NOT_FOUND', 'Answer option not found', 404);
|
|
}
|
|
$optKey = isset($body['optionKey'])
|
|
? qdb_validate_stable_key((string)$body['optionKey'], 'Option key')
|
|
: qdb_option_key($row);
|
|
if ($optKey === '') {
|
|
json_error('MISSING_FIELDS', 'optionKey is required', 400);
|
|
}
|
|
$labelGerman = trim($body['defaultText'] ?? $body['labelGerman'] ?? '');
|
|
if ($labelGerman === '') {
|
|
$labelGerman = qdb_option_german_label($pdo, $id, $row['defaultText']);
|
|
}
|
|
qdb_require_non_empty_german($labelGerman, 'German label');
|
|
qdb_assert_unique_option_key($pdo, $row['questionID'], $optKey, $id);
|
|
$points = (int)($body['points'] ?? $row['points']);
|
|
$order = (int)($body['orderIndex'] ?? $row['orderIndex']);
|
|
$nextQ = trim($body['nextQuestionId'] ?? $row['nextQuestionId']);
|
|
|
|
$pdo->prepare('UPDATE answer_option SET defaultText = :t, points = :p, orderIndex = :o, nextQuestionId = :nq WHERE answerOptionID = :id')
|
|
->execute([':t' => $optKey, ':p' => $points, ':o' => $order, ':nq' => $nextQ, ':id' => $id]);
|
|
qdb_upsert_source_translation($pdo, 'answer_option', $id, $labelGerman);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success(['answerOption' => [
|
|
'answerOptionID' => $id,
|
|
'questionID' => $row['questionID'],
|
|
'optionKey' => $optKey,
|
|
'defaultText' => $optKey,
|
|
'labelGerman' => $labelGerman,
|
|
'points' => $points,
|
|
'orderIndex' => $order,
|
|
'nextQuestionId' => $nextQ,
|
|
]]);
|
|
} catch (Throwable $e) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
error_log($e->getMessage());
|
|
json_error('SERVER_ERROR', 'Server error', 500);
|
|
}
|
|
break;
|
|
|
|
case 'DELETE':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
if (empty($body['answerOptionID'])) {
|
|
json_error('MISSING_FIELDS', 'answerOptionID is required', 400);
|
|
}
|
|
$id = $body['answerOptionID'];
|
|
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
|
|
try {
|
|
$pdo->exec('PRAGMA foreign_keys = OFF;');
|
|
$pdo->beginTransaction();
|
|
$pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id')->execute([':id' => $id]);
|
|
$pdo->prepare('UPDATE client_answer SET answerOptionID = NULL WHERE answerOptionID = :id')->execute([':id' => $id]);
|
|
$pdo->prepare('DELETE FROM answer_option WHERE answerOptionID = :id')->execute([':id' => $id]);
|
|
$pdo->commit();
|
|
$pdo->exec('PRAGMA foreign_keys = ON;');
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success(['deleted' => true]);
|
|
} catch (Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
qdb_discard($tmpDb, $lockFp);
|
|
error_log($e->getMessage());
|
|
json_error('SERVER_ERROR', 'Server error', 500);
|
|
}
|
|
break;
|
|
|
|
case 'PATCH':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
if (empty($body['questionID']) || !is_array($body['order'] ?? null)) {
|
|
json_error('MISSING_FIELDS', 'questionID and order[] required', 400);
|
|
}
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
|
|
try {
|
|
$stmt = $pdo->prepare('UPDATE answer_option SET orderIndex = :o WHERE answerOptionID = :id AND questionID = :qid');
|
|
foreach ($body['order'] as $idx => $aoid) {
|
|
$stmt->execute([':o' => $idx, ':id' => $aoid, ':qid' => $body['questionID']]);
|
|
}
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success(['reordered' => true]);
|
|
} catch (Throwable $e) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
error_log($e->getMessage());
|
|
json_error('SERVER_ERROR', 'Server error', 500);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
|
}
|