Compare commits

..

90 Commits

Author SHA1 Message Date
4ab28370bc Update KC Wahlen Button Reihenfolge
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-02-26 22:58:11 +00:00
c56aa70617 Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2m29s
2026-02-19 09:50:08 +00:00
21553050f8 Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2m30s
2026-02-19 08:43:37 +00:00
f18664251e Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2m31s
update Readme
2026-02-19 08:35:05 +00:00
1a090dd8dc Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-02-19 08:23:53 +00:00
82f2af7e7a Update install.php
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 15s
2026-02-06 18:45:45 +00:00
bbfa923c9d Merge pull request 'update deploy workflow' (#6) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 16s
Reviewed-on: #6
2026-02-06 18:43:57 +00:00
ProgrammGamer
58c70908c6 update deploy workflow
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 15s
2026-02-06 19:42:40 +01:00
8827e5b324 Merge pull request 'develop' (#5) from develop into main
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 4s
Reviewed-on: #5
2026-02-06 18:41:46 +00:00
ProgrammGamer
a65651358a upstream develop
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 33s
2026-02-06 19:40:17 +01:00
ProgrammGamer
077ba03a3f update deploy.yml to implement new wp host
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-02-06 19:39:35 +01:00
ProgrammGamer
271af99fad deleted: CHANGELOG.md 2026-02-06 15:51:51 +01:00
ProgrammGamer
3f207a3612 0# Git commit message
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-02-01 19:28:52 +01:00
ProgrammGamer
92c4023fff Move CHANGELOG to Notes/CHANGELOG.md and update README link 2026-02-01 19:28:37 +01:00
ProgrammGamer
a3c08127f8 Release 1.0 notes + fixes: remove kc_phasen query, init $debug_output; add CHANGELOG and link from README 2026-02-01 19:26:18 +01:00
f1fb4cd4ea Merge pull request 'develop' (#4) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
Reviewed-on: #4
2026-01-31 13:26:30 +00:00
ProgrammGamer
7184d64116 Merge branch 'develop' of https://git.konfi-castle.com/linus/Workshop-Wahlen into develop
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-31 14:16:18 +01:00
ProgrammGamer
5c9c00b48e csv anpassungen 2026-01-31 14:16:06 +01:00
a827275995 Merge pull request 'develop loesung zu Lösung' (#3) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
Reviewed-on: #3
2026-01-31 10:56:48 +00:00
Blitz08
49621a4423 admin: collapse participant groups (Wahl & Phase) by default
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-31 11:48:51 +01:00
Blitz08
67222fc6f9 admin: add AJAX fallback for wahl phase count; use ajax in force-zuteilung JS
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 11s
2026-01-31 11:45:36 +01:00
Blitz08
eb7afe06ff admin: read phase-count from Wahl option data-einheiten; fallback to mapping
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-31 11:42:46 +01:00
Blitz08
b312cae473 admin: make phase a select and let JS populate options per Wahl
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 11s
2026-01-31 11:39:54 +01:00
Blitz08
e34ce64906 force zuteilung form anpassung
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-31 11:37:11 +01:00
Blitz08
8fca38f6fe admin: make phase select dependent on chosen Wahl (dynamic phases)
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-31 11:23:23 +01:00
Blitz08
1c08324b24 admin: improve force-zuteilung create/edit form UI
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-31 11:19:05 +01:00
Blitz08
a4719e9c58 loeschen zu löschen geändert
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-31 11:15:34 +01:00
Blitz08
c33eb3eaac Rechtschribfelher Korigiert
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-31 11:02:25 +01:00
cd5a0f72de Merge pull request 'frontend: hide results when wahl is freigegeben in shortcode' (#2) from to-main/frontend-ergebnis-hide-results into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
Reviewed-on: #2
2026-01-31 09:56:01 +00:00
Blitz08
b77b1fe0fb frontend: hide results when wahl is freigegeben in shortcode
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-31 10:53:03 +01:00
8029ebfb27 Merge pull request 'develop' (#1) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
Reviewed-on: #1
2026-01-30 21:54:02 +00:00
66131a3830 Merge branch 'main' into develop
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:53:41 +00:00
ProgrammGamer
e3f2f638bc Frontend: Phase-Anzeige hinter Teilnehmernamen entfernt persönliche Zuteilung, Teilnehmerlisten und Nicht-zugeteilt ohne (Phase X).
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 21:40:11 +01:00
ProgrammGamer
e17b38a091 Privacy: Keine internen IDs im Frontend Fallback 'ID <workshop_id>' durch 'Workshop unbekannt' ersetzt (persönliche Zuteilung).
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 21:36:24 +01:00
ProgrammGamer
9b2d7b77fd Frontend: Hinweis entfernt keine Freigeschaltet-Meldung mehr im Formular und Ergebnis; Inhalte bleiben sichtbar ohne Banner.
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:33:29 +01:00
ProgrammGamer
7fc12ba7bb Sync: Inhalte aus origin/main in develop übernommen; .gitea und .github unverändert belassen (falls vorhanden).
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:30:23 +01:00
ProgrammGamer
64546f3b62 Frontend: Aus main übernommen assets/ und includes/frontend-* synchronisiert in develop (CSS/Ergebnis/Formular).
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:27:15 +01:00
ProgrammGamer
9911947c3a Frontend: Dynamische Workshop-Karten pro Zeile responsive minmax (2402201901601fr) und angepasste Gaps für sauberes Layout.
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:17:45 +01:00
ProgrammGamer
824a49abcf Frontend: fix
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:14:00 +01:00
ProgrammGamer
3b51602f66 Frontend: Ergebnis-Styles wiederhergestellt .kc-inner auf 1100px, Grid mit minmax(260px) & gap 14, Karten mit weißem Hintergrund, dezenter Border & Shadow.
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 21:12:32 +01:00
ProgrammGamer
3f91746f17 Frontend: Teilnehmer-Grid und Teilnehmer-Style wie im PHP-Template 2-spaltig, dezente Chips, Hervorhebung für eigene Person.
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 21:09:46 +01:00
ProgrammGamer
67a5182670 Frontend: Formular-Styles (kc-form-container & Inputs) wiederhergestellt Hintergrund, Border, Input-Design wie ursprünglich.
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:09:15 +01:00
ProgrammGamer
b29fb937c8 change ws card border color
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:06:53 +01:00
ProgrammGamer
d9a18beae1 frondend change
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 21:04:55 +01:00
ProgrammGamer
68523a9fb5 changed frondend collor
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 21:02:54 +01:00
ProgrammGamer
731194683b Frontend: Teilnehmernamen wieder als klassische Liste, keine Chips mehr. Workshopkarten-Header zurück auf ursprüngliches Design (zentriert, farbig, mit Gradient). HTML/CSS bereinigt.
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 20:59:35 +01:00
ProgrammGamer
f4b84f43ea css back
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 20:55:01 +01:00
ProgrammGamer
acd972bffc back
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 20:53:42 +01:00
ProgrammGamer
e0c670b1c0 Frontend: Design zurück auf vorherigen modernen, leichten Stand (dezente Chips, weniger Kontrast)
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 20:52:22 +01:00
ProgrammGamer
2405ef6151 Frontend: Lesbarkeit und Kontrast verbessert, größere Schrift, klarere Chips, mehr Abstand
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 20:48:26 +01:00
ProgrammGamer
13aeebcbb7 Frontend: Workshop-Karten und Teilnehmer-Chips mit modernem Schatten, Hover, mehr Abstand und kräftigem Verlauf
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 38s
2026-01-30 20:45:53 +01:00
ProgrammGamer
e1f1b465a2 Frontend: Workshop-Karten und Teilnehmer optisch modernisiert (Header, Chips, Farben, Schatten)
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 47s
2026-01-30 20:40:33 +01:00
ProgrammGamer
4e2b0e10c3 Frontend: Grid wieder dynamisch, Teilnehmernamen optisch verbessert
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 20:38:22 +01:00
ProgrammGamer
154be87aae Fix: Grid wirklich auf maximal 3 Karten pro Zeile begrenzt (mit max-width und Media Queries)
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 20s
2026-01-30 20:37:35 +01:00
ProgrammGamer
98c4adeab7 Fix: Maximal 3 Workshop-Karten pro Zeile im Frontend
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 20:36:30 +01:00
ProgrammGamer
0fa4d693e5 5
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 20:34:42 +01:00
ProgrammGamer
7a59e69960 frintend test 4
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 20:32:10 +01:00
ProgrammGamer
caca09f98d frintend test 3
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 20:30:20 +01:00
ProgrammGamer
bd0f0ff4ca frintend test2
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 20:28:50 +01:00
ProgrammGamer
4104bb5d5d frintend test1
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 20:27:00 +01:00
ProgrammGamer
52972daf95 Frondend Roleback
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
2026-01-30 20:16:31 +01:00
ProgrammGamer
e177680b69 css anpassung
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 20:13:41 +01:00
ProgrammGamer
32cb20ddcd css anpassung
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 18s
2026-01-30 19:52:11 +01:00
ProgrammGamer
5f3a129d09 erneute css anpassung
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 19:47:26 +01:00
ProgrammGamer
fa25fe2571 a
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 19:30:27 +01:00
ProgrammGamer
4fc99c717b card adustment
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 18:44:10 +01:00
ProgrammGamer
df4a04399f frondend css
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 18:41:52 +01:00
ProgrammGamer
d044529e09 update Frondend
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 18:39:23 +01:00
ProgrammGamer
a0d0af1e48 Update Frindend ergebniss
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
2026-01-30 18:34:43 +01:00
0faba96901 Update .gitea/workflows/deploy.yml
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
2026-01-30 17:31:11 +00:00
221c9214ef Update .gitea/workflows/deploy.yml
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 1m5s
2026-01-30 17:26:15 +00:00
fcaf329b99 Update .gitea/workflows/deploy.yml
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 5s
2026-01-30 17:23:29 +00:00
e81cd0bc58 Update .gitea/workflows/deploy.yml
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 5s
2026-01-30 17:20:54 +00:00
defd7e1a36 Update .gitea/workflows/deploy.yml
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 36s
2026-01-30 17:16:58 +00:00
445e39183b revert 76490ef016
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2s
revert Update .gitea/workflows/deploy.yml
2026-01-30 17:16:04 +00:00
ProgrammGamer
f7de19ec89 test: node runner image 2026-01-30 18:14:40 +01:00
76490ef016 Update .gitea/workflows/deploy.yml 2026-01-30 17:13:31 +00:00
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
15 changed files with 954 additions and 277 deletions

119
.gitea/workflows/deploy.yml Normal file
View File

@@ -0,0 +1,119 @@
name: Deploy Workshop-Wahlen (DEV / PROD)
on:
push:
branches:
- develop
- main
jobs:
deploy:
runs-on: ubuntu-latest
container:
image: docker.gitea.com/runner-images:ubuntu-latest
steps:
# =====================
# CHECKOUT
# =====================
- name: Checkout Repository
uses: actions/checkout@v4
# =====================
# DEBUG (kann später raus)
# =====================
- name: Debug workspace
run: |
echo "PWD:"
pwd
echo "FILES:"
ls -la
# =====================
# DEV DEPLOY (FTP)
# =====================
- name: Deploy to DEV via FTP
if: github.ref == 'refs/heads/develop'
shell: bash
run: |
apt-get update
apt-get install -y lftp
lftp -u "${{ secrets.FTP_USER }},${{ secrets.FTP_PASS }}" ftp://${{ secrets.FTP_HOST }}:${{ secrets.FTP_PORT }} <<EOF
set ftp:ssl-allow no
set net:timeout 20
set net:max-retries 2
set mirror:parallel-transfer-count 2
mirror -R --delete --verbose ./ /dev.konfi-castle.com/wp-content/plugins/konficastle-workshopwahl/
quit
EOF
# =====================
# PROD DEPLOY (FTP)
# =====================
- name: Deploy to PROD via SSH
if: github.ref == 'refs/heads/main'
env:
SSH_HOST: ${{ secrets.PROD_SSH_HOST }}
SSH_USER: ${{ secrets.PROD_SSH_USER }}
SSH_PORT: ${{ secrets.PROD_SSH_PORT }}
SSH_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
HOST_WP_ROOT: ${{ secrets.PROD_HOST_WP_ROOT }}
HOST_ABS_PLUGIN_PATH: ${{ secrets.PROD_HOST_ABS_PLUGIN_PATH }}
shell: bash
run: |
set -euo pipefail
apt-get update
# openssh-client to auth; tar for packaging with excludes
apt-get install -y openssh-client tar
# Prepare SSH key
mkdir -p ~/.ssh
# Write the multiline private key exactly as provided
printf "%s\n" "$SSH_KEY" > ~/.ssh/prod_docker_key
chmod 600 ~/.ssh/prod_docker_key
# Write SSH config so DOCKER_HOST can reference a named host
cat > ~/.ssh/config <<CFG
Host prod-docker
HostName ${SSH_HOST}
User ${SSH_USER}
Port ${SSH_PORT}
IdentityFile ~/.ssh/prod_docker_key
IdentitiesOnly yes
PubkeyAuthentication yes
StrictHostKeyChecking accept-new
CFG
# Prime known_hosts (optional, StrictHostKeyChecking accept-new will handle first connect)
ssh-keyscan -p "$SSH_PORT" "$SSH_HOST" >> ~/.ssh/known_hosts || true
# Create a tarball with excludes to avoid copying VCS and CI folders
TAR_FILE="/tmp/kc-plugin.tar.gz"
tar -czf "$TAR_FILE" \
--exclude .git \
--exclude .gitea \
--exclude .github \
--exclude Notes \
--exclude "*.yml" \
--exclude "*.yaml" \
--exclude "*.md" \
.
# Choose target path: prefer absolute plugin path; otherwise derive from HOST_WP_ROOT
if [ -n "${HOST_ABS_PLUGIN_PATH}" ]; then
echo "Deploying to absolute host path: ${HOST_ABS_PLUGIN_PATH}"
REMOTE_PATH_DECL="PLUGIN_PATH=\"${HOST_ABS_PLUGIN_PATH%/}\""
elif [ -n "${HOST_WP_ROOT}" ]; then
echo "Deploying to host path relative to remote home: \$HOME/${HOST_WP_ROOT%/}/wp-content/plugins/konficastle-workshopwahl"
REMOTE_PATH_DECL="PLUGIN_PATH=\$HOME/${HOST_WP_ROOT%/}/wp-content/plugins/konficastle-workshopwahl"
else
echo "Error: set PROD_HOST_ABS_PLUGIN_PATH (absolute) or PROD_HOST_WP_ROOT (relative)" >&2
exit 1
fi
# Upload tarball to host home and extract
scp -o StrictHostKeyChecking=accept-new -o IdentitiesOnly=yes -i ~/.ssh/prod_docker_key "$TAR_FILE" prod-docker:~/kc-plugin.tar.gz
# Use \$HOME for remote expansion (tilde does not expand inside quotes)
ssh prod-docker "$REMOTE_PATH_DECL; mkdir -p \"\$PLUGIN_PATH\" && rm -rf \"\$PLUGIN_PATH\"/* && tar -xzf \"\$HOME/kc-plugin.tar.gz\" -C \"\$PLUGIN_PATH\" && rm -f \"\$HOME/kc-plugin.tar.gz\" && ls -la \"\$PLUGIN_PATH\" | head -n 50"

View File

@@ -47,4 +47,3 @@ Dieses WordPress-Plugin ermöglicht die Verwaltung und Durchführung von Worksho
---
Für Detailfragen zu Datenbankstruktur, Shortcodes oder Zuteilungslogik siehe die jeweiligen Dateien im `includes/`-Verzeichnis.

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();
}
});
});

175
assets/frontend.css Normal file
View File

@@ -0,0 +1,175 @@
.teamers {
color: #888;
font-size: 0.95em;
margin-left: 8px;
white-space: nowrap;
}
.kc-form-container {
box-sizing: border-box;
width: 100%;
max-width: 680px;
margin: 28px auto;
background: #f8fbe7;
border-left: 8px solid #b6d333;
border-radius: 14px;
box-shadow: 0 5px 22px #8eae291a;
padding: 28px 22px;
font-family: 'Segoe UI', Arial, sans-serif;
}
.kc-form-container h2 {
color: #3b5323;
font-family: 'Montserrat', Arial, sans-serif;
font-size: 2em;
margin-top: 0;
margin-bottom: 0.5em;
letter-spacing: -1px;
}
.kc-form-row {
margin-bottom: 22px;
}
.kc-form-row label {
display: block;
font-weight: 700;
color: #1c3866;
margin-bottom: 7px;
font-size: 1.07em;
}
.kc-form-row select {
box-sizing: border-box;
width: 100%;
padding: 10px;
border: 1.7px solid #d6e39f;
border-radius: 5px;
background: #fcffe9;
font-size: 1.09em;
margin-top: 3px;
transition: border .2s;
}
.kc-form-row input[type="text"]:focus,
.kc-form-row select:focus {
outline: none;
border-color: #b6d333;
background: #fffde8;
}
.kc-form-row input[type="submit"] {
background: #2f5393;
color: #fff;
padding: 13px 28px;
font-weight: bold;
font-size: 1.13em;
border: 0;
border-radius: 7px;
cursor: pointer;
margin-top: 6px;
box-shadow: 0 2px 8px #1c38661a;
transition: background .2s;
}
.kc-form-row input[type="submit"]:hover {
background: #1c3866;
}
.kc-required {
color: #e42626;
font-weight: bold;
}
.kc-success-msg {
color: #288830;
background: #f2fbe2;
border-left: 4px solid #b6d333;
padding: 15px 15px 15px 22px;
margin-bottom: 25px;
font-size: 1.1em;
border-radius: 7px;
}
.kc-error-msg {
color: #a80000;
background: #ffeaea;
border-left: 4px solid #e12b2b;
padding: 15px 15px 15px 22px;
margin-bottom: 25px;
font-size: 1.1em;
border-radius: 7px;
}
@media (max-width: 900px) {
.kc-form-container {padding:20px 14px;}
}
@media (max-width: 520px) {
.kc-form-container {padding:14px 10px;border-radius:10px;margin:14px 10px;}
.kc-form-container h2 {font-size:1.4em}
.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(240px, 1fr));
gap: 16px;
margin-top: 12px;
align-items: start;
}
/* Responsive tuning: adapt card count per row */
@media (max-width: 1024px) {
.kc-workshops-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
}
@media (max-width: 768px) {
.kc-workshops-grid { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
}
@media (max-width: 560px) {
.kc-workshops-grid { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; }
}
@media (max-width: 420px) {
.kc-workshops-grid { grid-template-columns: 1fr; gap: 8px; }
}
.kc-workshop-card {
box-sizing: border-box;
min-width: 0;
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;
white-space: normal;
display: block;
max-width: 100%;
overflow: visible;
text-overflow: unset;
margin: 0;
}
.kc-participant.me {
background: #fffbe6;
border: 1px solid #ffeab2;
color: inherit;
font-weight: bold;
}
.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;} }

