Upload files to "includes"

This commit is contained in:
2026-01-30 14:31:47 +00:00
parent 8b3e8ba9ba
commit 5049d22824
4 changed files with 880 additions and 0 deletions

160
includes/admin-data.php Normal file
View File

@@ -0,0 +1,160 @@
<?php
function kc_data_page() {
global $wpdb;
$prefix = $wpdb->prefix;
// Only allow WP user with ID 1 to access this page
$current = wp_get_current_user();
if (!$current || intval($current->ID) !== 1) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Datenverwaltung</h2>';
echo '<div class="notice notice-error">Nur der Hauptadministrator (User ID 1) kann die Testdatenverwaltung verwenden.</div>';
echo '</div>';
return;
}
kc_admin_tabs('kc_wahlen');
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Datenverwaltung / Testdaten</h2>';
// Handle actions (with nonce)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (empty($_POST['kc_data_nonce']) || !wp_verify_nonce($_POST['kc_data_nonce'], 'kc_data_action')) {
echo '<div class="notice notice-error">Ungültiger Request (Nonce).</div>';
} else {
if (isset($_POST['kc_generate_testdata'])) {
// Read generation options from the form (with sensible defaults)
$num_workshops = max(1, intval($_POST['num_workshops'] ?? 3));
$num_part_per_phase = max(1, intval($_POST['num_part_per_phase'] ?? 10));
$phases = max(1, min(4, intval($_POST['phases'] ?? 2)));
$min_cap = max(1, intval($_POST['min_capacity'] ?? 4));
$max_cap = max($min_cap, intval($_POST['max_capacity'] ?? 8));
$created = ['wahlen'=>0, 'workshops'=>0, 'ww'=>0, 'teilnehmer'=>0];
// Create a random Wahl
$wahl_name = 'TEST - Zufallswahl ' . time();
$wpdb->insert("{$prefix}kc_wahlen", [
'name' => $wahl_name,
'beschreibung' => 'Automatisch erzeugte zufällige Testdaten',
'anzahl_einheiten' => $phases,
'freigegeben' => 1,
'deleted' => 0
]);
$wahl_id = intval($wpdb->insert_id);
if ($wahl_id) $created['wahlen']++;
// Create randomized workshops and map to the Wahl for each phase
$ws_ids = [];
for ($i=0; $i<$num_workshops; $i++) {
$cap = rand($min_cap, $max_cap);
$wn = 'TEST - WS '.($i+1).' #'.rand(1000,9999);
$wpdb->insert("{$prefix}kc_workshops", [
'name' => $wn,
'beschreibung' => 'Auto-generated',
'max_teilnehmer' => $cap
]);
$wid = intval($wpdb->insert_id);
if ($wid) {
$ws_ids[] = $wid;
$created['workshops']++;
for ($ph=1; $ph<=$phases; $ph++) {
$wpdb->insert("{$prefix}kc_wahl_workshops", ['wahl_id'=>$wahl_id, 'workshop_id'=>$wid, 'phase'=>$ph]);
$created['ww']++;
}
}
}
// Create randomized participants with random wishes
$firsts = ['Lukas','Mia','Jonas','Emma','Noah','Hannah','Paul','Lea','Tim','Lina','Max','Sara'];
$lasts = ['Müller','Schmidt','Schneider','Fischer','Weber','Mayer','Wagner','Becker','Hoffmann','Schulz'];
for ($n=0; $n<$num_part_per_phase; $n++) {
for ($ph=1; $ph<=$phases; $ph++) {
$fn = $firsts[array_rand($firsts)].rand(1,999);
$ln = $lasts[array_rand($lasts)];
// pick three wishes (distinct if possible)
$w1 = $ws_ids[array_rand($ws_ids)];
$w2 = $ws_ids[array_rand($ws_ids)];
$w3 = $ws_ids[array_rand($ws_ids)];
$wpdb->insert("{$prefix}kc_teilnehmer", [
'vorname' => $fn,
'nachname' => $ln,
'wahl_id' => $wahl_id,
'phase' => $ph,
'wunsch1' => intval($w1),
'wunsch2' => intval($w2),
'wunsch3' => intval($w3),
'deleted' => 0
]);
if (intval($wpdb->insert_id)) $created['teilnehmer']++;
}
}
echo '<div class="notice notice-success">Zufalls-Testdaten erzeugt: '.intval($created['wahlen']).' Wahl(en), '.intval($created['workshops']).' Workshops, '.intval($created['ww']).' Zuweisungen, '.intval($created['teilnehmer']).' Teilnehmer.</div>';
}
if (isset($_POST['kc_reset_plugin'])) {
// Reset plugin DB: uninstall tables, then recreate
if (function_exists('kc_uninstall_tables') && function_exists('kc_install_tables')) {
kc_uninstall_tables();
kc_install_tables();
echo '<div class="notice notice-success">Plugin-Daten zurückgesetzt: Tabellen wurden gelöscht und neu erstellt.</div>';
} else {
echo '<div class="notice notice-error">Reset fehlgeschlagen: Install/Uninstall Funktionen nicht verfügbar.</div>';
}
}
if (isset($_POST['kc_clear_testdata'])) {
// Remove everything that has names starting with 'TEST - '
// Find workshop ids
$test_ws = $wpdb->get_col($wpdb->prepare("SELECT id FROM {$prefix}kc_workshops WHERE name LIKE %s", 'TEST - %'));
if (!empty($test_ws)) {
foreach($test_ws as $tw) {
$wpdb->delete("{$prefix}kc_workshop_teamer", ['workshop_id'=>$tw]);
}
$wpdb->query("DELETE FROM {$prefix}kc_wahl_workshops WHERE workshop_id IN (".implode(',', array_map('intval', $test_ws)).")");
$wpdb->query("DELETE FROM {$prefix}kc_workshops WHERE id IN (".implode(',', array_map('intval', $test_ws)).")");
}
// Find test wahlen
$test_wahlen = $wpdb->get_col($wpdb->prepare("SELECT id FROM {$prefix}kc_wahlen WHERE name LIKE %s", 'TEST - %'));
if (!empty($test_wahlen)) {
// delete related teilnehmer and zuteilung and force zuteilung
$ids = implode(',', array_map('intval', $test_wahlen));
$wpdb->query("DELETE FROM {$prefix}kc_zuteilung WHERE wahl_id IN (".$ids.")");
$wpdb->query("DELETE FROM {$prefix}kc_force_zuteilung WHERE wahl_id IN (".$ids.")");
$wpdb->query("DELETE FROM {$prefix}kc_teilnehmer WHERE wahl_id IN (".$ids.")");
$wpdb->query("DELETE FROM {$prefix}kc_wahl_workshops WHERE wahl_id IN (".$ids.")");
$wpdb->query("DELETE FROM {$prefix}kc_wahlen WHERE id IN (".$ids.")");
}
echo '<div class="notice notice-success">Alle Testdaten (Präfix "TEST - ") wurden entfernt.</div>';
}
}
}
// Form with actions and generation options
$nonce = wp_create_nonce('kc_data_action');
echo '<form method="post" style="max-width:800px;margin-top:12px;">';
echo '<p>Mit diesen Schaltern kannst du in der Testumgebung automatisch Beispiel-Daten erzeugen oder wieder komplett entfernen. Die Aktionen sind reversibel und zielen nur auf Einträge mit dem Präfix "TEST - ".</p>';
echo '<input type="hidden" name="kc_data_nonce" value="'.esc_attr($nonce).'">';
echo '<h3>Generierungs-Optionen</h3>';
echo '<table style="width:100%;max-width:600px">';
echo '<tr><td style="width:40%;padding:6px 8px;">Anzahl Workshops</td><td><input type="number" name="num_workshops" value="3" min="1" style="width:120px;padding:6px;"></td></tr>';
echo '<tr><td style="padding:6px 8px;">Teilnehmer pro Phase</td><td><input type="number" name="num_part_per_phase" value="10" min="1" style="width:120px;padding:6px;"></td></tr>';
echo '<tr><td style="padding:6px 8px;">Phasen (Einheiten)</td><td><input type="number" name="phases" value="2" min="1" max="4" style="width:120px;padding:6px;"></td></tr>';
echo '<tr><td style="padding:6px 8px;">Min. Workshop-Kapazität</td><td><input type="number" name="min_capacity" value="4" min="1" style="width:120px;padding:6px;"></td></tr>';
echo '<tr><td style="padding:6px 8px;">Max. Workshop-Kapazität</td><td><input type="number" name="max_capacity" value="8" min="1" style="width:120px;padding:6px;"></td></tr>';
echo '</table>';
echo '<p style="margin-top:12px;"><button class="kc-btn" name="kc_generate_testdata">Testdaten erzeugen</button> ';
echo '<button class="kc-btn del" name="kc_clear_testdata" onclick="return confirm(\'Sicher? Alle Testdaten löschen?\')">Testdaten entfernen</button> ';
echo '<button class="kc-btn del" name="kc_reset_plugin" onclick="return confirm(\'Achtung: ALLE Plugin-Daten löschen und Tabellen neu erstellen? Dies ist unwiderruflich.\')">Reset Plugin (Tabellen neu erstellen)</button></p>';
echo '</form>';
echo '</div>';
}
?>

