Upload files to "includes"

This commit is contained in:
2026-01-30 14:32:00 +00:00
parent 5049d22824
commit 95cb79cf0c
4 changed files with 1736 additions and 0 deletions

View File

@@ -0,0 +1,721 @@
<?php
function kc_workshops_page() {
global $wpdb;
$prefix = $wpdb->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 '<div class="notice notice-success">Workshop gelöscht!</div>';
}
// 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 '<div class="notice notice-success">Workshop aktualisiert!</div>';
} else {
$wpdb->insert("{$prefix}kc_workshops", $data);
$wid = intval($wpdb->insert_id);
echo '<div class="notice notice-success">Workshop angelegt!</div>';
}
}
// 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 '<div class="notice notice-success">Feste Teamer gespeichert!</div>';
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_workshops')) . "'>";
return;
}
// Redirect nach Speichern von wahl-spezifischen Teamern zurück zur Workshop-Bearbeitung
if ($wahl_id !== null && empty($_POST['name'])) {
echo '<div class="notice notice-success">Teamer-Zuordnung gespeichert!</div>';
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_workshops&edit_workshop=' . $wid)) . "'>";
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 '<div class="kc-admin-table-wrap">';
echo '<div class="notice notice-error">Workshop nicht gefunden.</div>';
echo '<a href="?page=kc_workshops" class="kc-btn del" style="margin-top:12px;">Zur\u00fcck zur \u00dcbersicht</a>';
echo '</div>';
return;
}
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Workshop bearbeiten: '.esc_html($ws->name).'</h2>';
// 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 '<div style="background:#e8f5e9;border:1px solid #4CAF50;border-radius:6px;padding:12px;margin-bottom:16px;">
<strong>Feste Teamer (für alle Wahlen):</strong> '.$teamer_str.'
<a href="?page=kc_workshops&edit_workshop='.$wid.'&edit_feste=1" class="kc-btn edit" style="margin-left:12px;font-size:90%;padding:4px 10px;">Bearbeiten</a>
</div>';
}
}
// Zeige Wahl/Phase-Kombinationen mit ihren Teamern
echo '<p style="margin-bottom:12px;"><strong>Teamer pro Wahl:</strong></p>';
echo '<div style="margin-bottom:12px;">';
if ($has_wahl && $has_phase) {
echo '<a href="?page=kc_workshops&edit_workshop='.$wid.'&edit_feste=1" class="kc-btn" style="background:#4CAF50;">+ Feste Teamer festlegen</a>';
}
echo '</div>';
echo '<table class="kc-admin-table" style="width:100%;margin-bottom:20px;">';
echo '<thead><tr><th>Wahl</th><th>Teamer</th><th>Aktion</th></tr></thead><tbody>';
// 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 '<tr><td colspan="3"><em style="color:#999;">Dieser Workshop ist noch keiner Wahl zugeordnet.</em></td></tr>';
} 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)) : '<em style="color:#999;">Keine</em>';
echo '<tr>
<td>'.esc_html($w->name).'</td>
<td>'.$teamer_str.'</td>
<td><a class="kc-btn edit" href="?page=kc_workshops&edit_workshop='.$wid.'&select_wahl='.$w->wahl_id.'">Bearbeiten</a></td>
</tr>';
}
}
echo '</tbody></table>';
// Wenn Feste Teamer bearbeitet werden sollen
if (isset($_GET['edit_feste'])) {
echo '<h3 style="margin-top:20px;">Feste Teamer (gelten für alle Wahlen)</h3>
<form method="post" style="max-width:600px;">
<input type="hidden" name="wid" value="'.intval($ws->id).'">
<input type="hidden" name="edit_feste" value="1">
<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Teamer auswählen</label>
<input type="text" id="teamer-search" placeholder="Suchen..." style="width:100%;padding:8px;margin-bottom:8px;border:1px solid #ddd;border-radius:4px;">
<div id="teamer-list" style="border:1px solid #ddd;border-radius:6px;padding:12px;margin-bottom:10px;max-height:300px;overflow-y:auto;background:#fafafa;">';
// Feste Teamer auslesen
if ($has_wahl && $has_phase) {
$assigned = $wpdb->get_col($wpdb->prepare(
"SELECT teamer_id FROM {$prefix}kc_workshop_teamer WHERE workshop_id=%d AND wahl_id IS NULL AND phase IS NULL",
$wid
));
} else {
$assigned = [];
}
foreach($teamer_liste as $t) {
$checked = in_array($t->id, (array)$assigned) ? ' checked' : '';
echo '<label class="teamer-item" style="display:block;margin-bottom:6px;cursor:pointer;" data-name="'.esc_attr(strtolower($t->vorname.' '.$t->nachname)).'"><input type="checkbox" name="teamer_ids[]" value="'.intval($t->id).'"'.$checked.' style="margin-right:8px;"> '.esc_html($t->vorname.' '.$t->nachname).'</label>';
}
echo '</div>
<script>
document.getElementById("teamer-search").addEventListener("input", function(e) {
var search = e.target.value.toLowerCase().trim();
var items = document.querySelectorAll(".teamer-item");
if (search === "") {
items.forEach(function(item) { item.style.display = "block"; });
return;
}
var searchTerms = search.split(",").map(function(term) { return term.trim(); }).filter(function(term) { return term.length > 0; });
items.forEach(function(item) {
var name = item.getAttribute("data-name");
var matches = false;
for (var i = 0; i < searchTerms.length; i++) {
if (name.indexOf(searchTerms[i]) > -1) {
matches = true;
break;
}
}
item.style.display = matches ? "block" : "none";
});
});
</script>
<button name="kc_workshop_save" class="kc-btn">Speichern</button>
<a href="?page=kc_workshops&edit_workshop='.$wid.'" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>';
}
// 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 '<div style="background:#fee;border:1px solid #e57373;border-radius:8px;padding:12px;margin-top:12px;max-width:600px;">Wahl nicht gefunden.</div>';
echo '<a href="?page=kc_workshops&edit_workshop='.$wid.'" class="kc-btn del" style="margin-left:12px;margin-top:12px;display:inline-block;">Abbrechen</a>';
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 '<div style="background:#f9f9f9;border:1px solid #ddd;border-radius:8px;padding:20px;margin-top:20px;max-width:600px;">';
echo '<h3 style="margin-top:0;">Teamer für: '.esc_html($wahl_info->name).'</h3>';
echo '<p style="margin-bottom:16px;">Sollen die Teamer für eine <strong>bestimmte Phase</strong> gelten?';
echo ' <span style="color:#666;">(Dieser Workshop ist zugeordnet: '.(!empty($assigned_phases) ? 'Phase '.esc_html(implode(', ', $assigned_phases)) : 'keiner Phase').')</span></p>';
echo '<div style="display:flex;gap:12px;flex-wrap:wrap;">';
// "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 '<a href="?page=kc_workshops&edit_workshop='.$wid.'&edit_wahl='.$select_wahl_id.'" class="kc-btn" style="background:#4CAF50;">Für alle Phasen</a>';
}
// Buttons nur für tatsächlich zugeordnete Phasen
foreach ($assigned_phases as $p) {
echo '<a href="?page=kc_workshops&edit_workshop='.$wid.'&edit_wahl='.$select_wahl_id.'&edit_phase='.$p.'" class="kc-btn edit">Phase '.$p.'</a>';
}
if (empty($assigned_phases)) {
echo '<span style="color:#a33;">Dieser Workshop ist in dieser Wahl aktuell keiner Phase zugeordnet.</span>';
}
echo '</div>';
echo '<a href="?page=kc_workshops&edit_workshop='.$wid.'" class="kc-btn del" style="margin-left:12px;margin-top:12px;display:inline-block;">Abbrechen</a>';
echo '</div>';
} else {
// Nur eine Phase: direkt weiterleiten
echo "<meta http-equiv='refresh' content='0;url=" . esc_url(admin_url('admin.php?page=kc_workshops&edit_workshop=' . $wid . '&edit_wahl=' . $select_wahl_id . '&edit_phase=1')) . "'>";
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 '<div style="background:#fee;border:1px solid #e57373;border-radius:8px;padding:12px;margin-top:12px;max-width:600px;">Wahl nicht gefunden.</div>';
echo '<a href="?page=kc_workshops&edit_workshop='.$wid.'" class="kc-btn del" style="margin-left:12px;margin-top:12px;display:inline-block;">Abbrechen</a>';
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 '<h3 style="margin-top:20px;">'.$title.'</h3>
<form method="post" style="max-width:600px;">
<input type="hidden" name="wid" value="'.intval($ws->id).'">
<input type="hidden" name="select_wahl_id" value="'.intval($edit_wahl_id).'">';
if ($edit_phase !== null) {
echo '<input type="hidden" name="select_phase" value="'.intval($edit_phase).'">';
}
echo '<label style="display:block;margin:8px 0 4px 0;font-weight:700;">Teamer auswählen</label>
<input type="text" id="teamer-search" placeholder="Suchen..." style="width:100%;padding:8px;margin-bottom:8px;border:1px solid #ddd;border-radius:4px;">
<div id="teamer-list" style="border:1px solid #ddd;border-radius:6px;padding:12px;margin-bottom:10px;max-height:300px;overflow-y:auto;background:#fafafa;">';
// Stelle sicher, dass Spalten existieren
$columns = $wpdb->get_col("SHOW COLUMNS FROM {$prefix}kc_workshop_teamer");
$has_wahl = in_array('wahl_id', $columns);
$has_phase = in_array('phase', $columns);
// Teamer für diese Wahl/Phase auslesen
if ($has_wahl && $has_phase) {
if ($edit_phase !== null) {
$assigned = $wpdb->get_col($wpdb->prepare(
"SELECT teamer_id FROM {$prefix}kc_workshop_teamer WHERE workshop_id=%d AND wahl_id=%d AND phase=%d",
$wid, $edit_wahl_id, $edit_phase
));
} else {
$assigned = $wpdb->get_col($wpdb->prepare(
"SELECT teamer_id FROM {$prefix}kc_workshop_teamer WHERE workshop_id=%d AND wahl_id=%d AND phase IS NULL",
$wid, $edit_wahl_id
));
}
} else {
$assigned = [];
}
foreach($teamer_liste as $t) {
$checked = in_array($t->id, (array)$assigned) ? ' checked' : '';
echo '<label class="teamer-item" style="display:block;margin-bottom:6px;cursor:pointer;" data-name="'.esc_attr(strtolower($t->vorname.' '.$t->nachname)).'"><input type="checkbox" name="teamer_ids[]" value="'.intval($t->id).'"'.$checked.' style="margin-right:8px;"> '.esc_html($t->vorname.' '.$t->nachname).'</label>';
}
echo '</div>
<script>
document.getElementById("teamer-search").addEventListener("input", function(e) {
var search = e.target.value.toLowerCase().trim();
var items = document.querySelectorAll(".teamer-item");
if (search === "") {
items.forEach(function(item) { item.style.display = "block"; });
return;
}
// Teile Suchbegriffe an Kommas
var searchTerms = search.split(",").map(function(term) { return term.trim(); }).filter(function(term) { return term.length > 0; });
items.forEach(function(item) {
var name = item.getAttribute("data-name");
var matches = false;
// Prüfe ob einer der Suchbegriffe im Namen vorkommt
for (var i = 0; i < searchTerms.length; i++) {
if (name.indexOf(searchTerms[i]) > -1) {
matches = true;
break;
}
}
item.style.display = matches ? "block" : "none";
});
});
</script>
<button name="kc_workshop_save" class="kc-btn">Speichern</button>
<a href="?page=kc_workshops&edit_workshop='.$wid.'" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>';
} else {
// Workshop-Details bearbeiten
echo '<h3 style="margin-top:20px;">Workshop-Details</h3>
<form method="post" style="max-width:600px;">
<input type="hidden" name="wid" value="'.intval($ws->id).'">
<input type="text" name="name" placeholder="Name" value="'.esc_attr($ws->name).'" required style="margin-bottom:8px;width:100%;padding:7px;">
<input type="text" name="beschreibung" placeholder="Beschreibung (Bitte KC eintragen)" value="'.esc_attr($ws->beschreibung).'" style="margin-bottom:8px;width:100%;padding:7px;">
<div style="display:flex;gap:8px;margin-bottom:8px;">
<input type="number" name="min_teilnehmer" placeholder="Min. Teilnehmer" value="'.(isset($ws->min_teilnehmer)?intval($ws->min_teilnehmer):0).'" style="width:120px;padding:7px;">
<input type="number" name="max_teilnehmer" placeholder="Max. Teilnehmer" value="'.intval($ws->max_teilnehmer).'" required style="width:120px;padding:7px;">
</div>
<button name="kc_workshop_save" class="kc-btn">Speichern</button>
<a href="?page=kc_workshops" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>';
}
echo '</div>';
return;
}
// Neuer Workshop anlegen
if (isset($_GET['new'])) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Neuen Workshop anlegen</h2>';
echo '<form method="post" id="kc-new-workshop-form" style="max-width:600px;">';
echo '<input type="text" name="name" placeholder="Name" required style="margin-bottom:8px;width:100%;padding:7px;" id="ws-name">';
echo '<input type="text" name="beschreibung" placeholder="Beschreibung (Bitte KC eintragen)" style="margin-bottom:8px;width:100%;padding:7px;" id="ws-desc">';
echo '<div style="display:flex;gap:8px;margin-bottom:8px;">';
echo '<input type="number" name="min_teilnehmer" placeholder="Min. Teilnehmer" value="0" style="width:120px;padding:7px;" id="ws-min">';
echo '<input type="number" name="max_teilnehmer" placeholder="Max. Teilnehmer" required style="width:120px;padding:7px;" id="ws-max">';
echo '</div>';
echo '<button name="kc_workshop_save" class="kc-btn">Speichern</button>';
echo '<a href="?page=kc_workshops" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>';
echo '</form>';
echo '</div>';
return;
}
// Übersicht
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Workshops</h2>';
echo '<a class="kc-btn" style="float:right;margin-bottom:12px;" href="?page=kc_workshops&new=1">+ Neuer Workshop</a>';
echo '<div style="margin:12px 0;clear:both;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">'
.'<label for="kc-desc-filter" style="font-weight:600;">Beschreibung filtern:</label>'
.'<input type="text" id="kc-desc-filter" placeholder="Text in Beschreibung suchen..." style="padding:6px 8px;min-width:220px;border:1px solid #ccc;border-radius:4px;">'
.'<span id="kc-desc-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 kc-desc-quick-btn" style="padding:6px 10px;" data-desc-preset="">Alle</button>'
.'<button type="button" class="kc-btn kc-desc-quick-btn" style="padding:6px 10px;" data-desc-preset="(zu ändern)">(zu ändern)</button>'
.'<button type="button" class="kc-btn kc-desc-quick-btn" style="padding:6px 10px;" data-desc-preset="KC1">KC1</button>'
.'<button type="button" class="kc-btn kc-desc-quick-btn" style="padding:6px 10px;" data-desc-preset="KC2">KC2</button>'
.'<button type="button" class="kc-btn kc-desc-quick-btn" style="padding:6px 10px;" data-desc-preset="KC3">KC3</button>'
.'<button type="button" class="kc-btn kc-desc-quick-btn" style="padding:6px 10px;" data-desc-preset="NOTEAMER">Ohne Teamer</button>'
.'</div>'
.'</div>';
echo '<div style="margin:12px 0;display:flex;gap:8px;align-items:center;flex-wrap:wrap;">'
.'<label for="kc-wahl-filter" style="font-weight:600;">Wahlen filtern:</label>'
.'<input type="text" id="kc-wahl-filter" placeholder="Wahl suchen..." style="padding:6px 8px;min-width:220px;border:1px solid #ccc;border-radius:4px;">'
.'<span id="kc-wahl-filter-count" style="color:#666;font-size:90%;">Alle anzeigen</span>'
.'</div>';
// 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 '<table class="kc-admin-table">';
echo '<thead><tr>';
echo '<th><a href="?page=kc_workshops&sort=name&order='.$name_order.'" style="text-decoration:none;color:inherit;">Name'.$name_arrow.'</a></th>';
echo '<th><a href="?page=kc_workshops&sort=beschreibung&order='.$beschreibung_order.'" style="text-decoration:none;color:inherit;">Beschreibung'.$beschreibung_arrow.'</a></th>';
echo '<th>Teilnehmer (Min - Max)</th>';
echo '<th>Teamer</th>';
echo '<th>Wahlen</th>';
echo '<th>Aktion</th>';
echo '</tr></thead><tbody>';
$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 = '<em style="color:#999;">Keine</em>';
// 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) : '<em style="color:#999;">Keine</em>';
echo "<tr>
<td>".esc_html($ws->name)."</td>
<td>".esc_html($ws->beschreibung)."</td>
<td>".(isset($ws->min_teilnehmer)?intval($ws->min_teilnehmer):0)." - ".intval($ws->max_teilnehmer)."</td>
<td>". $tnames_str ."</td>
<td>". $wahl_str ."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_workshops&edit_workshop={$ws->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_workshops&delete_workshop={$ws->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
</td>
</tr>";
}
echo '</tbody></table>';
echo '<script>
(function() {
var descInput = document.getElementById("kc-desc-filter");
var wahlInput = document.getElementById("kc-wahl-filter");
if (!descInput || !wahlInput) return;
var rows = Array.prototype.slice.call(document.querySelectorAll(".kc-admin-table tbody tr"));
var descStatus = document.getElementById("kc-desc-filter-count");
var wahlStatus = document.getElementById("kc-wahl-filter-count");
var total = rows.length;
var quickBtns = Array.prototype.slice.call(document.querySelectorAll(".kc-desc-quick-btn"));
// Lade gespeicherte Filter-Werte aus localStorage
var savedDescValue = localStorage.getItem("kc_workshops_desc_filter") || "";
var savedWahlValue = localStorage.getItem("kc_workshops_wahl_filter") || "";
var savedQuickFilters = (localStorage.getItem("kc_workshops_quick_filters") || "").split(",").filter(function(v) { return v.length > 0; });
descInput.value = savedDescValue;
wahlInput.value = savedWahlValue;
// Restore active buttons
quickBtns.forEach(function(btn) {
if (savedQuickFilters.indexOf(btn.getAttribute("data-desc-preset")) !== -1) {
btn.style.fontWeight = "bold";
btn.style.border = "2px solid #333";
}
});
function applyFilters() {
var descQuery = descInput.value.toLowerCase().trim();
var wahlQuery = wahlInput.value.toLowerCase().trim();
// Sammle aktive Quick-Filter (ausser "zu ändern" und "Ohne Teamer")
var activeQuickFilters = [];
var zuAendernActive = false;
var noTeamerActive = false;
quickBtns.forEach(function(btn) {
var preset = btn.getAttribute("data-desc-preset");
if (btn.style.fontWeight === "bold") {
if (preset === "NOTEAMER") {
noTeamerActive = true;
} else if (preset === "(zu ändern)") {
zuAendernActive = true;
} else if (preset !== "") {
activeQuickFilters.push(preset.toLowerCase());
}
}
});
var shown = 0;
rows.forEach(function(row) {
var descCell = row.querySelector("td:nth-child(2)");
var teamerCell = row.querySelector("td:nth-child(4)");
var wahlCell = row.querySelector("td:nth-child(5)");
if (!descCell || !teamerCell || !wahlCell) return;
var descText = descCell.textContent.toLowerCase();
var wahlText = wahlCell.textContent.toLowerCase();
var teamerText = teamerCell.textContent.toLowerCase();
// Text-Filter
var descMatch = descQuery === "" || descText.indexOf(descQuery) > -1;
var wahlMatch = wahlQuery === "" || wahlText.indexOf(wahlQuery) > -1;
// Quick-Filter (KC1/KC2/KC3 mit OR, "zu ändern" und "Ohne Teamer" mit UND)
var quickMatch = true;
if (activeQuickFilters.length > 0 || zuAendernActive || noTeamerActive) {
quickMatch = false;
// Prüfe normale Quick-Filter (OR - eine muss passen)
if (activeQuickFilters.length > 0) {
for (var i = 0; i < activeQuickFilters.length; i++) {
if (descText.indexOf(activeQuickFilters[i]) > -1) {
quickMatch = true;
break;
}
}
} else if (zuAendernActive || noTeamerActive) {
// Wenn nur "zu ändern" oder "Ohne Teamer" aktiv sind
quickMatch = true;
}
// Prüfe "zu ändern" Filter (UND)
if (quickMatch && zuAendernActive && descText.indexOf("(zu ändern)") === -1) {
quickMatch = false;
}
// Prüfe "Ohne Teamer" Filter (UND)
if (quickMatch && noTeamerActive && teamerText.indexOf("keine") === -1) {
quickMatch = false;
}
}
var show = descMatch && wahlMatch && quickMatch;
row.style.display = show ? "" : "none";
if (show) shown++;
});
if (descStatus) {
descStatus.textContent = descQuery === "" && activeQuickFilters.length === 0 && !zuAendernActive && !noTeamerActive ? "Alle anzeigen (" + total + ")" : shown + " von " + total + " Treffer";
}
if (wahlStatus) {
wahlStatus.textContent = wahlQuery === "" ? "Alle anzeigen (" + total + ")" : shown + " von " + total + " Treffer";
}
// Speichere Filter-Werte in localStorage
localStorage.setItem("kc_workshops_desc_filter", descInput.value);
localStorage.setItem("kc_workshops_wahl_filter", wahlInput.value);
var activeFilters = [];
quickBtns.forEach(function(btn) {
if (btn.style.fontWeight === "bold") {
activeFilters.push(btn.getAttribute("data-desc-preset"));
}
});
localStorage.setItem("kc_workshops_quick_filters", activeFilters.join(","));
}
descInput.addEventListener("input", applyFilters);
wahlInput.addEventListener("input", applyFilters);
quickBtns.forEach(function(btn) {
btn.addEventListener("click", function(e) {
e.preventDefault();
var preset = btn.getAttribute("data-desc-preset");
// "Alle" Button setzt alles zurück
if (preset === "") {
quickBtns.forEach(function(b) {
b.style.fontWeight = "";
b.style.border = "";
});
descInput.value = "";
wahlInput.value = "";
localStorage.removeItem("kc_workshops_desc_filter");
localStorage.removeItem("kc_workshops_wahl_filter");
localStorage.removeItem("kc_workshops_quick_filters");
applyFilters();
descInput.focus();
} else if (preset === "KC1" || preset === "KC2" || preset === "KC3") {
// KC-Buttons sind exklusiv: deaktiviere andere KC-Buttons
quickBtns.forEach(function(b) {
var bpreset = b.getAttribute("data-desc-preset");
if (bpreset === "KC1" || bpreset === "KC2" || bpreset === "KC3") {
b.style.fontWeight = "";
b.style.border = "";
}
});
// Aktiviere den angeklickten Button
btn.style.fontWeight = "bold";
btn.style.border = "2px solid #333";
applyFilters();
} else {
// Toggle für "(zu ändern)" und "Ohne Teamer"
if (btn.style.fontWeight === "bold") {
btn.style.fontWeight = "";
btn.style.border = "";
} else {
btn.style.fontWeight = "bold";
btn.style.border = "2px solid #333";
}
applyFilters();
}
});
});
applyFilters();
})();
</script>';
echo '</div>';
}
?>

