Upload files to "includes"
This commit is contained in:
721
includes/admin-workshops.php
Normal file
721
includes/admin-workshops.php
Normal 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>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
238
includes/frontend-ergebnis.php
Normal file
238
includes/frontend-ergebnis.php
Normal 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
356
includes/frontend-form.php
Normal 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();
|
||||||
|
});
|
||||||
421
includes/zuteilungslogik.php
Normal file
421
includes/zuteilungslogik.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user