141
includes/admin-teamer.php Normal file
View File

@@ -0,0 +1,141 @@
<?php
function kc_teamer_page() {
global $wpdb;
$prefix = $wpdb->prefix;
kc_admin_tabs('kc_teamer');
// --- Teamer access password (admin only) ---
if (isset($_POST['kc_teamer_pass_save'])) {
if (empty($_POST['kc_teamer_pass_nonce']) || !wp_verify_nonce($_POST['kc_teamer_pass_nonce'], 'kc_teamer_pass_action')) {
echo '<div class="notice notice-error">Ungültiger Request (Nonce).</div>';
} else {
$pw = trim($_POST['kc_teamer_password'] ?? '');
if ($pw === '') {
delete_option('kc_teamer_password_hash');
echo '<div class="notice notice-success">Teamer-Passwort entfernt.</div>';
} else {
update_option('kc_teamer_password_hash', wp_hash_password($pw));
echo '<div class="notice notice-success">Teamer-Passwort gespeichert.</div>';
}
}
}
// Show small management box for the password
$saved = get_option('kc_teamer_password_hash', '');
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Teamer Zugriff</h2>';
echo '<form method="post" style="max-width:480px;">';
$nonce = wp_create_nonce('kc_teamer_pass_action');
echo '<input type="hidden" name="kc_teamer_pass_nonce" value="'.esc_attr($nonce).'">';
echo '<label style="display:block;margin-bottom:6px;font-weight:700;">Neues Teamer-Passwort (leer = entfernen)</label>';
echo '<label style="display:block;margin-bottom:6px;font-weight:700;">Shortcut = [konficastle_teamer_create]</label>';
echo '<input type="password" name="kc_teamer_password" value="" style="display:block;width:100%;padding:7px;margin-bottom:8px;">';
echo '<button name="kc_teamer_pass_save" class="kc-btn">Speichern</button>';
echo '</form>';
echo '</div>';
// Teamer l<>schen
if (isset($_GET['delete_teamer'])) {
$tid = intval($_GET['delete_teamer']);
$wpdb->delete("{$prefix}kc_teamer", ['id' => $tid]);
echo '<div class="notice notice-success">Teamer gelöscht!</div>';
}
// Teamer speichern (neu/<2F>ndern)
if (isset($_POST['kc_teamer_save'])) {
$data = [
'vorname' => sanitize_text_field($_POST['vorname']),
'nachname' => sanitize_text_field($_POST['nachname'])
];
if (!empty($_POST['tid'])) {
$wpdb->update("{$prefix}kc_teamer", $data, ['id'=>intval($_POST['tid'])]);
echo '<div class="notice notice-success">Teamer aktualisiert!</div>';
} else {
// Prüfe ob Kombination aus Vorname und Nachname bereits existiert
$existing = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$prefix}kc_teamer WHERE vorname = %s AND nachname = %s",
$data['vorname'],
$data['nachname']
));
if ($existing > 0) {
echo '<div class="notice notice-error">Ein Teamer mit dieser Kombination aus Vor- und Nachname existiert bereits!</div>';
} else {
$wpdb->insert("{$prefix}kc_teamer", $data);
echo '<div class="notice notice-success">Teamer angelegt!</div>';
}
}
}
// Teamer bearbeiten
if (isset($_GET['edit_teamer'])) {
$tid = intval($_GET['edit_teamer']);
$tm = $wpdb->get_row("SELECT * FROM {$prefix}kc_teamer WHERE id=$tid");
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Teamer bearbeiten</h2>
<form method="post">
<input type="hidden" name="tid" value="'.intval($tm->id).'">
<input type="text" name="vorname" placeholder="Vorname" value="'.esc_attr($tm->vorname).'" required style="margin-bottom:8px;width:100%;padding:7px;">
<input type="text" name="nachname" placeholder="Nachname (optional)" value="'.esc_attr($tm->nachname).'" style="margin-bottom:8px;width:100%;padding:7px;">
<button name="kc_teamer_save" class="kc-btn">Speichern</button>
<a href="?page=kc_teamer" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>';
echo '</div>';
return;
}
// Neuen Teamer anlegen
if (isset($_GET['new'])) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Neuen Teamer anlegen</h2>
<form method="post">
<input type="text" name="vorname" placeholder="Vorname" required style="margin-bottom:8px;width:100%;padding:7px;">
<input type="text" name="nachname" placeholder="Nachname (optional)" style="margin-bottom:8px;width:100%;padding:7px;">
<button name="kc_teamer_save" class="kc-btn">Speichern</button>
<a href="?page=kc_teamer" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>';
echo '</div>';
return;
}
// <20>bersicht
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Teamer</h2>';
echo '<a class="kc-btn" style="float:right;margin-bottom:12px;" href="?page=kc_teamer&new=1">+ Neuer Teamer</a>';
// Sortierung
$sort = isset($_GET['sort']) ? sanitize_text_field($_GET['sort']) : 'vorname';
$order = isset($_GET['order']) ? (($_GET['order'] === 'desc') ? 'DESC' : 'ASC') : 'ASC';
$allowed_sort = ['vorname', 'nachname', 'id'];
if (!in_array($sort, $allowed_sort)) {
$sort = 'vorname';
}
// Sortier-Links
$vorname_order = ($sort === 'vorname' && $order === 'ASC') ? 'desc' : 'asc';
$nachname_order = ($sort === 'nachname' && $order === 'ASC') ? 'desc' : 'asc';
$vorname_arrow = ($sort === 'vorname') ? ($order === 'ASC' ? ' ▲' : ' ▼') : '';
$nachname_arrow = ($sort === 'nachname') ? ($order === 'ASC' ? ' ▲' : ' ▼') : '';
echo '<table class="kc-admin-table">';
echo '<thead><tr>';
echo '<th><a href="?page=kc_teamer&sort=vorname&order='.$vorname_order.'" style="text-decoration:none;color:inherit;">Vorname'.$vorname_arrow.'</a></th>';
echo '<th><a href="?page=kc_teamer&sort=nachname&order='.$nachname_order.'" style="text-decoration:none;color:inherit;">Nachname'.$nachname_arrow.'</a></th>';
echo '<th>Aktion</th>';
echo '</tr></thead><tbody>';
$teamer = $wpdb->get_results("SELECT * FROM {$prefix}kc_teamer ORDER BY {$sort} {$order}");
foreach ($teamer as $tm) {
echo "<tr>
<td>".esc_html($tm->vorname)."</td>
<td>".esc_html($tm->nachname)."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_teamer&edit_teamer={$tm->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_teamer&delete_teamer={$tm->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
</td>
</tr>";
}
echo '</tbody></table>';
echo '</div>';
}
?>

394
includes/admin-wahlen.php Normal file
View File

@@ -0,0 +1,394 @@
<?php
// Admin-Tabs (zentraler Aufruf! CSS wird global über kc-admin-style.css geladen)
function kc_admin_tabs($active) {
$menu = [
'kc_wahlen' => 'Wahlen',
'kc_teamer' => 'Teamer',
'kc_workshops' => 'Workshops',
'kc_teilnehmer' => 'Teilnehmer',
'kc_force_zuteilung'=> 'Force-Zuteilung',
'kc_zuteilungen' => 'Zuteilungen'
];
echo '<div class="kc-admin-tabs">';
foreach($menu as $slug=>$label) {
$url = admin_url('admin.php?page='.$slug);
$class = ($active==$slug) ? "kc-tabnav-active" : "";
echo "<a href=\"$url\" class=\"kc-tabnav $class\">$label</a>";
}
echo '</div>';
}
function kc_wahlen_page() {
global $wpdb;
$prefix = $wpdb->prefix;
kc_admin_tabs('kc_wahlen');
// Workshops zuweisen (Formular und Speicherung)
if (isset($_GET['zuweisen'])) {
$wahl_id = intval($_GET['zuweisen']);
$wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id=$wahl_id");
$workshops = $wpdb->get_results("SELECT * FROM {$prefix}kc_workshops ORDER BY name");
// Sortiere erst nach KC-Nummer (kleinste zuerst), dann nach Wochentag
usort($workshops, function($a, $b) {
$kcA = 999; $kcB = 999;
if (preg_match('/(kc\s*)?(\d+)/i', $a->name, $m)) { $kcA = intval($m[2]); }
if (preg_match('/(kc\s*)?(\d+)/i', $b->name, $m)) { $kcB = intval($m[2]); }
if ($kcA !== $kcB) return $kcA <=> $kcB;
$days = [
'montag' => 1,
'dienstag' => 2,
'mittwoch' => 3,
'donnerstag' => 4,
'freitag' => 5,
'samstag' => 6,
'sonntag' => 7
];
$dA = 99; $dB = 99;
$la = strtolower($a->name);
$lb = strtolower($b->name);
foreach ($days as $day => $order) {
if ($dA === 99 && strpos($la, $day) !== false) $dA = $order;
if ($dB === 99 && strpos($lb, $day) !== false) $dB = $order;
}
if ($dA !== $dB) return $dA <=> $dB;
return strcasecmp($a->name, $b->name);
});
if (isset($_POST['kc_workshop_zuweisung_save'])) {
$wpdb->delete("{$prefix}kc_wahl_workshops", ['wahl_id'=>$wahl_id]);
$anzahl_einheiten = intval($wahl->anzahl_einheiten);
for($phase=1; $phase<=$anzahl_einheiten; $phase++) {
if (!empty($_POST["phase{$phase}_workshops"])) {
foreach($_POST["phase{$phase}_workshops"] as $workshop_id) {
$wpdb->insert("{$prefix}kc_wahl_workshops", [
'wahl_id'=>$wahl_id,
'workshop_id'=>intval($workshop_id),
'phase'=>$anzahl_einheiten>1?$phase:1
]);
}
}
}
echo '<div class="notice notice-success">Workshops zugewiesen!</div>';
}
$zugeordnet = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahl_workshops WHERE wahl_id=$wahl_id");
$phase_map = [];
foreach($zugeordnet as $z) $phase_map[$z->phase][] = $z->workshop_id;
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Workshops der Wahl „'.esc_html($wahl->name).'“ zuweisen</h2>';
echo '<form method="post">';
echo '<div style="margin:12px 0;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">'
.'<label for="ws-desc-filter" style="font-weight:600;">Beschreibung filtern:</label>'
.'<input type="text" id="ws-desc-filter" placeholder="Text in Beschreibung suchen..." style="padding:6px 8px;min-width:220px;border:1px solid #ccc;border-radius:4px;">'
.'<span id="ws-filter-count" style="color:#666;font-size:90%;">Alle anzeigen</span>'
.'<div style="display:flex;gap:6px;flex-wrap:wrap;">'
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="">Alle</button>'
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="(zu ändern)">(zu ändern)</button>'
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="KC1">KC1</button>'
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="KC2">KC2</button>'
.'<button type="button" class="kc-btn" style="padding:6px 10px;" data-ws-preset="KC3">KC3</button>'
.'</div>'
.'</div>';
$anzahl_einheiten = intval($wahl->anzahl_einheiten);
for($phase=1; $phase<=$anzahl_einheiten; $phase++) {
echo "<h4>Phase {$phase}".($anzahl_einheiten==2?($phase==1?" (Früh)":" (Spät)"):"")."</h4>";
echo '<input type="text" class="ws-phase-search" placeholder="Workshops suchen..." data-phase="'.$phase.'" style="width:100%;padding:8px;margin-bottom:8px;border:1px solid #ddd;border-radius:4px;">';
echo '<div style="max-width:410px;background:#fafbfc;border-radius:9px;padding:12px 16px 9px 16px;box-shadow:0 1px 5px #0001;">';
foreach ($workshops as $ws) {
$checked = !empty($phase_map[$phase]) && in_array($ws->id, $phase_map[$phase]) ? 'checked' : '';
$label = esc_html($ws->name);
if (!empty($ws->beschreibung)) {
$label .= ' <span style="color:#666;font-size:90%;">(' . esc_html($ws->beschreibung) . ')</span>';
}
echo "<label class='ws-item-phase-{$phase}' style='display:block;margin-bottom:6px;cursor:pointer;' data-name='".esc_attr(strtolower($ws->name.' '.$ws->beschreibung))."' data-desc='".esc_attr(strtolower($ws->beschreibung))."'>"
."<input type='checkbox' name='phase{$phase}_workshops[]' value='{$ws->id}' {$checked} style='margin-right:8px;'> {$label}"
."</label>";
}
echo '</div><br>';
}
echo '<button name="kc_workshop_zuweisung_save" class="kc-btn" style="margin-top:12px;">Speichern</button>
<a href="?page=kc_wahlen" class="kc-btn del" style="margin-left:20px;">Zurück</a>
</form>';
echo '<script>
(function() {
var descInput = document.getElementById("ws-desc-filter");
var presets = Array.prototype.slice.call(document.querySelectorAll("[data-ws-preset]"));
var filterCount = document.getElementById("ws-filter-count");
var searchInputs = Array.prototype.slice.call(document.querySelectorAll(".ws-phase-search"));
function applyDescFilter() {
var q = descInput.value.toLowerCase().trim();
var totalItems = 0, shownItems = 0;
for(var p=1; p<='.intval($wahl->anzahl_einheiten).'; p++) {
var items = Array.prototype.slice.call(document.querySelectorAll(".ws-item-phase-" + p));
items.forEach(function(item) {
var desc = item.getAttribute("data-desc") || "";
totalItems++;
var match = q === "" || desc.indexOf(q) > -1;
item.style.display = match ? "block" : "none";
if (match) shownItems++;
});
}
if (filterCount) {
filterCount.textContent = q === "" ? "Alle anzeigen (" + totalItems + ")" : shownItems + " von " + totalItems + " Treffer";
}
}
function applyPhaseSearch() {
searchInputs.forEach(function(input) {
var phase = input.getAttribute("data-phase");
var search = input.value.toLowerCase().trim();
var items = Array.prototype.slice.call(document.querySelectorAll(".ws-item-phase-" + phase));
items.forEach(function(item) {
var name = item.getAttribute("data-name") || "";
var match = search === "" || name.indexOf(search) > -1;
item.style.display = match ? "block" : "none";
});
});
}
descInput.addEventListener("input", applyDescFilter);
presets.forEach(function(btn) {
btn.addEventListener("click", function() {
descInput.value = btn.getAttribute("data-ws-preset") || "";
applyDescFilter();
descInput.focus();
});
});
searchInputs.forEach(function(input) {
input.addEventListener("input", applyPhaseSearch);
});
applyDescFilter();
})();
</script>';
echo '</div>';
return;
}
// Wahl speichern (Bearbeiten)
if (isset($_POST['kc_wahl_update'])) {
$wahl_id = intval($_POST['wahl_id']);
$wpdb->update("{$prefix}kc_wahlen", [
'name' => sanitize_text_field($_POST['name']),
'beschreibung' => sanitize_textarea_field($_POST['beschreibung']),
'anzahl_einheiten' => intval($_POST['anzahl_einheiten']),
'freigegeben' => isset($_POST['freigegeben']) ? 1 : 0
], ['id' => $wahl_id]);
echo '<div class="notice notice-success">Wahl gespeichert!</div>';
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_wahlen')) . "'>";
return;
}
// Bearbeitungsformular
if (isset($_GET['edit_wahl'])) {
$wahl_id = intval($_GET['edit_wahl']);
$wahl = $wpdb->get_row("SELECT * FROM {$prefix}kc_wahlen WHERE id=$wahl_id");
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Wahl bearbeiten</h2>
<form method="post" style="max-width:600px;">
<input type="hidden" name="wahl_id" value="'.intval($wahl->id).'">
<input type="text" name="name" placeholder="Name" required value="'.esc_attr($wahl->name).'" style="margin-bottom:6px;width:100%;padding:7px;">
<input type="text" name="beschreibung" placeholder="Beschreibung" value="'.esc_attr($wahl->beschreibung).'" style="margin-bottom:6px;width:100%;padding:7px;">
<label style="display:block;margin-bottom:6px;">
<select name="anzahl_einheiten" required>
<option value="1" '.($wahl->anzahl_einheiten==1?"selected":"").'>Nur eine Workshop-Phase</option>
<option value="2" '.($wahl->anzahl_einheiten==2?"selected":"").'>Zwei Workshop-Phasen (Früh/Spät)</option>
</select> Workshop-Phasen
</label>
<label style="margin-bottom:8px;display:block;">
<input type="checkbox" name="freigegeben" '.($wahl->freigegeben?'checked':'').'> Freigegeben
</label>
<button name="kc_wahl_update" class="kc-btn" style="margin-left:0;">Speichern</button>
<a class="kc-btn del" style="margin-left:24px;" href="?page=kc_wahlen">Abbrechen</a>
</form>';
echo '</div>';
return;
}
// Neue Wahl anlegen
if(isset($_POST['kc_wahl_neu'])) {
$wpdb->insert("{$prefix}kc_wahlen", [
'name' => sanitize_text_field($_POST['name']),
'beschreibung' => sanitize_textarea_field($_POST['beschreibung']),
'anzahl_einheiten' => intval($_POST['anzahl_einheiten']),
'freigegeben' => isset($_POST['freigegeben']) ? 1 : 0
]);
echo '<div class="notice notice-success">Wahl angelegt!</div>';
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_wahlen')) . "'>";
return;
}
// Freigabe ändern
if(isset($_POST['kc_wahl_freigabe'])) {
$wpdb->update("{$prefix}kc_wahlen", ['freigegeben' => intval($_POST['kc_wahl_freigabe'])], ['id' => $_POST['wahl_id']]);
echo '<div class="notice notice-success">Wahl-Freigabe geändert!</div>';
}
// Wahl löschen (cascade: Teilnehmer, Zuteilungen, Force-Zuteilungen, Wahl-Workshop-Mapping)
if (isset($_GET['delete_wahl'])) {
$wahl_id = intval($_GET['delete_wahl']);
// delete zuteilungen for this wahl
$wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $wahl_id]);
// delete force zuteilungen
$wpdb->delete("{$prefix}kc_force_zuteilung", ['wahl_id' => $wahl_id]);
// delete teilnehmer
$wpdb->delete("{$prefix}kc_teilnehmer", ['wahl_id' => $wahl_id]);
// delete wahl-workshop mappings
$wpdb->delete("{$prefix}kc_wahl_workshops", ['wahl_id' => $wahl_id]);
// mark the wahl as deleted (soft delete)
$wpdb->update("{$prefix}kc_wahlen", ['deleted'=>1], ['id'=>$wahl_id]);
echo '<div class="notice notice-success">Wahl und zugehörige Teilnehmer/Zuteilungen wurden entfernt.</div>';
}
// Zuteilung ausführen (per Button auf der Übersicht)
if (isset($_GET['run_zuteilung'])) {
$wahl_id = intval($_GET['run_zuteilung']);
// nonce check for safety
if (!empty($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'kc_run_zuteilung_' . $wahl_id)) {
if (function_exists('kc_run_zuteilung')) {
kc_run_zuteilung($wahl_id);
// nach Zuteilung anzeigen
echo '<div class="notice notice-success">Zuteilung wurde ausgeführt.</div>';
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_wahlen&show_zuteilung=' . $wahl_id)) . "'>";
return;
} else {
echo '<div class="notice notice-error">Zuteilungsfunktion nicht verfügbar.</div>';
}
} else {
echo '<div class="notice notice-error">Ungültiger Sicherheits-Token.</div>';
}
}
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Wahlen</h2>';
$wahlen = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahlen WHERE deleted=0");
// Sortiere Übersicht: erst KC-Nummer (aufsteigend), dann Wochentag (Mo-So), dann Name
usort($wahlen, function($a, $b) {
$kcA = 999; $kcB = 999;
if (preg_match('/(kc\s*)?(\d+)/i', $a->name, $m)) { $kcA = intval($m[2]); }
if (preg_match('/(kc\s*)?(\d+)/i', $b->name, $m)) { $kcB = intval($m[2]); }
if ($kcA !== $kcB) return $kcA <=> $kcB;
$days = [
'montag' => 1,
'dienstag' => 2,
'mittwoch' => 3,
'donnerstag' => 4,
'freitag' => 5,
'samstag' => 6,
'sonntag' => 7
];
$dA = 99; $dB = 99;
$la = strtolower($a->name);
$lb = strtolower($b->name);
foreach ($days as $day => $order) {
if ($dA === 99 && strpos($la, $day) !== false) $dA = $order;
if ($dB === 99 && strpos($lb, $day) !== false) $dB = $order;
}
if ($dA !== $dB) return $dA <=> $dB;
return strcasecmp($a->name, $b->name);
});
echo '<table class="kc-admin-table">';
echo '<thead><tr><th>Name</th><th>Phasen</th><th>Kapazität (pro Phase)</th><th>Teilnehmer (pro Phase)</th><th>Freigegeben</th><th>Shortcode</th><th>Aktion</th></tr></thead><tbody>';
foreach($wahlen as $wahl) {
// compute per-phase capacity and participant counts
$anz = intval($wahl->anzahl_einheiten);
$caps = [];
$counts = [];
$insufficient = false;
for($phase=1;$phase<=$anz;$phase++) {
// total capacity = sum of max_teilnehmer for workshops assigned to this wahl and phase
$caps[$phase] = intval($wpdb->get_var($wpdb->prepare(
"SELECT COALESCE(SUM(ws.max_teilnehmer),0) FROM {$prefix}kc_workshops ws JOIN {$prefix}kc_wahl_workshops ww ON ws.id=ww.workshop_id WHERE ww.wahl_id=%d AND ww.phase=%d",
$wahl->id, $phase
)));
// number of participants for this wahl and phase
$counts[$phase] = intval($wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE wahl_id=%d AND phase=%d",
$wahl->id, $phase
)));
if ($caps[$phase] < $counts[$phase]) $insufficient = true;
}
echo '<tr>';
echo '<td>'.esc_html($wahl->name).'</td>';
echo '<td>'.intval($wahl->anzahl_einheiten).'</td>';
// capacity column (show per phase)
echo '<td>';
$cap_labels = [];
for($phase=1;$phase<=$anz;$phase++) {
$cap_labels[] = 'P'.$phase.': '.esc_html($caps[$phase]);
}
echo implode('<br>', $cap_labels);
echo '</td>';
// participants column (show per phase, highlight if exceeding capacity)
echo '<td>';
$count_labels = [];
for($phase=1;$phase<=$anz;$phase++) {
$label = 'P'.$phase.': '.esc_html($counts[$phase]);
if ($caps[$phase] < $counts[$phase]) {
$diff = $counts[$phase] - $caps[$phase];
$label .= ' <span style="color:#e12b2b;font-weight:700;">(+'.intval($diff).' fehlen)</span>';
} else {
$label .= ' <span style="color:#2b8a2b;font-weight:700;">(OK)</span>';
}
$count_labels[] = $label;
}
echo implode('<br>', $count_labels);
echo '</td>';
echo '<td>';
echo "<form method='post' style='display:inline'>";
echo "<input type='hidden' name='wahl_id' value='".intval($wahl->id)."'>";
echo "<button name='kc_wahl_freigabe' value='".($wahl->freigegeben?"0":"1")."' class='kc-btn edit' type='submit' style='background:".($wahl->freigegeben?"#e12b2b":"#b6d333").";color:#fff;font-weight:bold;'>".($wahl->freigegeben?"Schlie<EFBFBD>en":"Freigeben (zum eintragen)")."</button>";
echo "</form>";
echo '<span style="color:'.($wahl->freigegeben?"#4176be":"#d40000").';margin-left:6px;font-size:90%;font-weight:bold;">'.($wahl->freigegeben ? "freigegeben" : "geschlossen").'</span>';
echo '</td>';
echo '<td><code>[konficastle_workshopwahl wahl="'.intval($wahl->id).'"]</code></td>';
echo '<td class="kc-actions">';
echo '<a class="kc-btn" href="?page=kc_wahlen&zuweisen='.intval($wahl->id).'">Workshops zuweisen</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&edit_wahl='.intval($wahl->id).'">Bearbeiten</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&show_zuteilung='.intval($wahl->id).'">Zuteilung anzeigen</a>';
$nonce = wp_create_nonce('kc_run_zuteilung_' . intval($wahl->id));
echo '<a class="kc-btn" href="?page=kc_wahlen&run_zuteilung='.intval($wahl->id).'&_wpnonce='.$nonce.'" onclick="return confirm(\'Zuteilung wirklich starten? Dies überschreibt vorhandene Zuteilungen.\');">Zuteilung starten</a>';
echo '<a href="?page=kc_wahlen&delete_wahl='.intval($wahl->id).'" class="kc-btn del" onclick="return confirm(\'Wirklich loeschen?\');">Loeschen</a>';
echo '</td>';
echo '</tr>';
}
echo '</tbody></table>';
// Neue Wahl anlegen-Formular
echo '<hr><h2>Neue Wahl anlegen</h2>
<form method="post" style="max-width:600px;">
<input type="text" name="name" placeholder="Name" required style="margin-bottom:6px;width:100%;padding:7px;">
<input type="text" name="beschreibung" placeholder="Beschreibung" style="margin-bottom:6px;width:100%;padding:7px;">
<label style="display:block;margin-bottom:6px;">
<select name="anzahl_einheiten" required>
<option value="1">Nur eine Workshop-Phase</option>
<option value="2">Zwei Workshop-Phasen (z.B. morgens/abends)</option>
</select> Workshop-Phasen
</label>
<label style="margin-bottom:8px;display:block;"><input type="checkbox" name="freigegeben"> Freigeben (zum eintragen)</label>
<button name="kc_wahl_neu" class="kc-btn" style="margin-left:0;">Wahl anlegen</button>
</form>';
echo '</div>';
// Zeige die Zuteilung, wenn angefordert:
if (isset($_GET['show_zuteilung'])) {
$wahl_id = intval($_GET['show_zuteilung']);
if (function_exists('kc_zeige_zuteilung')) kc_zeige_zuteilung($wahl_id);
}
}
?>

