From 73dc7afebb3afa5e32123c3c0b464cfc26de4d69 Mon Sep 17 00:00:00 2001 From: Linus Maximilian Nilson Date: Fri, 30 Jan 2026 14:32:11 +0000 Subject: [PATCH] Upload files to "includes" --- includes/admin-teilnehmer.php | 403 +++++++++++++++++++++++++++ includes/admin-zuteilungen.php | 487 +++++++++++++++++++++++++++++++++ 2 files changed, 890 insertions(+) create mode 100644 includes/admin-teilnehmer.php create mode 100644 includes/admin-zuteilungen.php diff --git a/includes/admin-teilnehmer.php b/includes/admin-teilnehmer.php new file mode 100644 index 0000000..b4caf65 --- /dev/null +++ b/includes/admin-teilnehmer.php @@ -0,0 +1,403 @@ +prefix; + kc_admin_tabs('kc_teilnehmer'); + // Alle Wahlen + Workshops für Dropdowns und Validierung + $all_wahlen = $wpdb->get_results("SELECT id, name, anzahl_einheiten FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY id DESC"); + $all_workshops = $wpdb->get_results("SELECT id, name FROM {$prefix}kc_workshops ORDER BY name"); + + // Map of workshop id => name for quick lookup in overview + $workshops_map = []; + if (!empty($all_workshops)) { + foreach ($all_workshops as $ws) { + $workshops_map[intval($ws->id)] = $ws->name; + } + } + + // Build map of wahl -> phases + workshops (for JS) + $wahl_map = []; + foreach($all_wahlen as $w) { + $wahl_map[intval($w->id)] = ['phases' => max(1,intval($w->anzahl_einheiten)), 'workshops' => []]; + } + if (!empty($all_workshops)) { + foreach($all_workshops as $ws) { + $ww = $wpdb->get_col($wpdb->prepare("SELECT wahl_id FROM {$prefix}kc_wahl_workshops WHERE workshop_id=%d", $ws->id)); + if (!empty($ww)) { + foreach($ww as $wid) { + if (isset($wahl_map[intval($wid)])) { + $wahl_map[intval($wid)]['workshops'][] = ['id'=>intval($ws->id),'name'=>$ws->name]; + } + } + } + } + } + + // Teilnehmer l�schen + if (isset($_GET['delete_teilnehmer'])) { + $tid = intval($_GET['delete_teilnehmer']); + $wpdb->delete("{$prefix}kc_teilnehmer", ['id' => $tid]); + echo '
Teilnehmer gel�scht!
'; + } + + // Teilnehmer speichern (neu/�ndern) + if (isset($_POST['kc_teilnehmer_save'])) { + // sanitize inputs + $vorname = sanitize_text_field($_POST['vorname']); + $nachname = sanitize_text_field($_POST['nachname']); + $wahl_id_post = intval($_POST['wahl_id']); + $phase_post = intval($_POST['phase']); + $w1 = intval($_POST['wunsch1']); + $w2 = intval($_POST['wunsch2']); + $w3 = intval($_POST['wunsch3']); + + // Server-side validation: duplicate name in same Wahl (exclude self on edit) + $norm_v = mb_strtolower(trim($vorname)); + $norm_n = mb_strtolower(trim($nachname)); + if (!empty($_POST['tid'])) { + $exclude_id = intval($_POST['tid']); + $exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d AND id<>%d", $norm_v, $norm_n, $wahl_id_post, $exclude_id)); + } else { + $exists = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d", $norm_v, $norm_n, $wahl_id_post)); + } + if ($exists && $exists > 0) { + echo '
Diese Kombination aus Vorname und Nachname existiert bereits für diese Wahl.
'; + } else { + // validate phase within wahl + $wahl_row = $wpdb->get_row($wpdb->prepare("SELECT anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d", $wahl_id_post)); + $max_ph = $wahl_row ? max(1,intval($wahl_row->anzahl_einheiten)) : 1; + if ($phase_post < 1 || $phase_post > $max_ph) { + echo '
Ungültige Phase für die gewählte Wahl.
'; + } else { + // validate that selected workshops belong to the chosen wahl (if mapping exists) + $valid_ws = []; + $ww_rows = $wpdb->get_col($wpdb->prepare("SELECT workshop_id FROM {$prefix}kc_wahl_workshops WHERE wahl_id=%d", $wahl_id_post)); + if (!empty($ww_rows)) foreach($ww_rows as $r) $valid_ws[] = intval($r); + // if mapping exists, enforce membership + $check_membership = function($wid) use ($valid_ws) { + if (empty($valid_ws)) return true; // no mapping -> allow + return in_array(intval($wid), $valid_ws); + }; + if (!$check_membership($w1) || !$check_membership($w2) || !$check_membership($w3)) { + echo '
Einer oder mehrere ausgewählte Workshops gehören nicht zur gewählten Wahl.
'; + } else { + $data = [ + 'vorname' => $vorname, + 'nachname' => $nachname, + 'wahl_id' => $wahl_id_post, + 'phase' => $phase_post, + 'wunsch1' => $w1, + 'wunsch2' => $w2, + 'wunsch3' => $w3 + ]; + if (!empty($_POST['tid'])) { + $wpdb->update("{$prefix}kc_teilnehmer", $data, ['id'=>intval($_POST['tid'])]); + echo '
Teilnehmer aktualisiert!
'; + } else { + $wpdb->insert("{$prefix}kc_teilnehmer", $data); + echo '
Teilnehmer angelegt!
'; + } + } + } + } + } + + // Teilnehmer bearbeiten + if (isset($_GET['edit_teilnehmer'])) { + $tid = intval($_GET['edit_teilnehmer']); + $tn = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teilnehmer WHERE id=%d", $tid)); + echo '
'; + echo '

