Compare commits

..

13 Commits

Author SHA1 Message Date
ProgrammGamer
59e52faf1b test: node runner image
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 12s
2026-01-30 18:10:23 +01:00
ProgrammGamer
61d2a0398e test: node runner image 2026-01-30 18:09:58 +01:00
ProgrammGamer
deab49ce94 test: runner persistence fixed
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 1s
2026-01-30 18:06:48 +01:00
ProgrammGamer
f216c558c3 css
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 12s
2026-01-30 17:51:13 +01:00
ProgrammGamer
6203a74a2d reorder css
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-01-30 17:41:56 +01:00
ProgrammGamer
7e661c4a58 add deployment
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-01-30 17:31:00 +01:00
ProgrammGamer
306868735c deleted 2026-01-30 17:27:43 +01:00
ProgrammGamer
e57ac1bd6d gesperte Wahl angepasst 2026-01-30 17:12:12 +01:00
ProgrammGamer
7a45a78753 Ich habe im Frondend die Ergebniss anzeifge verbessert 2026-01-30 17:08:58 +01:00
ProgrammGamer
440956320d adding encryption to form and addid form checks 2026-01-30 17:00:32 +01:00
ProgrammGamer
5942fe7e18 generated importent files 2026-01-30 16:33:09 +01:00
ProgrammGamer
80cf6539ed replacing of header 2026-01-30 15:54:15 +01:00
ProgrammGamer
0c35946b58 Adding plugin header for Workshop-Wahlen 2026-01-30 15:52:55 +01:00
12 changed files with 542 additions and 476 deletions

View File

@@ -0,0 +1,49 @@
name: Deploy Workshop-Wahlen (DEV / PROD)
on:
push:
branches:
- develop
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
# DEV
- name: Deploy to DEV
if: github.ref == 'refs/heads/develop'
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SFTP_HOST }}
port: ${{ secrets.SFTP_PORT }}
username: ${{ secrets.SFTP_USER }}
password: ${{ secrets.SFTP_PASS }}
source: |
assets
includes
*.php
README.md
target: "/dev.konfi-castle.com/wp-content/plugins/konficastle-workshopwahl/"
rm: true
# PROD
- name: Deploy to PROD
if: github.ref == 'refs/heads/main'
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SFTP_HOST }}
port: ${{ secrets.SFTP_PORT }}
username: ${{ secrets.SFTP_USER }}
password: ${{ secrets.SFTP_PASS }}
source: |
assets
includes
*.php
README.md
target: "/httpdocs/wp-content/plugins/konficastle-workshopwahl/"
rm: true

View File

@@ -1,37 +0,0 @@
# Copilot Instructions for Workshop-Wahlen (Production)
## Project Overview
This WordPress plugin manages workshop elections for Konfi-Castle events. It provides an admin backend (elections, workshops, participants, teamers, assignments) and a participant frontend.
## Architecture & Components
- **Main file:** `konficastle-workshopwahl.php` Initializes hooks, loads assets, admin menus.
- **Backend:**
- `includes/` with modules for elections, workshops, participants, teamers, assignments, data management, force assignment
- Core assignment logic: `includes/zuteilungslogik.php`
- **Frontend:**
- Shortcodes: `[konficastle_workshopwahl wahl=ID]`, `[konficastle_workshop_ergebnis wahl=ID]`
- **Database:** Tables are created via `install.php`, prefix: `kc_`
## Key Patterns & Workflows
- **Admin tabs:** Navigation via `kc_admin_tabs()`
- **Naming:** Functions and tables use `kc_` prefix
- **Force assignments:** Manual assignments take precedence
- **Test data:** Only user ID 1 can generate via admin (`admin-data.php`)
- **CSV export:** Possible via admin assignments
- **Teamer password:** Managed via admin, hash in WP options
## Examples & Entry Points
- **Admin menu:** `konficastle-workshopwahl.php`, `includes/admin-wahlen.php`
- **Assignment logic:** `includes/zuteilungslogik.php`
- **Frontend form:** `includes/frontend-form.php`
## Notes for AI Agents
- Always use `$wpdb->prefix` for DB tables
- Backend logic is modular, each entity has its own file
- No complex JS logic in frontend, validation is server-side
- For changes to assignment logic: use test scenarios via test data admin page
---
See README.md for further details.

View File

@@ -1,4 +1,4 @@
# Workshop-Wahlen # Workshop-Wahlen Entwickler-Übersicht
## Überblick ## Überblick

60
assets/frontend-form.js Normal file
View File

