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
+14 -5
View File
@@ -10,19 +10,28 @@ ini_wkvs_session();
verify_csrf();
check_multiple_allowed_permissions(['kampfrichter', 'wk_leitung']);
$accessPermission = trim($_POST['accessPermission'] ?? '');
$access = preg_replace("/[\W]/", "", trim($_POST['access'] ?? ''));
$accesstype = preg_replace("/[\W]/", "", trim($_POST['accesstype'] ?? ''));
if (!isset($_POST['access'])) {
if ($accessPermission === '' || $access === '' || $accesstype === '') {
echo json_encode(['success' => false, 'message' => 'Parameters not correctly set']);
http_response_code(400);
exit;
}
$access = preg_replace("/[\W]/", "", trim($_POST['access']));
require $baseDir . "/../scripts/websocket/ws-create-token.php";
$token = generateWSToken($access);
if ($accessPermission === "R") {
$token = generateWSReadToken($accesstype, $access);
} elseif ($accessPermission === "W") {
check_multiple_allowed_permissions(['kampfrichter', 'wk_leitung']);
$token = generateWSAdminToken($accesstype, $access);
} else {
http_response_code(403);
exit;
}
$responseBool = $token != null;
@@ -29,7 +29,7 @@ if (!$new_value) {
}
if ($type === 'kampfrichter'){
$_SESSION['selectedFreigabeKampfrichter'] = $new_value;
$_SESSION['selectedFreigabeIdKampfrichter'] = $new_value;
}
if ($type === 'trainer'){
@@ -10,6 +10,9 @@ check_multiple_allowed_permissions(['trainer', 'wk_leitung']);
verify_csrf();
$isTrainer = isset($_SESSION['access_granted_trainer']) && $_SESSION['access_granted_trainer'];
$userId = $isTrainer ? intval($_SESSION['user_id_trainer'] ?? 0) : intval($_SESSION['user_id_wk_leitung'] ?? 0);
// Allow large uploads and enough memory for GD processing
ini_set('memory_limit', '256M');
ini_set('max_execution_time', '120');
@@ -41,7 +44,23 @@ $normalDir = $saveDir;
$uploadDir = $baseDir . $saveDir;
$maxLengthMusic = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['maxLengthMusic']);
$maxLengthMusic = 0;
if ($isTrainer) {
$geraet_id = (int) $_POST['geraetId'] ?? 0;
$geraet_exists = db_get_var($mysqli, "SELECT 1 FROM $tableGeraete WHERE `id` = ?", [$geraet_id]);
if ($geraet_exists === null) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalides Gerät angegeben.']);
exit;
}
$geraet_max_duration_audio_db = db_get_var($mysqli, "SELECT `audiofile_max_duration` FROM $tableGeraete WHERE `id` = ?", [$geraet_id]);
$maxLengthMusic = (int) $geraet_max_duration_audio_db;
}
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
@@ -109,10 +128,10 @@ if ($isTrainer && $maxLengthMusic !== null && intval($maxLengthMusic) !== 0) {
}
$sql = "INSERT INTO $tableAudiofiles (`file_name`,`file_path`) VALUES (?, ?)";
$sql = "INSERT INTO $tableAudiofiles (`file_name`,`file_path`,`edited_by`) VALUES (?, ?, ?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ss", $originalName, $normalPath);
$stmt->bind_param("ssi", $originalName, $normalPath, $userId);
if (!$stmt->execute()) {
http_response_code(500);
@@ -26,7 +26,7 @@ if ($type === 'ctext'){
$ctext = isset($_POST['ctext']) ? $_POST['ctext'] : '';
}
$folder = realpath($baseDir.'/displays/json');
$folder = realpath($baseDir.'/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
@@ -1,9 +1,5 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
header('Content-Type: application/json');
if (!isset($baseDir)) {
@@ -31,10 +27,14 @@ require $baseDir . '/../scripts/db/db-tables.php';
$allowedTypes = [
'wkName',
'displayCTextLogo',
'displayCTextWKName',
'displayColourLogo',
'displayTextColourLogo',
'displayColorScoringBg',
'displayColorScoringBgSoft',
'displayColorScoringShadowColor',
'displayColorScoringBorderColor',
'displayColorScoringPanel',
'displayColorScoringPanelSoft',
'displayColorScoringPanelText',
@@ -53,8 +53,15 @@ $allowedTypes = [
'maxLengthMusic',
'linkWebseite',
'rangNote',
'orderBestRang'
'orderBestRang',
'rechnungenPostversand',
'rechnungenPostversandKosten',
'personEinzahl',
'personMehrzahl',
'rankLivePublic',
'riegeneinteilungPublic'
];
$type = $_POST['type'] ? trim($_POST['type']) : '';
if (!in_array($type, $allowedTypes)) {
@@ -16,7 +16,6 @@ verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
@@ -25,20 +24,24 @@ if ($dbconnection['success'] !== true){
exit;
}
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$start_index = intval($_POST['start_index'] ?? 0);
$color = trim($_POST['color'] ?? '#424242');
$audiofile = intval($_POST['audiofile'] ?? 0);
$audiofile_max_duration = (int) $_POST['audiofileMaxDuration'] ?? 0;
if (!$name) {
if ($name === '') {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $tableGeraete (name, start_index, color_kampfrichter) VALUES (?, ?, ?)");
$stmt->bind_param("sis", $name, $start_index, $color);
$stmt = $mysqli->prepare("INSERT INTO $tableGeraete (`name`, `start_index`, `color_kampfrichter`, `audiofile`, `audiofile_max_duration`) VALUES (?, ?, ?, ?, ?)");
$stmt->bind_param("sisii", $name, $start_index, $color, $audiofile, $audiofile_max_duration);
$success = $stmt->execute();
$new_id = $mysqli->insert_id;
$stmt->close();
@@ -49,14 +52,17 @@ if ($action === 'add') {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
}
generateIntersectCache($mysqli, $baseDir);
} elseif ($action === 'update') {
$id = intval($_POST['id'] ?? 0);
$field = $_POST['field'] ?? '';
$value = $_POST['value'] ?? '';
$audiofile = intval($_POST['audiofile'] ?? 0);
$allowedFields = ['name', 'start_index', 'color_kampfrichter'];
$allowedFields = ['name', 'start_index', 'color_kampfrichter', 'audiofile', 'audiofile_max_duration'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'start_index') {
if ($field === 'start_index' || $field === 'audiofile' || $field === 'audiofile_max_duration') {
$value = intval($value);
}
@@ -80,6 +86,8 @@ if ($action === 'add') {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
}
generateIntersectCache($mysqli, $baseDir);
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
}
@@ -1,4 +1,5 @@
<?php
ini_set("display_errors",1);
header('Content-Type: application/json');
if (!isset($baseDir)) {
@@ -16,6 +17,8 @@ verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
@@ -25,6 +28,11 @@ if ($dbconnection['success'] !== true){
exit;
}
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-noten-cache.php';
$recalculateJSONs = false;
$action = $_POST['action'] ?? '';
@@ -77,6 +85,8 @@ if ($action === 'add') {
$recalculateJSONs = true;
if ($success) {
regenerate_noten_cache($mysqli, $tableNotenBezeichnungen, $baseDir);
generateIntersectCache($mysqli, $baseDir);
if (!$recalculateJSONs) { echo json_encode(['success' => true, 'id' => $new_id]); }
} else {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
@@ -88,7 +98,7 @@ if ($action === 'add') {
$field = $_POST['field'] ?? '';
$value = $_POST['value'] ?? '';
$allowedFields = ['name', 'type', 'berechnung', 'pro_geraet', 'geraete_json', 'anzahl_laeufe_json', 'default_value', 'min_value', 'max_value', 'zeige_in_tabelle', 'zeige_in_tabelle_mobile', 'zeige_in_tabelle_admin', 'zeige_auf_rangliste', 'nullstellen', 'prefix_display'];
$allowedFields = ['name', 'type', 'berechnung', 'pro_geraet', 'geraete_json', 'anzahl_laeufe_json', 'default_value', 'min_value', 'max_value', 'zeige_auf_rangliste', 'nullstellen', 'display_string', 'groesse_auf_rangliste'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'pro_geraet') {
$value = intval($value);
@@ -100,6 +110,8 @@ if ($action === 'add') {
$updated = db_update($mysqli, $tableNotenBezeichnungen, [$field => $value], ['id' => $id]);
if ($updated !== false) {
regenerate_noten_cache($mysqli, $tableNotenBezeichnungen, $baseDir);
generateIntersectCache($mysqli, $baseDir);
if (!$recalculateJSONs) { echo json_encode(['success' => true]); }
} else {
echo json_encode(['success' => false, 'message' => 'DB Update failed.']);
@@ -115,6 +127,8 @@ if ($action === 'add') {
if ($id > 0) {
db_delete($mysqli, $tableNotenBezeichnungen, ['id' => $id]);
regenerate_noten_cache($mysqli, $tableNotenBezeichnungen, $baseDir);
generateIntersectCache($mysqli, $baseDir);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
@@ -143,7 +157,6 @@ if ($recalculateJSONs) {
exit;
}
require $baseDir . "/../scripts/string-calculator/string-calculator-functions.php";
$notenRechner = new NotenRechner();
// 1. Build the direct map
@@ -151,15 +164,23 @@ if ($recalculateJSONs) {
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArray = $notenRechner->getBenoetigteIdsComplex($calc['berechnung']);
$neededIdsArrayWithRun = $notenRechner->getBenoetigteIdsComplexWithRun($calc['berechnung']);
if (empty($neededIdsArray)) {
if (empty($neededIdsArrayWithRun) || count($neededIdsArrayWithRun) < 2) {
continue;
}
$calcId = (int)$calc['id'];
foreach ($neededIdsArray as $needed) {
$neededIdsArray = $neededIdsArrayWithRun;
unset($neededIdsArray['targetRun']);
unset($neededIdsArray['targetGeraet']);
$targetRunId = $neededIdsArrayWithRun['targetRun'];
$target_geraet_id = $neededIdsArrayWithRun['targetGeraet'];
foreach ($neededIdsArray as $key => $needed) {
$nId = (int)$needed['noteId'];
@@ -167,14 +188,14 @@ if ($recalculateJSONs) {
$gId = is_numeric($needed['geraetId']) ? (int)$needed['geraetId'] : $needed['geraetId'];
// Create a unique string key so we don't store exact duplicates
$nodeKey = $calcId . '|' . $gId;
$nodeKey = $calcId . '|' . $gId . '|' . $targetRunId . '|' . $target_geraet_id; // e.g., "10|S|R2" or "12|3|RA"
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
// Store it as the "little array" you requested: [DependentCalcId, GeraetId]
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId];
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId, [$targetRunId, $target_geraet_id]];
}
}
@@ -0,0 +1,41 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($_POST['css_content'])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Missing css_content parameter.']);
exit;
}
$css_content = $_POST['css_content'] ?? '';
if (strlen($css_content) > 200000) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'File size too large.']);
exit;
}
if (preg_match('/javascript:/i', $css_content) || preg_match('/<script/i', $css_content)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid characters detected.']);
exit;
}
file_put_contents($baseDir . '/intern/css/user.css', $css_content);
http_response_code(200);
echo json_encode(['success' => true, 'message' => 'CSS saved successfully.']);
exit;
@@ -0,0 +1,87 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$has_endtime = intval($_POST['has_endtime'] ?? 0);
$index = intval($_POST['index'] ?? 0);
if (!$name) {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $tableZeitplanTypes (`name`, `has_endtime`, `display_index`) VALUES (?, ?, ?)");
$stmt->bind_param("sii", $name, $has_endtime, $index);
$success = $stmt->execute();
$new_id = $mysqli->insert_id;
$stmt->close();
if ($success) {
echo json_encode(['success' => true, 'id' => $new_id]);
} else {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
}
} elseif ($action === 'update') {
$id = intval($_POST['id'] ?? 0);
$field = $_POST['field'] ?? '';
$value = $_POST['value'] ?? '';
$audiofile = intval($_POST['audiofile'] ?? 0);
$allowedFields = ['name', 'has_endtime', 'display_index'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'display_index') {
$value = intval($value);
}
$updated = db_update($mysqli, $tableZeitplanTypes, [$field => $value], ['id' => $id]);
if ($updated !== false) {
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'DB Update failed.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
}
} elseif ($action === 'delete') {
$id = intval($_POST['id'] ?? 0);
if ($id > 0) {
db_delete($mysqli, $tableZeitplanTypes, ['id' => $id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
}
@@ -0,0 +1,111 @@
<?php
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
try {
$mysqli->set_charset("utf8mb4");
// 4. Fetch all rows ordered by ID so the layout hierarchy is preserved
$sql = "SELECT * FROM `$tableNotenBezeichnungen` ORDER BY `id` ASC";
$result = $mysqli->query($sql);
$exportData = [];
while ($row = $result->fetch_assoc()) {
$presetItem = [];
foreach ($row as $columnName => $value) {
if ($columnName === 'berechnung_json') {
continue;
}
if ($value === null) {
$presetItem[$columnName] = null;
} elseif (is_numeric($value)) {
$presetItem[$columnName] = (strpos($value, '.') !== false) ? (float)$value : (int)$value;
} else {
$presetItem[$columnName] = $value;
}
}
$exportDataDisc[] = $presetItem;
}
$result->free();
$geraete = db_select($mysqli, $tableGeraete, '*', '', [], 'start_index ASC');
$programme = db_select($mysqli, $tableProgramme, '*', '', [], 'id ASC');
$zeitplanTypen = db_select($mysqli, $tableZeitplanTypes, '*', '', [], 'id ASC');
$rangNote = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rangNote']);
$wkName = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['wkName']);
$orderBestRang = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['orderBestRang']);
$personEinzahl = db_get_variable($mysqli, $tableVar, ['personEinzahl']);
if ($personEinzahl === null) $personEinzahl = 'Person';
$personMehrzahl = db_get_variable($mysqli, $tableVar, ['personMehrzahl']);
if ($personMehrzahl === null) $personMehrzahl = 'Personen';
$mysqli->close();
$exportData = [
'metadata' => [
'erstellt_am' => date("d.m.Y H:i"),
'ersteller' => $_SERVER['HTTP_HOST'],
'erstellt_mit' => 'WKVS Auto-Export',
'wkvs_json_version' => '1.0.0',
'titel' => 'WKVS Preset ' . $wkName . ' (V 1.0.0)'
],
'noten' => $exportDataDisc,
'disziplinen' => $geraete,
'zeitplan_typen' => $zeitplanTypen,
'rang_konfiguration' => [
'rang_note_id' => $rangNote,
'order_best_rang' => $orderBestRang
],
"leistungsklassen" => $programme,
"bezeichnungen" =>[
"singular" => $personEinzahl,
"plural"=> $personMehrzahl
]
];
$jsonOutput = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// 6. Force Browser File Download
$fileName = 'WKVS-preset-config-' . date('Y-m-d_H-i') . '.json';
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($jsonOutput));
echo $jsonOutput;
exit;
} catch (mysqli_sql_exception $e) {
http_response_code(500);
echo "Database export failed";
}
@@ -0,0 +1,350 @@
<?php
ini_set("display_errors",1);
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($_FILES['configFile']) || $_FILES['configFile']['error'] !== UPLOAD_ERR_OK) {
echo json_encode([
'success' => false,
'message' => 'Keine Config-Datei ausgewählt'
]);
exit;
}
$wkvsJSONVersion = '1.0.0';
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$saveDir = '/../private-files/config-uploads/';
$uploadDir = $baseDir . $saveDir;
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$tmpPath = $_FILES['configFile']['tmp_name'];
$originalName = $_FILES['configFile']['name'];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$allowedExt = ['json'];
if (!in_array($extension, $allowedExt, true)) {
echo json_encode(['success' => false, 'message' => 'Falsches Format (Endung)']);
http_response_code(422);
exit;
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($tmpPath);
$allowedMime = ['application/json'];
if (!in_array($mimeType, $allowedMime, true)) {
echo json_encode(['success' => false, 'message' => 'Dateiinhalt ist kein gültiges JSON']);
http_response_code(422);
exit;
}
$filename = date("Y_m_d_H_i_s") . '-wkvs-config.json';
$destination = $uploadDir . $filename;
if (!move_uploaded_file($tmpPath, $destination)) {
http_response_code(500);
exit;
}
$JSONcontent = file_get_contents($destination);
$content = json_decode($JSONcontent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
die("Invalid JSON layout.");
}
if (!isset($content['metadata']['wkvs_json_version']) || $content['metadata']['wkvs_json_version'] !== $wkvsJSONVersion) {
echo json_encode(['success' => false, 'message' => 'Inkompatible Datei-Version. (V ' . $wkvsJSONVersion . ' benötigt'. (isset($content['metadata']['wkvs_json_version']) ? ", benutzte Datei-Version: V " . $content['metadata']['wkvs_json_version'] : '') . ')' ]);
unlink($destination);
http_response_code(422);
exit;
}
if (!isset($content['noten']) || !is_array($content['noten']) || !isset($content['disziplinen']) || !is_array($content['disziplinen'])) {
echo json_encode(['success' => false, 'message' => 'Ungültige JSON-Struktur']);
unlink($destination);
http_response_code(422);
exit;
}
$content['added_timestamp'] = date("Y-m-d H:i:s");
file_put_contents($destination, json_encode($content));
$allowedColumns = [
'noten' => [
'id', 'name', 'default_value', 'type', 'berechnung', 'berechnung_json',
'max_value', 'min_value', 'pro_geraet', 'anzahl_laeufe_json', 'geraete_json',
'nullstellen', 'display_string'
],
'disziplinen' => ['id', 'name', 'start_index', 'color_kampfrichter', 'audiofile'],
'zeitplan_typen' => ['id', 'name', 'has_endtime', 'display_index'],
'leistungsklassen' => ['id', 'programm', 'order_index', 'preis', 'aktiv']
];
$tableTable = [
'noten' => $tableNotenBezeichnungen,
'disziplinen' => $tableGeraete,
'zeitplan_typen' => $tableZeitplanTypes,
'leistungsklassen' => $tableProgramme
];
$successCount = 0;
try {
$mysqli->begin_transaction();
foreach ($allowedColumns as $type => $columns) {
$tableName = "`" . $tableTable[$type] . "`";
$mysqli->query("DELETE FROM " . $tableName);
$columnsSql = implode(', ', array_map(fn($col) => "`$col`", $columns));
$placeholdersSql = implode(', ', array_fill(0, count($columns), '?'));
$sql = "INSERT INTO " . $tableName . " ($columnsSql)
VALUES ($placeholdersSql)";
$typesString = str_repeat('s', count($columns));
$stmt = $mysqli->prepare($sql);
foreach ($content[$type] as $index => $row) {
$bindValues = [];
foreach ($columns as $column) {
if (!array_key_exists($column, $row) || $row[$column] === null) {
$bindValues[] = null;
} else {
$bindValues[] = $row[$column];
}
}
$stmt->bind_param($typesString, ...$bindValues);
$stmt->execute();
$successCount++;
}
$stmt->close();
}
$rangNote = intval($content['rang_konfiguration']['rang_note_id'] ?? 0);
$orderBestRang = $content['rang_konfiguration']['order_best_rang'] ?? '';
$allowableOrderValues = ['ASC', 'DESC'];
if ($rangNote > 0) {
$mysqli->query("INSERT INTO $tableVar (`name`, `value`) VALUES ('rangNote', '" . $rangNote . "') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
}
if (in_array($orderBestRang, $allowableOrderValues, true)) {
$mysqli->query("INSERT INTO $tableVar (`name`, `value`) VALUES ('orderBestRang', '" . $orderBestRang . "') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
}
if (isset($content['bezeichnungen']['singular']) && $content['bezeichnungen']['singular'] !== null) {
$stmt = $mysqli->prepare("INSERT INTO $tableVar (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
$varName = 'personEinzahl';
$stmt->bind_param("ss", $varName, $content['bezeichnungen']['singular']);
$stmt->execute();
$stmt->close();
}
if (isset($content['bezeichnungen']['plural']) && $content['bezeichnungen']['plural'] !== null) {
$stmt = $mysqli->prepare("INSERT INTO $tableVar (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
$varName = 'personMehrzahl';
$stmt->bind_param("ss", $varName, $content['bezeichnungen']['plural']);
$stmt->execute();
$stmt->close();
}
$noten = db_select($mysqli, $tableNotenBezeichnungen, "id, berechnung, type");
$notenById = array_column($noten, null, 'id');
$berechnungen = [];
foreach ($notenById as $id => $sn) {
if ($sn['type'] === 'berechnung') {
$berechnungen[] = $sn;
}
}
if (empty($berechnungen)) {
$mysqli->commit();
echo json_encode(['success' => true, 'message' => "Successfully imported $successCount rows."]);
exit;
}
$notenRechner = new NotenRechner();
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArrayWithRun = $notenRechner->getBenoetigteIdsComplexWithRun($calc['berechnung']);
if (empty($neededIdsArrayWithRun) || count($neededIdsArrayWithRun) < 2) {
continue;
}
$calcId = (int)$calc['id'];
$neededIdsArray = $neededIdsArrayWithRun;
unset($neededIdsArray['targetRun']);
unset($neededIdsArray['targetGeraet']);
$targetRunId = $neededIdsArrayWithRun['targetRun'];
foreach ($neededIdsArray as $key => $needed) {
$nId = (int)$needed['noteId'];
$gId = is_numeric($needed['geraetId']) ? (int)$needed['geraetId'] : $needed['geraetId'];
$nodeKey = $calcId . '|' . $gId . '|' . $targetRunId;
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId, [$targetRunId]];
}
}
function getCompleteDependencyChain($id, $directMap, $visited = [])
{
if (!isset($directMap[$id])) {
return [];
}
$allDependencies = [];
foreach ($directMap[$id] as $nodeKey => $complexNode) {
if (isset($visited[$nodeKey])) {
continue;
}
$visited[$nodeKey] = true;
$allDependencies[$nodeKey] = $complexNode;
$childDependencies = getCompleteDependencyChain($complexNode[0], $directMap, $visited);
foreach ($childDependencies as $childKey => $childNode) {
$allDependencies[$childKey] = $childNode;
$visited[$childKey] = true;
}
}
return $allDependencies;
}
$flatDependencyMap = [];
foreach (array_keys($notenById) as $id) {
$chain = getCompleteDependencyChain($id, $dependencyMap);
if (!empty($chain)) {
$flatDependencyMap[$id] = array_values($chain);
}
}
$resetSql = "UPDATE $tableNotenBezeichnungen SET `berechnung_json` = NULL";
$mysqli->query($resetSql);
// Step 2: Prepare the statement
$updateSql = "UPDATE $tableNotenBezeichnungen SET `berechnung_json` = ? WHERE id = ?";
$stmt = $mysqli->prepare($updateSql);
foreach ($flatDependencyMap as $id => $completeDependencyArray) {
if (empty($completeDependencyArray)) {
continue;
}
$jsonString = json_encode($completeDependencyArray);
$stmt->bind_param("si", $jsonString, $id);
$stmt->execute();
}
$stmt->close();
$mysqli->commit();
echo json_encode(['success' => true, 'message' => "Successfully imported $successCount rows."]);
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-noten-cache.php';
generateIntersectCache($mysqli, $baseDir);
regenerate_noten_cache($mysqli, $tableNotenBezeichnungen, $baseDir);
} catch (mysqli_sql_exception $e) {
$mysqli->rollback();
unlink($destination);
if ($e->getCode() === 1048) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => "Import failed: A required data field is missing in your preset file."]);
exit;
} else {
http_response_code(400);
echo json_encode(['success' => false, 'message' => "Database error during import." . " Error Code: " . $e->getMessage()]);
exit;
}
} finally {
$mysqli->close();
$allFiles = glob($uploadDir . '*.json');
$timestamps = array_map(function($file) {
$jsonContent = file_get_contents($file) ?? '';
$data = json_decode($jsonContent, true) ?? [];
return $data['added_timestamp'] ?? 0;
}, $allFiles);
array_multisort($timestamps, SORT_DESC, $allFiles);
$filesToDelete = array_slice($allFiles, 3);
foreach ($filesToDelete as $file) {
unlink($file);
}
}
@@ -0,0 +1,85 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
ini_set('display_errors', 1);
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
check_user_permission('kampfrichter');
require_once $baseDir . '/../scripts/delete_request_type_functions.php';
parse_input_to_delete();
verify_delete_csrf($_DELETE);
$programm_id = (int) ($_DELETE['programmId'] ?? 0);
if ($programm_id <= 0) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Keine valide Programm-ID']);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
$programm_name = db_get_var($mysqli, "SELECT `programm` FROM $tableProgramme WHERE id = ? LIMIT 1", [$programm_id]) ?: null;
if ($programm_name === null) {
echo json_encode(['success' => true, 'message' => 'Dieses Programm existiert nicht.']);
http_response_code(400);
exit;
}
$personen = db_select($mysqli, $tableTeilnehmende, "id", '`programm` = ?', [$programm_name]);
if (count($personen) < 1) {
echo json_encode(['success' => true, 'message' => 'Es existieren keine Personen mit diesem Programm.']);
http_response_code(400);
exit;
}
$personen_id_array = array_column($personen, 'id');
$parram_array = array_fill(0, count($personen_id_array), "?");
$parram_string = implode(', ', $parram_array);
$year = date('n') > 6 ? intval(date('Y')) + 1 : intval(date('Y'));
$value_array = array_merge($personen_id_array, [$year]);
$stmt = $mysqli->prepare("DELETE FROM $tableNoten WHERE `person_id` IN ($parram_string) AND `jahr` = ?");
$types_string = str_repeat("i", count($personen_id_array)) . "i";
$stmt->bind_param($types_string, ...$value_array);
$stmt->execute();
$stmt->close();
$stmt = $mysqli->prepare("DELETE FROM $tableNotenChanges WHERE `person_id` IN ($parram_string) AND `jahr` = ?");
$stmt->bind_param($types_string, ...$value_array);
$stmt->execute();
$stmt->close();
echo json_encode(['success' => true, 'message' => 'Noten gelöscht']);
http_response_code(200);
@@ -0,0 +1,85 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$abt_id = intval($_POST['abteilungId'] ?? 1);
// Determine current year
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT
t.id AS person_id
FROM $tableTeilnehmendeAbt tab
INNER JOIN $tableTeilnehmende t ON tab.turnerin_id = t.id
INNER JOIN $tableAbt ab ON ab.id = tab.abteilung_id
WHERE ab.id = ?
ORDER BY t.id ASC
");
$stmt->bind_param('i', $abt_id);
$stmt->execute();
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
$indexedPersonen = array_column($personen, null, 'person_id');
// Close statement
$stmt->close();
// 1. Get the IDs from the first query results
$personenIds = array_column($personen, 'person_id');
if (empty($personenIds)) {
echo json_encode(["success" => false, "message" => "Keine Notenänderungen vorhanden"]);
http_response_code(400);
exit;
}
$placeholders = implode(',', array_fill(0, count($personenIds), '?'));
$sqlNotenChanges = "DELETE FROM $tableNotenChanges WHERE person_id IN ($placeholders) AND `jahr` = ?";
$stmtNotenChanges = $mysqli->prepare($sqlNotenChanges);
$paramsArray = array_merge($personenIds, [$current_year]);
$types = str_repeat('i', count($personenIds)) . 's';
$stmtNotenChanges->bind_param($types, ...$paramsArray);
$stmtNotenChanges->execute();
$stmtNotenChanges->close();
echo json_encode(["success" => true, "message" => "Notenänderung für Abteilung " . $abt_id . " gelöscht"]);
http_response_code(200);
@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['wkName']);
$mysqli->close();
if ($wkName === null) {
echo json_encode(["success" => false, "message" => "Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen"]);
http_response_code(400);
exit;
}
$programm = trim($_POST['prog'] ?? '');
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
$dir = $baseDir . '/files/ranglisten/';
$localPath = $dir . str_replace("/", "", str_replace(" ", "_", $wkName)) . "_Ergebnisse_" . str_replace("/", "", $programm) . "_" . $current_year . ".pdf";
if (!file_exists($localPath)) {
echo json_encode(["success" => false, "message" => "Diese Rangliste existiert nicht."]);
http_response_code(400);
exit;
}
unlink($localPath);
echo json_encode(["success" => true, "message" => "Rangliste gelöscht"]);
http_response_code(200);
@@ -14,13 +14,6 @@ check_user_permission('kampfrichter');
verify_csrf();
if (!verify_csrf()) {
echo json_encode(['success' => false, 'message' => 'Forbidden']);
exit;
}
// Validate editId from POST
if (isset($_POST['editId'])) {
$editId = intval($_POST['editId']);
@@ -37,8 +30,6 @@ if ($editId === false) {
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
@@ -51,21 +42,41 @@ if (!($data['success'] ?? false)) {
exit;
}
$isAdmin = (($_SESSION['selectedFreigabeKampfrichter'] ?? '') === 'admin') ? true : false;
$is_payed = db_get_var($mysqli, "SELECT 1 FROM $tableTeilnehmende WHERE id = ? AND ((bezahltoverride = ?) OR (bezahlt = ? AND bezahltoverride != ? AND bezahltoverride != ?))", [$editId, 5, 2, 3, 4]);
if ($is_payed != '1') {
echo json_encode(['success'=> false, 'message'=> 'Startgebühr nicht bezahlt']);
http_response_code(400);
exit;
}
$isAdmin = (($_SESSION['selectedFreigabeIdKampfrichter'] ?? '') === 'A') ? true : false;
$disciplines = db_select($mysqli, $tableGeraete, 'id', '', [], 'start_index ASC');
$disciplines = array_column($disciplines, "id");
if (!$isAdmin) {
$requested_discipline = trim($_POST['geraet'] ?? '');
$discipline = intval($_POST['geraet']) ?? 0;
$all_disciplines = false;
if (!in_array($discipline, $disciplines)) {
if ($isAdmin && $requested_discipline === 'A') {
$all_disciplines = true;
} else {
$requested_discipline = (int) $requested_discipline;
if (!in_array($requested_discipline, $disciplines)) {
echo json_encode(['success' => false, 'message' => 'Falsche Geräte ID']);
exit;
}
$discipline = $requested_discipline;
}
if ($all_disciplines) {
$stmt = $mysqli->prepare("SELECT t.`name`, t.`vorname`, t.`programm`, p.id as programm_id FROM $tableTeilnehmende t LEFT JOIN $tableProgramme p ON p.programm = t.programm WHERE t.id = ?");
} else {
$disciplines = [$discipline];
@@ -78,7 +89,7 @@ if (!$isAdmin) {
agg.abteilung,
agg.geraeteIndex,
agg.startIndex
FROM $tableTurnerinnen t
FROM $tableTeilnehmende t
LEFT JOIN $tableProgramme p ON p.programm = t.programm
LEFT JOIN (
SELECT
@@ -86,7 +97,7 @@ if (!$isAdmin) {
GROUP_CONCAT(DISTINCT a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(DISTINCT g.start_index SEPARATOR ', ') AS geraeteIndex,
ta.turnerin_index AS startIndex
FROM $tableTurnerinnenAbt ta
FROM $tableTeilnehmendeAbt ta
INNER JOIN $tableAbt a
ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete g
@@ -96,10 +107,7 @@ if (!$isAdmin) {
WHERE t.id = ?
");
} else {
$stmt = $mysqli->prepare("SELECT t.`name`, t.`vorname`, t.`programm`, p.id as programm_id FROM $tableTurnerinnen t LEFT JOIN $tableProgramme p ON p.programm = t.programm WHERE t.id = ?");
}
$stmt->bind_param('i', $editId);
$stmt->execute();
@@ -118,7 +126,7 @@ $now = new DateTime();
$jahr = ($now->format('n') > 6) ? $now->modify('+1 year')->format('Y') : $now->format('Y');
if ($isAdmin) {
if ($all_disciplines) {
$stmt = $mysqli->prepare("SELECT `note_bezeichnung_id`, `value`, `geraet_id`, `run_number` FROM $tableNoten WHERE `person_id` = ? AND `jahr` = ?");
$stmt->bind_param('ss', $editId, $jahr);
@@ -177,7 +185,7 @@ foreach ($disciplines as $d) {
// Determine number of runs for this program
$anzRunsConfig = !empty($snC['anzahl_laeufe_json']) ? json_decode($snC['anzahl_laeufe_json'], true) : [];
$runs = $anzRunsConfig[$d][$programm_id] ?? $anzRunsConfig['default'] ?? 1;
$runs = $anzRunsConfig[$d][$programm_id] ?? $anzRunsConfig[$d]["all"] ?? $anzRunsConfig['default'] ?? 1;
if (isset($displayNoten) && array_key_exists($snC['id'], $displayNoten)) {
$displayNoten[$snC['id']] = $runs;
@@ -199,10 +207,9 @@ if (isset($displayNoten)) {
$titel = $row['vorname'].' '.$row['name'].', '.$row['programm'];
if (!$isAdmin) {
// $entries = db_select($mysqli, $tableTurnerinnen, 'name, vorname, programm, id', 'abteilung = ? AND startgeraet = ?', [$row['abteilung'], $row['startgeraet']]);
if (!$all_disciplines) {
$stmt = $mysqli->prepare("
SELECT
t.name,
@@ -212,14 +219,14 @@ if (!$isAdmin) {
agg.abteilung,
agg.geraeteIndex,
agg.startIndex
FROM $tableTurnerinnen t
FROM $tableTeilnehmende t
LEFT JOIN (
SELECT
ta.turnerin_id,
GROUP_CONCAT(DISTINCT a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(DISTINCT g.start_index SEPARATOR ', ') AS geraeteIndex,
ta.turnerin_index AS startIndex
FROM $tableTurnerinnenAbt ta
FROM $tableTeilnehmendeAbt ta
INNER JOIN $tableAbt a
ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete g
@@ -1,13 +1,7 @@
<?php
/*ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
// Show all errors except deprecation notices (these come from vendor libraries
// that aren't yet typed for newer PHP versions). Long-term fix: update
// dependencies to versions compatible with your PHP runtime.
error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED);*/
use TCPDF;
// Start session if not already started
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -20,13 +14,6 @@ check_user_permission('kampfrichter');
verify_csrf();
// Validate POST input
if (!isset($_POST['abteilung'])) {
http_response_code(400);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -34,296 +21,472 @@ if (!isset($baseDir)) {
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
if ($data['success'] === false) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$abteilung = trim($_POST['abteilung']);
require $baseDir . '/../scripts/db/db-functions.php';
$disciplines = db_select($mysqli, $tableGeraete, '*', '', [], 'start_index ASC');
$wkName = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['wkName']);
$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;
$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';
// Load TCPDF
require $baseDir . '/wp-content/uploads/TCPDF-main/tcpdf.php';
$abt = intval($_POST['abteilungId'] ?? 1);
$current_year = (date('n') > 6) ? date('Y') + 1 : date('Y');
$now = trim($_POST['date'] ?? date('Y-m-d H:i:s'));
class MYPDF extends TCPDF
{
public $current_year;
public $abteilung;
// Page header
public function Header()
{
$image_file = 'https://kutu-tage-beider-basel.testseite-fh-ht.ch/wp-content/uploads/2025/06/ktbb-logo.png';
$this->SetY(15);
$this->SetFont('helvetica', 'B', 20);
$this->Cell(0, 0, 'Protokoll Kutu-Tage beider Basel ' . $this->current_year . ' Abt. ' . $this->abteilung, 0, false, 'L', 0, '', 0, false, 'M', 'M');
$this->Image($image_file, 272, 5, 15, '', 'PNG', '', 'T', false, 300, '', false, false, 0, false, false, false);
}
$geraete = db_select($mysqli, $tableGeraete, "*", '', [], "start_index ASC");
// Page footer
public function Footer()
{
$this->SetY(-15);
$this->SetFont('helvetica', 'I', 8);
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M');
}
}
$IndexedGeraete = array_column($geraete, 'name', 'id');
$pdf = new MYPDF('L', 'mm', 'A4', true, 'UTF-8', false);
//$pdf->AddFont('outfit-bold', '', 'outfitb.php');
$notenBezeichnungen = db_select($mysqli, $tableNotenBezeichnungen, "`name`, `id`, `nullstellen`, `anzahl_laeufe_json`", '', [], "");
$IndexedNotenBezeichnungen = array_column($notenBezeichnungen, 'name', 'id');
$pdf->current_year = $current_year;
$pdf->abteilung = $abteilung;
$IndexedNotenNullstellen = array_column($notenBezeichnungen, 'nullstellen', 'id');
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Turnerinnen System');
$pdf->SetTitle("KTBB_Protokoll_Abt." . $abteilung . "_" . $current_year . ".pdf");
$pdf->SetMargins(10, 20, 10);
$pdf->SetAutoPageBreak(TRUE, 10);
$pdf->SetFont('helvetica', '', 9);
$IndexedNotenJsonGeraete = array_column($notenBezeichnungen, 'anzahl_laeufe_json', 'id');
$startindex = 0;
$programme = db_select($mysqli, $tableProgramme, "`programm`, `id`", '', [], "");
foreach ($disciplines as $singledisciplin){
$startindex ++;
$indexedProgramme = array_column($programme, 'id', 'programm');
$name_kampfrichterin1 = 'Nicht eingetragen';
$name_kampfrichterin2 = 'Nicht eingetragen';
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT * FROM $tableKrProtokoll
WHERE abteilung = ?
AND geraet = ?
");
$stmt->bind_param('ss', $abteilung, $singledisciplin['name']);
$stmt->execute();
$result = $stmt->get_result();
$krresults = $result->fetch_all(MYSQLI_ASSOC);
foreach ($krresults as $krresult){
if ($krresult['aufgabe'] == 1){
$name_kampfrichterin1 = $krresult['name'];
} elseif ($krresult['aufgabe'] == 2){
$name_kampfrichterin2 = $krresult['name'];
}
}
// Determine current year
$current_year = date('Y');
$monat = date('n');
if ($monat > 6) $current_year++;
foreach ($disciplines as $shiftstartindex => $subdisciplin){
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT
t.id AS person_id,
t.name,
t.vorname,
t.programm,
t.verein,
ab.id AS abteilung_id,
tab.geraet_id AS start_geraet_id,
tab.turnerin_index
FROM $tableTeilnehmendeAbt tab
INNER JOIN $tableTeilnehmende t ON tab.turnerin_id = t.id
INNER JOIN $tableAbt ab ON ab.id = tab.abteilung_id
WHERE ab.id = ?
ORDER BY t.id ASC
");
$pdf->AddPage();
$stmt->bind_param('i', $abt);
$stmt->execute();
$pdf->Ln(10);
$pdf->SetFont('', 'B', 14);
$pdf->Cell(0, 0, ucfirst($singledisciplin['name']), 0, 1, 'L');
$pdf->Ln(3);
$pdf->SetFont('', 'I', 9);
$newshiftindex = $shiftstartindex + 1;
$pdf->Cell(0, 0, 'Gruppe '. $newshiftindex , 0, 1, 'L');
$pdf->SetFont('', '');
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
$pdf->Ln(5);
$pdf->Cell(0, 0, '1. Kampfrichterin: ' . $name_kampfrichterin1, 0, 1, 'L');
$pdf->Ln(2);
$pdf->Cell(0, 0, '2. Kampfrichterin: ' . $name_kampfrichterin2, 0, 1, 'L');
$pdf->Ln(8);
$startg = $shiftstartindex - $startindex;
if ($startg < 1) {
$startg += 4; // shift into positive range
}
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT
t.*,
agg.abteilung,
agg.geraete_index,
agg.start_index
FROM $tableTurnerinnen t
LEFT JOIN (
SELECT
ta.turnerin_id,
GROUP_CONCAT(DISTINCT a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(DISTINCT g.start_index SEPARATOR ', ') AS geraete_index,
ta.turnerin_index AS start_index
FROM $tableTurnerinnenAbt ta
INNER JOIN $tableAbt a
ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete g
ON g.id = ta.geraet_id
GROUP BY ta.turnerin_id
) agg ON agg.turnerin_id = t.id
WHERE (t.bezahlt = ? OR t.bezahltoverride = ?) AND agg.abteilung = ? AND agg.geraete_index = ?
ORDER BY agg.start_index DESC
");
$bezahlt1 = '2';
$bezahlt2 = '5';
$stmt->bind_param('ssss', $bezahlt1, $bezahlt2, $abteilung, $startg);
$stmt->execute();
$result = $stmt->get_result();
$entries = $result->fetch_all(MYSQLI_ASSOC);
if (!empty($entries)) {
$maxindex = $entries[0]['start_index'];
// Recalculate
foreach ($entries as &$singleorderentry) {
$singleorderentry['start_index'] -= $shiftstartindex;
if ($singleorderentry['start_index'] < 1) {
$singleorderentry['start_index'] += $maxindex;
}
}
unset($singleorderentry); // break the reference
// Reorder by recalculated startindex (ascending)
usort($entries, function($a, $b) {
return $a['start_index'] <=> $b['start_index'];
});
}
// Define columns (including discipline sub-columns)
$columns = [
'start_index' => ['header' => 'Str.'],
'name' => ['header' => 'Name'],
'vorname' => ['header' => 'Vorname'],
'programm' => ['header' => 'Programm'],
'geburtsdatum' => ['header' => 'Jg.'],
'verein' => ['header' => 'Verein'],
'e1 note '.strtolower($subdisciplin['name']) => ['header' => 'E1 Note '.ucfirst($subdisciplin['name'])],
'e2 note '.strtolower($subdisciplin['name']) => ['header' => 'E2 Note '.ucfirst($subdisciplin['name'])],
'e note '.strtolower($subdisciplin['name']) => ['header' => 'E Note '.ucfirst($subdisciplin['name'])],
'd-note '.strtolower($subdisciplin['name']) => ['header' => 'D Note '.ucfirst($subdisciplin['name'])],
'neutrale abzuege '.strtolower($subdisciplin['name']) => ['header' => 'N. Abzüge '.ucfirst($subdisciplin['name'])],
'note '.strtolower($subdisciplin['name']) => ['header' => 'Note '.ucfirst($subdisciplin['name'])],
];
// Initialize max widths with header widths (+4 mm padding)
foreach ($columns as $key => &$col) {
$col['max_width'] = $pdf->GetStringWidth($col['header']) + 4;
}
// Calculate max width of each column based on all data
foreach ($entries as $r) {
foreach ($columns as $key => &$col) {
isset($r[$key]) ? $r[$key] : '';
$text = '';
// Handle different column types
if ($key === 'start_index' && !empty($r['start_index'])) {
$text = (int)$r[$key];
} elseif ($key === 'geburtsdatum' && !empty($r['geburtsdatum'])) {
$text = (new DateTime($r['geburtsdatum']))->format('Y');
} elseif (strpos($key, 'd-note') === 0 && isset($r[$key]) && is_numeric($r[$key])) {
$text = number_format((float)$r[$key], 2, '.', ''); // 2 decimals
} elseif (isset($r[$key]) && is_numeric($r[$key])) {
$text = number_format((float)$r[$key], 3, '.', ''); // 3 decimals
} elseif (isset($r[$key])) {
$text = (string)$r[$key];
} else {
$text = '';
}
// Calculate width with padding
$w = $pdf->GetStringWidth($text) + 4;
// Update max width
if ($w > $col['max_width']) {
$col['max_width'] = $w;
}
}
}
unset($col);
$columns['geburtsdatum']['max_width'] = $pdf->GetStringWidth("0000") + 4;
// Print first header row
$pdf->SetFont('', 'B');
foreach ($columns as $key => $col) {
if (
$key === 'd-note' || // only column with dash
strpos($key, 'note') === 0 || // matches 'note...' at start
strpos($key, 'e note') === 0 || // matches 'e-note...'
strpos($key, 'e1 note') === 0 || // matches 'e1 note ...'
strpos($key, 'e2 note') === 0 || // matches 'e2 note ...'
strpos($key, 'neutrale abzuege') === 0 ||
$key === 'geburtsdatum' ||
$key === 'start_index'
) {
$align = 'C';
} else {
$align = 'L';
}
$pdf->Cell($col['max_width'], 7, $col['header'], 0, 0, $align);
}
// Move to next line after headers
$pdf->Ln();
$pdf->SetFont('', '');
foreach ($entries as $r) {
foreach ($columns as $key => $col) {
// Determine alignment
if (strpos($key, 'note') === 0 || strpos($key, 'e note') === 0 || strpos($key, 'd-note') === 0 || strpos($key, 'neutrale abzuege') === 0 || $key === 'start_index' || $key === 'geburtsdatum' || strpos($key, 'e1 note') === 0 || strpos($key, 'e2 note') === 0){
$align = 'C';
} else {
$align = 'L';
}
// Format the value based on type
if ($key === 'geburtsdatum' && !empty($r['geburtsdatum'])) {
$text = (new DateTime($r['geburtsdatum']))->format('Y');
} elseif (strpos($key, 'note') === 0 || strpos($key, 'e note') === 0 || strpos($key, 'd-note') === 0 || strpos($key, 'neutrale abzuege') === 0 || $key === 'gesamt' || strpos($key, 'e1 note') === 0 || strpos($key, 'e2 note') === 0){
$text = isset($r[$key]) ? number_format((float)$r[$key], 3) : '';
} else {
$text = isset($r[$key]) ? $r[$key] : '';
}
$pdf->Cell($col['max_width'], 6, $text, 1, 0, $align);
}
$pdf->Ln(); // Move to next row
}
$pdf->SetY(-50);
if ($name_kampfrichterin1 !== 'Nicht eingetragen' || $name_kampfrichterin2 !== 'Nicht eingetragen'){
$pdf->SetFont('', 'B');
$pdf->Cell(0, 28, 'Unterschrift:', 0, 1, 'L');
$pdf->SetFont('', '');
if ($name_kampfrichterin1 !== 'Nicht eingetragen'){
$pdf->Cell(120, 8, $name_kampfrichterin1.': ___________________________________________', 0, 0, 'L');
}
if ($name_kampfrichterin2 !== 'Nicht eingetragen'){
$pdf->Cell(90, 8, $name_kampfrichterin2.': ___________________________________________', 0, 0, 'L');
}
}
}
}
$indexedPersonen = array_column($personen, null, 'person_id');
// Close statement
$stmt->close();
$pdf->Output("Protokoll_Kutu-Tage_beider_Basel_{$current_year}_Abt_{$abteilung}.pdf", 'D');
// 1. Get the IDs from the first query results
$personenIds = array_column($personen, 'person_id');
if (!empty($personenIds)) {
$placeholders = implode(',', array_fill(0, count($personenIds), '?'));
$sqlNotenChanges = "SELECT * FROM $tableNotenChanges WHERE person_id IN ($placeholders) AND `jahr` = ?";
$stmtNotenChanges = $mysqli->prepare($sqlNotenChanges);
$paramsArray = array_merge($personenIds, [$current_year]);
$types = str_repeat('i', count($personenIds)) . 's';
$stmtNotenChanges->bind_param($types, ...$paramsArray);
$stmtNotenChanges->execute();
$notenChangesResult = $stmtNotenChanges->get_result();
$notenChangesEntries = $notenChangesResult->fetch_all(MYSQLI_ASSOC);
$stmtNotenChanges->close();
} else {
$notenChangesEntries = [];
}
$kampfrichterIds = array_column($notenChangesEntries, 'edited_by');
if (!empty($kampfrichterIds)) {
$placeholders = implode(',', array_fill(0, count($kampfrichterIds), '?'));
$sqlKampfrichter = "SELECT `id` AS `id_kampfrichter`, `name_person` AS `name_kampfrichter` FROM $tableInternUsers WHERE id IN ($placeholders)";
$stmtKampfrichter = $mysqli->prepare($sqlKampfrichter);
$types = str_repeat('i', count($kampfrichterIds));
$stmtKampfrichter->bind_param($types, ...$kampfrichterIds);
$stmtKampfrichter->execute();
$kampfrichterResult = $stmtKampfrichter->get_result();
$kampfrichter = $kampfrichterResult->fetch_all(MYSQLI_ASSOC);
$indexedKampfrichter = array_column($kampfrichter, 'name_kampfrichter', 'id_kampfrichter');
$stmtKampfrichter->close();
} else {
$indexedKampfrichter = [];
}
$entriesArray = [];
foreach ($notenChangesEntries as $snce) {
$programmId = $indexedProgramme[$indexedPersonen[$snce['person_id']]['programm']] ?? 0;
$runs = json_decode($IndexedNotenJsonGeraete[$snce['note_bezeichnung_id']], true) ?? [];
$countRuns = (int) $runs[$snce['geraet_id']][$programmId] ?? (int) $runs['default'] ?? 1;
$notenTextRaw = $IndexedNotenBezeichnungen[$snce['note_bezeichnung_id']] ?? 'Unbekannter Notenname';
$notenText = ($countRuns > 1) ? $notenTextRaw . ' (R' . ($snce['run_number'] ?? 1 ). ')' : $notenTextRaw;
$nullstellen = (int) $IndexedNotenNullstellen[$snce['note_bezeichnung_id']] ?? 2;
$value = ($snce['old_value'] !== null) ? number_format((float) $snce['old_value'], $nullstellen) . ' ' . json_decode('"\u2192"') . ' ' . number_format((float) $snce['new_value'], $nullstellen) : number_format((float) $snce['new_value'], $nullstellen);
$entriesArray[$snce['geraet_id']][$indexedPersonen[$snce['person_id']]['start_geraet_id']][] = [
'name' => ($indexedPersonen[$snce['person_id']]['name'] ?? 'Unbekannter Nachname') . ', ' . ($indexedPersonen[$snce['person_id']]['vorname'] ?? 'Unbekannter Vorname'),
'programm' => $indexedPersonen[$snce['person_id']]['programm'] ?? 'Unbekanntes Programm',
'verein' => $indexedPersonen[$snce['person_id']]['verein'] ?? 'Unbekannter Verein',
'note' => $notenText,
'value' => $value,
'kampfrichter' => $indexedKampfrichter[$snce['edited_by']] ?? 'Unbekannter Kampfrichter',
'timestamp' => new DateTime($snce['timestamp'])->format('d.m.Y H:i:s') ?? '---'
];
}
// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';
/*
// Optional: load custom font
$fontfile = $baseDir . '/wp-content/uploads/fonts/Inter-Regular.ttf'; // adjust path
if (file_exists($fontfile)) {
$fontname = TCPDF_FONTS::addTTFfont($fontfile, 'TrueTypeUnicode', '', 32);
}
*/
class ProtokollPDF extends TCPDF
{
public $current_year;
public $subAbt;
public $abt;
public $columns;
public $discipline;
public $wkName;
public $marginSide;
public $marginTop;
public $pdfHeight;
public $pdfWidth;
public $headerType;
public $now;
// Page header
public $headerBottomY = 0;
public function Header()
{
$this->SetFillColor(255, 255, 255);
$this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');
$arrayTitles = [];
$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
$this->SetY($this->marginTop);
$this->SetX(($this->marginSide ?? 10));
$this->SetFont('freesans', '', 20);
if ($this->headerType === 'bigHeader') {
$this->Cell(0, 0, 'Protokoll ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->SetY($preimageY + 5);
$this->SetX($this->marginSide ?? 10); // Force back to left margin
$this->SetFont('freesans', '', 11);
$this->Cell(0, 6, 'Gereat: ' . $this->discipline, 0, 1, 'L');
$this->Cell(0, 6, 'Abteilung: ' . $this->abt, 0, 1, 'L');
$this->Cell(0, 6, 'Gruppe: ' . $this->subAbt, 0, 1, 'L');
$this->Ln(5);
} else {
$this->Cell(10, 0, 'Protokoll ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$this->SetFont('freesans', '', 11);
$info = "Gerät: {$this->discipline} | Abt: {$this->abt} | Gruppe: {$this->subAbt}";
$this->Cell(10, 6, $info, 0, 1, 'L'); // Align all info to the right while title is on the left
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->Ln(18);
}
$columns = $this->columns;
$startY = $this->GetY();
$this->SetFont('', 'B');
$this->SetX($this->marginSide ?? 10);
foreach ($columns as $col) {
$title = $col['header'];
if (($col['rotate'] ?? false)) {
$x = $this->GetX();
$y = $this->GetY() + 7;
$this->StartTransform();
$this->Rotate(45, $x, $y);
$lineY = $startY + 7;
if (($col['id'] ?? '') === 'gesamt') {
$this->SetLineWidth(0.6);
} else {
$this->SetLineWidth(0.2);
}
$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
$this->SetLineWidth(0.2);
$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
$this->StopTransform();
$this->SetX($x + ($col['max_width'] ?? 0));
} else {
$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
}
}
$this->Ln();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetLineWidth(0.6);
$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(0);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(-15);
$this->SetFont('freesans', 'I', 8);
// 1. Page Number - Centered
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C');
// 2. Timestamp - Force to Left
$this->SetX($this->getMargins()['left']); // Reset to left margin
$this->Cell(0, 10, 'Zeitstempel: ' . $this->now, 0, false, 'L');
// 3. Host - Force to Right (Cell with width 0 and align 'R' handles this)
$this->SetX($this->getMargins()['left']); // Reset again so 'R' alignment works from the start
$this->Cell(0, 10, $_SERVER['HTTP_HOST'], 0, 0, 'R', false, 'https://'.$_SERVER['HTTP_HOST']);
}
}
$pdf = new ProtokollPDF($orientation, 'mm', $format, true, 'UTF-8', false);
$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();
$pdf->current_year = $current_year;
$pdf->wkName = $wkName;
$pdf->abt = $abt;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;
$pdf->now = $now;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Protokoll_" . $abt . "_" . $current_year . ".pdf");
$pdf->SetAutoPageBreak(FALSE);
$pdf->SetFont('freesans', '', 11);
// Define columns dynamically
$columns = [
['id' => 'name', 'header' => 'Name Person', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'name'],
['id' => 'programm', 'header' => 'Programm', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'programm'],
['id' => 'verein', 'header' => 'Verein', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'verein'],
['id' => 'note', 'header' => 'Note', 'align' => 'C', 'flex' => false, 'type' => 'field', 'field' => 'note'],
['id' => 'value', 'header' => 'Wert', 'align' => 'C', 'flex' => false, 'type' => 'field', 'field' => 'value'],
['id' => 'kr', 'header' => 'Kampfrichter/in', 'align' => 'C', 'flex' => true, 'type' => 'field', 'field' => 'kampfrichter'],
['id' => 'timestamp', 'header' => 'Zeitstempel', 'align' => 'C', 'flex' => false, 'type' => 'field', 'field' => 'timestamp'],
];
$padding = 4;
unset($col);
// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
$col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
if ($col['type'] !== 'note') {
$col['max_width'] = $col['width_header'];
} else {
$col['max_width'] = 0;
}
}
foreach ($entriesArray as $geratId => $singleGeraet) {
$pdf->discipline = $IndexedGeraete[$geratId] ?? 'Unbekanntes Gerät';
foreach ($singleGeraet as $geratIdStart => $singleStartGeraet) {
$pdf->subAbt = $geratIdStart;
$tcolumns = $columns;
foreach ($singleStartGeraet as $se) {
foreach ($tcolumns as &$col) {
$text = '';
if (isset($se[$col['field']])) {
$text = $se[$col['field']];
}
$width = $pdf->GetStringWidth($text) + ($padding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
}
unset($col);
}
// 4. Distribute Flexible Widths
$tablew = $pdfWidth - (2 * $marginSide); // total table width (A4 landscape in mm minus margins)
$fixedWidth = 0;
$flexCols = [];
foreach ($tcolumns as $col) {
if ($col['flex'] ?? false) {
$flexCols[] = $col['id'];
$fixedWidth += $col['max_width'];
} else {
$fixedWidth += $col['max_width'];
}
}
$effTableWidth = $tablew - $fixedWidth;
if (!empty($flexCols) && $effTableWidth > 0) {
$flexTotalInitial = 0;
foreach ($tcolumns as $col) {
if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
}
foreach ($tcolumns as &$col) {
if ($col['flex'] ?? false) {
$col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
}
}
unset($col);
}
$pdf->columns = $tcolumns;
$pdf->headerType = 'bigHeader';
$pdf->AddPage();
$pdf->headerType = '';
// After AddPage(), the header has been drawn
$margin_top = $pdf->headerBottomY;
// Now adjust your margins if needed
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$pdf->SetFont('', '');
$rIndex = 0;
foreach ($singleStartGeraet as $r) {
$pdf->SetTextColor(0, 0, 0);
if ($rIndex % 2 == 0) {
$pdf->SetFillColor(255, 255, 255);
} else {
$pdf->SetFillColor(240, 240, 240);
}
$rowHeight = 10;
foreach ($tcolumns as $col) {
if ($pdf->getY() + $rowHeight > $pdfHeight - $minMarginBottomTable) {
$pdf->addPage();
$margin_top = $pdf->headerBottomY;
$pdf->setY($margin_top);
}
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
}
$startX = $pdf->GetX();
$startY = $pdf->GetY();
$text = strip_tags($r[$col['field']]);
if ($col['type'] === 'field') {
$pdf->MultiCell($col['max_width'], $rowHeight, $text, 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->SetY($startY + $rowHeight); // Move Y manually instead of Ln() to account for rowHeight
$rIndex++;
}
$textanzEintaege = (count($singleStartGeraet) > 1) ? 'Einträge': 'Eintrag';
$pdf->SetFont('', '', 7);
$pdf->Cell(20, 6, count($singleStartGeraet).' '.$textanzEintaege, 0, 0, 'L');
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->Output($wkName . "_Protokoll_" . $abt . "_" . $current_year . ".pdf", 'I');
exit;
@@ -14,13 +14,6 @@ check_user_permission('kampfrichter');
verify_csrf();
// Validate POST input
/*
if (!isset($_POST['prog']) && !isset($_POST['type'])) {
http_response_code(400);
exit;
}*/
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -28,7 +21,8 @@ if (!isset($baseDir)) {
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
if ($data['success'] === false) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
@@ -36,9 +30,28 @@ if ($data['success'] === false){
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['wkName']);
$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;
$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';
$programm = trim($_POST['prog'] ?? 'P6A');
$buttontype = trim($_POST['type'] ?? 'downloadRangliste');
$allowedOperations = ['downloadRangliste', 'updateServerRangliste'];
if (!in_array($buttontype, $allowedOperations)) {
header('Content-Type: application/json');
echo json_encode(["success" => false, "message" => "Invalide Operation"]);
exit;
}
$programm = trim($_POST['prog'] ?? 'p6a');
$buttontype = trim($_POST['type'] ?? 'export_programm');
$upperprogramm = strtoupper($programm);
$stmt = $mysqli->prepare("SELECT `id`, `name` FROM $tableGeraete ORDER BY start_index ASC");
@@ -64,26 +77,40 @@ if ($monat > 6) $current_year++;
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT * FROM $tableTurnerinnen
SELECT * FROM $tableTeilnehmende
WHERE LOWER(programm) = LOWER(?)
AND (bezahlt = ? OR bezahltoverride = ?)
AND ((bezahlt = ? OR bezahltoverride = ?) AND bezahltoverride != ?)
ORDER BY rang ASC
");
$bezahlt1 = '2';
$bezahlt2 = '5';
$stmt->bind_param('sss', $programm, $bezahlt1, $bezahlt2);
$bezahlt3 = '3';
$stmt->bind_param('ssss', $programm, $bezahlt1, $bezahlt2, $bezahlt3);
$stmt->execute();
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
// Close statement
$stmt->close();
$programmId = db_get_var($mysqli, "SELECT `id` FROM $tableProgramme WHERE LOWER(`programm`) = LOWER(?)", [$programm]);
$cache = require $baseDir . '/../scripts/cache/noten-config-calculating-cache.php';
$ascArrayAnzahlLaeufeJSON = $cache['anzahl_laeufe_json'];
// 1. Get the IDs from the first query results
$turnerinnenIds = array_column($personen, 'id');
$rangNote = intval(db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rangNote']));
if ($rangNote === 0) {
header('Content-Type: application/json');
echo json_encode(["success" => false, "message" => "Keine Note für Rangvergabe ausgewählt"]);
http_response_code(400);
exit;
}
if (!empty($turnerinnenIds)) {
// 2. Create a string of placeholders: ?,?,?
$placeholders = implode(',', array_fill(0, count($turnerinnenIds), '?'));
@@ -115,26 +142,34 @@ if (!empty($turnerinnenIds)) {
$notenEntries = [];
}
$rangNote = intval(db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rangNote']));
var_dump($rangNote);
$indexedNotenEntries = [];
foreach ($notenEntries as $sn) {
if (!isset($indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']])) {
$indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']] = [];
}
$indexedNotenEntries[$sn['person_id']][$sn['note_bezeichnung_id']][$sn['geraet_id']][$sn['run_number']] = $sn['value'];
}
$orderBestRang = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['orderBestRang']);
$okValuesOrderBestRang = ["ASC", "DESC"];
if (!in_array($orderBestRang, $okValuesOrderBestRang)) {
header('Content-Type: application/json');
echo json_encode(["success" => false, "message" => "Invalide Sortierung der Ränge"]);
http_response_code(400);
exit;
}
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, default_value, nullstellen, pro_geraet, geraete_json, zeige_auf_rangliste, name", "zeige_auf_rangliste = ? OR id = ?", ['1', $rangNote]);
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, default_value, nullstellen, pro_geraet, geraete_json, zeige_auf_rangliste, groesse_auf_rangliste, name", "zeige_auf_rangliste = ? OR id = ?", ['1', $rangNote]);
$displayedNoten = [];
foreach ($alleNoten as $sN) {
$alleNotenIndexed = array_column($alleNoten, null, 'id');
foreach ($alleNotenIndexed as $key => $sN) {
if (intval($sN['zeige_auf_rangliste']) === 1) {
$displayedNoten[$sN['id']] = $sN;
$displayedNoten[$key] = $sN;
}
}
@@ -147,31 +182,38 @@ foreach ($ascArrayGeraeteJSON as $key => $saagj) {
// 1. Initialize the structure for every person
$indexedNotenArray = [];
$sortArray = [];
$notenGeraeteArray = array_merge($geraete, array(array("id" => 0)));
foreach ($personen as $sp) {
$pId = $sp['id'];
foreach ($geraete as $g) {
$indexedNotenArray[$pId][$g['id']] = [];
}
$indexedNotenArray[$pId][0] = []; // Total/Overall device per person
}
foreach ($displayedNoten as $an) {
$nId = $an['id'];
$isProGeraet = (intval($an['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$nId] ?? [];
// 2. Fill with existing database values
$sortArray = [];
foreach ($notenEntries as $sn) {
$pId = $sn['person_id'];
$gId = $sn['geraet_id'];
$nId = $sn['note_bezeichnung_id'];
$val = $sn['value'];
foreach ($notenGeraeteArray as $g) {
$gId = $g['id'];
if ($isProGeraet) {
if ($gId === 0) continue;
} else {
if (!in_array($gId, $allowedGeraete)) continue;
}
$indexedNotenArray[$pId][$gId][$nId] = $val;
// Check if this specific note is the one we rank by
// Usually ranking happens on Device 0 (Total) for the $rangNote ID
if (intval($nId) === $rangNote && intval($gId) === 0) {
$sortArray[$pId] = $val; // FIXED: used = instead of ===
$indexedNotenArray[$pId][$gId][$nId] = $indexedNotenEntries[$pId][$nId][$gId] ?? [$an['default_value']];
}
if (intval($nId) === $rangNote) {
$val = $indexedNotenArray[$pId][0][$nId] ?? [$an['default_value']];
$sortArray[$pId] = is_array($val) ? end($val) : $val; // fallback to the last run for sorting if needed, or total should just be one run
}
}
}
$wkWebpageDomain = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['linkWebseite']);
// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';
@@ -181,227 +223,318 @@ if (file_exists($fontfile)) {
$fontname = TCPDF_FONTS::addTTFfont($fontfile, 'TrueTypeUnicode', '', 32);
}
class MYPDF extends TCPDF
class RanglistePDF extends TCPDF
{
public $current_year;
public $programm;
public $upperprogramm;
public $columns;
public $disciplines;
public $wkName;
public $marginSide;
public $marginTop;
public $pdfHeight;
public $pdfWidth;
public $wkWebpageDomain;
// Page header
public $headerBottomY = 0;
public function Header()
{
$image_file = 'https://kutu-tage-beider-basel.testseite-fh-ht.chhttps://kutu-tage-beider-basel.testseite-fh-ht.ch/wp-content/uploads/2025/06/ktbb-logo.png';
$this->SetY(15);
$this->SetFont('helvetica', 'B', 20);
$this->Cell(0, 0, 'Rangliste Kutu-Tage beider Basel ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'M', 'M');
$this->SetFillColor(255, 255, 255);
$this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');
$arrayTitles = [];
$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
$this->SetY($this->marginTop);
$this->SetX(($this->marginSide ?? 10));
$this->SetFont('freesans', '', 20);
$this->Cell(0, 0, 'Rangliste ' . $this->wkName . ' ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, 272, 5, 15, '', 'JPG', '', 'T', false, 300, '', false, false, 0, false, false, false);
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->SetX($preimageX);
$this->SetY($preimageY);
$this->SetY($preimageY);
$this->SetX($this->marginSide ?? 10); // Force back to left margin
$this->SetFont('freesans', '', 11);
$this->Cell(0, 10, 'Programm: ' . $this->upperprogramm, 0, 1, 'L');
$this->SetFont('helvetica', '', 11);
$this->Cell(0, 10, 'Programm: ' . $this->upperprogramm, 0, 1, 'L');
$this->Ln(5);
$columns = $this->columns;
$disciplines = $this->disciplines;
// Print first header row
$preheaderX = $this->GetX();
$preheaderY = $this->GetY();
$this->SetFont('', 'B');
$this->Cell($columns['rang']['max_width'], 7, $columns['rang']['header'], 0, 0, 'C');
$this->Cell($columns['name']['max_width'], 7, $columns['name']['header'], 0, 0, 'L');
$this->Cell($columns['vorname']['max_width'], 7, $columns['vorname']['header'], 0, 0, 'L');
$this->Cell($columns['geburtsdatum']['max_width'], 7, $columns['geburtsdatum']['header'], 0, 0, 'C');
$this->Cell($columns['verein']['max_width'], 7, $columns['verein']['header'], 0, 0, 'L');
$startX = $this->GetX();
$startY = $this->GetY();
$this->SetLineWidth(0.2);
$this->SetFont('', 'B');
// Loop through disciplines for rotated headers
foreach ($disciplines as $d) {
$w = $columns["d-note $d"]['max_width'] + $columns["note $d"]['max_width'];
$ws = $columns["note $d"]['max_width'];
$this->SetX($this->marginSide ?? 10);
$x = $this->GetX();
$y = $this->GetY() + 7;
foreach ($columns as $col) {
$isDuplicate = in_array($col['header'], $arrayTitles);
// Start transformation for rotation
$this->StartTransform();
$title = $isDuplicate ? '' : $col['header'];
// Rotate around top-left of cell
$this->Rotate(45, $x, $y);
if (!$isDuplicate) {
$arrayTitles[] = $col['header'];
}
// Draw a line **above the first data row**
$lineY = $startY + 7; // adjust 7 depending on cell height
$this->Line($x, $lineY, $x + $ws, $lineY);
if (($col['rotate'] ?? false) && !$isDuplicate) {
$x = $this->GetX();
$y = $this->GetY() + 7;
// Print the rotated header text
$this->Cell($w, 7, ucfirst($d), 0, 0, 'L');
$this->StartTransform();
$this->Rotate(45, $x, $y);
$this->StopTransform();
$lineY = $startY + 7;
if (($col['id'] ?? '') === 'gesamt') {
$this->SetLineWidth(0.6);
} else {
$this->SetLineWidth(0.2);
}
$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
$this->SetLineWidth(0.2);
$this->SetX($x + $w);
$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
$this->StopTransform();
$this->SetX($x + ($col['max_width'] ?? 0));
} else {
$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
}
}
$w = $columns["gesamt"]['max_width'];
$ws = $w;
$x = $this->GetX();
$y = $this->GetY() + 7;
// Start transformation for rotation
$this->StartTransform();
// Rotate around top-left of cell
$this->Rotate(45, $x, $y);
// Draw a line **above the first data row**
$lineY = $startY + 7; // adjust 7 depending on cell height
$this->SetLineWidth(0.6);
$this->Line($x, $lineY, $x + $ws, $lineY);
$this->SetLineWidth(0.2);
// Print the rotated header text
$this->Cell($w, 7, $columns['gesamt']['header'], 0, 0, 'L');
$this->StopTransform();
$this->SetX($x + $w);
$afterheaderX = $this->GetX();
$this->Ln();
$xheaderline = $preheaderX;
$yheaderline = $this->GetY();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetY(-15);
$this->SetFont('helvetica', 'I', 8);
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M');
$this->SetFont('', '');
$this->Cell(0, 10, $_SERVER['HTTP_HOST'], 0, 0, 'R', false, 'https://'.$_SERVER['HTTP_HOST'].'/ergebnis/'.$this->current_year, 0, false, 'T', 'M');
$this->SetLineWidth(0.6);
$this->Line(10, $this->headerBottomY, 297 - 10, $this->headerBottomY);
$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(5);
$this->SetX(0);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(5, 190, '', 0, 0, 'L', true);
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX(297 - 10);
$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));
$this->Cell(5, 190, '', 0, 0, 'L', true);
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(-15);
$this->SetFont('freesans', 'I', 8);
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M');
$this->SetFont('', '');
$this->Cell(0, 10, ($this->wkWebpageDomain ?? $_SERVER['HTTP_HOST']), 0, 0, 'R', false, 'https://' . ($this->wkWebpageDomain ?? $_SERVER['HTTP_HOST']), 0, false, 'T', 'M');
}
}
$pdf = new MYPDF('L', 'mm', 'A4', true, 'UTF-8', false);
$pdf = new RanglistePDF($orientation, 'mm', $format, true, 'UTF-8', false);
$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();
$pdf->current_year = $current_year;
$pdf->wkName = $wkName;
$pdf->wkWebpageDomain = $wkWebpageDomain;
$pdf->programm = $programm;
$pdf->upperprogramm = $upperprogramm;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('WKVS-FH');
$pdf->SetTitle("KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf");
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Ergebnisse_" . $programm . "_" . $current_year . ".pdf");
$pdf->SetAutoPageBreak(TRUE, 10);
$pdf->SetFont('helvetica', '', 11);
$pdf->SetAutoPageBreak(FALSE);
// Define columns (including discipline sub-columns)
$pdf->SetFont('freesans', '', 11);
// Define columns dynamically
$columns = [
'rang' => [ 0 => ['header' => 'Rang']],
'name' => [ 0 => ['header' => 'Name']],
'vorname' => [ 0 => ['header' => 'Vorname']],
'geburtsdatum' => [ 0 => ['header' => 'Jg.']],
'verein' => [ 0 => ['header' => 'Verein']],
['id' => 'rang', 'header' => 'Rang', 'align' => 'C', 'flex' => false, 'type' => 'rank'],
['id' => 'name', 'header' => 'Name', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'name'],
['id' => 'vorname', 'header' => 'Vorname', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'vorname'],
['id' => 'geburtsdatum', 'header' => 'Jg.', 'align' => 'C', 'flex' => false, 'type' => 'year', 'field' => 'geburtsdatum'],
['id' => 'verein', 'header' => 'Verein', 'align' => 'L', 'flex' => true, 'type' => 'field', 'field' => 'verein'],
];
foreach ($displayedNoten as $sdN) {
if (intval($sdN['pro_geraet']) === 1) {
foreach ($geraete as $g) {
$columns[$sdN['name']][$g['id']] = ['header' => $sdN['name'], 'nullstellen' => $sdN['nullstellen'] ?? 2];
}
} else {
$allowed = $ascArrayGeraeteJSON[$sdN['id']] ?? [];
foreach ($allowed as $gId) {
$columns[$sdN['name']][$gId] = ['header' => $sdN['name'], 'nullstellen' => $sdN['nullstellen'] ?? 2];
// Add notes dynamically per device
foreach ($geraete as $g) {
foreach ($displayedNoten as $noteDef) {
$neni = $noteDef['id'];
$isProGeraet = (intval($noteDef['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if ($isProGeraet || in_array($g['id'], $allowedGeraete)) {
$columns[] = [
'id' => 'note_' . $g['id'] . '_' . $neni,
'header' => $g['name'],
'align' => 'C',
'flex' => false,
'rotate' => true,
'type' => 'note',
'geraet_id' => $g['id'],
'note_bezeichnung_id' => $neni,
'nullstellen' => $noteDef['nullstellen'] ?? 2,
'font_size' => intval($noteDef['groesse_auf_rangliste']) ?? 0
];
}
}
}
}
// Initialize max widths with header widths (+4 mm padding)
foreach ($columns as $key => $col) {
foreach ($col as $skey => $scol) {
$columns[$key][$skey]['max_width'] = $pdf->GetStringWidth($scol['header']) + 8;
}
// Add Gesamt (Total) column
$columns[] = [
'id' => 'gesamt',
'header' => $alleNotenIndexed[$rangNote]['name'] ?? 'Total',
'align' => 'C',
'flex' => false,
'rotate' => true,
'type' => 'note',
'geraet_id' => 0,
'note_bezeichnung_id' => $rangNote,
'nullstellen' => $alleNotenIndexed[$rangNote]['nullstellen'] ?? 2,
'font_size' => intval($alleNotenIndexed[$rangNote]['groesse_auf_rangliste']) ?? 0
];
$padding = 4;
unset($col);
// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
$col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
if ($col['type'] !== 'note') {
$col['max_width'] = $col['width_header'];
} else {
$col['max_width'] = 0;
}
}
// 3. Fill missing defaults and finalize the sortArray
// 2. Fill missing defaults and update max widths based on data
foreach ($personen as $sp) {
$pId = $sp['id'];
// We check all possible devices (including 0)
foreach ($indexedNotenArray[$pId] as $sG => $currentNoten) {
foreach ($alleNoten as $noteDef) {
foreach ($displayedNoten as $noteDef) {
$neni = $noteDef['id'];
// Skip if note already exists for this person/device
if (isset($currentNoten[$neni])) continue;
// Logic for "Pro Gerät" vs "Specific/Total"
$isProGeraet = (intval($noteDef['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if ($isProGeraet) {
if ($sG === 0) continue;
if (isset($currentNoten[$neni])) {
$runs = $currentNoten[$neni];
if (is_array($runs)) {
$runsConfig = $ascArrayAnzahlLaeufeJSON[$neni] ?? [];
$runsCount = $runsConfig[$sG][$programmId] ?? $runsConfig[$sG]['all'] ?? $runsConfig["default"] ?? 1;
$defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
for ($r = 1; $r <= $runsCount - 1; $r++) {
if (!isset($runs[$r])) {
$runs[$r] = $defaultValue;
}
}
$indexedNotenArray[$pId][$sG][$neni] = $runs;
ksort($runs);
$maxLenStr = "";
foreach ($runs as $rVal) {
$str = number_format((float)$rVal, $noteDef['nullstellen']);
// Approximate max width by string length, GetStringWidth will do the real check later
if (strlen($str) > strlen($maxLenStr)) {
$maxLenStr = $str;
}
}
$val = $maxLenStr;
} else {
$val = number_format((float)$runs, $noteDef['nullstellen']);
}
} else {
if (!in_array($sG, $allowedGeraete)) continue;
// Logic for "Pro Gerät" vs "Specific/Total"
$isProGeraet = (intval($noteDef['pro_geraet']) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if ($isProGeraet) {
if ($sG === 0) continue;
} else {
if (!in_array($sG, $allowedGeraete)) continue;
}
$defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
$runsConfig = $ascArrayAnzahlLaeufeJSON[$neni] ?? [];
$runsCount = $runsConfig[$sG][$programmId] ?? $runsConfig[$sG]['all'] ?? $runsConfig["default"] ?? 1;
$valArray = [];
$maxLenStr = "";
for ($r = 1; $r <= $runsCount - 1; $r++) {
$valArray[$r] = $defaultValue;
$str = number_format((float)$defaultValue, $noteDef['nullstellen']);
if (strlen($str) > strlen($maxLenStr)) {
$maxLenStr = $str;
}
}
$val = $maxLenStr;
$indexedNotenArray[$pId][$sG][$neni] = $valArray;
if ($neni === $rangNote && $sG === 0) {
$sortArray[$pId] = $defaultValue;
}
}
// Assign default value
$defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
$string = nuber_format($defaultValue, $noteDef['nullstellen']);
// Update column width if this note is displayed
foreach ($columns as &$col) {
if ($col['type'] === 'note' && $col['geraet_id'] == $sG && $col['note_bezeichnung_id'] == $neni) {
$indexedNotenArray[$pId][$sG][$neni] = $string;
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
$width = $pdf->GetStringWidth($string) + 4;
$cpadding = $padding;
if ($w > $columns[$noteDef['name']][$sG]['max_width']) {
$columns[$noteDef['name']][$sG] = $w;
}
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
$cpadding = ($col['font_size'] / $normalFontSize) * $padding;
}
// If this missing note was our ranking note, add default to sortArray
if ($neni === $rangNote && $sG === 0) {
$sortArray[$pId] = $defaultValue;
$width = $pdf->GetStringWidth($val) + ($cpadding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
}
unset($col);
}
}
// Also update widths for static fields
foreach ($columns as &$col) {
if ($col['type'] === 'field' || $col['type'] === 'year' || $col['type'] === 'rank') {
$text = '';
if ($col['type'] === 'rank') {
$text = '999'; // Placeholder for rank width
} elseif ($col['type'] === 'year') {
$text = '0000';
} elseif (isset($sp[$col['field']])) {
$text = $sp[$col['field']];
}
$width = $pdf->GetStringWidth($text) + ($padding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
}
}
unset($col);
}
function calculateRanks(array $scoreArray, $direction = 'DESC') {
@@ -431,66 +564,40 @@ function calculateRanks(array $scoreArray, $direction = 'DESC') {
return $ranks;
}
// 4. Calculate Ranks
// FIXED: Passing $orderBestRang (the string "ASC"/"DESC") instead of the array
// 3. Finalize Ranks
$ranks = calculateRanks($sortArray, $orderBestRang);
// Calculate max width of each column based on all data
foreach ($personen as $p) {
foreach ($columns as $key => &$col) {
if (isset($p[$key])) {
$text = $p[$key];
} else {
$text = '';
}
$w = $pdf->GetStringWidth($text) + 4;
// Add some padding
if ($w > $col['max_width']) {
$col['max_width'] = $w;
}
}
}
unset($col);
$columns['geburtsdatum'][0]['max_width'] = $pdf->GetStringWidth("0000") + 8;
$tablew = 297 - 20; // total table width (A4 landscape in mm)
// Step 1: Calculate total width of fixed columns
// 4. Distribute Flexible Widths
$tablew = $pdfWidth - (2 * $marginSide); // total table width (A4 landscape in mm minus margins)
$fixedWidth = 0;
$flexCols = [];
/*
// Discipline columns (each discipline has two columns: "d-note" and "note")
foreach ($disciplines as $d) {
$fixedWidth += $columns["d-note $d"]['max_width'] + $columns["note $d"]['max_width'];
foreach ($columns as $col) {
if ($col['flex'] ?? false) {
$flexCols[] = $col['id'];
$fixedWidth += $col['max_width'];
} else {
$fixedWidth += $col['max_width'];
}
}
// Other fixed columns
$fixedColumns = ['geburtsdatum','rang','gesamt'];
foreach ($fixedColumns as $col) {
$fixedWidth += $columns[$col]['max_width'];
}
// Step 2: Compute extra space for flexible columns
$flexCols = ['name','vorname','verein']; // columns that can grow
$flexTotal = 0;
// Compute current total width of flexible columns
foreach ($flexCols as $col) {
$flexTotal += $columns[$col]['max_width'];
}
// Available width for flexible columns
$effTableWidth = $tablew - $fixedWidth;
$extraWidth = $effTableWidth - $flexTotal;
// Step 3: Distribute extra width proportionally among flexible columns
if ($extraWidth > 0) {
foreach ($flexCols as $col) {
$columns[$col]['max_width'] += ($columns[$col]['max_width'] / $flexTotal) * $extraWidth;
}
if (!empty($flexCols) && $effTableWidth > 0) {
$flexTotalInitial = 0;
foreach ($columns as $col) {
if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
}
foreach ($columns as &$col) {
if ($col['flex'] ?? false) {
$col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
}
}
unset($col);
}
*/
$pdf->columns = $columns;
$pdf->disciplines = $disciplines;
@@ -501,14 +608,19 @@ $pdf->AddPage();
$margin_top = $pdf->headerBottomY;
// Now adjust your margins if needed
$pdf->SetMargins(10, $margin_top, 10); // +5 mm padding below header
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$mbs = false;
$mbl = false;
$pdf->SetFont('', '');
$index = 0;
$rIndex = 0;
usort($personen, function($a, $b) use ($ranks) {
$rankA = $ranks[$a['id']];
$rankB = $ranks[$b['id']];
return $rankA <=> $rankB;
});
foreach ($personen as $r) {
$pdf->SetTextColor(0, 0, 0);
@@ -519,81 +631,118 @@ foreach ($personen as $r) {
$pdf->SetFillColor(255, 235, 128);
} elseif($rang == 2) {
$pdf->SetFillColor(224, 224, 224);
} elseif($$rang == 3) {
} elseif($rang == 3) {
$pdf->SetFillColor(230, 191, 153);
} elseif ($index % 2 == 0) {
} elseif ($rIndex % 2 == 0) {
$pdf->SetFillColor(255, 255, 255); // white
} else {
$pdf->SetFillColor(240, 240, 240); // light gray
}
$pdf->SetFont('', 'B');
/*
if ($buttontype === 'export_programm_bm') {
if (($r['verein'] === 'BTV Basel' || $r['verein'] === 'TV Basel') && $mbs === false){
$pdf->SetFillColor(0, 0, 0);
$pdf->SetTextColor(255, 255, 255);
$mbs = true;
} elseif (($r['verein'] === 'Kutu Regio Basel') && $mbl === false) {
$pdf->SetFillColor(255, 0, 0);
$pdf->SetTextColor(255, 255, 255);
$mbl = true;
}
}*/
$pdf->Cell($columns['rang'][0]['max_width'], 8, $rang, 'B', 0, 'C', true);
$pdf->SetFont('', '');
$pdf->Cell($columns['name'][0]['max_width'], 8, $r['name'], 'B', 0, 'L', true);
$pdf->Cell($columns['vorname'][0]['max_width'], 8, $r['vorname'], 'B', 0, 'L', true);
$pdf->Cell($columns['geburtsdatum'][0]['max_width'], 8, (new DateTime($r['geburtsdatum']))->format("Y"), 'B', 0, 'C', true);
$pdf->Cell($columns['verein'][0]['max_width'], 8, $r['verein'], 'B', 0, 'L', true);
foreach ($disciplines as $d) {
$d_note = isset($r["d-note $d"]) ?
number_format((float)$r["d-note $d"], 2) : '';
$note = isset($r["note $d"]) ? number_format((float)$r["note $d"], 3) : '';
$pdf->SetFont('', '', 9);
$pdf->Cell($columns["d-note $d"]['max_width'], 8, $d_note, 'LB', 0, 'C', true);
$pdf->SetFont('', '', 11);
$pdf->Cell($columns["note $d"]['max_width'], 8, $note, 'B', 0, 'C', true);
// Determine row height based on max runs
$maxRuns = 1;
foreach ($columns as $col) {
if ($col['type'] === 'note') {
$runs = $indexedNotenArray[$r['id']][$col['geraet_id']][$col['note_bezeichnung_id']] ?? [];
if (is_array($runs) && count($runs) > $maxRuns) {
$maxRuns = count($runs);
}
}
}
$gesamt = isset($r["gesamtpunktzahl"]) ? number_format((float)$r["gesamtpunktzahl"], 3) : '';
// Base height is 8, add extra height for additional runs. Let's say 4mm per additional line.
// But to keep it centered properly, we will use MultiCell with valign='M' (Middle).
$rowHeight = max(8, $maxRuns * 5 + 3); // 5mm per line
$pdf->SetFont('', 'B');
$pdf->Cell($columns['gesamt'][0]['max_width'], 8, $gesamt, 'B', 0, 'C', true);
$pdf->SetFont('', '');
if ($pdf->getY() + $rowHeight >= $pdfHeight - $minMarginBottomTable) {
$pdf->addPage();
$pdf->setX($marginSide);
}
$x = $pdf->GetX() - $columns['gesamt'][0]['max_width'];
$y = $pdf->GetY();
foreach ($columns as $col) {
$pdf->SetLineWidth(0.6);
$pdf->Line($x, $y, $x, $y + 7.7);
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
$pdf->SetLineWidth(0.2);
$pdf->Ln();
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
}
$index++;
$startX = $pdf->GetX();
$startY = $pdf->GetY();
if ($col['id'] === 'rang') {
$pdf->SetFont('', 'B');
$pdf->MultiCell($col['max_width'], $rowHeight, $rang, 'B', 'C', true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
$pdf->SetFont('', '');
} elseif ($col['type'] === 'field') {
$pdf->MultiCell($col['max_width'], $rowHeight, $r[$col['field']], 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
} elseif ($col['type'] === 'year') {
$pdf->MultiCell($col['max_width'], $rowHeight, (new DateTime($r[$col['field']]))->format("Y"), 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
} elseif ($col['type'] === 'note') {
$runs = $indexedNotenArray[$r['id']][$col['geraet_id']][$col['note_bezeichnung_id']] ?? [];
if (!is_array($runs)) {
$runs = [$runs];
} else {
ksort($runs);
}
$numRuns = max(count($runs), 1);
$lineHeight = $rowHeight / $numRuns;
$pdf->SetXY($startX, $startY);
$pdf->Cell($col['max_width'], $rowHeight, '', 0, 0, '', true);
$runIndex = 0;
foreach (array_values($runs) as $rnVal) {
$formatted = number_format((float)$rnVal, $col['nullstellen'] ?? 0);
$currentY = $startY + ($runIndex * $lineHeight);
$pdf->SetXY($startX, $currentY);
$pdf->Cell($col['max_width'], $lineHeight, $formatted, 0, 0, $col['align'], false);
$runIndex++;
}
// Draw the bottom border for the entire row height
$pdf->Line($startX, $startY + $rowHeight, $startX + $col['max_width'], $startY + $rowHeight);
// Reset pointer for the next column
$pdf->SetXY($startX + $col['max_width'], $startY);
if ($col['id'] === 'gesamt') {
// Draw the heavy separator line on the left side of the "Gesamt" column
$pdf->SetLineWidth(0.6);
$pdf->Line($startX, $startY, $startX, $startY + $rowHeight);
$pdf->SetLineWidth(0.2);
}
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->SetY($startY + $rowHeight); // Move Y manually instead of Ln() to account for rowHeight
$rIndex++;
}
$pdf->SetFont('', '', 7);
$textanzturnerinnen = (count($entries) > 1) ? 'Turnerinnen': 'Turnerin';
$pdf->Cell(20, 6, count($entries).' '.$textanzturnerinnen, 0, 0, 'L');
$textanzturnerinnen = (count($personen) > 1) ? 'Turnerinnen': 'Turnerin';
$pdf->Cell(20, 6, count($personen).' '.$textanzturnerinnen, 0, 0, 'L');
if ($buttontype === 'export_programm' || $buttontype === 'export_programm_bm') {
$pdf->Output("KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf", 'D');
if ($buttontype === 'downloadRangliste') {
$pdf->Output($wkName . "_Ergebnisse_" . $programm . "_" . $current_year . ".pdf", 'I');
exit;
} elseif ($buttontype === 'upload_programm') {
$dir = $baseDir . '/wp-content/ergebnisse/';
} elseif ($buttontype === 'updateServerRangliste') {
$dir = $baseDir . '/files/ranglisten/';
if (!file_exists($dir)) mkdir($dir, 0755, true);
$localPath = $dir . "KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf";
$localPath = $dir . str_replace("/", "", str_replace(" ", "_", $wkName)) . "_Ergebnisse_" . str_replace("/", "", $programm) . "_" . $current_year . ".pdf";
if (file_exists($localPath)) unlink($localPath);
$pdf->Output($localPath, 'F');
$_SESSION['form_message'] = '<div class="success">PDF wurde hochgeladen</div>';
header('Content-Type: application/json');
echo json_encode(["success" => true, "message" => "PDF wurde aktualisiert"]);
exit;
}
@@ -24,34 +24,54 @@ if ($data['success'] === false){
require $baseDir . '/../scripts/db/db-tables.php';
// ---------- Get and sanitize input ----------
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$discipline = isset($_POST['discipline']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_POST['discipline']) : '';
$personId = intval($_POST['personId'] ?? 0);
$gereatId = intval($_POST['geraetId'] ?? 0);
if ($discipline !== 'boden') {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
if ($gereatId < 1) {
echo json_encode(['success' => false, 'message' => 'Kein Gerät angegeben']);
exit;
}
if ($id <= 0) {
if ($personId < 1) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$result = $mysqli->query("SELECT t.bodenmusik, agg.filePath FROM `$tableTurnerinnen` t LEFT JOIN (SELECT a.id, a.file_path AS filePath FROM $tableAudiofiles a) agg ON agg.id = t.bodenmusik WHERE t.id = $id");
$row = $result->fetch_assoc();
if (!$row || !isset($row['bodenmusik'])) {
echo json_encode(['success' => false, 'message' => 'Row fetch failed '.$id]);
exit;
}
$sqlQuery = "SELECT
paf.`audiofile_id`,
af.`file_path`,
af.`file_name`,
tu.`name`,
tu.`vorname`
FROM $tableTeilnehmendeAudiofiles paf
INNER JOIN $tableAudiofiles af
ON paf.`audiofile_id` = af.`id`
LEFT JOIN $tableTeilnehmende tu
ON paf.`person_id` = tu.`id`
WHERE paf.`person_id` = ?
AND paf.`geraet_id` = ?
";
if ($row['bodenmusik'] === "0" || $row['filePath'] == null) {
$stmt = $mysqli->prepare($sqlQuery);
$stmt->bind_param('ii', $personId, $gereatId);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
if (count($rows) !== 1 || !isset($rows[0]['file_path']) || $rows[0]['file_path'] === null) {
echo json_encode(['success' => false, 'message' => 'Keine Musik']);
exit;
}
$folder = realpath($baseDir . '/displays/json');
$row = $rows[0];
$folder = realpath($baseDir . '/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
@@ -60,11 +80,11 @@ if ($folder === false) {
exit;
}
$filename = 'audio.json';
$filename = 'audio-id-'. $gereatId .'.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
echo json_encode(['success' => false, 'message' => 'Folder not writable: ']);
echo json_encode(['success' => false, 'message' => 'Folder not writable.']);
exit;
}
@@ -74,22 +94,23 @@ if (!is_file($filepath)){
}
$jsonString = file_get_contents($filepath);
$json = [
"audio_path" => $row['file_path'],
"audio_name" => $row['file_name'],
"start" => true,
"person" => [
"name" => $row['name'],
"vorname" => $row['vorname']
]
];
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
$oldjson["musik"] = $row['filePath'];
$oldjson["start"] = true;
$jsonData = json_encode($oldjson);
$jsonData = json_encode($json);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode([
'success' => false,
'message' => 'Failed to write JSON file: ' . $filepath
'message' => 'Failed to write JSON file'
]);
exit;
}
@@ -99,7 +120,6 @@ if (file_put_contents($filepath, $jsonData) === false) {
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for '.$discipline,
'disable_musik_button' => true
'message' => 'Audio JSON updated successfully'
]);
exit;
@@ -13,9 +13,14 @@ check_user_permission('kampfrichter');
verify_csrf();
$discipline = 'boden';
$gereatId = intval($_POST['geraetId'] ?? 0);
$folder = realpath($baseDir . '/displays/json');
if ($gereatId < 1) {
echo json_encode(['success' => false, 'message' => 'Kein Gerät angegeben']);
exit;
}
$folder = realpath($baseDir . '/externe-geraete/json');
if ($folder === false) {
echo json_encode([
'success' => false,
@@ -24,7 +29,7 @@ if ($folder === false) {
exit;
}
$filename = 'audio.json';
$filename = 'audio-id-'. $gereatId .'.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
@@ -36,14 +41,11 @@ if (!is_file($filepath)){
file_put_contents($filepath, []);
}
$jsonString = file_get_contents($filepath);
$json = [
"start" => false
];
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
$oldjson["start"] = false;
$jsonData = json_encode($oldjson);
$jsonData = json_encode($json);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
@@ -1,111 +0,0 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('kampfrichter');
verify_csrf();
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
$abteilung = isset($_POST['abteilung']) ? strval($_POST['abteilung']) : '1';
$aufgabe = isset($_POST['aufgabe']) ? strval($_POST['aufgabe']) : '2';
$name = isset($_POST['name']) ? strval($_POST['name']) : 'RK';
$geraet = isset($_POST['geraet']) ? strtolower($_POST['geraet']) : 'boden';
if ($abteilung === ''){
echo json_encode(['success' => false, 'message' => 'Keine Abteilung angegeben.']);
exit;
}
if ($aufgabe === ''){
echo json_encode(['success' => false, 'message' => 'Keine Aufgabe angegeben.']);
exit;
}
if (!is_numeric($aufgabe)){
echo json_encode(['success' => false, 'message' => 'Keine valide Aufgabe. (is not numeric)']);
exit;
}
if ($name === ''){
echo json_encode(['success' => false, 'message' => 'Kein Namen angegeben']);
exit;
}
$stmt = $mysqli->prepare("SELECT `name` FROM $tableGeraete ORDER BY start_index ASC");
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
$disciplines = array_map(
'strtolower',
array_column($result->fetch_all(MYSQLI_ASSOC), 'name')
);
$stmt->close();
if (!in_array($geraet, $disciplines)){
echo json_encode(['success' => false, 'message' => 'Invalides Gerät: '.$geraet]);
exit;
}
$stmt = $mysqli->prepare("SELECT * FROM $tableKrProtokoll WHERE abteilung = ? AND geraet = ? AND aufgabe = ? LIMIT 1");
$stmt->bind_param("sss", $abteilung, $geraet, $aufgabe);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row){
$updatestmt = $mysqli->prepare("UPDATE $tableKrProtokoll SET name = ? WHERE id = ?");
$updatestmt->bind_param("si", $name, $row['id']);
if (!$updatestmt->execute()) {
echo json_encode([
'success' => false,
'message' => 'Insert ERROR: ' . $updatestmt->error
]);
exit;
}
echo json_encode(['success' => true, 'message' => 'Updated: updated']);
exit;
} else {
$insertstmt = $mysqli->prepare("INSERT INTO $tableKrProtokoll (abteilung, geraet, name, aufgabe) VALUES (?, ?, ?, ?)");
$insertstmt->bind_param("ssss", $abteilung, $geraet, $name, $aufgabe);
if (!$insertstmt->execute()) {
echo json_encode([
'success' => false,
'message' => 'Insert ERROR: ' . $insertstmt->error
]);
exit;
}
echo json_encode(['success' => true, 'message' => 'Updated: inserted']);
exit;
}
?>
@@ -24,7 +24,6 @@ if ($data['success'] === false){
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$person_id = isset($_POST['personId']) ? intval($_POST['personId']) : 0;
$field_type_id = intval($_POST['fieldTypeId'] ?? 0);
$gereat_id = intval($_POST['gereatId'] ?? 0);
@@ -84,6 +83,8 @@ if ($singleNoteConfig['min_value'] !== null && $valueNoteUpdate < $singleNoteCon
exit;
}
$oldNote = db_get_var($mysqli, "SELECT `value` FROM $tableNoten WHERE `person_id` = ? AND `note_bezeichnung_id` = ? AND `geraet_id` = ? AND `jahr` = ? AND `run_number` = ?", [$person_id, $field_type_id, $gereat_id, $jahr, $run_number]);
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`, `run_number`)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
@@ -96,6 +97,19 @@ $stmt->execute();
$stmt->close();
$userId = (int) $_SESSION['user_id_kampfrichter'];
$sql = "INSERT INTO $tableNotenChanges (`new_value`, `old_value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`, `run_number`, `edited_by`)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ssiiiiii", $valueNoteUpdate, $oldNote, $person_id, $field_type_id, $gereat_id, $jahr, $run_number, $userId);
$stmt->execute();
$stmt->close();
if ($singleNoteConfig['berechnung_json'] === null) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert"]);
exit;
@@ -116,23 +130,21 @@ try {
$geraete = db_select($mysqli, $tableGeraete, "id");
$programmName = db_get_var($mysqli, "SELECT `programm` FROM $tableTurnerinnen WHERE `id` = ?", [$person_id]);
$programmName = db_get_var($mysqli, "SELECT `programm` FROM $tableTeilnehmende WHERE `id` = ?", [$person_id]);
$programmId = db_get_var($mysqli, "SELECT `id` FROM $tableProgramme WHERE `programm` = ?", [$programmName]);
// Alle Werte werden von der Datenbank geholt und werden, wenn nicht vorhanden, durch den Standartwert ersetzt.
// We load the configuration native cached array
$cache = require $baseDir . '/../scripts/cache/noten-config-calculating-cache.php';
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, berechnung, default_value, nullstellen, pro_geraet, geraete_json, anzahl_laeufe_json");
$noten = db_select($mysqli, $tableNoten, "`value`, `note_bezeichnung_id`, `geraet_id`, `run_number`", "`person_id` = ? AND `jahr` = ? AND `value` IS NOT NULL", [$person_id, $jahr]);
$noten = db_select($mysqli, $tableNoten, "`value`, `note_bezeichnung_id`, `geraet_id`, `run_number`", "`person_id` = ? AND `jahr` = ?", [$person_id, $jahr]);
$ascArrayDefaultValues = array_column($alleNoten, 'default_value', 'id');
$ascArrayProGeraet = array_column($alleNoten, 'pro_geraet', 'id');
$ascArrayGeraeteJSON = array_column($alleNoten, 'geraete_json', 'id');
$ascArrayAnzahlLaeufeJSON = array_column($alleNoten, 'anzahl_laeufe_json', 'id');
$ascArrayRechnungen = array_column($alleNoten, 'berechnung', 'id');
// $proGeraet = intval($calc['pro_geraet']) !== 1;
$ascArrayDefaultValues = $cache['default_values'];
$ascArrayProGeraet = $cache['pro_geraet'];
$ascArrayGeraeteJSON = $cache['geraete_json'];
$ascArrayAnzahlLaeufeJSON = $cache['anzahl_laeufe_json'];
$ascArrayRechnungen = $cache['rechnungen'];
$indexedNullstellen = $cache['nullstellen'];
$mRunFunctions = [];
@@ -143,20 +155,11 @@ foreach ($abhaenigeRechnungen as $saRechnung) {
if ($mRunCalc) {
$mRunFunctions[] = $saRechnung[0];
break;
}
}
$indexedNotenArray = [];
foreach ($ascArrayGeraeteJSON as $key => $saagj) {
$ascArrayGeraeteJSON[$key] = json_decode($saagj, true);
}
foreach ($ascArrayAnzahlLaeufeJSON as $key => $saalj) {
$ascArrayAnzahlLaeufeJSON[$key] = json_decode($saalj, true) ?? [];
}
foreach ($geraete as $g) {
$indexedNotenArray[$g['id']] = [];
}
@@ -164,76 +167,61 @@ foreach ($geraete as $g) {
$indexedNotenArray[0] = [];
foreach ($noten as $sn) {
$indexedNotenArray[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = $sn['value'];
$indexedNotenArray[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = ["value" => $sn['value'], "type" => "note"];
}
$alleNotenIds = array_column($alleNoten, 'id') ?? [];
$requiredNoteIds = [];
$requiredNoteIds[$field_type_id] = true;
foreach ($abhaenigeRechnungen as $saRechnung) {
// Only load dependencies for the specifically connected calculations
$sRechnungStr = $ascArrayRechnungen[$saRechnung[0]] ?? '';
if ($sRechnungStr !== '') {
$ids = $notenRechner->getBenoetigteIdsComplex($sRechnungStr);
foreach ($ids as $idConfig) {
$requiredNoteIds[(int)$idConfig['noteId']] = true;
}
}
}
$alleNotenIds = array_keys($requiredNoteIds);
if (count($mRunFunctions) > 0) {
foreach ($indexedNotenArray as $sG => $siNA) {
foreach ($alleNotenIds as $neni) { // Use $neni as the ID
// 1. Skip if no default value is defined
if (!isset($ascArrayDefaultValues[$neni])) {
continue;
}
foreach ($alleNotenIds as $neni) {
if (!isset($ascArrayDefaultValues[$neni])) continue;
// 2. Logic Check: Is this note assigned to this device?
$isProGeraet = (int)($ascArrayProGeraet[$neni] ?? 0);
if ($isProGeraet === 1 && (int)$sG === 0) {
continue;
}
if ($isProGeraet === 1 && (int)$sG === 0) continue;
if ($isProGeraet !== 1) {
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if (!is_array($allowedGeraete) || !in_array($sG, $allowedGeraete)) {
continue;
}
if (!is_array($allowedGeraete) || !in_array($sG, $allowedGeraete)) continue;
}
$runs = $ascArrayAnzahlLaeufeJSON[$neni][$sG][$programmId] ?? $ascArrayAnzahlLaeufeJSON[$neni]["default"] ?? 1;
$runs = $ascArrayAnzahlLaeufeJSON[$neni][$sG][$programmId] ?? $ascArrayAnzahlLaeufeJSON[$neni][$sG]['all'] ?? $ascArrayAnzahlLaeufeJSON[$neni]["default"] ?? 1;
for ($r = 1; $r <= $runs; $r++) {
if (isset($indexedNotenArray[$sG][$neni][$r])) {
continue;
if (!isset($indexedNotenArray[$sG][$neni][$r])) {
$indexedNotenArray[$sG][$neni][$r] = ["value" => $ascArrayDefaultValues[$neni], "type" => "default_value"];
}
$indexedNotenArray[$sG][$neni][$r] = $ascArrayDefaultValues[$neni];
}
}
}
} else {
foreach ($indexedNotenArray as $sG => $siNA) {
foreach ($alleNotenIds as $neni) { // Use $neni as the ID
// 1. Skip if value already exists for this specific run
if (isset($indexedNotenArray[$sG][$neni][$run_number])) {
continue;
}
foreach ($alleNotenIds as $neni) {
if (isset($indexedNotenArray[$sG][$neni][$run_number])) continue;
if (!isset($ascArrayDefaultValues[$neni])) continue;
// 2. Skip if no default value is defined
if (!isset($ascArrayDefaultValues[$neni])) {
continue;
}
// 3. Logic Check: Is this note assigned to this device?
$isProGeraet = (int)($ascArrayProGeraet[$neni] ?? 0);
if ($isProGeraet === 1 && (int)$sG === 0) {
continue;
}
if ($isProGeraet === 1 && (int)$sG === 0) continue;
if ($isProGeraet !== 1) {
$allowedGeraete = $ascArrayGeraeteJSON[$neni] ?? [];
if (!is_array($allowedGeraete) || !in_array($sG, $allowedGeraete)) {
continue;
}
if (!is_array($allowedGeraete) || !in_array($sG, $allowedGeraete)) continue;
}
// 4. Assign the default value
$indexedNotenArray[$sG][$neni][$run_number] = $ascArrayDefaultValues[$neni];
$indexedNotenArray[$sG][$neni][$run_number] = ["value" => $ascArrayDefaultValues[$neni], "type" => "default_value"];
}
}
}
@@ -244,6 +232,7 @@ $idsToSave = [];
foreach ($abhaenigeRechnungen as $sRechnung) {
$targetNoteId = $sRechnung[0];
$rechnungType = $sRechnung[1];
$targetRun = $sRechnung[2][0] ?? 'A';
// 1. Initial Filter
if ($rechnungType !== "A" && intval($rechnungType) !== $gereat_id) continue;
@@ -266,14 +255,14 @@ foreach ($abhaenigeRechnungen as $sRechnung) {
// 3. Calculation Logic
$runsConfig = $ascArrayAnzahlLaeufeJSON[$targetNoteId] ?? [];
$runs = $runsConfig[$gereat_id][$programmId] ?? $runsConfig["default"] ?? 1;
$runs = $runsConfig[$targetGeraetKey][$programmId] ?? $runsConfig[$targetGeraetKey]["all"] ?? $runsConfig["default"] ?? 1;
$acrun = min($runs, $run_number);
if (in_array($targetNoteId, $mRunFunctions)) {
$calcResult = $notenRechner->berechneStringComplexRun($rechnung, $indexedNotenArray, $gereat_id, $programmId, $ascArrayAnzahlLaeufeJSON);
$calcResult = $notenRechner->berechneStringComplexRun($rechnung, $indexedNotenArray, $targetGeraetKey, $programmId, $ascArrayAnzahlLaeufeJSON);
} else {
$calcResult = $notenRechner->berechneStringComplex($rechnung, $indexedNotenArray, $gereat_id, $acrun);
$calcResult = $notenRechner->berechneStringComplex($rechnung, $indexedNotenArray, $targetGeraetKey, $acrun);
}
if (!($calcResult['success'] ?? false)) {
@@ -281,10 +270,13 @@ foreach ($abhaenigeRechnungen as $sRechnung) {
exit;
}
$acTargetRun = ($targetRun === "A") ? $acrun : intval($targetRun);
// 4. Update State
$val = $calcResult['value'];
$indexedNotenArray[$targetGeraetKey][$targetNoteId][$acrun] = $val;
$updatedValues[$targetGeraetKey][$targetNoteId][$acrun] = $val;
$indexedNotenArray[$targetGeraetKey][$targetNoteId][$acTargetRun] = ["value" => $val, "type" => "berechnet"];
$updatedValues[$targetGeraetKey][$targetNoteId][$acTargetRun] = $val;
}
// Prepare the statement once
@@ -293,11 +285,10 @@ $sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `g
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
$indexedNullstellen = array_column($alleNoten, 'nullstellen', 'id');
$formatedNoten = [];
$mysqli->begin_transaction();
foreach ($updatedValues as $gereat => $notenArray) {
foreach ($notenArray as $note => $runArray) {
foreach ($runArray as $run => $value) {
@@ -308,6 +299,8 @@ foreach ($updatedValues as $gereat => $notenArray) {
}
}
$mysqli->commit();
$formatedNoten[$gereat_id][$field_type_id][$run_number] = number_format($valueNoteUpdate ,$indexedNullstellen[$field_type_id] ?? 2);
$stmt->close();
@@ -14,10 +14,10 @@ check_user_permission('kampfrichter');
verify_csrf();
// ---------- Get and sanitize input ----------
$id = intval($_POST['personId']) ?? 0;
$run = intval($_POST['run']) ?? 0;
$geraetId = intval($_POST['geraetId']) ?? 0;
$dataType = intval($_POST['dataType']) ?? 0;
$id = intval($_POST['personId'] ?? 0);
$initNoteRun = intval($_POST['run'] ?? 0);
$geraetId = intval($_POST['geraetId'] ?? 0);
$dataType = intval($_POST['dataType'] ?? 0);
$jahr = isset($_POST['jahr']) ? preg_replace('/[^0-9]/', '', $_POST['jahr']) : '';
$anfrageType = $_POST['type'] ?? '';
@@ -70,7 +70,7 @@ $geraetName = $geraetData['name'];
$stmt->close();
$folder = realpath($baseDir . '/displays/json');
$folder = realpath($baseDir . '/externe-geraete/json');
if ($folder === false) {
echo json_encode([
@@ -80,7 +80,7 @@ if ($folder === false) {
exit;
}
$filename = 'display_' . strtolower($geraetName) . '.json';
$filename = 'display-id-' . $geraetId . '.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
@@ -95,7 +95,7 @@ $oldjson = json_decode($jsonString, true) ?? [];
switch ($anfrageType) {
case "neu":
$stmt = $mysqli->prepare("SELECT * FROM `$tableTurnerinnen` WHERE id = ? LIMIT 1");
$stmt = $mysqli->prepare("SELECT `id`, `name`, `vorname`, `programm`, `verein` FROM `$tableTeilnehmende` WHERE id = ? LIMIT 1");
$stmt->bind_param("i", $id);
$stmt->execute();
@@ -109,9 +109,6 @@ switch ($anfrageType) {
$row = $rows[0];
// safely get value, default 0 if missing
$olduniqueid = $oldjson['uniqueid'] ?? 0;
$uniqueid = $olduniqueid + 1;
$data = ["noteLinks" => '',
"noteRechts" => '',
@@ -120,10 +117,10 @@ switch ($anfrageType) {
"vorname" => $row['vorname'],
"programm" => $row['programm'],
"verein" => $row['verein'],
"start" => false,
"musik" => 'nan',
"uniqueid" => $uniqueid];
$jsonData = json_encode($data);
"start" => false
];
$arrayData = $data;
break;
case "start":
if (!array_key_exists("id", $oldjson) || intval($oldjson["id"]) !== $id || !array_key_exists("start", $oldjson)) {
@@ -133,21 +130,68 @@ switch ($anfrageType) {
$oldjson["start"] = (bool) $dataType;
$jsonData = json_encode($oldjson);
$arrayData = $oldjson;
break;
case "result":
// 1. Get IDs and filter out empty values
$noteLinksId = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['displayIdNoteL']);
$noteRechtsId = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['displayIdNoteR']);
$noteLinksId = db_get_variable($mysqli, $tableVar, ['displayIdNoteL']);
$noteRechtsId = db_get_variable($mysqli, $tableVar, ['displayIdNoteR']);
$stmt = $mysqli->prepare("UPDATE $tableNoten SET `is_public` = 1, `public_value` = `value` WHERE `person_id` = ? AND `jahr` = ? AND `geraet_id` = ? AND `run_number` = ?");
$programm_id = db_get_var($mysqli, "SELECT p.`id` FROM $tableTeilnehmende t INNER JOIN $tableProgramme p ON p.`programm` = t.`programm` WHERE t.`id` = ?", [$id]) ?? null;
$stmt->bind_param("ssss", $id, $jahr, $geraetId, $run);
$display_cache = require $baseDir . '/../scripts/cache/display-dependencies.php';
$stmt->execute();
$this_display_cache = $display_cache[$programm_id][$geraetId][$initNoteRun];
$stmt->close();
$sql_where_values = $this_display_cache['values'];
$sql_where_string = $this_display_cache['SQL'];
$intersect_noten = $this_display_cache['intersect_noten'];
$rankLiveArray = [];
if (!empty($sql_where_values) && $sql_where_string !== '') {
$notenDB = db_select($mysqli, $tableNoten, '`value`, `note_bezeichnung_id`, `geraet_id`, `run_number`', "`person_id` = ? AND `jahr` = ? AND ($sql_where_string)", array_merge([$id, $jahr], $sql_where_values));
$indexedNotenDB = [];
foreach ($notenDB as $sn) {
$indexedNotenDB[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = $sn;
}
}
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("INSERT INTO $tableNoten (`person_id`, `note_bezeichnung_id`, `jahr`, `geraet_id`, `run_number`, `is_public`, `public_value`) VALUES (?, ?, ?, ?, ?, 1, ?) ON DUPLICATE KEY UPDATE `public_value` = VALUES(`public_value`), `is_public` = 1");
foreach ($intersect_noten as $s_note) {
$n_id = $s_note['n_id'];
$g_id = $s_note['g_id'];
$run = $s_note['r'];
$value = $indexedNotenDB[$g_id][$n_id][$run]['value'] ?? $notenConfig['default_values'][$n_id] ?? 0;
$rankLiveArray[$g_id][$run][$n_id] = number_format($value, $notenConfig['nullstellen'][$n_id] ?? 2);
$stmt->bind_param("iiiiid", $id, $n_id, $jahr, $g_id, $run, $value);
$stmt->execute();
}
$mysqli->commit();
$stmt->close();
} catch (Exception $e) {
$mysqli->rollback();
echo json_encode(['success' => false, 'message' => 'DB Transaction failed Error: ' . $e->getMessage()]);
exit;
}
// Create an array of IDs that actually exist
$validIds = array_filter([$noteLinksId, $noteRechtsId]);
@@ -164,7 +208,7 @@ switch ($anfrageType) {
$stmt = $mysqli->prepare($sqlNoten);
// Combine standard params with our dynamic ID list
$params = array_merge([$id, $jahr, $geraetId, $run], $validIds);
$params = array_merge([$id, $jahr, $geraetId, $initNoteRun], $validIds);
$types = str_repeat('s', count($params));
$stmt->bind_param($types, ...$params);
$stmt->execute();
@@ -173,7 +217,7 @@ switch ($anfrageType) {
$stmt->close();
// 3. Fetch Config
$sqlConfig = "SELECT `id`, `default_value`, `nullstellen`, `prefix_display`
$sqlConfig = "SELECT `id`, `default_value`, `nullstellen`, `display_string`
FROM $tableNotenBezeichnungen WHERE `id` IN ($placeholders)";
$stmt = $mysqli->prepare($sqlConfig);
@@ -194,20 +238,34 @@ switch ($anfrageType) {
$conf = $notenConfig[$id];
$val = $noten[$id] ?? $conf['default_value'] ?? 0;
$prec = $conf['nullstellen'] ?? 2;
$pre = $conf['prefix_display'] ?? '';
return $pre . number_format((float)$val, (int)$prec, '.', '');
$display_schema = $conf['display_string'] ?? '${note}';
$formatted_note = number_format((float)$val, (int)$prec, '.', '');
$result = str_ireplace('${note}', $formatted_note, $display_schema);
if ($result === $display_schema) {
return $formatted_note;
}
return $result;
};
// 5. Assign to JSON
$oldjson["noteLinks"] = $formatNote($noteLinksId);
$oldjson["noteRechts"] = $formatNote($noteRechtsId);
$jsonData = json_encode($oldjson);
$arrayData = $oldjson;
break;
default:
http_response_code(400);
exit;
}
$jsonData = json_encode($arrayData);
// Write file
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode([
@@ -217,11 +275,21 @@ if (file_put_contents($filepath, $jsonData) === false) {
exit;
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for ' . $geraetName,
'data' => json_decode($jsonData, true),
'nameGeraet' => strtolower($geraetName)
]);
if ($anfrageType === "result") {
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for ' . $geraetName,
'data' => $arrayData,
'rankLive' => $rankLiveArray,
'nameGeraet' => strtolower($geraetName)
]);
} else {
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'JSON updated successfully for ' . $geraetName,
'data' => $arrayData,
'nameGeraet' => strtolower($geraetName)
]);
}
exit;
@@ -1,201 +0,0 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['user_id_kampfrichter']) || $_SESSION['user_id_kampfrichter'] < 1) {
http_response_code(403);
exit;
}
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$person_id = isset($_POST['personId']) ? intval($_POST['personId']) : 0;
$field_type_id = intval($_POST['fieldTypeId'] ?? 0);
$gereat_id = intval($_POST['gereatId'] ?? 0);
$jahr = isset($_POST['jahr']) ? intval($_POST['jahr']) : 0;
if (!isset($_POST['value'])) {
echo json_encode(['success' => false, 'message' => 'Kein Value angegeben']);
exit;
}
$value = floatval($_POST['value']);
if ($person_id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Personen-ID']);
exit;
}
if ($jahr < 1) {
echo json_encode(['success' => false, 'message' => 'Invalides Jahr']);
exit;
}
if ($gereat_id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Geraet-ID']);
exit;
}
$geratExistiert = db_get_var($mysqli, "SELECT 1 FROM $tableNotenBezeichnungen WHERE id = ? LIMIT 1", [$gereat_id]);
if (!$geratExistiert) {
echo json_encode(['success' => false, 'message' => 'Invalide Geraet-ID']);
exit;
}
if ($field_type_id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Notentyp-ID']);
exit;
}
$noteConfig = db_select($mysqli, $tableNotenBezeichnungen, "*", "id = ?", [$field_type_id]);
if (count($noteConfig) !== 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Notentyp-ID']);
exit;
}
$singleNoteConfig = $noteConfig[0];
if ($singleNoteConfig['max_value'] !== null && $value > $singleNoteConfig['max_value']) {
echo json_encode(['success' => false, 'message' => "Wert zu hoch (max " . $singleNoteConfig['max_value'].")"]);
exit;
}
if ($singleNoteConfig['min_value'] !== null && $value < $singleNoteConfig['min_value']){
echo json_encode(['success' => false, 'message' => "Wert zu niedrig (min " . $singleNoteConfig['min_value'].")"]);
exit;
}
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("siiii", $value, $person_id, $field_type_id, $gereat_id, $jahr);
$stmt->execute();
$stmt->close();
if ($singleNoteConfig['berechnung_json'] === null) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert"]);
exit;
}
require $baseDir . "/../scripts/string-calculator/string-calculator-functions.php";
$updateNoten = [];
$notenRechner = new NotenRechner();
try {
$abhaenigeRechnungen = json_decode($singleNoteConfig['berechnung_json']);
} catch (Exception $e) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, fehler bei der Berechnung der weiteren Werte"]);
exit;
}
// Alle Werte werden von der Datenbank geholt und werden, wenn nicht vorhanden, durch den Standartwert ersetzt.
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, berechnung, default_value, nullstellen");
$noten = db_select($mysqli, $tableNoten, "`value`, `note_bezeichnung_id`", "`person_id` = ? AND `geraet_id` = ? AND `jahr` = ?", [$person_id, $gereat_id, $jahr]);
$ascArrayDefaultValues = array_column($alleNoten, 'default_value', 'id');
$ascArrayRechnungen = array_column($alleNoten, 'berechnung', 'id');
$existierendeNotenIds = array_column($noten, 'note_bezeichnung_id') ?? [];
$alleNotenIds = array_column($alleNoten, 'id') ?? [];
$nichtExistierendeNotenIds = array_diff($alleNotenIds, $existierendeNotenIds) ?? [];
$noten = array_column($noten, 'value', 'note_bezeichnung_id');
foreach ($nichtExistierendeNotenIds as $neni) {
if (!isset($ascArrayDefaultValues[$neni])) { continue; }
$noten[$neni] = $ascArrayDefaultValues[$neni];
}
// We only want to save the IDs that were actually recalculated
$idsToSave = [];
foreach ($abhaenigeRechnungen as $sRechnung) {
$rechnung = $ascArrayRechnungen[$sRechnung] ?? null;
if ($rechnung === null) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, Fehler: Rechnung $sRechnung nicht gefunden"]);
exit;
}
$calcResult = $notenRechner->berechneString($rechnung, $noten);
if ($calcResult['success'] !== true) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, Rechenfehler: " . ($calcResult['value'] ?? '')]);
exit;
}
// Update the local array so the NEXT calculation in the loop can use this new value
$noten[$sRechnung] = $calcResult['value'];
// Track that this ID needs to be written to the database
$idsToSave[] = $sRechnung;
}
// Prepare the statement once
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
foreach ($idsToSave as $notenId) {
$currentValue = $noten[$notenId]; // Get the calculated value from our array
// i = integer, s = string (use 's' for decimals/floats to prevent rounding issues in PHP)
$stmt->bind_param("siiii", $currentValue, $person_id, $notenId, $gereat_id, $jahr);
$stmt->execute();
}
$stmt->close();
$mysqli->close();
// 1. Combine the ID the user sent with the IDs we recalculated
$changedIds = array_merge([$field_type_id], $idsToSave);
// 2. Filter the $noten array to only include these keys
// array_flip turns [10, 20] into [10 => 0, 20 => 1] so we can intersect by keys
$changedNoten = array_intersect_key($noten, array_flip($changedIds));
$indexedNullstellen = array_column($alleNoten, 'nullstellen', 'id');
foreach ($changedNoten as $key => $scN) {
$changedNoten[$key] = number_format($scN ,$indexedNullstellen[$key] ?? 2);
}
echo json_encode([
'success' => true,
'message' => "Wert aktualisiert, alle Berechnungen durchgeführt",
"noten" => $changedNoten
]);
@@ -1,11 +1,20 @@
jQuery(document).ready(function($) {
const csrf_token = window.CSDR_TOKEN;
const allowedRanglistenTypes = ['downloadRangliste', 'updateServerRangliste'];
$('.ranglisteExport').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
const fieldType = $button.data('field_type');
if (!allowedRanglistenTypes.includes(fieldType)) {
displayMsg(0, "Dieser Button besitzt eine fehlerhafte Konfiguration");
return;
}
// Visual feedback (optional but recommended)
$button.prop('disabled', true).text('Generating...');
$button.addClass('opacity50');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_rangliste.php';
@@ -13,81 +22,193 @@ jQuery(document).ready(function($) {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
prog: progId,
type: fieldType
})
})
.then(response => {
// CRITICAL: Check if the server actually returned a success code
if (!response.ok) {
throw new Error('Server returned an error (Status ' + response.status + ')');
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
// Return both the response and the blob/json promise
// to keep the chain clean, or handle them based on fieldType here.
if (fieldType === 'downloadRangliste') {
return res.blob().then(blob => ({ type: 'blob', data: blob }));
} else {
return res.json().then(json => ({ type: 'json', data: json }));
}
return response.blob();
})
.then(blob => {
if (fieldType !== 'upload_programm') {
const url = window.URL.createObjectURL(blob);
.then(result => {
if (result.type === 'blob') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = `Ergebnisse_${progId}.pdf`;
a.download = "Rangliste.pdf";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
a.remove();
window.URL.revokeObjectURL(url);
} else {
alert('PDF auf Webseite geladen!');
displayMsg(1, "Rangliste wurde erfolgreich heuntergeladen.");
} else if (result.type === 'json') {
if (result.data.success) {
const $container = $button.closest(".singleAbtDiv");
$container.find(".uploadRanglisteText").addClass("hidden");
$container.find(".updateRanglisteText").removeClass("hidden");
$container.find(".rangliseOnlineSpan").removeClass("hidden");
$container.find(".ranglisteDelete").removeClass("hidden");
displayMsg(1, "Die Rangliste wurde auf dem Server aktualisiert.");
} else {
displayMsg(0, "Fehler beim Aktualiseren der Rangliste.");
}
}
$button.removeClass('opacity50');
})
.catch(error => {
console.error('Export Error:', error);
alert('Fehler beim Exportieren der Rangliste.');
})
.finally(() => {
// Restore button state
$button.prop('disabled', false).text('Export PDF');
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
$button.removeClass('opacity50');
});
});
$('.protokollExport').on('click', function() {
console.log('ok');
const $input = $(this);
// Build the data to send
const data = new URLSearchParams();
data.append('abteilung', $input.data('abteilung'));
// Record start time
const start = performance.now();
$('.ranglisteDelete').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_protokoll.php';
// Visual feedback (optional but recommended)
$button.addClass('opacity50');
const url = '/intern/scripts/kampfrichter/ajax/ajax-delete_rangliste.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
abteilung: $input.data('abteilung')
csrf_token,
prog: progId
})
})
.then(res => res.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = "KTBB_Protokoll.pdf"; // optional
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json().then(json => ({ type: 'json', data: json }));
})
.then(result => {
if (result.data.success) {
const $container = $button.closest(".singleAbtDiv");
$container.find(".uploadRanglisteText").removeClass("hidden");
$container.find(".updateRanglisteText").addClass("hidden");
$container.find(".rangliseOnlineSpan").addClass("hidden");
$container.find(".ranglisteDelete").addClass("hidden");
displayMsg(1, "Die Rangliste wurde erfolgreich entfernt.");
} else {
displayMsg(0, "Fehler beim Löschen der Rangliste.");
}
$button.removeClass('opacity50');
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
$button.removeClass('opacity50');
});
});
$('.protokollExport').on('click', function() {
const $button = $(this);
const abteilungId = $button.data('abteilung-id');
const now = new Date();
const formattedDate = new Intl.DateTimeFormat('en-CA', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}).format(now).replace(/,/g, '');
const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_protokoll.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
abteilungId: abteilungId,
date: formattedDate
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.blob().then(blob => ({ type: 'blob', data: blob }));
})
.then(result => {
if (result.type === 'blob') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = "Protokoll.pdf";
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
displayMsg(1, "Protokoll wurde erfolgreich heuntergeladen.");
}
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
});
});
$('.protokollDeleteEntries').on('click', async function() {
const $button = $(this);
const abt = $button.data('abteilung-id');
if (!await displayConfirmImportant("Möchten Sie wirklich alle Protokolleinträge für diese Abteilung löschen? Diese Aktion kann nicht rückgängig gemacht werden.")) { return; }
const url = '/intern/scripts/kampfrichter/ajax/ajax-delete_protokoll_entries.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
abteilungId: abt
})
})
.then(res => {
if (!res.ok) throw new Error('Network response was not ok');
return res.json().then(json => ({ type: 'json', data: json }));
})
.then(result => {
if (result.data.success) {
displayMsg(1, "Die Einträge wurden erfolgreich entfernt.");
} else {
displayMsg(0, "Fehler beim Löschen der Einträge.");
}
})
.catch(err => {
displayMsg(0, "Error processing request:", err);
console.error("Error processing request:", err);
});
});
let activeEdit = null;
console.log('pre');
$(document).on('click', '.editableValue', function () {
console.log('ok');
const input = $(this);
// Already editing
@@ -189,4 +310,248 @@ jQuery(document).ready(function($) {
activeEdit = null;
}
let rangNotenArray = JSON.parse(window.RANG_NOTEN_ARRAY) ?? [];
const rangNotenId = window.RANG_NOTE_ID ?? 0;
const rangSort = window.RANG_SORT ?? '';
const selecteddiscipline = window.FREIGABE;
ws.addEventListener("message", event => { // Use 'event' as it's more standard than 'blob'
let msgOBJ;
try {
msgOBJ = JSON.parse(event.data);
} catch (error) {
return;
}
// Ensure it's an UPDATE type (matches your sendToGroup logic)
if (msgOBJ?.type === "UPDATE") {
const data = msgOBJ.payload;
// Check access rights
if (selecteddiscipline === 'A') {
const noten = data.noten;
if (noten[0][rangNotenId][1] !== undefined && rangNotenArray[data.programmId][data.personId] !== undefined) {
rangNotenArray[data.programmId][data.personId] = noten[0][rangNotenId][1];
applyRanks(rankProgramm(rangNotenArray, data.programmId, rangSort), true);
}
}
}
});
function rankProgramm(dataObj, programmId, order = 'DESC') {
if (!dataObj[programmId]) {
console.error(`ID ${programmId} not found in data.`);
return [];
}
const values = dataObj[programmId];
// 1. Transform and Sort
const sorted = Object.entries(values)
.map(([id, val]) => ({ id, val }))
.sort((a, b) => {
// 1. Handle null values first (Always push them to the bottom)
if (a.val === null && b.val === null) return 0;
if (a.val === null) return 1; // Move 'a' to a higher index (down)
if (b.val === null) return -1; // Move 'b' to a higher index (down)
// 2. Normal sorting logic for valid numbers
return order.toUpperCase() === 'ASC'
? a.val - b.val
: b.val - a.val;
});
let lastVal = null;
let lastRank = 0;
let sortedMap = [];
sortedMap[programmId] = sorted.map((item, index) => {
// If current value is different from the last, update rank to current position
if (item.val !== lastVal) {
lastRank = index + 1;
}
lastVal = item.val;
return {
rang: lastRank,
...item
};
});
return sortedMap;
}
function rankAll(dataObj, order = 'DESC') {
return Object.fromEntries(
Object.entries(dataObj).map(([groupKey, values]) => {
const sorted = Object.entries(values)
.map(([id, val]) => ({ id, val }))
.sort((a, b) => {
// 1. Handle null values first (Always push them to the bottom)
if (a.val === null && b.val === null) return 0;
if (a.val === null) return 1; // Move 'a' to a higher index (down)
if (b.val === null) return -1; // Move 'b' to a higher index (down)
// 2. Normal sorting logic for valid numbers
return order.toUpperCase() === 'ASC'
? a.val - b.val
: b.val - a.val;
});
let lastVal = null;
let lastRank = 0;
const ranked = sorted.map((item, index) => {
if (item.val !== lastVal) {
lastRank = index + 1;
}
lastVal = item.val;
return {
rang: lastRank,
...item
};
});
return [groupKey, ranked];
})
);
}
function applyRanks(array, sort = false) {
for (const [key, entries] of Object.entries(array)) {
const $tbody = $(`.customDisplayEditorTable tbody[data-programm-id="${key}"]`);
if ($tbody.length === 0) continue;
for (const [skey, entry] of Object.entries(entries)) {
const $row = $tbody.find(`tr[data-person-id="${entry.id}"]:not(.notpaid)`);
if ($row.length === 0) continue;
$row.attr('data-sort-rank', entry.rang);
const $cell = $row.find('.rangField');
const points = parseFloat(rangNotenArray[key][entry.id]);
const text = !isNaN(points) ? entry.rang : '';
$cell.text(text);
}
if (sort) {
const rows = $tbody.find('tr').get();
rows.sort((a, b) => {
const rankA = parseFloat($(a).attr('data-sort-rank')) || 999;
const rankB = parseFloat($(b).attr('data-sort-rank')) || 999;
return rankA - rankB;
});
$.each(rows, (index, row) => {
$tbody.append(row);
});
}
}
}
applyRanks(rankAll(rangNotenArray, rangSort), true);
window.addEventListener('valueChanged', (e) => {
// Use e.detail to get your object
const { noten, programmId, personId } = e.detail;
if (noten[0][rangNotenId][1] !== undefined && rangNotenArray[programmId][personId] !== undefined) {
rangNotenArray[programmId][personId] = noten[0][rangNotenId][1];
applyRanks(rankProgramm(rangNotenArray, programmId, rangSort), true);
}
});
const $parent = $('.allAbtContainer');
const $children = $parent.children('.shiftedGeraetTable');
$children.sort((a, b) => {
const idA = parseFloat($(a).data('geraet-index'));
const idB = parseFloat($(b).data('geraet-index'));
return idA - idB;
});
$parent.append($children);
const wsMessage = window.WS_MESSAGE || '';
if (wsMessage !== '') {
ws.addEventListener('open', () => {
setTimeout(() => {
ws.send(wsMessage);
}, 300);
});
}
$('.deleteNotenAbt').on('click', async function () {
if (!(await displayConfirmImportant('Alle Noten dieses Programmes wirklich löschen? Diese Aktion ist unumkehrbar!'))) return;
const $btn = $(this);
const programmId = parseInt($btn.data('programm-id') || 0);
$.ajax({
url: '/intern/scripts/kampfrichter/ajax/ajax-delete-noten-turnerinnen.php',
type: 'DELETE',
data: {
csrf_token,
programmId
},
success: function (response) {
if (response.success) {
const $tbody = $btn.closest('.singleAbtDiv').find('tbody');
if ($tbody.length !== 1 || $tbody.data('programm-id') !== programmId) {
location.reload();
return;
}
const $allValues = $tbody.find('.changebleValue:not(.emtyPlaceholder)');
$allValues.each((ind, el) => {
$(el).text('-').addClass('emtyPlaceholder');
updateDependentVisibility($(el));
});
const $allRanks = $tbody.find('.rangField');
$allRanks.each((ind, el) => {
$(el).text('');
});
const $allRows = $tbody.find('tr');
$allRows.each((ind, el) => {
$(el).attr('data-sort-rank', "0");
rangNotenArray[programmId][$(el).attr('data-person-id')] = null;
});
displayMsg(1, 'Noten gelöscht');
} else {
displayMsg(0, response.message || 'Fehler beim Löschen');
}
},
error: function () {
displayMsg(0, 'Fehler beim Löschen');
}
})
});
});
@@ -5,6 +5,50 @@ document.addEventListener('keydown', function (e) {
if (isOptionOrAlt && e.shiftKey && e.key.toLowerCase() === 'f') {
toggleFullscreen();
}
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
}
});
const menuburger = document.querySelector('.kampfrichterBurgerMenuDiv');
const menudiv = document.querySelector('.internMenuDiv');
const closeMenuMobile = document.querySelector('.closeInternMenuMobileDiv');
const storageKey = "kampfrichterInternMenuOpen";
const isOpen = localStorage.getItem(storageKey) === "true";
if (isOpen) {
menuburger.classList.add("open");
menudiv.classList.add("open");
}
menuburger.addEventListener("click", function () {
menuburger.classList.add("menuTransition");
menudiv.classList.add("menuTransition");
menuburger.classList.toggle("open");
menudiv.classList.toggle("open");
const isOpenEl =
menudiv.classList.contains("open") &&
menuburger.classList.contains("open");
localStorage.setItem(storageKey, isOpenEl);
});
closeMenuMobile.addEventListener("click", function () {
const isOpenEl =
menudiv.classList.contains("open") &&
menuburger.classList.contains("open");
if (isOpenEl) {
menuburger.classList.remove("open");
menudiv.classList.remove("open");
localStorage.setItem(storageKey, false);
}
});
function toggleFullscreen() {
@@ -22,61 +66,33 @@ function toggleFullscreen() {
let messagePosArray = [];
const csrf_token = window.CSDR_TOKEN;
function displayMsg(type, msg) {
const colors = ["#900000ff", "#00b200ff"];
if (type !== 0 && type !== 1) return;
// idx is ALWAYS a valid non-negative index now
const $div = $('<div class="msgBox"></div>')
.css({'border-color': colors[type]})
.text(msg);
$('.msgDiv').append($div);
// trigger entry animation
setTimeout(() => {
$div.addClass('show');
}, 200);
setTimeout(() => {
// First: set the transition properties
$div.css({
'transition': 'all 0.3s ease'
});
// Next frame: apply the transform so the transition animates
requestAnimationFrame(() => {
$div.removeClass('show');
$div.css({
'transform': 'scaleY(0) translateX(calc(100% + 40px))'
});
});
}, 3000);
// auto-remove
setTimeout(() => {
$div.remove();
}, 3500);
}
const text = document.getElementById('wsInfo');
const rect = document.getElementById('wsInfoRectangle');
let ws;
let firstConnect = true;
let wsOpen = false;
const RETRY_DELAY = 2000; // ms
const WSaccesstype = "kampfrichter";
const WSaccess = window.FREIGABE;
const WSaccessPermission = "W";
const urlAjaxNewWSToken = '/intern/scripts/ajax-create-ws-token.php';
async function fetchNewWSToken(freigabe) {
async function fetchNewWSToken(accesstype, access, accessPermission) {
try {
const response = await fetch(urlAjaxNewWSToken, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ access: freigabe, csrf_token })
body: new URLSearchParams({
csrf_token,
accesstype,
access,
accessPermission
})
});
if (response.status === 403) {
@@ -102,7 +118,7 @@ async function startWebSocket() {
if (firstConnect) {
token = window.WS_ACCESS_TOKEN;
} else {
token = await fetchNewWSToken('kampfrichter');
token = await fetchNewWSToken(WSaccesstype, WSaccess, WSaccessPermission);
}
if (!token) {
@@ -125,6 +141,8 @@ async function startWebSocket() {
displayMsg(1, "Live Syncronisation wieder verfügbar");
}
firstConnect = true;
wsOpen = true;
rect.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#28a745"></circle><path d="M7 12l3 3 7-7" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
@@ -138,8 +156,13 @@ async function startWebSocket() {
};
ws.onclose = (event) => {
displayMsg(0, "Live Syncronisation verloren");
if (firstConnect) {
displayMsg(0, "Live Syncronisation verloren");
console.log(event);
}
firstConnect = false;
wsOpen = false;
rect.innerHTML =
'<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#dc3545"/><path d="M8 8l8 8M16 8l-8 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
@@ -167,6 +190,8 @@ function updateRunButtons(targetCount, personId, $container) {
const currentCount = $container.find('.submit-display-result').length;
$container.find('.submit-display-result').removeClass('hidden');
if (targetCount > currentCount) {
for (let i = currentCount + 1; i <= targetCount; i++) {
const buttonHtml = `
@@ -214,6 +239,11 @@ $.fn.updateCurrentEdit = function() {
name: 'next_subabt_submit',
value: '>'
}))
.append($('<input>', {
type: 'hidden',
name: 'csrf_token',
value: csrf_token
}))
.appendTo('body')
.submit();
}
@@ -256,6 +286,9 @@ $.fn.updateCurrentEdit = function() {
const $editAllDiv = $('.div_edit_values_all_gereate');
const noten = response.noten;
const personId = response.id;
const programmId = response.programm_id;
$editAllDiv.find(`.submit-display-turnerin`).closest('.all_vaules_div').addClass('hidden');
// 1. Loop directly through the 'noten' object
for (const [geraetId, disciplineData] of Object.entries(noten)) {
@@ -266,6 +299,8 @@ $.fn.updateCurrentEdit = function() {
if ($disciplineWrapper.length === 0) continue;
$disciplineWrapper.removeClass('hidden');
// --- UPDATE GENERAL BUTTONS FOR THIS GERAET ---
$disciplineWrapper.find(".submit-display-turnerin, .submit-display-start").attr({
'data-person-id': personId,
@@ -280,17 +315,18 @@ $.fn.updateCurrentEdit = function() {
// 2. Identify master containers for this specific discipline
const $masterContainer = $disciplineWrapper.find('.singleNotentable').first();
const $displayresultDiv = $disciplineWrapper.find('.div-submit-display-result');
$masterContainer.find('.note-container').addClass('hidden');
// 3. CLEANUP: Remove previously generated runs and buttons
$disciplineWrapper.find('.singleNotentable').not(':first').remove();
$displayresultDiv.find('.submit-display-result').not(':first').remove();
const $originalResultBtn = $displayresultDiv.find('.submit-display-result').first();
$displayresultDiv.find('.submit-display-result').addClass('hidden');
const runKeys = Object.keys(disciplineData).sort((a, b) => a - b);
const totalRuns = runKeys.length;
console.log(totalRuns);
// 4. Process each Run in the data
runKeys.forEach(runNum => {
const runInt = parseInt(runNum);
@@ -310,6 +346,7 @@ $.fn.updateCurrentEdit = function() {
// 5. Update all Tables and Inputs inside this Run Container
for (const [noteId, value] of Object.entries(disciplineData[runNum])) {
const $table = $currentRunContainer.find(`.note-container[data-note-id="${noteId}"]`);
$table.removeClass('hidden');
// Update Header to show Run Number
if (runInt > 1) {
@@ -324,7 +361,8 @@ $.fn.updateCurrentEdit = function() {
$input.attr({
'data-run': runNum,
'data-person-id': personId,
'data-geraet-id': geraetId
'data-geraet-id': geraetId,
'data-programm-id': programmId
}).val(value ?? '');
}
@@ -362,6 +400,10 @@ $.fn.updateCurrentEdit = function() {
});
};
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
jQuery(document).ready(function($) {
@@ -370,16 +412,17 @@ jQuery(document).ready(function($) {
});
const $ajaxInputDiv = $('.div_edit_values_all_gereate');
$ajaxInputDiv.on('change', '.ajax-input', function(e) {
const start = performance.now();
const $input = $(this);
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter.php`;
personId = $input.data('person-id');
fieldTypeId = $input.data('field-type-id');
gereatId = $input.data('geraet-id');
personId = $input.attr('data-person-id');
fieldTypeId = $input.attr('data-field-type-id');
programmId = $input.attr('data-programm-id');
gereatId = $input.attr('data-geraet-id');
runNum = $input.attr('data-run') || 1;
jahr = window.AKTUELLES_JAHR;
value = $input.val();
@@ -399,8 +442,6 @@ jQuery(document).ready(function($) {
})
.then(res => res.json())
.then(response => {
const end = performance.now();
console.log(`Total AJAX time: ${(end - start).toFixed(3)} ms`);
if (response.success) {
@@ -415,32 +456,63 @@ jQuery(document).ready(function($) {
const noten = response.noten;
for (const [keyN, noteGroup] of Object.entries(noten)) {
for (const [keyG, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
for (const [run, value] of Object.entries(runGroup)) {
const selectorBase = `[data-field-type-id="${key}"][data-geraet-id="${keyN}"][data-person-id="${personId}"][data-run="${run}"]`;
// Handle Inputs (excluding current one)
$(`input.changebleValue${selectorBase}`)
.not(this)
.val(value ?? '');
// Handle Display elements (Spans/Divs)
$(`.changebleValue:not(input)${selectorBase}`)
.text(value ?? '');
const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${personId}"][data-run="${run}"]`);
$elements.each((ind, el) => {
const $el = $(el);
if ($el.is('input')) {
$el.val(value ?? '');
} else {
const isNullable = ($el.attr('data-noten-nullable') ?? 'false') === 'true';
const floatValZero = parseFloat(value.replace(",", ".").replace("'", "")) === 0.0;
let hasValue = false;
if (isNullable && floatValZero || value === '') {
$el.text('-').addClass('emtyPlaceholder');
} else {
$el.text(value).removeClass('emtyPlaceholder');
hasValue = true;
}
updateDependentVisibility($el, hasValue);
}
});
}
}
}
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: window.FREIGABE,
gereatId: gereatId,
personId: personId,
jahr: jahr,
noten: noten
}
}));
if (selecteddiscipline === 'A') {
const event = new CustomEvent('valueChanged', {
detail: {
noten: noten,
programmId: programmId,
personId: personId
}
});
window.dispatchEvent(event);
}
if (wsOpen) {
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: window.FREIGABE,
gereatId: gereatId,
personId: personId,
programmId: programmId,
jahr: jahr,
noten: noten
}
}));
}
} else {
// Flash red on error
@@ -455,59 +527,9 @@ jQuery(document).ready(function($) {
});
});
$('.inputnamekr').on('change', function() {
const $input = $(this);
const url = '/intern/scripts/kampfrichter/ajax/ajax-update_name_kampfrichter_protokoll.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
aufgabe: $input.data('id'),
abteilung: $input.data('abt'),
geraet: $input.data('user'),
name: $input.val()
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
console.log(response.message);
$input.css({
'background-color': '#a4bf4a',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
} else {
console.error(response.message);
$input.css({
'background-color': '#f8d7da',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
}
})
.catch(err => {
$input.css('background-color', '#f8d7da');
console.error('AJAX fetch error:', err);
displayMsg(0, 'AJAX fetch error:' + err);
});
});
$('.submit-display-turnerin').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
@@ -518,7 +540,7 @@ jQuery(document).ready(function($) {
body: new URLSearchParams({
csrf_token,
personId: $input.attr('data-person-id'),
geraetId: $input.attr('data-geraet-id'),
geraetId,
jahr: window.AKTUELLES_JAHR,
type: "neu"
})
@@ -527,14 +549,19 @@ jQuery(document).ready(function($) {
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
displayMsg(1, 'Neue Turnerin wird angezigt');
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
geraetId,
data: response.data
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
displayMsg(1, 'Neue Person wird angezigt');
$input.css('opacity', 0.5);
} else {
@@ -550,6 +577,8 @@ jQuery(document).ready(function($) {
$('.submit-display-start').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
const personId = $input.attr('data-person-id')
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
const dataType = $input.attr('data-type');
@@ -559,8 +588,8 @@ jQuery(document).ready(function($) {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
geraetId: $input.attr('data-geraet-id'),
personId: $input.attr('data-person-id'),
geraetId,
personId,
dataType: dataType,
type: "start"
})
@@ -569,13 +598,18 @@ jQuery(document).ready(function($) {
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
geraetId,
data: response.data
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
if (dataType == 1) {
displayMsg(1, 'Start freigegeben');
@@ -598,6 +632,7 @@ jQuery(document).ready(function($) {
$('.submit-musik-start').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_start_musik.php`;
@@ -607,17 +642,22 @@ jQuery(document).ready(function($) {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
id: $input.attr('data-id'),
discipline: $input.data('geraet')
personId: $input.attr('data-id'),
geraetId
})
})
.then(res => res.json())
.then(response => {
const end = performance.now();
ws.send(JSON.stringify({
type: "AUDIO",
payload: {}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {
audioDiscipline: geraetId
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
if (response.success) {
displayMsg(1, 'Musik wird abgespielt werden');
@@ -634,6 +674,7 @@ jQuery(document).ready(function($) {
$('.submit-musik-stopp').on('click', function() {
const $input = $(this);
const geraetId = $input.attr('data-geraet-id');
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_stopp_musik.php`;
@@ -642,16 +683,23 @@ jQuery(document).ready(function($) {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token
csrf_token,
geraetId
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {
audioDiscipline: geraetId
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
displayMsg(1, 'Musik wird gestoppt werden');
} else {
alert('Error: ' + response.message);
@@ -666,6 +714,8 @@ jQuery(document).ready(function($) {
$('.div-submit-display-result').on('click', '.submit-display-result', function() {
$input = $(this);
const geraetId = $input.attr('data-geraet-id');
const personId = $input.attr('data-person-id');
// Build the URL with GET parameters safely
const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php';
@@ -675,8 +725,8 @@ jQuery(document).ready(function($) {
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token,
personId: $input.attr('data-person-id'),
geraetId: $input.attr('data-geraet-id'),
personId,
geraetId,
run: $input.attr("data-run"),
jahr: window.AKTUELLES_JAHR,
type: "result"
@@ -686,13 +736,27 @@ jQuery(document).ready(function($) {
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
if (wsOpen) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraetId,
geraet: response.nameGeraet,
data: response.data
}
}));
ws.send(JSON.stringify({
type: "UPDATE_RANKLIVE_SCORE",
payload: {
geraetId,
personId,
jahr: window.AKTUELLES_JAHR,
rankLive: response.rankLive
}
}));
} else {
displayMsg(2, 'Keine live Syncrosation, Verzögerungen');
}
$input.css('opacity', 0.5);
displayMsg(1, 'Resultat wird angezeigt');
} else {
@@ -707,6 +771,58 @@ jQuery(document).ready(function($) {
});
});
function updateDependentVisibility($changedEl, hasValue = false) {
const uid = $changedEl.attr('data-uid');
if (uid === undefined || uid === null) return;
const $row = $changedEl.closest('tr');
if ($row.length !== 1) return;
const isVisible = $changedEl.hasClass('emtyPlaceholder');
const doesExist = $changedEl.hasClass('nonExistentEl');
const $linkedEls = $row.find(`.singleValueSpan[data-linked-el-uid="${uid}"]`);
$linkedEls.each((ind, el) => {
const $linkedEl = $(el);
if (!hasValue) {
hasValue = $linkedEl.hasClass('changebleValue') && $linkedEl.text().trim() !== '-';
}
$linkedEl.toggleClass('emtyPlaceholder', (isVisible && !hasValue)).toggleClass('nonExistentEl', doesExist);
const linkedElUid = $linkedEl.data('linked-el-uid');
const $subLinkedEls = $row.find(`.singleValueSpan[data-linked-el-uid="${linkedElUid}"]`);
if ($subLinkedEls.length > 0) updateDependentVisibility($linkedEl);
});
}
function initConditionalVisibility() {
const $table = $('table.customDisplayEditorTable');
const $rows = $table.find('tr');
$rows.each((ind, el) => {
const $row = $(el);
$row.find('.singleValueSpan[data-linked-el-uid]').each(function() {
const linkedUid = $(this).data('linked-el-uid');
const $linkedEl = $row.find(`.singleValueSpan[data-uid="${linkedUid}"]`);
if ($linkedEl.length === 1) {
updateDependentVisibility($linkedEl);
}
});
})
}
initConditionalVisibility();
initConditionalVisibility();
const selecteddiscipline = window.FREIGABE;
@@ -724,39 +840,53 @@ ws.addEventListener("message", event => { // Use 'event' as it's more standard t
const data = msgOBJ.payload;
// Check access rights
if (data.discipline === selecteddiscipline.toLowerCase() || selecteddiscipline.toLowerCase() === 'admin') {
if (data.discipline === selecteddiscipline.toLowerCase() || selecteddiscipline === 'A') {
const noten = data.noten;
for (const [keyG, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
for (const [run, value] of Object.entries(runGroup)) {
// OPTIONAL: Skip if the current user is currently focused on this specific input
if (document.activeElement.dataset.fieldTypeId === key &&
document.activeElement.dataset.geraetId === keyG &&
document.activeElement.dataset.run == run) continue;
// Select all matching elements (inputs and spans)
const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${data.personId}"][data-run="${run}"]`);
$elements.each(function() {
const $el = $(this);
$elements.each((ind, el) => {
const $el = $(el);
// Update value or text
if ($el.is('input')) {
$el.val(value ?? '');
} else {
$el.text(value ?? '');
}
// Visual feedback: Flash color
const nativeEl = $el[0];
nativeEl.style.transition = 'color 0.3s';
nativeEl.style.color = '#008e85';
setTimeout(() => {
nativeEl.style.color = '';
}, 1000);
// Visual feedback: Flash color
const nativeEl = $el[0];
nativeEl.style.transition = 'color 0.3s';
nativeEl.style.color = '#008e85';
setTimeout(() => {
nativeEl.style.color = '';
}, 1000);
} else {
const isNullable = ($el.attr('data-noten-nullable') ?? 'false') === 'true';
const floatValZero = parseFloat(value.replace(",", ".").replace("'", "")) === 0.0;
let hasValue = false;
if (isNullable && floatValZero || value === '') {
$el.text('-').addClass('emtyPlaceholder');
} else {
$el.text(value).removeClass('emtyPlaceholder');
hasValue = true;
}
updateDependentVisibility($el, hasValue);
$el.removeClass('updated');
$el.addClass('updated');
setTimeout(() => {
$el.removeClass('updated');
}, 2000);
}
});
}
}
@@ -1,34 +0,0 @@
function allNameKrValid() {
var isValid = true;
$('.ajax-input-namekr').each(function () {
if ($(this).val() === '---') {
isValid = false;
return false; // break loop
}
});
return isValid;
}
function updateSelectKrDiv() {
const krDiv = $('.noKampfrichterDiv');
if (krDiv.length !== 1) {
return;
}
if (allNameKrValid()) {
krDiv.css('display', 'none');
} else {
krDiv.css('display', 'flex');
}
}
// initial check
updateSelectKrDiv();
// react to changes
$('.ajax-input-namekr').on('change', function () {
updateSelectKrDiv();
});
@@ -4,6 +4,7 @@ use Dotenv\Dotenv;
header('Content-Type: application/json');
date_default_timezone_set('Europe/Zurich');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
@@ -32,6 +33,26 @@ if ($dbconnection['success'] !== true){
exit;
}
$link_expires_at = $_POST['linkExpiresAt'] ?? '';
if ($link_expires_at === '' || strtotime($link_expires_at) === false) {
echo json_encode(['success' => false, 'message' => 'Ungültiges Ablaufdatum']);
http_response_code(400);
exit;
}
if (strtotime($link_expires_at) > strtotime('+8 days')) {
echo json_encode(['success' => false, 'message' => 'Datum liegt zu weit in der Zukunft']);
http_response_code(400);
exit;
}
if (strtotime($link_expires_at) < time()) {
echo json_encode(['success' => false, 'message' => 'Datum liegt in der Vergangenheit']);
http_response_code(400);
exit;
}
$editor_id = $_SESSION['user_id_wk_leitung'];
$plain = trim($_POST['password'] ?? null);
@@ -136,15 +157,12 @@ if (!$updated) {
$new_id = $mysqli->insert_id;
// Delete old OTL links for this user (recommended)
db_delete($mysqli, $tableOTL, ['user_id' => $new_id]);
// Insert the row — url + timestamp are auto-generated by MySQL
$typeOp = "create_profile";
$stmt = $mysqli->prepare("INSERT INTO {$tableOTL} (user_id, `type`) VALUES (?, ?)");
$stmt->bind_param("is", $new_id, $typeOp);
$stmt = $mysqli->prepare("INSERT INTO $tableOTL (user_id, `type`, `expires_at`) VALUES (?, ?, ?)");
$stmt->bind_param("iss", $new_id, $typeOp, $link_expires_at);
if (!$stmt->execute()) {
@@ -164,4 +182,4 @@ if (!$url) {
exit;
}
echo json_encode(['success' => true, 'url' => $url]);
echo json_encode(['success' => true, 'url' => $url, 'expiresAt' => $link_expires_at]);
@@ -16,7 +16,7 @@ verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
date_default_timezone_set('Europe/Zurich');
$type = 'wkl';
@@ -45,10 +45,12 @@ if ($id <= 0) {
// Delete old OTL links for this user (recommended)
db_delete($mysqli, $tableOTL, ['user_id' => $id]);
// Insert the row — url + timestamp are auto-generated by MySQL
$timestamp_one_day = strtotime('+1 days');
$stmt = $mysqli->prepare("INSERT INTO {$tableOTL} (user_id, `type`) VALUES (?, ?)");
$stmt->bind_param("is", $id, $typeOp);
$link_expires_at = date("Y-m-d H:i:s", $timestamp_one_day);
$stmt = $mysqli->prepare("INSERT INTO $tableOTL (user_id, `type`, `expires_at`) VALUES (?, ?, ?)");
$stmt->bind_param("iss", $id, $typeOp, $link_expires_at);
if (!$stmt->execute()) {
@@ -60,7 +62,6 @@ $row_id = $stmt->insert_id;
$stmt->close();
// Now fetch the auto-generated URL
$url = db_get_var($mysqli, "SELECT url FROM $tableOTL WHERE id = ? LIMIT 1", [$row_id]);
if (!$url) {
@@ -68,4 +69,4 @@ if (!$url) {
exit;
}
echo json_encode(['success' => true, 'url' => $url]);
echo json_encode(['success' => true, 'url' => $url, 'expiresAt' => $link_expires_at]);
@@ -33,6 +33,7 @@ if ($id <= 0) {
}
db_delete($mysqli, $tableInternUsers, ['id' => $id]);
db_delete($mysqli, $tableOTL, ['user_id' => $id]);
echo json_encode(['success' => true, 'message' => "Benutzer $id erfolgreich gelöscht.", 'id' => $id]);
exit;
@@ -1,4 +1,5 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
@@ -10,9 +11,13 @@ ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require_once $baseDir . '/../scripts/delete_request_type_functions.php';
$id = intval($_POST['field_id'] ?? 0);
parse_input_to_delete();
verify_delete_csrf($_DELETE);
$id = (int) ($_DELETE['id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'No valid ID']);
@@ -31,6 +36,15 @@ if ($dbconnection['success'] !== true){
exit;
}
$entrys = db_select($mysqli, $tableVereine, "1", '`id` = ?', [$id]);
if (count($entrys) < 1) {
echo json_encode(['success' => true, 'message' => 'Verein bereits gelöscht']);
http_response_code(410);
exit;
}
db_delete($mysqli, $tableVereine, ['id' => $id]);
echo json_encode(['success' => true, 'message' => 'Verein gelöscht']);
echo json_encode(['success' => true, 'message' => 'Verein gelöscht']);
http_response_code(200);
@@ -23,6 +23,7 @@ if ($data['success'] === false){
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$value = isset($_POST['value']) ? preg_replace('/[^a-zA-Z0-9\s\-"]/u', '', $_POST['value']) : '';
@@ -65,10 +66,15 @@ while ($entry = $programme->fetch_assoc()) {
'id' => $entry['id'],
'programm' => $entry['programm'],
'aktiv' => intval($entry['aktiv']),
'preis' => floatval($entry['preis'])
'preis' => floatval($entry['preis']),
'orderIndex' => intval($entry['order_index'])
];
}
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
// Return JSON
echo json_encode([
'success' => true,
@@ -1,10 +1,6 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -15,7 +11,11 @@ ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require_once $baseDir . '/../scripts/delete_request_type_functions.php';
parse_input_to_delete();
verify_delete_csrf($_DELETE);
$type = 'wkl';
@@ -27,57 +27,34 @@ if ($data['success'] === false){
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$id = (int) ($_DELETE['id'] ?? 0);
if ($id <= 0) {
if ($id < 1) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
http_response_code(400);
exit;
}
$entrys = db_select($mysqli, $tableProgramme, "programm", '`id` = ?', [$id]);
// ---------- Step 2: Get values from DB ----------
$stmt = $mysqli->prepare("DELETE FROM `$tableProgramme` WHERE id = ?");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
if (count($entrys) !== 1) {
echo json_encode(['success' => true, 'message' => 'Alters-/ Leistungsklasse bereits gelöscht']);
http_response_code(410);
exit;
}
$stmt->bind_param("i", $id);
$bezeichnung = $entrys[0]['programm'];
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
db_delete($mysqli, $tableProgramme, ['id' => $id]);
// Fetch all rows
$query2 = "SELECT * FROM `$tableProgramme` ORDER BY programm ASC";
$programme = $mysqli->query($query2);
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
if (!$programme) {
echo json_encode(['success' => false, 'message' => 'Select failed']);
exit;
}
generateIntersectCache($mysqli, $baseDir);
$output = [];
while ($entry = $programme->fetch_assoc()) {
$output[] = [
'id' => $entry['id'],
'programm' => $entry['programm'],
'aktiv' => intval($entry['aktiv']),
'preis' => floatval($entry['preis'])
];
}
// Return JSON
echo json_encode([
'success' => true,
'output' => $output,
'message' => $id.' gelöscht'
]);
echo json_encode(['success' => true, 'message' => $bezeichnung.' gelöscht']);
http_response_code(200);
exit;
@@ -2,6 +2,8 @@
use Dotenv\Dotenv;
ini_set('display_errors', 1);
header('Content-Type: application/json');
if (!isset($baseDir)) {
@@ -28,25 +30,73 @@ if ($dbconnection['success'] !== true){
exit;
}
if (isset($_POST['field_id'])){
$id = intval($_POST['field_id']);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
$allowed_update_types = ['all', 'no_password', 'new_user'];
$update_type = $_POST['updateType'] ?? '';
if (!in_array($update_type, $allowed_update_types)) {
echo json_encode(['success' => false, 'message' => 'Unbekannte Aktion']);
http_response_code(400);
exit;
}
$editor_id = $_SESSION['user_id_wk_leitung'];
$plain = trim($_POST['password'] ?? '');
$editor_id = (int) $_SESSION['user_id_wk_leitung'];
if (!$plain) {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
if ($update_type === 'new_user') {
$stmt = $mysqli->prepare(
"INSERT INTO {$tableInternUsers}
(created_at, updated_at, edited_by, login_active)
VALUES (?, ?, ?, ?)"
);
$loginActive = 1;
$created_at = date('Y-m-d H:i:s');
$updated_at = $created_at;
$stmt->bind_param(
"ssis",
$created_at,
$updated_at,
$editor_id,
$loginActive
);
$updated = $stmt->execute();
$new_id = $mysqli->insert_id;
echo json_encode(['success' => true, 'message' => 'Neuer Benutzer wurde erfolgreich erstellt', 'id' => $new_id]);
http_response_code(200);
exit;
}
$id = (int) ($_POST['personId'] ?? 0);
if ($id < 1 && $update_type !== 'new_user') {
echo json_encode(['success' => false, 'message' => 'Die angegebene ID ist nicht valide']);
http_response_code(400);
exit;
}
$plain_password = trim($_POST['password'] ?? '');
if ($plain_password === '' && $update_type !== 'no_password') {
echo json_encode(['success' => false, 'message' => 'Kein Passwort angegeben']);
http_response_code(422);
exit;
}
if ($update_type !== 'no_password' && strlen($plain_password) < 6) {
echo json_encode(['success' => false, 'message' => 'Das Passwort muss mindestens 6 Zeichen enthalten']);
http_response_code(400);
exit;
}
$username = htmlspecialchars(trim($_POST['username'] ?? ''));
if (!$username) {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
if ($username === '') {
echo json_encode(['success' => false, 'message' => 'Keinen Benutzernamen angegeben']);
http_response_code(422);
exit;
}
@@ -75,45 +125,52 @@ $array = [
// Store as proper JSON string
$freigabe_store = json_encode($array);
// Hash for login
$hash = password_hash($plain, PASSWORD_ARGON2ID);
if ($update_type !== 'no_password') {
$hash = password_hash($plain_password, PASSWORD_ARGON2ID);
require $baseDir . '/../composer/vendor/autoload.php';
require $baseDir . '/../composer/vendor/autoload.php';
$envFile = realpath($baseDir . '/../config/.env.pw-encryption-key');
$envFile = realpath($baseDir . '/../config/.env.pw-encryption-key');
if ($envFile === false) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Environment file not found"
]);
exit;
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.pw-encryption-key');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
$iv_length = openssl_cipher_iv_length('aes-256-cbc');
$iv = random_bytes($iv_length);
$encrypted = openssl_encrypt($plain_password, 'aes-256-cbc', $_ENV['PW_ENCRYPTION_KEY'], 0, $iv);
$cipher_store = base64_encode($iv . $encrypted);
}
try {
$envDir = dirname($envFile);
$dotenv = Dotenv::createImmutable($envDir, '.env.pw-encryption-key');
$dotenv->load();
} catch (Throwable $e) {
http_response_code(500);
echo json_encode([
'success' => false,
'message' => "Dotenv error"
]);
}
// Encrypt for display
$iv_length = openssl_cipher_iv_length('aes-256-cbc');
$iv = random_bytes($iv_length);
$encrypted = openssl_encrypt($plain, 'aes-256-cbc', $_ENV['PW_ENCRYPTION_KEY'], 0, $iv);
$cipher_store = base64_encode($iv . $encrypted);
if ($id > 0) {
if ($update_type === 'no_password') {
$updated = db_update($mysqli, $tableInternUsers, [
'username' => $username,
'name_person' => $namePerson,
'freigabe' => $freigabe_store,
'updated_at' => date('Y-m-d H:i:s'),
'edited_by' => $editor_id
], ['id' => $id]);
} else {
$updated = db_update($mysqli, $tableInternUsers, [
'password_hash' => $hash,
'password_cipher' => $cipher_store,
@@ -123,42 +180,14 @@ if ($id > 0) {
'updated_at' => date('Y-m-d H:i:s'),
'edited_by' => $editor_id
], ['id' => $id]);
} else {
$stmt = $mysqli->prepare(
"INSERT INTO {$tableInternUsers}
(username, name_person, password_hash, password_cipher, freigabe, created_at, updated_at, edited_by, login_active)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
);
$loginActive = 1;
$stmt->bind_param(
"sssssssis",
$username,
$namePerson,
$hash,
$cipher_store,
$freigabe_store,
$created_at,
$updated_at,
$editor_id,
$loginActive
);
$created_at = date('Y-m-d H:i:s');
$updated_at = $created_at;
$updated = $stmt->execute();
}
if ($updated !== false) {
if ($id == 0) { // new user
$new_id = $mysqli->insert_id;
echo json_encode(['success' => true, 'message' => $username.' wurde erfolgreich erstellt.', 'id' => $new_id]);
} else {
echo json_encode(['success' => true, 'message' => $username.' wurde erfolgreich aktualisiert.']);
}
} else {
if ($updated === false) {
echo json_encode(['success' => false, 'message' => 'DB Error']);
}
http_response_code(500);
exit;
}
echo json_encode(['success' => true, 'message' => $username.' wurde erfolgreich aktualisiert.']);
http_response_code(200);
exit;
@@ -1,6 +1,8 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -23,11 +25,19 @@ if ($data['success'] === false){
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
// ---------- Get and sanitize input ----------
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$value = isset($_POST['value']) ? floatval($_POST['value']) : 0;
$type = isset($_POST['type']) ? $_POST['type'] : '';
$allowedTypes = ['preis', 'order_index', 'aktiv'];
if (!in_array($type, $allowedTypes, true)) {
echo json_encode(['success' => false, 'message' => 'Invalid type']);
http_response_code(400);
exit;
}
if ($id < 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
@@ -37,7 +47,7 @@ if ($id < 0) {
// ---------- Step 2: Get values from DB ----------
$query = "UPDATE `$tableProgramme` SET aktiv = '$value' WHERE id = $id";
$query = "UPDATE `$tableProgramme` SET $type = '$value' WHERE id = $id";
$result = $mysqli->query($query);
if (!$result) {
@@ -45,6 +55,12 @@ if (!$result) {
exit;
}
if ($type === 'aktiv') {
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
@@ -1,6 +1,8 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -23,31 +25,27 @@ if ($data['success'] === false){
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
// ---------- Get and sanitize input ----------
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$value = isset($_POST['value']) ? round(floatval($_POST['value']), 2) : 0;
$slug = $_POST['slug'] ?? '';
$allowed_slugs = ['rankLive-overview', 'rankLive-geraet', 'kampfrichter-admin', 'kampfrichter-geraet', 'rangliste'];
if ($id < 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
if (!in_array($slug, $allowed_slugs)){
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$_SESSION['current_ranklive_slug'] = $slug;
$db_res = db_select($mysqli, $tableRankLiveConfigs, '`json`', '`type_slug` = ?', [$slug], '', 1);
$query = "UPDATE `$tableProgramme` SET preis = '$value' WHERE id = $id";
$result = $mysqli->query($query);
$res = $db_res[0]['json'] ?? '';
if (!$result) {
echo json_encode(['success' => false, 'message' => 'Update failed']);
exit;
}
// ---------- Return JSON ----------
// Return JSON
echo json_encode([
'success' => true,
'message' => 'Startgebüren set to '.$value.' Fr.'
'data' => $res,
'message' => 'gespeichert'
]);
exit;
@@ -0,0 +1,148 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$json = $_POST['json'] ?? '';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = ['rankLive-overview' => 'all', 'rankLive-geraet' => 'geraet', 'kampfrichter-admin' => 'all', 'kampfrichter-geraet' => 'geraet', 'rangliste' => 'all'];
if (!array_key_exists($slug, $allowed_slugs)){
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
if (!$json || $json === ''){
echo json_encode(['success' => false, 'message' => 'No input']);
exit;
}
$array = json_decode($json, true) ?? [];
$body = $array['body'];
$all_noten = [];
function extractNotenData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenData($value));
}
}
return $result;
}
function extractNotenGeraetData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenGeraetData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenGeraetData($value));
}
}
return $result;
}
$all_noten = ($allowed_slugs[$slug] === 'all') ? extractNotenData($body) : extractNotenGeraetData($body);
$all_noten_export = [];
foreach ($all_noten as $single_note) {
$all_noten_export[] = [
'g_id' => $single_note['geraet-id'] ?? 'A',
'n_id' => $single_note['field-type-id'],
'r' => $single_note['run']
];
}
function unique_matrix($matrix) {
$matrixAux = $matrix;
foreach($matrix as $key => $subMatrix) {
unset($matrixAux[$key]);
foreach($matrixAux as $subMatrixAux) {
if($subMatrix === $subMatrixAux) {
unset($matrix[$key]);
}
}
}
return $matrix;
}
$all_noten_json = json_encode(unique_matrix($all_noten_export) ?? []) ?? '';
$user_id = (int) $_SESSION['user_id_wk_leitung'] ?? 0;
$stmt = $mysqli->prepare("INSERT INTO `$tableRankLiveConfigs` (`json`, `all_noten_json`, `type_slug`, `created_by`, `updated_by`) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `json` = VALUES(`json`), `all_noten_json` = VAlUES(`all_noten_json`), `updated_by` = VALUES(`updated_by`)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
exit;
}
$stmt->bind_param("sssii", $json, $all_noten_json, $slug, $user_id, $user_id);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
if (str_contains($slug, 'rankLive')) {
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
}
// Return JSON
echo json_encode([
'success' => true,
'message' => 'gespeichert'
]);
exit;
@@ -0,0 +1,82 @@
<?php
ini_set("display_errors",1);
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$tableJSONVersion = '1.0.0';
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = [
'rankLive-overview' => 'RankLive Overview',
'rankLive-geraet' => 'RankLive einzelnes Gerät',
'kampfrichter-admin' => 'Kampfrichter Admin (alle Geräte)',
'kampfrichter-geraet' => 'Kampfrichter einzelnes Gerät' //, 'rangliste' => 'all'
];
if (!array_key_exists($slug, $allowed_slugs)){
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
$json = db_get_var($mysqli, "SELECT `json` FROM $tableRankLiveConfigs WHERE `type_slug` = ?", [$slug]) ?: '';
try {
$json_array = json_decode($json, true) ?? [];
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Error parsing JSON']);
exit;
}
$wk_name = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['wkName']);
$exportData = [
'metadata' => [
'erstellt_am' => date("d.m.Y H:i"),
'ersteller' => $_SERVER['HTTP_HOST'],
'erstellt_mit' => 'WKVS Auto-Export',
'wkvs_json_table_version' => '1.0.0',
'titel' => 'WKVS Table für ' . $allowed_slugs[$slug] . ' ' . $wk_name . ' (V 1.0.0)'
],
'data' => $json_array,
'type' => $slug
];
$jsonOutput = json_encode($exportData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// 6. Force Browser File Download
$fileName = 'WKVS-table-config-' . date('Y-m-d_H-i') . '-' . $slug . '.conf.wkvs';
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . strlen($jsonOutput));
echo $jsonOutput;
exit;
@@ -0,0 +1,193 @@
<?php
ini_set("display_errors",1);
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($_FILES['configFile']) || $_FILES['configFile']['error'] !== UPLOAD_ERR_OK) {
echo json_encode([
'success' => false,
'message' => 'Keine Config-Datei ausgewählt'
]);
exit;
}
$tableJSONVersion = '1.0.0';
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/string-calculator/string-calculator-functions.php';
$slug = $_POST['slug'] ?? '';
$allowed_slugs = ['rankLive-overview' => 'all', 'rankLive-geraet' => 'geraet', 'kampfrichter-admin' => 'Kampfrichter Admin (alle Geräte)', 'kampfrichter-geraet' => 'geraet', 'rangliste' => 'all'];
if (!array_key_exists($slug, $allowed_slugs)){
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalid Type']);
exit;
}
$tmpPath = $_FILES['configFile']['tmp_name'];
$originalName = $_FILES['configFile']['name'];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
$allowedExt = ['json', 'wkvs'];
if (!in_array($extension, $allowedExt, true)) {
echo json_encode(['success' => false, 'message' => 'Falsches Format (Endung)']);
http_response_code(422);
exit;
}
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mimeType = $finfo->file($tmpPath);
$allowedMime = ['application/json'];
if (!in_array($mimeType, $allowedMime, true)) {
echo json_encode(['success' => false, 'message' => 'Dateiinhalt ist kein gültiges JSON']);
http_response_code(422);
exit;
}
$JSONcontent = file_get_contents($tmpPath);
$content = json_decode($JSONcontent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
die("Invalid JSON layout.");
}
if (!isset($content['metadata']['wkvs_json_table_version']) || $content['metadata']['wkvs_json_table_version'] !== $tableJSONVersion) {
echo json_encode(['success' => false, 'message' => 'Inkompatible Datei-Version. (V ' . $tableJSONVersion . ' benötigt'. (isset($content['metadata']['wkvs_json_version']) ? ", benutzte Datei-Version: V " . $content['metadata']['wkvs_json_version'] : '') . ')' ]);
unlink($destination);
http_response_code(422);
exit;
}
if (!isset($content['data']) || !is_array($content['data'])) {
echo json_encode(['success' => false, 'message' => 'Ungültige JSON-Struktur']);
unlink($destination);
http_response_code(422);
exit;
}
$body = $content['data']['body'];
$all_noten = [];
function extractNotenData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenData($value));
}
}
return $result;
}
function extractNotenGeraetData(array $array): array {
$result = [];
// Check if the current array is a 'notenData' object itself
if (isset($array['type']) && $array['type'] === 'notenGeraetData') {
return [$array];
}
// Otherwise, loop through the elements to go deeper
foreach ($array as $value) {
if (is_array($value)) {
// Recursively extract from the sub-array and merge into our result list
$result = array_merge($result, extractNotenGeraetData($value));
}
}
return $result;
}
$all_noten = ($allowed_slugs[$slug] === 'all') ? extractNotenData($body) : extractNotenGeraetData($body);
$all_noten_export = [];
foreach ($all_noten as $single_note) {
$all_noten_export[] = [
'g_id' => $single_note['geraet-id'] ?? 'A',
'n_id' => $single_note['field-type-id'],
'r' => $single_note['run']
];
}
function unique_matrix($matrix) {
$matrixAux = $matrix;
foreach($matrix as $key => $subMatrix) {
unset($matrixAux[$key]);
foreach($matrixAux as $subMatrixAux) {
if($subMatrix === $subMatrixAux) {
unset($matrix[$key]);
}
}
}
return $matrix;
}
$all_noten_json = json_encode(unique_matrix($all_noten_export) ?? []) ?? '';
$user_id = (int) $_SESSION['user_id_wk_leitung'] ?? 0;
$stmt = $mysqli->prepare("INSERT INTO `$tableRankLiveConfigs` (`json`, `all_noten_json`, `type_slug`, `created_by`, `updated_by`) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `json` = VALUES(`json`), `all_noten_json` = VAlUES(`all_noten_json`), `updated_by` = VALUES(`updated_by`)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
exit;
}
$json = json_encode($content['data']);
$stmt->bind_param("sssii", $json, $all_noten_json, $slug, $user_id, $user_id);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
if (str_contains($slug, 'rankLive')) {
require_once $baseDir . '/../scripts/cache/cache-creators/regenerate-display-dependencies-cache.php';
generateIntersectCache($mysqli, $baseDir);
}
// Return JSON
echo json_encode([
'success' => true,
'message' => 'gespeichert'
]);
exit;
@@ -0,0 +1,366 @@
<?php
use TCPDF;
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$data = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'message' => $data['message']]);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$wkName = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['wkName']);
$wkName = ($wkName === null) ? 'Keinen Wettkampfname angegeben. Setzen unter: /intern/wk-leitung/einstellungen' : $wkName;
$normalFontSize = 11;
$marginSide = 10;
$marginTop = 10;
$minMarginBottomTable = 20;
$format = 'A4';
$orientation = 'L';
$svgs = [
'1' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6 2h12v2a6 6 0 0 1-6 6 6 6 0 0 1-6-6V2zm0 20h12v-2a6 6 0 0 0-6-6 6 6 0 0 0-6 6v2zm6-10c1.657 0 3-1.343 3-3H9c0 1.657 1.343 3 3 3z" fill="#0073aa"/></svg>',
'2' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#28a745"/><path d="M7 12l3 3 7-7" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>',
'3' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="10" fill="#dc3545"/><path d="M8 8l8 8M16 8l-8 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
];
$now = trim($_POST['date'] ?? date('Y-m-d H:i:s'));
$logEntries = db_select($mysqli, $tableOrdersLog, "*");
$personen = db_select($mysqli, $tableInternUsers, "`id`, `name_person`, `username`");
$indexedPersonen = array_column($personen, null, 'id');
$entriesArray = [];
foreach ($logEntries as $slE) {
// $value = ($slE['old_order_status'] !== null) ? ($svgs[$slE['old_order_status']] ?? $slE['old_order_status']) . ' ' . json_decode('"\u2192"') . ' ' . ($svgs[$slE['new_order_status'] ?? ''] ?? $slE['new_order_status'] ?? '') : ($svgs[$slE['new_order_status'] ?? ''] ?? $slE['new_order_status'] ?? '');
$valueArray = [
1 => $svgs[$slE['old_order_status'] ?? ''] ?? '',
2 => isset($svgs[$slE['old_order_status']]),
3 => $svgs[$slE['new_order_status'] ?? ''] ?? '',
4 => isset($svgs[$slE['new_order_status']])
];
$entriesArray[] = [
'order_id' => $slE['order_id'] ?? '',
'value' => $valueArray,
'edited_by' => $indexedPersonen[$slE['edited_by']]['name_person'] ?? 'Unbekannter User',
'timestamp' => new DateTime($slE['edited_at'])->format('d.m.Y H:i:s') ?? '---'
];
}
// Load TCPDF
require $baseDir . '/../composer/vendor/autoload.php';
class ProtokollPDF extends TCPDF
{
public $columns;
public $wkName;
public $marginSide;
public $marginTop;
public $pdfHeight;
public $pdfWidth;
public $headerType;
public $now;
// Page header
public $headerBottomY = 0;
public function Header()
{
$this->SetFillColor(255, 255, 255);
$this->Rect(0, 0, $this->pdfWidth, $this->pdfHeight, 'F');
$arrayTitles = [];
$image_file = $_SERVER['DOCUMENT_ROOT'] . '/intern/img/logo-normal.png';
$this->SetY($this->marginTop);
$this->SetX(($this->marginSide ?? 10));
$this->SetFont('freesans', '', 20);
$this->Cell(0, 0, 'Protokoll Rechnungen ' . $this->wkName, 0, 1, 'L', 0, '', 0, false, 'T', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, $this->pdfWidth - 10 - $this->marginSide, $this->marginTop, '', 10, 'png', '', 'T');
$this->SetY($preimageY + 5);
$this->SetX($this->marginSide ?? 10); // Force back to left margin
$this->SetFont('freesans', '', 11);
$this->Ln(5);
$columns = $this->columns;
$startY = $this->GetY();
$this->SetFont('', 'B');
$this->SetX($this->marginSide ?? 10);
foreach ($columns as $col) {
$title = $col['header'];
if (($col['rotate'] ?? false)) {
$x = $this->GetX();
$y = $this->GetY() + 7;
$this->StartTransform();
$this->Rotate(45, $x, $y);
$lineY = $startY + 7;
if (($col['id'] ?? '') === 'gesamt') {
$this->SetLineWidth(0.6);
} else {
$this->SetLineWidth(0.2);
}
$this->Line($x, $lineY, $x + ($col['width_header'] ?? 0), $lineY);
$this->SetLineWidth(0.2);
$this->Cell($col['width_header'] ?? 0, 7, $title ?? '', 0, 0, 'C');
$this->StopTransform();
$this->SetX($x + ($col['max_width'] ?? 0));
} else {
$this->Cell($col['max_width'] ?? 0, 7, $title ?? '', 0, 0, $col['align'] ?? 'L');
}
}
$this->Ln();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetLineWidth(0.6);
$this->Line($this->marginSide ?? 10, $this->headerBottomY, $this->pdfWidth - ($this->marginSide ?? 10), $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(0);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX($this->pdfWidth - ($this->marginSide ?? 10));
$this->Cell(($this->marginSide ?? 10), $this->pdfHeight, '', 0, 0, 'L', true);
$this->SetY(-15);
$this->SetFont('freesans', 'I', 8);
// 1. Page Number - Centered
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C');
// 2. Timestamp - Force to Left
$this->SetX($this->getMargins()['left']); // Reset to left margin
$this->Cell(0, 10, 'Zeitstempel: ' . $this->now, 0, false, 'L');
// 3. Host - Force to Right (Cell with width 0 and align 'R' handles this)
$this->SetX($this->getMargins()['left']); // Reset again so 'R' alignment works from the start
$this->Cell(0, 10, $_SERVER['HTTP_HOST'], 0, 0, 'R', false, 'https://'.$_SERVER['HTTP_HOST']);
}
}
$pdf = new ProtokollPDF($orientation, 'mm', $format, true, 'UTF-8', false);
$pdfHeight = $pdf->getPageHeight();
$pdfWidth = $pdf->getPageWidth();
$pdf->wkName = $wkName;
$pdf->marginSide = $marginSide;
$pdf->marginTop = $marginTop;
$pdf->pdfHeight = $pdfHeight;
$pdf->pdfWidth = $pdfWidth;
$pdf->now = $now;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor($wkName);
$pdf->SetTitle($wkName . "_Protokoll.pdf");
$pdf->SetAutoPageBreak(FALSE);
$pdf->SetFont('freesans', '', 11);
// Define columns dynamically
$columns = [
['id' => 'order_id', 'header' => 'Rechnungsnummer', 'align' => 'L', 'flex' => true, 'type' => 'link', 'field' => 'order_id'],
['id' => 'value', 'header' => 'Änderung', 'align' => 'L', 'flex' => true, 'type' => 'value', 'field' => 'value'],
['id' => 'edited_by', 'header' => 'Geändert durch', 'align' => 'C', 'flex' => true, 'type' => 'field', 'field' => 'edited_by'],
['id' => 'timestamp', 'header' => 'Zeitstempel', 'align' => 'C', 'flex' => true, 'type' => 'field', 'field' => 'timestamp'],
];
$padding = 4;
// 1. Calculate initial max widths based on headers
foreach ($columns as &$col) {
$col['width_header'] = $pdf->GetStringWidth($col['header']) + ($padding * 2);
if ($col['type'] !== 'note') {
$col['max_width'] = $col['width_header'];
} else {
$col['max_width'] = 0;
}
}
unset($col);
foreach ($entriesArray as $row) {
foreach ($columns as $col) {
$text = '';
if ($col['type'] !== 'value') {
if (isset($row[$col['field']])) {
$text = $row[$col['field']];
}
$width = $pdf->GetStringWidth($text) + ($padding * 2);
if ($width > $col['max_width']) {
$col['max_width'] = $width;
}
} else {
if (40 > $col['max_width']) {
$col['max_width'] = 40;
}
}
}
}
$fixedWidth = 0;
foreach ($columns as $col) {
if ($col['flex'] ?? false) {
$flexCols[] = $col['id'];
$fixedWidth += $col['max_width'];
} else {
$fixedWidth += $col['max_width'];
}
}
$tablew = $pdfWidth - (2 * $marginSide);
$effTableWidth = $tablew - $fixedWidth;
if (!empty($flexCols) && $effTableWidth > 0) {
$flexTotalInitial = 0;
foreach ($columns as $col) {
if ($col['flex'] ?? false) $flexTotalInitial += $col['max_width'];
}
foreach ($columns as &$col) {
if ($col['flex'] ?? false) {
$col['max_width'] += ($col['max_width'] / $flexTotalInitial) * $effTableWidth;
}
}
unset($col);
}
$pdf->columns = $columns;
$pdf->AddPage();
$margin_top = $pdf->headerBottomY;
$pdf->SetMargins($marginSide, $margin_top, $marginSide); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$pdf->SetFont('', '');
foreach ($entriesArray as $row) {
$rowHeight = 10;
foreach ($columns as $col) {
$pdf->setFillColor(255, 255, 255);
if ($pdf->getY() + $rowHeight > $pdfHeight - $minMarginBottomTable) {
$pdf->addPage();
$margin_top = $pdf->headerBottomY;
$pdf->setY($margin_top);
}
$customFontSize = (isset($col['font_size']) && $col['font_size'] != 0);
if ($customFontSize) {
$pdf->SetFont('', '', $col['font_size']);
}
$startX = $pdf->GetX();
$startY = $pdf->GetY();
if ($col['type'] === 'field') {
$text = $row[$col['field']] ?? '';
$pdf->MultiCell($col['max_width'], $rowHeight, $text, 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
} elseif ($col['type'] === 'value') {
$pdf->MultiCell($col['max_width'], $rowHeight, '', 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
$valuesArrary = $row[$col['field']];
if (isset($valuesArrary[1]) && isset($valuesArrary[2]) && $valuesArrary[2]) {
$pdf->ImageSVG('@' . $valuesArrary[1], $startX, $startY + 2, $rowHeight - 4, $rowHeight - 4);
$pdf->MultiCell(10, $rowHeight, json_decode('"\u2192"'), '', 'C', false, 0, $startX + $rowHeight - 4, $startY, true, 0, false, true, $rowHeight, 'M');
}
if (isset($valuesArrary[3]) && isset($valuesArrary[3]) && $valuesArrary[4]) {
$pdf->ImageSVG('@' . $valuesArrary[3], $startX + $rowHeight - 4 + 10, $startY + 2, $rowHeight - 4, $rowHeight - 4);
}
$pdf->SetX($startX + $col['max_width']);
} elseif ($col['type'] === 'link') {
$text = $row[$col['field']] ?? '';
$pdf->MultiCell($col['max_width'], $rowHeight, $text, 'B', $col['align'], true, 0, $startX, $startY, true, 0, false, true, $rowHeight, 'M');
$pdf->Link($startX, $startY, $col['max_width'], $rowHeight, 'https://'.$_SERVER['HTTP_HOST'].'/intern/wk-leitung/rechnungen-viewer?order_id='.$text);
}
if ($customFontSize) {
$pdf->SetFont('', '', $normalFontSize);
}
}
$pdf->SetY($startY + $rowHeight);
}
$textanzEintaege = (count($entriesArray) > 1) ? 'Einträge': 'Eintrag';
$pdf->SetFont('', '', 7);
$pdf->Cell(20, 6, count($entriesArray).' '.$textanzEintaege, 0, 0, 'L');
$pdf->SetFont('', '', $normalFontSize);
$pdf->Output($wkName . "_Protokoll_Rechnungen.pdf", 'I');
exit;
@@ -1,74 +0,0 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
http_response_code(500);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
if (!isset($_POST['ids']) || !is_array($_POST['ids']) || count($_POST['ids']) < 1) {
echo json_encode(['success' => false, 'message' => 'Keine Id angegeben']);
http_response_code(422);
exit;
}
$ids = $_POST['ids'];
// Validate: all IDs must be integers
$ids = array_filter($ids, fn($id) => ctype_digit(strval($id)));
if (count($ids) === 0) {
echo json_encode(['success' => false, 'message' => 'Kein gültiger Input']);
http_response_code(422);
exit;
}
// Build placeholders for prepared statement
$placeholders = implode(',', array_fill(0, count($ids), '?'));
// Prepare the SQL statement
$sql = "DELETE FROM $tableOrders WHERE order_id IN ($placeholders)";
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Vorbereiten der Abfrage']);
http_response_code(500);
exit;
}
// Bind parameters dynamically
$types = str_repeat('i', count($ids)); // all integers
$stmt->bind_param($types, ...$ids);
// Execute
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Löschen']);
http_response_code(500);
exit;
}
$stmt->close();
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Bestellungen erfolgreich gelöscht']);
http_response_code(200);
exit;
@@ -1,6 +1,8 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
@@ -13,6 +15,20 @@ check_user_permission('wk_leitung');
verify_csrf();
$allowedActions = ['payEntrys' => 2, 'deleteEntrys' => 3];
$requestedAction = trim($_POST['action'] ?? '');
if (!array_key_exists($requestedAction, $allowedActions)) {
echo json_encode(['success' => false, 'message' => 'Invalid Action']);
http_response_code(400);
exit;
}
$actionValue = $allowedActions[$requestedAction];
$user = $_SESSION['user_id_wk_leitung'] ?? 0;
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
@@ -24,47 +40,70 @@ if ($data['success'] === false){
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
if (!isset($_POST['scor']) || empty($_POST['scor']) || $_POST['scor'] === '') {
echo json_encode(['success' => false, 'message' => 'Keine Referenz angegeben']);
if (!isset($_POST['ids']) || !is_array($_POST['ids']) || count($_POST['ids']) < 1) {
echo json_encode(['success' => false, 'message' => 'Keine Id angegeben']);
http_response_code(422);
exit;
}
$scorFullStr = strval($_POST['scor']);
$ids = $_POST['ids'];
$scor = intval(substr($scorFullStr, 2));
// Validate: all IDs must be integers
$ids = array_filter($ids, fn($id) => ctype_digit(strval($id)));
$stmt = $mysqli->prepare(
"SELECT 1 FROM $tableOrders WHERE order_id = ? LIMIT 1"
);
$stmt->bind_param('i', $scor);
$stmt->execute();
$stmt->store_result();
if (count($ids) === 0) {
echo json_encode(['success' => false, 'message' => 'Kein gültiger Input']);
http_response_code(422);
exit;
}
if ($stmt->num_rows !== 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Referenz']);
http_response_code(403);
exit;
// Build placeholders for prepared statement
$placeholders = implode(',', array_fill(0, count($ids), '?'));
$affectedRows = db_select($mysqli, $tableOrders, "order_status, order_id", "order_id IN ($placeholders)", $ids);
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("INSERT INTO $tableOrdersLog (`old_order_status`, `new_order_status`, `order_id`, `edited_by`) VALUES (?, ?, ?, ?)");
foreach ($affectedRows as $row) {
$stmt->bind_param("ssss", $row['order_status'], $actionValue, $row['order_id'], $user);
$stmt->execute();
}
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
throw $e;
}
// Prepare the SQL statement
$sql = "UPDATE $tableOrders SET order_status = ? WHERE order_id IN ($placeholders)";
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Vorbereiten der Abfrage']);
http_response_code(500);
exit;
}
$params = array_merge([$actionValue], $ids);
// Bind parameters dynamically
$types = 's' . str_repeat('i', count($ids)); // all integers
$stmt->bind_param($types, ...$params);
// Execute
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Löschen']);
http_response_code(500);
exit;
}
$stmt->close();
$sql = "UPDATE $tableOrders SET order_status = 2 WHERE order_id = ?";
$nstmt = $mysqli->prepare($sql);
$nstmt->bind_param('i', $scor);
if (!$nstmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler']);
http_response_code(500);
exit;
}
$nstmt->close();
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Rechnung wurde aktualisiert']);
echo json_encode(['success' => true, 'message' => 'Bestellungen erfolgreich gelöscht']);
http_response_code(200);
exit;
exit;
@@ -0,0 +1,85 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$data = include $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($data['success'] === false){
echo json_encode(['success' => false, 'message' => $data['message']]);
http_response_code(500);
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
if (!isset($_POST['scor']) || empty($_POST['scor']) || $_POST['scor'] === '') {
echo json_encode(['success' => false, 'message' => 'Keine Referenz angegeben']);
http_response_code(422);
exit;
}
$user = $_SESSION['user_id_wk_leitung'] ?? 0;
$scorFullStr = strval($_POST['scor'] ?? '');
$scor = intval(substr($scorFullStr, 2));
$stmt = $mysqli->prepare(
"SELECT 1 FROM $tableOrders WHERE order_id = ? LIMIT 1"
);
$stmt->bind_param('i', $scor);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows !== 1) {
echo json_encode(['success' => false, 'message' => 'Invalide Referenz']);
http_response_code(403);
exit;
}
$stmt->close();
$affectedRow = db_select($mysqli, $tableOrders, "order_status, order_id", "order_id = ?", [$scor]);
$stmt = $mysqli->prepare("INSERT INTO $tableOrdersLog (`old_order_status`, `new_order_status`, `order_id`, `edited_by`) VALUES (?, 2, ?, ?)");
foreach ($affectedRow as $row) {
$stmt->bind_param("sss", $row['order_status'], $row['order_id'], $user);
$stmt->execute();
}
$sql = "UPDATE $tableOrders SET order_status = 2 WHERE order_id = ?";
$nstmt = $mysqli->prepare($sql);
$nstmt->bind_param('i', $scor);
if (!$nstmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler']);
http_response_code(500);
exit;
}
$nstmt->close();
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Rechnung wurde aktualisiert']);
http_response_code(200);
exit;
@@ -0,0 +1,86 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo json_encode(['success' => false, 'message' => 'Critical DB Error.']);
exit;
}
$action = $_POST['action'] ?? '';
if ($action === 'update') {
$abt_id = intval($_POST['abt'] ?? 0);
$field = $_POST['type'] ?? '';
$type_id = intval($_POST['type_id'] ?? 0);
$value = $_POST['value'] ?? '';
if (!preg_match('/^\d{2}:\d{2}$/', $value)) {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
http_response_code(400);
exit;
}
$allowedFields = ['start', 'end'];
if (
$abt_id > 0 &&
$type_id > 0 &&
db_check_var($mysqli, $tableAbt, 'id = ?', [$abt_id]) &&
db_check_var($mysqli, $tableZeitplanTypes, 'id = ?', [$type_id]) &&
($field === 'start' || ($field === 'end' && db_check_var($mysqli, $tableZeitplanTypes, 'id = ? AND has_endtime = ?', [$type_id, 1])))
) {
$stmt = $mysqli->prepare("INSERT INTO $tableAbtZeiten (`abt_id`, `zeitplan_id`, `" . $field . "_time`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `" . $field . "_time` = VALUES(`" . $field . "_time`)");
$stmt->bind_param("iis", $abt_id, $type_id, $value);
$stmt->execute();
$stmt->close();
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
http_response_code(400);
exit;
}
} elseif ($action === 'delete') {
$abt_id = intval($_POST['abt'] ?? 0);
$type_id = intval($_POST['type_id'] ?? 0);
if ($type_id > 0 && $abt_id > 0) {
db_delete($mysqli, $tableAbtZeiten, ['abt_id' => $abt_id, 'zeitplan_id' => $type_id]);
echo json_encode(['success' => true]);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid ID.']);
http_response_code(400);
exit;
}
} else {
echo json_encode(['success' => false, 'message' => 'Action not found.']);
}
@@ -19,6 +19,16 @@ check_user_permission('wk_leitung');
verify_csrf();
$allowed_operations = ['overrite', 'add'];
if (!isset($_POST['operation']) || !in_array($_POST['operation'], $allowed_operations, true)) {
http_response_code(400);
echo 'Invalid operation.';
exit;
}
$operation = $_POST['operation'];
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
@@ -32,43 +42,18 @@ require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$allGeraete = db_select($mysqli, $tableGeraete, "id, name, start_index", "", [], "start_index ASC");
$allAbt = db_select($mysqli, $tableAbt, "name, id", "", [], "name ASC");
$allAbt = db_select($mysqli, $tableAbt, "name, id, order_index", "", [], "order_index ASC");
$indexded_order_index_abt = array_column($allAbt, null, 'order_index');
$maxTurnerinnenGruppe = 8;
// $allGeraete[] = ['name' => 'null', 'id' => 'null'];
// $allAbt[] = ['name' => 'null', 'id' => 'null'];
/*$stmt = $mysqli->prepare("
SELECT
t.id,
t.programm,
t.verein,
GROUP_CONCAT(ta.id SEPARATOR ', ') AS abt_table_id
FROM $tableTurnerinnen t
LEFT JOIN $tableTurnerinnenAbt ta ON ta.turnerin_id = t.id
GROUP BY t.id, t.programm, t.verein
ORDER BY t.id DESC
");
$stmt->execute();
$result = $stmt->get_result();
$allTurnerinnen = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
print_r($allTurnerinnen);*/
$allTurnerinnen = db_select($mysqli, $tableTurnerinnen, "id, programm, verein, name" , "", [], "programm ASC");
$allTurnerinnen = db_select($mysqli, $tableTeilnehmende, "id, programm, verein, name" , "", [], "programm ASC");
$grouped = [];
foreach ($allTurnerinnen as $t) {
$sanitasedprogramm = str_replace(['-kader'], '', strtolower($t['programm']));
$grouped[$sanitasedprogramm][$t['verein']][] = $t;
$grouped[strtolower($t['programm'])][$t['verein']][] = $t;
}
function getMinKey(array $array): string|int {
@@ -159,28 +144,35 @@ function arrayRiegeneiteilung($allturnerinnen, $maxTurnerinnenGruppe, $allGeraet
$arrayAutoRiegeneinteilung = arrayRiegeneiteilung($grouped, $maxTurnerinnenGruppe, $allGeraete);
if ($operation === 'overrite') {
$stmt = $mysqli->prepare("DELETE FROM $tableAbt");
$stmt->execute();
$stmt->close();
}
$stmt = $mysqli->prepare("DELETE FROM $tableTurnerinnenAbt");
$stmt = $mysqli->prepare("DELETE FROM $tableTeilnehmendeAbt");
$stmt->execute();
$stmt->close();
$stmt = $mysqli->prepare("DELETE FROM $tableAbt");
$stmt->execute();
$stmt->close();
foreach ($arrayAutoRiegeneinteilung as $indAbt => $abt) {
$stmt = $mysqli->prepare("INSERT INTO $tableAbt (name) VALUES (?)");
$stmt->bind_param("s", $indAbt);
$stmt->execute();
$idAbt = $stmt->insert_id;
$stmt->close();
if ($operation === 'overrite' || !isset($indexded_order_index_abt[$indAbt])) {
$var_name = "Abteilung $indAbt";
$stmt = $mysqli->prepare("INSERT INTO $tableAbt (`name`, `order_index`) VALUES (?, ?)");
$stmt->bind_param("si", $var_name, $indAbt);
$stmt->execute();
$idAbt = $stmt->insert_id;
$stmt->close();
} else {
$idAbt = $indexded_order_index_abt[$indAbt]['id'];
}
foreach ($abt as $indGeraet => $geraet) {
foreach ($geraet as $indTurnerin => $turnerin) {
$stmt = $mysqli->prepare("INSERT INTO $tableTurnerinnenAbt (turnerin_id, abteilung_id, geraet_id, turnerin_index) VALUES (?, ?, ?, ?)");
$stmt = $mysqli->prepare("INSERT INTO $tableTeilnehmendeAbt (turnerin_id, abteilung_id, geraet_id, turnerin_index) VALUES (?, ?, ?, ?)");
$stmt->bind_param("ssss", $turnerin['id'], $idAbt, $indGeraet, $turnerin['turnerin_index']);
$stmt->execute();
$stmt->close();
@@ -188,47 +180,4 @@ foreach ($arrayAutoRiegeneinteilung as $indAbt => $abt) {
}
}
return http_response_code(201);
/*foreach ($grouped as $ind => $g) {
echo $ind;
foreach ($g as $verein) {
foreach ($verein as $turnerin) {
echo '<div class="riegeneinteilung_turnerin_row" data-turnerin_id="' . $turnerin['id'] . '">';
echo '<div class="riegeneinteilung_turnerin_info">';
echo '<span class="riegeneinteilung_turnerin_name">' . htmlspecialchars($turnerin['name']) . '</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['verein']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['abt']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['geraet_id']) . ')</span>';
echo '</div>';
echo '</div></div>';
}
}
echo '<br><br>';
}
foreach ($arrayAutoRiegeneinteilung as $ind => $abt) {
echo '<br>ABT:' . $ind;
foreach ($abt as $g) {
echo '<br>GERÄT';
foreach ($g as $turnerin) {
echo '<div class="riegeneinteilung_turnerin_row" data-turnerin_id="' . $turnerin['id'] . '">';
echo '<div class="riegeneinteilung_turnerin_info">';
echo '<span class="riegeneinteilung_turnerin_name">' . htmlspecialchars($turnerin['name']) . '</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['verein']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['turnerin_index']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['programm']) . ')</span>';
echo '</div>';
echo '</div></div>';
}
}
echo '<br><br>';
}
print_r($arrayAutoRiegeneinteilung);*/
return http_response_code(200);
@@ -0,0 +1,44 @@
<?php
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
// Validate input
if (!isset($_POST['abt']) || !ctype_digit($_POST['abt']) || !isset($_POST['newName'])) {
http_response_code(406);
exit;
}
$abtInput = (int) $_POST['abt'];
$new_name = trim($_POST['newName']);
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
db_update(
$mysqli,
$tableAbt,
['name' => $new_name],
['id' => $abtInput]
);
http_response_code(200);
echo json_encode(['success' => true]);
exit;
@@ -44,20 +44,21 @@ $abts = db_select($mysqli, $tableAbt, '*');
// Delete excess Abteilungen
foreach ($abts as $dbabt) {
if ((int)$dbabt['name'] > $anz_abt) {
db_delete($mysqli, $tableAbt, ['name' => $dbabt['name']]);
if ((int) $dbabt['order_index'] > $anz_abt) {
db_delete($mysqli, $tableAbt, ['id' => $dbabt['id']]);
}
}
// Build lookup of existing names
$existing = array_map('intval', array_column($abts, 'name'));
$existing = array_map('intval', array_column($abts, 'order_index'));
// Insert missing Abteilungen
$stmt = $mysqli->prepare("INSERT INTO $tableAbt (name) VALUES (?)");
$stmt = $mysqli->prepare("INSERT INTO $tableAbt (`name`, `order_index`) VALUES (?, ?)");
for ($i = 1; $i <= $anz_abt; $i++) {
if (!in_array($i, $existing, true)) {
$stmt->bind_param('i', $i);
$var_name = "Abteilung $i";
$stmt->bind_param('si', $var_name, $i);
$stmt->execute();
}
}
@@ -0,0 +1,67 @@
<?php
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
// Validate input
if (!isset($_POST['abt']) || !ctype_digit($_POST['abt']) || !isset($_POST['newOrderIndex'])) {
http_response_code(406);
exit;
}
$abtInput = (int) $_POST['abt'];
$newOrderIndex_input = (int) $_POST['newOrderIndex'];
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$allAbts = db_select($mysqli, $tableAbt, 'id', 'id != ? ORDER BY order_index ASC', [$abtInput]);
$ids = array_column($allAbts, 'id');
$targetIndex = max(0, $newOrderIndex_input - 1);
array_splice($ids, $targetIndex, 0, $abtInput);
$mysqli->begin_transaction();
$one_based_indexes = [];
try {
foreach ($ids as $zeroBasedIndex => $abtId) {
$newIndex = $zeroBasedIndex + 1;
db_update(
$mysqli,
$tableAbt,
['order_index' => $newIndex],
['id' => $abtId]
);
$one_based_indexes[$abtId] = $newIndex;
}
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
http_response_code(500);
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
exit;
}
http_response_code(200);
echo json_encode(['success' => true, 'newIndexes' => $one_based_indexes]);
exit;
@@ -21,12 +21,12 @@ check_user_permission('wk_leitung');
verify_csrf();
// Validate input
if (!isset($_POST['abt']) || !ctype_digit($_POST['abt'])) {
if (!isset($_POST['abtId']) || !ctype_digit($_POST['abtId'])) {
http_response_code(406);
exit;
}
$abtInput = (int) $_POST['abt'];
$abtInputId = (int) $_POST['abtId'];
$type = 'wkl';
@@ -40,34 +40,27 @@ if ($dbconnection['success'] !== true){
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
// Fetch existing Abteilungen ordered by name
$abts = db_select($mysqli, $tableAbt, '*', '', [], 'name ASC');
// Fetch existing Abteilungen ordered by order_index
$abts = db_select($mysqli, $tableAbt, '*', '', [], 'order_index ASC');
$deleteId = null;
$deleteIndex = null;
$indexed_abts = array_column($abts, null, 'id');
foreach ($abts as $ind => $abt) {
if ((int)$abt['name'] === (int)$abtInput) {
$deleteId = (int)$abt['id'];
$deleteIndex = $ind;
break;
}
}
$deleteIndex = $indexed_abts[$abtInputId]['order_index'] ?? null;
if ($deleteId === null) {
if ($deleteIndex === null) {
http_response_code(406);
exit;
}
// Delete selected row
db_delete($mysqli, $tableAbt, ['id' => $deleteId]);
db_delete($mysqli, $tableAbt, ['id' => $abtInputId]);
// Reindex subsequent rows
for ($i = $deleteIndex + 1; $i < count($abts); $i++) {
db_update(
$mysqli,
$tableAbt,
['name' => (int)$abts[$i]['name'] - 1],
['order_index' => (int)$abts[$i]['order_index'] - 1],
['id' => (int)$abts[$i]['id']]
);
}
@@ -1,58 +0,0 @@
<?php
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('wk_leitung');
verify_csrf();
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
$input = $_POST;
if (!$input || !isset($input['new'])) {
http_response_code(400);
exit;
}
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("
UPDATE $tableTurnerinnenAbt
SET turnerin_index = ?
WHERE turnerin_id = ?
");
foreach (['new', 'old'] as $bucket) {
if (empty($input[$bucket])) continue;
foreach ($input[$bucket] as $row) {
$stmt->bind_param("ii", $row['order_index'], $row['id']);
$stmt->execute();
}
}
$mysqli->commit();
http_response_code(200);
} catch (Throwable $e) {
$mysqli->rollback();
http_response_code(500);
}
@@ -1,7 +1,10 @@
<?php
ini_set("display_errors", 1);
ini_set("display_startup_errors", 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
@@ -16,60 +19,143 @@ $type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
if ($dbconnection['success'] !== true) {
echo 'Critical DB Error.';
exit;
}
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/db/db-functions.php';
$turnerinId = (int)$_POST['turnerin_id'];
$abteilung = $_POST['abteilung'];
$geraet = $_POST['geraet'];
// Default to NULL if frontend sends "null"
$abtId = null;
$geraetId = null;
$turnerin_ids = $_POST['turnerin_ids'] ?? [];
$neu_abteilung_id = intval($_POST['neuAbteilungId'] ?? 0);
$neu_geraet_id = intval($_POST['neuGeraetId'] ?? 0);
$alt_abteilung_id = intval($_POST['altAbteilungId'] ?? 0);
$alt_geraet_id = intval($_POST['altGeraetId'] ?? 0);
$neu_first_index = intval($_POST['neuFirstIndex'] ?? 0);
// Resolve Abteilung ID
if ($abteilung !== 'null') {
$stmt = $mysqli->prepare("SELECT id FROM $tableAbt WHERE name = ?");
$stmt->bind_param("s", $abteilung);
if ($neu_abteilung_id < 1) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Keine Abteilungs ID angegeben.']);
exit;
} else {
$stmt = $mysqli->prepare("SELECT 1 FROM $tableAbt WHERE id = ?");
$stmt->bind_param("i", $neu_abteilung_id);
$stmt->execute();
if ($row = $stmt->get_result()->fetch_assoc()) {
$abtId = (int)$row['id'];
if (!$stmt->get_result()->fetch_assoc()) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalide Abteilungs ID angegeben.']);
exit;
}
$stmt->close();
}
// Resolve Gerät ID
if ($geraet !== 'null') {
$stmt = $mysqli->prepare("SELECT id FROM $tableGeraete WHERE name = ?");
$stmt->bind_param("s", $geraet);
if ($neu_geraet_id < 1) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Keine neue Geräte ID angegeben.']);
exit;
} else {
$stmt = $mysqli->prepare("SELECT 1 FROM $tableGeraete WHERE id = ?");
$stmt->bind_param("i", $neu_geraet_id);
$stmt->execute();
if ($row = $stmt->get_result()->fetch_assoc()) {
$geraetId = (int)$row['id'];
if (!$stmt->get_result()->fetch_assoc()) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Invalide Geräte ID angegeben.']);
exit;
}
$stmt->close();
}
// Upsert into turnerinnen_abt (turnerin_id is UNIQUE)
$stmt = $mysqli->prepare("
INSERT INTO $tableTurnerinnenAbt (turnerin_id, abteilung_id, geraet_id)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
abteilung_id = VALUES(abteilung_id),
geraet_id = VALUES(geraet_id)
");
$stmt->bind_param("iii", $turnerinId, $abtId, $geraetId);
$stmt->execute();
$stmt->close();
function arrayshift($v) {
return intval($v) + 1;
}
// Return JSON
http_response_code(200);
echo json_encode([
'success' => true,
'turnerin_id' => $turnerinId,
'abteilung_id' => $abtId,
'geraet_id' => $geraetId
]);
$array_ids = array_map('intval', $turnerin_ids);
$mysqli->begin_transaction();
try {
// 1. Move/Upsert the specific gymnasts into the new group
$stmt = $mysqli->prepare("
INSERT INTO $tableTeilnehmendeAbt (turnerin_id, abteilung_id, geraet_id)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE abteilung_id = VALUES(abteilung_id), geraet_id = VALUES(geraet_id)
");
foreach ($array_ids as $id) {
$stmt->bind_param("iii", $id, $neu_abteilung_id, $neu_geraet_id);
$stmt->execute();
}
$stmt->close();
// 2. Reorder the old group to fill any gaps left by the moved gymnasts
if ($alt_abteilung_id !== 0 && $alt_geraet_id !== 0 && ($alt_abteilung_id !== $neu_abteilung_id || $alt_geraet_id !== $neu_geraet_id)) {
$old_group_rows_zero_based = db_select($mysqli, $tableTeilnehmendeAbt, "turnerin_id",
"abteilung_id = ? AND geraet_id = ?", [$alt_abteilung_id, $alt_geraet_id], "turnerin_index ASC");
$updOld = $mysqli->prepare("UPDATE $tableTeilnehmendeAbt SET `turnerin_index` = ? WHERE `turnerin_id` = ?");
$old_group_rows = [];
foreach ($old_group_rows_zero_based as $i => $row) {
$new_idx = $i + 1; // Start indexing at 1
$updOld->bind_param("ii", $new_idx, $row['turnerin_id']);
$updOld->execute();
$old_group_rows[$row['turnerin_id']] = $new_idx;
}
$updOld->close();
}
// 3. Fetch all gymnasts now in this group to determine final order
// Order by start_index so we preserve the existing relative order of gymnasts already there
$rows = db_select(
$mysqli,
$tableTeilnehmendeAbt,
"turnerin_id",
"abteilung_id = ? AND geraet_id = ?",
[$neu_abteilung_id, $neu_geraet_id],
"turnerin_index ASC"
);
$current_ids = array_column($rows, 'turnerin_id');
// 4. Reconstruct the order: [Remaining ones before index] -> [Prioritized] -> [Remaining ones after index]
$remaining_ids = array_values(array_diff($current_ids, $array_ids));
$final_order_zero_based = [];
// Adjust index to 0-based for array manipulation
$target_pos = max(0, $neu_first_index - 1);
// Splice the prioritized IDs into the remaining list at the target position
array_splice($remaining_ids, $target_pos, 0, $array_ids);
$final_order_zero_based = $remaining_ids;
$final_order = [];
// 5. Update the database with the new continuous indices
$updStmt = $mysqli->prepare("UPDATE $tableTeilnehmendeAbt SET `turnerin_index` = ? WHERE `turnerin_id` = ?");
foreach ($final_order_zero_based as $array_index => $t_id) {
$actual_index = $array_index + 1;
$updStmt->bind_param("ii", $actual_index, $t_id);
$updStmt->execute();
$final_order[$t_id] = $actual_index;
}
$updStmt->close();
$mysqli->commit();
http_response_code(200);
echo json_encode([
'success' => true,
'old_new_start_indexes' => $old_group_rows ?? null,
'new_new_start_indexes' => $final_order
]);
} catch (Throwable $e) {
$mysqli->rollback();
http_response_code(500);
echo json_encode(['success' => false]);
}
-206
View File
@@ -1,206 +0,0 @@
<?php
/**
* Shared Sidebar Navigation
*
* Include this file after <body> on any intern page.
* Set $currentPage before including, e.g.:
* $currentPage = 'trainer';
* require $baseDir . '/intern/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>',
'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' => '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; }
}
function sidebarRender(string $mode) {
global $isWKL, $isTrainer, $isKampfrichter, $links, $currentPage, $icons, $renderMenu, $username, $freigabenSidebar;
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 = $_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="selectTrigger" aria-expanded="false">
<span class="selectLabel"><?= $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="selectOptions">
<?php foreach ($freigabenSidebar[$freigbenArrayName] as $f) :?>
<?php $selected = ($f === $selectedFreigabe) ? 'selected' : '' ?>
<li data-value="<?= htmlspecialchars($f) ?>" class="<?= $selected ?>"><?= htmlspecialchars($f) ?></li>
<?php endforeach; ?>
</ul>
<input type="hidden" name="type" class="selectValue" id="freigabenSidebarSelect" value="">
</div>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php
if (isset($username)) : ?>
<div class="sidebarUsername">Benutzer: <?= $username ?></div>
<?php endif; ?>
<div class="sidebar-footer">
<form method="POST" action="" class="abmelden"><input class="abmeldenbutton" type="submit" href="?logout=1" name="abmelden" value="Abmelden"></form>
</div>
</nav>
<script>
window.CSRF_TOKEN = "<?= $csrf_token ?? $_SESSION['csrf_token'] ?? '' ?>";
const siteType = '<?= $currentPage ?>';
// Close button binding (inline to avoid race condition with sidebar.js)
document.addEventListener('DOMContentLoaded', function() {
var closeBtn = document.getElementById('sidebar-close');
if (closeBtn) {
closeBtn.addEventListener('click', function() {
document.getElementById('sidebar-nav').classList.remove('open');
document.getElementById('sidebar-overlay').classList.remove('open');
document.getElementById('sidebar-toggle').classList.remove('open');
localStorage.setItem('intern_sidebar_open', 'false');
});
}
});
</script>
<script src="/intern/js/sidebar.js"></script>
<?php
}
}
@@ -0,0 +1,188 @@
<?php
use Shuchkin\SimpleXLSX;
header('Content-Type: application/json');
if (!isset($baseDir))
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('trainer');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../composer/vendor/autoload.php';
$type = 'tr';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true) {
echo 'Critical DB Error.';
exit;
}
if ($_FILES['xlsx_file']['error'] !== UPLOAD_ERR_OK) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Datei-Upload']);
http_response_code(400);
exit;
}
$tmpName = $_FILES['xlsx_file']['tmp_name'];
if (!class_exists('Shuchkin\\SimpleXLSX')) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Laden des Plugins']);
http_response_code(400);
exit;
}
$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) {
echo json_encode(['success' => false, 'message' => 'Die Excel-Datei muss mindestens einen Eintrag enthalten']);
http_response_code(400);
exit;
}
$selectedverein = $_SESSION['selectedFreigabeTrainer'] ?? '';
$isAdmin = $selectedverein === "admin";
$columnMap = [
'Nachname' => 'name',
'Vorname' => 'vorname',
'Geburtsdatum' => 'geburtsdatum',
'Programm' => 'programm'
];
if ($isAdmin) {
$columnMap['Verein'] = 'verein';
}
$header_row = false;
foreach ($rows as $row_index => $row) {
$formated_row = array_map('trim', $row);
$columnIndexes = [];
$column_not_found = false;
foreach ($columnMap as $excelHeader => $dbColumn) {
$index = array_search($excelHeader, $formated_row);
if ($index === false) {
$column_not_found = true;
break;
}
$columnIndexes[$dbColumn] = $index;
}
if ($column_not_found) {
continue;
} else {
$header_row = $row_index;
break;
}
}
if (!$header_row) {
echo json_encode(['success' => false, 'message' => 'Es wurde keine vollständige Header-Zeile gefunden']);
http_response_code(400);
exit;
}
for ($i = 0; $i <= (max($header_row, 1)); $i++) {
unset($rows[$i]);
}
$programme_db = db_select($mysqli, $tableProgramme, 'programm', 'aktiv = ?', [1], 'programm ASC');
$programme = array_column($programme_db, 'programm');
$mysqli->begin_transaction();
$columns = ['name', 'vorname', 'geburtsdatum', 'programm', 'verein'];
$set = implode(', ', array_map(fn($col) => "$col = ?", $columns));
$sql = "INSERT INTO $tableTeilnehmende SET $set";
$stmt = $mysqli->prepare($sql);
$types = str_repeat('s', count($columns));
try {
foreach ($rows as $row) {
if (!array_filter($row))
continue;
$data = [];
foreach ($columnIndexes as $dbCol => $excel_column_index) {
$data[$dbCol] = isset($row[$excel_column_index]) ? trim($row[$excel_column_index]) : null;
}
if (!$isAdmin) {
$data['verein'] = $selectedverein;
} else {
if (!in_array($data['verein'], $vereine)) {
echo json_encode(['success' => false, 'message' => $data['verein'] . " existiert nicht im System"]);
http_response_code(400);
exit;
}
}
$raw = trim($data['geburtsdatum']);
$temp = DateTime::createFromFormat('d.m.Y', $raw);
if ($temp && $temp->format('d.m.Y') === $raw) {
$data['geburtsdatum'] = $temp->format('Y-m-d');
} else {
$data['geburtsdatum'] = substr($raw, 0, 10);
}
if (is_array($programme) && !(in_array($data['programm'], $programme))) {
echo json_encode(['success' => false, 'message' => "Programm '{$data['programm']}' nicht valide bei " . $personEinzahl . " " . $data['name'] . " " . $data['vorname']]);
http_response_code(400);
exit;
}
$values = array_values($data);
$stmt->bind_param($types, ...$values);
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => "Fehler beim Einfügen eines Datensatzes"]);
http_response_code(400);
exit;
}
}
$stmt->close();
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
echo json_encode(['success' => false, 'message' => "Fehler beim Einfügen der Datensätze"]);
http_response_code(400);
exit;
}
echo json_encode(['success' => true, 'message' => count($rows) . " Datensätze importiert"]);
http_response_code(200);
exit;
@@ -47,7 +47,7 @@ foreach ($arrayids as $id) {
exit;
}
$sql = "SELECT bezahlt FROM $tableTurnerinnen WHERE id = ?";
$sql = "SELECT bezahlt FROM $tableTeilnehmende WHERE id = ?";
$checkstmt = $mysqli->prepare($sql);
$checkstmt->bind_param("i", $id);
@@ -108,7 +108,7 @@ if (!$success) {
// Prepare statements once
$sSql = "SELECT `name`, `vorname`, `programm`
FROM $tableTurnerinnen
FROM $tableTeilnehmende
WHERE id = ?";
$pSql = "SELECT preis
@@ -0,0 +1,91 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir))
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session();
check_user_permission('trainer');
verify_csrf();
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$type = 'tr';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true) {
echo 'Critical DB Error.';
exit;
}
$personen_ids_json = $_POST['personen_ids'] ?? '';
$personen_ids = json_decode($personen_ids_json) ?? [];
$personen_ids = !is_array($personen_ids) ? array(intval($personen_ids)) : array_map('intval', $personen_ids);
if (count($personen_ids) < 1) {
echo json_encode(['success' => false, 'message' => "Keine Personen angegeben"]);
http_response_code(422);
exit;
}
$userid = intval($_SESSION['user_id_trainer'] ?? 0);
$arrayfreigaben = [];
if ($userid > 0) {
$freigabe_json = db_get_var($mysqli, "SELECT freigabe FROM $tableInternUsers WHERE id = ?", [$userid]);
if (is_string($freigabe_json) && $freigabe_json !== '') {
$arrayfreigaben = json_decode($freigabe_json, true) ?: [];
$arrayfreigaben = $arrayfreigaben['freigabenTrainer'] ?? [];
}
}
if (!empty($arrayfreigaben)) {
$selectedverein = $_SESSION['selectedFreigabeTrainer'] ?? '';
if (!in_array($selectedverein, $arrayfreigaben)) {
$selectedverein = $arrayfreigaben[0];
$_SESSION['selectedFreigabeTrainer'] = $selectedverein;
}
}
$isAdmin = $selectedverein === "admin";
$mysqli->begin_transaction();
try {
if (!$isAdmin) {
$stmt = $mysqli->prepare("DELETE FROM $tableTeilnehmende WHERE `id` = ? AND `verein` = ?");
foreach ($personen_ids as $s_id) {
$stmt->bind_param("is", $s_id, $selectedverein);
$stmt->execute();
}
} else {
$stmt = $mysqli->prepare("DELETE FROM $tableTeilnehmende WHERE `id` = ?");
foreach ($personen_ids as $s_id) {
$stmt->bind_param("i", $s_id);
$stmt->execute();
}
}
$stmt->close();
$mysqli->commit();
} catch (Exception $e) {
$mysqli->rollback();
echo json_encode(['success' => false, 'message' => "Datenbankfehler beim Löschen"]);
http_response_code(500);
exit;
}
echo json_encode(['success' => true, 'message' => count($personen_ids) . " Datensätze gelöscht"]);
http_response_code(200);
exit;
@@ -70,7 +70,7 @@ require $baseDir . '/../scripts/db/db-tables.php';
$sql = "SELECT bi.id AS basket_id, bi.item_id, p.programm AS programm_name, p.preis, bi.id
FROM $tableBasketItems bi
LEFT JOIN $tableTurnerinnen t ON bi.item_id = t.id
LEFT JOIN $tableTeilnehmende t ON bi.item_id = t.id
LEFT JOIN $tableProgramme p ON p.programm = t.programm
WHERE bi.user_id = ?";
@@ -12,9 +12,13 @@ check_user_permission('trainer');
verify_csrf();
if (!isset($_POST['musicId']) || !isset($_POST['turnerinId']) || intval($_POST['musicId']) < 1 || intval($_POST['turnerinId']) < 1) {
echo json_encode(['success' => false]);
http_response_code(422);
$audiofileId = intval($_POST['musicId'] ?? 0);
$personId = intval($_POST['turnerinId'] ?? 0);
$geraetId = intval($_POST['geraetId'] ?? 0);
if ($audiofileId < 1 || $personId < 1 || $geraetId < 1) {
echo json_encode(['success' => false, 'message' => 'Ungültige Eingabe.']);
http_response_code(400);
exit;
}
@@ -30,13 +34,10 @@ if ($data['success'] === false){
require $baseDir . '/../scripts/db/db-tables.php';
$musicId = intval($_POST['musicId']);
$turnerinId = intval($_POST['turnerinId']);
$sql = "UPDATE $tableTurnerinnen SET bodenmusik = ? WHERE id = ?";
$sql = "INSERT INTO $tableTeilnehmendeAudiofiles (`person_id`, `geraet_id`, `audiofile_id`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `audiofile_id` = VALUES(`audiofile_id`)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ss", $musicId, $turnerinId);
$stmt->bind_param("iii", $personId, $geraetId, $audiofileId);
if (!$stmt->execute()) {
http_response_code(500);
@@ -45,7 +46,7 @@ if (!$stmt->execute()) {
$stmt->close();
$sql = "SELECT name, vorname FROM $tableTurnerinnen WHERE id = ?";
$sql = "SELECT name, vorname FROM $tableTeilnehmende WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("s", $turnerinId);
@@ -82,6 +82,9 @@ $rechnungenOrt = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name`
$rechnungenIBAN = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rechnungenIBAN']);
$linkWebseite = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['linkWebseite']);
$rechnungenPostversand = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rechnungenPostversand']);
$rechnungenPostversandKosten = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rechnungenPostversandKosten']);
$dbresult = $dbresult[0];
@@ -222,7 +225,7 @@ $totalPreis = 0.00;
$dbdata = [];
foreach ($turnerinnnenIds as $singleid){
$newdbresult = db_select($mysqli, $tableTurnerinnen, 'name, vorname, programm, verein', 'id = ?', [$singleid]);
$newdbresult = db_select($mysqli, $tableTeilnehmende, 'name, vorname, programm, verein', 'id = ?', [$singleid]);
if ($newdbresult && count($newdbresult) === 1){
$dbdata[$singleid] = $newdbresult;
$pdf->SetFont('', '', 10);
@@ -322,26 +325,25 @@ foreach ($dbdata as $singleid => $newdbresult){
$pdf->SetDrawColor(0, 0, 0);
}
/*
if (isset($_POST['postversand'])) {
if ($rechnungenPostversand && isset($_POST['postversand'])) {
$pdf->SetFont('', '', 10);
$text = 'Postversand der Rechnung durch WKVS';
$text = 'Postversand der Rechnung';
$pdf->Cell($columns['name']['max_width'], 10, $text, 0, 0, 'L');
$pdf->Cell($columns['programm']['max_width'], 10, '', 0, 0, 'L');
$pdf->Cell($columns['verein']['max_width'], 10, '', 0, 0, 'L');
$pdf->SetFillColor(100, 100, 100);
$pdf->Cell($columns['preis']['max_width'], 10, 'CHF 2.50', 0, 1, 'C');
$pdf->Cell($columns['preis']['max_width'], 10, 'CHF '. number_format(floatval($rechnungenPostversandKosten ?? 0), 2), 0, 1, 'C');
$pdf->SetDrawColor(100, 100, 100);
$pdf->Line(15, $pdf->getY(), 210 - 15, $pdf->getY());
$pdf->SetDrawColor(0, 0, 0);
$totalPreis += 2.5;
$totalPreis += floatval($rechnungenPostversandKosten ?? 0);
}
*/
if ($totalPreis !== 0) {
require __DIR__ . '/ajax-neu_qr_rechnung.php';
-13
View File
@@ -1,13 +0,0 @@
const texts = document.querySelectorAll('.textWelcomeScreen');
texts.forEach(text => {
const characters = text.textContent.split('');
text.textContent = '';
characters.forEach((char, index) => {
const span = document.createElement('span');
span.textContent = char;
span.style.animationDelay = `${index * 0.1}s`;
text.appendChild(span);
});
});
-24
View File
@@ -1,24 +0,0 @@
<link rel="stylesheet" href="/intern/css/wkvs.css">
<div class="welcomeScreen">
<div class="innerWelcomeScreen">
<p class="textWelcomeScreen title">WKVS</p>
<p class="textWelcomeScreen text">Kunstturnen</p>
</div>
</div>
<script>
const texts = document.querySelectorAll('.textWelcomeScreen');
texts.forEach(text => {
const characters = text.textContent.split('');
text.textContent = '';
characters.forEach((char, index) => {
const span = document.createElement('span');
span.textContent = char;
span.style.animationDelay = `${index * 0.1}s`;
text.appendChild(span);
});
});
</script>