Teilnehmer bearbeiten

'; + echo '
'; + echo ''; + echo ''; + echo ''; + + // Wahl select + echo ''; + echo ''; + + // Phase select (fallback content) + echo ''; + echo ''; + + // Wunsch selects + for ($k=1;$k<=3;$k++){ + $cur = intval($tn->{'wunsch'.$k}); + echo ''; + echo ''; + } + + echo ''; + echo 'Abbrechen'; + echo '
'; + + // Provide JS map and init JS: restrict phases/workshops based on Wahl and init Select2 + echo ''; + echo ''; + echo '
'; + return; + } + + // Neuer Teilnehmer anlegen + if (isset($_GET['new'])) { + echo '
'; + echo '

Neuen Teilnehmer anlegen

'; + echo '
'; + echo ''; + echo ''; + + echo ''; + echo ''; + + echo ''; + echo ''; + + for ($k=1;$k<=3;$k++){ + echo ''; + echo ''; + } + + echo ''; + echo 'Abbrechen'; + echo '
'; + // Init Select2 for nicer selects if available + echo ''; + echo '
'; + return; + } + + // Übersicht + // Map für Wahl-ID => Name + $wahl_name_map = []; + foreach($all_wahlen as $w) { + $wahl_name_map[intval($w->id)] = $w->name; + } + echo '
'; + echo '

Alle Teilnehmer