@@ -0,0 +1,60 @@
// Client-side validation for Workshopwahl frontend form
// This script validates required fields and email format before submission
document.addEventListener('DOMContentLoaded', function () {
var form = document.querySelector('.kc-workshopwahl-form');
if (!form) return;
form.addEventListener('submit', function (e) {
var valid = true;
var errorMessages = [];
// Example: Validate required text fields
var requiredFields = form.querySelectorAll('[required]');
requiredFields.forEach(function (field) {
if (!field.value.trim()) {
valid = false;
errorMessages.push(field.getAttribute('data-label') || field.name + ' ist erforderlich.');
field.classList.add('kc-field-error');
} else {
field.classList.remove('kc-field-error');
}
});
// Example: Validate email format
var emailField = form.querySelector('input[type="email"]');
if (emailField && emailField.value) {
var emailPattern = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
if (!emailPattern.test(emailField.value)) {
valid = false;
errorMessages.push('Bitte eine gültige E-Mail-Adresse eingeben.');
emailField.classList.add('kc-field-error');
} else {
emailField.classList.remove('kc-field-error');
}
}
// Example: Validate max workshop selections (if relevant)
var maxWorkshops = parseInt(form.getAttribute('data-max-workshops'), 10);
if (maxWorkshops) {
var checked = form.querySelectorAll('input[type="checkbox"][name^="workshop_"]:checked');
if (checked.length > maxWorkshops) {
valid = false;
errorMessages.push('Es dürfen maximal ' + maxWorkshops + ' Workshops gewählt werden.');
}
}
// Show error messages
var errorBox = form.querySelector('.kc-form-errors');
if (!errorBox) {
errorBox = document.createElement('div');
errorBox.className = 'kc-form-errors';
form.prepend(errorBox);
}
errorBox.innerHTML = errorMessages.length ? '<ul><li>' + errorMessages.join('</li><li>') + '</li></ul>' : '';
if (!valid) {
e.preventDefault();
}
});
});

View File

@@ -102,3 +102,20 @@
.kc-form-container h2 {font-size:1.4em} .kc-form-container h2 {font-size:1.4em}
.kc-form-row {margin-bottom:14px} .kc-form-row {margin-bottom:14px}
} }
.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;}
.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%;}
.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;}
.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;}
@media(max-width:700px){ .kc-participants{grid-template-columns:1fr;} }

View File

@@ -187,6 +187,8 @@ details[open] summary:before { transform: rotate(0deg); }
font-weight: bold; font-weight: bold;
margin-left: 2px; margin-left: 2px;
} }
.kc-wahl-filter-btn.active{background:#4CAF50;color:#fff;}
.kc-phase-filter-btn.active{background:#1976d2;color:#fff;}
@media (max-width: 800px) { @media (max-width: 800px) {
.kc-admin-table-wrap {padding: 12px;} .kc-admin-table-wrap {padding: 12px;}
.kc-admin-table th, .kc-admin-table td {padding: 8px 6px;} .kc-admin-table th, .kc-admin-table td {padding: 8px 6px;}

View File

@@ -14,8 +14,10 @@ function kc_teamer_page() {
delete_option('kc_teamer_password_hash'); delete_option('kc_teamer_password_hash');
echo '<div class="notice notice-success">Teamer-Passwort entfernt.</div>'; echo '<div class="notice notice-success">Teamer-Passwort entfernt.</div>';
} else { } else {
update_option('kc_teamer_password_hash', wp_hash_password($pw)); // Sichere Speicherung mit password_hash
echo '<div class="notice notice-success">Teamer-Passwort gespeichert.</div>'; $hash = password_hash($pw, PASSWORD_DEFAULT);
update_option('kc_teamer_password_hash', $hash);
echo '<div class="notice notice-success">Teamer-Passwort gespeichert.</div>';
} }
} }
} }

View File

