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

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

View File

@@ -0,0 +1,89 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'wkl';
$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';
$allowedTypes = [
'wkName',
'displayColourLogo',
'displayTextColourLogo',
'displayColorScoringBg',
'displayColorScoringBgSoft',
'displayColorScoringPanel',
'displayColorScoringPanelSoft',
'displayColorScoringPanelText',
'displayColorScoringPanelTextSoft',
'displayColorScoringPanelTextNoteL',
'displayColorScoringPanelTextNoteR',
'displayIdNoteL',
'displayIdNoteR',
'rechnungenName',
'rechnungenVorname',
'rechnungenStrasse',
'rechnungenHausnummer',
'rechnungenPostleitzahl',
'rechnungenOrt',
'rechnungenIBAN',
'maxLengthMusic',
'linkWebseite',
'rangNote',
'orderBestRang'
];
$type = $_POST['type'] ? trim($_POST['type']) : '';
if (!in_array($type, $allowedTypes)) {
echo json_encode(['success' => false, 'message' => 'Invalid input']);
exit;
}
$value = $_POST['value'] ? trim($_POST['value']) : null;
// ---------- Step 2: Get values from DB ----------
$stmt = $mysqli->prepare("INSERT INTO $tableVar (`name`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical db error']);
exit;
}
$stmt->bind_param("ss", $type, $value);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
// Return JSON
echo json_encode([
'success' => true
]);
exit;

View File

@@ -0,0 +1,84 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_functions.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'] ?? '');
$start_index = intval($_POST['start_index'] ?? 0);
$color = trim($_POST['color'] ?? '#424242');
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);
$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'] ?? '';
$allowedFields = ['name', 'start_index', 'color_kampfrichter'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'start_index') {
$value = intval($value);
}
$updated = db_update($mysqli, $tableGeraete, [$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, $tableGeraete, ['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.']);
}

View File