'; + echo '+ Neuer Teilnehmer'; + + // Wahl-Filter-Buttons sortiert nach KC1, KC2, KC3, dann Rest + $kc_buttons = []; + $rest_buttons = []; + foreach($all_wahlen as $w) { + $name = strtoupper($w->name); + if ($name === 'KC1' || $name === 'KC2' || $name === 'KC3') { + $kc_buttons[$name] = $w; + } else { + $rest_buttons[] = $w; + } + } + echo '
' + .''; + echo ''; + foreach(["KC1","KC2","KC3"] as $kc) { + if(isset($kc_buttons[$kc])) { + $w = $kc_buttons[$kc]; + echo ''; + } + } + foreach($rest_buttons as $w) { + echo ''; + } + echo 'Alle anzeigen'; + echo '
'; + + // Platzhalter für Phasen-Filter + echo ''; + + // Teilnehmer laden und gruppieren nach Wahl und Phase + $teilnehmer = $wpdb->get_results("SELECT * FROM {$prefix}kc_teilnehmer ORDER BY wahl_id, phase, nachname, vorname"); + $gruppen = []; + foreach ($teilnehmer as $tn) { + $wid = intval($tn->wahl_id); + $ph = intval($tn->phase); + $gruppen[$wid][$ph][] = $tn; + } + + foreach ($gruppen as $wid => $phasen) { + $wahl_disp = isset($wahl_name_map[$wid]) ? esc_html($wahl_name_map[$wid]) : $wid; + // Gesamtanzahl Teilnehmer für diese Wahl berechnen + $gesamt = 0; + foreach ($phasen as $tns) $gesamt += count($tns); + echo '
'; + echo ''. $wahl_disp . ' (' . $gesamt . ' TN)'; + foreach ($phasen as $phase => $tns) { + echo '
'; + echo 'Phase '.intval($phase).' ('.count($tns).' TN)'; + echo ''; + echo ''; + foreach ($tns as $tn) { + $w1_id = intval($tn->wunsch1); + $w2_id = intval($tn->wunsch2); + $w3_id = intval($tn->wunsch3); + $w1_disp = $w1_id && isset($workshops_map[$w1_id]) ? esc_html($workshops_map[$w1_id]) : ($w1_id ? intval($w1_id) : 'Keine'); + $w2_disp = $w2_id && isset($workshops_map[$w2_id]) ? esc_html($workshops_map[$w2_id]) : ($w2_id ? intval($w2_id) : 'Keine'); + $w3_disp = $w3_id && isset($workshops_map[$w3_id]) ? esc_html($workshops_map[$w3_id]) : ($w3_id ? intval($w3_id) : 'Keine'); + $wahl_disp = isset($wahl_name_map[intval($tn->wahl_id)]) ? esc_html($wahl_name_map[intval($tn->wahl_id)]) : intval($tn->wahl_id); + echo " + + + + + + + + + "; + } + echo '
VornameNachnameWahlPhaseWunsch 1Wunsch 2Wunsch 3Aktion
".esc_html($tn->vorname)."".esc_html($tn->nachname)."".$wahl_disp."".intval($tn->phase)."".$w1_disp."".$w2_disp."".$w3_disp." + Bearbeiten + Loeschen +
'; + echo '
'; + } + echo '
'; + } + // JS für Wahl- und Phasen-Filter + echo ''; + echo ''; + echo '
'; +} +?> \ No newline at end of file diff --git a/includes/admin-zuteilungen.php b/includes/admin-zuteilungen.php new file mode 100644 index 0000000..d64fc89 --- /dev/null +++ b/includes/admin-zuteilungen.php @@ -0,0 +1,487 @@ +prefix; + $wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id={$wahl_id}"); + if (!$wahl) exit('Wahl nicht gefunden'); + + // Workshops für diese Wahl holen + $workshops = $wpdb->get_results( + "SELECT ws.id, ws.name FROM {$prefix}kc_workshops ws JOIN {$prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id WHERE ww.wahl_id = ".intval($wahl_id) + ); + $ws_names = []; + foreach($workshops as $ws) $ws_names[$ws->id] = $ws->name; + + // Zuteilungen holen + $rows = $wpdb->get_results("SELECT * FROM {$prefix}kc_zuteilung WHERE wahl_id=".intval($wahl_id)); + + // Phasen bestimmen + $phases = []; + foreach($rows as $row) { + if ($row->phase === null || $row->phase === '') continue; + $phases[intval($row->phase)] = true; + } + $phases = array_keys($phases); + sort($phases, SORT_NUMERIC); + + // Teamer pro Workshop/Phase (wie frontend-ergebnis) + $ws_teamers = []; + $ws_ids = array_keys($ws_names); + if (!empty($ws_ids)) { + foreach($ws_ids as $ws_id) { + foreach($phases as $phase) { + $trows = $wpdb->get_results($wpdb->prepare( + "SELECT t.vorname, t.nachname FROM {$prefix}kc_workshop_teamer wt ". + "JOIN {$prefix}kc_teamer t ON wt.teamer_id = t.id ". + "WHERE wt.workshop_id = %d AND wt.wahl_id = %d AND wt.phase = %d", + $ws_id, $wahl_id, $phase + )); + if (empty($trows)) { + $trows = $wpdb->get_results($wpdb->prepare( + "SELECT t.vorname, t.nachname FROM {$prefix}kc_workshop_teamer wt ". + "JOIN {$prefix}kc_teamer t ON wt.teamer_id = t.id ". + "WHERE wt.workshop_id = %d AND wt.wahl_id = %d AND wt.phase IS NULL", + $ws_id, $wahl_id + )); + } + if (empty($trows)) { + $trows = $wpdb->get_results($wpdb->prepare( + "SELECT t.vorname, t.nachname FROM {$prefix}kc_workshop_teamer wt ". + "JOIN {$prefix}kc_teamer t ON wt.teamer_id = t.id ". + "WHERE wt.workshop_id = %d AND wt.wahl_id IS NULL AND wt.phase IS NULL", + $ws_id + )); + } + if (!empty($trows)) { + $key = intval($ws_id) . '_' . intval($phase); + $ws_teamers[$key] = []; + foreach ($trows as $tr) { + $ws_teamers[$key][] = trim($tr->vorname.' '.$tr->nachname); + } + } + } + } + } + + // Gruppieren und sortieren wie im Frontend: erst nach Phase, dann Workshop, dann Teilnehmer + $workshop_groups = []; + $phases = []; + foreach($rows as $row) { + if ($row->phase === null || $row->phase === '') continue; + $phaseNum = intval($row->phase); + $phases[$phaseNum] = true; + if ($row->workshop_id && isset($ws_names[$row->workshop_id])) { + $workshop_groups[$phaseNum][$row->workshop_id][] = $row; + } + } + $phases = array_keys($phases); + sort($phases, SORT_NUMERIC); + + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename="kc_zuteilung_wahl_'.$wahl_id.'.csv"'); + // Schreibe UTF-8 BOM, damit Excel Umlaute korrekt erkennt + echo "\xEF\xBB\xBF"; + $output = fopen('php://output', 'w'); + // Excel erwartet in DE meist ; als Trennzeichen + $delimiter = ';'; + fputcsv($output, ['Phase','Workshop','Teamer','Teilnehmer'], $delimiter); + foreach($phases as $phase) { + if (empty($workshop_groups[$phase])) continue; + $display = []; + foreach($ws_names as $ws_id => $ws_name) { + $teilnehmer = isset($workshop_groups[$phase][$ws_id]) ? $workshop_groups[$phase][$ws_id] : []; + $count = count($teilnehmer); + if ($count === 0) continue; + $display[] = [ + 'id' => $ws_id, + 'name' => $ws_name, + 'teilnehmer' => $teilnehmer, + 'count' => $count + ]; + } + usort($display, function($a, $b) { return $a['count'] - $b['count']; }); + foreach($display as $d) { + $ws_id = $d['id']; + $ws_name = $d['name']; + $teilnehmer = $d['teilnehmer']; + $teamer_key = intval($ws_id) . '_' . intval($phase); + $teamer_str = !empty($ws_teamers[$teamer_key]) ? implode(', ', $ws_teamers[$teamer_key]) : ''; + usort($teilnehmer, function($a, $b) { + return strcmp($a->nachname . $a->vorname, $b->nachname . $b->vorname); + }); + foreach($teilnehmer as $t) { + $vorname = preg_replace('/<[^>]*>/', '', $t->vorname); + $nachname = preg_replace('/<[^>]*>/', '', $t->nachname); + $teilnehmer_name = trim($vorname . ', ' . $nachname); + $ws_name_clean = preg_replace('/<[^>]*>/', '', $ws_name); + $teamer_str_clean = preg_replace('/<[^>]*>/', '', $teamer_str); + fputcsv($output, [ + $phase, + $ws_name_clean, + $teamer_str_clean, + $teilnehmer_name, + $wunsch + ], $delimiter); + } + } + } + fclose($output); + exit; +} +function kc_zeige_zuteilung($wahl_id) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($wahl_id); + $wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id={$wahl_id}"); + if (!$wahl) return; + + // Workshops dieser Wahl holen + $workshops = $wpdb->get_results( + "SELECT ws.id, ws.name + FROM {$prefix}kc_workshops ws + JOIN {$prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id + WHERE ww.wahl_id = " . $wahl_id + ); + $ws_names = []; + foreach($workshops as $ws) $ws_names[$ws->id] = $ws->name; + + // Zuteilungen holen + $rows = $wpdb->get_results( + "SELECT * FROM {$prefix}kc_zuteilung WHERE wahl_id=" . $wahl_id + ); + + $workshop_groups = []; + $nicht_zugeteilt = []; + foreach($rows as $row) { + if ($row->workshop_id && isset($ws_names[$row->workshop_id])) { + $workshop_groups[$row->workshop_id][] = $row; + } else { + $nicht_zugeteilt[] = $row; + } + } + uasort($workshop_groups, function($a, $b) { + return count($a) - count($b); + }); + + echo '
'; + echo '

