376 lines
15 KiB
PHP
376 lines
15 KiB
PHP
<?php
|
|
|
|
$tokenRec = require_valid_token_web();
|
|
|
|
$validTypes = ['question', 'answer_option', 'string', 'app_string', 'language', 'language_enabled', 'questionnaire_language'];
|
|
|
|
switch ($method) {
|
|
|
|
case 'GET':
|
|
if (!empty($_GET['exportBundle'])) {
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
try {
|
|
$opened = qdb_open_read_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
$bundle = qdb_export_translations_bundle($pdo);
|
|
qdb_discard($tmpDb, $lockFp);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Export translations', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
|
|
$filename = 'translations_bundle_' . date('Y-m-d_His') . '.json';
|
|
qdb_http_download(
|
|
json_encode($bundle, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
|
|
[
|
|
'Content-Type' => 'application/json; charset=UTF-8',
|
|
'Content-Disposition' => 'attachment; filename="' . $filename . '"',
|
|
]
|
|
);
|
|
}
|
|
|
|
if (isset($_GET['languages'])) {
|
|
try {
|
|
$opened = qdb_open_read_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
$rows = qdb_load_languages($pdo);
|
|
$disables = qdb_questionnaire_language_disables($pdo);
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_success([
|
|
'languages' => $rows,
|
|
'questionnaireLanguageDisables' => $disables,
|
|
]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Load languages', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
}
|
|
|
|
if (!empty($_GET['all'])) {
|
|
try {
|
|
$opened = qdb_open_read_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
|
|
$languages = qdb_load_languages($pdo);
|
|
if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) {
|
|
array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German', 'enabled' => 1]);
|
|
}
|
|
|
|
$qnRows = $pdo->query("
|
|
SELECT questionnaireID, name, orderIndex FROM questionnaire ORDER BY orderIndex
|
|
")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$appEntries = qdb_app_ui_string_entries_for_website($pdo);
|
|
$appTranslations = qdb_load_translations_for_entries($pdo, $appEntries);
|
|
qdb_attach_translation_texts($appEntries, $appTranslations);
|
|
|
|
$questionnaires = [];
|
|
$allEntries = $appEntries;
|
|
|
|
foreach ($qnRows as $qn) {
|
|
$lists = qdb_translation_entry_lists($pdo, $qn['questionnaireID']);
|
|
$entries = array_merge($lists['stringEntries'], $lists['contentEntries']);
|
|
if (!$entries) {
|
|
continue;
|
|
}
|
|
$translations = qdb_load_translations_for_entries($pdo, $entries);
|
|
qdb_attach_translation_texts($entries, $translations);
|
|
$questionnaires[] = [
|
|
'questionnaireID' => $qn['questionnaireID'],
|
|
'name' => $qn['name'],
|
|
'orderIndex' => (int)$qn['orderIndex'],
|
|
'entries' => $entries,
|
|
];
|
|
foreach ($entries as $e) {
|
|
$allEntries[] = $e;
|
|
}
|
|
}
|
|
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_success([
|
|
'languages' => $languages,
|
|
'appStrings' => $appEntries,
|
|
'questionnaires' => $questionnaires,
|
|
'entries' => $allEntries,
|
|
'sourceLanguage' => QDB_SOURCE_LANGUAGE,
|
|
'questionnaireLanguageDisables' => qdb_questionnaire_language_disables($pdo),
|
|
]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Load translations', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
}
|
|
|
|
$qnID = trim((string)($_GET['questionnaireID'] ?? ''));
|
|
if ($qnID !== '') {
|
|
try {
|
|
$opened = qdb_open_read_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
|
|
$languages = qdb_load_languages($pdo);
|
|
if (!array_filter($languages, fn($l) => $l['languageCode'] === QDB_SOURCE_LANGUAGE)) {
|
|
array_unshift($languages, ['languageCode' => QDB_SOURCE_LANGUAGE, 'name' => 'German', 'enabled' => 1]);
|
|
}
|
|
|
|
$qnRow = $pdo->prepare('SELECT name FROM questionnaire WHERE questionnaireID = :id');
|
|
$qnRow->execute([':id' => $qnID]);
|
|
$qnName = $qnRow->fetchColumn();
|
|
if ($qnName === false) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('NOT_FOUND', 'Questionnaire not found', 404);
|
|
}
|
|
|
|
$lists = qdb_translation_entry_lists($pdo, $qnID);
|
|
$entries = array_merge($lists['stringEntries'], $lists['contentEntries']);
|
|
$translations = qdb_load_translations_for_entries($pdo, $entries);
|
|
qdb_attach_translation_texts($entries, $translations);
|
|
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_success([
|
|
'languages' => $languages,
|
|
'entries' => $entries,
|
|
'questionnaire' => ['questionnaireID' => $qnID, 'name' => (string)$qnName],
|
|
'sourceLanguage' => QDB_SOURCE_LANGUAGE,
|
|
'questionnaireLanguageDisables' => qdb_questionnaire_language_disables($pdo),
|
|
]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Load translations', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
}
|
|
|
|
$type = $_GET['type'] ?? '';
|
|
$id = $_GET['id'] ?? '';
|
|
if (!in_array($type, $validTypes, true) || (!$id && $type !== 'string')) {
|
|
json_error('BAD_REQUEST', 'type (question|answer_option|string) and id required', 400);
|
|
}
|
|
|
|
try {
|
|
$opened = qdb_open_read_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
if ($type === 'question') {
|
|
$stmt = $pdo->prepare("SELECT languageCode, text FROM question_translation WHERE questionID = :id");
|
|
$stmt->execute([':id' => $id]);
|
|
} elseif ($type === 'answer_option') {
|
|
$stmt = $pdo->prepare("SELECT languageCode, text FROM answer_option_translation WHERE answerOptionID = :id");
|
|
$stmt->execute([':id' => $id]);
|
|
} else {
|
|
if ($id) {
|
|
$stmt = $pdo->prepare("SELECT stringKey, languageCode, text FROM string_translation WHERE stringKey = :id");
|
|
$stmt->execute([':id' => $id]);
|
|
} else {
|
|
$stmt = $pdo->query("SELECT stringKey, languageCode, text FROM string_translation ORDER BY stringKey, languageCode");
|
|
}
|
|
}
|
|
if ($type === 'question' || $type === 'answer_option') {
|
|
if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('NOT_FOUND', 'Translation entity not found', 404);
|
|
}
|
|
}
|
|
|
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_success(['translations' => $rows]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Load translations', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
break;
|
|
|
|
case 'POST':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
if (($body['action'] ?? '') === 'importBundle') {
|
|
$bundle = $body['bundle'] ?? null;
|
|
if (!is_array($bundle)) {
|
|
json_error('MISSING_FIELDS', 'bundle object is required', 400);
|
|
}
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
|
|
try {
|
|
$result = qdb_import_translations_bundle($pdo, $bundle);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success($result);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Import translations', null, $tmpDb, $lockFp);
|
|
}
|
|
break;
|
|
}
|
|
if (($body['action'] ?? '') === 'deleteTranslationsScope') {
|
|
$scope = trim((string)($body['scope'] ?? ''));
|
|
$qnID = trim((string)($body['questionnaireID'] ?? ''));
|
|
$lang = isset($body['languageCode']) ? trim((string)$body['languageCode']) : '';
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
|
|
try {
|
|
$deleted = qdb_delete_translations_for_scope(
|
|
$pdo,
|
|
$scope,
|
|
$qnID !== '' ? $qnID : null,
|
|
$lang !== '' ? $lang : null
|
|
);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success(['deleted' => $deleted]);
|
|
} catch (InvalidArgumentException $e) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('INVALID_FIELD', $e->getMessage(), 400);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Delete translations', null, $tmpDb, $lockFp);
|
|
}
|
|
break;
|
|
}
|
|
json_error('BAD_REQUEST', 'Unknown action', 400);
|
|
break;
|
|
|
|
case 'PUT':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
$type = $body['type'] ?? '';
|
|
|
|
if ($type === 'language') {
|
|
$lang = trim($body['languageCode'] ?? '');
|
|
$name = trim($body['name'] ?? '');
|
|
if (!$lang) {
|
|
json_error('MISSING_FIELDS', 'languageCode required', 400);
|
|
}
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
|
|
try {
|
|
if ($lang === QDB_SOURCE_LANGUAGE) {
|
|
qdb_ensure_source_language($pdo);
|
|
} elseif (qdb_column_exists($pdo, 'language', 'enabled')) {
|
|
$pdo->prepare('INSERT INTO language (languageCode, name, enabled) VALUES (:lc, :n, 1) '
|
|
. qdb_upsert_update(['languageCode'], ['name' => 'excluded.name', 'enabled' => '1']))
|
|
->execute([':lc' => $lang, ':n' => $name]);
|
|
} else {
|
|
$pdo->prepare('INSERT INTO language (languageCode, name) VALUES (:lc, :n) '
|
|
. qdb_upsert_update(['languageCode'], ['name' => 'excluded.name']))
|
|
->execute([':lc' => $lang, ':n' => $name]);
|
|
}
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success([]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Save language', null, $tmpDb, $lockFp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ($type === 'language_enabled') {
|
|
$lang = trim($body['languageCode'] ?? '');
|
|
if (!$lang) {
|
|
json_error('MISSING_FIELDS', 'languageCode required', 400);
|
|
}
|
|
$enabled = !empty($body['enabled']);
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
|
|
try {
|
|
qdb_set_language_enabled($pdo, $lang, $enabled);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success([]);
|
|
} catch (InvalidArgumentException $e) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('INVALID_FIELD', $e->getMessage(), 400);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Save language setting', null, $tmpDb, $lockFp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ($type === 'questionnaire_language') {
|
|
$qnID = trim($body['questionnaireID'] ?? '');
|
|
$lang = trim($body['languageCode'] ?? '');
|
|
if (!$qnID || !$lang) {
|
|
json_error('MISSING_FIELDS', 'questionnaireID and languageCode required', 400);
|
|
}
|
|
$enabled = !empty($body['enabled']);
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
|
|
try {
|
|
qdb_set_questionnaire_language_disabled($pdo, $qnID, $lang, !$enabled);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success([]);
|
|
} catch (InvalidArgumentException $e) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('INVALID_FIELD', $e->getMessage(), 400);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Save questionnaire language setting', null, $tmpDb, $lockFp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
$id = $body['id'] ?? '';
|
|
$lang = trim($body['languageCode'] ?? '');
|
|
$text = $body['text'] ?? '';
|
|
if (!in_array($type, $validTypes, true) || !$id || !$lang) {
|
|
json_error('MISSING_FIELDS', 'type, id, and languageCode required', 400);
|
|
}
|
|
|
|
try {
|
|
$opened = qdb_open_write_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('NOT_FOUND', 'Translation entity not found', 404);
|
|
}
|
|
qdb_put_translation($pdo, $type, $id, $lang, $text);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success([]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Save translation', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
break;
|
|
|
|
case 'DELETE':
|
|
require_role(['admin', 'supervisor'], $tokenRec);
|
|
$body = read_json_body();
|
|
$type = $body['type'] ?? '';
|
|
|
|
if ($type === 'language') {
|
|
$lang = trim($body['languageCode'] ?? '');
|
|
if (!$lang) {
|
|
json_error('MISSING_FIELDS', 'languageCode required', 400);
|
|
}
|
|
if ($lang === QDB_SOURCE_LANGUAGE) {
|
|
json_error('BAD_REQUEST', 'German (de) cannot be removed', 400);
|
|
}
|
|
[$pdo, $tmpDb, $lockFp] = qdb_open_write_or_fail();
|
|
try {
|
|
$pdo->prepare("DELETE FROM language WHERE languageCode = :lc")->execute([':lc' => $lang]);
|
|
$pdo->prepare("DELETE FROM question_translation WHERE languageCode = :lc")->execute([':lc' => $lang]);
|
|
$pdo->prepare("DELETE FROM answer_option_translation WHERE languageCode = :lc")->execute([':lc' => $lang]);
|
|
$pdo->prepare("DELETE FROM string_translation WHERE languageCode = :lc")->execute([':lc' => $lang]);
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success([]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Delete language', null, $tmpDb, $lockFp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
$id = $body['id'] ?? '';
|
|
$lang = trim($body['languageCode'] ?? '');
|
|
if (!in_array($type, $validTypes, true) || !$id || !$lang) {
|
|
json_error('MISSING_FIELDS', 'type, id, and languageCode required', 400);
|
|
}
|
|
|
|
try {
|
|
$opened = qdb_open_write_or_fail();
|
|
[$pdo, $tmpDb, $lockFp] = $opened;
|
|
if ($type === 'question' || $type === 'answer_option') {
|
|
if (!qdb_translation_entity_exists($pdo, $type, (string)$id)) {
|
|
qdb_discard($tmpDb, $lockFp);
|
|
json_error('NOT_FOUND', 'Translation entity not found', 404);
|
|
}
|
|
}
|
|
if ($type === 'question') {
|
|
$pdo->prepare('DELETE FROM question_translation WHERE questionID = :id AND languageCode = :lang')
|
|
->execute([':id' => $id, ':lang' => $lang]);
|
|
} elseif ($type === 'answer_option') {
|
|
$pdo->prepare('DELETE FROM answer_option_translation WHERE answerOptionID = :id AND languageCode = :lang')
|
|
->execute([':id' => $id, ':lang' => $lang]);
|
|
} else {
|
|
$pdo->prepare('DELETE FROM string_translation WHERE stringKey = :id AND languageCode = :lang')
|
|
->execute([':id' => $id, ':lang' => $lang]);
|
|
}
|
|
qdb_save($tmpDb, $lockFp);
|
|
json_success([]);
|
|
} catch (Throwable $e) {
|
|
qdb_handler_fail($e, 'Delete translation', null, $tmpDb ?? null, $lockFp ?? null);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
json_error('METHOD_NOT_ALLOWED', 'Method not allowed', 405);
|
|
}
|