First version, for githup; UNSTABLE, DO NOT USE!

This commit is contained in:
Fabio Herzig
2026-04-12 21:25:44 +02:00
commit a51fd9dbeb
423 changed files with 58560 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
<?php
function csrf_token() {
if (session_status() === PHP_SESSION_NONE) session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(64));
}
return $_SESSION['csrf_token'];
}
function verify_csrf() {
if (!isset($_POST['csrf_token']) ||
!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
return false;
}
return true;
}

170
scripts/db/.rstdb.php Normal file
View File

@@ -0,0 +1,170 @@
<?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();
?>

132
scripts/db/db-functions.php Normal file
View File

@@ -0,0 +1,132 @@
<?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;
}

64
scripts/db/db-tables.php Normal file
View File

@@ -0,0 +1,64 @@
<?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 = [
'Turnerinnen' => 'DB_TABLE_TURNERINNEN',
'Orders' => 'DB_TABLE_ORDERS',
'BasketItems' => 'DB_TABLE_BASKET_ITEMS',
'Var' => 'DB_TABLE_VARIABLES',
'OTL' => 'DB_TABLE_OTL',
'KrProtokoll' => 'DB_TABLE_KR_PROTOKOLL',
'Programme' => 'DB_TABLE_PROGRAMME',
'InternUsers' => 'DB_TABLE_INTERN_USERS',
'Vereine' => 'DB_TABLE_VEREINE',
'Abt' => 'DB_TABLE_ABTEILUNGEN',
'TurnerinnenAbt' => 'DB_TABLE_TURNERINNEN_ABTEILUNGEN',
'Geraete' => 'DB_TABLE_GERAETE',
'Audiofiles' => 'DB_TABLE_AUDIOFILES',
'Noten' => 'DB_TABLE_NOTEN',
'NotenBezeichnungen' => 'DB_TABLE_NOTEN_BEZEICHNUNGEN'
];
foreach ($tableDefinitions as $baseName => $envVarKey) {
$rawTableName = $_ENV[$envVarKey] ?? '';
$fullTableName = $prefix . $rawTableName;
$variableName = 'table' . ucfirst($baseName);
$$variableName = $fullTableName;
}

View File

@@ -0,0 +1,67 @@
<?php
use Dotenv\Dotenv;
if (!isset($token)){
http_response_code(403);
echo json_encode([
'success' => false,
'message' => 'security check failed: ERROR 01'
]);
exit;
}
if ($token !== 'QQa2UMbEYW8oOL7wz9DjtqECVCikSZsDuSdmzxiadEXFsKyujEUyQOW1AYMD2OqU8VXxClIRweRuWLzvBrZpPYL41e89Rs96tM7Lq1KpjA5E2mg2UfgvztheGRV'){
http_response_code(403);
echo json_encode([
'success' => false,
'message' => 'security check failed: ERROR 02'
]);
exit;
}
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'])){
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']);
if ($guest->connect_error) {
echo json_encode([
'success' => false,
'message' => "DB connection failed"
]);
exit;
}
$guest->set_charset("utf8");

View File