@@ -0,0 +1,261 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_functions.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;
}
$recalculateJSONs = false;
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$type_val = $_POST['type_val'] ?? 'input';
$berechnung = trim($_POST['berechnung'] ?? '');
$pro_geraet = intval($_POST['pro_geraet'] ?? 0);
$default_value = (isset($_POST['default_value'])) ? floatval($_POST['default_value']) : null;
$min_value = (isset($_POST['min_value'])) ? floatval($_POST['min_value']) : null;
$max_value = (isset($_POST['max_value'])) ? floatval($_POST['max_value']) : null;
$id = (isset($_POST['id'])) ? floatval($_POST['id']) : 1;
if (!$id) {
echo json_encode(['success' => false, 'message' => 'Id ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("SELECT 1 FROM $tableNotenBezeichnungen WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo json_encode([
'success' => false,
'message' => 'Eine Note mit dieser Id existiert bereits.'
]);
$stmt->close();
exit;
}
$stmt->close();
if (!$name) {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $tableNotenBezeichnungen (id, name, type, berechnung, pro_geraet, default_value, min_value, max_value) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->bind_param("isssisss", $id, $name, $type_val, $berechnung, $pro_geraet, $default_value, $min_value, $max_value);
$success = $stmt->execute();
$new_id = $mysqli->insert_id;
$stmt->close();
$recalculateJSONs = true;
if ($success) {
if (!$recalculateJSONs) { echo json_encode(['success' => true, 'id' => $new_id]); }
} else {
echo json_encode(['success' => false, 'message' => 'Fehler beim Hinzufügen.']);
exit;
}
} elseif ($action === 'update') {
$id = intval($_POST['id'] ?? 0);
$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'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'pro_geraet') {
$value = intval($value);
}
if ($field === 'berechnung' || $field === 'type') {
$recalculateJSONs = true;
}
$updated = db_update($mysqli, $tableNotenBezeichnungen, [$field => $value], ['id' => $id]);
if ($updated !== false) {
if (!$recalculateJSONs) { echo json_encode(['success' => true]); }
} else {
echo json_encode(['success' => false, 'message' => 'DB Update failed.']);
exit;
}
} else {
echo json_encode(['success' => false, 'message' => 'Invalid parameters.']);
exit;
}
} elseif ($action === 'delete') {
$id = intval($_POST['id'] ?? 0);
if ($id > 0) {
db_delete($mysqli, $tableNotenBezeichnungen, ['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.']);
exit;
}
if ($recalculateJSONs) {
$noten = db_select($mysqli, $tableNotenBezeichnungen, "id, berechnung, type");
// 1. Re-index the array so the keys match the database IDs
$notenById = array_column($noten, null, 'id');
$berechnungen = [];
foreach ($notenById as $id => $sn) {
if ($sn['type'] === 'berechnung') {
$berechnungen[] = $sn;
}
}
if (empty($berechnungen)) {
echo json_encode(['success' => true, 'message' => "Keine Berechnungen ausgewählt"]);
exit;
}
require $baseDir . "/../scripts/string-calculator/string-calculator-functions.php";
$notenRechner = new NotenRechner();
// 1. Build the direct map
// Format: [ Changed_Note_ID => [ "CalcId|GeraetId" => [CalcId, GeraetId] ] ]
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArray = $notenRechner->getBenoetigteIdsComplex($calc['berechnung']);
if (empty($neededIdsArray)) {
continue;
}
$calcId = (int)$calc['id'];
foreach ($neededIdsArray as $needed) {
$nId = (int)$needed['noteId'];
// Keep geraetId as integer if it's a number (e.g., 3), otherwise string ('S')
$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;
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
// Store it as the "little array" you requested: [DependentCalcId, GeraetId]
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId];
}
}
// 2. Our recursive helper function (Updated for complex nodes)
function getCompleteDependencyChain($id, $directMap, $visited = [])
{
// If this ID doesn't have anything depending on it, return empty
if (!isset($directMap[$id])) {
return [];
}
$allDependencies = [];
foreach ($directMap[$id] as $nodeKey => $complexNode) {
// CIRCULAR DEPENDENCY CHECK:
// We check against the string key (e.g., "10|S") to prevent infinite loops
if (isset($visited[$nodeKey])) {
continue;
}
// 1. Mark this specific node as visited
$visited[$nodeKey] = true;
// 2. Add the little array [CalcId, GeraetId] to our master list
$allDependencies[$nodeKey] = $complexNode;
// 3. Recursively find everything that depends on THIS calculation ID
// $complexNode[0] is the dependent Calc ID
$childDependencies = getCompleteDependencyChain($complexNode[0], $directMap, $visited);
// 4. Merge the child results into our master list safely
foreach ($childDependencies as $childKey => $childNode) {
$allDependencies[$childKey] = $childNode;
$visited[$childKey] = true; // Ensure the parent loop knows this was visited
}
}
return $allDependencies;
}
// 3. Create the final flattened map for ALL IDs
$flatDependencyMap = [];
foreach (array_keys($notenById) as $id) {
$chain = getCompleteDependencyChain($id, $dependencyMap);
// Only add it if dependencies exist
if (!empty($chain)) {
// array_values() removes the "10|S" string keys, turning it into a perfect
// 0-indexed array for clean JSON encoding: [[10, "S"], [12, 3]]
$flatDependencyMap[$id] = array_values($chain);
}
}
// 4. Database Updates
// Step 1: Reset all rows to NULL in a single query
$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);
// Bind parameters: 's' for string (JSON), 'i' for integer (ID)
$stmt->bind_param("si", $jsonString, $id);
$stmt->execute();
}
$stmt->close();
echo json_encode(['success' => true, 'message' => "Abhaengigkeiten berechnet"]);
exit;
}

View File