Zuteilung: '.esc_html($wahl->name).'

'; + + // Anzahl Phasen der Wahl + $phasen = intval($wahl->anzahl_einheiten); + if ($phasen < 1) $phasen = 1; + + // Für jede Phase eine einklappbare Sektion + for ($phase = 1; $phase <= $phasen; $phase++) { + echo '
'; + echo 'Phase '.intval($phase).'   (' . intval(count(array_filter($rows, function($r) use ($phase){ return intval($r->phase) === $phase; }))) . ' TN)'; + + // Innerhalb der Phase: zeige pro Workshop die Teilnehmer (falls vorhanden) + foreach ($ws_names as $ws_id => $ws_name) { + // Filtere Teilnehmer dieser Phase & dieses Workshops + $filtered = array_filter($rows, function($r) use ($phase, $ws_id) { + return intval($r->phase) === $phase && intval($r->workshop_id) === intval($ws_id); + }); + if (empty($filtered)) continue; + + echo '
'; + echo ''.esc_html($ws_name).' (' . count($filtered) . ' TN)
'; + echo ''; + echo ''; + foreach ($filtered as $t) { + if ($t->wunsch_rang == 0) { + $wunsch = "Vorzuteilung"; + } elseif ($t->wunsch_rang == -1) { + $wunsch = "zugelost"; + } elseif ($t->wunsch_rang == 99) { + $wunsch = "Zulosung"; + } elseif ($t->wunsch_rang > 0) { + $wunsch = intval($t->wunsch_rang) . '.'; + } else { + $wunsch = ''; + } + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
NameWunschWorkshop
'.esc_html($t->vorname.' '.$t->nachname).''.$wunsch.''.esc_html($ws_name).'
'; + } + + // Teilnehmer dieser Phase, die keinem Workshop zugeordnet sind + $unassigned_phase = array_filter($rows, function($r) use ($phase) { return intval($r->phase) === $phase && (empty($r->workshop_id) || !intval($r->workshop_id)); }); + if (!empty($unassigned_phase)) { + echo '
'; + echo 'Nicht zugeteilt (Phase '.intval($phase).'):
    '; + foreach($unassigned_phase as $t) { + echo '
  • '.esc_html($t->vorname.' '.$t->nachname).'
  • '; + } + echo '