@@ -309,7 +309,7 @@ function kc_teilnehmer_page() {
echo '</details>'; echo '</details>';
} }
// JS für Wahl- und Phasen-Filter // JS für Wahl- und Phasen-Filter
echo '<style>.kc-wahl-filter-btn.active{background:#4CAF50;color:#fff;} .kc-phase-filter-btn.active{background:#1976d2;color:#fff;}</style>'; // CSS moved to admin-teilnehmer.css
echo '<script> echo '<script>
(function() { (function() {
var btns = Array.prototype.slice.call(document.querySelectorAll(".kc-wahl-filter-btn")); var btns = Array.prototype.slice.call(document.querySelectorAll(".kc-wahl-filter-btn"));

View File

@@ -106,28 +106,7 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
ob_start(); ob_start();
// Inline styles, angepasst an konfi-castle.com Look (dezent, grün/türkis Akzent) // 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 '<div class="kc-result">';
echo '<h3>Ergebnis für diese Wahl</h3>'; echo '<h3>Ergebnis für diese Wahl</h3>';
@@ -209,8 +188,8 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
foreach($teilnehmer as $t) { foreach($teilnehmer as $t) {
$is_me = in_array(intval($t->id), $my_ids); $is_me = in_array(intval($t->id), $my_ids);
$name = esc_html($t->vorname.' '.$t->nachname); $name = esc_html($t->vorname.' '.$t->nachname);
$label = $name . ' <span style="color:#6b6b6b;font-size:85%;">('.intval($t->phase).')</span>'; $label = $name . ' <span style="color:#6b6b6b;font-size:85%;white-space:nowrap;">' . intval($t->phase) . '</span>';
echo '<div class="kc-participant'.($is_me ? ' me' : '').'">'. $label .'</div>'; echo '<div class="kc-participant'.($is_me ? ' me' : '').'" style="white-space:nowrap;">'. $label .'</div>';
} }
echo '</div>'; // kc-participants echo '</div>'; // kc-participants
echo '</div>'; // content echo '</div>'; // content
@@ -225,11 +204,11 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
if (!empty($nicht_zugeteilt)) { if (!empty($nicht_zugeteilt)) {
echo '<div class="kc-notassigned">'; echo '<div class="kc-notassigned">';
echo '<b>Nicht zugeteilt:</b><br>'; echo '<b>Nicht zugeteilt:</b><br>';
echo '<ul style="margin:6px 0 0 18px;">'; echo '<div style="margin:6px 0 0 18px;">';
foreach($nicht_zugeteilt as $t) { foreach($nicht_zugeteilt as $t) {
echo '<li>'.esc_html($t->vorname.' '.$t->nachname).' (Phase '.intval($t->phase).')</li>'; echo '<span style="display:inline-block;white-space:nowrap;margin-right:12px;">'.esc_html($t->vorname.' '.$t->nachname).' <span style="color:#6b6b6b;font-size:85%;">'.intval($t->phase).'</span></span>';
} }
echo '</ul></div>'; echo '</div></div>';
} }
echo '</div>'; // kc-result echo '</div>'; // kc-result

View File

@@ -16,7 +16,7 @@ add_shortcode('konficastle_workshopwahl', function($atts) {
$wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $wahl_id)); $wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $wahl_id));
if(!$wahl || !$wahl->freigegeben) { if(!$wahl || !$wahl->freigegeben) {
return $debug_output . '<div class="kc-error-msg">Die Workshopwahl ist aktuell nicht freigeschaltet.</div>'; return $debug_output . '<div"></div>';
} }
// Ermittle erlaubte Workshops pro Phase für diese Wahl. // Ermittle erlaubte Workshops pro Phase für diese Wahl.
@@ -152,26 +152,7 @@ add_shortcode('konficastle_workshopwahl', function($atts) {
ob_start(); 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; ?> <?php echo $debug_output; ?>

View File

@@ -8,12 +8,12 @@ function kc_install_tables() {
global $wpdb; global $wpdb;
$prefix = $wpdb->prefix; $prefix = $wpdb->prefix;
$charset_collate = $wpdb->get_charset_collate(); $charset_collate = $wpdb->get_charset_collate();
require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// Tables definitions // --- Tabellen-Definitionen ---
$tables_sql = []; $tables_sql = [];
// Wahl-Tabelle
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahlen ( $tables_sql[] = "CREATE TABLE {$prefix}kc_wahlen (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL, name varchar(191) NOT NULL,
@@ -26,6 +26,7 @@ function kc_install_tables() {
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
// Workshop-Tabelle
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshops ( $tables_sql[] = "CREATE TABLE {$prefix}kc_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL, name varchar(191) NOT NULL,
@@ -35,6 +36,7 @@ function kc_install_tables() {
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
// Teamer-Tabelle
$tables_sql[] = "CREATE TABLE {$prefix}kc_teamer ( $tables_sql[] = "CREATE TABLE {$prefix}kc_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL, vorname varchar(191) DEFAULT NULL,
@@ -42,6 +44,7 @@ function kc_install_tables() {
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
// Teilnehmer-Tabelle
$tables_sql[] = "CREATE TABLE {$prefix}kc_teilnehmer ( $tables_sql[] = "CREATE TABLE {$prefix}kc_teilnehmer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL, vorname varchar(191) DEFAULT NULL,
@@ -51,10 +54,11 @@ function kc_install_tables() {
wunsch1 bigint(20) unsigned DEFAULT NULL, wunsch1 bigint(20) unsigned DEFAULT NULL,
wunsch2 bigint(20) unsigned DEFAULT NULL, wunsch2 bigint(20) unsigned DEFAULT NULL,
wunsch3 bigint(20) unsigned DEFAULT NULL, wunsch3 bigint(20) unsigned DEFAULT NULL,
deleted tinyint(1) NOT NULL DEFAULT 0, deleted tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
// Zuordnungstabelle Wahl <-> Workshops
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahl_workshops ( $tables_sql[] = "CREATE TABLE {$prefix}kc_wahl_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0, wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
@@ -63,6 +67,7 @@ function kc_install_tables() {
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
// Manuelle (Force-)Zuteilungen
$tables_sql[] = "CREATE TABLE {$prefix}kc_force_zuteilung ( $tables_sql[] = "CREATE TABLE {$prefix}kc_force_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0, teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
@@ -73,6 +78,7 @@ function kc_install_tables() {
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
// Ergebnis-Zuteilungen
$tables_sql[] = "CREATE TABLE {$prefix}kc_zuteilung ( $tables_sql[] = "CREATE TABLE {$prefix}kc_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0, teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
@@ -85,25 +91,26 @@ function kc_install_tables() {
PRIMARY KEY (id) PRIMARY KEY (id)
) $charset_collate"; ) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshop_teamer ( // Zuordnung Workshop <-> Teamer
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_workshop_teamer (
workshop_id bigint(20) unsigned DEFAULT NULL, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teamer_id bigint(20) unsigned DEFAULT NULL, workshop_id bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id) teamer_id bigint(20) unsigned DEFAULT NULL,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
// Tabellen anlegen
foreach ($tables_sql as $sql) { foreach ($tables_sql as $sql) {
dbDelta($sql); dbDelta($sql);
} }
// Post-creation migration guard: ensure min_teilnehmer exists for older installs // Migration: min_teilnehmer nachziehen, falls bei Update nötig
$col = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer')); $col = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer'));
if (empty($col)) { if (empty($col)) {
// try to add the column (no-op on newer installs)
$wpdb->query("ALTER TABLE {$prefix}kc_workshops ADD COLUMN min_teilnehmer INT NOT NULL DEFAULT 0"); $wpdb->query("ALTER TABLE {$prefix}kc_workshops ADD COLUMN min_teilnehmer INT NOT NULL DEFAULT 0");
} }
// Optionally store plugin version // Plugin-Version speichern
add_option('kc_workshopwahl_db_version', '1.0'); add_option('kc_workshopwahl_db_version', '1.0');
} }

View File

@@ -1,22 +1,24 @@
<?php <?php
/** /**
* Plugin Name: Workshop-Wahlen * Plugin Name: Workshop-Wahlen
* Description: Workshop-Wahl-System für Konfi-Castle.com * Description: Workshop-Wahl-System für Konfi-Castle.com
* Version: 1.0 * Version: 1.1 - dev
* Author: Linus Maximilian Nilson * Author: Linus Maximilian Nilson
*/ */
if (!defined('ABSPATH')) exit; if (!defined('ABSPATH')) exit;
// Stylesheet einbinden // Stylesheet einbinden
add_action('admin_enqueue_scripts', function($hook) { add_action('admin_enqueue_scripts', function($hook) {
// Nur auf den Plugin-Seiten laden (optional: prüfe $hook!) // Nur auf den Plugin-Seiten laden (optional: prüfe $hook!)
if (strpos($hook, 'kc_') !== false) { if (strpos($hook, 'kc_') !== false) {
wp_enqueue_style( wp_enqueue_style(
'kc-admin-style', 'kc-admin-style',
plugin_dir_url(__FILE__) . 'assets/kc-admin-style.css', plugin_dir_url(__FILE__) . 'assets/kc-admin.css',
[], [],
filemtime(plugin_dir_path(__FILE__) . 'assets/kc-admin-style.css') filemtime(plugin_dir_path(__FILE__) . 'assets/kc-admin.css')
); );
} }
}); });
@@ -31,10 +33,14 @@ add_action('admin_enqueue_scripts', function($hook) {
add_action('wp_enqueue_scripts', function() { add_action('wp_enqueue_scripts', function() {
wp_enqueue_style('kc-workshopwahl-form', plugins_url('assets/frontend-form.css', __FILE__)); wp_enqueue_style('kc-workshopwahl-frontend', plugins_url('assets/frontend.css', __FILE__));
}); });
// Zentrale Admin-Menüstruktur // Zentrale Admin-Menüstruktur
add_action('admin_menu', function() { add_action('admin_menu', function() {
add_menu_page('Wahlen', 'Wahlen', 'manage_options', 'kc_wahlen', 'kc_wahlen_page'); add_menu_page('Wahlen', 'Wahlen', 'manage_options', 'kc_wahlen', 'kc_wahlen_page');