View File

@@ -0,0 +1,238 @@
<?php
add_shortcode('konficastle_workshop_ergebnis', function($atts) {
$atts = shortcode_atts(['wahl'=>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 '<div style="color:#e12b2b;font-weight:bold;"></div>';
// 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 '<style>
.kc-result{font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:#222;}
.kc-result h3{margin-top:0;text-align:center;color:#154a3b;}
.kc-phase{margin:18px 0;padding:12px;border-radius:12px;background:#fbfffe;border:1px solid #e6f3ee;}
/* Responsive Kachel-Layout: auto-fit with minmax so cards wrap based on viewport */
.kc-result .kc-inner { max-width:1100px; margin:0 auto; padding:0 14px; box-sizing:border-box; }
.kc-workshops-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));gap:14px;margin-top:12px;align-items:start;}
.kc-workshop-card{background:#ffffff;border-radius:12px;overflow:hidden;border:1px solid #e9f4f0;box-shadow:0 2px 6px rgba(8,38,28,0.04);width:100%;}
/* Zentrierter, prominenter Titelbereich in der Mitte oben der Kachel */
.kc-workshop-card .title{background:linear-gradient(90deg, rgba(45,166,106,0.04), rgba(13,89,71,0.02));display:flex;flex-wrap:wrap;align-items:center;justify-content:center;padding:18px 12px;font-weight:800;color:#0d5947;font-size:1.06rem;letter-spacing:0.2px;border-bottom:1px solid rgba(229,244,240,0.8);gap:12px;}
.kc-workshop-card .title .count{color:#6b6b6b;font-weight:600;font-size:0.9rem;margin-left:6px;}
.kc-workshop-card .title .teamers{font-weight:600;color:#145a47;font-size:0.9rem;opacity:0.92;}
.kc-workshop-card .title .teamers small{font-weight:500;color:#4c7a6a;opacity:0.9;font-size:0.85rem;margin-left:6px;}
/* Content-Bereich unter dem Titel mit etwas mehr Luft */
.kc-workshop-card .content{padding:12px 18px 18px 18px;}
.kc-participants{display:grid;grid-template-columns:1fr 1fr;gap:6px 12px;font-size:0.95rem;color:#2b2b2b;}
.kc-participant{padding:6px 8px;border-radius:6px;background:transparent;}
.kc-participant.me{background:#fffbe6;border:1px solid #ffeab2;}
.kc-notassigned{background:#fff6f6;border:1px solid #ffd2d2;padding:12px;border-radius:10px;margin-top:12px;}
/* Auf sehr kleinen Bildschirmen die Teilnehmer ebenfalls einspaltig */
@media(max-width:700px){ .kc-participants{grid-template-columns:1fr;} }
</style>';
echo '<div class="kc-result">';
echo '<h3>Ergebnis für diese Wahl</h3>';
// 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 <br> nach "Deine Zuteilung"
echo '<div style="background:#e8f9ef;border-left:6px solid #2da66a;padding:12px 14px;margin-bottom:12px;border-radius:8px;">';
echo '<b>Deine Zuteilung</b><ul style="margin:6px 0 0 16px;">';
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 '<li>'.esc_html($mr->vorname.' '.$mr->nachname).' (Phase '.intval($mr->phase).') — '.esc_html($wslabel).'</li>';
}
echo '</ul></div>';
$personal_shown = true;
}
}
// Für jede Phase: Workshops nebeneinander darstellen
foreach($phases as $phase) {
echo '<div class="kc-phase">';
echo '<h4 style="margin:6px 0;text-align:center;">Phase '.intval($phase).'</h4>';
echo '<div class="kc-workshops-grid">';
// 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 '<div class="kc-workshop-card">';
// Teamernamen (falls vorhanden) anzeigen - mit Phase-Key
$teamer_html = '';
$teamer_key = intval($d['id']) . '_' . intval($phase);
if (!empty($ws_teamers[$teamer_key])) {
$teamer_html = '<span class="teamers">'.esc_html(implode(', ', $ws_teamers[$teamer_key])).'</span>';
}
echo '<div class="title">'.esc_html($ws_name).' '. $teamer_html .'<span class="count">('.$count.' TN)</span></div>';
echo '<div class="content">';
echo '<div class="kc-participants">';
foreach($teilnehmer as $t) {
$is_me = in_array(intval($t->id), $my_ids);
$name = esc_html($t->vorname.' '.$t->nachname);
$label = $name . ' <span style="color:#6b6b6b;font-size:85%;">('.intval($t->phase).')</span>';
echo '<div class="kc-participant'.($is_me ? ' me' : '').'">'. $label .'</div>';
}
echo '</div>'; // kc-participants
echo '</div>'; // content
echo '</div>';
}
echo '</div>'; // kc-workshops-grid
echo '</div>'; // kc-phase
}
// Extra: Nicht zugeteilt
if (!empty($nicht_zugeteilt)) {
echo '<div class="kc-notassigned">';
echo '<b>Nicht zugeteilt:</b><br>';
echo '<ul style="margin:6px 0 0 18px;">';
foreach($nicht_zugeteilt as $t) {
echo '<li>'.esc_html($t->vorname.' '.$t->nachname).' (Phase '.intval($t->phase).')</li>';
}
echo '</ul></div>';
}
echo '</div>'; // kc-result
return ob_get_clean();
});
?>

356
includes/frontend-form.php Normal file
View File

@@ -0,0 +1,356 @@
<?php
add_shortcode('konficastle_workshopwahl', function($atts) {
$atts = shortcode_atts(['wahl'=>null], $atts);
$wahl_id = intval($atts['wahl']);
global $wpdb;
// KRITISCHER TEST: Ausgabe ganz am Anfang
//$debug_output = '<div style="background:yellow;padding:20px;margin:20px 0;border:3px solid red;">';
//$debug_output .= '<h3>DEBUG INFO</h3>';
//$debug_output .= 'Shortcode läuft!<br>';
//$debug_output .= 'REQUEST_METHOD: ' . ($_SERVER['REQUEST_METHOD'] ?? 'NICHT GESETZT') . '<br>';
//$debug_output .= 'POST vorhanden: ' . (empty($_POST) ? 'NEIN' : 'JA') . '<br>';
//$debug_output .= 'POST Inhalt: <pre>' . print_r($_POST, true) . '</pre>';
//$debug_output .= '</div>';
$wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $wahl_id));
if(!$wahl || !$wahl->freigegeben) {
return $debug_output . '<div class="kc-error-msg">Die Workshopwahl ist aktuell nicht freigeschaltet.</div>';
}
// 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 .= '<div style="background:lime;padding:10px;">POST WIRD VERARBEITET!</div>';
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();
?>
<style>
.kc-form-container {background:#f8fbe7; border-left:8px solid #b6d333; max-width:600px; margin:40px auto; padding:32px 28px; border-radius:14px; box-shadow:0 2px 8px #b6d33322;}
.kc-form-container h2 {margin-top:0; font-size:2em; font-weight:700;}
.kc-form-container label {font-weight:600;}
.kc-form-row {margin-bottom:20px;}
.kc-form-row input[type="text"], .kc-form-row select {
width:100%; padding:10px 12px; border-radius:7px; border:1.2px solid #aac484; background:#fafcf6; font-size:1.09em;
transition:border 0.17s;
}
.kc-form-row input[type="text"]:focus, .kc-form-row select:focus {border:1.8px solid #b6d333;}
.kc-form-row input[type="submit"] {
background:#326dd2; color:#fff; font-weight:600; font-size:1.15em;
padding:11px 30px; border:0; border-radius:8px; cursor:pointer; margin-top:5px; box-shadow:0 2px 6px #aac48422;
}
.kc-form-row input[type="submit"]:hover {background:#2559a2;}
.kc-required {color:#d82626; font-weight:bold;}
.kc-success-msg {color:#21952c; background:#e3f7e4; padding:16px 20px; border-radius:8px; font-weight:600; margin-bottom:15px; text-align:center;}
.kc-error-msg {color:#a80000; background:#ffeaea; padding:16px 20px; border-radius:8px; font-weight:600; margin-bottom:15px; text-align:center;}
@media (max-width: 700px) {.kc-form-container {padding:17px 7px;}}
</style>
<?php echo $debug_output; ?>
<div class="kc-form-container">
<h2><?= esc_html($wahl->name) ?></h2>
<?php if($wahl->beschreibung): ?>
<div style="margin-bottom:12px; color:#3b3b3b;"><?= nl2br(esc_html($wahl->beschreibung)) ?></div>
<?php endif; ?>
<?php if ($error_message): ?>
<div class="kc-error-msg"><?= esc_html($error_message) ?></div>
<?php endif; ?>
<?php if ($success_redirect): ?>
<div class="kc-success-msg">
Danke! Deine Wünsche wurden gespeichert.<br>
Erfolgreich eingefügte IDs: <?= implode(', ', $inserted_ids) ?>
</div>
<?php else: ?>
<form method="post" autocomplete="off">
<input type="hidden" name="kc_workshop_submit" value="1">
<div class="kc-form-row">
<label>Vorname <span class="kc-required">*</span></label>
<input type="text" name="vorname" required>
</div>
<div class="kc-form-row">
<label>Nachname <span class="kc-required">*</span></label>
<input type="text" name="nachname" required>
</div>
<?php for($phase=1; $phase<=$phasen; $phase++): ?>
<div class="kc-form-row"><b>
<?php if($phasen>1): ?>Phase <?= $phase ?><?php endif; ?>
</b></div>
<?php for($w=1; $w<=3; $w++): ?>
<div class="kc-form-row">
<label>
<span style="font-weight:bold;color:#2355be;"><?= $phase ?>. Phase; <?= $w ?>. Wunsch</span> <span class="kc-required">*</span>
</label>
<select name="phase<?= $phase ?>_wunsch<?= $w ?>" required>
<option value="">Bitte wähle einen Workshop aus</option>
<?php foreach(($workshops_by_phase[$phase] ?? []) as $ws): ?>
<option value="<?= esc_attr($ws->id) ?>"><?= esc_html($ws->name) ?></option>
<?php endforeach; ?>
</select>
</div>
<?php endfor; ?>
<script>
(function(){
// Disable already selected options within this phase and validate on submit
var phase = <?= json_encode($phase) ?>;
document.addEventListener('DOMContentLoaded', function(){
var selector = 'select[name^="phase' + phase + '_wunsch"]';
var selects = Array.prototype.slice.call(document.querySelectorAll(selector));
if (!selects.length) return;
function refresh(){
var values = selects.map(function(s){ return s.value; }).filter(function(v){ return v !== ''; });
selects.forEach(function(s){
Array.prototype.slice.call(s.options).forEach(function(opt){
if (opt.value === '') { opt.disabled = false; return; }
// disable option if selected in another select
var usedElsewhere = values.indexOf(opt.value) !== -1 && s.value !== opt.value;
opt.disabled = usedElsewhere;
});
});
}
selects.forEach(function(s){ s.addEventListener('change', refresh); });
// initial
refresh();
// Add submit-time validation to prevent duplicates (safety)
var form = selects[0] && selects[0].form;
if (form) {
form.addEventListener('submit', function(e){
var vals = selects.map(function(s){ return s.value; }).filter(function(v){ return v !== ''; });
var unique = vals.filter(function(v,i,a){ return a.indexOf(v) === i; });
if (vals.length !== unique.length) {
e.preventDefault();
alert('Bitte wähle in Phase ' + phase + ' drei verschiedene Workshops (keine Duplikate).');
}
});
}
});
})();
</script>
<?php endfor; ?>
<div class="kc-form-row">
<input type="submit" name="submit_finish" value="Senden" />
</div>
</form>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
});
// Shortcode: allow teamers to create a workshop after entering password
add_shortcode('konficastle_teamer_create', function($atts) {
global $wpdb;
$prefix = $wpdb->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 = '<div class="kc-error-msg">Ungültiger Request.</div>';
} 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 = '<div class="kc-error-msg">Falsches Passwort.</div>';
} 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 = '<div class="kc-error-msg">Bitte alle Felder ausfüllen.</div>';
} 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 = '<div class="kc-error-msg">Die minimale Teilnehmerzahl darf nicht größer als die maximale sein.</div>';
} 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 = '<div class="kc-success-msg">Workshop angelegt und Teamer zugeordnet.</div>';
} else {
$msg = '<div class="kc-error-msg">Fehler beim Anlegen des Workshops.</div>';
}
}
}
}
}
}
ob_start();
$nonce = wp_create_nonce('kc_teamer_create_action');
?>
<div class="kc-form-container">
<h2>Teamer: Workshop erstellen</h2>
<?php echo $msg; ?>
<form method="post">
<input type="hidden" name="kc_teamer_create_nonce" value="<?= esc_attr($nonce) ?>">
<div class="kc-form-row">
<label>Passwort</label>
<input type="password" name="kc_teamer_pw" required />
</div>
<div class="kc-form-row">
<label>Vorname</label>
<input type="text" name="vorname" required />
</div>
<div class="kc-form-row">
<label>Nachname</label>
<input type="text" name="nachname" required />
</div>
<div class="kc-form-row">
<label>Workshop-Name</label>
<input type="text" name="workshop_name" required />
</div>
<div class="kc-form-row" style="display:flex;gap:8px;">
<div style="flex:1"><label>Min. Teilnehmer</label><input type="number" name="min_teilnehmer" value="0" required /></div>
<div style="flex:1"><label>Max. Teilnehmer</label><input type="number" name="max_teilnehmer" value="10" required /></div>
</div>
<div class="kc-form-row">
<input type="submit" value="Erstellen" />
</div>
</form>
</div>
<?php
return ob_get_clean();
});

View File

@@ -0,0 +1,421 @@
<?php
// Zuteilungslogik für Konficastle Workshopwahl
function kc_run_zuteilung($wahl_id) {
global $wpdb;
$prefix = $wpdb->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;
}
}
}