Semi-stable version with old variable names

This commit is contained in:
Fabio Herzig
2026-07-16 18:43:30 +02:00
parent 3731183654
commit c8e7ce9174
165 changed files with 25654 additions and 9728 deletions
@@ -0,0 +1,206 @@
<?php
function generateIntersectCache($mysqli, $baseDir) {
global $tableProgramme;
global $tableGeraete;
global $tableNotenBezeichnungen;
global $tableRankLiveConfigs;
global $tableVar;
// 1. Fetch all unique IDs you need to loop through
$all_programms = array_column(db_select($mysqli, $tableProgramme, '`id`', '`aktiv` = ?', [1]) ?? [], 'id');
$all_geraete = array_column(db_select($mysqli, $tableGeraete, 'id') ?? [], 'id');
// 2. Fetch the static DB configs ONCE before starting the massive loop
$rang_note_id = db_get_variable($mysqli, $tableVar, ['rangNote']);
$json_geraet = db_get_var($mysqli, "SELECT `all_noten_json` FROM $tableRankLiveConfigs WHERE `type_slug` = ?", ["rankLive-geraet"]) ?: '';
$json_overview = db_get_var($mysqli, "SELECT `all_noten_json` FROM $tableRankLiveConfigs 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, $tableNotenBezeichnungen, '`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, $tableNotenBezeichnungen, $baseDir) {
if (!function_exists('db_select')) require $baseDir . '/../scripts/db/db-functions.php';
$notenRechnerCache = new NotenRechner();
$all = db_select($mysqli, $tableNotenBezeichnungen, "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,
)
];
-170
View File
@@ -1,170 +0,0 @@
<?php
use Dotenv\Dotenv;
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: " . $e->getMessage()
]);
}
if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_USER']) || !isset($_ENV['DB_PASSWORD'])){
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']);
if ($mysqli->connect_error) {
echo json_encode([
'success' => false,
'message' => "DB connection failed"
]);
exit;
}
$mysqli->set_charset("utf8");
require __DIR__ . "/db-tables.php";
$tables = [$tableTurnerinnen, $tableOrders, $tableBasketItems, $tableKrProtokoll];
$cleartablearray = [$tableOrders, $tableBasketItems, $tableKrProtokoll];
require __DIR__ . "/../../resultate/newjson.php";
// Columns to set to 0
$columns0 = [
'd-note balken', 'd-note boden',
'd-note sprung', 'd-note barren',
'e1 note sprung','e2 note sprung','e note sprung','neutrale abzuege sprung',
'e1 note barren','e2 note barren','e note barren','neutrale abzuege barren',
'e1 note balken','e2 note balken','e note balken','neutrale abzuege balken',
'e1 note boden','e2 note boden','e note boden','neutrale abzuege boden',
'bezahlt', 'bezahltoverride', 'rang', 'abteilung', 'startgeraet', 'anzabteilungen', 'startindex', 'bodenmusik'
];
$dir = __DIR__ . '/../../../test-wkvs/dbbackups';
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
$newdir = $dir ."/". date('Ymd_His');
if (!is_dir($newdir)) {
mkdir($newdir, 0755, true);
}
foreach ($tables as $t){
$backupFile = $t . '_backup' . '.sql';
$filename = $newdir . '/' . $backupFile;
$handle = fopen($filename, 'w');
if ($handle === false) {
die("Cannot open file: $filename");
}
$res = $mysqli->query("SHOW CREATE TABLE `$t`");
$row = $res->fetch_assoc();
fwrite($handle, $row['Create Table'] . ";\n\n");
$res = $mysqli->query("SELECT * FROM `$t`");
while ($row = $res->fetch_assoc()) {
$columns = array_map(function($col){ return "`$col`"; }, array_keys($row));
$values = array_map(function($val) use ($mysqli) { return "'" . $mysqli->real_escape_string($val) . "'"; }, array_values($row));
fwrite($handle, "INSERT INTO `$t` (" . implode(", ", $columns) . ") VALUES (" . implode(", ", $values) . ");\n");
}
fclose($handle);
}
// Columns to set to 10
$columns10 = [
'note balken', 'note boden',
'note sprung', 'note barren'
];
$set = [];
$params = [];
$types = '';
// Add 0 columns
foreach ($columns0 as $col) {
$set[] = "`$col` = ?";
$params[] = '0';
$types .= 's';
}
// Add 10 columns
foreach ($columns10 as $col) {
$set[] = "`$col` = ?";
$params[] = '10';
$types .= 's';
}
// Add gesammtpunktzahl column
$set[] = "`gesamtpunktzahl` = ?";
$params[] = '40';
$types .= 's';
// Build SQL
$sql = "UPDATE turnerinnen SET " . implode(", ", $set);
// Prepare
$stmt = $mysqli->prepare($sql);
if ($stmt === false) {
die("Prepare failed: " . $mysqli->error);
}
// Bind parameters dynamically
$bind_names[] = $types;
for ($i = 0; $i < count($params); $i++) {
$bind_names[] = &$params[$i]; // reference required
}
call_user_func_array([$stmt, 'bind_param'], $bind_names);
// Execute
if (!$stmt->execute()) {
echo "Error: " . $stmt->error;
}
// Close
$stmt->close();
foreach ($cleartablearray as $t) {
$stmt = $mysqli->prepare("DELETE FROM ".$t);
if (!$stmt->execute()) {
echo "Error: " . $stmt->error;
}
$stmt->close();
}
//
$mysqli->close();
?>
+53
View File
@@ -130,3 +130,56 @@ function db_get_var($mysqli, $sql, $params = []) {
$stmt->close();
return $value;
}
function db_get_variable($mysqli, $tableVar, $params = []) {
if (count($params) !== 1) return null;
$stmt = $mysqli->prepare("SELECT `value` FROM $tableVar WHERE `name` = ?");
$stmt->bind_param("s", ...$params);
$stmt->execute();
$stmt->bind_result($value);
$stmt->fetch();
$stmt->close();
return $value;
}
function db_update_variable($mysqli, $tableVar, $params = ['name', 'value']) {
if (count($params) !== 2) return false;
$stmt = $mysqli->prepare("INSERT INTO $tableVar (`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;
}
+9 -3
View File
@@ -34,7 +34,7 @@ $prefix = $_ENV['DB_PREFIX'] ?? '';
$tableDefinitions = [
'Turnerinnen' => 'DB_TABLE_TURNERINNEN',
'Teilnehmende' => 'DB_TABLE_TEILNEHMENDE',
'Orders' => 'DB_TABLE_ORDERS',
'BasketItems' => 'DB_TABLE_BASKET_ITEMS',
'Var' => 'DB_TABLE_VARIABLES',
@@ -44,11 +44,17 @@ $tableDefinitions = [
'InternUsers' => 'DB_TABLE_INTERN_USERS',
'Vereine' => 'DB_TABLE_VEREINE',
'Abt' => 'DB_TABLE_ABTEILUNGEN',
'TurnerinnenAbt' => 'DB_TABLE_TURNERINNEN_ABTEILUNGEN',
'TeilnehmendeAbt' => 'DB_TABLE_TEILNEHMENDE_ABTEILUNGEN',
'Geraete' => 'DB_TABLE_GERAETE',
'Audiofiles' => 'DB_TABLE_AUDIOFILES',
'TeilnehmendeAudiofiles' => 'DB_TABLE_TEILNEHMENDE_AUDIOFILES',
'Noten' => 'DB_TABLE_NOTEN',
'NotenBezeichnungen' => 'DB_TABLE_NOTEN_BEZEICHNUNGEN'
'NotenChanges' => 'DB_TABLE_NOTEN_CHANGES',
'NotenBezeichnungen' => 'DB_TABLE_NOTEN_BEZEICHNUNGEN',
'OrdersLog' => 'DB_TABLE_ORDERS_LOG',
'ZeitplanTypes' => 'DB_TABLE_ZEITPLAN_TYPES',
'AbtZeiten' => 'DB_TABLE_ABTEILUNGEN_ZEITEN',
'RankLiveConfigs' => 'DB_TABLE_RANKLIVE_CONFIGS'
];
+2 -2
View File
@@ -29,7 +29,7 @@ try {
]);
}
if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_GUEST_USER']) || !isset($_ENV['DB_GUEST_PASSWORD'])){
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'
@@ -37,7 +37,7 @@ if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_GUE
exit;
}
$guest = @new mysqli($_ENV['DB_HOST'], $_ENV['DB_GUEST_USER'], $_ENV['DB_GUEST_PASSWORD'], $_ENV['DB_NAME']);
$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,
+2 -2
View File
@@ -56,7 +56,7 @@ try {
]);
}
if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_USER']) || !isset($_ENV['DB_PASSWORD'])){
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'
@@ -64,7 +64,7 @@ if (!isset($_ENV['DB_HOST']) || !isset($_ENV['DB_NAME']) || !isset($_ENV['DB_USE
exit;
}
$mysqli = @new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
$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,
+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);
}
}
+20 -21
View File
@@ -10,11 +10,11 @@ if ($userid > 0) {
$result = $stmt->get_result();
if ($result) {
$dbarray = $result->fetch_assoc(); // $programme is an array
$dbarray = $result->fetch_assoc();
}
$freigabe_json = $dbarray['freigabe'];
$username = $dbarray['username'];
$freigabe_json = $dbarray['freigabe'] ?? '';
$username = $dbarray['username'] ?? '';
$stmt->close();
@@ -24,27 +24,26 @@ if ($userid > 0) {
$arrayfreigaben = $arrayfreigaben['freigabenKampfrichter'] ?? [];
}
}
if (!empty($arrayfreigaben)) {
$key = array_search('admin', $arrayfreigaben, true);
if ($key !== false) {
unset($arrayfreigaben[$key]);
array_unshift($arrayfreigaben, 'admin');
$arrayfreigaben = array_values($arrayfreigaben);
}
$selectedfreigabe = $_SESSION['selectedFreigabeKampfrichter'] ?? $arrayfreigaben[0];
if (!in_array($selectedfreigabe, $arrayfreigaben, true)) {
$selectedfreigabe = $arrayfreigaben[0];
}
$_SESSION['selectedFreigabeKampfrichter'] = $selectedfreigabe;
} else {
if (empty($arrayfreigaben)) {
echo 'Keine gültigen Freigaben! Sie wurden abgemeldet.';
$_SESSION['access_granted_kampfrichter'] = false;
$_SESSION['logoDisplay'] = true;
exit;
}
$selecteduser = $selectedfreigabe;
$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';
+55 -26
View File
@@ -4,44 +4,57 @@
$form_message = $_SESSION['form_message'] ?? '';
unset($_SESSION['form_message']);
if ((isset($_POST['prev_abt'])) && !empty($_POST['prev_abt_submit'])) {
if (isset($_POST['prev_abt_submit'])) {
verify_csrf();
$value = $aktabt;
if ($value > 1){
$value -= 1;
$name = 'wk_panel_current_abt';
$stmt = $mysqli->prepare("INSERT INTO $tableVar (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUE(`value`)");
$stmt->bind_param("ss", $name, $value);
$stmt->execute();
$stmt->close();
db_update_variable($mysqli, $tableVar, ['wk_panel_current_abt', $value]);
}
header("Location: /intern/kampfrichter");
exit;
}
if ((isset($_POST['next_abt'])) && !empty($_POST['next_abt_submit'])) {
if (isset($_POST['next_abt_submit'])) {
verify_csrf();
$value = $aktabt;
$maxvalue = db_get_var($mysqli, "SELECT name FROM $tableAbt ORDER BY name DESC LIMIT 1");
$maxvalue = (int) (db_get_var($mysqli, "SELECT name FROM $tableAbt ORDER BY name DESC LIMIT 1") ?? 1);
if ($value < $maxvalue){
$value += 1;
$name = 'wk_panel_current_abt';
$stmt = $mysqli->prepare("INSERT INTO $tableVar (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUE(`value`)");
$stmt->bind_param("ss", $name, $value);
$stmt->execute();
$stmt->close();
db_update_variable($mysqli, $tableVar, ['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, $tableVar, ['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, $tableVar, ['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;
@@ -56,7 +69,7 @@ if ($_SESSION['last_abt'] !== $aktabt){
$_SESSION['last_abt'] = $aktabt;
}
if ((isset($_POST['prev_subabt'])) && !empty($_POST['prev_subabt_submit'])) {
if (isset($_POST['prev_subabt_submit'])) {
verify_csrf();
$value = $_SESSION['currentsubabt'];
if ($value > 1){
@@ -68,10 +81,10 @@ if ((isset($_POST['prev_subabt'])) && !empty($_POST['prev_subabt_submit'])) {
exit;
}
if ((isset($_POST['next_subabt'])) && !empty($_POST['next_subabt_submit'])) {
if (isset($_POST['next_subabt_submit'])) {
verify_csrf();
$value = $_SESSION['currentsubabt'];
if ($value < $maxsubabt){
if ($value < $max_subabt){
$_SESSION['currentsubabt']++;
$_SESSION['currentEditId'] = false;
$_SESSION['last_abt'] = $aktabt;
@@ -80,12 +93,28 @@ if ((isset($_POST['next_subabt'])) && !empty($_POST['next_subabt_submit'])) {
exit;
}
if ( isset($_POST['togle_advanced_mode_admin']) && !empty($_POST['togle_advanced_mode_admin_submit']) && !empty($_POST['csrf_token'])) {
if (isset($_POST['prog_view_admin_submit'])) {
verify_csrf();
$current_value = $focus_view_admin;
$new_value = !$current_value;
$_SESSION['abtViewAdmin'] = $new_value;
$_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;
+78 -174
View File
@@ -1,5 +1,22 @@
<?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();
@@ -83,187 +100,74 @@ if ($_SESSION['lockout_time_'. $logintype] > time()) {
}
}
$array_logintitles = [
'wk_leitung' => 'Wettkampfleitung',
'trainer' => 'Trainer',
'kampfrichter' => 'Kampfrichter'
]
?>
<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">
<?php
if (str_contains($logintype, '_')) {
$titlelogintype = str_replace('_', '-', $logintype);
} else {
$titlelogintype = $logintype . 'panel';
}
?>
<h1>Anmeldung<br><?= ucfirst($titlelogintype) ?></h1>
<p style="font-weight:400; line-height: 1.5; margin-bottom: 50px;">Bitte verwenden Sie hier Ihren
individuellen Zugang
</p>
<!DOCTYPE html>
<html lang="de">
<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>
<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">
<?php if ($error !== ''): ?>
<p style="color:red;"><?php echo $error; ?></p>
<?php endif; ?>
</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>
</section>
<div class="bg-secure-login">
<div class="bg-secure-login-form">
<h1>Anmeldung<br><?= $array_logintitles[$logintype] ?? "" ?></h1>
<a class="seclog_home_link" href="/"><img src="/intern/img/logo-normal.png" width="64" height="64"></a>
<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>
<style>
body{
overflow: hidden;
}
.page-secure-login{
display: flex;
}
.bg-picture-secure-login{
width: calc(100vw - 450px);
height: 100vh;
position: absolute;
left: 0px;
top: 0px;
}
.bg-picture-secure-login img{
width: 100%;
height: 100vh;
object-fit: cover;
}
.bg-secure-login{
display: flex;
width: 100vw;
max-width: 450px;
height: 100vh;
background-color: #fff;
position: absolute;
right: 0px;
top: 0px;
align-items: center;
padding: 30px;
}
.bg-secure-login-form > h1{
color: #000 !important;
font-size: 32px;
}
.bg-secure-login-form input[type=password], .bg-secure-login-form input[type=text]{
padding: 5px;
width: 100%;
max-width: 300px;
border-top: none !important;
border-left: none !important;
border-right: none !important;
font-size: 16px;
border-bottom: 1px solid #000 !important;
border-radius: 0px !important;
}
#access_username {
margin-bottom: 20px;
}
<?php if ($error !== ''): ?>
<p style="color:red;"><?php echo $error; ?></p>
<?php endif; ?>
</div>
</div>
</section>
.bg-secure-login-form input[type=password]:focus, .bg-secure-login-form input[type=text]:focus{
outline: none;
border-bottom: 1px solid #000 !important;
}
<a class="seclog_home_link" href="/"><img src="/intern/img/logo-normal.png"></a>
.bg-secure-login-form input[type=password]::placeholder, .bg-secure-login-form input[type=text]::placeholder {
color: #ccc !important;
}
<script>
const passwordInput = document.getElementById('access_passcode');
const toggleButton = document.getElementById('togglePassword');
const eyeIcon = document.getElementById('eyeIcon');
.bg-secure-login-form input[type=submit]{
background-color: #fff !important;
padding: 10px 20px !important;
margin-top: 25px !important;
border: 1px solid #000 !important;
color: #000 !important;
transition: all 0.3s ease-out !important;
border-radius: 0px !important;
}
toggleButton.addEventListener('click', () => {
const type = passwordInput.getAttribute('type') === 'password' ? 'text' : 'password';
passwordInput.setAttribute('type', type);
body{
color: #000 !important;
}
.bg-secure-login-form input[type=submit]:hover{
background-color: #000 !important;
color: #fff !important;
}
.bg-secure-login-form > p{
margin-bottom: 30px;
}
.seclog_home_link{
position: fixed;
z-index: 1000;
top: 30px;
right: 30px;
}
#div_showpw, #access_username {
margin-top: 10px;
}
#togglePassword {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
transition: all 0.5s ease;
}
#togglePassword:hover {
transform: translateY(-50%) scale(1.15);
}
#div_showpw{
position: relative;
display: inline-block;
width: 100%;
max-width: 300px;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
box-shadow: 0 0 0 1000px #ffffff inset !important;
-webkit-text-fill-color: #000000 !important;
transition: background-color 5000s ease-in-out 0s;
}
</style>
<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);
// Swap between eye and eye-with-line
if (type === 'password') {
// Eye (show)
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 {
// Eye with slash (hide)
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>
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;
}
}
?>
+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],
];
}
}
+31 -6
View File
@@ -48,22 +48,41 @@ function verify_csrf() {
die("Access Denied: Invalid CSRF Token.");
}
} else {
http_response_code(403);
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) {
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 {
http_response_code(403);
die("Invalid User Type Configuration");
if ($display) {
http_response_code(403);
include __DIR__ . "/../www/error-pages/403.html";
exit;
} else {
http_response_code(403);
die("Invalid User Type Configuration");
}
}
}
@@ -77,8 +96,14 @@ function check_user_permission(string $type, bool $return = false) {
if ($return) {
return false;
} else {
http_response_code(403);
die("Access Denied");
if ($display) {
http_response_code(403);
include __DIR__ . "/../www/error-pages/403.html";
exit;
} else {
http_response_code(403);
die("Access Denied");
}
}
}
+212
View File
@@ -0,0 +1,212 @@
<?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>',
'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($tableInternUsers)) {
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 $tableInternUsers 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];
}
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
}
}
@@ -33,10 +33,20 @@ class NotenRechner {
private function santinzeString(string $string) : string
{
$replacePattern = '/[^a-zA-Z0-9(){}+*\/.\-\[\]]/';
$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);
@@ -78,29 +88,67 @@ class NotenRechner {
}
private function insertValues(string $schemaRaw, array $valuesArray = [])
public function getBenoetigteIdsComplexWithRun(string $schemaRaw)
{
$schema = $this->santinzeString($schemaRaw);
$schema = $this->sanitizeStringWithRun($schemaRaw);
$indexedMatches = [];
$idsNeeded = $this->getBenoetigteIds($schemaRaw);
$missingIds = array_diff($idsNeeded, array_keys($valuesArray));
if (!empty($missingIds)) {
return ['success' => false, 'value' => 'Fehlende IDs'];
}
try {
$returnStr = preg_replace_callback('/\{(\d+)\}/', function($m) use ($valuesArray) {
return $valuesArray[$m[1]];
}, $schema);
return ['success' => true, 'value' => $returnStr];
} catch (\Exception $e) {
return ['success' => false, 'value' => 'Problem beim Einsetzen der Werte'];
// 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;
}
private function insertValuesComplex(string $schemaRaw, array $valuesArray, int $currentId, int $runNumber)
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);
@@ -110,22 +158,26 @@ class NotenRechner {
$noteId = $sina['noteId'];
$geraetIdSearch = ($sina['geraetId'] === 'A') ? $currentId : $sina['geraetId'];
if (!isset($valuesArray[$geraetIdSearch][$noteId][$runNumber])) {
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) {
$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;
// Return value from [Device][Note][Run]
return $valuesArray[$geraetId][$noteId][$runNumber] ?? 0;
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];
@@ -144,27 +196,15 @@ class NotenRechner {
$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'];
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 berechneString(string $schemaRaw, array $valuesArray = []) {
public function berechneStringComplex(string $schemaRaw, array $valuesArray = [], int $geraetId = -1, int $run_number = 0) {
if ($schemaRaw === '') { return ['success' => false, 'value' => 'Leeres Schema']; }
$rechnungArray = $this->insertValues($schemaRaw, $valuesArray);
if (!isset($rechnungArray['success']) || !isset($rechnungArray['value']) || !$rechnungArray['success']) {
return ['success' => false, 'value' => $rechnungArray['value'] ?? 'Fehler beim Einsetzen der Werte oder Werte fehlen'];
}
return $this->calculate($rechnungArray['value']);
}
public function berechneStringComplex(string $schemaRaw, array $valuesArray = [], int $geraetId = 0, int $run_number = 0) {
if ($schemaRaw === '') { return ['success' => false, 'value' => 'Leeres Schema']; }
if ($geraetId === 0) { return ['success' => false, 'value' => 'Keine Geraet Id angegeben.']; }
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);
@@ -176,9 +216,9 @@ class NotenRechner {
return $this->calculate($rechnungArray['value']);
}
public function berechneStringComplexRun(string $schemaRaw, array $valuesArray = [], int $geraetId = 0, int $programm = 0, array $arrayRuns = []) {
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 ($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.']; }
@@ -213,9 +253,7 @@ class NotenRechner {
foreach ($ids as $sid) {
$geraetId = ($sid['geraetId'] === 'A') ? $currentId : $sid['geraetId'];
$runCountN = $arrayRuns[$sid['noteId']][$geraetId][$programm] ?? $arrayRuns["default"] ?? 1;
//var_dump($arrayRuns[$sid['noteId']][$geraetId][$programm]);
$runCountN = $arrayRuns[$sid['noteId']][$geraetId][$programm] ?? $arrayRuns[$sid['noteId']][$geraetId]["all"] ?? $arrayRuns["default"] ?? 1;
if ($runCount !== $runCountN && $runCount !== 0) { return; }
$runCount = $runCountN;
@@ -225,21 +263,26 @@ class NotenRechner {
switch ($name) {
case "AVGR":
case "SUMR":
$nulls = 0;
for ($r = 1; $r <= $runCount; $r++) {
$res = $this->insertValuesComplex($content, $valuesArray, $currentId, $r);
// FIXED: Only bail if success is false
$res = $this->insertValuesComplex($content, $valuesArray, $currentId, $r, true);
if (!$res['success']) {
return "0"; // Or handle error as needed
return "0";
} elseif ($res['value'] === 'default_value_skip') {
$nulls++;
} else {
$parts[] = "(" . $res['value'] . ")";
}
$parts[] = "(" . $res['value'] . ")";
}
$innerMath = implode(" + ", $parts);
$string = ($name === "AVGR") ? "(($innerMath) / $runCount)" : "($innerMath)";
$div_count = $runCount - $nulls;
if ($div_count === 0 || count($parts) < 1) return "0";
$string = ($name === "AVGR") ? "(($innerMath) / $div_count)" : "($innerMath)";
//var_dump($string);
break;
case "MAXR":
+48 -183
View File
@@ -1,13 +1,9 @@
<?php
require $baseDir . '/../composer/vendor/autoload.php';
use Shuchkin\SimpleXLSX;
if (isset($_POST['apply_bulk_action']) ) {
if (isset($_POST['apply_bulk_action'])) {
verify_csrf();
if ( empty($_POST['turnerin_ids']) || !is_array($_POST['turnerin_ids']) ) {
$_SESSION['form_message'] = 'Keine Turnerinnen für die Aktion ausgewählt.';
if (empty($_POST['turnerin_ids']) || !is_array($_POST['turnerin_ids'])) {
$_SESSION['form_message'] = 'Keine ' . $personMehrzahl . ' für die Aktion ausgewählt.';
$_SESSION['form_message_type'] = 0;
} elseif (!isset($_POST['bulk_action_programm']) && !isset($_POST['bulk_action_bezahlt'])) {
$_SESSION['form_message'] = 'Kein Programm für die Massenänderung ausgewählt.';
@@ -36,7 +32,7 @@ if (isset($_POST['apply_bulk_action']) ) {
if (in_array($bezahlt_update, ['0', '3', '4', '5'], true)) {
$set_clauses[] = 'bezahltoverride = ?';
$params[] = (int)$bezahlt_update;
$params[] = (int) $bezahlt_update;
$types .= 'i';
}
@@ -53,7 +49,7 @@ if (isset($_POST['apply_bulk_action']) ) {
/* WHERE id IN (?, ?, ...) */
$placeholders = implode(',', array_fill(0, count($ids_to_update), '?'));
$sql = "UPDATE $tableTurnerinnen SET " . implode(', ', $set_clauses) . " WHERE id IN ($placeholders)";
$sql = "UPDATE $tableTeilnehmende SET " . implode(', ', $set_clauses) . " WHERE id IN ($placeholders)";
$stmt = $mysqli->prepare($sql);
@@ -86,17 +82,17 @@ if (isset($_POST['apply_bulk_action']) ) {
}
header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
exit;
}
if (isset($_POST['delete_id'])) {
verify_csrf();
$delete_id = intval($_POST['delete_id']);
$stmt = $mysqli->prepare("DELETE FROM $tableTurnerinnen where id = ?");
$stmt = $mysqli->prepare("DELETE FROM $tableTeilnehmendee where id = ?");
$stmt->bind_param('i', $delete_id);
if ($stmt->execute()) {
$_SESSION['form_message'] = 'Eintrag erfolgreich gelöscht.';
$_SESSION['form_message_type'] = 1;
@@ -105,211 +101,80 @@ if (isset($_POST['delete_id'])) {
$_SESSION['form_message_type'] = 0;
}
header("Location: ". $_SERVER['REQUEST_URI']);
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['xlsx_file'])) {
verify_csrf();
if ($_FILES['xlsx_file']['error'] === UPLOAD_ERR_OK) {
$tmpName = $_FILES['xlsx_file']['tmp_name'];
if (class_exists('Shuchkin\\SimpleXLSX') && $xlsx = SimpleXLSX::parse($tmpName)) {
$rows = $xlsx->rows();
$vereine_rows = db_select($mysqli, $tableVereine, 'verein', '', [], 'verein ASC');
$vereine = array_column($vereine_rows, 'verein');
if (count($rows) < 2) {
$excelMessage = '❌ Excel must have headers and at least one data row.';
} else {
$headers = array_map('trim', $rows[0]);
unset($rows[0]);
$columnMap = [
'Nachname' => 'name',
'Vorname' => 'vorname',
'Geburtsdatum' => 'geburtsdatum',
'Programm' => 'programm'
];
if ($selectedverein === 'admin') {
$columnMap['Verein'] = 'verein';
}
$columnIndexes = [];
foreach ($columnMap as $excelHeader => $dbColumn) {
$index = array_search($excelHeader, $headers);
if ($index === false) {
$excelMessage = "❌ Column '$excelHeader' not found in Excel.";
break;
}
$columnIndexes[$dbColumn] = $index;
}
if (empty($excelMessage)) {
$inserted = 0;
foreach ($rows as $row) {
if (!array_filter($row)) continue;
$data = [];
foreach ($columnIndexes as $dbCol => $i) {
$data[$dbCol] = isset($row[$i]) ? trim($row[$i]) : null;
}
if ($selectedverein !== 'admin'){
$data['verein'] = $selectedverein;
} else {
if (!in_array($data['verein'], $vereine, true)) {
$excelMessage = "❌ admin: {$data['verein']} not valid";
}
}
$raw = trim($data['geburtsdatum']);
// Try DD.MM.YYYY first
$temp = DateTime::createFromFormat('d.m.Y', $raw);
if ($temp && $temp->format('d.m.Y') === $raw) {
$data['geburtsdatum'] = $temp->format('Y-m-d');
} else {
// Fallback: if it's already YYYY-MM-DD or YYYY-MM-DD HH:MM:SS
$data['geburtsdatum'] = substr($raw, 0, 10); // take first 10 chars
}
if (!(in_array($data['programm'], $programmes)) && is_array($programmes)){
$_SESSION['form_message'] = "❌ Programm '{$data['programm']}' nicht valide bei Turnerin ".$data['name']." ".$data['vorname'].". Alle Turnereinnen nach ".$data['name']." ".$data['vorname']." wurden nicht geladen.";
$_SESSION['form_message_type'] = 0;
header('Location: '. $_SERVER['REQUEST_URI']); // Redirect to same page
exit;
}
if (!empty($tableTurnerinnen)) {
$columns = array_keys($data);
$set = implode(
', ',
array_map(fn($col) => "$col = ?", $columns)
);
$sql = "INSERT INTO $tableTurnerinnen SET $set";
$stmt = $mysqli->prepare($sql);
$types = str_repeat('s', count($data));
$values = array_values($data);
$stmt->bind_param($types, ...$values);
if (!$stmt->execute()) {
echo 'DB error: ' . $stmt->error;
}
$stmt->close();
$inserted++;
}
}
$_SESSION['form_message'] = "✅ Erfolgreich $inserted Turnerinnen via Excel geladen.";
$_SESSION['form_message_type'] = 1;
header("Location: ". $_SERVER['REQUEST_URI']); // Redirect to same page
exit;
}
}
} else {
$parseError = SimpleXLSX::parseError();
$excelMessage = '❌ Failed to parse Excel file: ' . $parseError;
}
} else {
$excelMessage = '❌ File upload error.';
}
}
$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, $tableTurnerinnen, "*", 'id = ?', [$edit_id]);
if (!isset($edit_rows) || !is_array($edit_rows) || count($edit_rows) !== 1){http_response_code(422); exit;}
$edit_rows = db_select($mysqli, $tableTeilnehmende, "*", '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 || $selectedverein === 'admin')) {
if ($edit_row && ($edit_row['verein'] === $selectedverein || $isAdmin)) {
$_POST['nachname'] = $edit_row['name'] ?? '';
$_POST['vorname'] = $edit_row['vorname'] ?? '';
$_POST['geburtsdatum'] = $edit_row['geburtsdatum'] ?? '';
$_POST['programm'] = $edit_row['programm'] ?? '';
$_POST['edit_id'] = $edit_id;
if ($selectedverein === 'admin'){
if ($isAdmin) {
$_POST['verein'] = $edit_row['verein'] ?? '';
if (intval($edit_row['bezahltoverride']) !== 0) {
$_POST['bezahltoverride'] = $edit_row['bezahltoverride'] ?? '';
} else {
$_POST['bezahltoverride'] = $edit_row['bezahlt'] ?? '';
}
$_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']);
header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
}
// === INSERT/UPDATE Handler ===
if ( $access_granted_trainer && isset($_POST['submit_turnerinnen_form']) ) {
// Check nonce
if ($access_granted_trainer && isset($_POST['submit_turnerinnen_form'])) {
verify_csrf();
$name = htmlspecialchars( $_POST['nachname'] );
$vorname = htmlspecialchars( $_POST['vorname'] );
$geburtsdatum = trim($_POST['geburtsdatum'] );
$programm = htmlspecialchars( $_POST['programm'] );
if ($selectedverein !== 'admin'){
$verein = $selectedverein;
} else {$verein = htmlspecialchars( $_POST['verein'] ); $bezahlt = htmlspecialchars( $_POST['bezahlt'] ); }
if ( empty($name) || empty($vorname) || empty($geburtsdatum) || empty($programm)) {
$name = htmlspecialchars($_POST['nachname']);
$vorname = htmlspecialchars($_POST['vorname']);
$geburtsdatum = trim($_POST['geburtsdatum']);
$programm = htmlspecialchars($_POST['programm']);
if (!$isAdmin) {
$verein = $selectedverein;
} else {
$verein = htmlspecialchars($_POST['verein']);
$bezahlt = intval($_POST['bezahltOverride']);
}
if (empty($name) || empty($vorname) || empty($geburtsdatum) || empty($programm)) {
$_SESSION['form_message'] = 'Bitte füllen Sie alle erforderlichen Felder aus.';
$_SESSION['form_message_type'] = 0;
} else {
$data_to_insert = [];
$data_to_insert = array(
'name' => $name,
'vorname' => $vorname,
$data_to_insert = [
'name' => $name,
'vorname' => $vorname,
'geburtsdatum' => $geburtsdatum,
'programm' => $programm,
'verein' => $verein,
);
'verein' => $verein,
];
$data_formats = array('%s', '%s', '%s', '%s', '%s');
if ($selectedverein === 'admin') {
if ($isAdmin) {
$data_to_insert['bezahltoverride'] = $bezahlt;
$data_formats[] = '%d';
}
print_r($data_to_insert);
// 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']);
$entries = db_select($mysqli, $tableTurnerinnen, '*', 'id = ?', [$edit_id], 'rang ASC');
$entries = db_select($mysqli, $tableTeilnehmende, '*', 'id = ?', [$edit_id], 'rang ASC');
$entry = $entries[0]; // since you're fetching by ID, this should return exactly one row
@@ -320,7 +185,7 @@ if ( $access_granted_trainer && isset($_POST['submit_turnerinnen_form']) ) {
array_map(fn($col) => "$col = ?", $columns)
);
$sql = "UPDATE $tableTurnerinnen SET $set WHERE id = ?";
$sql = "UPDATE $tableTeilnehmende SET $set WHERE id = ?";
var_dump($sql);
$stmt = $mysqli->prepare($sql);
@@ -337,13 +202,13 @@ if ( $access_granted_trainer && isset($_POST['submit_turnerinnen_form']) ) {
if ($updated === false) {
error_log('DB Update Error: ' . $wpdb->last_error);
$_SESSION['form_message'] = 'Fehler beim Aktualisieren des Eintrags.';
$_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'] = 'Eintrag erfolgreich aktualisiert!';
$_SESSION['form_message'] = $personEinzahl . ' erfolgreich aktualisiert!';
$_SESSION['form_message_type'] = 1;
$_POST = [];
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
@@ -358,11 +223,11 @@ if ( $access_granted_trainer && isset($_POST['submit_turnerinnen_form']) ) {
array_map(fn($col) => "$col = ?", $columns)
);
$sql = "INSERT INTO $tableTurnerinnen SET $set";
$sql = "INSERT INTO $tableTeilnehmende SET $set";
$stmt = $mysqli->prepare($sql);
$types = str_repeat('s', count($data_to_insert));
$types = str_repeat('s', count($data_to_insert));
$values = array_values($data_to_insert);
$stmt->bind_param($types, ...$values);
@@ -372,7 +237,7 @@ if ( $access_granted_trainer && isset($_POST['submit_turnerinnen_form']) ) {
$stmt->close();
if ( $inserted ) {
if ($inserted) {
$_SESSION['form_message'] = 'Daten erfolgreich gespeichert!';
$_SESSION['form_message_type'] = 1;
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
+80 -13
View File
@@ -1,5 +1,14 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
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'],
@@ -11,16 +20,13 @@ $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once __DIR__ . '/../redis/connect-to-redis.php';
$redisSaveTime = 100;
$redis = null;
connectToRedis();
function checkWSTokenPermissions($RPerm) {
switch ($RPerm) {
case 'displaycontrol':
return $_SESSION['access_granted_wk_leitung'] ?? false;
case 'einstellungen':
case 'wk_leitung':
return $_SESSION['access_granted_wk_leitung'] ?? false;
case 'kampfrichter';
return $_SESSION['access_granted_kampfrichter'] ?? false;
@@ -29,22 +35,56 @@ function checkWSTokenPermissions($RPerm) {
}
}
function generateWSToken($wsRequestedPermission) {
global $redis;
$envFile = realpath(__DIR__ . '/../../config/.env.wk-id');
if (!checkWSTokenPermissions($wsRequestedPermission)) return null;
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 ($wsRequestedPermission === 'kampfrichter') {
if (!isset($_SESSION['selectedFreigabeKampfrichter'])) return null;
if ($wsRequestedAcesstype === 'kampfrichter' && isset($_SESSION['selectedFreigabeIdKampfrichter'])) {
$wsGrantedPermission['type'] = 'kampfrichter';
$wsGrantedPermission['access'] = $_SESSION['selectedFreigabeKampfrichter'];
$wsGrantedPermission['access'] = $_SESSION['selectedFreigabeIdKampfrichter'];
} elseif ($wsRequestedAcesstype === 'wk_leitung' && in_array($wsRequestedAccess, $allowedWkLeitungAccess)) {
$wsGrantedPermission['type'] = $wsRequestedAcesstype;
$wsGrantedPermission['access'] = $wsRequestedAccess;
} else {
$wsGrantedPermission['type'] = $wsRequestedPermission;
return null;
}
$wsGrantedPermission['wk_id'] = $wkId;
$wsGrantedPermission['authenticated'] = true;
$token = bin2hex(random_bytes(32));
$key = "ws:token:" . $token;
@@ -52,5 +92,32 @@ function generateWSToken($wsRequestedPermission) {
$_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;
}