@@ -0,0 +1,87 @@
<?php
use Dotenv\Dotenv;
if(session_status() !== PHP_SESSION_ACTIVE) session_start();
if (!isset($type)){
return [
'success' => false,
'message' => 'no type'
];
}
if ($type === 'kr'){
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
} elseif ($type === 'tr'){
if (empty($_SESSION['access_granted_trainer']) || $_SESSION['access_granted_trainer'] !== true || empty($_SESSION['passcodetrainer_id']) || $_SESSION['passcodetrainer_id'] < 1) {
http_response_code(403);
exit;
}
} elseif ($type === 'wkl') {
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
} elseif ($type === 'otl') {
if (empty($_SESSION['set_new_password_id_user']) || empty($_SESSION['set_new_password_granted']) || $_SESSION['set_new_password_granted'] !== true || $_SESSION['set_new_password_id_user'] < 1 ) {
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'])){
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");
return [
'success' => true
];

View File

@@ -0,0 +1,56 @@
<?php
if ( empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || intval($_SESSION['passcodekampfrichter_id']) < 1 ) {
http_response_code(403);
exit;
}
// ========== Access control setup ==========
$userid = intval($_SESSION['passcodekampfrichter_id'] ?? 0);
$arrayfreigaben = [];
if ($userid > 0) {
$stmt = $mysqli->prepare("SELECT freigabe, username FROM $tableInternUsers WHERE id = ?");
$stmt->bind_param("s", $userid);
$stmt->execute();
$result = $stmt->get_result();
if ($result) {
$dbarray = $result->fetch_assoc(); // $programme is an array
}
$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)) {
$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 {
echo 'Keine gültigen Freigaben! Sie wurden abgemeldet.';
$_SESSION['access_granted_kampfrichter'] = false;
$_SESSION['logoDisplay'] = true;
exit;
}
$selecteduser = $selectedfreigabe;

View File

@@ -0,0 +1,381 @@
<?php
if ( empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || intval($_SESSION['passcodekampfrichter_id']) < 1 ) {
http_response_code(403);
exit;
}
// ========== Form handling logic ==========
$form_message = $_SESSION['form_message'] ?? '';
unset($_SESSION['form_message']);
// Handle recalculate all scores action
if ($selecteduser === 'admin' && isset($_POST['recalculate_scores'])) {
if (!verify_csrf()) {
$form_message = 'Sicherheitsproblem bei der Neuberechnung.';
} else {
$disciplines = ['sprung', 'barren', 'balken', 'boden'];
// Build column list
$columns = implode(', ', array_map(fn ($d) => "`note $d`", $disciplines));
$columns_array = array_merge(
['id', 'programm'],
array_map(fn($d) => "note $d", $disciplines)
);
$all_rows = db_select($mysqli, $tableTurnerinnen, $columns_array, 'bezahlt = ? OR bezahltoverride = ?', ['2', '5']);
$success = 0;
// Step 1: Calculate gesamtpunktzahl and update per row
foreach ($all_rows as $row) {
$sum = 0;
foreach ($disciplines as $discipline) {
$value = $row["note $discipline"];
if (is_numeric($value)) {
$sum += floatval($value);
}
}
db_update($mysqli, $tableTurnerinnen, ['gesamtpunktzahl' => $sum], ['id' => $row['id']]);
$success++;
}
// Step 2: Re-fetch rows grouped by programm with updated gesamtpunktzahl
$all_programms = db_get_col($mysqli, "SELECT DISTINCT programm FROM ".$tableTurnerinnen);
foreach ($all_programms as $programm) {
$group = db_select($mysqli, $tableTurnerinnen, ['id', 'gesamtpunktzahl', 'note sprung', 'note barren', 'note balken', 'note boden', 'geburtsdatum'], 'programm = ? AND (bezahlt = ? OR bezahltoverride = ?)', [$programm, '2', '5']);
usort($group, function ($a, $b) {
$scoreA = floatval($a['gesamtpunktzahl']);
$scoreB = floatval($b['gesamtpunktzahl']);
if ($scoreA !== $scoreB) return $scoreB <=> $scoreA;
// Only for top 3 tie-breaking
$scoresA = [
floatval($a['note sprung']),
floatval($a['note barren']),
floatval($a['note balken']),
floatval($a['note boden']),
];
$scoresB = [
floatval($b['note sprung']),
floatval($b['note barren']),
floatval($b['note balken']),
floatval($b['note boden']),
];
rsort($scoresA);
rsort($scoresB);
$sumTop3A = $scoresA[0] + $scoresA[1] + $scoresA[2];
$sumTop3B = $scoresB[0] + $scoresB[1] + $scoresB[2];
if (abs($sumTop3A - $sumTop3B) > 0.001) return $sumTop3B <=> $sumTop3A;
$sumTop2A = $scoresA[0] + $scoresA[1];
$sumTop2B = $scoresB[0] + $scoresB[1];
if (abs($sumTop2A - $sumTop2B) > 0.001) return $sumTop2B <=> $sumTop2A;
if (abs($scoresA[0] - $scoresB[0]) > 0.001) return $scoresB[0] <=> $scoresA[0];
// Younger participant ranks higher in case of full tie
$dateA = strtotime($a['geburtsdatum']);
$dateB = strtotime($b['geburtsdatum']);
return $dateB <=> $dateA; // later birthdate = younger = better
});
// Step 2: Assign ranks
$ranked = [];
$current_rank = 1;
$i = 0;
while ($i < count($group)) {
$current = $group[$i];
$tie_group = [$current];
$j = $i + 1;
while ($j < count($group)) {
$next = $group[$j];
// Tie logic
if ($current_rank <= 3) {
// Top 3: full tie-breaking
$is_tie =
round(floatval($current['gesamtpunktzahl']), 3) === round(floatval($next['gesamtpunktzahl']), 3) &&
round(floatval($current['note sprung']), 3) === round(floatval($next['note sprung']), 3) &&
round(floatval($current['note barren']), 3) === round(floatval($next['note barren']), 3) &&
round(floatval($current['note balken']), 3) === round(floatval($next['note balken']), 3) &&
round(floatval($current['note boden']), 3) === round(floatval($next['note boden']), 3) &&
$current['geburtsdatum'] === $next['geburtsdatum'];
} else {
// Ranks > 3: only check gesamtpunktzahl
$is_tie = round(floatval($current['gesamtpunktzahl']), 3) === round(floatval($next['gesamtpunktzahl']), 3);
}
if ($is_tie) {
$tie_group[] = $next;
$j++;
} else {
break;
}
}
// Assign the same rank for all ties in ranks > 3
foreach ($tie_group as $entry) {
$ranked[] = [
'id' => $entry['id'],
'rang' => $current_rank
];
}
$i += count($tie_group);
$current_rank += count($tie_group);
}
// Step 3: Write all ranks to DB
foreach ($ranked as $r) {
db_update($mysqli, $tableTurnerinnen, ['rang' => $r['rang']], ['id' => $r['id']]);
}
}
$_SESSION['form_message'] =
$success . ' Einträge wurden aktualisiert und neu gerankt.';
header("Location: ". $_SERVER['REQUEST_URI']);
exit;
}
}
if ($selecteduser === 'admin' && isset($_POST['reset_scores'])) {
if (!verify_csrf()) {
$form_message = 'Sicherheitsproblem bei der Neuberechnung.';
} else {
$all_rows = db_select($mysqli, $tableTurnerinnen, 'id');
$success = 0;
foreach ($all_rows as $row) {
db_update($mysqli, $tableTurnerinnen, ['gesamtpunktzahl' => 0, 'rang' => 0], ['id' => $row['id']]);
$success++;
}
$_SESSION['form_message'] = '<div class="success">' . $success . ' Einträge wurden zurückgesetzt.</div>';
header("Location: ". $_SERVER['REQUEST_URI']);
exit;
}
}
// === EDIT MODE: Load existing data if edit_id is present in URL ===
$edit_row = null;
if (isset($_GET['edit_id']) && is_numeric($_GET['edit_id']) && !isset($_POST['submit_turnerinnen_form'])) {
$edit_id = intval($_GET['edit_id']);
$edit_row = db_select($mysqli, $tableTurnerinnen, '*', 'id = ?', [$edit_id]);
foreach ($disciplines as $discipline) {
if ($selecteduser === ucfirst($discipline) || $selecteduser === 'admin') {
$_POST["d-note_{$discipline}"] = $edit_row["d-note {$discipline}"] ??
'';
$_POST["note_{$discipline}"] = $edit_row["note {$discipline}"] ?? '';
}
}
if ($selecteduser === 'admin') {
$gesamt = 0;
foreach ($disciplines as $discipline) {
if (isset($edit_row[0]["note {$discipline}"]) && is_numeric($edit_row[0]["note {$discipline}"])) {
$gesamt += floatval($edit_row[0]["note {$discipline}"]);
}
}
$_POST["gesamtpunktzahl"] = $gesamt;
$rang = isset($_POST['rang']) ? intval($_POST['rang']) : 0; // Safely get and cast to int
$data_to_insert["rang"] = $rang;
$data_formats[] = '%d';
}
$_POST['edit_id'] = $edit_id;
}
if (isset($_POST['submit_turnerinnen_form'])) {
// Check nonce
if (!verify_csrf()) {
$form_message = 'Sicherheitsproblem: Ungültige Formularübermittlung.';
} else {
foreach ($disciplines as $discipline) {
if ($selecteduser === ucfirst($discipline) || $selecteduser === 'admin') {
${"d_note_$discipline"} = floatval($_POST["d-note_{$discipline}"]);
${"note_$discipline"} = floatval($_POST["note_{$discipline}"]);
// Add to data array
$data_to_insert["d-note $discipline"] = ${"d_note_$discipline"};
$data_to_insert["note $discipline"] = ${"note_$discipline"};
$data_formats[] = '%f'; // float format for d-note
$data_formats[] = '%f';
// float format for note
}
}
if ($selecteduser === 'admin') {
$gesamtpunktzahl = isset($_POST['gesamtpunktzahl']) ?
intval($_POST['gesamtpunktzahl']) : 0; // Safely get and cast to int
$data_to_insert["gesamtpunktzahl"] = $gesamtpunktzahl;
$data_formats[] = '%f';
// CORRECTED LINE: Get 'rang' from $_POST
$rang = isset($_POST['rang']) ?
intval($_POST['rang']) : 0; // Safely get and cast to int
$data_to_insert["rang"] = $rang;
$data_formats[] = '%d';
}
// 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']);
$updated = db_update($mysqli, $tableTurnerinnen, $data_to_insert, ['id' => $edit_id]);
if ($updated === false) {
$form_message = 'Fehler beim Aktualisieren des Eintrags.';
} else if ($updated === 0) {
$form_message = 'Keine Änderungen vorgenommen.';
} else {
$_SESSION['form_message'] = 'Eintrag erfolgreich aktualisiert!';
$_POST = [];
$parsed = parse_url($_SERVER['REQUEST_URI']);
if (!isset($parsed['query'])) {
return $url;
}
parse_str($parsed['query'], $query);
unset($query[$param]); // remove the parameter
$base = $parsed['path'] ?? '';
$new_query = http_build_query($query);
$url = $new_query ? $base . '?' . $new_query : $base;
header("Location: ". $url);
exit;
}
}
}
}
if ((isset($_POST['prev_abt'])) && !empty($_POST['prev_abt_submit'])) {
$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();
}
header("Location: /intern/kampfrichter");
exit;
}
if ((isset($_POST['next_abt'])) && !empty($_POST['next_abt_submit'])) {
$value = $aktabt;
$maxvalue = db_get_var($mysqli, "SELECT name FROM $tableAbt ORDER BY name DESC LIMIT 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();
}
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'])) && !empty($_POST['prev_subabt_submit'])) {
$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'])) && !empty($_POST['next_subabt_submit'])) {
$value = $_SESSION['currentsubabt'];
if ($value < $maxsubabt){
$_SESSION['currentsubabt']++;
$_SESSION['currentEditId'] = false;
$_SESSION['last_abt'] = $aktabt;
}
header("Location: /intern/kampfrichter");
exit;
}
if ( isset($_POST['togle_advanced_mode_admin']) && !empty($_POST['togle_advanced_mode_admin_submit']) ) {
$current_value = $focus_view_admin;
$new_value = !$current_value;
$_SESSION['abtViewAdmin'] = $new_value;
header("Location: /intern/kampfrichter");
exit;
}
if ((isset($_POST['upload_remove_pdf_for_programm'])) && !empty($_POST['programm_remove_export'])) {
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
$programm = trim($_POST['programm_remove_export']);
$dir = $_SERVER['DOCUMENT_ROOT'] . '/wp-content/ergebnisse';
if (!file_exists($dir)) {
mkdir($dir, 0755, true);
}
$localPath = $_SERVER['DOCUMENT_ROOT'] . "/wp-content/ergebnisse/KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf";
// --- ADDED CODE START ---
// Check if the file already exists and delete it
if (file_exists($localPath)) {
unlink($localPath);
}
// --- ADDED CODE END ---
$_SESSION['form_message'] = 'PDF wurde gelöscht';
header("Location: ". $_SERVER['REQUEST_URI']);
}

