WKVS v 1.0.0

This commit is contained in:
Fabio Herzig
2026-07-24 21:49:40 +02:00
commit 6cb5386205
469 changed files with 76191 additions and 0 deletions
@@ -0,0 +1,206 @@
<?php
function generateIntersectCache($mysqli, $baseDir) {
global $db_tabelle_kategorien;
global $db_tabelle_disziplinen;
global $db_tabelle_wertungstypen;
global $db_tabelle_tabellen_konfiguration;
global $db_tabelle_var;
// 1. Fetch all unique IDs you need to loop through
$all_programms = array_column(db_select($mysqli, $db_tabelle_kategorien, '`id`', '`aktiv` = ?', [1]) ?? [], 'id');
$all_geraete = array_column(db_select($mysqli, $db_tabelle_disziplinen, 'id') ?? [], 'id');
// 2. Fetch the static DB configs ONCE before starting the massive loop
$rang_note_id = db_get_variable($mysqli, $db_tabelle_var, ['rangNote']);
$json_geraet = db_get_var($mysqli, "SELECT `all_noten_json` FROM $db_tabelle_tabellen_konfiguration WHERE `type_slug` = ?", ["rankLive-geraet"]) ?: '';
$json_overview = db_get_var($mysqli, "SELECT `all_noten_json` FROM $db_tabelle_tabellen_konfiguration WHERE `type_slug` = ?", ["rankLive-overview"]) ?: '';
$noten_rank_live_geraet = json_decode($json_geraet, true) ?? [];
$noten_rank_live_overview = json_decode($json_overview, true) ?? [];
$notenConfig_db = db_select($mysqli, $db_tabelle_wertungstypen, '`berechnung_json`, `id`, `type`, `anzahl_laeufe_json`');
$noten_config = array_column($notenConfig_db, null, 'id');
$following_config_array = array_column($notenConfig_db, 'berechnung_json', 'id');
$all_inputs_noten_ids = array_filter(array_column($notenConfig_db, 'type', 'id'), fn($type) => $type === 'input');
$masterCache = [];
// 2. Extract ALL possible unique IDs dynamically from your JSON configurations
$max_runs = 1; // Default fallback to at least 1 run
foreach ($noten_config as $n_id => $data) {
$config = json_decode($data['anzahl_laeufe_json'], true);
foreach ($config as $geraetKey => $value) {
if ($geraetKey === 'default') {
$max_runs = max($max_runs, (int)$value);
continue;
}
if (is_array($value)) {
foreach ($value as $progKey => $runsCount) {
$max_runs = max($max_runs, (int)$runsCount);
}
}
}
}
// Ensure we have array lists instead of associative maps
$all_programms = array_values($all_programms);
$all_geraete = array_values($all_geraete);
// Generate standard run sequences (e.g., if max_runs is 2, creates [1, 2])
$all_runs = range(1, $max_runs);
$masterCache = [];
// 3. The Grand Loop
foreach ($all_programms as $programm_id) {
foreach ($all_geraete as $geraetId) {
foreach ($all_runs as $initNoteRun) {
$all_needed_noten = [];
foreach ($noten_rank_live_geraet as $ind => $s_note) {
$n_id = $s_note['n_id'];
$anzahl_laeufe_json = $noten_config[$n_id]['anzahl_laeufe_json'] ?? '';
$anzahl_laeufe = json_decode($anzahl_laeufe_json, true);
$g_id = ($s_note['g_id'] === 'A') ? (int) $geraetId : (int) $s_note['g_id'];
$run = (int) $s_note['r'];
$runs = $anzahl_laeufe[$g_id][(int) $programm_id] ?? $anzahl_laeufe[$g_id]['all'] ?? $anzahl_laeufe['default'] ?? 0;
if (((int) $runs) < $run) {
unset($noten_rank_live_geraet[$ind]);
continue;
}
$key = "$n_id|$g_id|$run";
if (!isset($all_needed_noten[$key])) {
$all_needed_noten[$key] = [
"r" => $run,
"g_id" => $g_id,
"n_id" => $n_id
];
}
}
foreach ($noten_rank_live_overview as $s_note) {
$n_id = $s_note['n_id'];
$g_id = $s_note['g_id'];
$run = $s_note['r'];
$key = "$n_id|$g_id|$run";
if (!isset($all_needed_noten[$key])) {
$all_needed_noten[$key] = [
"r" => $run,
"g_id" => $g_id,
"n_id" => $n_id
];
}
}
$all_updating_noten = [];
foreach ($following_config_array as $this_note_id => $s_note_abhaenige_rechnungen_json) {
$s_note_abhaenige_rechnungen = json_decode($s_note_abhaenige_rechnungen_json ?? '') ?? [];
foreach ($s_note_abhaenige_rechnungen as $s_rechnung) {
$this_note_geraet_id = $s_rechnung[1];
if ($this_note_geraet_id !== 'A' && (int) $this_note_geraet_id !== (int) $geraetId) {
continue;
}
$target_note_id = $s_rechnung[0];
$target_run_raw = $s_rechnung[2][0] ?? 'A';
$target_geraet_id_raw = $s_rechnung[2][1] ?? 'A';
$target_run = ($target_run_raw === 'A') ? $initNoteRun : (int) $target_run_raw;
$target_geraet_id = ($target_geraet_id_raw === 'A') ? $geraetId : (int) $target_geraet_id_raw;
$key = "$target_note_id|$target_geraet_id|$target_run";
if (!isset($all_updating_noten[$key])) {
$all_updating_noten[$key] = [
"r" => $target_run,
"g_id" => $target_geraet_id,
"n_id" => $target_note_id
];
}
}
}
foreach ($all_inputs_noten_ids as $n_id => $_) {
$anzahl_laeufe_json = $noten_config[$n_id]['anzahl_laeufe_json'] ?? '';
$anzahl_laeufe = json_decode($anzahl_laeufe_json, true);
$runs = $anzahl_laeufe[$geraetId][(int) $programm_id] ?? $anzahl_laeufe[$geraetId]['all'] ?? $anzahl_laeufe['default'] ?? 0;
if ($initNoteRun > $runs) {
continue;
}
$key = "$n_id|$geraetId|$initNoteRun";
if (!isset($all_updating_noten[$key])) {
$all_updating_noten[$key] = [
"r" => $initNoteRun,
"g_id" => $geraetId,
"n_id" => $n_id
];
}
}
$intersect_noten = array_intersect_key($all_updating_noten, $all_needed_noten);
$key = "$rang_note_id|0|1";
if (!isset($all_updating_noten[$key])) {
$all_updating_noten[$key] = [
"r" => 1,
"g_id" => 0,
"n_id" => $rang_note_id
];
}
$sql_where_array = [];
$sql_where_values = [];
$i = 1;
foreach ($intersect_noten as $s_note) {
$sql_where_values[] = $s_note['n_id'];
$sql_where_values[] = $s_note['g_id'];
$sql_where_values[] = $s_note['r'];
$sql_where_array[] = '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)';
}
$sql_where_string = '';
if (!empty($sql_where_values)) {
$sql_where_string = implode(' OR ', $sql_where_array);
}
if (!empty($intersect_noten)) {
$masterCache[$programm_id][$geraetId][$initNoteRun] = ["SQL" => $sql_where_string, "values" => $sql_where_values, "intersect_noten" => $intersect_noten];
}
}
}
}
// 4. Save to file
$fileContent = "<?php\nreturn " . var_export($masterCache, true) . ";";
file_put_contents($baseDir . '/../scripts/cache/display-dependencies.php', $fileContent);
}
+57
View File
@@ -0,0 +1,57 @@
<?php
function regenerate_noten_cache($mysqli, $db_tabelle_wertungstypen, $baseDir) {
if (!function_exists('db_select')) require $baseDir . '/../scripts/db/db-functions.php';
$notenRechnerCache = new NotenRechner();
$all = db_select($mysqli, $db_tabelle_wertungstypen, "id, default_value, pro_geraet, geraete_json, anzahl_laeufe_json, berechnung, nullstellen, zeige_in_rank_live, display_string");
$ascArrayDefaultValues = array_column($all, 'default_value', 'id');
$ascArrayProGeraet = array_column($all, 'pro_geraet', 'id');
$ascArrayRechnungen = array_column($all, 'berechnung', 'id');
$indexedNullstellen = array_column($all, 'nullstellen', 'id');
$ascPrefixDisplay = array_column($all, 'display_string', 'id');
$ascArrayGeraeteJSON = array_column($all, 'geraete_json', 'id');
foreach ($ascArrayGeraeteJSON as $key => $val) {
$ascArrayGeraeteJSON[$key] = json_decode((string)$val, true);
}
$ascArrayAnzahlLaeufeJSON = array_column($all, 'anzahl_laeufe_json', 'id');
foreach ($ascArrayAnzahlLaeufeJSON as $key => $val) {
$ascArrayAnzahlLaeufeJSON[$key] = json_decode((string)$val, true) ?? [];
}
$export = "<?php\n\nreturn [\n";
$export .= " 'default_values' => " . var_export($ascArrayDefaultValues, true) . ",\n";
$export .= " 'pro_geraet' => " . var_export($ascArrayProGeraet, true) . ",\n";
$export .= " 'geraete_json' => " . var_export($ascArrayGeraeteJSON, true) . ",\n";
$export .= " 'anzahl_laeufe_json' => " . var_export($ascArrayAnzahlLaeufeJSON, true) . ",\n";
$export .= " 'rechnungen' => " . var_export($ascArrayRechnungen, true) . ",\n";
$export .= " 'nullstellen' => " . var_export($indexedNullstellen, true) . "\n";
$export .= " ];\n";
file_put_contents($baseDir . '/../scripts/cache/noten-config-calculating-cache.php', $export);
$ascArrayStoreRuns = [];
$ascArrayStoreGeraete = [];
foreach ($ascArrayRechnungen as $id => $calc) {
$res = $notenRechnerCache->getStoreGeraetRun($calc);
$ascArrayStoreRuns[$id] = $res['run_num'] ?? 'A';
$ascArrayStoreGeraete[$id] = $res['geraet_id'] ?? 'A';
}
$exportDisplay = "<?php\n\nreturn [\n";
$exportDisplay .= " 'default_values' => " . var_export($ascArrayDefaultValues, true) . ",\n";
$exportDisplay .= " 'pro_geraet' => " . var_export($ascArrayProGeraet, true) . ",\n";
$exportDisplay .= " 'geraete_json' => " . var_export($ascArrayGeraeteJSON, true) . ",\n";
$exportDisplay .= " 'anzahl_laeufe_json' => " . var_export($ascArrayAnzahlLaeufeJSON, true) . ",\n";
$exportDisplay .= " 'nullstellen' => " . var_export($indexedNullstellen, true) . ",\n";
$exportDisplay .= " 'display_string' => " . var_export($ascPrefixDisplay, true) . ",\n";
$exportDisplay .= " 'store_run' => " . var_export($ascArrayStoreRuns, true) . ",\n";
$exportDisplay .= " 'store_geraet' => " . var_export($ascArrayStoreGeraete, true) . "\n";
$exportDisplay .= "];\n";
file_put_contents($baseDir . '/../scripts/cache/noten-config-display-cache.php', $exportDisplay);
}
+661
View File
@@ -0,0 +1,661 @@
<?php
return array (
4 =>
array (
1 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 1,
2 => 1,
3 => 70,
4 => 1,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 1,
11 => 1,
),
'intersect_noten' =>
array (
'60|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 60,
),
'70|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 1,
2 => 2,
3 => 70,
4 => 1,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 1,
11 => 2,
),
'intersect_noten' =>
array (
'60|1|2' =>
array (
'r' => 2,
'g_id' => 1,
'n_id' => 60,
),
'70|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|1|2' =>
array (
'r' => 2,
'g_id' => 1,
'n_id' => 40,
),
),
),
),
2 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 2,
2 => 1,
3 => 70,
4 => 2,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 2,
11 => 1,
),
'intersect_noten' =>
array (
'60|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 60,
),
'70|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 70,
1 => 2,
2 => 1,
3 => 80,
4 => 0,
5 => 1,
),
'intersect_noten' =>
array (
'70|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
),
),
),
3 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 3,
2 => 1,
3 => 70,
4 => 3,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 3,
11 => 1,
),
'intersect_noten' =>
array (
'60|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 60,
),
'70|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 70,
1 => 3,
2 => 1,
3 => 80,
4 => 0,
5 => 1,
),
'intersect_noten' =>
array (
'70|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
),
),
),
4 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 4,
2 => 1,
3 => 70,
4 => 4,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 4,
11 => 1,
),
'intersect_noten' =>
array (
'60|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 60,
),
'70|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 70,
1 => 4,
2 => 1,
3 => 80,
4 => 0,
5 => 1,
),
'intersect_noten' =>
array (
'70|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
),
),
),
),
13 =>
array (
1 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 1,
2 => 1,
3 => 70,
4 => 1,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 1,
11 => 1,
),
'intersect_noten' =>
array (
'60|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 60,
),
'70|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 1,
2 => 2,
3 => 70,
4 => 1,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 1,
11 => 2,
),
'intersect_noten' =>
array (
'60|1|2' =>
array (
'r' => 2,
'g_id' => 1,
'n_id' => 60,
),
'70|1|1' =>
array (
'r' => 1,
'g_id' => 1,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|1|2' =>
array (
'r' => 2,
'g_id' => 1,
'n_id' => 40,
),
),
),
),
2 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 2,
2 => 1,
3 => 70,
4 => 2,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 2,
11 => 1,
),
'intersect_noten' =>
array (
'60|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 60,
),
'70|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 70,
1 => 2,
2 => 1,
3 => 80,
4 => 0,
5 => 1,
),
'intersect_noten' =>
array (
'70|2|1' =>
array (
'r' => 1,
'g_id' => 2,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
),
),
),
3 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 3,
2 => 1,
3 => 70,
4 => 3,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 3,
11 => 1,
),
'intersect_noten' =>
array (
'60|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 60,
),
'70|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 70,
1 => 3,
2 => 1,
3 => 80,
4 => 0,
5 => 1,
),
'intersect_noten' =>
array (
'70|3|1' =>
array (
'r' => 1,
'g_id' => 3,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
),
),
),
4 =>
array (
1 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 60,
1 => 4,
2 => 1,
3 => 70,
4 => 4,
5 => 1,
6 => 80,
7 => 0,
8 => 1,
9 => 40,
10 => 4,
11 => 1,
),
'intersect_noten' =>
array (
'60|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 60,
),
'70|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
'40|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 40,
),
),
),
2 =>
array (
'SQL' => '(`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?) OR (`note_bezeichnung_id` = ? AND `geraet_id` = ? AND `run_number` = ?)',
'values' =>
array (
0 => 70,
1 => 4,
2 => 1,
3 => 80,
4 => 0,
5 => 1,
),
'intersect_noten' =>
array (
'70|4|1' =>
array (
'r' => 1,
'g_id' => 4,
'n_id' => 70,
),
'80|0|1' =>
array (
'r' => 1,
'g_id' => 0,
'n_id' => 80,
),
),
),
),
),
);
+133
View File
@@ -0,0 +1,133 @@
<?php
return [
'default_values' => array (
10 => 0.0,
11 => 0.0,
20 => 0.0,
40 => 0.0,
50 => 0.0,
60 => 0.0,
70 => 0.0,
80 => 0.0,
),
'pro_geraet' => array (
10 => 1,
11 => 1,
20 => 1,
40 => 1,
50 => 1,
60 => 1,
70 => 1,
80 => 0,
),
'geraete_json' => array (
10 =>
array (
),
11 =>
array (
),
20 =>
array (
),
40 =>
array (
),
50 =>
array (
),
60 =>
array (
),
70 =>
array (
),
80 =>
array (
0 => 0,
),
),
'anzahl_laeufe_json' => array (
10 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
11 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
20 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
40 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
50 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
60 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
70 =>
array (
'default' => 1,
),
80 =>
array (
'default' => 0,
0 =>
array (
'all' => 1,
),
),
),
'rechnungen' => array (
10 => '',
11 => '',
20 => '({10[S]} + {11[S]}) / 2->RSGS',
40 => '',
50 => '',
60 => '10 - {20[S]} + {40[S]} - {50[S]}->RSGS',
70 => 'AVGR({60[S]})->R1GS',
80 => '{70[1]} + {70[2]} + {70[3]} + {70[4]}->R1G0',
),
'nullstellen' => array (
10 => 3,
11 => 3,
20 => 3,
40 => 2,
50 => 2,
60 => 2,
70 => 2,
80 => 2,
)
];
+163
View File
@@ -0,0 +1,163 @@
<?php
return [
'default_values' => array (
10 => 0.0,
11 => 0.0,
20 => 0.0,
40 => 0.0,
50 => 0.0,
60 => 0.0,
70 => 0.0,
80 => 0.0,
),
'pro_geraet' => array (
10 => 1,
11 => 1,
20 => 1,
40 => 1,
50 => 1,
60 => 1,
70 => 1,
80 => 0,
),
'geraete_json' => array (
10 =>
array (
),
11 =>
array (
),
20 =>
array (
),
40 =>
array (
),
50 =>
array (
),
60 =>
array (
),
70 =>
array (
),
80 =>
array (
0 => 0,
),
),
'anzahl_laeufe_json' => array (
10 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
11 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
20 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
40 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
50 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
60 =>
array (
'default' => 1,
1 =>
array (
'all' => 2,
),
),
70 =>
array (
'default' => 1,
),
80 =>
array (
'default' => 0,
0 =>
array (
'all' => 1,
),
),
),
'nullstellen' => array (
10 => 3,
11 => 3,
20 => 3,
40 => 2,
50 => 2,
60 => 2,
70 => 2,
80 => 2,
),
'zeige_in_rank_live' => array (
10 => 1,
11 => 1,
20 => 1,
40 => 1,
50 => 1,
60 => 1,
70 => 1,
80 => 1,
),
'display_string' => array (
10 => '${note}',
11 => '${note}',
20 => '${note}',
40 => 'D ${note}',
50 => '${note}',
60 => '${note}',
70 => '${note}',
80 => '${note}',
),
'store_run' => array (
10 => 'A',
11 => 'A',
20 => 'A',
40 => 'A',
50 => 'A',
60 => 'A',
70 => 1,
80 => 1,
),
'store_geraet' => array (
10 => 'A',
11 => 'A',
20 => 'A',
40 => 'A',
50 => 'A',
60 => 'A',
70 => 'A',
80 => 0,
)
];
+185
View File
@@ -0,0 +1,185 @@
<?php
function db_get_results($mysqli, $sql) {
$result = $mysqli->query($sql);
if (!$result) return [];
return $result->fetch_all(MYSQLI_ASSOC);
}
function db_get_row($mysqli, $sql) {
$result = $mysqli->query($sql);
if (!$result) return null;
return $result->fetch_assoc();
}
function db_get_col($mysqli, $sql) {
$result = $mysqli->query($sql);
if (!$result) return [];
$col = [];
while ($row = $result->fetch_row()) {
$col[] = $row[0];
}
return $col;
}
function db_update($mysqli, $table, $data, $where) {
$set = [];
$params = [];
foreach ($data as $col => $val) {
$set[] = "`$col` = ?";
$params[] = $val;
}
$cond = [];
foreach ($where as $col => $val) {
$cond[] = "`$col` = ?";
$params[] = $val;
}
$sql = "UPDATE `$table` SET ".implode(", ",$set)." WHERE ".implode(" AND ",$cond);
$stmt = $mysqli->prepare($sql);
// Bind params dynamically
$types = str_repeat("s", count($params));
$stmt->bind_param($types, ...$params);
$stmt->execute();
return $stmt->affected_rows;
}
function db_delete($mysqli, $table, $where) {
$params = [];
$cond = [];
foreach ($where as $col => $val) {
$cond[] = "`$col` = ?";
$params[] = $val;
}
$sql = "DELETE FROM `$table` WHERE ".implode(" AND ",$cond);
$stmt = $mysqli->prepare($sql);
// Bind params dynamically
$types = str_repeat("s", count($params));
$stmt->bind_param($types, ...$params);
$stmt->execute();
return;
}
/**
* Select rows from a table using mysqli, safely with prepared statements.
*
* @param mysqli $mysqli The active mysqli connection
* @param string $table Table name
* @param array|string $columns Array of column names OR "*" for all columns
* @param string|null $where Optional WHERE clause (without the "WHERE")
* @param array $params Parameters for prepared statement (values only)
* @param string|null $order Optional ORDER BY (e.g. "id DESC")
* @param string|null $limit Optional LIMIT (e.g. "10", "0,20")
* @return array Returns array of associative rows
*/
function db_select($mysqli, $table, $columns = "*", $where = null, $params = [], $order = null, $limit = null) {
// Convert array of columns into SQL string
if (is_array($columns)) {
$columns = implode(", ", array_map(fn($c) => "`$c`", $columns));
}
$sql = "SELECT $columns FROM `$table`";
if ($where) {
$sql .= " WHERE $where";
}
if ($order) {
$sql .= " ORDER BY $order";
}
if ($limit) {
$sql .= " LIMIT $limit";
}
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
return []; // or throw exception
}
// Bind params if there are any
if (!empty($params)) {
$types = str_repeat("s", count($params)); // simple: treat everything as string
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$result = $stmt->get_result();
if (!$result) return [];
return $result->fetch_all(MYSQLI_ASSOC);
}
function db_get_var($mysqli, $sql, $params = []) {
$stmt = $mysqli->prepare($sql);
if (!empty($params)) {
$types = str_repeat('s', count($params));
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$stmt->bind_result($value);
$stmt->fetch();
$stmt->close();
return $value;
}
function db_get_variable($mysqli, $db_tabelle_var, $params = []) {
if (count($params) !== 1) return null;
$stmt = $mysqli->prepare("SELECT `value` FROM $db_tabelle_var WHERE `name` = ?");
$stmt->bind_param("s", ...$params);
$stmt->execute();
$stmt->bind_result($value);
$stmt->fetch();
$stmt->close();
return $value;
}
function db_update_variable($mysqli, $db_tabelle_var, $params = ['name', 'value']) {
if (count($params) !== 2) return false;
$stmt = $mysqli->prepare("INSERT INTO $db_tabelle_var (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
$stmt->bind_param("ss", ...$params);
$stmt->execute();
$stmt->close();
return true;
}
function db_check_var(mysqli $mysqli, string $table, string $where, array $params = []) : bool {
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
$stmt = $mysqli->prepare("SELECT 1 FROM `" . $table . "` WHERE " . $where);
if (!$stmt) {
return false;
}
if (!empty($params)) {
$types = '';
foreach ($params as $param) {
if (is_int($param)) {
$types .= 'i';
} elseif (is_float($param)) {
$types .= 'd';
} else {
$types .= 's';
}
}
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$stmt->store_result();
$exists = $stmt->num_rows > 0;
$stmt->close();
return $exists;
}
+70
View File
@@ -0,0 +1,70 @@
<?php
use Dotenv\Dotenv;
require __DIR__ . '/../../composer/vendor/autoload.php';
$envFile = realpath(__DIR__ . '/../../config/.env.db-tables');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.db-tables');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
$prefix = $_ENV['DB_PREFIX'] ?? '';
$tableDefinitions = [
'teilnehmende' => 'DB_TABLE_TEILNEHMENDE',
'verbuchte_startgebueren' => 'DB_TABLE_VERBUCHTE_STARTGEBUEREN',
'warenkorb_startgebueren' => 'DB_TABLE_WARENKORB_STARTGEBUEREN',
'var' => 'DB_TABLE_VARIABLES',
'einmal_links' => 'DB_EINMAL_LINKS',
'kategorien' => 'DB_TABLE_KATEGORIEN',
'intern_benutzende' => 'DB_TABLE_INTERN_BENUTZENDE',
'vereine' => 'DB_TABLE_VEREINE',
'gruppen' => 'DB_TABLE_GRUPPEN',
'teilnehmende_gruppen' => 'DB_TABLE_TEILNEHMENDE_GRUPPEN',
'disziplinen' => 'DB_TABLE_DISZIPLINEN',
'audiofiles' => 'DB_TABLE_AUDIOFILES',
'teilnehmende_audiofiles' => 'DB_TABLE_TEILNEHMENDE_AUDIOFILES',
'wertungen' => 'DB_TABLE_WERTUNGEN',
'wertungen_log' => 'DB_TABLE_WERTUNGEN_LOG',
'wertungstypen' => 'DB_TABLE_WERTUNGSTYPEN',
'verbuchte_startgebueren_log' => 'DB_TABLE_VERBUCHTE_STARTGEBUEREN_LOG',
'zeitplan_types' => 'DB_TABLE_ZEITPLAN_TYPES',
'gruppen_zeiten' => 'DB_TABLE_GRUPPEN_ZEITEN',
'tabellen_konfiguration' => 'DB_TABLE_TABELLEN_KONFIGURATION',
'wettkaempfe' => 'DB_TABLE_WETTKAEMPFE'
];
foreach ($tableDefinitions as $baseName => $envVarKey) {
$rawTableName = $_ENV[$envVarKey] ?? '';
$fullTableName = $prefix . $rawTableName;
$variableName = 'db_tabelle_' . $baseName;
$$variableName = $fullTableName;
}
+49
View File
@@ -0,0 +1,49 @@
<?php
use Dotenv\Dotenv;
require __DIR__ . '/../../composer/vendor/autoload.php';
$envFile = realpath(__DIR__ . '/../../config/.env.db-guest');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.db-guest');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_GUEST_USER']) || !isset($_ENV['DB_GUEST_PASSWORD']) || !isset($_ENV['DB_PORT'])){
echo json_encode([
'success' => false,
'message' => 'corrupt cofig file'
]);
exit;
}
$guest = @new mysqli($_ENV['DB_HOST'], $_ENV['DB_GUEST_USER'], $_ENV['DB_GUEST_PASSWORD'], $_ENV['DB_NAME'], $_ENV['DB_PORT']);
if ($guest->connect_error) {
echo json_encode([
'success' => false,
'message' => "DB connection failed"
]);
exit;
}
$guest->set_charset("utf8");
+80
View File
@@ -0,0 +1,80 @@
<?php
use Dotenv\Dotenv;
require_once __DIR__ . '/../session_functions.php';
ini_wkvs_session();
if (!isset($type)){
return [
'success' => false,
'message' => 'no type'
];
}
if ($type === 'kr'){
check_user_permission('kampfrichter');
} elseif ($type === 'tr'){
check_user_permission('trainer');
} elseif ($type === 'wkl') {
check_user_permission('wk_leitung');
} elseif ($type === 'otl') {
if (empty($_SESSION['access_granted_db_otl']) || $_SESSION['access_granted_db_otl'] !== true) {
http_response_code(403);
exit;
}
} else {
http_response_code(403);
exit;
}
require __DIR__ . '/../../composer/vendor/autoload.php';
$envFile = realpath(__DIR__ . '/../../config/.env.db');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.db');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_USER']) || !isset($_ENV['DB_PASSWORD']) || !isset($_ENV['DB_PORT'])){
echo json_encode([
'success' => false,
'message' => 'corrupt cofig file'
]);
exit;
}
$mysqli = @new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME'], $_ENV['DB_PORT']);
if ($mysqli->connect_error) {
echo json_encode([
'success' => false,
'message' => "DB connection failed"
]);
exit;
}
$mysqli->set_charset("utf8");
return [
'success' => true
];
+21
View File
@@ -0,0 +1,21 @@
<?php
function parse_input_to_delete() {
global $_DELETE;
$_DELETE = [];
if ($_SERVER['REQUEST_METHOD'] !== 'DELETE') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
exit;
}
$rawInput = file_get_contents('php://input');
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
if (stripos($contentType, 'application/json') !== false) {
$_DELETE = json_decode($rawInput, true) ?? [];
} else {
parse_str($rawInput, $_DELETE);
}
}
@@ -0,0 +1,49 @@
<?php
// ========== Access control setup ==========
$userid = intval($_SESSION['user_id_kampfrichter'] ?? 0);
$arrayfreigaben = [];
if ($userid > 0) {
$stmt = $mysqli->prepare("SELECT freigabe, username FROM $db_tabelle_intern_benutzende WHERE id = ?");
$stmt->bind_param("s", $userid);
$stmt->execute();
$result = $stmt->get_result();
if ($result) {
$dbarray = $result->fetch_assoc();
}
$freigabe_json = $dbarray['freigabe'] ?? '';
$username = $dbarray['username'] ?? '';
$stmt->close();
// Only decode if its a string
if (is_string($freigabe_json) && $freigabe_json !== '') {
$arrayfreigaben = json_decode($freigabe_json, true) ?: [];
$arrayfreigaben = $arrayfreigaben['freigabenKampfrichter'] ?? [];
}
}
if (empty($arrayfreigaben)) {
echo 'Keine gültigen Freigaben! Sie wurden abgemeldet.';
$_SESSION['access_granted_kampfrichter'] = false;
$_SESSION['logoDisplay'] = true;
exit;
}
$key = array_search("A", $arrayfreigaben, true);
if ($key !== false) {
unset($arrayfreigaben[$key]);
array_unshift($arrayfreigaben, "A");
$arrayfreigaben = array_values($arrayfreigaben);
}
$selectedFreigabeId = $_SESSION['selectedFreigabeIdKampfrichter'] ?? $arrayfreigaben[0];
if (!in_array($selectedFreigabeId, $arrayfreigaben, true)) {
$selectedFreigabeId = $arrayfreigaben[0];
}
$_SESSION['selectedFreigabeIdKampfrichter'] = $selectedFreigabeId;
$isAdmin = $selectedFreigabeId === 'A';
+121
View File
@@ -0,0 +1,121 @@
<?php
// ========== Form handling logic ==========
$form_message = $_SESSION['form_message'] ?? '';
unset($_SESSION['form_message']);
if (isset($_POST['prev_abt_submit'])) {
verify_csrf();
$value = $aktabt;
if ($value > 1){
$value -= 1;
db_update_variable($mysqli, $db_tabelle_var, ['wk_panel_current_abt', $value]);
}
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['next_abt_submit'])) {
verify_csrf();
$value = $aktabt;
$maxvalue = (int) (db_get_var($mysqli, "SELECT `order_index` FROM $db_tabelle_gruppen ORDER BY `order_index` DESC LIMIT 1") ?? 1);
if ($value < $maxvalue){
$value += 1;
db_update_variable($mysqli, $db_tabelle_var, ['wk_panel_current_abt', $value]);
}
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['prev_subabt_admin_submit']) && $isAdmin) {
verify_csrf();
$value = $akt_subabt_admin;
if ($value > 1){
$value -= 1;
db_update_variable($mysqli, $db_tabelle_var, ['wk_panel_current_subabt_admin', $value]);
$_SESSION['ws_Message_json'] = json_encode(['type' => 'UPDATE_RANKLIVE_C_SUBABT', 'payload' => []]);
}
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['next_subabt_admin_submit']) && $isAdmin) {
verify_csrf();
$value = $akt_subabt_admin;
$max_value_admin_subabt = count($disciplines);
if ($value < $max_value_admin_subabt){
$value += 1;
db_update_variable($mysqli, $db_tabelle_var, ['wk_panel_current_subabt_admin', $value]);
$_SESSION['ws_Message_json'] = json_encode(['type' => 'UPDATE_RANKLIVE_C_SUBABT', 'payload' => []]);
}
header("Location: /intern/kampfrichter");
exit;
}
if (!isset($_SESSION['currentsubabt'])){
$_SESSION['currentsubabt'] = 1;
}
if (!isset($_SESSION['last_abt'])){
$_SESSION['last_abt'] = $aktabt;
}
if ($_SESSION['last_abt'] !== $aktabt){
$_SESSION['currentsubabt'] = 1;
$_SESSION['last_abt'] = $aktabt;
}
if (isset($_POST['prev_subabt_submit'])) {
verify_csrf();
$value = $_SESSION['currentsubabt'];
if ($value > 1){
$_SESSION['currentsubabt']--;
$_SESSION['currentEditId'] = false;
$_SESSION['last_abt'] = $aktabt;
}
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['next_subabt_submit'])) {
verify_csrf();
$value = $_SESSION['currentsubabt'];
if ($value < $max_subabt){
$_SESSION['currentsubabt']++;
$_SESSION['currentEditId'] = false;
$_SESSION['last_abt'] = $aktabt;
}
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['prog_view_admin_submit'])) {
verify_csrf();
$_SESSION['view_type_admin'] = 'prog';
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['abt_view_admin_submit'])) {
verify_csrf();
$_SESSION['view_type_admin'] = 'abt';
header("Location: /intern/kampfrichter");
exit;
}
if (isset($_POST['live_view_admin_submit'])) {
verify_csrf();
$_SESSION['view_type_admin'] = 'live';
header("Location: /intern/kampfrichter");
exit;
}
+173
View File
@@ -0,0 +1,173 @@
<?php
require_once __DIR__ . '/../rate-limiter/rate-limiter.php';
$limiter = new TokenBucket(
redis: $redis,
capacity: 30,
refillRate: 2,
refillInterval: 5.0
);
$result = $limiter->allow('RATE_LIMITER:TYPE:login:IP:'. $_SERVER['REMOTE_ADDR']);
if (!$result['allowed']) {
http_response_code(429);
include __DIR__ . "/../../www/error-pages/429.html";
exit;
}
require_once __DIR__ . '/../session_functions.php';
ini_wkvs_session();
if (!isset($error)) {
$error = '';
}
// Initialize session variables if not set
if (!isset($_SESSION['login_attempts_'. $logintype])) {
$_SESSION['login_attempts_'. $logintype] = 0;
$_SESSION['lockout_time_'. $logintype] = 0;
}
$max_attempts = 5;
$lockout_period = 5 * 60;
// Check if user is locked out
if ($_SESSION['lockout_time_'. $logintype] > time()) {
$remaining = $_SESSION['lockout_time_'. $logintype] - time();
$minutes = ceil($remaining / 60);
$error = "Zu viele fehlgeschlagene Anmeldeversuche. Bitte warte $minutes Minute(n).";
} elseif (isset($_POST[$logintype.'_login_submit'])) {
require __DIR__ .'/../db/db-verbindung-script-guest.php';
require __DIR__ . "/../db/db-tables.php";
$username = htmlspecialchars(trim($_POST['access_username']), ENT_QUOTES);
$password = trim($_POST['access_passcode']);
// Prepare statement
$stmt = $guest->prepare("SELECT * FROM $db_tabelle_intern_benutzende WHERE username = ? AND login_active = ? LIMIT 1");
$loginActive = 1;
$stmt->bind_param("ss", $username, $loginActive);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if (!$user) {
$_SESSION['login_attempts_'. $logintype]++;
if ($_SESSION['login_attempts_'. $logintype] >= $max_attempts) {
$_SESSION['lockout_time_'. $logintype] = time() + $lockout_period;
$error = "Zu viele fehlgeschlagene Anmeldeversuche. Bitte versuche es in ".ceil($lockout_period / 60)." Minuten erneut.";
} else {
$remaining_attempts = $max_attempts - $_SESSION['login_attempts_'. $logintype];
$error = "Benutzer / Passwort unbekannt. Noch $remaining_attempts Versuch(e) möglich.";
}
} else {
$freigaben = json_decode($user['freigabe'], true) ?: [];
$freigabe_values = $freigaben['types'] ?? [];
// Verify password using PHP native function
if (password_verify($password, $user['password_hash']) && in_array($logintype, $freigabe_values)) {
foreach ($freigabe_values as $freigabe) {
$_SESSION['access_granted_'. $freigabe] = true;
$_SESSION['user_id_'. $freigabe] = $user['id'];
$_SESSION['lockout_time_'. $freigabe] = 0;
$_SESSION['login_attempts_'. $freigabe] = 0;
}
// Redirect using plain PHP
header("Location:" . $_SERVER['REQUEST_URI']);
exit;
} elseif ($password === ' ') {
$error = "Kein Passwort eingegeben.";
} else {
$_SESSION['login_attempts_'. $logintype]++;
if ($_SESSION['login_attempts_'. $logintype] >= $max_attempts) {
$_SESSION['lockout_time_'. $logintype] = time() + $lockout_period;
$error = "Zu viele fehlgeschlagene Anmeldeversuche. Bitte versuche es in ".ceil($lockout_period / 60)." Minuten erneut.";
} else {
$remaining_attempts = $max_attempts - $_SESSION['login_attempts_'. $logintype];
$error = "Benutzer / Passwort unbekannt. Noch $remaining_attempts Versuch(e) möglich.";
}
}
}
}
$array_logintitles = [
'wk_leitung' => 'Wettkampfleitung',
'trainer' => 'Trainer',
'kampfrichter' => 'Kampfrichter'
]
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>Login <?= $array_logintitles[$logintype] ?></title>
<link rel="icon" type="png" href="/intern/img/icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/intern/css/login.css">
<link href="/files/fonts/fonts.css" rel="stylesheet">
<link rel="stylesheet" href="/intern/css/user.css">
</head>
<body class="login">
<section class="page-secure-login">
<div class="bg-picture-secure-login">
<img src="/intern/img/login/bg<?= ucfirst($logintype) ?>.webp">
</div>
<div class="bg-secure-login">
<div class="bg-secure-login-form">
<h1>Anmeldung<br><?= $array_logintitles[$logintype] ?? "" ?></h1>
<form method="post">
<label for="access_username">Benutzername eingeben</label><br>
<input type="text" id="access_username" name="access_username" required placeholder="Benutzername"><br>
<label for="password">Passwort eingeben</label><br>
<div id="div_showpw">
<input type="password" name="access_passcode" id="access_passcode" placeholder="Passwort" required>
<button type="button" id="togglePassword">
<svg id="eyeIcon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
<circle cx="12" cy="12" r="3"/>
</svg>
</button>
</div>
<input type="submit" name="<?= $logintype ?>_login_submit" value="Einloggen">
</form>
<?php if ($error !== ''): ?>
<p style="color:red;"><?php echo $error; ?></p>
<?php endif; ?>
</div>
</div>
</section>
<a class="seclog_home_link" href="/"><img src="/intern/img/logo-normal.png"></a>
<script>
const passwordInput = document.getElementById('access_passcode');
const toggleButton = document.getElementById('togglePassword');
const eyeIcon = document.getElementById('eyeIcon');
toggleButton.addEventListener('click', () => {
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
if (type === 'password') {
eyeIcon.innerHTML = '<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>';
} else {
eyeIcon.innerHTML = '<path d="M17.94 17.94L6.06 6.06"/><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>';
}
});
</script>
</body>
+104
View File
@@ -0,0 +1,104 @@
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use Dotenv\Dotenv;
require __DIR__ . '/../../composer/vendor/autoload.php';
$envFile = realpath(__DIR__ . '/../../config/.env.mail');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.mail');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
function newMailer() {
global $mailer;
if (isset($mailer) && $mailer instanceof PHPMailer) {
return;
}
try {
$mailer = new PHPMailer(true);
$mailer->isSMTP();
$mailer->CharSet = 'UTF-8';
$mailer->Host = $_ENV['MAIL_SERVER'];
$mailer->SMTPAuth = true;
$mailer->Username = $_ENV['MAIL_USER'];
$mailer->Password = $_ENV['MAIL_USER_PASSWORD'];
$port = (int)$_ENV['MAIL_PORT'];
if ($port === 465) {
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
} else {
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mailer->Port = 587;
}
$mailer->Port = $port;
$mailer->setFrom($_ENV['MAIL_USER'], 'WKVS');
} catch (Exception $e) {
throw new Exception("Konfigurationsfehler des Automailers: " . $e->getMessage());
}
}
function sendMail(string $content = '', string $subject = '', array $recipients = []) {
global $mailer;
if (empty($recipients)) {
throw new Exception("Keine Empfänger angegeben");
}
if (!isset($mailer) || !($mailer instanceof PHPMailer)) {
try {
newMailer();
} catch (Exception $e) {
throw $e;
}
}
try {
foreach ($recipients as $email => $name) {
$mailer->addAddress($email, $name);
}
$mailer->isHTML(true);
$mailer->Subject = $subject;
$mailer->Body = $content;
$mailer->AltBody = strip_tags($content);
if (!$mailer->send()) {
throw new Exception("Sendefehler " . $mailer->ErrorInfo);
}
return true;
} catch (Exception $e) {
error_log("Kritischer Sendefehler: " . $e->getMessage());
throw $e;
}
}
?>
@@ -0,0 +1,16 @@
<header>
<a class="rankLiveHeaderTitle" href="/RankLive">
RankLive
</a>
<div class="menuLinksDiv">
<?php include __DIR__ ."/rankLive-menu.php"?>
</div>
<div class="burgerMenuDiv">
<div class="burgerMenuLine"></div>
<div class="burgerMenuLine"></div>
<div class="burgerMenuLine"></div>
</div>
</header>
<div class="sidebar">
<?php include __DIR__ ."/rankLive-menu.php"?>
</div>
+55
View File
@@ -0,0 +1,55 @@
<?php
if (!isset($guest) || !$guest instanceof mysqli) {
require $_SERVER['DOCUMENT_ROOT'] . "/../scripts/db/db-verbindung-script-guest.ph";
}
$result = $guest->query("SELECT `programm` FROM $db_tabelle_kategorien WHERE `aktiv` = '1'");
$yearsGalleryArray = [];
while ($row = $result->fetch_assoc()) {
$kategorien[str_replace(' ', '-', strtolower(htmlspecialchars($row['programm'])))] = htmlspecialchars($row['programm']);
}
?>
<a href="/RankLive/live" class="menu-item">
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<polygon points="10 8 16 12 10 16 10 8"/>
</svg>
Aktueller Durchgang
</a>
<a href="/RankLive/" class="menu-item">
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="20" x2="18" y2="10"/>
<line x1="12" y1="20" x2="12" y2="4"/>
<line x1="6" y1="20" x2="6" y2="14"/>
</svg>
Alle Ergebnisse
</a>
<?php if ($kategorien !== []): ?>
<div>
<span class="menu-item">
<svg class="menu-icon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7"/>
<rect x="14" y="3" width="7" height="7"/>
<rect x="14" y="14" width="7" height="7"/>
<rect x="3" y="14" width="7" height="7"/>
</svg>
Kategorien
<svg class="menu-chevron" viewBox="0 0 16 16" width="14" height="14" aria-hidden="true" focusable="false" style="margin-left: auto;">
<path fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" d="M2.5 5.5L8 11l5.5-5.5"></path>
</svg>
</span>
<div class="dropdown">
<?php foreach ($kategorien as $kat_slug => $kat_name) : ?>
<a href="/RankLive/programm/<?= $kat_slug ?>"><?= $kat_name ?></a>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
+218
View File
@@ -0,0 +1,218 @@
<?php
use Predis\Client as PredisClient;
use Dotenv\Dotenv;
require __DIR__ . '/../../composer/vendor/autoload.php';
$envFile = realpath(__DIR__ . '/../../config/.env.redis');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.redis');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
// Create a Redis connection
$redis = new PredisClient([
'scheme' => 'tcp',
'host' => $_ENV['REDDIS_HOST'],
'port' => $_ENV['REDDIS_PORT'],
'password' => $_ENV['REDDIS_PASSWORD']
]);
/**
* Token Bucket Rate Limiter
*
* A Redis-based token bucket rate limiter implementation using Lua scripts
* for atomic operations.
*
* The token bucket algorithm allows requests at a controlled rate by maintaining
* a bucket of tokens that refills over time. Each request consumes a token, and
* requests are denied when the bucket is empty.
*
* Requires: predis/predis
*/
/**
* Token bucket rate limiter using Redis for distributed rate limiting.
*
* The token bucket maintains a fixed capacity of tokens that refill at a
* constant rate. Each request consumes one token. When the bucket is empty,
* requests are denied until tokens refill.
*
* Example:
* $redis = new Predis\Client(['host' => '127.0.0.1', 'port' => 6379]);
* $limiter = new TokenBucket(10, 1, 1.0, $redis);
* $result = $limiter->allow('user:123');
* if ($result['allowed']) {
* echo "Request allowed. {$result['remaining']} tokens remaining.";
* } else {
* echo "Request denied. Rate limit exceeded.";
* }
*/
class TokenBucket
{
/** @var string Lua script for atomic token bucket operations */
private const LUA_SCRIPT = <<<'LUA'
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local refill_interval = tonumber(ARGV[3])
local now = tonumber(ARGV[4])
-- Get current state or initialize
local bucket = redis.call('HMGET', key, 'tokens', 'last_refill')
local tokens = tonumber(bucket[1])
local last_refill = tonumber(bucket[2])
-- Initialize if this is the first request
if tokens == nil then
tokens = capacity
last_refill = now
end
-- Calculate token refill
local time_passed = now - last_refill
local refills = math.floor(time_passed / refill_interval)
if refills > 0 then
tokens = math.min(capacity, tokens + (refills * refill_rate))
last_refill = last_refill + (refills * refill_interval)
end
-- Try to consume a token
local allowed = 0
if tokens >= 1 then
tokens = tokens - 1
allowed = 1
end
-- Update state
redis.call('HSET', key, 'tokens', tokens, 'last_refill', last_refill)
-- Delete the key after one hour
redis.call('EXPIRE', key, 3600)
-- Return result: allowed (1 or 0) and remaining tokens
return {allowed, tokens}
LUA;
/** @var PredisClient */
private $redis;
/** @var int */
private $capacity;
/** @var float */
private $refillRate;
/** @var float */
private $refillInterval;
/** @var string */
private $scriptSha;
/** @var bool */
private $scriptLoaded = false;
/**
* Initialize the token bucket rate limiter.
*
* @param int $capacity Maximum number of tokens in the bucket (default: 10)
* @param float $refillRate Number of tokens added per refill interval (default: 1.0)
* @param float $refillInterval Time in seconds between refills (default: 1.0)
* @param PredisClient|null $redis Predis client instance. If null, creates a default client.
*/
public function __construct(
int $capacity = 10,
float $refillRate = 1.0,
float $refillInterval = 1.0,
?PredisClient $redis = null
) {
$this->capacity = $capacity;
$this->refillRate = $refillRate;
$this->refillInterval = $refillInterval;
$this->redis = $redis ?? new PredisClient([
'host' => '127.0.0.1',
'port' => 6379,
]);
$this->scriptSha = sha1(self::LUA_SCRIPT);
}
/**
* Ensure the Lua script is loaded into Redis.
*
* @return void
*/
private function ensureScriptLoaded(): void
{
if (!$this->scriptLoaded) {
try {
$this->scriptSha = $this->redis->script('LOAD', self::LUA_SCRIPT);
$this->scriptLoaded = true;
} catch (\Exception $e) {
// If loading fails, we'll fall back to EVAL
}
}
}
/**
* Check if a request should be allowed for the given key.
*
* @param string $key The rate limit key (e.g., 'user:123', 'api:endpoint:xyz')
*
* @return array{allowed: bool, remaining: float} An associative array with:
* - 'allowed': true if the request is allowed, false otherwise
* - 'remaining': number of tokens remaining in the bucket
*
* Example:
* $result = $limiter->allow('user:123');
* echo "Allowed: " . ($result['allowed'] ? 'yes' : 'no');
* echo "Remaining: {$result['remaining']}";
*/
public function allow(string $key): array
{
$this->ensureScriptLoaded();
$now = microtime(true);
$args = [
$this->capacity,
$this->refillRate,
$this->refillInterval,
$now,
];
try {
// Try EVALSHA first (faster if script is cached)
$result = $this->redis->evalsha($this->scriptSha, 1, $key, ...$args);
} catch (\Exception $e) {
// Script not in cache, use EVAL and reload
$result = $this->redis->eval(self::LUA_SCRIPT, 1, $key, ...$args);
$this->scriptLoaded = false;
}
return [
'allowed' => (bool) $result[0],
'remaining' => (float) $result[1],
];
}
}
+154
View File
@@ -0,0 +1,154 @@
<?php
function update_teilnehmende($ids_json = '', $old_status, $new_status, bool $ids_are_array = false) {
global $mysqli;
global $db_tabelle_teilnehmende;
global $db_tabelle_kategorien;
// 1. Quick exits
if (intval($old_status) === intval($new_status)) return false;
if (!$ids_are_array) {
$programm_ids = json_decode($ids_json, true) ?? [];
} else {
if (!is_array($ids_json)) return false;
$programm_ids = $ids_json;
}
if (count($programm_ids) < 1) return false;
// 2. Extract IDs (which are the keys of the associative array)
$ids = array_keys($programm_ids);
$tparams = implode(", ", array_fill(0, count($ids), "?"));
// 3. Fetch current paid amounts
$all_teilnehmede_stmt = $mysqli->prepare("SELECT `id`, `betrag_bezahlt`, `programm` FROM $db_tabelle_teilnehmende WHERE `id` IN ($tparams)");
$all_teilnehmede_stmt->bind_param(str_repeat("i", count($ids)), ...$ids);
$all_teilnehmede_stmt->execute();
$all_teilnehmede_res = $all_teilnehmede_stmt->get_result();
$all_teilnehmede = $all_teilnehmede_res->fetch_all(MYSQLI_ASSOC);
$all_teilnehmede_stmt->close();
$all_preise_db = db_select($mysqli, $db_tabelle_kategorien, "`programm`, `preis`");
$all_preise = array_column($all_preise_db, 'preis', 'programm');
// Creates a clean map of [id => betrag_bezahlt]
$all_teilnehmede_indexed = array_column($all_teilnehmede, 'betrag_bezahlt', 'id');
$all_teilnehmede_indexed_kat = array_column($all_teilnehmede, 'programm', 'id');
// 4. Prepare the update statement ONCE
$update_teiln_stmt = $mysqli->prepare("UPDATE $db_tabelle_teilnehmende SET `betrag_bezahlt` = ?, `bezahlt` = ? WHERE `id` = ?");
// Bind variables by reference once
$n_preis = 0.0;
$normal_order_status = match ($new_status) {
1 => 3,
2 => 4,
3 => 1,
default => 1
};
$status_to_set = $normal_order_status;
$s_teiln_id = 0;
$update_teiln_stmt->bind_param("dii", $n_preis, $status_to_set, $s_teiln_id);
// 5. Loop through and execute updates
foreach ($programm_ids as $id_key => $preis) {
$s_teiln_id = (int) $id_key;
// FIX: Check directly against the flat ID key map
if (!isset($all_teilnehmede_indexed[$s_teiln_id])) continue;
$eff_preis = 0.0;
if (intval($new_status) === 2) {
$eff_preis = (float) $preis;
} elseif (intval($new_status) !== 2 && intval($old_status) === 2) {
$eff_preis = ((float) $preis) * -1;
}
// Calculate the new absolute value
$n_preis = max(((float) $all_teilnehmede_indexed[$s_teiln_id]) + $eff_preis, 0);
if ($normal_order_status === 1) {
$kat = $all_teilnehmede_indexed_kat[$s_teiln_id];
$preis_kat = (float) $all_preise[$kat] ?? PHP_INT_MAX;
if ($n_preis >= $preis_kat) {
$status_to_set = 4;
} elseif ($n_preis !== 0.00) {
$status_to_set = 2;
} else {
$status_to_set = 1;
}
} else {
$status_to_set = $normal_order_status;
}
// Execute using the bound references
$update_teiln_stmt->execute();
}
$update_teiln_stmt->close();
return true;
}
function update_konten_vereine($benutzte_konten = '', $old_status, $new_status, bool $ids_are_array = false): bool {
global $mysqli, $db_tabelle_vereine;
$int_old_status = (int) $old_status;
$int_new_status = (int) $new_status;
// 1. Quick exit: No status change
if ($int_old_status === $int_new_status) {
return false;
}
// 2. Validate status transitions (Fixed $old_status === 0 || $old_status === 1 bug)
$isValidTransition =
(in_array($int_old_status, [0, 1, 3]) && $int_new_status === 2) ||
(in_array($int_new_status, [0, 1, 3]) && $int_old_status === 2) ||
($int_old_status === 0 && $int_new_status === 1);
if (!$isValidTransition) {
return false;
}
if (!$ids_are_array) {
$benutzte_konten_array = json_decode($benutzte_konten, true) ?? [];
} else {
if (!is_array($benutzte_konten)) {
return false;
}
$benutzte_konten_array = $benutzte_konten;
}
if (empty($benutzte_konten_array)) {
return false;
}
$faktor = ($int_new_status === 3) ? 1 : -1;
$stmt = $mysqli->prepare("UPDATE `$db_tabelle_vereine` SET `konto` = `konto` + ? WHERE `verein` = ?");
$konto_change = 0.0;
$verein = '';
$stmt->bind_param("ds", $konto_change, $verein);
foreach ($benutzte_konten_array as $verein_key => $konto_change_absolute) {
$verein = (string) $verein_key;
$konto_change = (float) $konto_change_absolute * $faktor;
if ($konto_change == 0) {
continue;
}
$stmt->execute();
}
$stmt->close();
return true;
}
+49
View File
@@ -0,0 +1,49 @@
<?php
use Dotenv\Dotenv;
require __DIR__ . '/../../composer/vendor/autoload.php';
$envFile = realpath(__DIR__ . '/../../config/.env.redis');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.redis');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
function connectToRedis(): bool
{
global $redis;
if ($redis instanceof Redis) {
return true;
}
$redis = new Redis();
if (!$redis->connect($_ENV['REDDIS_HOST'], $_ENV['REDDIS_PORT'])) {
return false;
}
if (!$redis->auth($_ENV['REDDIS_PASSWORD'])) {
return false;
}
return true;
}
+129
View File
@@ -0,0 +1,129 @@
<?php
function deleteSession() {
$_SESSION = array();
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
session_destroy();
}
function ini_wkvs_session(bool $set_csrf = false, bool $regenerate = false) {
if (session_status() === PHP_SESSION_NONE) {
session_name('wkvs_cookie');
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
}
if ($regenerate) {
session_regenerate_id(true);
}
if ($set_csrf && !isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(64));
}
}
function verify_csrf() {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['csrf_token'] ?? '';
if (!hash_equals($_SESSION['csrf_token'], $token)) {
http_response_code(403);
die("Access Denied: Invalid CSRF Token.");
}
} else {
http_response_code(405);
die("Access Denied: Invalid Request Type.");
}
}
function verify_delete_csrf($_DELETE) {
if ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
$token = $_DELETE['csrf_token'] ?? '';
if (!hash_equals($_SESSION['csrf_token'], $token)) {
http_response_code(403);
die("Access Denied: Invalid CSRF Token.");
}
} else {
http_response_code(405);
die("Access Denied: Invalid Request Type.");
}
}
$allowedUserTypes = ['trainer', 'kampfrichter', 'wk_leitung'];
function check_user_permission(string $type, bool $return = false, bool $display = false) {
global $allowedUserTypes;
if (!in_array($type, $allowedUserTypes, true)) {
if ($return) {
return false;
} else {
if ($display) {
http_response_code(403);
include __DIR__ . "/../www/error-pages/403.html";
exit;
} else {
http_response_code(403);
die("Invalid User Type Configuration");
}
}
}
$accessKey = "access_granted_{$type}";
$idKey = "user_id_{$type}";
$hasAccess = ($_SESSION[$accessKey] ?? false) === true;
$hasValidId = isset($_SESSION[$idKey]) && intval($_SESSION[$idKey]) > 0;
if (!$hasAccess || !$hasValidId) {
if ($return) {
return false;
} else {
if ($display) {
http_response_code(403);
include __DIR__ . "/../www/error-pages/403.html";
exit;
} else {
http_response_code(403);
die("Access Denied");
}
}
}
if ($return) {
return true;
}
}
function check_multiple_allowed_permissions(array $types) {
$authorized = false;
foreach ($types as $type) {
if (check_user_permission($type, true)) {
$authorized = true;
break;
}
}
if (!$authorized) {
http_response_code(403);
die("Access Denied");
}
}
+214
View File
@@ -0,0 +1,214 @@
<?php
/**
* Shared Sidebar Navigation
*
* Include this file after <body> on any intern page.
* Set $currentPage before including, e.g.:
* $currentPage = 'trainer';
* require $baseDir . '/../scripts/sidebar/sidebar.php';
*/
$isWKL = $_SESSION['access_granted_wk_leitung'] ?? false;
$isTrainer = $_SESSION['access_granted_trainer'] ?? false;
$isKampfrichter = $_SESSION['access_granted_kampfrichter'] ?? false;
if (!isset($currentPage)) $currentPage = '';
if (isset($_POST['abmelden'])) {
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION = array();
session_destroy();
}
header("Location: " . $_SERVER['PHP_SELF']);
exit;
}
// SVG Icons (stroke-based, 24x24 viewBox)
$icons = [
'trainer' => '<svg viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
'kampfrichter' => '<svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>',
'rechnungen' => '<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>',
'logindata' => '<svg viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>',
'displaycontrol' => '<svg viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>',
//'kalender' => '<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>',
'rankLive' => '<svg viewBox="0 0 24 24"><path d="M12 4h6a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h6"/><line x1="9" y1="9" x2="15" y2="9"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/></svg>',
'wettkaempfe' => '<svg viewBox="0 0 24 24"><path d="M12 4h6a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h6"/><line x1="9" y1="9" x2="15" y2="9"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/></svg>',
'riegeneinteilung' => '<svg viewBox="0 0 24 24"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/><line x1="9" y1="12" x2="15" y2="12"/><line x1="9" y1="16" x2="15" y2="16"/></svg>',
'einstellungen' => '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>',
];
if (isset($mysqli) && isset($db_tabelle_intern_benutzende)) {
if ($currentPage === 'kampfrichter' && checkIfUserHasSessionId('kampfrichter')):
$userDispId = intval($_SESSION['user_id_kampfrichter']);
elseif ($currentPage === 'kampfrichter' && checkIfUserHasSessionId('trainer')):
$userDispId = intval($_SESSION['user_id_trainer']);
elseif ($isWKL && checkIfUserHasSessionId('wk_leitung')):
$userDispId = intval($_SESSION['user_id_wk_leitung']);
endif;
$sql = "SELECT `username`, `freigabe` FROM $db_tabelle_intern_benutzende WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('i', $userDispId);
$stmt->execute();
$stmt->bind_result($usernameDB, $freigabenDB);
$stmt->fetch();
$username = $usernameDB ?? '';
$decoded = json_decode($freigabenDB, true);
$freigabenSidebar = is_array($decoded) ? $decoded : [];
$stmt->close();
}
$links = [];
$renderMenu = true;
// Trainer & Kampfrichter are visible to their own role + WKL
if ($isTrainer || $isWKL) {
$links[] = ['key' => 'trainer', 'label' => 'Trainer', 'url' => '/intern/trainer', 'freigaben' => true];
}
if ($isKampfrichter || $isWKL) {
$links[] = ['key' => 'kampfrichter', 'label' => 'Kampfrichter', 'url' => '/intern/kampfrichter', 'freigaben' => true];
}
// WKL-only pages
if ($isWKL) {
$links[] = ['key' => 'rechnungen', 'label' => 'Rechnungen', 'url' => '/intern/wk-leitung/rechnungen', 'freigaben' => false];
$links[] = ['key' => 'logindata', 'label' => 'Benutzerverwaltung', 'url' => '/intern/wk-leitung/logindata', 'freigaben' => false];
$links[] = ['key' => 'displaycontrol', 'label' => 'Displaycontrol', 'url' => '/intern/wk-leitung/displaycontrol', 'freigaben' => false];
//$links[] = ['key' => 'kalender', 'label' => 'Kalender', 'url' => '/intern/wk-leitung/kalender'];
$links[] = ['key' => 'rankLive', 'label' => 'Rank Live', 'url' => '/intern/wk-leitung/rank-live', 'freigaben' => false];
$links[] = ['key' => 'riegeneinteilung', 'label' => 'Riegeneinteilung', 'url' => '/intern/wk-leitung/riegeneinteilung', 'freigaben' => false];
$links[] = ['key' => 'einstellungen', 'label' => 'Einstellungen', 'url' => '/intern/wk-leitung/einstellungen', 'freigaben' => false];
$links[] = ['key' => 'wettkaempfe', 'label' => 'Wettkämpfe', 'url' => '/intern/wk-leitung/wettkaempfe', 'freigaben' => false];
}
function checkIfUserHasSessionId($type) : bool {
if (isset($_SESSION['user_id_'.$type]) && intval(['user_id_'.$type]) > 0) { return true; }
else { return false; }
}
// if (!isset($indexedNotenNames)) { $indexedNotenNames = []; }
function sidebarRender(string $mode) {
global $isWKL, $isTrainer, $isKampfrichter, $links, $currentPage, $icons, $renderMenu, $username, $freigabenSidebar, $indexedArrayGeraete;
if (!$renderMenu) { return; }
if ($mode === 'button') {
?>
<!-- Sidebar Toggle Button -->
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="Navigation öffnen">
<span></span>
<span></span>
<span></span>
</button>
<?php } elseif ($mode === 'modal') { ?>
<!-- Sidebar Overlay -->
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<!-- Sidebar Panel -->
<nav class="sidebar-nav" id="sidebar-nav">
<div class="sidebar-header">
<h3>Navigation</h3>
<button class="sidebar-close-btn" id="sidebar-close" aria-label="Schliessen">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<?php if ($isWKL && ($isTrainer || $isKampfrichter || true)): ?>
<div class="sidebar-section-label">Allgemein</div>
<?php endif; ?>
<ul class="sidebar-links">
<?php foreach ($links as $i => $link):
$isCurrentPage = ($currentPage === $link['key']);
$activeClass = $isCurrentPage ? ' active' : '';
$icon = $icons[$link['key']] ?? '';
$freigbenArrayName = 'freigaben' . ucfirst($link['key']);
// Insert section divider before WKL-only links
if ($isWKL && $link['key'] === 'rechnungen' && $i > 0): ?>
</ul>
<div class="sidebar-section-label">WK-Leitung</div>
<ul class="sidebar-links">
<?php endif; ?>
<li>
<a href="<?php echo $link['url']; ?>" class="<?php echo trim($activeClass); ?>">
<?php echo $icon; ?>
<?php echo htmlspecialchars($link['label']); ?>
</a>
</li>
<?php if ($isCurrentPage && $link['freigaben'] === true && isset($freigabenSidebar[$freigbenArrayName]) && count($freigabenSidebar[$freigbenArrayName]) > 1) : ?>
<?php $selectedFreigabe = ($currentPage === 'kampfrichter') ? $_SESSION['selectedFreigabeIdKampfrichter'] : $_SESSION['selectedFreigabe' . ucfirst($link['key'])] ?? ''; ?>
<li class="sidebar-li-freigaben">
<label class="sidebar-freigaben-label" for="selectTriggerFreigabe">Freigabe</label>
<div class="customSelect" id="selectedOption" data-value="[]">
<button type="button" id="selectTriggerFreigabe" class="selectTriggerSidebar" aria-expanded="false">
<span class="selectLabel">
<?= htmlspecialchars(($currentPage === 'kampfrichter') ? (($selectedFreigabe !== 'A') ? $indexedArrayGeraete[$selectedFreigabe] ?? $selectedFreigabe : 'Admin') : $selectedFreigabe) ?>
</span>
<svg class="selectArrow" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' height="14" width="14">
<path d='M6 9L12 15L18 9' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/>
</svg>
</button>
<ul class="selectOptionsSidebar">
<?php foreach ($freigabenSidebar[$freigbenArrayName] as $f) :?>
<?php $selected = ($f === $selectedFreigabe) ? 'selected' : '' ?>
<li data-value="<?= htmlspecialchars($f) ?>" class="<?= $selected ?>"><?= htmlspecialchars(($currentPage === 'kampfrichter') ? (($f !== 'A') ? $indexedArrayGeraete[$f] ?? $f : 'Admin') : $f) ?></li>
<?php endforeach; ?>
</ul>
<input type="hidden" name="type" class="selectValue" id="freigabenSidebarSelect" value="">
</div>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php
if (isset($username)) : ?>
<div class="sidebarUsername">Benutzer: <?= $username ?></div>
<?php endif; ?>
<div class="sidebar-footer">
<form method="POST" action="" class="abmelden"><input class="abmeldenbutton" type="submit" href="?logout=1" name="abmelden" value="Abmelden"></form>
</div>
</nav>
<script>
window.CSRF_TOKEN = "<?= $csrf_token ?? $_SESSION['csrf_token'] ?? '' ?>";
const siteType = '<?= $currentPage ?>';
// Close button binding (inline to avoid race condition with sidebar.js)
document.addEventListener('DOMContentLoaded', function() {
var closeBtn = document.getElementById('sidebar-close');
if (closeBtn) {
closeBtn.addEventListener('click', function() {
document.getElementById('sidebar-nav').classList.remove('open');
document.getElementById('sidebar-overlay').classList.remove('open');
document.getElementById('sidebar-toggle').classList.remove('open');
localStorage.setItem('intern_sidebar_open', 'false');
});
}
});
</script>
<script src="/intern/js/sidebar.js"></script>
<?php
}
}
@@ -0,0 +1,324 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1);
require $baseDir . '/../composer/vendor/autoload.php';
use ChrisKonnertz\StringCalc\StringCalc;
class NotenRechner {
private $Rfunctions = ["MAXR", "MINR", "AVGR", "SUMR"];
private $RfunctionsSan = [];
public function __construct(
private StringCalc $rechner = new StringCalc()
) {
$this->RfunctionsSan = implode('|', array_map('preg_quote', $this->Rfunctions));
}
/*
Es wird ein Array mit allen Notentypen pro Gerät, Jahr und Person erwartet, so dass noten_bezeichnung_id als Key verwendet werden kann:
$valuesArray = [
noten_bezeichnung_id => value,
];
*/
private function santinzeString(string $string) : string
{
$replacePatterns = [
'/->R\w+/', // 1. Removes ->R followed by digits
'/[^a-zA-Z0-9(){}+*\/.[\]\-]/' // 2. Fixed syntax: Allowed characters list
];
return preg_replace($replacePatterns,'', $string);
}
private function sanitizeStringWithRun(string $string) : string
{
$replacePattern = '/[^a-zA-Z0-9(){}+*\/.\-\[\]>]/';
return preg_replace($replacePattern, "", $string);
}
public function getBenoetigteIds(string $schemaRaw)
{
$schema = $this->santinzeString($schemaRaw);
preg_match_all('/\{(\d+)\}/', $schema, $matches);
return $matches[1];
}
public function getBenoetigteIdsComplex(string $schemaRaw)
{
$schema = $this->santinzeString($schemaRaw);
$indexedMatches = [];
// Pattern breakdown:
// \{(\d+) -> Match { and capture the first number (Note ID)
// (?:\[(\w*)\])? -> OPTIONALLY match [ and capture alphanumeric content inside (Apparatus ID)
// (?:\[(\d+)\])? -> OPTIONALLY match [ and capture number inside (Run Number)
// \} -> Match the closing }
if (preg_match_all('/\{(\d+)(?:\[(\w*)\])?(?:\[(\d+)\])?\}/', $schema, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$noteId = $match[1];
$rawGeraet = $match[2] ?? 'S';
$geraetId = ($rawGeraet === 'S' || $rawGeraet === '') ? 'A' : $rawGeraet;
$runNumber = isset($match[3]) ? (int)$match[3] : 1;
$indexedMatches[] = [
'noteId' => $noteId,
'geraetId' => $geraetId,
'run' => $runNumber
];
}
}
return $indexedMatches;
}
public function getBenoetigteIdsComplexWithRun(string $schemaRaw)
{
$schema = $this->sanitizeStringWithRun($schemaRaw);
$indexedMatches = [];
// Pattern breakdown:
// \{(\d+) -> Match { and capture the first number (Note ID)
// (?:\[(\w*)\])? -> OPTIONALLY match [ and capture alphanumeric content inside (Apparatus ID)
// (?:\[(\d+)\])? -> OPTIONALLY match [ and capture number inside (Run Number)
// \} -> Match the closing }
if (preg_match_all('/\{(\d+)(?:\[(\w*)\])?(?:\[(\d+)\])?\}/', $schema, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$noteId = $match[1];
$rawGeraet = $match[2] ?? 'S';
$geraetId = ($rawGeraet === 'S' || $rawGeraet === '') ? 'A' : $rawGeraet;
$runNumber = isset($match[3]) ? (int)$match[3] : 1;
$indexedMatches[] = [
'noteId' => $noteId,
'geraetId' => $geraetId,
'run' => $runNumber
];
}
}
$arrayRes = $this->getStoreGeraetRun($schemaRaw);
$indexedMatches['targetRun'] = $arrayRes['run_num'] ?? 'A';
$indexedMatches['targetGeraet'] = $arrayRes['geraet_id'] ?? 'A';
return $indexedMatches;
}
public function getStoreGeraetRun(string $schemaRaw): array
{
$schema = $this->sanitizeStringWithRun($schemaRaw);
$pattern = '/->R(?<run_num>\d+|S)(?:G(?<geraet_id>\d+|S))?/';
if (preg_match($pattern, $schema, $matchesTagetRun)) {
$run = $matchesTagetRun['run_num'] ?? 'S';
$geraet = $matchesTagetRun['geraet_id'] ?? 'S';
return [
'run_num' => $run === 'S' ? 'A' : (int)$run,
'geraet_id' => $geraet === 'S' ? 'A' : (int)$geraet,
];
}
return [
'run_num' => 'A',
'geraet_id' => 'A'
];
}
private function insertValuesComplex(string $schemaRaw, array $valuesArray, int $currentId, int $runNumber, bool $avr = false)
{
$schema = $this->santinzeString($schemaRaw);
$idsNeededArray = $this->getBenoetigteIdsComplex($schemaRaw);
// 1. Validation Loop
foreach ($idsNeededArray as $sina) {
$noteId = $sina['noteId'];
$geraetIdSearch = ($sina['geraetId'] === 'A') ? $currentId : $sina['geraetId'];
if (!isset($valuesArray[$geraetIdSearch][$noteId][$runNumber]["value"])) {
return ['success' => false, 'value' => "Fehlende Daten für Gerät $geraetIdSearch, Note $noteId, Lauf $runNumber"];
}
}
try {
$returnStr = preg_replace_callback('/\{(\d+)(?:\[(\w*)\])?(?:\[(\d+)\])?\}/', function($m) use ($valuesArray, $currentId, $runNumber, $avr) {
$noteId = $m[1];
// Get value inside brackets, default to 'S' if none exists
$rawGeraet = $m[2] ?? 'S';
$geraetId = ($rawGeraet === 'S' || $rawGeraet === '') ? $currentId : $rawGeraet;
if ($avr && $valuesArray[$geraetId][$noteId][$runNumber]["type"] === 'default_value') {
return 'default_value_skip';
} else {
// Return value from [Device][Note][Run]
return $valuesArray[$geraetId][$noteId][$runNumber]["value"] ?? 0;
}
}, $schema);
return ['success' => true, 'value' => $returnStr];
} catch (\Exception $e) {
return ['success' => false, 'value' => 'Problem beim Einsetzen der Werte'];
}
}
private function calculate(string $rechnungRaw) {
global $rechner;
$rechnung = $this->santinzeString($rechnungRaw);
try {
$result = $this->rechner->calculate($rechnung);
return ['success' => true, 'value' => floatval($result)];
} catch (\ChrisKonnertz\StringCalc\Exceptions\StringCalcException $e) {
return ['success' => false, 'value' => 'Problem bei der Berechnung: Ungültige Syntax '. $rechnung];
} catch (\Exception $e) {
return ['success' => false, 'value' => 'Problem bei der Berechnung'];
}
}
public function berechneStringComplex(string $schemaRaw, array $valuesArray = [], int $geraetId = -1, int $run_number = 0) {
if ($schemaRaw === '') { return ['success' => false, 'value' => 'Leeres Schema']; }
if ($geraetId < 0) { return ['success' => false, 'value' => 'Keine Geraet Id angegeben.']; }
if ($run_number === 0) { return ['success' => false, 'value' => 'Keine Run_Number angegeben.']; }
$rechnungArray = $this->insertValuesComplex($schemaRaw, $valuesArray, $geraetId, $run_number);
if (!isset($rechnungArray['success']) || !isset($rechnungArray['value']) || !$rechnungArray['success']) {
return ['success' => false, 'value' => $rechnungArray['value'] . " RECHNUNG: " . $schemaRaw ?? 'Fehler beim Einsetzen der Werte oder Werte fehlen'];
}
return $this->calculate($rechnungArray['value']);
}
public function berechneStringComplexRun(string $schemaRaw, array $valuesArray = [], int $geraetId = -1, int $programm = 0, array $arrayRuns = []) {
if ($schemaRaw === '') { return ['success' => false, 'value' => 'Leeres Schema']; }
if ($geraetId < 0) { return ['success' => false, 'value' => 'Keine Geraet Id angegeben.']; }
if ($programm === 0) { return ['success' => false, 'value' => 'Kein Programm angegeben.']; }
if ($arrayRuns === []) { return ['success' => false, 'value' => 'Leeres Array Runs Noten.']; }
$rechnung = $this->insertRunValues($schemaRaw, $valuesArray, $geraetId, $programm, $arrayRuns);
if (!isset($rechnung) || $rechnung === '') {
return ['success' => false, 'value' => 'Fehler beim Einsetzen der Werte oder Werte fehlen'];
}
return $this->calculate($rechnung);
}
private function insertRunValues($schema, $valuesArray, $currentId, $programm, $arrayRuns) {
$pattern = '/(?P<fn_name>' . $this->RfunctionsSan . ')\((?P<content>(?:(?R)|[^()])*)\)/';
return preg_replace_callback($pattern, function($m) use ($valuesArray, $currentId, $programm, $arrayRuns) {
$name = $m['fn_name'];
$content = $m['content'];
// Resolve inner nested functions first
$content = $this->insertRunValues($content, $valuesArray, $currentId, $programm, $arrayRuns);
if (!in_array($name, $this->Rfunctions)) {
return $m[0];
}
$parts = [];
$ids = $this->getBenoetigteIdsComplex($content);
$runCount = 0;
foreach ($ids as $sid) {
$geraetId = ($sid['geraetId'] === 'A') ? $currentId : $sid['geraetId'];
$runCountN = $arrayRuns[$sid['noteId']][$geraetId][$programm] ?? $arrayRuns[$sid['noteId']][$geraetId]["all"] ?? $arrayRuns["default"] ?? 1;
if ($runCount !== $runCountN && $runCount !== 0) { return; }
$runCount = $runCountN;
}
$string = "";
switch ($name) {
case "AVGR":
case "SUMR":
$nulls = 0;
for ($r = 1; $r <= $runCount; $r++) {
$res = $this->insertValuesComplex($content, $valuesArray, $currentId, $r, true);
if (!$res['success']) {
return "0";
} elseif ($res['value'] === 'default_value_skip') {
$nulls++;
} else {
$parts[] = "(" . $res['value'] . ")";
}
}
$innerMath = implode(" + ", $parts);
$div_count = $runCount - $nulls;
if ($div_count === 0 || count($parts) < 1) return "0";
$string = ($name === "AVGR") ? "(($innerMath) / $div_count)" : "($innerMath)";
break;
case "MAXR":
case "MINR":
$arrayRunValues = [];
for ($r = 1; $r <= $runCount; $r++) {
$res = $this->berechneStringComplex($content, $valuesArray, $currentId, $r);
// FIXED: Access the ['value'] key of the returned array
if ($res['success']) {
$arrayRunValues[] = floatval($res['value']);
} else {
$arrayRunValues[] = 0;
}
}
// Safety check for empty arrays to prevent max() errors
if (empty($arrayRunValues)) return "0";
$string = ($name === "MAXR") ? max($arrayRunValues) : min($arrayRunValues);
break;
}
return (string)$string;
}, $schema);
}
public function checkRunFunctions(string $schemaRaw) {
if ($schemaRaw === '') {
return ['success' => false, 'value' => 'Leeres Schema'];
}
$schema = $this->santinzeString($schemaRaw);
$pattern = '/(?P<fn_name>' . $this->RfunctionsSan . ')\((?:(?R)|[^()])*+\)/';
return (bool) preg_match($pattern, $schema);
}
}
+244
View File
@@ -0,0 +1,244 @@
<?php
function get_kat_price_diff(array $old_data, string $new_kat): array {
global $preise_kats;
global $vereine_plus_array;
$return_array = [];
$old_kat = $old_data['programm'] ?? '';
$verein = $old_data['verein'] ?? '';
$bezahlt = (int) ($old_data['bezahlt'] ?? 1);
if ($old_kat === '' || $old_kat === $new_kat || !in_array($bezahlt, [2, 4], true) || !isset($preise_kats[$new_kat])) {
return $return_array;
}
$betrag_bezahlt = (float) ($old_data['betrag_bezahlt'] ?? 0.00);
$new_kat_preis = (float) $preise_kats[$new_kat];
if ($new_kat_preis > $betrag_bezahlt) {
$return_array['bezahlt'] = ($betrag_bezahlt === 0.00) ? 1 : 2;
} elseif ($new_kat_preis < $betrag_bezahlt) {
$return_array['bezahlt'] = 4;
$vereine_plus_array[$verein] = ((float) ($vereine_plus_array[$verein] ?? 0.00)) + $betrag_bezahlt - $new_kat_preis;
} else {
$return_array['bezahlt'] = 4;
}
return $return_array;
}
function update_verein_balances(array $vereine_plus_array_funct) {
global $mysqli;
global $db_tabelle_vereine;
if (empty($vereine_plus_array_funct)) {
return;
}
$stmt = $mysqli->prepare("UPDATE $db_tabelle_vereine SET `konto` = `konto` + ? WHERE `verein` = ?");
if ($stmt) {
foreach ($vereine_plus_array_funct as $verein => $betrag) {
if ($betrag <= 0.00) {
continue;
}
$stmt->bind_param("ds", $betrag, $verein);
$stmt->execute();
}
$stmt->close();
}
}
function remove_from_basket(array $ids_array = []) : bool {
global $mysqli;
global $db_tabelle_warenkorb_startgebueren;
$tplaceholders = implode(", ", array_fill(0, count($ids_array), "?"));
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_warenkorb_startgebueren WHERE `item_id` IN ($tplaceholders)");
$stmt->bind_param(str_repeat('i', count($ids_array)), ...$ids_array);
$ret = $stmt->execute();
$stmt->close();
return $ret;
}
$vereine_plus_array = [];
if (isset($_POST['delete_id'])) {
verify_csrf();
$delete_id = intval($_POST['delete_id']);
$stmt = $mysqli->prepare("DELETE FROM $db_tabelle_teilnehmendee where id = ?");
$stmt->bind_param('i', $delete_id);
if ($stmt->execute()) {
$_SESSION['form_message'] = 'Eintrag erfolgreich gelöscht.';
$_SESSION['form_message_type'] = 1;
} else {
$_SESSION['form_message'] = 'Löschen fehlgeschlagen.';
$_SESSION['form_message_type'] = 0;
}
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
$edit_row = null;
if ($access_granted_trainer && isset($_GET['edit_id']) && is_numeric($_GET['edit_id']) && !isset($_POST['submit_turnerinnen_form'])) {
$edit_id = intval($_GET['edit_id']);
$edit_rows = db_select($mysqli, $db_tabelle_teilnehmende, "*", 'id = ?', [$edit_id]);
if (!isset($edit_rows) || !is_array($edit_rows) || count($edit_rows) !== 1) {
http_response_code(422);
exit;
}
$edit_row = $edit_rows[0];
if ($edit_row && ($edit_row['verein'] === $selectedverein || $is_admin)) {
$_POST['nachname'] = $edit_row['name'] ?? '';
$_POST['vorname'] = $edit_row['vorname'] ?? '';
$_POST['geburtsdatum'] = $edit_row['geburtsdatum'] ?? '';
$kat = $edit_row['programm'] ?? '';
$kat_id = db_get_var($mysqli, "SELECT `id` FROM $db_tabelle_kategorien WHERE `programm` = ?", [$kat]) ?: '';
$_POST['programm'] = $kat_id;
$_POST['edit_id'] = $edit_id;
if ($is_admin) {
$_POST['verein'] = $edit_row['verein'] ?? '';
$_POST['bezahltOverride'] = $edit_row['bezahltoverride'] ?? 0;
}
} else {
$_SESSION['form_message'] = 'Ungültiger Eintrag zum Bearbeiten.';
$_SESSION['form_message_type'] = 0;
header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
}
// === INSERT/UPDATE Handler ===
if ($access_granted_trainer && isset($_POST['submit_turnerinnen_form'])) {
verify_csrf();
$name = htmlspecialchars($_POST['nachname']);
$vorname = htmlspecialchars($_POST['vorname']);
$geburtsdatum = trim($_POST['geburtsdatum']);
$kat_id = (int) ($_POST['programm'] ?? 0);
if (!$is_admin) {
$verein = $selectedverein;
} else {
$verein = htmlspecialchars($_POST['verein']);
$bezahlt = intval($_POST['bezahltOverride']);
}
$kat = db_get_var($mysqli, "SELECT `programm` FROM $db_tabelle_kategorien WHERE `id` = ?", [$kat_id]) ?: '';
if (empty($name) || empty($vorname) || empty($geburtsdatum) || empty($kat_id) || empty($kat)) {
$_SESSION['form_message'] = 'Bitte füllen Sie alle erforderlichen Felder aus.';
$_SESSION['form_message_type'] = 0;
} else {
$data_to_insert = [
'name' => $name,
'vorname' => $vorname,
'geburtsdatum' => $geburtsdatum,
'programm' => $kat,
'verein' => $verein,
];
if ($is_admin) {
$data_to_insert['bezahltoverride'] = $bezahlt;
}
// Check if we are editing an existing entry
$is_editing = isset($_POST['edit_id']) && is_numeric($_POST['edit_id']) && $_POST['edit_id'] > 0;
if ($is_editing) {
$edit_id = intval($_POST['edit_id']);
$entry = db_select($mysqli, $db_tabelle_teilnehmende, '*', 'id = ?', [$edit_id], '', '1');
if (count($entry) === 1) {
$old_data = $entry[0];
$data_to_insert = array_merge($data_to_insert, get_kat_price_diff($old_data, $kat));
remove_from_basket([$edit_id]);
update_verein_balances($vereine_plus_array);
}
var_dump($data_to_insert);
$columns = array_keys($data_to_insert);
$set = implode(
', ',
array_map(fn($col) => "$col = ?", $columns)
);
$sql = "UPDATE $db_tabelle_teilnehmende SET $set WHERE id = ?";
var_dump($sql);
$stmt = $mysqli->prepare($sql);
$types = str_repeat('s', count($data_to_insert)) . 'i';
$values = array_values($data_to_insert);
$values[] = $edit_id;
$stmt->bind_param($types, ...$values);
$updated = $stmt->execute();
$stmt->close();
if ($updated === false) {
error_log('DB Update Error: ' . $wpdb->last_error);
$_SESSION['form_message'] = 'Fehler beim Aktualisieren der Personendaten.';
$_SESSION['form_message_type'] = 0;
} else if ($updated === 0) {
$_SESSION['form_message'] = 'Keine Änderungen vorgenommen.';
$_SESSION['form_message_type'] = 0;
} else {
$_SESSION['form_message'] = $personEinzahl . ' erfolgreich aktualisiert!';
$_SESSION['form_message_type'] = 1;
$_POST = [];
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
exit;
}
} else {
$columns = array_keys($data_to_insert);
$set = implode(
', ',
array_map(fn($col) => "$col = ?", $columns)
);
$sql = "INSERT INTO $db_tabelle_teilnehmende SET $set";
$stmt = $mysqli->prepare($sql);
$types = str_repeat('s', count($data_to_insert));
$values = array_values($data_to_insert);
$stmt->bind_param($types, ...$values);
$inserted = $stmt->execute();
$stmt->close();
if ($inserted) {
$_SESSION['form_message'] = 'Daten erfolgreich gespeichert!';
$_SESSION['form_message_type'] = 1;
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
exit;
} else {
$_SESSION['form_message'] = 'Fehler beim Speichern der Daten. Bitte versuchen Sie es später erneut.';
$_SESSION['form_message_type'] = 0;
}
}
}
}
+123
View File
@@ -0,0 +1,123 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
use Dotenv\Dotenv;
$wsPermissions = [
'displaycontrol' => ['access_granted_wk_leitung'],
'einstellungen' => ['access_granted_wk_leitung'],
'kampfrichter' => ['access_granted_kampfrichter']
];
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once __DIR__ . '/../redis/connect-to-redis.php';
$redis = null;
connectToRedis();
function checkWSTokenPermissions($RPerm) {
switch ($RPerm) {
case 'wk_leitung':
return $_SESSION['access_granted_wk_leitung'] ?? false;
case 'kampfrichter';
return $_SESSION['access_granted_kampfrichter'] ?? false;
default:
return false;
}
}
$envFile = realpath(__DIR__ . '/../../config/.env.wk-id');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.wk-id');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
$wkId = $_ENV['WETTKAMPF_ID'];
$allowedWkLeitungAccess = ['displaycontrol', 'einstellungen'];
function generateWSAdminToken($wsRequestedAcesstype, $wsRequestedAccess) {
global $redis, $wkId, $allowedWkLeitungAccess;
if (!checkWSTokenPermissions($wsRequestedAcesstype)) return null;
$wsGrantedPermission = [];
if ($wsRequestedAcesstype === 'kampfrichter' && isset($_SESSION['selectedFreigabeIdKampfrichter'])) {
$wsGrantedPermission['type'] = 'kampfrichter';
$wsGrantedPermission['access'] = $_SESSION['selectedFreigabeIdKampfrichter'];
} elseif ($wsRequestedAcesstype === 'wk_leitung' && in_array($wsRequestedAccess, $allowedWkLeitungAccess)) {
$wsGrantedPermission['type'] = $wsRequestedAcesstype;
$wsGrantedPermission['access'] = $wsRequestedAccess;
} else {
return null;
}
$wsGrantedPermission['wk_id'] = $wkId;
$wsGrantedPermission['authenticated'] = true;
$token = bin2hex(random_bytes(32));
$key = "ws:token:" . $token;
$success = $redis->set($key, json_encode($wsGrantedPermission), ['nx', 'ex' => 10]);
$_SESSION['WS_KEY'] = $key;
return $key;
}
$allowedReadTypes = ['display', 'audio', 'rankLive'];
function generateWSReadToken($wsRequestedAcesstype, $wsRequestedAccess) {
global $redis, $wkId, $allowedReadTypes;
$wsGrantedPermission = [];
if (!in_array($wsRequestedAcesstype, $allowedReadTypes)) {
return null;
}
$wsGrantedPermission['access'] = $wsRequestedAccess;
$wsGrantedPermission['type'] = $wsRequestedAcesstype;
$wsGrantedPermission['wk_id'] = $wkId;
$wsGrantedPermission['authenticated'] = false;
$token = bin2hex(random_bytes(16));
$key = "ws:token:" . $token;
$success = $redis->set($key, json_encode($wsGrantedPermission), ['nx', 'ex' => 30]);
$_SESSION['WS_KEY'] = $key;
return $key;
}