Upload files to "includes"

This commit is contained in:
2026-01-30 14:32:11 +00:00
parent 95cb79cf0c
commit 73dc7afebb
2 changed files with 890 additions and 0 deletions

View File

@@ -0,0 +1,403 @@
<?php
function kc_teilnehmer_page() {
global $wpdb;
$prefix = $wpdb->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 '<div class="notice notice-success">Teilnehmer gel<65>scht!</div>';
}
// Teilnehmer speichern (neu/<2F>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 '<div class="notice notice-error">Diese Kombination aus Vorname und Nachname existiert bereits für diese Wahl.</div>';
} 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 '<div class="notice notice-error">Ungültige Phase für die gewählte Wahl.</div>';
} 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 '<div class="notice notice-error">Einer oder mehrere ausgewählte Workshops gehören nicht zur gewählten Wahl.</div>';
} 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 '<div class="notice notice-success">Teilnehmer aktualisiert!</div>';
} else {
$wpdb->insert("{$prefix}kc_teilnehmer", $data);
echo '<div class="notice notice-success">Teilnehmer angelegt!</div>';
}
}
}
}
}
// 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 '<div class="kc-admin-table-wrap">';
echo '<h2>Teilnehmer bearbeiten</h2>';
echo '<form method="post">';
echo '<input type="hidden" name="tid" value="'.intval($tn->id).'">';
echo '<input type="text" name="vorname" placeholder="Vorname" value="'.esc_attr($tn->vorname).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<input type="text" name="nachname" placeholder="Nachname" value="'.esc_attr($tn->nachname).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
// Wahl select
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wahl</label>';
echo '<select id="kc-wahl-select" name="wahl_id" required style="margin-bottom:8px;width:100%;padding:7px;">';
foreach($all_wahlen as $w) {
$selw = (intval($tn->wahl_id) === intval($w->id)) ? ' selected' : '';
echo '<option value="'.intval($w->id).'"'.$selw.'>'.esc_html($w->name).'</option>';
}
echo '</select>';
// Phase select (fallback content)
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Phase</label>';
echo '<select id="kc-phase-select" name="phase" required style="margin-bottom:8px;width:100%;padding:7px;">';
$cur_phases = isset($wahl_map[intval($tn->wahl_id)]) ? $wahl_map[intval($tn->wahl_id)]['phases'] : 1;
for($i=1;$i<=$cur_phases;$i++){
$selp = (intval($tn->phase) === $i) ? ' selected' : '';
echo '<option value="'.intval($i).'"'.$selp.'>'.intval($i).'</option>';
}
echo '</select>';
// Wunsch selects
for ($k=1;$k<=3;$k++){
$cur = intval($tn->{'wunsch'.$k});
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wunsch '.intval($k).'</label>';
echo '<select id="kc-wunsch'.intval($k).'" name="wunsch'.intval($k).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<option value="">Bitte Wähle einen Workshop aus</option>';
foreach($all_workshops as $ws) {
$sel = ($cur === intval($ws->id)) ? ' selected' : '';
echo '<option value="'.intval($ws->id).'"'.$sel.'>'.esc_html($ws->name).'</option>';
}
echo '</select>';
}
echo '<button name="kc_teilnehmer_save" class="kc-btn">Speichern</button>';
echo '<a href="?page=kc_teilnehmer" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>';
echo '</form>';
// Provide JS map and init JS: restrict phases/workshops based on Wahl and init Select2
echo '<script>var KC_WAHL_MAP = '.json_encode($wahl_map, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP).';</script>';
echo '<script>
(function(){
function fillPhaseOptions(wid){
var ph = KC_WAHL_MAP[wid] ? KC_WAHL_MAP[wid].phases : 1;
var sel = document.getElementById("kc-phase-select"); if(!sel) return; sel.innerHTML = "";
for(var i=1;i<=ph;i++){ var o=document.createElement("option"); o.value=i; o.text=i; sel.appendChild(o); }
}
function fillWorkshopOptions(wid){
var list = KC_WAHL_MAP[wid] ? KC_WAHL_MAP[wid].workshops : [];
["kc-wunsch1","kc-wunsch2","kc-wunsch3"].forEach(function(sid){
var sel = document.getElementById(sid); if(!sel) return; var cur = sel.value; sel.innerHTML = "";
var empty = document.createElement("option"); empty.value = ""; empty.text = "Bitte Wähle einen Workshop aus"; sel.appendChild(empty);
list.forEach(function(w){ var o=document.createElement("option"); o.value = w.id; o.text = w.name; sel.appendChild(o); });
if(cur) sel.value = cur;
});
}
jQuery(function($){
try{ $("#kc-wunsch1,#kc-wunsch2,#kc-wunsch3,#kc-wahl-select,#kc-phase-select").select2({width: "100%"}); }catch(e){}
var wsel = document.getElementById("kc-wahl-select"); if(!wsel) return;
wsel.addEventListener("change", function(){ fillPhaseOptions(this.value); fillWorkshopOptions(this.value); try{ $("#kc-wunsch1,#kc-wunsch2,#kc-wunsch3,#kc-phase-select").trigger("change.select2"); }catch(e){} });
// initial fill
fillPhaseOptions(wsel.value); fillWorkshopOptions(wsel.value);
});
})();
</script>';
echo '</div>';
return;
}
// Neuer Teilnehmer anlegen
if (isset($_GET['new'])) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Neuen Teilnehmer anlegen</h2>';
echo '<form method="post">';
echo '<input type="text" name="vorname" placeholder="Vorname" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<input type="text" name="nachname" placeholder="Nachname" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wahl</label>';
echo '<select id="kc-wahl-select" name="wahl_id" required style="margin-bottom:8px;width:100%;padding:7px;">';
foreach($all_wahlen as $w) {
echo '<option value="'.intval($w->id).'">'.esc_html($w->name).'</option>';
}
echo '</select>';
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Phase</label>';
echo '<select id="kc-phase-select" name="phase" required style="margin-bottom:8px;width:100%;padding:7px;">';
$first_wahl = reset($all_wahlen);
$first_ph = $first_wahl ? max(1,intval($first_wahl->anzahl_einheiten)) : 1;
for($i=1;$i<=$first_ph;$i++) echo '<option value="'.intval($i).'">'.intval($i).'</option>';
echo '</select>';
for ($k=1;$k<=3;$k++){
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Wunsch '.intval($k).'</label>';
echo '<select id="kc-wunsch'.intval($k).'" name="wunsch'.intval($k).'" required style="margin-bottom:8px;width:100%;padding:7px;">';
echo '<option value="">Bitte Wähle einen Workshop aus</option>';
foreach($all_workshops as $ws) {
echo '<option value="'.intval($ws->id).'">'.esc_html($ws->name).'</option>';
}
echo '</select>';
}
echo '<button name="kc_teilnehmer_save" class="kc-btn">Speichern</button>';
echo '<a href="?page=kc_teilnehmer" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>';
echo '</form>';
// Init Select2 for nicer selects if available
echo '<script>jQuery(function($){try{ $("#kc-wunsch1,#kc-wunsch2,#kc-wunsch3,#kc-wahl-select,#kc-phase-select").select2({width:"100%"}); }catch(e){} });</script>';
echo '</div>';
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 '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Teilnehmer</h2>';
echo '<a class="kc-btn" style="float:right;margin-bottom:12px;" href="?page=kc_teilnehmer&new=1">+ Neuer Teilnehmer</a>';
// 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 '<div style="margin:12px 0;clear:both;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">'
.'<label style="font-weight:600;">Wahlen filtern:</label>';
echo '<button type="button" class="kc-btn kc-wahl-filter-btn" data-wahl-id="" style="padding:6px 10px;">Alle</button>';
foreach(["KC1","KC2","KC3"] as $kc) {
if(isset($kc_buttons[$kc])) {
$w = $kc_buttons[$kc];
echo '<button type="button" class="kc-btn kc-wahl-filter-btn" data-wahl-id="'.intval($w->id).'" style="padding:6px 10px;">'.esc_html($w->name).'</button>';
}
}
foreach($rest_buttons as $w) {
echo '<button type="button" class="kc-btn kc-wahl-filter-btn" data-wahl-id="'.intval($w->id).'" style="padding:6px 10px;">'.esc_html($w->name).'</button>';
}
echo '<span id="kc-wahl-filter-count" style="color:#666;font-size:90%;margin-left:12px;">Alle anzeigen</span>';
echo '</div>';
// Platzhalter für Phasen-Filter
echo '<div id="kc-phase-filter-row" style="display:none;margin:8px 0 16px 0;gap:8px;align-items:center;flex-wrap:wrap;"></div>';
// 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 '<details open style="margin:10px 0 18px 0;border:1px solid #eaeaea;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:700;cursor:pointer;">'. $wahl_disp . ' <span style="color:#555;font-weight:600;">(' . $gesamt . ' TN)</span></summary>';
foreach ($phasen as $phase => $tns) {
echo '<details open style="margin:10px 0 10px 0;border:1px solid #f0f0f0;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:600;">Phase '.intval($phase).' <span style="color:#555;font-weight:600;">('.count($tns).' TN)</span></summary>';
echo '<table class="kc-admin-table" style="margin:8px 0;">';
echo '<thead><tr><th>Vorname</th><th>Nachname</th><th>Wahl</th><th>Phase</th><th>Wunsch 1</th><th>Wunsch 2</th><th>Wunsch 3</th><th>Aktion</th></tr></thead><tbody>';
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) : '<em style="color:#999;">Keine</em>');
$w2_disp = $w2_id && isset($workshops_map[$w2_id]) ? esc_html($workshops_map[$w2_id]) : ($w2_id ? intval($w2_id) : '<em style="color:#999;">Keine</em>');
$w3_disp = $w3_id && isset($workshops_map[$w3_id]) ? esc_html($workshops_map[$w3_id]) : ($w3_id ? intval($w3_id) : '<em style="color:#999;">Keine</em>');
$wahl_disp = isset($wahl_name_map[intval($tn->wahl_id)]) ? esc_html($wahl_name_map[intval($tn->wahl_id)]) : intval($tn->wahl_id);
echo "<tr data-wahl-id='".intval($tn->wahl_id)."'>
<td>".esc_html($tn->vorname)."</td>
<td>".esc_html($tn->nachname)."</td>
<td>".$wahl_disp."</td>
<td>".intval($tn->phase)."</td>
<td>".$w1_disp."</td>
<td>".$w2_disp."</td>
<td>".$w3_disp."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_teilnehmer&edit_teilnehmer={$tn->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_teilnehmer&delete_teilnehmer={$tn->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
</td>
</tr>";
}
echo '</tbody></table>';
echo '</details>';
}
echo '</details>';
}
// JS für Wahl- und Phasen-Filter
echo '<style>.kc-wahl-filter-btn.active{background:#4CAF50;color:#fff;} .kc-phase-filter-btn.active{background:#1976d2;color:#fff;}</style>';
echo '<script>
(function() {
var btns = Array.prototype.slice.call(document.querySelectorAll(".kc-wahl-filter-btn"));
var rows = Array.prototype.slice.call(document.querySelectorAll(".kc-admin-table tbody tr"));
var status = document.getElementById("kc-wahl-filter-count");
var phaseRow = document.getElementById("kc-phase-filter-row");
// Wahl-Map für Phasen
var wahlMap = '.json_encode($wahl_map, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_QUOT|JSON_HEX_AMP).';
var currentWahl = "";
var currentPhase = "";
function renderPhaseButtons(wahlId) {
phaseRow.innerHTML = "";
if (!wahlId || !wahlMap[wahlId]) { phaseRow.style.display = "none"; currentPhase = ""; return; }
var phases = wahlMap[wahlId].phases || 1;
var label = document.createElement("label");
label.textContent = "Phasen filtern:";
label.style.fontWeight = "600";
label.style.marginRight = "8px";
phaseRow.appendChild(label);
var allBtn = document.createElement("button");
allBtn.type = "button";
allBtn.className = "kc-btn kc-phase-filter-btn active";
allBtn.setAttribute("data-phase", "");
allBtn.style.padding = "6px 10px";
allBtn.textContent = "Alle";
phaseRow.appendChild(allBtn);
for(var i=1;i<=phases;i++){
var btn = document.createElement("button");
btn.type = "button";
btn.className = "kc-btn kc-phase-filter-btn";
btn.setAttribute("data-phase", i);
btn.style.padding = "6px 10px";
btn.textContent = i;
phaseRow.appendChild(btn);
}
phaseRow.style.display = "flex";
}
function applyFilter(wahlId, phase) {
var shown = 0;
rows.forEach(function(row) {
var rid = row.getAttribute("data-wahl-id");
var ph = row.querySelector("td:nth-child(4)").textContent.trim();
var show = false;
if (!wahlId) {
show = true;
} else if (!phase) {
show = (rid == wahlId);
} else {
show = (rid == wahlId && ph == phase);
}
row.style.display = show ? "" : "none";
if (show) shown++;
});
if (status) status.textContent = (wahlId ? (shown+" angezeigt") : "Alle anzeigen");
}
function attachPhaseEvents() {
var phaseBtns = Array.prototype.slice.call(document.querySelectorAll(".kc-phase-filter-btn"));
phaseBtns.forEach(function(btn) {
btn.addEventListener("click", function() {
phaseBtns.forEach(function(b){b.classList.remove("active");});
btn.classList.add("active");
currentPhase = btn.getAttribute("data-phase");
applyFilter(currentWahl, currentPhase);
});
});
}
btns.forEach(function(btn) {
btn.addEventListener("click", function() {
btns.forEach(function(b){b.classList.remove("active");});
btn.classList.add("active");
var wahlId = btn.getAttribute("data-wahl-id");
currentWahl = wahlId;
currentPhase = "";
renderPhaseButtons(wahlId);
applyFilter(wahlId, "");
attachPhaseEvents();
});
});
// Default: Alle aktiv
if(btns.length) btns[0].classList.add("active");
renderPhaseButtons("");
applyFilter("", "");
})();
</script>';
echo '</div>';
}
?>