270
scripts/login/login.php Normal file
View File

@@ -0,0 +1,270 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
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'])) {
$token = "QQa2UMbEYW8oOL7wz9DjtqECVCikSZsDuSdmzxiadEXFsKyujEUyQOW1AYMD2OqU8VXxClIRweRuWLzvBrZpPYL41e89Rs96tM7Lq1KpjA5E2mg2UfgvztheGRV";
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 $tableInternUsers WHERE username = ? LIMIT 1");
$stmt->bind_param("s", $username);
$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['passcode'. $freigabe .'_id'] = $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.";
}
}
}
}
?>
<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>
<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" width="64" height="64"></a>
<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;
}
.bg-secure-login-form input[type=password]:focus, .bg-secure-login-form input[type=text]:focus{
outline: none;
border-bottom: 1px solid #000 !important;
}
.bg-secure-login-form input[type=password]::placeholder, .bg-secure-login-form input[type=text]::placeholder {
color: #ccc !important;
}
.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;
}
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>

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;
}

View File

@@ -0,0 +1,175 @@
<?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 {
public function __construct(
private StringCalc $rechner = new StringCalc()
) {}
/*
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
{
$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;
}
private function insertValues(string $schemaRaw, array $valuesArray = [])
{
$schema = $this->santinzeString($schemaRaw);
$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'];
}
}
private function insertValuesComplex(string $schemaRaw, array $valuesArray, int $currentId)
{
$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'];
$runNumber = $sina['run'] ?? 1;
if (!isset($valuesArray[$geraetIdSearch][$noteId][$runNumber])) {
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) {
$noteId = $m[1];
// Get value inside brackets, default to 'S' if none exists
$rawGeraet = $m[2] ?? 'S';
$geraetId = ($rawGeraet === 'S' || $rawGeraet === '') ? $currentId : $rawGeraet;
$runNumber = isset($m[3]) ? (int)$m[3] : 1;
// Return value from [Device][Note][Run]
return $valuesArray[$geraetId][$noteId][$runNumber] ?? 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'];
} catch (\Exception $e) {
return ['success' => false, 'value' => 'Problem bei der Berechnung'];
}
}
public function berechneString(string $schemaRaw, array $valuesArray = []) {
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) {
if ($schemaRaw === '') { return ['success' => false, 'value' => 'Leeres Schema']; }
if ($geraetId === 0) { return ['success' => false, 'value' => 'Keine Geraet Id angegeben.']; }
$rechnungArray = $this->insertValuesComplex($schemaRaw, $valuesArray, $geraetId);
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']);
}
}

View File

@@ -0,0 +1,440 @@
<?php
require $baseDir . '/../composer/vendor/autoload.php';
use Shuchkin\SimpleXLSX;
if (isset($_POST['apply_bulk_action']) ) {
if (!verify_csrf()) {
$_SESSION['form_message'] = 'Sicherheitsüberprüfung fehlgeschlagen.';
$_SESSION['form_message_type'] = 0;
} elseif ( empty($_POST['turnerin_ids']) || !is_array($_POST['turnerin_ids']) ) {
$_SESSION['form_message'] = 'Keine Turnerinnen 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.';
$_SESSION['form_message_type'] = 0;
} else {
$ids_to_update = array_map('intval', $_POST['turnerin_ids'] ?? []);
$new_programm = isset($_POST['bulk_action_programm']) ? trim($_POST['bulk_action_programm']) : '';
$bezahlt_update = $_POST['bulk_action_bezahlt'] ?? null;
if (empty($ids_to_update)) {
$_SESSION['form_message'] = 'Keine Einträge ausgewählt.';
$_SESSION['form_message_type'] = 0;
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
$set_clauses = [];
$params = [];
$types = '';
if ($new_programm !== '') {
$set_clauses[] = 'programm = ?';
$params[] = $new_programm;
$types .= 's';
}
if (in_array($bezahlt_update, ['0', '3', '4', '5'], true)) {
$set_clauses[] = 'bezahltoverride = ?';
$params[] = (int)$bezahlt_update;
$types .= 'i';
}
if (empty($set_clauses)) {
$_SESSION['form_message'] = 'Keine gültigen Änderungen gewählt.';
$_SESSION['form_message_type'] = 0;
header("Location: " . $_SERVER['REQUEST_URI']);
exit;
}
if (strlen($types) !== count($params) || count($params) !== count($set_clauses)) {
die('Type/value mismatch: ' . strlen($types) . ' vs ' . count($params));
}
/* WHERE id IN (?, ?, ...) */
$placeholders = implode(',', array_fill(0, count($ids_to_update), '?'));
$sql = "UPDATE $tableTurnerinnen SET " . implode(', ', $set_clauses) . " WHERE id IN ($placeholders)";
$stmt = $mysqli->prepare($sql);
/* add ID params */
foreach ($ids_to_update as $id) {
$params[] = $id;
$types .= 'i';
}
$stmt->bind_param($types, ...$params);
if (!$stmt->execute()) {
throw new RuntimeException('DB error: ' . $stmt->error);
}
$updated_count = $stmt->affected_rows;
$stmt->close();
if ($updated_count === -1) {
$_SESSION['form_message'] = 'Ein Fehler ist bei der Aktualisierung aufgetreten.';
$_SESSION['form_message_type'] = 0;
} elseif ($updated_count > 0) {
$_SESSION['form_message'] = $updated_count . ' Einträge erfolgreich aktualisiert.';
$_SESSION['form_message_type'] = 1;
} else {
$_SESSION['form_message'] = 'Keine Änderungen vorgenommen.';
$_SESSION['form_message_type'] = 0;
}
}
header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
if (isset($_POST['delete_id']) && verify_csrf()) {
$delete_id = intval($_POST['delete_id']);
$stmt = $mysqli->prepare("DELETE FROM $tableTurnerinnen 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;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['xlsx_file'])) {
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.';
}
}
if (!empty($_FILES['music_file']['name']) && isset($_POST['music_id'])) {
echo 'ja';
$edit_id = (int) $_POST['music_id'];
$uploadedFile = $_FILES['music_file'];
// Validate actual MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $uploadedFile['tmp_name']);
finfo_close($finfo);
$allowedTypes = ['audio/mpeg', 'audio/wav', 'audio/ogg'];
if (!in_array($mimeType, $allowedTypes, true)) {
die('Invalid file type.');
}
// Fetch and remove old file
$stmt = $mysqli->prepare("SELECT bodenmusik FROM $tableTurnerinnen WHERE id = ?");
$stmt->bind_param("i", $edit_id);
$stmt->execute();
$stmt->bind_result($oldurl);
$stmt->fetch();
$stmt->close();
if (!empty($oldurl) && is_file($oldurl)) unlink($oldurl);
// Ensure upload directory exists
$uploadDir = $baseDir . '/wk-musik-boden/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
// Clean file name
$cleanName = preg_replace("/[^a-zA-Z0-9-_\.]/", "_", $uploadedFile['name']);
$filename = uniqid('music_', true) . '_' . $cleanName;
$targetPath = $uploadDir . $filename;
if (move_uploaded_file($uploadedFile['tmp_name'], $targetPath)) {
$stmt = $mysqli->prepare("UPDATE $tableTurnerinnen SET bodenmusik = ? WHERE id = ?");
$stmt->bind_param("si", $targetPath, $edit_id);
if (!$stmt->execute()) echo 'DB error: ' . $stmt->error;
$stmt->close();
} else {
die('Failed to move uploaded file.');
}
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, $tableTurnerinnen, "*", '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')) {
$_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'){
$_POST['verein'] = $edit_row['verein'] ?? '';
if (intval($edit_row['bezahltoverride']) !== 0) {
$_POST['bezahltoverride'] = $edit_row['bezahltoverride'] ?? '';
} else {
$_POST['bezahltoverride'] = $edit_row['bezahlt'] ?? '';
}
}
} 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']) ) {
// Check nonce
if ( !verify_csrf() ) {
$_SESSION['form_message'] = 'Sicherheitsproblem: Ungültige Formularübermittlung.';
$_SESSION['form_message_type'] = 0;
} else {
$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)) {
$_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,
'geburtsdatum' => $geburtsdatum,
'programm' => $programm,
'verein' => $verein,
);
$data_formats = array('%s', '%s', '%s', '%s', '%s');
if ($selectedverein === 'admin') {
$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');
$entry = $entries[0]; // since you're fetching by ID, this should return exactly one row
$columns = array_keys($data_to_insert);
$set = implode(
', ',
array_map(fn($col) => "$col = ?", $columns)
);
$sql = "UPDATE $tableTurnerinnen 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 des Eintrags.';
$_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_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 $tableTurnerinnen 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;
}
}
}
}
}

View File

@@ -0,0 +1,56 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$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';
$redisSaveTime = 100;
$redis = null;
connectToRedis();
function checkWSTokenPermissions($RPerm) {
switch ($RPerm) {
case 'displaycontrol':
return $_SESSION['access_granted_wk_leitung'] ?? false;
case 'einstellungen':
return $_SESSION['access_granted_wk_leitung'] ?? false;
case 'kampfrichter';
return $_SESSION['access_granted_kampfrichter'] ?? false;
default:
return false;
}
}
function generateWSToken($wsRequestedPermission) {
global $redis;
if (!checkWSTokenPermissions($wsRequestedPermission)) return null;
$wsGrantedPermission = [];
if ($wsRequestedPermission === 'kampfrichter') {
if (!isset($_SESSION['selectedFreigabeKampfrichter'])) return null;
$wsGrantedPermission['type'] = 'kampfrichter';
$wsGrantedPermission['access'] = $_SESSION['selectedFreigabeKampfrichter'];
} else {
$wsGrantedPermission['type'] = $wsRequestedPermission;
}
$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;
}