| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/clients/client2/web31/web/test/includes/ |
Upload File : |
<?php
declare(strict_types=1);
function app_config(): array
{
static $config = null;
if ($config === null) {
$config = require __DIR__ . '/../config/config.php';
}
return $config;
}
function e(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
function redirect_to(string $url): void
{
header('Location: ' . $url);
exit;
}
function set_flash(string $type, string $message): void
{
$_SESSION['flash'] = [
'type' => $type,
'message' => $message,
];
}
function get_flash(): ?array
{
if (!isset($_SESSION['flash']) || !is_array($_SESSION['flash'])) {
return null;
}
$flash = $_SESSION['flash'];
unset($_SESSION['flash']);
return $flash;
}
function ensure_dir(string $dir): void
{
if (!is_dir($dir)) {
mkdir($dir, 0775, true);
}
}
function read_json_file(string $path, array $default = []): array
{
if (!is_file($path)) {
return $default;
}
$content = file_get_contents($path);
if ($content === false || trim($content) === '') {
return $default;
}
$data = json_decode($content, true);
return is_array($data) ? $data : $default;
}
function write_json_file(string $path, array $data): bool
{
ensure_dir(dirname($path));
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($json === false) {
return false;
}
return file_put_contents($path, $json, LOCK_EX) !== false;
}
function append_json_line(string $path, array $data): bool
{
ensure_dir(dirname($path));
$json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($json === false) {
return false;
}
return file_put_contents($path, $json . PHP_EOL, FILE_APPEND | LOCK_EX) !== false;
}
function email_key(string $email): string
{
return sha1(mb_strtolower(trim($email), 'UTF-8'));
}
function slugify(string $text): string
{
$text = mb_strtolower(trim($text), 'UTF-8');
$map = [
'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue', 'ß' => 'ss',
];
$text = strtr($text, $map);
$text = preg_replace('/[^a-z0-9]+/u', '-', $text) ?? '';
$text = trim($text, '-');
return $text !== '' ? $text : 'test';
}
function current_user(): ?array
{
if (!isset($_SESSION['participant']) || !is_array($_SESSION['participant'])) {
return null;
}
return $_SESSION['participant'];
}
function require_user(): array
{
$user = current_user();
if ($user === null) {
set_flash('danger', 'Bitte melde dich zuerst mit Name und E-Mail an.');
redirect_to('index.php');
}
return $user;
}
function is_admin(): bool
{
return !empty($_SESSION['is_admin']);
}
function require_admin(): void
{
if (!is_admin()) {
set_flash('danger', 'Bitte erst im Adminbereich anmelden.');
redirect_to('index.php');
}
}
function storage_paths(): array
{
return app_config()['storage'];
}
function register_or_load_user(string $name, string $email): array
{
$paths = storage_paths();
$record = [
'name' => trim($name),
'email' => trim($email),
'email_key' => email_key($email),
'updated_at' => date('Y-m-d H:i:s'),
'created_at' => date('Y-m-d H:i:s'),
];
append_json_line($paths['users_file'], [
'action' => 'login',
'name' => $record['name'],
'email' => $record['email'],
'email_key' => $record['email_key'],
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
]);
return $record;
}
function load_tests(bool $onlyActive = true): array
{
$paths = storage_paths();
ensure_dir($paths['tests_dir']);
$files = glob($paths['tests_dir'] . '/*.json') ?: [];
$tests = [];
foreach ($files as $file) {
$data = read_json_file($file, []);
if (!$data) {
continue;
}
if ($onlyActive && empty($data['meta']['active'])) {
continue;
}
$tests[] = $data;
}
usort($tests, static function (array $a, array $b): int {
return strcmp((string)($a['meta']['title'] ?? ''), (string)($b['meta']['title'] ?? ''));
});
return $tests;
}
function load_test_by_slug(string $slug): ?array
{
$paths = storage_paths();
$file = $paths['tests_dir'] . '/' . basename($slug) . '.json';
if (!is_file($file)) {
return null;
}
$test = read_json_file($file, []);
return $test ?: null;
}
function submission_path(string $slug, string $email): string
{
$paths = storage_paths();
return $paths['submissions_dir'] . '/' . basename($slug) . '__' . email_key($email) . '.json';
}
function load_submission(string $slug, string $email): array
{
return read_json_file(submission_path($slug, $email), []);
}
function save_submission(string $slug, string $email, array $data): bool
{
return write_json_file(submission_path($slug, $email), $data);
}
function evaluate_test(array $test, array $answers): array
{
$scores = [];
foreach ($test['results'] as $result) {
$scores[(string)$result['key']] = 0;
}
foreach ($test['questions'] as $question) {
$questionId = (string)$question['id'];
$questionType = (string)$question['type'];
$selected = $answers[$questionId] ?? [];
if ($questionType === 'single') {
$selected = [$selected];
} elseif (!is_array($selected)) {
$selected = [];
}
foreach ($question['options'] as $option) {
if (in_array((string)$option['id'], array_map('strval', $selected), true)) {
$resultKey = (string)($option['result_key'] ?? '');
$weight = (int)($option['weight'] ?? 1);
if ($resultKey !== '' && array_key_exists($resultKey, $scores)) {
$scores[$resultKey] += $weight;
}
}
}
}
arsort($scores);
$topKey = (string)array_key_first($scores);
$winner = null;
foreach ($test['results'] as $result) {
if ((string)$result['key'] === $topKey) {
$winner = $result;
break;
}
}
return [
'scores' => $scores,
'winner' => $winner,
];
}
function validate_submission(array $test, array $postAnswers): array
{
$cleanAnswers = [];
$errors = [];
foreach ($test['questions'] as $question) {
$questionId = (string)$question['id'];
$questionType = (string)$question['type'];
$allowedIds = array_map(static function (array $opt): string {
return (string)$opt['id'];
}, $question['options']);
if ($questionType === 'single') {
$value = isset($postAnswers[$questionId]) ? (string)$postAnswers[$questionId] : '';
if ($value === '' || !in_array($value, $allowedIds, true)) {
$errors[] = 'Bitte beantworte die Frage: ' . ($question['text'] ?? $questionId);
continue;
}
$cleanAnswers[$questionId] = $value;
continue;
}
$values = $postAnswers[$questionId] ?? [];
if (!is_array($values) || $values === []) {
$errors[] = 'Bitte wähle mindestens eine Antwort bei: ' . ($question['text'] ?? $questionId);
continue;
}
$selected = [];
foreach ($values as $value) {
$value = (string)$value;
if (in_array($value, $allowedIds, true)) {
$selected[] = $value;
}
}
$selected = array_values(array_unique($selected));
if ($selected === []) {
$errors[] = 'Ungültige Auswahl bei: ' . ($question['text'] ?? $questionId);
continue;
}
$cleanAnswers[$questionId] = $selected;
}
return [
'errors' => $errors,
'answers' => $cleanAnswers,
];
}
function import_test_from_xml(string $xmlPath): array
{
if (!is_file($xmlPath)) {
return ['success' => false, 'message' => 'XML-Datei nicht gefunden.'];
}
libxml_use_internal_errors(true);
$xml = simplexml_load_file($xmlPath);
if ($xml === false) {
return ['success' => false, 'message' => 'XML konnte nicht gelesen werden.'];
}
$title = trim((string)($xml->meta->title ?? ''));
$description = trim((string)($xml->meta->description ?? ''));
$slug = trim((string)($xml->meta->slug ?? ''));
$active = (string)($xml->meta->active ?? '1') === '1';
if ($title === '') {
return ['success' => false, 'message' => 'Im XML fehlt der Titel.'];
}
if ($slug === '') {
$slug = slugify($title);
} else {
$slug = slugify($slug);
}
$results = [];
if (isset($xml->results->result)) {
foreach ($xml->results->result as $result) {
$key = slugify((string)($result['key'] ?? ''));
if ($key === '') {
continue;
}
$results[] = [
'key' => $key,
'title' => trim((string)($result->title ?? '')),
'description' => trim((string)($result->description ?? '')),
'badge_class' => trim((string)($result->badge_class ?? 'primary')),
'emoji' => trim((string)($result->emoji ?? '✨')),
];
}
}
if ($results === []) {
return ['success' => false, 'message' => 'Im XML wurden keine Ergebnistypen gefunden.'];
}
$questions = [];
$questionIndex = 1;
if (isset($xml->questions->question)) {
foreach ($xml->questions->question as $question) {
$questionType = trim((string)($question['type'] ?? 'single'));
$questionText = trim((string)($question->text ?? ''));
if ($questionText === '') {
continue;
}
$options = [];
$optionIndex = 1;
if (isset($question->options->option)) {
foreach ($question->options->option as $option) {
$resultKey = slugify((string)($option['result_key'] ?? ''));
$weight = (int)($option['weight'] ?? 1);
$optionText = trim((string)$option);
if ($optionText === '' || $resultKey === '') {
continue;
}
$options[] = [
'id' => 'o' . $questionIndex . '_' . $optionIndex,
'text' => $optionText,
'result_key' => $resultKey,
'weight' => $weight,
];
$optionIndex++;
}
}
if ($options === []) {
continue;
}
$questions[] = [
'id' => 'q' . $questionIndex,
'text' => $questionText,
'type' => $questionType === 'multiple' ? 'multiple' : 'single',
'options' => $options,
];
$questionIndex++;
}
}
if ($questions === []) {
return ['success' => false, 'message' => 'Im XML wurden keine Fragen gefunden.'];
}
$test = [
'meta' => [
'title' => $title,
'description' => $description,
'slug' => $slug,
'active' => $active,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
],
'results' => $results,
'questions' => $questions,
];
$paths = storage_paths();
ensure_dir($paths['tests_dir']);
$jsonPath = $paths['tests_dir'] . '/' . $slug . '.json';
$xmlBackupPath = $paths['uploads_dir'] . '/' . date('Ymd_His') . '_' . $slug . '.xml';
$saved = write_json_file($jsonPath, $test);
if (!$saved) {
return ['success' => false, 'message' => 'Die JSON-Testdatei konnte nicht gespeichert werden.'];
}
@copy($xmlPath, $xmlBackupPath);
return [
'success' => true,
'message' => 'Test erfolgreich importiert.',
'test' => $test,
];
}
function render_header(string $title): void
{
$config = app_config();
$flash = get_flash();
$user = current_user();
?>
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= e($title) ?> · <?= e($config['app_name']) ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background: #f6f8fb; }
.navbar-brand { font-weight: 700; }
.card-soft { border: 0; border-radius: 1rem; box-shadow: 0 .75rem 2rem rgba(0,0,0,.06); }
.option-label { display:block; padding:1rem; border:1px solid #dee2e6; border-radius:.75rem; cursor:pointer; transition:.15s ease-in-out; }
.option-label:hover { border-color:#0d6efd; background:#f8fbff; }
.choice-input:checked + .option-label { border-color:#0d6efd; background:#e7f1ff; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg bg-white border-bottom mb-4">
<div class="container">
<a class="navbar-brand" href="index.php"><?= e($config['app_name']) ?></a>
<div class="ms-auto d-flex gap-2 align-items-center">
<?php if ($user): ?>
<span class="text-muted small"><?= e($user['name']) ?> · <?= e($user['email']) ?></span>
<a class="btn btn-outline-secondary btn-sm" href="dashboard.php">Dashboard</a>
<a class="btn btn-outline-danger btn-sm" href="logout.php">Logout</a>
<?php else: ?>
<a class="btn btn-outline-primary btn-sm" href="index.php">Start</a>
<?php endif; ?>
<a class="btn btn-dark btn-sm" href="admin/index.php">Admin</a>
</div>
</div>
</nav>
<div class="container pb-5">
<?php if ($flash): ?>
<div class="alert alert-<?= e((string)$flash['type']) ?>"><?= e((string)$flash['message']) ?></div>
<?php endif; ?>
<?php
}
function render_footer(): void
{
?>
</div>
</body>
</html>
<?php
}