View File

@@ -0,0 +1,487 @@
<?php
// --- CSV Export Handler: Muss GANZ AN DEN ANFANG! ---
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);
// 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 '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Zuteilung: '.esc_html($wahl->name).'</h2>';
// 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 '<details class="kc-phase-details" open>';
echo '<summary>Phase '.intval($phase).' &nbsp; <span class="kc-count">(' . intval(count(array_filter($rows, function($r) use ($phase){ return intval($r->phase) === $phase; }))) . ' TN)</span></summary>';
// 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 '<div class="kc-workshop-card">';
echo '<b>'.esc_html($ws_name).'</b> <span class="kc-count">(' . count($filtered) . ' TN)</span><br>';
echo '<table style="width:100%;margin:8px 0">';
echo '<tr><th>Name</th><th>Wunsch</th><th>Workshop</th></tr>';
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 '<tr>';
echo '<td>'.esc_html($t->vorname.' '.$t->nachname).'</td>';
echo '<td>'.$wunsch.'</td>';
echo '<td>'.esc_html($ws_name).'</td>';
echo '</tr>';
}
echo '</table></div>';
}
// 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 '<div class="kc-unassigned">';
echo '<b>Nicht zugeteilt (Phase '.intval($phase).'):</b><br><ul style="margin:6px 0 0 16px;">';
foreach($unassigned_phase as $t) {
echo '<li>'.esc_html($t->vorname.' '.$t->nachname).'</li>';
}
echo '</ul></div>';
}
echo '</details>';
}
if (!empty($nicht_zugeteilt)) {
echo '<div style="background:#fee;padding:14px;margin:14px 0;border-radius:10px;box-shadow:0 1px 5px #b6d33321;">';
echo '<b>Nicht zugeteilt:</b><br><ul style="margin:0 0 0 15px;">';
foreach($nicht_zugeteilt as $t) {
echo '<li>'.esc_html($t->vorname.' '.$t->nachname).' (Phase '.intval($t->phase).')</li>';
}
echo '</ul></div>';
}
echo '</div>';
}
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 '<div class="notice notice-success">Zuteilung für Wahl ID '.intval($run_id).' ausgeführt.</div>';
echo "<meta http-equiv='refresh' content='1;url=".esc_url(admin_url('admin.php?page=kc_zuteilungen'))."'>";
return;
} else {
echo '<div class="notice notice-error">Die Zuteilungsfunktion ist nicht verfügbar.</div>';
}
}
}
if (isset($_GET['delete_zuteilung'])) {
if ($del_id) {
$wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $del_id]);
echo '<div class="notice notice-success">Zuteilungen für Wahl ID '.intval($del_id).' wurden gelöscht.</div>';
echo "<meta http-equiv='refresh' content='1;url=".esc_url(admin_url('admin.php?page=kc_zuteilungen'))."'>";
return;
}
}
// Übersicht: Zeige Zuteilungen für ALLE Wahlen
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Zuteilungen</h2>';
$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 '<details style="margin-top:18px;border:1px solid #eaeaea;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:700;cursor:pointer;">'.esc_html($wahl->name).' <span style="color:#555;font-weight:600;">('.count($rows).' TN)</span>';
echo ' <span style="float:right">';
echo '<a class="kc-btn" href="'.esc_url($details_url).'">Details</a> ';
echo '<a class="kc-btn" href="'.esc_url($run_url).'">Zuteilung ausf&uuml;hren</a> ';
echo '<a class="kc-btn del" href="'.esc_url($del_url).'" onclick="return confirm(\'Alle Zuteilungen f&uuml;r diese Wahl l&ouml;schen?\');">Zuteilungen l&ouml;schen</a> ';
echo '<a class="kc-btn" style="background:#2da66a;color:#fff;" href="'.esc_url($csv_url).'">CSV Export</a>';
echo '</span>';
echo '</summary>';
// --- 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 '<details open style="margin:10px 0;padding:8px;border:1px solid #f0f0f0;border-radius:6px;">';
echo '<summary style="font-weight:600;">Phase '.intval($phase).' &nbsp; <span style="color:#555;font-weight:600;">(' . intval(count(array_filter($rows, function($r) use ($phase){ return intval($r->phase) === $phase; }))) . ' TN)</span></summary>';
// 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 '<div style="background:#f9f9f9;margin:8px 0;padding:10px;border-radius:8px;">';
echo '<b>'.esc_html($ws_name).'</b> <span style="color:#555;font-size:90%">(' . count($filtered) . ' TN)</span><br>';
echo '<table style="width:100%;margin:8px 0">';
echo '<tr><th>Name</th><th>Wunsch</th><th>Workshop</th></tr>';
foreach ($filtered as $t) {
$wunsch = $t->wunsch_rang == 0 ? "Vorzuteilung" : ($t->wunsch_rang > 0 ? intval($t->wunsch_rang).'.' : ($t->wunsch_rang==-1?"zugelost":""));
echo '<tr>';
echo '<td>'.esc_html($t->vorname.' '.$t->nachname).'</td>';
echo '<td>'.$wunsch.'</td>';
echo '<td>'.esc_html($ws_name).'</td>';
echo '</tr>';
}
echo '</table></div>';
}
// 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 '<div style="background:#fee;padding:10px;margin-top:8px;border-radius:8px;">';
echo '<b>Nicht zugeteilt (Phase '.intval($phase).'):</b><br><ul style="margin:6px 0 0 16px;">';
foreach($unassigned_phase as $t) {
echo '<li>'.esc_html($t->vorname.' '.$t->nachname).'</li>';
}
echo '</ul></div>';
}
echo '</details>';
}
echo '</details>';
}
echo '</div>';
}
?>