View File

@@ -0,0 +1,185 @@
<?php
function kc_force_zuteilung_page() {
global $wpdb;
$prefix = $wpdb->prefix;
kc_admin_tabs('kc_force_zuteilung');
// Force-Zuteilung löschen
if (isset($_GET['delete_force'])) {
$fid = intval($_GET['delete_force']);
$wpdb->delete("{$prefix}kc_force_zuteilung", ['id' => $fid]);
echo '<div class="notice notice-success">Force-Zuteilung gelöscht!</div>';
}
// Force speichern (neu/ändern)
if (isset($_POST['kc_force_save'])) {
$data = [
'teilnehmer_id' => intval($_POST['teilnehmer_id']),
'wahl_id' => intval($_POST['wahl_id']),
'phase' => intval($_POST['phase']),
'workshop_id' => intval($_POST['workshop_id']),
'kommentar' => sanitize_text_field($_POST['kommentar'])
];
if (!empty($_POST['fid'])) {
$wpdb->update("{$prefix}kc_force_zuteilung", $data, ['id'=>intval($_POST['fid'])]);
echo '<div class="notice notice-success">Force-Zuteilung aktualisiert!</div>';
} else {
$wpdb->insert("{$prefix}kc_force_zuteilung", $data);
echo '<div class="notice notice-success">Force-Zuteilung angelegt!</div>';
}
}
// Listen für Dropdowns
$teilnehmer_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_teilnehmer ORDER BY nachname, vorname");
$workshop_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_workshops ORDER BY name");
$wahlen = $wpdb->get_results("SELECT id, name FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY name");
// Bearbeiten
if (isset($_GET['edit_force'])) {
$fid = intval($_GET['edit_force']);
$fz = $wpdb->get_row("SELECT * FROM {$prefix}kc_force_zuteilung WHERE id=$fid");
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Force-Zuteilung bearbeiten</h2>
<form method="post">
<input type="hidden" name="fid" value="'.intval($fz->id).'">
<div class="kc-form-row">
<label>Teilnehmer <span class="kc-required">*</span></label>
<select name="teilnehmer_id" id="kc-force-teilnehmer-dropdown" style="width:100%">
<option value="">Teilnehmer auswählen…</option>';
foreach($teilnehmer_liste as $t) {
echo '<option value="'.esc_attr($t->id).'"'.($fz->teilnehmer_id==$t->id?' selected':'').'>'
.esc_html($t->vorname.' '.$t->nachname.' (ID: '.$t->id.')')
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Wahl <span class="kc-required">*</span></label>
<select name="wahl_id" id="kc-force-wahl-dropdown" style="width:100%">
<option value="">Wahl auswählen…</option>';
foreach($wahlen as $w) {
echo '<option value="'.intval($w->id).'"'.($fz->wahl_id==$w->id?' selected':'').'>'
.esc_html($w->name.' (ID '.$w->id.')').'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Phase</label>
<input type="number" name="phase" value="'.intval($fz->phase).'" style="width:100%">
</div>
<div class="kc-form-row">
<label>Workshop <span class="kc-required">*</span></label>
<select name="workshop_id" id="kc-force-workshop-dropdown" style="width:100%">
<option value="">Workshop auswählen…</option>';
foreach($workshop_liste as $ws) {
echo '<option value="'.esc_attr($ws->id).'"'.($fz->workshop_id==$ws->id?' selected':'').'>'
.esc_html($ws->name)
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Kommentar</label>
<input type="text" name="kommentar" value="'.esc_attr($fz->kommentar).'" style="width:100%">
</div>
<button name="kc_force_save" class="kc-btn">Speichern</button>
<a href="?page=kc_force_zuteilung" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>
<script>
jQuery(function($){
$("#kc-force-teilnehmer-dropdown").select2({placeholder:"Teilnehmer auswählen…",allowClear:true});
$("#kc-force-wahl-dropdown").select2({placeholder:"Wahl auswählen…",allowClear:true});
$("#kc-force-workshop-dropdown").select2({placeholder:"Workshop auswählen…",allowClear:true});
});
</script>';
echo '</div>';
return;
}
// Neu anlegen
if (isset($_GET['new'])) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Neue Force-Zuteilung</h2>
<form method="post">
<div class="kc-form-row">
<label>Teilnehmer <span class="kc-required">*</span></label>
<select name="teilnehmer_id" id="kc-force-teilnehmer-dropdown" style="width:100%">
<option value="">Teilnehmer auswählen…</option>';
foreach($teilnehmer_liste as $t) {
echo '<option value="'.esc_attr($t->id).'">'
.esc_html($t->vorname.' '.$t->nachname.' (ID: '.$t->id.')')
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Wahl <span class="kc-required">*</span></label>
<select name="wahl_id" id="kc-force-wahl-dropdown" style="width:100%">
<option value="">Wahl auswählen…</option>';
foreach($wahlen as $w) {
echo '<option value="'.intval($w->id).'">'
.esc_html($w->name.' (ID '.$w->id.')').'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Phase</label>
<input type="number" name="phase" value="1" style="width:100%">
</div>
<div class="kc-form-row">
<label>Workshop <span class="kc-required">*</span></label>
<select name="workshop_id" id="kc-force-workshop-dropdown" style="width:100%">
<option value="">Workshop auswählen…</option>';
foreach($workshop_liste as $ws) {
echo '<option value="'.esc_attr($ws->id).'">'
.esc_html($ws->name)
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Kommentar</label>
<input type="text" name="kommentar" style="width:100%">
</div>
<button name="kc_force_save" class="kc-btn">Speichern</button>
<a href="?page=kc_force_zuteilung" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>
<script>
jQuery(function($){
$("#kc-force-teilnehmer-dropdown").select2({placeholder:"Teilnehmer auswählen…",allowClear:true});
$("#kc-force-wahl-dropdown").select2({placeholder:"Wahl auswählen…",allowClear:true});
$("#kc-force-workshop-dropdown").select2({placeholder:"Workshop auswählen…",allowClear:true});
});
</script>';
echo '</div>';
return;
}
// Übersicht
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Force-Zuteilungen</h2>';
echo '<a class="kc-btn" style="float:right;margin-bottom:12px;" href="?page=kc_force_zuteilung&new=1">+ Neue Force-Zuteilung</a>';
echo '<table class="kc-admin-table">';
echo '<thead><tr><th>Teilnehmer</th><th>Wahl</th><th>Phase</th><th>Workshop</th><th>Kommentar</th><th>Aktion</th></tr></thead><tbody>';
$fz = $wpdb->get_results("SELECT * FROM {$prefix}kc_force_zuteilung ORDER BY id DESC");
foreach ($fz as $f) {
// Teilnehmer-Namen holen
$teilnehmer = $wpdb->get_row($wpdb->prepare("SELECT vorname, nachname FROM {$prefix}kc_teilnehmer WHERE id=%d", $f->teilnehmer_id));
$teilnehmer_name = $teilnehmer ? ($teilnehmer->vorname . ' ' . $teilnehmer->nachname) : 'ID ' . $f->teilnehmer_id;
$workshop_name = $wpdb->get_var($wpdb->prepare("SELECT name FROM {$prefix}kc_workshops WHERE id=%d", $f->workshop_id));
$wahl_name = $wpdb->get_var($wpdb->prepare("SELECT name FROM {$prefix}kc_wahlen WHERE id=%d", $f->wahl_id));
echo "<tr>
<td>".esc_html($teilnehmer_name)."</td>
<td>".esc_html($wahl_name)." (ID ".intval($f->wahl_id).")</td>
<td>".intval($f->phase)."</td>
<td>".esc_html($workshop_name)."</td>
<td>".esc_html($f->kommentar)."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_force_zuteilung&edit_force={$f->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_force_zuteilung&delete_force={$f->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
</td>
</tr>";
}
echo '</tbody></table></div>';
}
?>