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,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>';
}
?>