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 '
';
+
+ // 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 '
';
+ // 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 '| Vorname | Nachname | Wahl | Phase | Wunsch 1 | Wunsch 2 | Wunsch 3 | Aktion |
';
+ 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 "
+ | ".esc_html($tn->vorname)." |
+ ".esc_html($tn->nachname)." |
+ ".$wahl_disp." |
+ ".intval($tn->phase)." |
+ ".$w1_disp." |
+ ".$w2_disp." |
+ ".$w3_disp." |
+
+ Bearbeiten
+ Loeschen
+ |
+
";
+ }
+ echo '
';
+ 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 '| Name | Wunsch | Workshop |
';
+ 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 '| '.esc_html($t->vorname.' '.$t->nachname).' | ';
+ echo ''.$wunsch.' | ';
+ echo ''.esc_html($ws_name).' | ';
+ echo '
';
+ }
+ echo '
';
+ }
+
+ // 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 '| Name | Wunsch | Workshop |
';
+ 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 '| '.esc_html($t->vorname.' '.$t->nachname).' | ';
+ echo ''.$wunsch.' | ';
+ echo ''.esc_html($ws_name).' | ';
+ echo '
';
+ }
+ echo '
';
+ }
+
+ // 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