@@ -0,0 +1,204 @@
<?php
/**
* Upload Image API
* Accepts multipart/form-data POST with 'image' file.
* Converts JPG/PNG to WebP (resized to max 2400px), saves to /files/img/admin_upload/{uid}.webp
* Returns JSON: { success: true }
*/
// Allow large uploads and enough memory for GD processing
ini_set('memory_limit', '256M');
ini_set('upload_max_filesize', '50M');
ini_set('post_max_size', '55M');
ini_set('max_execution_time', '120');
const MAX_DIMENSION = 2400;
const MAX_DIMENSION_ICON = 1000;
const WEBP_QUALITY = 80;
$baseDir = $_SERVER['DOCUMENT_ROOT'];
header('Content-Type: application/json');
session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
$baseDir = $_SERVER['DOCUMENT_ROOT'];
// Only accept POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
exit;
}
$allowedTypes = ['Kampfrichter', 'Trainer', 'Wk_leitung', 'Otl', 'icon', 'logo-normal'];
if (!isset($_POST['type']) || !in_array($_POST['type'], $allowedTypes)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'Typ nicht angegeben oder nicht erlaubt']);
exit;
}
$pngs = ['icon', 'logo-normal'];
$type = $_POST['type'];
$isIcon = in_array($type, $pngs);
// Check if $_FILES is empty entirely (can happen if post_max_size exceeded)
if (empty($_FILES)) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'No files received. The file may exceed the server upload limit).',
]);
exit;
}
// Check specific file field
if (!isset($_FILES['image'])) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'No "image" field in upload. Received fields: ' . implode(', ', array_keys($_FILES)),
]);
exit;
}
if ($_FILES['image']['error'] !== UPLOAD_ERR_OK) {
http_response_code(400);
$errors = [
UPLOAD_ERR_INI_SIZE => 'File exceeds upload_max_filesize',
UPLOAD_ERR_FORM_SIZE => 'File exceeds form MAX_FILE_SIZE',
UPLOAD_ERR_PARTIAL => 'File was only partially uploaded',
UPLOAD_ERR_NO_FILE => 'No file was uploaded',
UPLOAD_ERR_NO_TMP_DIR => 'Missing temp directory',
UPLOAD_ERR_CANT_WRITE => 'Failed to write to disk',
];
$code = $_FILES['image']['error'];
$msg = $errors[$code] ?? "Unknown error code: $code";
echo json_encode(['success' => false, 'message' => $msg]);
exit;
}
$file = $_FILES['image'];
$tmpPath = $file['tmp_name'];
// Validate MIME type
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($tmpPath);
$allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
if (!in_array($mime, $allowedMimes, true)) {
http_response_code(415);
echo json_encode(['success' => false, 'message' => "Unsupported file type: $mime"]);
exit;
}
// Target directory
if ($isIcon) {
$uploadDir = $baseDir . '/intern/img/';
} else {
$uploadDir = $baseDir . '/intern/img/login';
}
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Load image into GD
$srcImage = null;
switch ($mime) {
case 'image/jpeg':
$srcImage = @imagecreatefromjpeg($tmpPath);
break;
case 'image/png':
$srcImage = @imagecreatefrompng($tmpPath);
break;
case 'image/webp':
$srcImage = @imagecreatefromwebp($tmpPath);
break;
case 'image/gif':
$srcImage = @imagecreatefromgif($tmpPath);
break;
}
if (!$srcImage) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Failed to load image into GD. It may be corrupt or too large.']);
exit;
}
// ── Resize if needed ────────────────────────────────
$origW = imagesx($srcImage);
$origH = imagesy($srcImage);
$maxDimension = ($isIcon) ? MAX_DIMENSION_ICON : MAX_DIMENSION;
if ($origW > $maxDimension || $origH > $maxDimension) {
// Calculate new dimensions keeping aspect ratio
if ($origW >= $origH) {
$newW = $maxDimension;
$newH = intval(round($origH * ($maxDimension / $origW)));
} else {
$newH = $maxDimension;
$newW = intval(round($origW * ($maxDimension / $origH)));
}
$resized = imagecreatetruecolor($newW, $newH);
// Preserve transparency
imagealphablending($resized, false);
imagesavealpha($resized, true);
$transparent = imagecolorallocatealpha($resized, 0, 0, 0, 127);
imagefill($resized, 0, 0, $transparent);
imagecopyresampled($resized, $srcImage, 0, 0, 0, 0, $newW, $newH, $origW, $origH);
imagedestroy($srcImage);
$srcImage = $resized;
} else {
// Still preserve transparency
imagepalettetotruecolor($srcImage);
imagealphablending($srcImage, true);
imagesavealpha($srcImage, true);
}
if ($isIcon) {
// ── Save as PNG ────────────────────────────────────
$filename = $type . '.png';
$destPath = $uploadDir . '/' . $filename;
$webPath = '/intern/img/' . $filename;
$pngCompression = 6;
$success = imagepng($srcImage, $destPath, $pngCompression);
} else {
// ── Save as WebP ────────────────────────────────────
$filename = 'bg' . $_POST['type'] . '.webp';
$destPath = $uploadDir . '/' . $filename;
$webPath = '/intern/img/admin_upload/' . $filename;
$success = imagewebp($srcImage, $destPath, WEBP_QUALITY);
}
imagedestroy($srcImage);
if (!$success) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'Failed to convert to WebP']);
exit;
}
echo json_encode([
'success' => true
]);