205
assets/kc-admin.css Normal file
View File

@@ -0,0 +1,205 @@
.kc-admin-tabs {
margin-bottom: 28px;
border-bottom: 2px solid #e0e6ef;
background: #f8fbe7;
border-radius: 18px 18px 0 0;
box-shadow: 0 1px 10px #b6d33321;
padding: 8px 18px 0 18px;
}
.kc-tabnav {
display: inline-block;
padding: 12px 32px 11px 32px;
border-radius: 12px 12px 0 0;
font-weight: 700;
font-size: 1.12em;
margin-right: 7px;
box-shadow: 0 2px 11px #b6d33318;
text-decoration: none;
background: #f8fbe7;
color: #4176be;
border: none;
outline: none;
transition: all .18s;
letter-spacing: 0.5px;
}
.kc-tabnav-active,
.kc-tabnav:focus,
.kc-tabnav:hover {
background: linear-gradient(90deg, #326dd2 0%, #b6d333 100%);
color: #fff !important;
box-shadow: 0 6px 24px #326dd241;
font-weight: 800;
letter-spacing: 1px;
}
.kc-admin-table-wrap {
box-sizing: border-box;
background: #fff;
border-radius: 17px;
box-shadow: 0 4px 28px #326dd21c, 0 1.5px 7px #b6d33324;
padding: 28px 22px 30px 22px;
margin: 28px 0;
/* allow children to overflow (tables may scroll), don't clip action buttons */
overflow: visible;
}
/* Collapsible details styling for admin Zuteilungen */
details.kc-wahl-details, details.kc-phase-details {
background: #fff;
border: 1px solid #e9eef6;
border-radius: 10px;
padding: 8px;
margin-bottom: 12px;
}
details.kc-wahl-details > summary, details.kc-phase-details > summary {
list-style: none;
cursor: pointer;
padding: 8px 10px;
}
details.kc-wahl-details[open] > summary, details.kc-phase-details[open] > summary {
background: linear-gradient(90deg,#f2f9ff 0%, #f6fff5 100%);
border-radius: 8px;
}
/* Wahl summary layout */
details.kc-wahl-details > summary { display: flex; align-items: center; justify-content: space-between; }
details.kc-wahl-details > summary .kc-wahl-title { font-weight:700; color:#2b5f9a; }
details.kc-wahl-details > summary .kc-wahl-actions { margin-left: 12px; }
details.kc-wahl-details .kc-wahl-actions .kc-btn { margin-left:8px; }
/* Workshop card inside phase */
.kc-workshop-card {
background: #f9f9f9;
padding: 10px;
margin: 8px 0;
border-radius: 8px;
box-shadow: 0 1px 6px #00000012;
}
.kc-workshop-card table { margin-top:8px; }
.kc-workshop-card b { font-size: 1.05em; }
.kc-workshop-card .kc-count { color:#555; font-size:0.92em; }
.kc-unassigned {
background: #fff6f6;
border-left: 4px solid #f2b0b0;
padding: 10px;
margin-top: 8px;
border-radius: 6px;
}
/* Smaller tweaks for details summaries inside admin area */
details summary { outline: none; }
details summary::-webkit-details-marker { display: none; }
details summary:before { content: '\25B6'; display:inline-block; transform:rotate(90deg); margin-right:8px; }
details[open] summary:before { transform: rotate(0deg); }
.kc-admin-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
margin-bottom: 28px;
background: #fafdff;
border-radius: 13px;
overflow: auto; /* horizontal scroll on small screens */
box-shadow: 0 2px 12px #b6d33313;
}
.kc-admin-table th, .kc-admin-table td {
padding: 13px 18px;
text-align: left;
font-size: 1.07em;
vertical-align: middle;
}
.kc-admin-table thead {
background: #eaf6ff;
color: #4176be;
font-weight: 800;
}
.kc-admin-table tbody tr {
border-bottom: 1px solid #eef3fa;
}
.kc-admin-table tbody tr:nth-child(even) {
background: #f8fbe7;
}
.kc-admin-table tbody tr:hover {
background: #e0ebf6;
transition: background 0.2s;
}
.kc-admin-table th {
font-weight: bold;
border-bottom: 2px solid #b6d33342;
}
.kc-admin-table .kc-actions {
white-space: nowrap;
display: flex;
gap: 8px;
align-items: center;
justify-content: flex-end;
flex-wrap: wrap; /* allow buttons to wrap on very small widths */
}
.kc-btn {
background: linear-gradient(90deg,#4176be 40%, #b6d333 100%);
color: #fff;
padding: 10px 25px;
border-radius: 8px;
border: 0;
margin: 0 5px;
font-weight: 700;
cursor: pointer;
display: inline-block;
text-decoration: none;
font-size: 1.04em;
box-shadow: 0 1.5px 6px #b6d33321;
transition: background .17s, box-shadow .19s;
}
.kc-btn.del {
background: #e12b2b !important;
color: #fff;
}
.kc-btn.edit {
background: #ff9800 !important;
color: #fff;
}
.kc-btn:hover {
opacity: 0.91;
background: linear-gradient(90deg,#26529e 40%, #97b321 100%);
color: #fff;
box-shadow: 0 3px 10px #4176be29;
}
.notice-success {
background: #f6ffed;
border-left: 6px solid #b6d333;
color: #1c5322;
padding: 13px 20px;
margin: 0 0 22px 0;
border-radius: 9px;
font-size: 1.08em;
font-weight: 500;
}
.notice-error {
background: #fff3f3;
border-left: 6px solid #e12b2b;
color: #9d1d2e;
padding: 13px 20px;
margin: 0 0 22px 0;
border-radius: 9px;
font-size: 1.08em;
font-weight: 500;
}
.kc-required {
color: #e42626;
font-weight: bold;
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) {
.kc-admin-table-wrap {padding: 12px;}
.kc-admin-table th, .kc-admin-table td {padding: 8px 6px;}
.kc-btn {padding: 8px 13px;}
}
@media (max-width: 600px) {
/* Make tables readable on mobile by switching to block rows */
.kc-admin-table thead { display: none; }
.kc-admin-table, .kc-admin-table tbody, .kc-admin-table tr, .kc-admin-table td { display: block; width: 100%; }
.kc-admin-table tr { margin-bottom: 12px; border-bottom: 1px solid #eef3fa; }
.kc-admin-table td { text-align: left; padding: 10px 12px; white-space: normal; }
.kc-admin-table td:before { content: attr(data-label); font-weight:700; display:block; margin-bottom:6px; color:#4176be; }
}

View File

@@ -131,7 +131,7 @@ function kc_teamer_page() {
<td>".esc_html($tm->nachname)."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_teamer&edit_teamer={$tm->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_teamer&delete_teamer={$tm->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
<a class='kc-btn del' href='?page=kc_teamer&delete_teamer={$tm->id}' onclick=\"return confirm('Wirklich löschen?');\">Löschen</a>
</td>
</tr>";
}

View File

@@ -274,10 +274,10 @@ function kc_teilnehmer_page() {
// Gesamtanzahl Teilnehmer für diese Wahl berechnen
$gesamt = 0;
foreach ($phasen as $tns) $gesamt += count($tns);
echo '<details open style="margin:10px 0 18px 0;border:1px solid #eaeaea;border-radius:6px;padding:8px;">';
echo '<details style="margin:10px 0 18px 0;border:1px solid #eaeaea;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:700;cursor:pointer;">'. $wahl_disp . ' <span style="color:#555;font-weight:600;">(' . $gesamt . ' TN)</span></summary>';
foreach ($phasen as $phase => $tns) {
echo '<details open style="margin:10px 0 10px 0;border:1px solid #f0f0f0;border-radius:6px;padding:8px;">';
echo '<details style="margin:10px 0 10px 0;border:1px solid #f0f0f0;border-radius:6px;padding:8px;">';
echo '<summary style="font-weight:600;">Phase '.intval($phase).' <span style="color:#555;font-weight:600;">('.count($tns).' TN)</span></summary>';
echo '<table class="kc-admin-table" style="margin:8px 0;">';
echo '<thead><tr><th>Vorname</th><th>Nachname</th><th>Wahl</th><th>Phase</th><th>Wunsch 1</th><th>Wunsch 2</th><th>Wunsch 3</th><th>Aktion</th></tr></thead><tbody>';
@@ -299,7 +299,7 @@ function kc_teilnehmer_page() {
<td>".$w3_disp."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_teilnehmer&edit_teilnehmer={$tn->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_teilnehmer&delete_teilnehmer={$tn->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
<a class='kc-btn del' href='?page=kc_teilnehmer&delete_teilnehmer={$tn->id}' onclick=\"return confirm('Wirklich löschen?');\">Löschen</a>
</td>
</tr>";
}

View File

@@ -352,7 +352,7 @@ function kc_wahlen_page() {
echo '<td>';
echo "<form method='post' style='display:inline'>";
echo "<input type='hidden' name='wahl_id' value='".intval($wahl->id)."'>";
echo "<button name='kc_wahl_freigabe' value='".($wahl->freigegeben?"0":"1")."' class='kc-btn edit' type='submit' style='background:".($wahl->freigegeben?"#e12b2b":"#b6d333").";color:#fff;font-weight:bold;'>".($wahl->freigegeben?"Schlie<EFBFBD>en":"Freigeben (zum eintragen)")."</button>";
echo "<button name='kc_wahl_freigabe' value='".($wahl->freigegeben?"0":"1")."' class='kc-btn edit' type='submit' style='background:".($wahl->freigegeben?"#e12b2b":"#b6d333").";color:#fff;font-weight:bold;'>".($wahl->freigegeben?"Schließen":"Freigeben (zum eintragen)")."</button>";
echo "</form>";
echo '<span style="color:'.($wahl->freigegeben?"#4176be":"#d40000").';margin-left:6px;font-size:90%;font-weight:bold;">'.($wahl->freigegeben ? "freigegeben" : "geschlossen").'</span>';
echo '</td>';
@@ -360,10 +360,10 @@ function kc_wahlen_page() {
echo '<td class="kc-actions">';
echo '<a class="kc-btn" href="?page=kc_wahlen&zuweisen='.intval($wahl->id).'">Workshops zuweisen</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&edit_wahl='.intval($wahl->id).'">Bearbeiten</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&show_zuteilung='.intval($wahl->id).'">Zuteilung anzeigen</a>';
$nonce = wp_create_nonce('kc_run_zuteilung_' . intval($wahl->id));
echo '<a class="kc-btn" href="?page=kc_wahlen&run_zuteilung='.intval($wahl->id).'&_wpnonce='.$nonce.'" onclick="return confirm(\'Zuteilung wirklich starten? Dies überschreibt vorhandene Zuteilungen.\');">Zuteilung starten</a>';
echo '<a href="?page=kc_wahlen&delete_wahl='.intval($wahl->id).'" class="kc-btn del" onclick="return confirm(\'Wirklich loeschen?\');">Loeschen</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&show_zuteilung='.intval($wahl->id).'">Zuteilung anzeigen</a>';
echo '<a href="?page=kc_wahlen&delete_wahl='.intval($wahl->id).'" class="kc-btn del" onclick="return confirm(\'Wirklich löschen?\');">Löschen</a>';
echo '</td>';
echo '</tr>';
}

View File

@@ -10,8 +10,9 @@ function kc_workshops_page() {
// 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");
// Phasen werden pro Wahl über die Spalte `anzahl_einheiten` in `kc_wahlen` bestimmt.
// Eine separate Tabelle `kc_phasen` wird nicht benötigt.
$phasen_liste = [];
// Workshop löschen
if (isset($_GET['delete_workshop'])) {
@@ -542,7 +543,7 @@ function kc_workshops_page() {
<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>
<a class='kc-btn del' href='?page=kc_workshops&delete_workshop={$ws->id}' onclick=\"return confirm('Wirklich löschen?');\">Löschen</a>
</td>
</tr>";
}

View File

@@ -86,7 +86,8 @@ if (isset($_GET['export_csv']) && current_user_can('manage_options')) {
$output = fopen('php://output', 'w');
// Excel erwartet in DE meist ; als Trennzeichen
$delimiter = ';';
fputcsv($output, ['Phase','Workshop','Teamer','Teilnehmer'], $delimiter);
// Kopfzeile mit separaten Namensspalten und Wunsch
fputcsv($output, ['Phase','Workshop','Teamer','Nachname','Vorname','Wunsch'], $delimiter);
foreach($phases as $phase) {
if (empty($workshop_groups[$phase])) continue;
$display = [];
@@ -114,14 +115,27 @@ if (isset($_GET['export_csv']) && current_user_can('manage_options')) {
foreach($teilnehmer as $t) {
$vorname = preg_replace('/<[^>]*>/', '', $t->vorname);
$nachname = preg_replace('/<[^>]*>/', '', $t->nachname);
$teilnehmer_name = trim($vorname . ', ' . $nachname);
$ws_name_clean = preg_replace('/<[^>]*>/', '', $ws_name);
$teamer_str_clean = preg_replace('/<[^>]*>/', '', $teamer_str);
// Wunsch-Text ermitteln
if ($t->wunsch_rang == 0) {
$wunsch = 'Vorzuteilung';
} elseif ($t->wunsch_rang == -1) {
$wunsch = 'zugelost';
} elseif ($t->wunsch_rang == 99) {
$wunsch = 'Zulosung';
} elseif ($t->wunsch_rang > 0) {
$wunsch = intval($t->wunsch_rang) . '.';
} else {
$wunsch = '';
}
fputcsv($output, [
$phase,
$ws_name_clean,
$teamer_str_clean,
$teilnehmer_name,
$nachname,
$vorname,
$wunsch
], $delimiter);
}

View File

@@ -4,14 +4,14 @@ function kc_force_zuteilung_page() {
$prefix = $wpdb->prefix;
kc_admin_tabs('kc_force_zuteilung');
// Force-Zuteilung löschen
// Force-Zuteilung l<EFBFBD>schen
if (isset($_GET['delete_force'])) {
$fid = intval($_GET['delete_force']);
$wpdb->delete("{$prefix}kc_force_zuteilung", ['id' => $fid]);
echo '<div class="notice notice-success">Force-Zuteilung gelöscht!</div>';
echo '<div class="notice notice-success">Force-Zuteilung gel<EFBFBD>scht!</div>';
}
// Force speichern (neu/ändern)
// Force speichern (neu/<EFBFBD>ndern)
if (isset($_POST['kc_force_save'])) {
$data = [
'teilnehmer_id' => intval($_POST['teilnehmer_id']),
@@ -29,69 +29,143 @@ function kc_force_zuteilung_page() {
}
}
// Listen für Dropdowns
// Listen f<EFBFBD>r Dropdowns
$teilnehmer_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_teilnehmer ORDER BY nachname, vorname");
$workshop_liste = $wpdb->get_results("SELECT * FROM {$prefix}kc_workshops ORDER BY name");
$wahlen = $wpdb->get_results("SELECT id, name FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY name");
$wahlen = $wpdb->get_results("SELECT id, name, COALESCE(anzahl_einheiten,1) AS anzahl_einheiten FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY name");
// Bearbeiten
if (isset($_GET['edit_force'])) {
$fid = intval($_GET['edit_force']);
$fz = $wpdb->get_row("SELECT * FROM {$prefix}kc_force_zuteilung WHERE id=$fid");
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Force-Zuteilung bearbeiten</h2>
<form method="post">
<input type="hidden" name="fid" value="'.intval($fz->id).'">
<div class="kc-form-row">
<label>Teilnehmer <span class="kc-required">*</span></label>
<select name="teilnehmer_id" id="kc-force-teilnehmer-dropdown" style="width:100%">
<option value="">Teilnehmer auswählen…</option>';
foreach($teilnehmer_liste as $t) {
echo '<option value="'.esc_attr($t->id).'"'.($fz->teilnehmer_id==$t->id?' selected':'').'>'
.esc_html($t->vorname.' '.$t->nachname.' (ID: '.$t->id.')')
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Wahl <span class="kc-required">*</span></label>
<select name="wahl_id" id="kc-force-wahl-dropdown" style="width:100%">
<option value="">Wahl auswählen…</option>';
foreach($wahlen as $w) {
echo '<option value="'.intval($w->id).'"'.($fz->wahl_id==$w->id?' selected':'').'>'
.esc_html($w->name.' (ID '.$w->id.')').'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Phase</label>
<input type="number" name="phase" value="'.intval($fz->phase).'" style="width:100%">
</div>
<div class="kc-form-row">
<label>Workshop <span class="kc-required">*</span></label>
<select name="workshop_id" id="kc-force-workshop-dropdown" style="width:100%">
<option value="">Workshop auswählen…</option>';
foreach($workshop_liste as $ws) {
echo '<option value="'.esc_attr($ws->id).'"'.($fz->workshop_id==$ws->id?' selected':'').'>'
.esc_html($ws->name)
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Kommentar</label>
<input type="text" name="kommentar" value="'.esc_attr($fz->kommentar).'" style="width:100%">
</div>
<button name="kc_force_save" class="kc-btn">Speichern</button>
<a href="?page=kc_force_zuteilung" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>
<script>
jQuery(function($){
$("#kc-force-teilnehmer-dropdown").select2({placeholder:"Teilnehmer auswählen…",allowClear:true});
$("#kc-force-wahl-dropdown").select2({placeholder:"Wahl auswählen…",allowClear:true});
$("#kc-force-workshop-dropdown").select2({placeholder:"Workshop auswählen…",allowClear:true});
});
</script>';
echo '<h2>Force-Zuteilung bearbeiten</h2>';
echo '<div class="kc-form-container" style="max-width:880px;padding:18px 20px;border-radius:10px;background:#fff;border:1px solid #eef3f2;">';
echo '<form method="post">';
echo '<input type="hidden" name="fid" value="'.intval($fz->id).'">';
echo '<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:start">';
// Teilnehmer
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Teilnehmer <span class="kc-required">*</span></label>';
echo '<select name="teilnehmer_id" id="kc-force-teilnehmer-dropdown" style="width:100%">';
echo '<option value="">Teilnehmer auswählen…</option>';
foreach($teilnehmer_liste as $t) {
echo '<option value="'.esc_attr($t->id).'"'.($fz->teilnehmer_id==$t->id?' selected':'').'>'
.esc_html($t->vorname.' '.$t->nachname.' (ID: '.$t->id.')')
.'</option>';
}
echo '</select>';
echo '</div>';
// Wahl
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Wahl <span class="kc-required">*</span></label>';
echo '<select name="wahl_id" id="kc-force-wahl-dropdown" style="width:100%">';
echo '<option value="">Wahl auswählen…</option>';
foreach($wahlen as $w) {
echo '<option value="'.intval($w->id).'" data-einheiten="'.intval($w->anzahl_einheiten).'"'.($fz->wahl_id==$w->id?' selected':'').'>'.esc_html($w->name.' (ID '.$w->id.')').'</option>';
}
echo '</select>';
echo '</div>';
// Phase (select so JS can populate based on Wahl)
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Phase</label>';
echo '<select name="phase" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd">';
echo '<option value="'.intval($fz->phase).'">'.intval($fz->phase).'</option>';
echo '</select>';
echo '</div>';
// Workshop
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Workshop <span class="kc-required">*</span></label>';
echo '<select name="workshop_id" id="kc-force-workshop-dropdown" style="width:100%">';
echo '<option value="">Workshop auswählen…</option>';
foreach($workshop_liste as $ws) {
echo '<option value="'.esc_attr($ws->id).'"'.($fz->workshop_id==$ws->id?' selected':'').'>'
.esc_html($ws->name)
.'</option>';
}
echo '</select>';
echo '</div>';
// Kommentar (full width)
echo '<div style="grid-column:1/ -1">';
echo '<div class="kc-form-row">';
echo '<label>Kommentar</label>';
echo '<input type="text" name="kommentar" value="'.esc_attr($fz->kommentar).'" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd">';
echo '</div>';
echo '</div>';
echo '</div>'; // grid
echo '<div style="margin-top:12px;display:flex;gap:10px;justify-content:flex-start">';
echo '<button name="kc_force_save" class="kc-btn" style="padding:8px 16px;">Speichern</button>';
echo '<a href="?page=kc_force_zuteilung" class="kc-btn del" style="padding:8px 16px;">Abbrechen</a>';
echo '</div>';
echo '</form>';
echo '</div>';
// Prepare JS mapping of wahl_id -> number of phases
$wahl_phases = [];
foreach ($wahlen as $w) $wahl_phases[intval($w->id)] = intval($w->anzahl_einheiten);
echo '<script>var kcWahlPhases = ' . wp_json_encode($wahl_phases) . ';</script>';
echo <<<'JS'
<script>
jQuery(function($){
$("#kc-force-teilnehmer-dropdown").select2({placeholder:"Teilnehmer auswählen…",allowClear:true});
$("#kc-force-wahl-dropdown").select2({placeholder:"Wahl auswählen…",allowClear:true});
$("#kc-force-workshop-dropdown").select2({placeholder:"Workshop auswählen…",allowClear:true});
function refreshPhaseSelect($wahlSelect, $phaseSelect, current) {
// prefer data attribute on the selected option; fall back to kcWahlPhases mapping
var selOpt = $wahlSelect.find('option:selected');
var max = parseInt(selOpt.data('einheiten') || kcWahlPhases[parseInt($wahlSelect.val(),10)] || 1, 10);
$phaseSelect.empty();
for(var i=1;i<=max;i++){
var opt = $("<option>").val(i).text(i + (i===1? " (Phase)":" (Phase)"));
$phaseSelect.append(opt);
}
if (current) {
$phaseSelect.val(current);
}
$phaseSelect.prop("disabled", false);
}
// Bind change for all existing wahl selects on the page
$(document).on("change", "#kc-force-wahl-dropdown", function(){
var $w = $(this);
var $phase = $w.closest("form").find("[name='phase']");
// try immediate refresh; if no data found, fallback to AJAX
var sel = $w.find('option:selected');
var einheiten = sel.data('einheiten') || kcWahlPhases[parseInt($w.val(),10)];
if (einheiten) {
refreshPhaseSelect($w, $phase);
} else {
// AJAX fallback to ask server for anzahl_einheiten
$.get(ajaxurl, {action: 'kc_get_wahl', id: $w.val()}).done(function(r){
if (r && r.success && r.data && r.data.anzahl_einheiten) {
// set data attribute so future reads find it
sel.data('einheiten', parseInt(r.data.anzahl_einheiten,10));
}
refreshPhaseSelect($w, $phase);
}).fail(function(){ refreshPhaseSelect($w, $phase); });
}
});
// Initialize existing phase selects based on their selected wahl
$("[name='phase']").each(function(){
var $phase = $(this);
var $form = $phase.closest("form");
var $wahl = $form.find("#kc-force-wahl-dropdown");
var current = $phase.val();
refreshPhaseSelect($wahl, $phase, current);
});
});
</script>
JS;
echo '</div>';
return;
}
@@ -99,63 +173,82 @@ function kc_force_zuteilung_page() {
// Neu anlegen
if (isset($_GET['new'])) {
echo '<div class="kc-admin-table-wrap">';
echo '<h2>Neue Force-Zuteilung</h2>
<form method="post">
<div class="kc-form-row">
<label>Teilnehmer <span class="kc-required">*</span></label>
<select name="teilnehmer_id" id="kc-force-teilnehmer-dropdown" style="width:100%">
<option value="">Teilnehmer auswählen…</option>';
foreach($teilnehmer_liste as $t) {
echo '<option value="'.esc_attr($t->id).'">'
.esc_html($t->vorname.' '.$t->nachname.' (ID: '.$t->id.')')
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Wahl <span class="kc-required">*</span></label>
<select name="wahl_id" id="kc-force-wahl-dropdown" style="width:100%">
<option value="">Wahl auswählen…</option>';
foreach($wahlen as $w) {
echo '<option value="'.intval($w->id).'">'
.esc_html($w->name.' (ID '.$w->id.')').'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Phase</label>
<input type="number" name="phase" value="1" style="width:100%">
</div>
<div class="kc-form-row">
<label>Workshop <span class="kc-required">*</span></label>
<select name="workshop_id" id="kc-force-workshop-dropdown" style="width:100%">
<option value="">Workshop auswählen…</option>';
foreach($workshop_liste as $ws) {
echo '<option value="'.esc_attr($ws->id).'">'
.esc_html($ws->name)
.'</option>';
}
echo ' </select>
</div>
<div class="kc-form-row">
<label>Kommentar</label>
<input type="text" name="kommentar" style="width:100%">
</div>
<button name="kc_force_save" class="kc-btn">Speichern</button>
<a href="?page=kc_force_zuteilung" class="kc-btn del" style="margin-left:24px;">Abbrechen</a>
</form>
<script>
echo '<h2>Neue Force-Zuteilung</h2>';
echo '<div class="kc-form-container" style="max-width:880px;padding:18px 20px;border-radius:10px;background:#fff;border:1px solid #eef3f2;">';
echo '<form method="post">';
echo '<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;align-items:start">';
// Teilnehmer
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Teilnehmer <span class="kc-required">*</span></label>';
echo '<select name="teilnehmer_id" id="kc-force-teilnehmer-dropdown" style="width:100%">';
echo '<option value="">Teilnehmer auswählen…</option>';
foreach($teilnehmer_liste as $t) {
echo '<option value="'.esc_attr($t->id).'">'.esc_html($t->vorname.' '.$t->nachname.' (ID: '.$t->id.')').'</option>';
}
echo '</select>';
echo '</div>';
// Wahl
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Wahl <span class="kc-required">*</span></label>';
echo '<select name="wahl_id" id="kc-force-wahl-dropdown" style="width:100%">';
echo '<option value="">Wahl auswählen…</option>';
foreach($wahlen as $w) {
echo '<option value="'.intval($w->id).'" data-einheiten="'.intval($w->anzahl_einheiten).'">'.esc_html($w->name.' (ID '.$w->id.')').'</option>';
}
echo '</select>';
echo '</div>';
// Phase (select so JS can populate based on Wahl)
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Phase</label>';
echo '<select name="phase" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd">';
echo '<option value="1">1</option>';
echo '</select>';
echo '</div>';
// Workshop
echo '<div class="kc-form-row" style="min-width:0">';
echo '<label>Workshop <span class="kc-required">*</span></label>';
echo '<select name="workshop_id" id="kc-force-workshop-dropdown" style="width:100%">';
echo '<option value="">Workshop auswählen…</option>';
foreach($workshop_liste as $ws) {
echo '<option value="'.esc_attr($ws->id).'">'.esc_html($ws->name).'</option>';
}
echo '</select>';
echo '</div>';
// Kommentar full width
echo '<div style="grid-column:1/ -1">';
echo '<div class="kc-form-row">';
echo '<label>Kommentar</label>';
echo '<input type="text" name="kommentar" style="width:100%;padding:8px;border-radius:6px;border:1px solid #ddd">';
echo '</div>';
echo '</div>';
echo '</div>'; // grid
echo '<div style="margin-top:12px;display:flex;gap:10px;justify-content:flex-start">';
echo '<button name="kc_force_save" class="kc-btn" style="padding:8px 16px;">Speichern</button>';
echo '<a href="?page=kc_force_zuteilung" class="kc-btn del" style="padding:8px 16px;">Abbrechen</a>';
echo '</div>';
echo '</form>';
echo '</div>';
echo '<script>
jQuery(function($){
$("#kc-force-teilnehmer-dropdown").select2({placeholder:"Teilnehmer auswählen…",allowClear:true});
$("#kc-force-wahl-dropdown").select2({placeholder:"Wahl auswählen…",allowClear:true});
$("#kc-force-workshop-dropdown").select2({placeholder:"Workshop auswählen…",allowClear:true});
$("#kc-force-teilnehmer-dropdown").select2({placeholder:"Teilnehmer auswählen…",allowClear:true});
$("#kc-force-wahl-dropdown").select2({placeholder:"Wahl auswählen…",allowClear:true});
$("#kc-force-workshop-dropdown").select2({placeholder:"Workshop auswählen…",allowClear:true});
});
</script>';
echo '</div>';
return;
}
// Übersicht
// <EFBFBD>bersicht
echo '<div class="kc-admin-table-wrap">';
echo '<h2 style="margin-top:0;">Alle Force-Zuteilungen</h2>';
echo '<a class="kc-btn" style="float:right;margin-bottom:12px;" href="?page=kc_force_zuteilung&new=1">+ Neue Force-Zuteilung</a>';
@@ -176,7 +269,7 @@ function kc_force_zuteilung_page() {
<td>".esc_html($f->kommentar)."</td>
<td class='kc-actions'>
<a class='kc-btn edit' href='?page=kc_force_zuteilung&edit_force={$f->id}'>Bearbeiten</a>
<a class='kc-btn del' href='?page=kc_force_zuteilung&delete_force={$f->id}' onclick=\"return confirm('Wirklich loeschen?');\">Loeschen</a>
<a class='kc-btn del' href='?page=kc_force_zuteilung&delete_force={$f->id}' onclick=\"return confirm('Wirklich löschen?');\">schen</a>
</td>
</tr>";
}

View File

@@ -6,7 +6,11 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
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>';
// Zeige Ergebnisse nur, wenn die Wahl NICHT freigegeben ist.
// Ist die Wahl freigegeben, soll das Ergebnis-Shortcode nichts ausgeben (Formular wird stattdessen angezeigt).
if (!empty($wahl->freigegeben)) {
return '';
}
// Workshops für diese Wahl holen
$workshops = $wpdb->get_results(
@@ -142,8 +146,8 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
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>';
$wslabel = ($mr->workshop_id && isset($ws_names[$mr->workshop_id])) ? $ws_names[$mr->workshop_id] : ($mr->workshop_id ? 'Workshop unbekannt' : 'Nicht zugeteilt');
echo '<li>'.esc_html($mr->vorname.' '.$mr->nachname).' — '.esc_html($wslabel).'</li>';
}
echo '</ul></div>';
$personal_shown = true;
@@ -209,8 +213,7 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
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 class="kc-participant'.($is_me ? ' me' : '').'">'. $name .'</div>';
}
echo '</div>'; // kc-participants
echo '</div>'; // content
@@ -227,7 +230,7 @@ add_shortcode('konficastle_workshop_ergebnis', function($atts) {
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 '<li>'.esc_html($t->vorname.' '.$t->nachname).'</li>';
}
echo '</ul></div>';
}

View File

@@ -5,18 +5,13 @@ add_shortcode('konficastle_workshopwahl', function($atts) {
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>';
// Optionales Debug-Ausgabe-Fragment (standardmäßig leer, um Warnungen zu vermeiden)
$debug_output = '';
$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>';
// Keine Fehlermeldung im Frontend anzeigen; Formular ausblenden
return '';
}
// Ermittle erlaubte Workshops pro Phase für diese Wahl.

View File

@@ -1,139 +1,140 @@
<?php
if (!defined('ABSPATH')) exit;
/**
* Create plugin database tables on activation and remove them on deactivation.
*/
function kc_install_tables() {
global $wpdb;
$prefix = $wpdb->prefix;
$charset_collate = $wpdb->get_charset_collate();
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// Tables definitions
$tables_sql = [];
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahlen (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL,
beschreibung text DEFAULT NULL,
anzahl_einheiten tinyint NOT NULL DEFAULT 1,
min_kapazitaet int NOT NULL DEFAULT 0,
max_kapazitaet int NOT NULL DEFAULT 0,
freigegeben tinyint(1) NOT NULL DEFAULT 0,
deleted tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL,
beschreibung text DEFAULT NULL,
max_teilnehmer int NOT NULL DEFAULT 0,
min_teilnehmer int NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL,
nachname varchar(191) DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_teilnehmer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL,
nachname varchar(191) DEFAULT NULL,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
wunsch1 bigint(20) unsigned DEFAULT NULL,
wunsch2 bigint(20) unsigned DEFAULT NULL,
wunsch3 bigint(20) unsigned DEFAULT NULL,
deleted tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahl_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
workshop_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_force_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
workshop_id bigint(20) unsigned DEFAULT NULL,
kommentar text DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
vorname varchar(191) DEFAULT NULL,
nachname varchar(191) DEFAULT NULL,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
workshop_id bigint(20) unsigned DEFAULT NULL,
wunsch_rang tinyint DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshop_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
workshop_id bigint(20) unsigned DEFAULT NULL,
teamer_id bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
foreach ($tables_sql as $sql) {
dbDelta($sql);
}
// Post-creation migration guard: ensure min_teilnehmer exists for older installs
$col = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer'));
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");
}
// Optionally store plugin version
add_option('kc_workshopwahl_db_version', '1.0');
}
function kc_uninstall_tables() {
global $wpdb;
$prefix = $wpdb->prefix;
$tables = [
"{$prefix}kc_zuteilung",
"{$prefix}kc_force_zuteilung",
"{$prefix}kc_wahl_workshops",
"{$prefix}kc_teilnehmer",
"{$prefix}kc_teamer",
"{$prefix}kc_workshops",
"{$prefix}kc_wahlen",
"{$prefix}kc_workshop_teamer"
];
foreach ($tables as $t) {
$wpdb->query("DROP TABLE IF EXISTS $t");
}
delete_option('kc_workshopwahl_db_version');
}
// Backwards-compat wrapper for register_activation_hook usage
if (!function_exists('kc_register_hooks_internal')) {
function kc_register_hooks_internal() {
// intentionally left blank
}
}
?>
<?php
if (!defined('ABSPATH')) exit;
/**
* Create plugin database tables on activation and remove them on deactivation.
*/
function kc_install_tables() {
global $wpdb;
$prefix = $wpdb->prefix;
$charset_collate = $wpdb->get_charset_collate();
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// Tables definitions
$tables_sql = [];
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahlen (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL,
beschreibung text DEFAULT NULL,
anzahl_einheiten tinyint NOT NULL DEFAULT 1,
min_kapazitaet int NOT NULL DEFAULT 0,
max_kapazitaet int NOT NULL DEFAULT 0,
freigegeben tinyint(1) NOT NULL DEFAULT 0,
deleted tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
name varchar(191) NOT NULL,
beschreibung text DEFAULT NULL,
max_teilnehmer int NOT NULL DEFAULT 0,
min_teilnehmer int NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL,
nachname varchar(191) DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_teilnehmer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL,
nachname varchar(191) DEFAULT NULL,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
wunsch1 bigint(20) unsigned DEFAULT NULL,
wunsch2 bigint(20) unsigned DEFAULT NULL,
wunsch3 bigint(20) unsigned DEFAULT NULL,
deleted tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahl_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
workshop_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_force_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
workshop_id bigint(20) unsigned DEFAULT NULL,
kommentar text DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
vorname varchar(191) DEFAULT NULL,
nachname varchar(191) DEFAULT NULL,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1,
workshop_id bigint(20) unsigned DEFAULT NULL,
wunsch_rang tinyint DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshop_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
workshop_id bigint(20) unsigned DEFAULT NULL,
teamer_id bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id)
) $charset_collate";
foreach ($tables_sql as $sql) {
dbDelta($sql);
}
// Post-creation migration guard: ensure min_teilnehmer exists for older installs
$col = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer'));
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");
}
// Optionally store plugin version
add_option('kc_workshopwahl_db_version', '1.0');
}
function kc_uninstall_tables() {
global $wpdb;
$prefix = $wpdb->prefix;
$tables = [
"{$prefix}kc_zuteilung",
"{$prefix}kc_force_zuteilung",
"{$prefix}kc_wahl_workshops",
"{$prefix}kc_teilnehmer",
"{$prefix}kc_teamer",
"{$prefix}kc_workshops",
"{$prefix}kc_wahlen",
"{$prefix}kc_workshop_teamer"
];
foreach ($tables as $t) {
$wpdb->query("DROP TABLE IF EXISTS $t");
}
delete_option('kc_workshopwahl_db_version');
}
// Backwards-compat wrapper for register_activation_hook usage
if (!function_exists('kc_register_hooks_internal')) {
function kc_register_hooks_internal() {
// intentionally left blank
}
}
?>

View File

@@ -1,4 +1,16 @@
<?php
// AJAX endpoint to return wahl details (anzahl_einheiten)
add_action('wp_ajax_kc_get_wahl', function(){
global $wpdb;
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
if (!$id) {
wp_send_json_error('missing id');
}
$row = $wpdb->get_row($wpdb->prepare("SELECT id, COALESCE(anzahl_einheiten,1) as anzahl_einheiten FROM {$wpdb->prefix}kc_wahlen WHERE id=%d", $id));
if (!$row) wp_send_json_error('not found');
wp_send_json_success(['id'=>intval($row->id),'anzahl_einheiten'=>intval($row->anzahl_einheiten)]);
});
/**
* Plugin Name: Workshop-Wahlen
* Description: Workshop-Wahl-System für Konfi-Castle.com