Files
WKVS/html/RankLive/script.php
T

667 lines
25 KiB
PHP

<?php
ini_set('display_errors', 1);
if (!isset($segments) || !defined('APP_ROUTER')) {
http_response_code(403);
include $baseDir . "/error-pages/403.php";
exit;
}
$type = $segments[1] ?? null;
$slug = $segments[2] ?? null;
$allowed_types = ["programm", "live"];
if ($type !== null && !in_array($type, $allowed_types)) {
http_response_code(400);
include $baseDir . "/error-pages/400.html";
exit;
}
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require $baseDir . '/../scripts/db/db-verbindung-script-guest.php';
require $baseDir . "/../scripts/db/db-tables.php";
require $baseDir . "/../scripts/db/db-functions.php";
$rankLive_public = db_get_variable($guest, $db_tabelle_var, ['rankLivePublic']) ?? 0;
require $baseDir . '/../scripts/websocket/ws-create-token.php';
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session(true);
$csrf_token = $_SESSION['csrf_token'] ?? '';
if (!$rankLive_public) {
check_user_permission('wk_leitung', false, true);
}
$is_live = $type === 'live';
if ($slug !== null && !$is_live) {
$alle_programme_db = db_select($guest, $db_tabelle_kategorien, '`programm`', 'aktiv = ?', ['1']);
$raw_programme = array_column($alle_programme_db, 'programm', 'programm');
$alle_programme = [];
foreach ($raw_programme as $prog) {
$normalized_key = str_replace(' ', '-', strtolower($prog));
$alle_programme[$normalized_key] = $prog;
}
$req_prog = str_replace(' ', '-', strtolower($slug));
if (!isset($alle_programme[$req_prog])) {
http_response_code(400);
$message = 'Das gesuchte Programm kann nicht gefunden werden';
include $baseDir . "/error-pages/400.php";
exit;
}
$validated_programm_name = $alle_programme[$req_prog];
}
$selectedFreigabeId = 'A';
$json_type = $is_live ? 'rankLive-geraet' : 'rankLive-overview';
$jsonstr = db_select($guest, $db_tabelle_tabellen_konfiguration, 'json', 'type_slug = ?', [$json_type], '', 1);
$data = json_decode($jsonstr[0]['json'], true);
$headers = $data['header'] ?? [];
$bodyColumns = $data['body'] ?? [];
$alle_programme_db = db_select($guest, $db_tabelle_kategorien, '`programm`, `id`', 'aktiv = ?', ['1']);
$indexedProgrammes = array_column($alle_programme_db, 'programm', 'id');
$personData = [
'geburtsdatum' => 'Geburtsdatum',
'name' => 'Name',
'vorname' => 'Vorname',
'verein' => 'Verein',
'programm' => 'Programm'
];
$disciplines = db_select($guest, $db_tabelle_disziplinen, '*', '', [], 'start_index ASC');
$indexedArrayGeraete = array_column($disciplines, 'name', 'id');
if ($is_live) {
$indexed_array_geraete_id_index = array_column($disciplines, 'start_index', 'id');
$indexed_array_geraete_index_id = array_flip($indexed_array_geraete_id_index);
$abt = (int) (db_get_variable($guest, $db_tabelle_var, ['wk_panel_current_abt']) ?? 1);
$akt_subabt = (int) (db_get_variable($guest, $db_tabelle_var, ['wk_panel_current_subabt_admin']) ?? 1);
$max_subabt = count($disciplines);
$stmt = $guest->prepare("SELECT
t.id,
t.name,
t.vorname,
t.programm,
t.verein,
t.geburtsdatum,
p.id AS programm_id,
tabt.`turnerin_index` AS start_index,
tabt.`geraet_id` AS startgeraet,
tabt.`abteilung_id` AS abt_id
FROM $db_tabelle_teilnehmende t
LEFT JOIN $db_tabelle_kategorien p ON p.programm = t.programm
INNER JOIN $db_tabelle_teilnehmende_gruppen tabt ON tabt.`turnerin_id` = t.`id`
INNER JOIN $db_tabelle_gruppen abt ON abt.`id` = tabt.`abteilung_id`
WHERE (t.bezahlt = 4 OR t.bezahltoverride = 4) AND abt.`order_index` = ?");
$stmt->bind_param("i", $abt);
} elseif ($type === 'programm' && $slug !== null) {
$stmt = $guest->prepare("SELECT
u.id,
u.name,
u.vorname,
u.programm,
u.verein,
u.geburtsdatum,
p.id AS programm_id
FROM $db_tabelle_teilnehmende u
LEFT JOIN $db_tabelle_kategorien p ON p.programm = u.programm
WHERE (u.bezahlt = 4 OR u.bezahltoverride = 4) AND u.programm = ?");
$stmt->bind_param("s", $validated_programm_name);
} else {
$stmt = $guest->prepare("SELECT
u.id,
u.name,
u.vorname,
u.programm,
u.verein,
u.geburtsdatum,
p.id AS programm_id
FROM $db_tabelle_teilnehmende u
LEFT JOIN $db_tabelle_kategorien p ON p.programm = u.programm
WHERE (u.bezahlt = 4 OR u.bezahltoverride = 4)");
}
$stmt->execute();
$result = $stmt->get_result();
$tures = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$indexedTures = [];
foreach ($tures as $s_tures) {
$indexedTures[$s_tures['programm_id']][$s_tures['id']] = $s_tures;
}
$current_wk_id = (int) db_get_variable($guest, $db_tabelle_var, ['current_wk_id']) ?: 0;
$stmt = $guest->prepare("SELECT `note_bezeichnung_id`, `geraet_id`, `person_id`, `run_number`, `public_value`
FROM $db_tabelle_wertungen
WHERE `is_public` = 1
AND `wk_id` = ?");
$stmt->bind_param("i", $current_wk_id);
$stmt->execute();
$result = $stmt->get_result();
$noten = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$indexedNoten = [];
$stmt = $guest->prepare("SELECT `id`, `geraete_json`, `pro_geraet`, `anzahl_laeufe_json`, `name`, `default_value` FROM $db_tabelle_wertungstypen");
$stmt->execute();
$result = $stmt->get_result();
$notenConfig = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$disciplines = db_select($guest, $db_tabelle_disziplinen, '*', '', [], 'start_index ASC');
$indexedNotenConfig = array_column($notenConfig, null, 'id');
$rangNote = intval(db_get_var($guest, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['rangNote']));
$orderBestRang = db_get_var($guest, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['orderBestRang']);
$okValuesOrderBestRang = ["ASC", "DESC"];
$rangOrderOk = in_array($orderBestRang, $okValuesOrderBestRang) && intval($rangNote) > 0;
$notenIndexed = [];
foreach ($noten as $sn) {
$notenIndexed[$sn['person_id']][$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = $sn['public_value'];
}
$disciplinesExtended = array_merge(
[["id" => 0, "name" => "None"]],
$disciplines
);
$arrayIndexedNoten = [];
$wkName = db_get_var($guest, "SELECT `value` FROM $db_tabelle_var WHERE `name` = ?", ['wkName']);
$grouped = [];
function groop_by_key($array, $array_key, bool $int = false): array
{
$grouped = [];
if ($int) {
foreach ($array as $entry) {
$key = (int) $entry[$array_key];
$grouped[$key][] = $entry;
}
} else {
foreach ($array as $entry) {
$key = $entry[$array_key];
$grouped[$key][] = $entry;
}
}
return $grouped;
}
if ($is_live) {
$grouped = groop_by_key($tures, 'startgeraet', true);
} else {
$grouped = groop_by_key($tures, 'programm');
foreach ($grouped as $key => $group) {
$orderIndexes[$key] = $indexedProgramme[$key]['order_index'] ?? 10000;
}
if (isset($orderIndexes)) {
uksort($grouped, function ($a, $b) use ($orderIndexes) {
return $orderIndexes[$a] <=> $orderIndexes[$b];
});
}
}
foreach ($grouped as $entry_key => $entries_group):
endforeach;
$timeZone = new \DateTimeZone('Europe/Zurich');
$formatter = new \IntlDateFormatter(
'de_CH',
\IntlDateFormatter::FULL,
\IntlDateFormatter::NONE,
$timeZone
);
$display_over_array = [];
function constructSingleValueSpanPublic($token, $type, $row, $selected_geraet_id = 0)
{
global $formatter;
global $personData;
global $arrayIndexedNoten;
global $timeZone;
global $display_over_array;
global $indexedNotenConfig;
global $indexedProgramme;
$text = '';
$classes = 'singleValueSpan';
$bold = $token['bold'] ?? false;
$font_size = intval($token['fontSize'] ?? 0);
$min_display_width = intval($token['minDisplayWidth'] ?? 1) - 1;
$linked_el_uid = (int) ($token['linkedElUid'] ?? 0);
$uid = (int) ($token['uid'] ?? 0);
$style = '';
if ($font_size > 0) {
$style = ' style="font-size: ' . $font_size . 'px"';
}
if ($min_display_width > 0) {
$classes .= ' displayOver' . $min_display_width . 'px';
$display_over_array[] = $min_display_width;
}
$dataAttributes = 'data-bold="' . htmlspecialchars($bold) . '" data-uid="'. $uid .'"';
if ($linked_el_uid > 0) {
$dataAttributes .= ' data-linked-el-uid="' . $linked_el_uid . '"';
}
switch ($type) {
case 'text':
$text = $token['text'] ?? '';
$classes .= ' staticText';
break;
case 'sortData':
$column = $token['column'] ?? '';
switch ($column) {
case "rang":
$classes .= ' rangField';
break;
case "startindex":
$text = $row['calculedstartindex'];
break;
}
break;
case 'personData':
$column = $token['column'] ?? '';
if ($column === 'jahrgang') {
$geburtsdatum = $row['geburtsdatum'] ?? '';
$date = new \DateTimeImmutable($geburtsdatum, $timeZone);
$formatter->setPattern('yyyy');
$text = $formatter->format($date);
} else {
$text = array_key_exists($column, $personData) ? $row[$column] ?? '' : '';
if ($column === 'geburtsdatum') {
$date = new \DateTimeImmutable($text, $timeZone);
$formatter->setPattern(($token['phpDateFormat'] ?? '' !== '') ? $token['phpDateFormat'] : 'd. MMMM YYYY');
$text = $formatter->format($date);
}
}
break;
case 'notenGeraetData':
$field_type_id = intval($token['field-type-id'] ?? 0);
$run = intval($token['run'] ?? 0);
$person_id = $row['id'];
$programm_id = $indexedProgramme[$row['programm']]['id'] ?? 0;
$runsJSON = json_decode($indexedNotenConfig[$field_type_id]['anzahl_laeufe_json'] ?? '', true) ?? [];
$max_run = $runsJSON[$selected_geraet_id][$programm_id] ?? $runsJSON[$selected_geraet_id]['all'] ?? $runsJSON['default'] ?? 0;
if ($run > $max_run) {
$classes .= ' nonExistentEl';
} else {
$notenNullable = ($token['notenNullable'] ?? 'false') === 'true';
$dataAttributes .= ' data-field-type-id="' . $field_type_id . '"';
$dataAttributes .= ' data-geraet-id="' . $selected_geraet_id . '"';
$dataAttributes .= ' data-person-id="' . $person_id . '"';
$dataAttributes .= ' data-run="' . $run . '"';
$dataAttributes .= ' data-noten-nullable="' . (($notenNullable) ? "true" : "false") . '"';
$text = $arrayIndexedNoten[$person_id][$selected_geraet_id][$field_type_id][$run]['value'] ?? null;
if ($notenNullable && $text !== null && floatval($text) === 0.0) {
$text = ' ';
}
if ($text === null) {
$classes .= ' emtyPlaceholder';
$text = '-';
}
$classes .= ' changebleValue';
}
break;
case 'notenData':
$field_type_id = intval($token['field-type-id'] ?? 0);
$geraet_id = intval($token['geraet-id'] ?? 0);
$run = intval($token['run'] ?? 0);
$person_id = $row['id'];
$notenNullable = ($token['notenNullable'] ?? 'false') === 'true';
$dataAttributes .= ' data-field-type-id="' . $field_type_id . '"';
$dataAttributes .= ' data-geraet-id="' . $geraet_id . '"';
$dataAttributes .= ' data-person-id="' . $person_id . '"';
$dataAttributes .= ' data-run="' . $run . '"';
$dataAttributes .= ' data-noten-nullable="' . (($notenNullable) ? "true" : "false") . '"';
$text = $arrayIndexedNoten[$person_id][$geraet_id][$field_type_id][$run]['value'] ?? null;
if ($notenNullable && $text !== null && floatval($text) === 0.0) {
$text = ' ';
}
if ($text === null) {
$classes .= ' emtyPlaceholder';
$text = '-';
}
$classes .= ' changebleValue';
break;
}
?>
<span class="<?= $classes ?>" <?= $dataAttributes; ?><?= $style ?>><?= htmlspecialchars($text); ?></span>
<?php }
?>
<!DOCTYPE html>
<html lang="de-ch">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="png" href="/intern/img/icon.png">
<link rel="stylesheet" href="/intern/css/custom-msg-display.css">
<link href="/files/fonts/fonts.css" rel="stylesheet">
<link rel="stylesheet" href="/css/custom-display-editor-required-css.css">
<link rel="stylesheet" href="/css/rank-live.css">
<link rel="stylesheet" href="/css/header.css">
<script src="/intern/js/jquery/jquery-3.7.1.min.js"></script>
<script src="/intern/js/custom-msg-display.js"></script>
<script src="/js/header.js"></script>
<title>RankLive - <?= htmlspecialchars($wkName) ?></title>
</head>
<body>
<section class="bgSection">
<?php include $baseDir . '/../scripts/public-elements/rankLive-header.php'; ?>
<?php if (count($indexedTures) < 1) : ?>
<h3>Noch keine Daten verfügbar</h3>
<?php exit; ?>
<?php endif; ?>
<div class="headerWrapper">
<h2 class="headerMain">RankLive</h2>
<?php if ($is_live) : ?>
<h4>Gruppe: <?= (int) $abt ?></h4>
<h4>Rotation: <?= (int) $akt_subabt ?></h4>
<?php elseif ($type === 'programm' && $slug !== null) : ?>
<h4><?= htmlspecialchars($validated_programm_name) ?></h4>
<?php endif; ?>
</div>
<div class="allAbtContainer">
<?php foreach ($grouped as $entry_key => $entries_group):
$table_data_geraet_label = '';
$extra_table_classes = '';
if ($is_live) {
$entries_with_calculated_index = [];
// Optional: Vorab-Caching der MAX-Indices, um DB-Abfragen im Loop zu vermeiden
// (Nur aktivieren, wenn $entries_group sehr groß ist und Performance-Probleme auftreten)
$maxStartIndexCache = [];
foreach ($entries_group as $row) {
$old_geraet_id = $row['startgeraet'];
$old_geraet_index = $indexed_array_geraete_id_index[$row['startgeraet']];
$shifted_geraet_index = $old_geraet_index + $akt_subabt - 1;
if ($shifted_geraet_index > $max_subabt) {
$shifted_geraet_index -= $max_subabt;
}
$row['startgeraet'] = $indexed_array_geraete_index_id[$shifted_geraet_index];
$rohstartindex = intval($row['start_index']);
$abtId = intval($row['abt_id']);
// Hole maxStartIndex (mit Caching-Logik optional)
if (!isset($maxStartIndexCache["$abtId-$old_geraet_id "])) {
$maxStartIndexCache["$abtId-$old_geraet_id"] = db_get_var($guest, "SELECT COUNT(*) FROM `$db_tabelle_teilnehmende_gruppen` WHERE abteilung_id = ? AND geraet_id = ?", [$abtId, $old_geraet_id]);
}
$maxstartindex = $maxStartIndexCache["$abtId-$old_geraet_id"];
// Sicherheit: Vermeiden Sie Division durch Null oder Modulo durch 0
if ($maxstartindex < 1) {
$maxstartindex = 1;
}
// The order shift depends on the number of rotations the group has made
$rotation_shift = $akt_subabt - 1;
$calculedstartindex = $rohstartindex - $rotation_shift;
// Sicherstellen, dass das Ergebnis positiv ist (PHP Modulo kann negative Ergebnisse liefern)
// Wenn das Ergebnis negativ ist, addieren wir maxstartindex
if ($calculedstartindex < 1) {
$calculedstartindex += $maxstartindex;
}
// Kopie der Zeile erstellen, um Originaldaten nicht zu verändern
$row['calculedstartindex'] = $calculedstartindex;
$entries_with_calculated_index[] = $row;
}
// Sortieren nach dem berechneten Startindex
usort($entries_with_calculated_index, fn($a, $b) => $a['calculedstartindex'] <=> $b['calculedstartindex']);
$entries_to_display = $entries_with_calculated_index;
$shifted_geraet_index = $indexed_array_geraete_id_index[$entry_key] + $akt_subabt - 1;
if ($shifted_geraet_index > $max_subabt) {
$shifted_geraet_index -= $max_subabt;
}
$shifted_geraet_id = $indexed_array_geraete_index_id[$shifted_geraet_index];
$table_data_geraet_label = ' data-geraet-index="' . $shifted_geraet_index . '"';
$extra_table_classes = ' shiftedGeraetTable';
} else {
$entries_to_display = $entries_group;
}
foreach ($entries_to_display as $row):
$rangNotenArray[$entry_key][$row['id']] = isset($notenIndexed[$row['id']][0][$rangNote][1]) ? floatval($notenIndexed[$row['id']][0][$rangNote][1]) : null;
foreach ($disciplinesExtended as $discipline):
foreach ($notenConfig as $snC):
if (intval($snC['pro_geraet']) === 1 && intval($discipline['id']) === 0) {
continue;
}
if (intval($snC['pro_geraet']) !== 1) {
$allowedGeraete = !empty($snC['geraete_json']) ? json_decode($snC['geraete_json'], true) : [];
if (!in_array($discipline['id'], $allowedGeraete)) {
continue;
}
}
$runsJSON = !empty($snC['anzahl_laeufe_json']) ? json_decode($snC['anzahl_laeufe_json'], true) : [];
$runs = $runsJSON[$discipline['id']][$entry_key] ?? $runsJSON[$discipline['id']]['all'] ?? $runsJSON["default"] ?? 1;
for ($r = 1; $r <= $runs; $r++):
$note = $notenIndexed[$row['id']][$discipline['id']][$snC['id']][$r] ?? null;
$normalizedNote = ($note !== null) ? number_format($note, $snC['nullstellen'] ?? 2) : null;
$arrayIndexedNoten[intval($row['id'])][intval($discipline['id'])][intval($snC['id'])][intval($r)] = ["value" => $normalizedNote];
endfor;
endforeach;
endforeach;
endforeach; ?>
<div class="singleAbtDiv<?= $extra_table_classes ?>" <?= $table_data_geraet_label ?>>
<?php if (!($type === 'programm' && $slug !== null)) : ?>
<h2 class="headerAbt"><?= ($is_live) ? $indexedArrayGeraete[$shifted_geraet_id] ?? '' : $entry_key ?? '' ?></h2>
<?php endif; ?>
<?php $geraet_id_for_live_view = $is_live ? $shifted_geraet_id : 0 ?>
<div class="tableWraper">
<table class="customDisplayEditorTable">
<thead>
<tr>
<?php foreach ($headers as $header_data): ?>
<?php
$min_display_width = ($header_data['minDisplayWidth'] ?? 1) - 1;
$class = "";
if ($min_display_width > 0) {
$class = 'displayOver' . $min_display_width . 'px';
$display_over_array[] = $min_display_width;
}
?>
<th class="<?= $class ?>">
<?= htmlspecialchars($header_data['title']) ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody <?= !$is_live ? 'data-programm-id="'. $entry_key .'"' : ''?>>
<?php foreach ($entries_to_display as $row): ?>
<tr data-person-id="<?= $row['id'] ?>">
<?php foreach ($bodyColumns as $ind => $columnTokens): ?>
<?php
$class = '';
if (isset($headers[$ind]['minDisplayWidth']) && $headers[$ind]['minDisplayWidth'] > 1) {
$min_display_width = ($headers[$ind]['minDisplayWidth'] ?? 1) - 1;
$class = 'displayOver' . $min_display_width . 'px';
$display_over_array[] = $min_display_width;
}
?>
<td class=<?= $class ?>>
<span class="tdSpan">
<?php
foreach ($columnTokens as $token):
$type = $token['type'] ?? '';
if ($type === 'layoutEls') :
$column = $token['column'];
$content = $token['content'] ?? []; ?>
<div class="<?= $column ?>LayoutEl">
<?php foreach ($content as $layout_row) : ?>
<span class="<?= $column ?>LayoutElRow">
<?php foreach ($layout_row as $el) :
constructSingleValueSpanPublic($el, $el['type'], $row, $geraet_id_for_live_view);
endforeach; ?>
</span>
<?php endforeach; ?>
</div>
<?php else:
constructSingleValueSpanPublic($token, $type, $row, $geraet_id_for_live_view);
endif;
endforeach; ?>
</span>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php endforeach; ?>
</div>
<div class="tableCutter"></div>
</section>
<style>
<?php
$unique_display_over_array = array_unique($display_over_array);
foreach ($unique_display_over_array as $s_size):
$s_size = (int)$s_size;
?>
@media (max-width: <?= $s_size ?>px) {
.displayOver<?= $s_size ?>px {
display: none !important;
}
}
<?php endforeach; ?>
</style>
<script>
window.FREIGABE = "<?= $selectedFreigabeId; ?>";
window.CSDR_TOKEN = "<?= $csrf_token; ?>";
window.WS_ACCESS_TOKEN = "<?= generateWSReadToken('rankLive', $selectedFreigabeId) ?>";
window.AKTUELLES_JAHR = "<?= $current_wk_id ?>";
window.RANG_NOTEN_ARRAY = '<?= json_encode($rangNotenArray, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>';
window.RANG_SORT = "<?= $orderBestRang ?>";
window.RANG_NOTE_ID = <?= $rangNote ?>;
window.LIVE = <?= (int) $is_live ?>;
</script>
<script src="/RankLive/js/script.js"></script>
</body>