diff --git a/includes/admin-workshops.php b/includes/admin-workshops.php
new file mode 100644
index 0000000..80e7400
--- /dev/null
+++ b/includes/admin-workshops.php
@@ -0,0 +1,721 @@
+prefix;
+ kc_admin_tabs('kc_workshops');
+
+ // Liste aller Teamer für Multi-Select (sortiert wie in Teamer-Übersicht: Vorname A-Z)
+ $teamer_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_teamer ORDER BY vorname ASC");
+
+ // Liste aller Wahlen für Auswahl
+ $wahlen_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahlen ORDER BY id DESC");
+
+ // Liste aller Phasen für Auswahl
+ $phasen_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_phasen ORDER BY nummer");
+
+ // Workshop löschen
+ if (isset($_GET['delete_workshop'])) {
+ $wid = intval($_GET['delete_workshop']);
+ $wpdb->delete("{$prefix}kc_workshops", ['id' => $wid]);
+ echo '
Workshop gelöscht!
';
+ }
+
+ // Workshop speichern (neu/ändern)
+ if (isset($_POST['kc_workshop_save'])) {
+ $wid = !empty($_POST['wid']) ? intval($_POST['wid']) : null;
+
+ // Nur Workshop-Daten speichern, wenn Name übergeben wurde (nicht bei reiner Teamer-Bearbeitung)
+ if (!empty($_POST['name'])) {
+ // Ensure DB has min_teilnehmer column
+ $col = $wpdb->get_results($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer'));
+ if (empty($col)) {
+ $wpdb->query("ALTER TABLE {$prefix}kc_workshops ADD COLUMN min_teilnehmer INT DEFAULT 0");
+ }
+
+ $data = [
+ 'name' => sanitize_text_field($_POST['name']),
+ 'beschreibung' => sanitize_text_field($_POST['beschreibung']),
+ 'max_teilnehmer' => intval($_POST['max_teilnehmer']),
+ 'min_teilnehmer' => intval($_POST['min_teilnehmer'] ?? 0)
+ ];
+ if ($wid) {
+ $wpdb->update("{$prefix}kc_workshops", $data, ['id'=> $wid]);
+ echo 'Workshop aktualisiert!
';
+ } else {
+ $wpdb->insert("{$prefix}kc_workshops", $data);
+ $wid = intval($wpdb->insert_id);
+ echo 'Workshop angelegt!
';
+ }
+ }
+
+ // Teamer-Zuordnungen speichern mit Wahl/Phase (auch für neue Workshops)
+ if (!empty($wid)) {
+ // Stelle sicher, dass wahl_id und phase Spalten existieren
+ $columns = $wpdb->get_col("SHOW COLUMNS FROM {$prefix}kc_workshop_teamer");
+ if (!in_array('wahl_id', $columns)) {
+ $wpdb->query("ALTER TABLE {$prefix}kc_workshop_teamer ADD COLUMN wahl_id INT(11) NULL DEFAULT NULL");
+ }
+ if (!in_array('phase', $columns)) {
+ $wpdb->query("ALTER TABLE {$prefix}kc_workshop_teamer ADD COLUMN phase INT(11) NULL DEFAULT NULL");
+ }
+
+ $is_feste = !empty($_POST['edit_feste']);
+ $wahl_id = !empty($_POST['select_wahl_id']) ? intval($_POST['select_wahl_id']) : null;
+ $phase = !empty($_POST['select_phase']) ? intval($_POST['select_phase']) : null;
+
+ // Lösche alte Zuordnungen (nur wenn Teamer vorhanden sind, für neue Workshops)
+ if (!empty($_POST['teamer_ids'])) {
+ if ($is_feste) {
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM {$prefix}kc_workshop_teamer WHERE workshop_id=%d AND wahl_id IS NULL AND phase IS NULL",
+ $wid
+ ));
+ } elseif ($wahl_id !== null) {
+ if ($phase !== null) {
+ $wpdb->delete("{$prefix}kc_workshop_teamer", [
+ 'workshop_id' => $wid,
+ 'wahl_id' => $wahl_id,
+ 'phase' => $phase
+ ]);
+ } else {
+ $wpdb->query($wpdb->prepare(
+ "DELETE FROM {$prefix}kc_workshop_teamer WHERE workshop_id=%d AND wahl_id=%d AND phase IS NULL",
+ $wid, $wahl_id
+ ));
+ }
+ }
+ }
+
+ // Neue Zuordnungen einfügen
+ if (!empty($_POST['teamer_ids']) && is_array($_POST['teamer_ids'])) {
+ foreach ($_POST['teamer_ids'] as $tid) {
+ $tid = intval($tid);
+ if ($tid > 0) {
+ if ($is_feste) {
+ $wpdb->insert("{$prefix}kc_workshop_teamer", [
+ 'workshop_id' => $wid,
+ 'teamer_id' => $tid,
+ 'wahl_id' => null,
+ 'phase' => null
+ ]);
+ } else {
+ $wpdb->insert("{$prefix}kc_workshop_teamer", [
+ 'workshop_id' => $wid,
+ 'teamer_id' => $tid,
+ 'wahl_id' => $wahl_id,
+ 'phase' => $phase
+ ]);
+ }
+ }
+ }
+ }
+
+ // Redirect nach Speichern von festen Teamern zur Gesamtübersicht
+ if ($is_feste && empty($_POST['name'])) {
+ echo 'Feste Teamer gespeichert!
';
+ echo " ";
+ return;
+ }
+
+ // Redirect nach Speichern von wahl-spezifischen Teamern zurück zur Workshop-Bearbeitung
+ if ($wahl_id !== null && empty($_POST['name'])) {
+ echo 'Teamer-Zuordnung gespeichert!
';
+ echo " ";
+ return;
+ }
+ }
+ }
+
+ // Workshop bearbeiten
+ if (isset($_GET['edit_workshop'])) {
+ $wid = intval($_GET['edit_workshop']);
+ $ws = $wpdb->get_row("SELECT * FROM {$prefix}kc_workshops WHERE id=$wid");
+ if (!$ws) {
+ echo '';
+ return;
+ }
+
+ echo '';
+ echo '
Workshop bearbeiten: '.esc_html($ws->name).' ';
+
+ // Hole Spalten-Info
+ $columns = $wpdb->get_col("SHOW COLUMNS FROM {$prefix}kc_workshop_teamer");
+ $has_wahl = in_array('wahl_id', $columns);
+ $has_phase = in_array('phase', $columns);
+
+ // Zeige Feste Teamer (gelten für alle Wahlen)
+ if ($has_wahl && $has_phase) {
+ $feste_teamer = $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
+ ORDER BY t.vorname",
+ $wid
+ ));
+
+ if (!empty($feste_teamer)) {
+ $teamer_names = array_map(function($t) { return $t->vorname.' '.$t->nachname; }, $feste_teamer);
+ $teamer_str = implode(', ', array_map('esc_html', $teamer_names));
+ echo '
+
Feste Teamer (für alle Wahlen): '.$teamer_str.'
+
Bearbeiten
+
';
+ }
+ }
+
+ // Zeige Wahl/Phase-Kombinationen mit ihren Teamern
+ echo '
Teamer pro Wahl:
';
+ echo '
';
+ echo '
';
+ echo 'Wahl Teamer Aktion ';
+
+ // Hole alle Wahlen, die diesem Workshop zugeordnet sind
+ $wahlen_for_workshop = $wpdb->get_results($wpdb->prepare(
+ "SELECT DISTINCT ww.wahl_id, w.name, w.anzahl_einheiten
+ FROM {$prefix}kc_wahl_workshops ww
+ JOIN {$prefix}kc_wahlen w ON ww.wahl_id=w.id
+ WHERE ww.workshop_id=%d
+ ORDER BY w.id",
+ $wid
+ ));
+
+ if (empty($wahlen_for_workshop)) {
+ echo 'Dieser Workshop ist noch keiner Wahl zugeordnet. ';
+ } else {
+ foreach($wahlen_for_workshop as $w) {
+ // Sammle Teamer für diese Wahl
+ if ($has_wahl && $has_phase) {
+ // Prüfe ob es wahl-spezifische Teamer gibt
+ $wahl_teamer = $wpdb->get_results($wpdb->prepare(
+ "SELECT DISTINCT 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
+ ORDER BY t.vorname",
+ $wid, $w->wahl_id
+ ));
+
+ // Wenn keine wahl-spezifischen Teamer, nutze die Festen Teamer
+ if (empty($wahl_teamer) && !empty($feste_teamer)) {
+ $teamer = $feste_teamer;
+ } else {
+ $teamer = $wahl_teamer;
+ }
+ } else {
+ $teamer = [];
+ }
+
+ $teamer_names = array_map(function($t) { return $t->vorname.' '.$t->nachname; }, $teamer);
+ $teamer_str = !empty($teamer_names) ? implode(', ', array_map('esc_html', $teamer_names)) : 'Keine ';
+
+ echo '
+ '.esc_html($w->name).'
+ '.$teamer_str.'
+ Bearbeiten
+ ';
+ }
+ }
+
+ echo '
';
+
+ // Wenn Feste Teamer bearbeitet werden sollen
+ if (isset($_GET['edit_feste'])) {
+ echo '
Feste Teamer (gelten für alle Wahlen)
+
';
+ }
+
+ // Wenn eine Wahl ausgewählt wurde, zeige Phase-Auswahl oder direkt Teamer-Bearbeitung
+ elseif (isset($_GET['select_wahl']) && !isset($_GET['edit_wahl'])) {
+ $select_wahl_id = intval($_GET['select_wahl']);
+ $wahl_info = $wpdb->get_row($wpdb->prepare("SELECT name, anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d", $select_wahl_id));
+ if (!$wahl_info) {
+ echo '
Wahl nicht gefunden.
';
+ echo '
Abbrechen ';
+ return;
+ }
+
+ if (intval($wahl_info->anzahl_einheiten) > 1) {
+ // Mehrere Phasen: Frage stellen unter Berücksichtigung der tatsächlichen Zuordnung
+ // Ermittle, welchen Phasen dieser Workshop in dieser Wahl zugeordnet ist
+ $assigned_phases = $wpdb->get_col($wpdb->prepare(
+ "SELECT DISTINCT phase FROM {$prefix}kc_wahl_workshops WHERE workshop_id=%d AND wahl_id=%d AND phase IS NOT NULL",
+ $wid, $select_wahl_id
+ ));
+ $assigned_phases = array_map('intval', (array)$assigned_phases);
+ sort($assigned_phases);
+
+ echo '
';
+ echo '
Teamer für: '.esc_html($wahl_info->name).' ';
+ echo '
Sollen die Teamer für eine bestimmte Phase gelten?';
+ echo ' (Dieser Workshop ist zugeordnet: '.(!empty($assigned_phases) ? 'Phase '.esc_html(implode(', ', $assigned_phases)) : 'keiner Phase').')
';
+ echo '
';
+
+ // "Für alle Phasen" nur anbieten, wenn wirklich allen Phasen dieser Wahl zugeordnet
+ if (!empty($assigned_phases) && count($assigned_phases) === intval($wahl_info->anzahl_einheiten)) {
+ echo '
Für alle Phasen ';
+ }
+
+ // Buttons nur für tatsächlich zugeordnete Phasen
+ foreach ($assigned_phases as $p) {
+ echo '
Phase '.$p.' ';
+ }
+
+ if (empty($assigned_phases)) {
+ echo '
Dieser Workshop ist in dieser Wahl aktuell keiner Phase zugeordnet. ';
+ }
+
+ echo '
';
+ echo '
Abbrechen ';
+ echo '
';
+ } else {
+ // Nur eine Phase: direkt weiterleiten
+ echo "
";
+ return;
+ }
+ }
+
+ // Wenn eine Wahl ausgewählt wurde (mit oder ohne Phase), zeige Bearbeitung
+ elseif (isset($_GET['edit_wahl'])) {
+ $edit_wahl_id = intval($_GET['edit_wahl']);
+ $edit_phase = isset($_GET['edit_phase']) ? intval($_GET['edit_phase']) : null;
+ $edit_wahl = $wpdb->get_row($wpdb->prepare("SELECT name FROM {$prefix}kc_wahlen WHERE id=%d", $edit_wahl_id));
+ if (!$edit_wahl) {
+ echo '
Wahl nicht gefunden.
';
+ echo '
Abbrechen ';
+ return;
+ }
+
+ if ($edit_phase !== null) {
+ $title = 'Teamer für '.esc_html($edit_wahl->name).' Phase '.$edit_phase;
+ } else {
+ $title = 'Teamer für '.esc_html($edit_wahl->name).' (Alle Phasen)';
+ }
+
+ echo '
'.$title.'
+
';
+ } else {
+ // Workshop-Details bearbeiten
+ echo '
Workshop-Details
+
';
+ }
+
+ echo '
';
+ return;
+ }
+
+ // Neuer Workshop anlegen
+ if (isset($_GET['new'])) {
+ echo '';
+ echo '
Neuen Workshop anlegen ';
+
+ echo '
';
+
+ echo '
';
+ return;
+ }
+
+ // Übersicht
+ echo '';
+ echo '
Alle Workshops ';
+ echo '
+ Neuer Workshop ';
+ echo '
'
+ .'
Beschreibung filtern: '
+ .'
'
+ .'
Alle anzeigen '
+ .'
'
+ .'Alle '
+ .'(zu ändern) '
+ .'KC1 '
+ .'KC2 '
+ .'KC3 '
+ .'Ohne Teamer '
+ .'
'
+ .'
';
+ echo '
'
+ .'Wahlen filtern: '
+ .' '
+ .'Alle anzeigen '
+ .'
';
+
+ // Sortierung
+ $sort = isset($_GET['sort']) ? sanitize_text_field($_GET['sort']) : 'id';
+ $order = isset($_GET['order']) ? (($_GET['order'] === 'desc') ? 'DESC' : 'ASC') : 'DESC';
+ $allowed_sort = ['id', 'name', 'beschreibung'];
+ if (!in_array($sort, $allowed_sort)) {
+ $sort = 'id';
+ }
+
+ // Sortier-Links
+ $name_order = ($sort === 'name' && $order === 'ASC') ? 'desc' : 'asc';
+ $beschreibung_order = ($sort === 'beschreibung' && $order === 'ASC') ? 'desc' : 'asc';
+ $name_arrow = ($sort === 'name') ? ($order === 'ASC' ? ' ▲' : ' ▼') : '';
+ $beschreibung_arrow = ($sort === 'beschreibung') ? ($order === 'ASC' ? ' ▲' : ' ▼') : '';
+
+ echo '
';
+ echo '';
+ echo 'Name'.$name_arrow.' ';
+ echo 'Beschreibung'.$beschreibung_arrow.' ';
+ echo 'Teilnehmer (Min - Max) ';
+ echo 'Teamer ';
+ echo 'Wahlen ';
+ echo 'Aktion ';
+ echo ' ';
+
+ $workshops = $wpdb->get_results("SELECT * FROM {$prefix}kc_workshops ORDER BY {$sort} {$order}");
+ foreach ($workshops as $ws) {
+ // Teamer anzeigen
+ $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", $ws->id));
+ $tnames = array_map(function($r){ return $r->vorname.' '.$r->nachname; }, $trows);
+ $tnames_str = implode(', ', array_map('esc_html', $tnames));
+ if (empty($tnames_str)) $tnames_str = 'Keine ';
+
+ // Wahlen und Phasen anzeigen
+ $wahl_rows = $wpdb->get_results($wpdb->prepare(
+ "SELECT w.name, ww.phase
+ FROM {$prefix}kc_wahl_workshops ww
+ JOIN {$prefix}kc_wahlen w ON ww.wahl_id=w.id
+ WHERE ww.workshop_id=%d
+ ORDER BY w.id, ww.phase",
+ $ws->id
+ ));
+ $wahl_info = [];
+ foreach($wahl_rows as $wr) {
+ $wahl_info[] = esc_html($wr->name) . ($wr->phase ? ' (P'.intval($wr->phase).')' : '');
+ }
+ $wahl_str = !empty($wahl_info) ? implode(', ', $wahl_info) : 'Keine ';
+
+ echo "
+ ".esc_html($ws->name)."
+ ".esc_html($ws->beschreibung)."
+ ".(isset($ws->min_teilnehmer)?intval($ws->min_teilnehmer):0)." - ".intval($ws->max_teilnehmer)."
+ ". $tnames_str ."
+ ". $wahl_str ."
+
+ Bearbeiten
+ Loeschen
+
+ ";
+ }
+ echo '
';
+ echo '';
+ echo '
';
+}
+?>
\ No newline at end of file
diff --git a/includes/frontend-ergebnis.php b/includes/frontend-ergebnis.php
new file mode 100644
index 0000000..34b32e3
--- /dev/null
+++ b/includes/frontend-ergebnis.php
@@ -0,0 +1,238 @@
+null], $atts);
+ $wahl_id = intval($atts['wahl']);
+
+ global $wpdb;
+ $wahl = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=$wahl_id");
+ if(!$wahl) return '';
+ if($wahl->freigegeben) return '
';
+
+ // Workshops für diese Wahl holen
+ $workshops = $wpdb->get_results(
+ "SELECT ws.id, ws.name
+ FROM {$wpdb->prefix}kc_workshops ws
+ JOIN {$wpdb->prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id
+ WHERE ww.wahl_id = ".intval($wahl_id)
+ );
+ $ws_names = [];
+ $ws_ids = [];
+ foreach($workshops as $ws) {
+ $ws_names[$ws->id] = $ws->name;
+ $ws_ids[] = intval($ws->id);
+ }
+
+ // Zuteilungen holen
+ $rows = $wpdb->get_results(
+ "SELECT * FROM {$wpdb->prefix}kc_zuteilung WHERE wahl_id=".intval($wahl_id)
+ );
+
+ // Gruppieren nach Workshop, sortieren nach Teilnehmerzahl
+ $workshop_groups = [];
+ $nicht_zugeteilt = [];
+ $phases = [];
+ foreach($rows as $row) {
+ // Einträge ohne Phase komplett ignorieren
+ 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[$row->workshop_id][] = $row;
+ } else {
+ $nicht_zugeteilt[] = $row;
+ }
+ }
+ uasort($workshop_groups, function($a, $b) {
+ return count($a) - count($b);
+ });
+
+ $phases = array_keys($phases);
+ sort($phases, SORT_NUMERIC);
+
+ // Teamer pro Workshop und Phase laden (mit Hierarchie: Phase-spezifisch > Wahl-weit > Feste)
+ $ws_teamers = [];
+ if (!empty($ws_ids)) {
+ // Für JEDEN Workshop einzeln laden
+ foreach($ws_ids as $ws_id) {
+ foreach($phases as $phase) {
+ // 1. Versuche Phase-spezifische Teamer zu laden
+ $trows = $wpdb->get_results($wpdb->prepare(
+ "SELECT t.vorname, t.nachname FROM {$wpdb->prefix}kc_workshop_teamer wt
+ JOIN {$wpdb->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
+ ));
+
+ // 2. Falls nicht: Versuche Wahl-weite Teamer zu laden
+ if (empty($trows)) {
+ $trows = $wpdb->get_results($wpdb->prepare(
+ "SELECT t.vorname, t.nachname FROM {$wpdb->prefix}kc_workshop_teamer wt
+ JOIN {$wpdb->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
+ ));
+ }
+
+ // 3. Falls nicht: Lade Feste Teamer
+ if (empty($trows)) {
+ $trows = $wpdb->get_results($wpdb->prepare(
+ "SELECT t.vorname, t.nachname FROM {$wpdb->prefix}kc_workshop_teamer wt
+ JOIN {$wpdb->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);
+ }
+ }
+ }
+ }
+ }
+
+ // Versuche, Teilnehmer-IDs aus Cookie zu lesen (falls vorhanden)
+ $my_ids = [];
+ $cookie_name = 'kc_my_part_' . intval($wahl_id);
+ if (!empty($_COOKIE[$cookie_name])) {
+ $decoded = json_decode(stripslashes($_COOKIE[$cookie_name]), true);
+ if (is_array($decoded)) $my_ids = array_map('intval', $decoded);
+ }
+
+ ob_start();
+ // Inline styles, angepasst an konfi-castle.com Look (dezent, grün/türkis Akzent)
+ echo '';
+
+ echo '';
+ echo '
Ergebnis für diese Wahl ';
+
+ // Falls persönliche IDs vorhanden: zeige persönliche Zuteilung oben
+ $personal_shown = false;
+ if (!empty($my_ids)) {
+ // Suche Zuteilungen für meine IDs
+ $my_rows = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}kc_zuteilung WHERE wahl_id=".intval($wahl_id)." AND id IN (" . implode(',', array_map('intval', $my_ids)) . ")");
+ if (!empty($my_rows)) {
+ // Entferntes
nach "Deine Zuteilung"
+ echo '
';
+ echo '
Deine Zuteilung ';
+ foreach($my_rows as $mr) {
+ $wslabel = ($mr->workshop_id && isset($ws_names[$mr->workshop_id])) ? $ws_names[$mr->workshop_id] : ($mr->workshop_id ? 'ID '.$mr->workshop_id : 'Nicht zugeteilt');
+ echo ''.esc_html($mr->vorname.' '.$mr->nachname).' (Phase '.intval($mr->phase).') — '.esc_html($wslabel).' ';
+ }
+ echo ' ';
+ $personal_shown = true;
+ }
+ }
+
+ // Für jede Phase: Workshops nebeneinander darstellen
+ foreach($phases as $phase) {
+ echo '
';
+ echo '
Phase '.intval($phase).' ';
+ echo '
';
+ // Sammle nur Workshops mit Teilnehmern in dieser Phase
+ $display = [];
+ foreach($ws_names as $ws_id => $ws_name) {
+ $teilnehmer = [];
+ if (isset($workshop_groups[$ws_id])) {
+ foreach($workshop_groups[$ws_id] as $t) {
+ if (intval($t->phase) === intval($phase)) $teilnehmer[] = $t;
+ }
+ }
+ $count = count($teilnehmer);
+ if ($count === 0) continue; // nur Workshops mit TN anzeigen
+ $display[] = [
+ 'id' => $ws_id,
+ 'name' => $ws_name,
+ 'teilnehmer' => $teilnehmer,
+ 'count' => $count
+ ];
+ }
+
+ // Nach Teilnehmerzahl aufsteigend sortieren (kleinste oben, größte unten)
+ usort($display, function($a, $b) {
+ return $a['count'] - $b['count'];
+ });
+
+ // Ausgabe der sortierten Kacheln
+ foreach($display as $d) {
+ $ws_name = $d['name'];
+ $teilnehmer = $d['teilnehmer'];
+ $count = $d['count'];
+
+ // Sortiere so, dass persönliche Teilnehmer zuerst kommen
+ if (!empty($my_ids) && !empty($teilnehmer)) {
+ usort($teilnehmer, function($a, $b) use ($my_ids) {
+ $a_my = in_array(intval($a->id), $my_ids);
+ $b_my = in_array(intval($b->id), $my_ids);
+ if ($a_my && !$b_my) return -1;
+ if (!$a_my && $b_my) return 1;
+ return 0;
+ });
+ }
+
+ echo '
';
+ // Teamernamen (falls vorhanden) anzeigen - mit Phase-Key
+ $teamer_html = '';
+ $teamer_key = intval($d['id']) . '_' . intval($phase);
+ if (!empty($ws_teamers[$teamer_key])) {
+ $teamer_html = '
'.esc_html(implode(', ', $ws_teamers[$teamer_key])).' ';
+ }
+ echo '
'.esc_html($ws_name).' '. $teamer_html .'('.$count.' TN)
';
+ echo '
';
+ echo '
';
+ foreach($teilnehmer as $t) {
+ $is_me = in_array(intval($t->id), $my_ids);
+ $name = esc_html($t->vorname.' '.$t->nachname);
+ $label = $name . '
('.intval($t->phase).') ';
+ echo '
'. $label .'
';
+ }
+ echo '
'; // kc-participants
+ echo '
'; // content
+ echo '
';
+ }
+
+ echo '
'; // kc-workshops-grid
+ echo '
'; // kc-phase
+ }
+
+ // Extra: Nicht zugeteilt
+ if (!empty($nicht_zugeteilt)) {
+ echo '
';
+ echo '
Nicht zugeteilt: ';
+ echo '
';
+ foreach($nicht_zugeteilt as $t) {
+ echo ''.esc_html($t->vorname.' '.$t->nachname).' (Phase '.intval($t->phase).') ';
+ }
+ echo ' ';
+ }
+
+ echo '
'; // kc-result
+ return ob_get_clean();
+});
+?>
\ No newline at end of file
diff --git a/includes/frontend-form.php b/includes/frontend-form.php
new file mode 100644
index 0000000..a83effd
--- /dev/null
+++ b/includes/frontend-form.php
@@ -0,0 +1,356 @@
+null], $atts);
+ $wahl_id = intval($atts['wahl']);
+
+ global $wpdb;
+
+ // KRITISCHER TEST: Ausgabe ganz am Anfang
+ //$debug_output = '';
+ //$debug_output .= '
DEBUG INFO ';
+ //$debug_output .= 'Shortcode läuft!
';
+ //$debug_output .= 'REQUEST_METHOD: ' . ($_SERVER['REQUEST_METHOD'] ?? 'NICHT GESETZT') . '
';
+ //$debug_output .= 'POST vorhanden: ' . (empty($_POST) ? 'NEIN' : 'JA') . '
';
+ //$debug_output .= 'POST Inhalt:
' . print_r($_POST, true) . ' ';
+ //$debug_output .= '
';
+
+ $wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $wahl_id));
+ if(!$wahl || !$wahl->freigegeben) {
+ return $debug_output . 'Die Workshopwahl ist aktuell nicht freigeschaltet.
';
+ }
+
+ // Ermittle erlaubte Workshops pro Phase für diese Wahl.
+ $phasen = (int)$wahl->anzahl_einheiten;
+ $workshops_by_phase = [];
+ $mapping_candidates = [
+ "{$wpdb->prefix}kc_wahl_workshops",
+ "{$wpdb->prefix}kc_workshop_wahlen",
+ "{$wpdb->prefix}kc_wahl_workshop"
+ ];
+ $mapping_table = null;
+ foreach ($mapping_candidates as $t) {
+ $found = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $t));
+ if ($found === $t) { $mapping_table = $t; break; }
+ }
+ if ($mapping_table) {
+ $has_phase_col = (bool) $wpdb->get_var("SHOW COLUMNS FROM {$mapping_table} LIKE 'phase'");
+ for ($phase = 1; $phase <= $phasen; $phase++) {
+ if ($has_phase_col) {
+ $workshops_by_phase[$phase] = $wpdb->get_results($wpdb->prepare(
+ "SELECT w.* FROM {$wpdb->prefix}kc_workshops w
+ JOIN {$mapping_table} m ON w.id = m.workshop_id
+ WHERE m.wahl_id = %d AND m.phase = %d
+ ORDER BY w.name",
+ $wahl_id, $phase
+ ));
+ } else {
+ $workshops_by_phase[$phase] = $wpdb->get_results($wpdb->prepare(
+ "SELECT w.* FROM {$wpdb->prefix}kc_workshops w
+ JOIN {$mapping_table} m ON w.id = m.workshop_id
+ WHERE m.wahl_id = %d
+ ORDER BY w.name",
+ $wahl_id
+ ));
+ }
+ }
+ } else {
+ $all = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}kc_workshops ORDER BY name");
+ for ($phase = 1; $phase <= $phasen; $phase++) $workshops_by_phase[$phase] = $all;
+ }
+
+ // POST Verarbeitung
+ $error_message = null;
+ $success_redirect = false;
+
+ if($_SERVER['REQUEST_METHOD']=='POST' && isset($_POST['kc_workshop_submit'])) {
+ //$debug_output .= 'POST WIRD VERARBEITET!
';
+
+ if(empty($_POST['vorname']) || empty($_POST['nachname'])) {
+ $error_message = 'Bitte Vor- und Nachname ausfüllen.';
+ } else {
+ $inserted_ids = [];
+ $vorname = sanitize_text_field($_POST['vorname']);
+ $nachname = sanitize_text_field($_POST['nachname']);
+ $norm_v = mb_strtolower(trim($vorname));
+ $norm_n = mb_strtolower(trim($nachname));
+
+ // Duplikatsprüfung
+ $exists = $wpdb->get_var($wpdb->prepare(
+ "SELECT COUNT(*) FROM {$wpdb->prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d",
+ $norm_v, $norm_n, $wahl_id
+ ));
+
+ if ($exists && $exists > 0) {
+ $error_message = 'Diese Kombination aus Vorname und Nachname existiert bereits für diese Wahl.';
+ } else {
+ // Validierung und Insert
+ $validation_passed = true;
+
+ for ($phase = 1; $phase <= $phasen; $phase++) {
+ $allowed = array_map('intval', wp_list_pluck($workshops_by_phase[$phase] ?? [], 'id'));
+ $w1 = intval($_POST["phase{$phase}_wunsch1"] ?? 0);
+ $w2 = intval($_POST["phase{$phase}_wunsch2"] ?? 0);
+ $w3 = intval($_POST["phase{$phase}_wunsch3"] ?? 0);
+
+ if (empty($allowed) || !in_array($w1, $allowed, true) || !in_array($w2, $allowed, true) || !in_array($w3, $allowed, true)) {
+ $error_message = "Ungültige Workshop-Auswahl für Phase {$phase}.";
+ $validation_passed = false;
+ break;
+ }
+ // Ensure the three wishes in this phase are not duplicates
+ $selected = [$w1, $w2, $w3];
+ if (count(array_unique($selected)) !== count($selected)) {
+ $error_message = "Bitte wähle in Phase {$phase} drei verschiedene Workshops (keine Duplikate).";
+ $validation_passed = false;
+ break;
+ }
+ }
+
+ if ($validation_passed) {
+ for ($phase = 1; $phase <= $phasen; $phase++) {
+ $w1 = intval($_POST["phase{$phase}_wunsch1"]);
+ $w2 = intval($_POST["phase{$phase}_wunsch2"]);
+ $w3 = intval($_POST["phase{$phase}_wunsch3"]);
+
+ $result = $wpdb->insert("{$wpdb->prefix}kc_teilnehmer", [
+ 'vorname' => $vorname,
+ 'nachname' => $nachname,
+ 'wahl_id' => $wahl_id,
+ 'phase' => $phase,
+ 'wunsch1' => $w1,
+ 'wunsch2' => $w2,
+ 'wunsch3' => $w3,
+ 'deleted' => 0
+ ]);
+
+ if ($result === false) {
+ $error_message = 'Datenbankfehler: ' . $wpdb->last_error;
+ break;
+ } else {
+ $inserted_ids[] = intval($wpdb->insert_id);
+ }
+ }
+
+ if (empty($error_message)) {
+ // Cookie setzen
+ $cookie_name = 'kc_my_part_' . intval($wahl_id);
+ $existing = [];
+ if (!empty($_COOKIE[$cookie_name])) {
+ $dec = json_decode(stripslashes($_COOKIE[$cookie_name]), true);
+ if (is_array($dec)) $existing = array_map('intval', $dec);
+ }
+ $all_ids = array_values(array_unique(array_merge($existing, $inserted_ids)));
+ @setcookie($cookie_name, wp_json_encode($all_ids), time() + 30*24*3600, '/');
+
+ // Erfolg
+ $success_redirect = true;
+ }
+ }
+ }
+ }
+ }
+
+ ob_start();
+?>
+
+
+
+
+
+prefix;
+ $msg = '';
+
+ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['kc_teamer_create_nonce'])) {
+ if (empty($_POST['kc_teamer_create_nonce']) || !wp_verify_nonce($_POST['kc_teamer_create_nonce'], 'kc_teamer_create_action')) {
+ $msg = 'Ungültiger Request.
';
+ } else {
+ $pw = trim($_POST['kc_teamer_pw'] ?? '');
+ $saved_hash = get_option('kc_teamer_password_hash', '');
+ if (empty($saved_hash) || !wp_check_password($pw, $saved_hash)) {
+ $msg = 'Falsches Passwort.
';
+ } else {
+ $vorname = sanitize_text_field($_POST['vorname'] ?? '');
+ $nachname = sanitize_text_field($_POST['nachname'] ?? '');
+ $ws_name = sanitize_text_field($_POST['workshop_name'] ?? '');
+ if ($vorname === '' || $nachname === '' || $ws_name === '') {
+ $msg = 'Bitte alle Felder ausfüllen.
';
+ } else {
+ $existing = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teamer WHERE vorname=%s AND nachname=%s", $vorname, $nachname));
+ if ($existing) {
+ $tid = intval($existing->id);
+ } else {
+ $wpdb->insert("{$prefix}kc_teamer", ['vorname'=>$vorname,'nachname'=>$nachname]);
+ $tid = intval($wpdb->insert_id);
+ }
+ $min_t = max(0, intval($_POST['min_teilnehmer'] ?? 0));
+ $max_t = max(1, intval($_POST['max_teilnehmer'] ?? 10));
+ if ($min_t > $max_t) {
+ $msg = 'Die minimale Teilnehmerzahl darf nicht größer als die maximale sein.
';
+ } else {
+ $col = $wpdb->get_var("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE 'min_teilnehmer'");
+ if (empty($col)) {
+ $wpdb->query("ALTER TABLE {$prefix}kc_workshops ADD COLUMN min_teilnehmer INT NOT NULL DEFAULT 0");
+ }
+ $wpdb->insert("{$prefix}kc_workshops", ['name'=>$ws_name, 'beschreibung'=>'', 'max_teilnehmer'=>$max_t, 'min_teilnehmer'=>$min_t]);
+ $wid = intval($wpdb->insert_id);
+
+ if ($wid) {
+ $wpdb->insert("{$prefix}kc_workshop_teamer", ['workshop_id'=>$wid, 'teamer_id'=>$tid]);
+ $msg = 'Workshop angelegt und Teamer zugeordnet.
';
+ } else {
+ $msg = 'Fehler beim Anlegen des Workshops.
';
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ob_start();
+ $nonce = wp_create_nonce('kc_teamer_create_action');
+ ?>
+
+ prefix;
+
+ $wahl_id = intval($wahl_id);
+ error_log('kc_run_zuteilung: Start für Wahl '.$wahl_id);
+
+ // 0. Entferne alte Zuteilungen für diese Wahl
+ $wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $wahl_id]);
+
+ // 1. Teilnehmer einlesen (alle Phasen werden getrennt verarbeitet)
+ $phasen = intval($wpdb->get_var($wpdb->prepare("SELECT anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d", $wahl_id)));
+ if($phasen < 1) $phasen = 1;
+
+ // 2. Workshops einlesen (inkl. Kapazität)
+ $workshops_rows = $wpdb->get_results($wpdb->prepare(
+ "SELECT ws.id, ws.name, ws.max_teilnehmer FROM {$prefix}kc_workshops ws JOIN {$prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id WHERE ww.wahl_id=%d",
+ $wahl_id
+ ));
+ $workshops = [];
+ $workshop_caps = [];
+ foreach($workshops_rows as $w) {
+ $workshops[$w->id] = $w;
+ $workshop_caps[$w->id] = intval($w->max_teilnehmer);
+ }
+
+ // 3. Force-Zuteilungen lesen (global, nach Phase filtern)
+ $forces_all = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$prefix}kc_force_zuteilung WHERE wahl_id=%d", $wahl_id));
+
+ // Prozessiere jede Phase einzeln
+ for($phase=1; $phase<=$phasen; $phase++) {
+ error_log("kc_run_zuteilung: Verarbeite Phase $phase");
+
+ // 1) Teilnehmer einlesen (alle TN dieser Wahl+Phase)
+ $teilnehmer = $wpdb->get_results($wpdb->prepare(
+ "SELECT * FROM {$prefix}kc_teilnehmer WHERE wahl_id=%d AND phase=%d",
+ $wahl_id, $phase
+ ));
+
+ // Prepare helper maps
+ $teilnehmer_by_id = [];
+ foreach($teilnehmer as $t) $teilnehmer_by_id[$t->id] = $t;
+ // keep a full copy for later reassignments if needed
+ $all_teilnehmer_by_id = $teilnehmer_by_id;
+
+ // 2) Kopie der Kapazitäten für diese Phase (werden während Zuordnung reduziert)
+ $caps = $workshop_caps;
+
+ // 3) Force-Zuteilungen anwenden (nur für diese Phase)
+ foreach($forces_all as $f) {
+ if(intval($f->phase) !== $phase) continue;
+ $tn_id = intval($f->teilnehmer_id);
+ $ws_id = intval($f->workshop_id);
+ if(!isset($teilnehmer_by_id[$tn_id])) continue; // Teilnehmer nicht in dieser Phase
+ // Nur wenn Workshop existiert und noch Kapazität >=1
+ if(isset($caps[$ws_id]) && $caps[$ws_id] > 0) {
+ $t = $teilnehmer_by_id[$tn_id];
+ $wpdb->insert("{$prefix}kc_zuteilung", [
+ 'wahl_id' => $wahl_id,
+ 'teilnehmer_id' => $t->id,
+ 'vorname' => $t->vorname,
+ 'nachname' => $t->nachname,
+ 'phase' => $phase,
+ 'workshop_id' => $ws_id,
+ 'wunsch_rang' => 0
+ ]);
+ $caps[$ws_id]--;
+ // Entferne TN aus Liste (er ist bereits zugeteilt)
+ unset($teilnehmer_by_id[$tn_id]);
+ }
+ }
+
+ // 4) Teilnehmer mischen (zufällige Reihenfolge)
+ $remaining = array_values($teilnehmer_by_id);
+ if(count($remaining) > 1) shuffle($remaining);
+
+ // Hilfsfunktion: Try assign participant to workshop if cap>0
+ $assign = function($tn, $ws_id, $rang) use (&$caps, $wpdb, $prefix, $wahl_id, $phase, &$assigned_ids) {
+ if(!isset($caps[$ws_id]) || $caps[$ws_id] <= 0) return false;
+ $wpdb->insert("{$prefix}kc_zuteilung", [
+ 'wahl_id' => $wahl_id,
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $phase,
+ 'workshop_id' => $ws_id,
+ 'wunsch_rang' => $rang
+ ]);
+ $caps[$ws_id]--;
+ return true;
+ };
+
+ // 5) Wunschrunden 1..3
+ for($wunsch=1;$wunsch<=3;$wunsch++) {
+ $not_assigned = [];
+ foreach($remaining as $tn) {
+ $ws_choice = intval($tn->{"wunsch$wunsch"});
+ if($ws_choice > 0 && isset($caps[$ws_choice]) && $caps[$ws_choice] > 0) {
+ $assigned = $assign($tn, $ws_choice, $wunsch);
+ if(!$assigned) $not_assigned[] = $tn;
+ } else {
+ $not_assigned[] = $tn;
+ }
+ }
+ $remaining = $not_assigned;
+ // optional: reshuffle after each round to keep fairness
+ if(count($remaining) > 1) shuffle($remaining);
+ }
+
+ // 6) Restliche zufällig verteilen (auf alle freie Workshops)
+ $freie = array_keys(array_filter($caps, function($c){return $c>0;}));
+ foreach($remaining as $tn) {
+ $freie = array_keys(array_filter($caps, function($c){return $c>0;}));
+ if(count($freie) === 0) {
+ // Kein Platz mehr: TN bleibt unzugeordnet
+ $wpdb->insert("{$prefix}kc_zuteilung", [
+ 'wahl_id' => $wahl_id,
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $phase,
+ 'workshop_id' => null,
+ 'wunsch_rang' => -1
+ ]);
+ continue;
+ }
+ $ws_id = $freie[array_rand($freie)];
+ $assign($tn, $ws_id, 99);
+ }
+
+ // 6.5 Consolidate workshops that did not reach their minimal Teilnehmerzahl
+ // Load minimal requirements for workshops in this Wahl
+ $ws_ids_in_wahl = array_keys($workshops);
+ if (!empty($ws_ids_in_wahl)) {
+ $placeholders = implode(',', array_fill(0, count($ws_ids_in_wahl), '%d'));
+ $min_rows = $wpdb->get_results($wpdb->prepare("SELECT id, min_teilnehmer FROM {$prefix}kc_workshops WHERE id IN ($placeholders)", $ws_ids_in_wahl));
+ $min_map = [];
+ foreach($min_rows as $r) $min_map[intval($r->id)] = intval($r->min_teilnehmer);
+
+ // Count current assignments per workshop
+ $assigned_counts_raw = $wpdb->get_results($wpdb->prepare("SELECT workshop_id, COUNT(*) AS cnt FROM {$prefix}kc_zuteilung WHERE wahl_id=%d AND phase=%d AND workshop_id IS NOT NULL GROUP BY workshop_id", $wahl_id, $phase), ARRAY_A);
+ $assigned_counts = [];
+ foreach($assigned_counts_raw as $ar) $assigned_counts[intval($ar['workshop_id'])] = intval($ar['cnt']);
+
+ // Find failing workshops (assigned >0 but < min)
+ $failing = [];
+ foreach($ws_ids_in_wahl as $wsid) {
+ $min_req = intval($min_map[$wsid] ?? 0);
+ $cnt = intval($assigned_counts[$wsid] ?? 0);
+ if ($cnt > 0 && $min_req > 0 && $cnt < $min_req) {
+ $failing[] = $wsid;
+ }
+ }
+
+ if (!empty($failing)) {
+ // collect participants from failing workshops
+ $to_reassign = [];
+ foreach($failing as $fw) {
+ $rows = $wpdb->get_results($wpdb->prepare("SELECT teilnehmer_id FROM {$prefix}kc_zuteilung WHERE wahl_id=%d AND phase=%d AND workshop_id=%d", $wahl_id, $phase, $fw));
+ foreach($rows as $r) $to_reassign[] = intval($r->teilnehmer_id);
+ }
+
+ if (!empty($to_reassign)) {
+ // remove those assignments
+ $fw_placeholders = implode(',', array_map('intval', $failing));
+ $wpdb->query("DELETE FROM {$prefix}kc_zuteilung WHERE wahl_id=".intval($wahl_id)." AND phase=".intval($phase)." AND workshop_id IN ($fw_placeholders)");
+
+ // free capacity for the failing workshops
+ foreach($failing as $fw) {
+ $freed = intval($assigned_counts[$fw] ?? 0);
+ if (!isset($caps[$fw])) $caps[$fw] = 0;
+ $caps[$fw] += $freed;
+ }
+
+ // Try to reassign each participant preferring their wishes 1..3
+ foreach($to_reassign as $tid) {
+ $tn = isset($all_teilnehmer_by_id[$tid]) ? $all_teilnehmer_by_id[$tid] : $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teilnehmer WHERE id=%d", $tid));
+ if (!$tn) continue;
+ $reassigned = false;
+ for($w=1;$w<=3;$w++) {
+ $choice = intval($tn->{"wunsch$w"});
+ if ($choice > 0 && isset($caps[$choice]) && $caps[$choice] > 0) {
+ $assigned = $assign($tn, $choice, $w);
+ if ($assigned) { $reassigned = true; break; }
+ }
+ }
+ if ($reassigned) continue;
+
+ // otherwise assign to any workshop with free capacity
+ $available = array_keys(array_filter($caps, function($c){return $c>0;}));
+ if (!empty($available)) {
+ $target = $available[array_rand($available)];
+ $assign($tn, $target, 99);
+ continue;
+ }
+
+ // lastly, mark as unassigned
+ $wpdb->insert("{$prefix}kc_zuteilung", [
+ 'wahl_id' => $wahl_id,
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $phase,
+ 'workshop_id' => null,
+ 'wunsch_rang' => -1
+ ]);
+ }
+ }
+ }
+ }
+
+ // 7) Kapazitätsprüfung (Debug / Log)
+ foreach($caps as $wsid=>$capleft) {
+ if($capleft < 0) {
+ error_log("kc_run_zuteilung: Überbuchung Workshop $wsid in Wahl $wahl_id Phase $phase (restcap=$capleft)");
+ }
+ }
+
+ // 8) Ergebnis: alles in DB geschrieben (kc_zuteilung). Logge Zusammenfassung
+ $total_assigned = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$prefix}kc_zuteilung WHERE wahl_id=%d AND phase=%d AND workshop_id IS NOT NULL", $wahl_id, $phase));
+ $total_unassigned = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$prefix}kc_zuteilung WHERE wahl_id=%d AND phase=%d AND workshop_id IS NULL", $wahl_id, $phase));
+ error_log("kc_run_zuteilung: Phase $phase - zugeteilt: $total_assigned, ohne Platz: $total_unassigned");
+ }
+
+ error_log('kc_run_zuteilung: Fertig für Wahl '.$wahl_id);
+}
+
+function kc_execute_blocks($blocks, &$context, &$i = 0) {
+ $count = count($blocks);
+ while($i < $count) {
+ $block = $blocks[$i];
+ $block_name = is_array($block) && isset($block['block']) ? $block['block'] : (is_string($block) && strpos($block, ':') !== false ? explode(':', $block, 2)[0] : $block);
+ switch($block_name) {
+ case 'repeat':
+ $repeat_count = 3;
+ if(is_array($block) && isset($block['repeat_count'])) {
+ $repeat_count = intval($block['repeat_count']);
+ } elseif(is_string($block) && strpos($block, ':') !== false) {
+ $parts = explode(':', $block, 2);
+ $repeat_count = intval($parts[1]);
+ }
+ $i++;
+ $start = $i;
+ $inner = [];
+ $depth = 1;
+ while($i < $count && $depth > 0) {
+ $inner_block = $blocks[$i];
+ $inner_block_name = is_array($inner_block) && isset($inner_block['block']) ? $inner_block['block'] : (is_string($inner_block) && strpos($inner_block, ':') !== false ? explode(':', $inner_block, 2)[0] : $inner_block);
+ if($inner_block_name === 'repeat') $depth++;
+ if($inner_block_name === 'endrepeat') $depth--;
+ if($depth > 0) $inner[] = $inner_block;
+ $i++;
+ }
+ for($r=0;$r<$repeat_count;$r++) {
+ $j = 0;
+ kc_execute_blocks($inner, $context, $j);
+ }
+ break;
+ case 'for_teilnehmer':
+ $i++;
+ $start = $i;
+ $inner = [];
+ $depth = 1;
+ while($i < $count && $depth > 0) {
+ if($blocks[$i] === 'for_teilnehmer') $depth++;
+ if($blocks[$i] === 'endfor_teilnehmer') $depth--;
+ if($depth > 0) $inner[] = $blocks[$i];
+ $i++;
+ }
+ foreach($context['teilnehmer'] as $tn) {
+ $context['tn'] = $tn;
+ $j = 0;
+ kc_execute_blocks($inner, $context, $j);
+ }
+ break;
+ case 'for_workshop':
+ $i++;
+ $start = $i;
+ $inner = [];
+ $depth = 1;
+ while($i < $count && $depth > 0) {
+ if($blocks[$i] === 'for_workshop') $depth++;
+ if($blocks[$i] === 'endfor_workshop') $depth--;
+ if($depth > 0) $inner[] = $blocks[$i];
+ $i++;
+ }
+ foreach($context['workshops'] as $ws) {
+ $context['ws'] = $ws;
+ $j = 0;
+ kc_execute_blocks($inner, $context, $j);
+ }
+ break;
+ case 'for_wunsch':
+ $i++;
+ $start = $i;
+ $inner = [];
+ $depth = 1;
+ while($i < $count && $depth > 0) {
+ if($blocks[$i] === 'for_wunsch') $depth++;
+ if($blocks[$i] === 'endfor_wunsch') $depth--;
+ if($depth > 0) $inner[] = $blocks[$i];
+ $i++;
+ }
+ for($w=1;$w<=3;$w++) {
+ $context['wunsch_nr'] = $w;
+ $j = 0;
+ kc_execute_blocks($inner, $context, $j);
+ }
+ break;
+ // --- Hier die eigentliche Blocklogik einfügen ---
+ case 'shuffle':
+ $rest = array_filter($context['teilnehmer'], function($tn) use ($context) {
+ return !in_array($tn->id, $context['zugeteilt_ids']);
+ });
+ $rest = array_values($rest);
+ if(count($rest) > 1) {
+ shuffle($rest);
+ $neu = [];
+ $i2 = 0;
+ foreach($context['teilnehmer'] as $tn) {
+ if(!in_array($tn->id, $context['zugeteilt_ids'])) {
+ $neu[] = $rest[$i2++];
+ } else {
+ $neu[] = $tn;
+ }
+ }
+ $context['teilnehmer'] = $neu;
+ }
+ $i++;
+ break;
+ case 'force':
+ foreach($context['forces'] as $f) {
+ $tn = $context['wpdb']->get_row("SELECT * FROM {$context['prefix']}kc_teilnehmer WHERE id=".intval($f->teilnehmer_id));
+ if(!$tn) continue;
+ if(isset($context['workshop_caps'][$f->workshop_id]) && $context['workshop_caps'][$f->workshop_id] > 0 && !in_array($tn->id, $context['zugeteilt_ids'])) {
+ $context['wpdb']->insert("{$context['prefix']}kc_zuteilung", [
+ 'wahl_id' => $context['wahl_id'],
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $context['phase'],
+ 'workshop_id' => $f->workshop_id,
+ 'wunsch_rang' => 0
+ ]);
+ $context['workshop_caps'][$f->workshop_id]--;
+ $context['zugeteilt_ids'][] = $tn->id;
+ }
+ }
+ $i++;
+ break;
+ case 'wunsch1':
+ case 'wunsch1_kapazitaet':
+ case 'wunsch2':
+ case 'wunsch2_kapazitaet':
+ case 'wunsch3':
+ case 'wunsch3_kapazitaet':
+ $wunsch_num = ($block === 'wunsch1' || $block === 'wunsch1_kapazitaet') ? 1 : (($block === 'wunsch2' || $block === 'wunsch2_kapazitaet') ? 2 : 3);
+ foreach($context['teilnehmer'] as $tn) {
+ if(in_array($tn->id, $context['zugeteilt_ids'])) continue;
+ $ws_id = intval($tn->{"wunsch$wunsch_num"});
+ if(isset($context['workshop_caps'][$ws_id]) && $context['workshop_caps'][$ws_id] > 0) {
+ $context['wpdb']->insert("{$context['prefix']}kc_zuteilung", [
+ 'wahl_id' => $context['wahl_id'],
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $context['phase'],
+ 'workshop_id' => $ws_id,
+ 'wunsch_rang' => $wunsch_num
+ ]);
+ $context['workshop_caps'][$ws_id]--;
+ $context['zugeteilt_ids'][] = $tn->id;
+ }
+ }
+ $i++;
+ break;
+ case 'random':
+ $freie_workshops = array_keys(array_filter($context['workshop_caps'], function($cap){return $cap>0;}));
+ $rest = array_filter($context['teilnehmer'], function($tn) use ($context) {
+ return !in_array($tn->id, $context['zugeteilt_ids']);
+ });
+ $rest = array_values($rest);
+ foreach($rest as $tn) {
+ if(count($freie_workshops)>0) {
+ $ws_id = $freie_workshops[array_rand($freie_workshops)];
+ $context['wpdb']->insert("{$context['prefix']}kc_zuteilung", [
+ 'wahl_id' => $context['wahl_id'],
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $context['phase'],
+ 'workshop_id' => $ws_id,
+ 'wunsch_rang' => 99
+ ]);
+ $context['workshop_caps'][$ws_id]--;
+ $context['zugeteilt_ids'][] = $tn->id;
+ $freie_workshops = array_keys(array_filter($context['workshop_caps'], function($cap){return $cap>0;}));
+ } else {
+ $context['wpdb']->insert("{$context['prefix']}kc_zuteilung", [
+ 'wahl_id' => $context['wahl_id'],
+ 'teilnehmer_id' => $tn->id,
+ 'vorname' => $tn->vorname,
+ 'nachname' => $tn->nachname,
+ 'phase' => $context['phase'],
+ 'workshop_id' => null,
+ 'wunsch_rang' => -1
+ ]);
+ }
+ }
+ $i++;
+ break;
+ default:
+ $i++;
+ break;
+ }
+ }
+}
+