'; + } + + echo '
'; + } + + if (!empty($nicht_zugeteilt)) { + echo '
'; + echo 'Nicht zugeteilt:
    '; + foreach($nicht_zugeteilt as $t) { + echo '
  • '.esc_html($t->vorname.' '.$t->nachname).' (Phase '.intval($t->phase).')
  • '; + } + echo '
'; + } + echo '
'; +} + +function kc_zuteilungen_page() { + global $wpdb; + $prefix = $wpdb->prefix; + kc_admin_tabs('kc_zuteilungen'); + + // Aktionen: Zuteilung ausführen oder löschen (per Wahl) + if (isset($_GET['run_zuteilung'])) { + $run_id = intval($_GET['run_zuteilung']); + if ($run_id) { + // Delegiere an die Zuteilungslogik + if (function_exists('kc_run_zuteilung')) { + kc_run_zuteilung($run_id); + echo '
Zuteilung für Wahl ID '.intval($run_id).' ausgeführt.
'; + echo ""; + return; + } else { + echo '
Die Zuteilungsfunktion ist nicht verfügbar.
'; + } + } + } + + if (isset($_GET['delete_zuteilung'])) { + if ($del_id) { + $wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $del_id]); + echo '
Zuteilungen für Wahl ID '.intval($del_id).' wurden gelöscht.
'; + echo ""; + return; + } + } + + // Übersicht: Zeige Zuteilungen für ALLE Wahlen + echo '
'; + echo '

Alle Zuteilungen

'; + $wahlen = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY id DESC"); + foreach($wahlen as $wahl) { + $run_url = add_query_arg('run_zuteilung', $wahl->id, admin_url('admin.php?page=kc_zuteilungen')); + $del_url = add_query_arg('delete_zuteilung', $wahl->id, admin_url('admin.php?page=kc_zuteilungen')); + $details_url = add_query_arg('show_zuteilung', $wahl->id, admin_url('admin.php?page=kc_wahlen')); + $csv_url = add_query_arg(['export_csv' => $wahl->id], admin_url('admin.php?page=kc_zuteilungen')); + // Workshops & Zuteilungen für diese Wahl vorbereiten + $workshops = $wpdb->get_results( + "SELECT ws.id, ws.name + FROM {$prefix}kc_workshops ws + JOIN {$prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id + WHERE ww.wahl_id = ".intval($wahl->id) + ); + $ws_names = []; + foreach($workshops as $ws) $ws_names[$ws->id] = $ws->name; + + // Zuteilungen holen + $rows = $wpdb->get_results( + "SELECT * FROM {$prefix}kc_zuteilung WHERE wahl_id=".intval($wahl->id) + ); + + // Gesamt-Details pro Wahl: einklappbar + echo '
'; + echo ''.esc_html($wahl->name).' ('.count($rows).' TN)'; + echo ' '; + echo 'Details '; + echo 'Zuteilung ausführen '; + echo 'Zuteilungen löschen '; + echo 'CSV Export'; + echo ''; + echo ''; +// --- CSV Export Handler --- +if (isset($_GET['export_csv']) && current_user_can('manage_options')) { + $wahl_id = intval($_GET['export_csv']); + global $wpdb; + $prefix = $wpdb->prefix; + $wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id={$wahl_id}"); + if (!$wahl) exit('Wahl nicht gefunden'); + + // Workshops für diese Wahl holen + $workshops = $wpdb->get_results( + "SELECT ws.id, ws.name FROM {$prefix}kc_workshops ws JOIN {$prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id WHERE ww.wahl_id = ".intval($wahl_id) + ); + $ws_names = []; + foreach($workshops as $ws) $ws_names[$ws->id] = $ws->name; + + // Zuteilungen holen + $rows = $wpdb->get_results("SELECT * FROM {$prefix}kc_zuteilung WHERE wahl_id=".intval($wahl_id)); + + // Phasen bestimmen + $phases = []; + foreach($rows as $row) { + if ($row->phase === null || $row->phase === '') continue; + $phases[intval($row->phase)] = true; + } + $phases = array_keys($phases); + sort($phases, SORT_NUMERIC); + + // CSV Header + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename="kc_zuteilung_wahl_'.$wahl_id.'.csv"'); + $output = fopen('php://output', 'w'); + // Kopfzeile + fputcsv($output, ['Phase','Workshop','Teamer','Teilnehmer Vorname','Teilnehmer Nachname']); + + // Teamer pro Workshop/Phase (wie frontend-ergebnis) + $ws_teamers = []; + $ws_ids = array_keys($ws_names); + if (!empty($ws_ids)) { + foreach($ws_ids as $ws_id) { + foreach($phases as $phase) { + $trows = $wpdb->get_results($wpdb->prepare( + "SELECT t.vorname, t.nachname FROM {$prefix}kc_workshop_teamer wt ". + "JOIN {$prefix}kc_teamer t ON wt.teamer_id = t.id ". + "WHERE wt.workshop_id = %d AND wt.wahl_id = %d AND wt.phase = %d", + $ws_id, $wahl_id, $phase + )); + if (empty($trows)) { + $trows = $wpdb->get_results($wpdb->prepare( + "SELECT t.vorname, t.nachname FROM {$prefix}kc_workshop_teamer wt ". + "JOIN {$prefix}kc_teamer t ON wt.teamer_id = t.id ". + "WHERE wt.workshop_id = %d AND wt.wahl_id = %d AND wt.phase IS NULL", + $ws_id, $wahl_id + )); + } + if (empty($trows)) { + $trows = $wpdb->get_results($wpdb->prepare( + "SELECT t.vorname, t.nachname FROM {$prefix}kc_workshop_teamer wt ". + "JOIN {$prefix}kc_teamer t ON wt.teamer_id = t.id ". + "WHERE wt.workshop_id = %d AND wt.wahl_id IS NULL AND wt.phase IS NULL", + $ws_id + )); + } + if (!empty($trows)) { + $key = intval($ws_id) . '_' . intval($phase); + $ws_teamers[$key] = []; + foreach ($trows as $tr) { + $ws_teamers[$key][] = trim($tr->vorname.' '.$tr->nachname); + } + } + } + } + } + + // Gruppieren und sortieren wie im Frontend: erst nach Phase, dann Workshop, dann Teilnehmer + // 1. Gruppieren nach Phase und Workshop + $workshop_groups = []; + $phases = []; + foreach($rows as $row) { + if ($row->phase === null || $row->phase === '') continue; + $phaseNum = intval($row->phase); + $phases[$phaseNum] = true; + if ($row->workshop_id && isset($ws_names[$row->workshop_id])) { + $workshop_groups[$phaseNum][$row->workshop_id][] = $row; + } + } + $phases = array_keys($phases); + sort($phases, SORT_NUMERIC); + // 2. Pro Phase, pro Workshop (nur mit Teilnehmern), sortiert nach Teilnehmerzahl aufsteigend + foreach($phases as $phase) { + if (empty($workshop_groups[$phase])) continue; + // Workshops mit Teilnehmern in dieser Phase + $display = []; + foreach($ws_names as $ws_id => $ws_name) { + $teilnehmer = isset($workshop_groups[$phase][$ws_id]) ? $workshop_groups[$phase][$ws_id] : []; + $count = count($teilnehmer); + if ($count === 0) continue; + $display[] = [ + 'id' => $ws_id, + 'name' => $ws_name, + 'teilnehmer' => $teilnehmer, + 'count' => $count + ]; + } + // Sortieren nach Teilnehmerzahl aufsteigend + usort($display, function($a, $b) { return $a['count'] - $b['count']; }); + foreach($display as $d) { + $ws_id = $d['id']; + $ws_name = $d['name']; + $teilnehmer = $d['teilnehmer']; + $teamer_key = intval($ws_id) . '_' . intval($phase); + $teamer_str = !empty($ws_teamers[$teamer_key]) ? implode(', ', $ws_teamers[$teamer_key]) : ''; + // Teilnehmer sortieren (optional: nach Name) + usort($teilnehmer, function($a, $b) { + return strcmp($a->nachname . $a->vorname, $b->nachname . $b->vorname); + }); + foreach($teilnehmer as $t) { + $vorname = preg_replace('/<[^>]*>/', '', $t->vorname); + $nachname = preg_replace('/<[^>]*>/', '', $t->nachname); + $ws_name_clean = preg_replace('/<[^>]*>/', '', $ws_name); + $teamer_str_clean = preg_replace('/<[^>]*>/', '', $teamer_str); + $wunsch = $t->wunsch_rang == 0 ? "Vorzuteilung" : ($t->wunsch_rang > 0 ? intval($t->wunsch_rang).'.' : ($t->wunsch_rang==-1?"zugelost":"")); + $zuteilung = $t->wunsch_rang == 0 ? 'Vorzuteilung' : ($t->wunsch_rang==-1?'Zulosung':'Wunsch'); + fputcsv($output, [ + $phase, + $ws_name_clean, + $teamer_str_clean, + $vorname, + $nachname, + $wunsch, + $zuteilung + ]); + } + } + } + fclose($output); + exit; +} + + // Anzahl Phasen der Wahl + $phasen = intval($wahl->anzahl_einheiten); + if ($phasen < 1) $phasen = 1; + + for ($phase = 1; $phase <= $phasen; $phase++) { + echo '
'; + echo 'Phase '.intval($phase).'   (' . intval(count(array_filter($rows, function($r) use ($phase){ return intval($r->phase) === $phase; }))) . ' TN)'; + + // Für jede Workshop zeigen, falls Teilnehmer in dieser Phase zugeordnet sind + foreach ($ws_names as $ws_id => $ws_name) { + $filtered = array_filter($rows, function($r) use ($phase, $ws_id) { + return intval($r->phase) === $phase && intval($r->workshop_id) === intval($ws_id); + }); + if (empty($filtered)) continue; + echo '
'; + echo ''.esc_html($ws_name).' (' . count($filtered) . ' TN)
'; + echo ''; + echo ''; + foreach ($filtered as $t) { + $wunsch = $t->wunsch_rang == 0 ? "Vorzuteilung" : ($t->wunsch_rang > 0 ? intval($t->wunsch_rang).'.' : ($t->wunsch_rang==-1?"zugelost":"")); + echo ''; + echo ''; + echo ''; + echo ''; + echo ''; + } + echo '
NameWunschWorkshop
'.esc_html($t->vorname.' '.$t->nachname).''.$wunsch.''.esc_html($ws_name).'
'; + } + + // Teilnehmer dieser Phase ohne Workshop + $unassigned_phase = array_filter($rows, function($r) use ($phase) { return intval($r->phase) === $phase && (empty($r->workshop_id) || !intval($r->workshop_id)); }); + if (!empty($unassigned_phase)) { + echo '
'; + echo 'Nicht zugeteilt (Phase '.intval($phase).'):
    '; + foreach($unassigned_phase as $t) { + echo '
  • '.esc_html($t->vorname.' '.$t->nachname).'
  • '; + } + echo '
'; + } + + echo '
'; + } + + echo '
'; + } + echo '
'; +} +?> \ No newline at end of file