From 73ad38423d4e11f44ad55b9b52f0cb3fcf4348a6 Mon Sep 17 00:00:00 2001 From: ProgrammGamer Date: Wed, 25 Feb 2026 18:51:54 +0100 Subject: [PATCH] Implement feature X to enhance user experience and fix bug Y in module Z --- includes/api.php | 1343 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1318 insertions(+), 25 deletions(-) diff --git a/includes/api.php b/includes/api.php index 8166fa1..2675cfb 100644 --- a/includes/api.php +++ b/includes/api.php @@ -21,29 +21,856 @@ function kc_api_permission_check() { return true; } +function kc_api_owner_permission_check() { + $base = kc_api_permission_check(); + if (is_wp_error($base)) { + return $base; + } + + $current = wp_get_current_user(); + if (!$current || intval($current->ID) !== 1) { + return new WP_Error('kc_api_owner_only', 'Nur Owner (User ID 1) erlaubt.', ['status' => 403]); + } + + return true; +} + +function kc_api_request_data(WP_REST_Request $request) { + $json = $request->get_json_params(); + if (is_array($json) && !empty($json)) { + return $json; + } + $params = $request->get_params(); + return is_array($params) ? $params : []; +} + +function kc_api_bool($value) { + if (is_bool($value)) return $value; + if (is_numeric($value)) return intval($value) === 1; + if (is_string($value)) { + $v = strtolower(trim($value)); + return in_array($v, ['1', 'true', 'yes', 'ja', 'on'], true); + } + return false; +} + +function kc_api_ensure_workshop_teamer_columns() { + global $wpdb; + $prefix = $wpdb->prefix; + + $columns = $wpdb->get_col("SHOW COLUMNS FROM {$prefix}kc_workshop_teamer"); + if (!in_array('wahl_id', $columns, true)) { + $wpdb->query("ALTER TABLE {$prefix}kc_workshop_teamer ADD COLUMN wahl_id INT(11) NULL DEFAULT NULL"); + } + if (!in_array('phase', $columns, true)) { + $wpdb->query("ALTER TABLE {$prefix}kc_workshop_teamer ADD COLUMN phase INT(11) NULL DEFAULT NULL"); + } +} + +function kc_api_validate_teilnehmer_payload($data, $exclude_id = 0) { + global $wpdb; + $prefix = $wpdb->prefix; + + $vorname = sanitize_text_field($data['vorname'] ?? ''); + $nachname = sanitize_text_field($data['nachname'] ?? ''); + $wahl_id = intval($data['wahl_id'] ?? 0); + $phase = intval($data['phase'] ?? 0); + $w1 = intval($data['wunsch1'] ?? 0); + $w2 = intval($data['wunsch2'] ?? 0); + $w3 = intval($data['wunsch3'] ?? 0); + + if ($vorname === '' || $nachname === '' || $wahl_id <= 0 || $phase <= 0 || $w1 <= 0 || $w2 <= 0 || $w3 <= 0) { + return new WP_Error('kc_api_invalid_input', 'Pflichtfelder fehlen oder sind ungültig.', ['status' => 400]); + } + + if (count(array_unique([$w1, $w2, $w3])) !== 3) { + return new WP_Error('kc_api_duplicate_wishes', 'Wünsche müssen unterschiedlich sein.', ['status' => 400]); + } + + $wahl_row = $wpdb->get_row($wpdb->prepare("SELECT id, anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$wahl_row) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $max_ph = max(1, intval($wahl_row->anzahl_einheiten)); + if ($phase < 1 || $phase > $max_ph) { + return new WP_Error('kc_api_invalid_phase', 'Ungültige Phase für diese Wahl.', ['status' => 400]); + } + + $norm_v = mb_strtolower(trim($vorname)); + $norm_n = mb_strtolower(trim($nachname)); + + if ($exclude_id > 0) { + $exists = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d AND id<>%d", + $norm_v, + $norm_n, + $wahl_id, + $exclude_id + )); + } else { + $exists = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d", + $norm_v, + $norm_n, + $wahl_id + )); + } + + if (intval($exists) > 0) { + return new WP_Error('kc_api_duplicate_participant', 'Diese Kombination aus Vorname/Nachname existiert bereits in der Wahl.', ['status' => 409]); + } + + $allowed = $wpdb->get_col($wpdb->prepare("SELECT workshop_id FROM {$prefix}kc_wahl_workshops WHERE wahl_id=%d", $wahl_id)); + $allowed = array_map('intval', (array) $allowed); + + if (!empty($allowed)) { + foreach ([$w1, $w2, $w3] as $wid) { + if (!in_array(intval($wid), $allowed, true)) { + return new WP_Error('kc_api_invalid_workshop_for_wahl', 'Mindestens ein Wunsch gehört nicht zur Wahl.', ['status' => 400]); + } + } + } + + return [ + 'vorname' => $vorname, + 'nachname' => $nachname, + 'wahl_id' => $wahl_id, + 'phase' => $phase, + 'wunsch1' => $w1, + 'wunsch2' => $w2, + 'wunsch3' => $w3, + ]; +} + +function kc_api_get_status(WP_REST_Request $request) { + $user = wp_get_current_user(); + + return rest_ensure_response([ + 'success' => true, + 'authenticated' => is_user_logged_in(), + 'authorized' => current_user_can('manage_options'), + 'user' => [ + 'id' => intval($user->ID), + 'login' => (string) $user->user_login, + 'display_name' => (string) $user->display_name, + ], + ]); +} + function kc_api_get_wahlen(WP_REST_Request $request) { global $wpdb; $prefix = $wpdb->prefix; - $rows = $wpdb->get_results("SELECT id, name, anzahl_einheiten, freigegeben FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY id DESC"); - - $data = []; + $rows = $wpdb->get_results("SELECT * FROM {$prefix}kc_wahlen WHERE deleted=0 ORDER BY id DESC"); + $items = []; foreach ((array) $rows as $row) { - $data[] = [ + $items[] = [ 'id' => intval($row->id), 'name' => (string) $row->name, + 'beschreibung' => (string) $row->beschreibung, 'anzahl_einheiten' => max(1, intval($row->anzahl_einheiten)), + 'min_kapazitaet' => intval($row->min_kapazitaet), + 'max_kapazitaet' => intval($row->max_kapazitaet), 'freigegeben' => intval($row->freigegeben) === 1, ]; } + return rest_ensure_response(['success' => true, 'count' => count($items), 'items' => $items]); +} + +function kc_api_get_wahl(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $wahl_id = intval($request->get_param('id')); + $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$row) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + return rest_ensure_response([ + 'success' => true, + 'item' => [ + 'id' => intval($row->id), + 'name' => (string) $row->name, + 'beschreibung' => (string) $row->beschreibung, + 'anzahl_einheiten' => max(1, intval($row->anzahl_einheiten)), + 'min_kapazitaet' => intval($row->min_kapazitaet), + 'max_kapazitaet' => intval($row->max_kapazitaet), + 'freigegeben' => intval($row->freigegeben) === 1, + ] + ]); +} + +function kc_api_create_wahl(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $data = kc_api_request_data($request); + + $name = sanitize_text_field($data['name'] ?? ''); + if ($name === '') { + return new WP_Error('kc_api_invalid_name', 'Name ist erforderlich.', ['status' => 400]); + } + + $insert = [ + 'name' => $name, + 'beschreibung' => sanitize_textarea_field($data['beschreibung'] ?? ''), + 'anzahl_einheiten' => max(1, intval($data['anzahl_einheiten'] ?? 1)), + 'freigegeben' => kc_api_bool($data['freigegeben'] ?? false) ? 1 : 0, + 'deleted' => 0, + ]; + + if (isset($data['min_kapazitaet'])) $insert['min_kapazitaet'] = intval($data['min_kapazitaet']); + if (isset($data['max_kapazitaet'])) $insert['max_kapazitaet'] = intval($data['max_kapazitaet']); + + $wpdb->insert("{$prefix}kc_wahlen", $insert); + + return rest_ensure_response([ + 'success' => true, + 'id' => intval($wpdb->insert_id), + 'message' => 'Wahl angelegt.' + ]); +} + +function kc_api_update_wahl(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($request->get_param('id')); + $data = kc_api_request_data($request); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$exists) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $update = []; + if (array_key_exists('name', $data)) $update['name'] = sanitize_text_field($data['name']); + if (array_key_exists('beschreibung', $data)) $update['beschreibung'] = sanitize_textarea_field($data['beschreibung']); + if (array_key_exists('anzahl_einheiten', $data)) $update['anzahl_einheiten'] = max(1, intval($data['anzahl_einheiten'])); + if (array_key_exists('freigegeben', $data)) $update['freigegeben'] = kc_api_bool($data['freigegeben']) ? 1 : 0; + if (array_key_exists('min_kapazitaet', $data)) $update['min_kapazitaet'] = intval($data['min_kapazitaet']); + if (array_key_exists('max_kapazitaet', $data)) $update['max_kapazitaet'] = intval($data['max_kapazitaet']); + + if (empty($update)) { + return new WP_Error('kc_api_empty_update', 'Keine Felder zum Aktualisieren übergeben.', ['status' => 400]); + } + + $wpdb->update("{$prefix}kc_wahlen", $update, ['id' => $wahl_id]); + + return rest_ensure_response(['success' => true, 'message' => 'Wahl aktualisiert.']); +} + +function kc_api_delete_wahl(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($request->get_param('id')); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$exists) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $wahl_id]); + $wpdb->delete("{$prefix}kc_force_zuteilung", ['wahl_id' => $wahl_id]); + $wpdb->delete("{$prefix}kc_teilnehmer", ['wahl_id' => $wahl_id]); + $wpdb->delete("{$prefix}kc_wahl_workshops", ['wahl_id' => $wahl_id]); + $wpdb->update("{$prefix}kc_wahlen", ['deleted' => 1], ['id' => $wahl_id]); + + return rest_ensure_response(['success' => true, 'message' => 'Wahl gelöscht (soft delete + Cascade).']); +} + +function kc_api_get_wahl_workshops(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($request->get_param('id')); + + $wahl = $wpdb->get_row($wpdb->prepare("SELECT id, name, anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$wahl) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT ww.phase, ww.workshop_id, ws.name + FROM {$prefix}kc_wahl_workshops ww + JOIN {$prefix}kc_workshops ws ON ws.id = ww.workshop_id + WHERE ww.wahl_id = %d + ORDER BY ww.phase ASC, ws.name ASC", + $wahl_id + )); + + $phases = []; + foreach ((array) $rows as $row) { + $phase = intval($row->phase ?: 1); + if (!isset($phases[$phase])) $phases[$phase] = []; + $phases[$phase][] = [ + 'workshop_id' => intval($row->workshop_id), + 'workshop_name' => (string) $row->name, + ]; + } + return rest_ensure_response([ 'success' => true, - 'count' => count($data), - 'items' => $data, + 'wahl' => ['id' => intval($wahl->id), 'name' => (string) $wahl->name, 'anzahl_einheiten' => max(1, intval($wahl->anzahl_einheiten))], + 'phases' => $phases, ]); } +function kc_api_set_wahl_workshops(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($request->get_param('id')); + $body = kc_api_request_data($request); + + $wahl = $wpdb->get_row($wpdb->prepare("SELECT id, anzahl_einheiten FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$wahl) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $anzahl_einheiten = max(1, intval($wahl->anzahl_einheiten)); + $assignments = isset($body['phases']) && is_array($body['phases']) ? $body['phases'] : []; + + if (empty($assignments) && !empty($body['workshop_ids']) && is_array($body['workshop_ids'])) { + $assignments = [1 => $body['workshop_ids']]; + } + + if (empty($assignments)) { + return new WP_Error('kc_api_invalid_assignments', 'phases-Map oder workshop_ids erforderlich.', ['status' => 400]); + } + + $wpdb->delete("{$prefix}kc_wahl_workshops", ['wahl_id' => $wahl_id]); + + foreach ($assignments as $phase => $workshop_ids) { + $phase_num = max(1, intval($phase)); + if ($phase_num > $anzahl_einheiten) continue; + if (!is_array($workshop_ids)) continue; + + foreach ($workshop_ids as $wid) { + $wid = intval($wid); + if ($wid <= 0) continue; + $wpdb->insert("{$prefix}kc_wahl_workshops", [ + 'wahl_id' => $wahl_id, + 'workshop_id' => $wid, + 'phase' => $anzahl_einheiten > 1 ? $phase_num : 1, + ]); + } + } + + return rest_ensure_response(['success' => true, 'message' => 'Workshop-Zuordnung gespeichert.']); +} + +function kc_api_get_workshops(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $rows = $wpdb->get_results("SELECT * FROM {$prefix}kc_workshops ORDER BY name ASC"); + $items = []; + foreach ((array) $rows as $row) { + $items[] = [ + 'id' => intval($row->id), + 'name' => (string) $row->name, + 'beschreibung' => (string) $row->beschreibung, + 'max_teilnehmer' => intval($row->max_teilnehmer), + 'min_teilnehmer' => isset($row->min_teilnehmer) ? intval($row->min_teilnehmer) : 0, + ]; + } + + return rest_ensure_response(['success' => true, 'count' => count($items), 'items' => $items]); +} + +function kc_api_get_workshop(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wid = intval($request->get_param('id')); + + $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_workshops WHERE id=%d", $wid)); + if (!$row) { + return new WP_Error('kc_api_workshop_not_found', 'Workshop nicht gefunden.', ['status' => 404]); + } + + return rest_ensure_response([ + 'success' => true, + 'item' => [ + 'id' => intval($row->id), + 'name' => (string) $row->name, + 'beschreibung' => (string) $row->beschreibung, + 'max_teilnehmer' => intval($row->max_teilnehmer), + 'min_teilnehmer' => isset($row->min_teilnehmer) ? intval($row->min_teilnehmer) : 0, + ] + ]); +} + +function kc_api_create_workshop(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $data = kc_api_request_data($request); + + $name = sanitize_text_field($data['name'] ?? ''); + if ($name === '') { + return new WP_Error('kc_api_invalid_name', 'Name ist erforderlich.', ['status' => 400]); + } + + $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' => $name, + 'beschreibung' => sanitize_text_field($data['beschreibung'] ?? ''), + 'max_teilnehmer' => intval($data['max_teilnehmer'] ?? 0), + 'min_teilnehmer' => intval($data['min_teilnehmer'] ?? 0), + ]); + + return rest_ensure_response(['success' => true, 'id' => intval($wpdb->insert_id), 'message' => 'Workshop angelegt.']); +} + +function kc_api_update_workshop(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wid = intval($request->get_param('id')); + $data = kc_api_request_data($request); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_workshops WHERE id=%d", $wid)); + if (!$exists) { + return new WP_Error('kc_api_workshop_not_found', 'Workshop nicht gefunden.', ['status' => 404]); + } + + $update = []; + if (array_key_exists('name', $data)) $update['name'] = sanitize_text_field($data['name']); + if (array_key_exists('beschreibung', $data)) $update['beschreibung'] = sanitize_text_field($data['beschreibung']); + if (array_key_exists('max_teilnehmer', $data)) $update['max_teilnehmer'] = intval($data['max_teilnehmer']); + if (array_key_exists('min_teilnehmer', $data)) $update['min_teilnehmer'] = intval($data['min_teilnehmer']); + + if (empty($update)) { + return new WP_Error('kc_api_empty_update', 'Keine Felder zum Aktualisieren übergeben.', ['status' => 400]); + } + + $wpdb->update("{$prefix}kc_workshops", $update, ['id' => $wid]); + + return rest_ensure_response(['success' => true, 'message' => 'Workshop aktualisiert.']); +} + +function kc_api_delete_workshop(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wid = intval($request->get_param('id')); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_workshops WHERE id=%d", $wid)); + if (!$exists) { + return new WP_Error('kc_api_workshop_not_found', 'Workshop nicht gefunden.', ['status' => 404]); + } + + $wpdb->delete("{$prefix}kc_workshops", ['id' => $wid]); + + return rest_ensure_response(['success' => true, 'message' => 'Workshop gelöscht.']); +} + +function kc_api_get_workshop_teamer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wid = intval($request->get_param('id')); + + kc_api_ensure_workshop_teamer_columns(); + + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT wt.id, wt.teamer_id, wt.wahl_id, wt.phase, t.vorname, t.nachname + FROM {$prefix}kc_workshop_teamer wt + JOIN {$prefix}kc_teamer t ON t.id = wt.teamer_id + WHERE wt.workshop_id=%d + ORDER BY wt.wahl_id ASC, wt.phase ASC, t.vorname ASC", + $wid + )); + + $fixed = []; + $scoped = []; + foreach ((array) $rows as $row) { + $entry = [ + 'teamer_id' => intval($row->teamer_id), + 'name' => trim($row->vorname . ' ' . $row->nachname), + 'wahl_id' => $row->wahl_id !== null ? intval($row->wahl_id) : null, + 'phase' => $row->phase !== null ? intval($row->phase) : null, + ]; + if ($entry['wahl_id'] === null && $entry['phase'] === null) { + $fixed[] = $entry; + } else { + $scoped[] = $entry; + } + } + + return rest_ensure_response(['success' => true, 'workshop_id' => $wid, 'fixed' => $fixed, 'scoped' => $scoped]); +} + +function kc_api_set_workshop_teamer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wid = intval($request->get_param('id')); + $data = kc_api_request_data($request); + + kc_api_ensure_workshop_teamer_columns(); + + $teamer_ids = isset($data['teamer_ids']) && is_array($data['teamer_ids']) ? array_map('intval', $data['teamer_ids']) : []; + $teamer_ids = array_values(array_unique(array_filter($teamer_ids, function($v) { return $v > 0; }))); + + $is_fixed = kc_api_bool($data['fixed'] ?? false); + $wahl_id = array_key_exists('wahl_id', $data) ? intval($data['wahl_id']) : null; + $phase = array_key_exists('phase', $data) ? intval($data['phase']) : null; + + if ($is_fixed) { + $wpdb->query($wpdb->prepare( + "DELETE FROM {$prefix}kc_workshop_teamer WHERE workshop_id=%d AND wahl_id IS NULL AND phase IS NULL", + $wid + )); + } else { + if ($wahl_id === null || $wahl_id <= 0) { + return new WP_Error('kc_api_invalid_scope', 'Für scoped-Zuordnung ist wahl_id erforderlich.', ['status' => 400]); + } + + if ($phase !== null && $phase > 0) { + $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 + )); + $phase = null; + } + } + + foreach ($teamer_ids as $tid) { + $wpdb->insert("{$prefix}kc_workshop_teamer", [ + 'workshop_id' => $wid, + 'teamer_id' => $tid, + 'wahl_id' => $is_fixed ? null : $wahl_id, + 'phase' => $is_fixed ? null : $phase, + ]); + } + + return rest_ensure_response(['success' => true, 'message' => 'Teamer-Zuordnung gespeichert.', 'count' => count($teamer_ids)]); +} + +function kc_api_get_teamer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $id = intval($request->get_param('id')); + if ($id > 0) { + $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teamer WHERE id=%d", $id)); + if (!$row) return new WP_Error('kc_api_teamer_not_found', 'Teamer nicht gefunden.', ['status' => 404]); + return rest_ensure_response(['success' => true, 'item' => ['id' => intval($row->id), 'vorname' => (string) $row->vorname, 'nachname' => (string) $row->nachname]]); + } + + $rows = $wpdb->get_results("SELECT * FROM {$prefix}kc_teamer ORDER BY vorname ASC"); + $items = []; + foreach ((array) $rows as $row) { + $items[] = ['id' => intval($row->id), 'vorname' => (string) $row->vorname, 'nachname' => (string) $row->nachname]; + } + + return rest_ensure_response(['success' => true, 'count' => count($items), 'items' => $items]); +} + +function kc_api_create_teamer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $data = kc_api_request_data($request); + + $vorname = sanitize_text_field($data['vorname'] ?? ''); + $nachname = sanitize_text_field($data['nachname'] ?? ''); + if ($vorname === '') { + return new WP_Error('kc_api_invalid_input', 'Vorname ist erforderlich.', ['status' => 400]); + } + + $existing = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM {$prefix}kc_teamer WHERE vorname=%s AND nachname=%s", + $vorname, + $nachname + )); + if (intval($existing) > 0) { + return new WP_Error('kc_api_duplicate_teamer', 'Teamer existiert bereits.', ['status' => 409]); + } + + $wpdb->insert("{$prefix}kc_teamer", ['vorname' => $vorname, 'nachname' => $nachname]); + return rest_ensure_response(['success' => true, 'id' => intval($wpdb->insert_id), 'message' => 'Teamer angelegt.']); +} + +function kc_api_update_teamer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $id = intval($request->get_param('id')); + $data = kc_api_request_data($request); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_teamer WHERE id=%d", $id)); + if (!$exists) return new WP_Error('kc_api_teamer_not_found', 'Teamer nicht gefunden.', ['status' => 404]); + + $update = []; + if (array_key_exists('vorname', $data)) $update['vorname'] = sanitize_text_field($data['vorname']); + if (array_key_exists('nachname', $data)) $update['nachname'] = sanitize_text_field($data['nachname']); + if (empty($update)) return new WP_Error('kc_api_empty_update', 'Keine Felder zum Aktualisieren übergeben.', ['status' => 400]); + + $wpdb->update("{$prefix}kc_teamer", $update, ['id' => $id]); + return rest_ensure_response(['success' => true, 'message' => 'Teamer aktualisiert.']); +} + +function kc_api_delete_teamer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $id = intval($request->get_param('id')); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_teamer WHERE id=%d", $id)); + if (!$exists) return new WP_Error('kc_api_teamer_not_found', 'Teamer nicht gefunden.', ['status' => 404]); + + $wpdb->delete("{$prefix}kc_teamer", ['id' => $id]); + return rest_ensure_response(['success' => true, 'message' => 'Teamer gelöscht.']); +} + +function kc_api_set_teamer_password(WP_REST_Request $request) { + $data = kc_api_request_data($request); + $password = trim((string)($data['password'] ?? '')); + + if ($password === '') { + delete_option('kc_teamer_password_hash'); + return rest_ensure_response(['success' => true, 'message' => 'Teamer-Passwort entfernt.']); + } + + update_option('kc_teamer_password_hash', wp_hash_password($password)); + return rest_ensure_response(['success' => true, 'message' => 'Teamer-Passwort gespeichert.']); +} + +function kc_api_get_teilnehmer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $id = intval($request->get_param('id')); + if ($id > 0) { + $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_teilnehmer WHERE id=%d", $id)); + if (!$row) return new WP_Error('kc_api_tn_not_found', 'Teilnehmer nicht gefunden.', ['status' => 404]); + return rest_ensure_response(['success' => true, 'item' => $row]); + } + + $wahl_id = intval($request->get_param('wahl_id')); + $phase = intval($request->get_param('phase')); + + $sql = "SELECT * FROM {$prefix}kc_teilnehmer"; + $where = []; + $args = []; + + if ($wahl_id > 0) { + $where[] = 'wahl_id=%d'; + $args[] = $wahl_id; + } + if ($phase > 0) { + $where[] = 'phase=%d'; + $args[] = $phase; + } + + if (!empty($where)) { + $sql .= ' WHERE ' . implode(' AND ', $where); + $sql = $wpdb->prepare($sql, $args); + } + + $sql .= ' ORDER BY wahl_id, phase, nachname, vorname'; + $rows = $wpdb->get_results($sql); + + return rest_ensure_response(['success' => true, 'count' => count((array)$rows), 'items' => $rows]); +} + +function kc_api_create_teilnehmer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $data = kc_api_request_data($request); + $validated = kc_api_validate_teilnehmer_payload($data, 0); + if (is_wp_error($validated)) return $validated; + + $wpdb->insert("{$prefix}kc_teilnehmer", $validated); + + return rest_ensure_response(['success' => true, 'id' => intval($wpdb->insert_id), 'message' => 'Teilnehmer angelegt.']); +} + +function kc_api_update_teilnehmer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $id = intval($request->get_param('id')); + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_teilnehmer WHERE id=%d", $id)); + if (!$exists) return new WP_Error('kc_api_tn_not_found', 'Teilnehmer nicht gefunden.', ['status' => 404]); + + $data = kc_api_request_data($request); + $validated = kc_api_validate_teilnehmer_payload($data, $id); + if (is_wp_error($validated)) return $validated; + + $wpdb->update("{$prefix}kc_teilnehmer", $validated, ['id' => $id]); + + return rest_ensure_response(['success' => true, 'message' => 'Teilnehmer aktualisiert.']); +} + +function kc_api_delete_teilnehmer(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $id = intval($request->get_param('id')); + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_teilnehmer WHERE id=%d", $id)); + if (!$exists) return new WP_Error('kc_api_tn_not_found', 'Teilnehmer nicht gefunden.', ['status' => 404]); + + $wpdb->delete("{$prefix}kc_teilnehmer", ['id' => $id]); + + return rest_ensure_response(['success' => true, 'message' => 'Teilnehmer gelöscht.']); +} + +function kc_api_submit_wahl_anmeldung(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $wahl_id = intval($request->get_param('id')); + $wahl = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_wahlen WHERE id=%d", $wahl_id)); + if (!$wahl) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $data = kc_api_request_data($request); + $vorname = sanitize_text_field($data['vorname'] ?? ''); + $nachname = sanitize_text_field($data['nachname'] ?? ''); + $wishes = isset($data['wishes']) && is_array($data['wishes']) ? $data['wishes'] : []; + + if ($vorname === '' || $nachname === '') { + return new WP_Error('kc_api_invalid_input', 'Vorname und Nachname sind erforderlich.', ['status' => 400]); + } + + $norm_v = mb_strtolower(trim($vorname)); + $norm_n = mb_strtolower(trim($nachname)); + $exists = $wpdb->get_var($wpdb->prepare( + "SELECT COUNT(*) FROM {$prefix}kc_teilnehmer WHERE LOWER(TRIM(vorname))=%s AND LOWER(TRIM(nachname))=%s AND wahl_id=%d", + $norm_v, + $norm_n, + $wahl_id + )); + + if (intval($exists) > 0) { + return new WP_Error('kc_api_duplicate_participant', 'Diese Kombination aus Vorname/Nachname existiert bereits für diese Wahl.', ['status' => 409]); + } + + $phasen = max(1, intval($wahl->anzahl_einheiten)); + $inserted_ids = []; + + for ($phase = 1; $phase <= $phasen; $phase++) { + if (empty($wishes[$phase]) || !is_array($wishes[$phase])) { + return new WP_Error('kc_api_missing_phase_wishes', 'Wünsche für jede Phase erforderlich.', ['status' => 400]); + } + + $w1 = intval($wishes[$phase][0] ?? 0); + $w2 = intval($wishes[$phase][1] ?? 0); + $w3 = intval($wishes[$phase][2] ?? 0); + + if ($w1 <= 0 || $w2 <= 0 || $w3 <= 0 || count(array_unique([$w1, $w2, $w3])) !== 3) { + return new WP_Error('kc_api_invalid_wishes', 'Jede Phase benötigt 3 unterschiedliche Wünsche.', ['status' => 400]); + } + + $allowed = $wpdb->get_col($wpdb->prepare("SELECT workshop_id FROM {$prefix}kc_wahl_workshops WHERE wahl_id=%d AND phase=%d", $wahl_id, $phase)); + $allowed = array_map('intval', (array)$allowed); + if (!empty($allowed)) { + foreach ([$w1, $w2, $w3] as $wid) { + if (!in_array($wid, $allowed, true)) { + return new WP_Error('kc_api_invalid_workshop_for_phase', 'Ungültige Workshop-Auswahl für Phase ' . intval($phase), ['status' => 400]); + } + } + } + + $wpdb->insert("{$prefix}kc_teilnehmer", [ + 'vorname' => $vorname, + 'nachname' => $nachname, + 'wahl_id' => $wahl_id, + 'phase' => $phase, + 'wunsch1' => $w1, + 'wunsch2' => $w2, + 'wunsch3' => $w3, + 'deleted' => 0, + ]); + + $inserted_ids[] = intval($wpdb->insert_id); + } + + return rest_ensure_response([ + 'success' => true, + 'message' => 'Anmeldung gespeichert.', + 'participant_ids' => $inserted_ids, + ]); +} + +function kc_api_get_force_zuteilungen(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $id = intval($request->get_param('id')); + if ($id > 0) { + $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$prefix}kc_force_zuteilung WHERE id=%d", $id)); + if (!$row) return new WP_Error('kc_api_force_not_found', 'Force-Zuteilung nicht gefunden.', ['status' => 404]); + return rest_ensure_response(['success' => true, 'item' => $row]); + } + + $rows = $wpdb->get_results("SELECT * FROM {$prefix}kc_force_zuteilung ORDER BY id DESC"); + return rest_ensure_response(['success' => true, 'count' => count((array)$rows), 'items' => $rows]); +} + +function kc_api_create_force_zuteilung(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $data = kc_api_request_data($request); + + $insert = [ + 'teilnehmer_id' => intval($data['teilnehmer_id'] ?? 0), + 'wahl_id' => intval($data['wahl_id'] ?? 0), + 'phase' => intval($data['phase'] ?? 1), + 'workshop_id' => intval($data['workshop_id'] ?? 0), + 'kommentar' => sanitize_text_field($data['kommentar'] ?? ''), + ]; + + if ($insert['teilnehmer_id'] <= 0 || $insert['wahl_id'] <= 0 || $insert['phase'] <= 0 || $insert['workshop_id'] <= 0) { + return new WP_Error('kc_api_invalid_input', 'Ungültige Force-Zuteilung.', ['status' => 400]); + } + + $wpdb->insert("{$prefix}kc_force_zuteilung", $insert); + return rest_ensure_response(['success' => true, 'id' => intval($wpdb->insert_id), 'message' => 'Force-Zuteilung angelegt.']); +} + +function kc_api_update_force_zuteilung(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $id = intval($request->get_param('id')); + $data = kc_api_request_data($request); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_force_zuteilung WHERE id=%d", $id)); + if (!$exists) return new WP_Error('kc_api_force_not_found', 'Force-Zuteilung nicht gefunden.', ['status' => 404]); + + $update = []; + if (array_key_exists('teilnehmer_id', $data)) $update['teilnehmer_id'] = intval($data['teilnehmer_id']); + if (array_key_exists('wahl_id', $data)) $update['wahl_id'] = intval($data['wahl_id']); + if (array_key_exists('phase', $data)) $update['phase'] = intval($data['phase']); + if (array_key_exists('workshop_id', $data)) $update['workshop_id'] = intval($data['workshop_id']); + if (array_key_exists('kommentar', $data)) $update['kommentar'] = sanitize_text_field($data['kommentar']); + + if (empty($update)) return new WP_Error('kc_api_empty_update', 'Keine Felder zum Aktualisieren übergeben.', ['status' => 400]); + + $wpdb->update("{$prefix}kc_force_zuteilung", $update, ['id' => $id]); + return rest_ensure_response(['success' => true, 'message' => 'Force-Zuteilung aktualisiert.']); +} + +function kc_api_delete_force_zuteilung(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $id = intval($request->get_param('id')); + + $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}kc_force_zuteilung WHERE id=%d", $id)); + if (!$exists) return new WP_Error('kc_api_force_not_found', 'Force-Zuteilung nicht gefunden.', ['status' => 404]); + + $wpdb->delete("{$prefix}kc_force_zuteilung", ['id' => $id]); + return rest_ensure_response(['success' => true, 'message' => 'Force-Zuteilung gelöscht.']); +} + function kc_api_get_wahl_zuteilungen(WP_REST_Request $request) { global $wpdb; $prefix = $wpdb->prefix; @@ -97,21 +924,264 @@ function kc_api_get_wahl_zuteilungen(WP_REST_Request $request) { ]); } -function kc_api_get_status(WP_REST_Request $request) { - $user = wp_get_current_user(); +function kc_api_run_wahl_zuteilung(WP_REST_Request $request) { + $wahl_id = intval($request->get_param('id')); + if ($wahl_id <= 0) { + return new WP_Error('kc_api_invalid_id', 'Ungültige Wahl-ID.', ['status' => 400]); + } + + if (!function_exists('kc_run_zuteilung')) { + return new WP_Error('kc_api_zuteilung_missing', 'Zuteilungsfunktion nicht verfügbar.', ['status' => 500]); + } + + kc_run_zuteilung($wahl_id); + + return rest_ensure_response(['success' => true, 'message' => 'Zuteilung ausgeführt.', 'wahl_id' => $wahl_id]); +} + +function kc_api_delete_wahl_zuteilungen(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($request->get_param('id')); + + $wpdb->delete("{$prefix}kc_zuteilung", ['wahl_id' => $wahl_id]); + + return rest_ensure_response(['success' => true, 'message' => 'Zuteilungen gelöscht.', 'wahl_id' => $wahl_id]); +} + +function kc_api_get_wahl_ergebnis(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $wahl_id = intval($request->get_param('id')); + + $wahl = $wpdb->get_row($wpdb->prepare("SELECT id, name, freigegeben FROM {$prefix}kc_wahlen WHERE id=%d AND deleted=0", $wahl_id)); + if (!$wahl) { + return new WP_Error('kc_api_wahl_not_found', 'Wahl nicht gefunden.', ['status' => 404]); + } + + $workshops = $wpdb->get_results($wpdb->prepare( + "SELECT ws.id, ws.name + FROM {$prefix}kc_workshops ws + JOIN {$prefix}kc_wahl_workshops ww ON ws.id = ww.workshop_id + WHERE ww.wahl_id=%d", + $wahl_id + )); + + $ws_names = []; + foreach ((array) $workshops as $ws) $ws_names[intval($ws->id)] = (string)$ws->name; + + $rows = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$prefix}kc_zuteilung WHERE wahl_id=%d", $wahl_id)); + + $grouped = []; + $not_assigned = []; + foreach ((array) $rows as $row) { + $phase = intval($row->phase); + if ($phase <= 0) continue; + if (!isset($grouped[$phase])) $grouped[$phase] = []; + + if (!empty($row->workshop_id) && isset($ws_names[intval($row->workshop_id)])) { + $wid = intval($row->workshop_id); + if (!isset($grouped[$phase][$wid])) { + $grouped[$phase][$wid] = [ + 'workshop_id' => $wid, + 'workshop_name' => $ws_names[$wid], + 'teilnehmer' => [], + ]; + } + $grouped[$phase][$wid]['teilnehmer'][] = [ + 'id' => intval($row->id), + 'teilnehmer_id' => intval($row->teilnehmer_id), + 'vorname' => (string)$row->vorname, + 'nachname' => (string)$row->nachname, + 'wunsch_rang' => intval($row->wunsch_rang), + ]; + } else { + $not_assigned[] = [ + 'id' => intval($row->id), + 'teilnehmer_id' => intval($row->teilnehmer_id), + 'vorname' => (string)$row->vorname, + 'nachname' => (string)$row->nachname, + 'phase' => $phase, + ]; + } + } return rest_ensure_response([ 'success' => true, - 'authenticated' => is_user_logged_in(), - 'authorized' => current_user_can('manage_options'), - 'user' => [ - 'id' => intval($user->ID), - 'login' => (string) $user->user_login, - 'display_name' => (string) $user->display_name, + 'wahl' => [ + 'id' => intval($wahl->id), + 'name' => (string)$wahl->name, + 'freigegeben' => intval($wahl->freigegeben) === 1, ], + 'phases' => $grouped, + 'not_assigned' => $not_assigned, ]); } +function kc_api_create_workshop_by_teamer_password(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $data = kc_api_request_data($request); + $pw = trim((string)($data['teamer_password'] ?? '')); + $vorname = sanitize_text_field($data['vorname'] ?? ''); + $nachname = sanitize_text_field($data['nachname'] ?? ''); + $ws_name = sanitize_text_field($data['workshop_name'] ?? ''); + $min_t = max(0, intval($data['min_teilnehmer'] ?? 0)); + $max_t = max(1, intval($data['max_teilnehmer'] ?? 10)); + + $saved_hash = get_option('kc_teamer_password_hash', ''); + if ($pw === '' || empty($saved_hash) || !wp_check_password($pw, $saved_hash)) { + return new WP_Error('kc_api_invalid_teamer_password', 'Teamer-Passwort ungültig.', ['status' => 403]); + } + + if ($vorname === '' || $nachname === '' || $ws_name === '') { + return new WP_Error('kc_api_invalid_input', 'Vorname, Nachname und Workshop-Name sind erforderlich.', ['status' => 400]); + } + + if ($min_t > $max_t) { + return new WP_Error('kc_api_invalid_capacity', 'min_teilnehmer darf nicht größer als max_teilnehmer sein.', ['status' => 400]); + } + + $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); + } + + $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 <= 0) { + return new WP_Error('kc_api_create_workshop_failed', 'Fehler beim Anlegen des Workshops.', ['status' => 500]); + } + + $wpdb->insert("{$prefix}kc_workshop_teamer", ['workshop_id' => $wid, 'teamer_id' => $tid]); + + return rest_ensure_response(['success' => true, 'workshop_id' => $wid, 'teamer_id' => $tid, 'message' => 'Workshop angelegt und Teamer zugeordnet.']); +} + +function kc_api_generate_testdata(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + $data = kc_api_request_data($request); + + $num_workshops = max(1, intval($data['num_workshops'] ?? 3)); + $num_part_per_phase = max(1, intval($data['num_part_per_phase'] ?? 10)); + $phases = max(1, min(4, intval($data['phases'] ?? 2))); + $min_cap = max(1, intval($data['min_capacity'] ?? 4)); + $max_cap = max($min_cap, intval($data['max_capacity'] ?? 8)); + + $created = ['wahlen' => 0, 'workshops' => 0, 'ww' => 0, 'teilnehmer' => 0]; + + $wahl_name = 'TEST - Zufallswahl ' . time(); + $wpdb->insert("{$prefix}kc_wahlen", [ + 'name' => $wahl_name, + 'beschreibung' => 'Automatisch erzeugte zufällige Testdaten', + 'anzahl_einheiten' => $phases, + 'freigegeben' => 1, + 'deleted' => 0, + ]); + $wahl_id = intval($wpdb->insert_id); + if ($wahl_id) $created['wahlen']++; + + $ws_ids = []; + for ($i = 0; $i < $num_workshops; $i++) { + $cap = rand($min_cap, $max_cap); + $wn = 'TEST - WS ' . ($i + 1) . ' #' . rand(1000, 9999); + $wpdb->insert("{$prefix}kc_workshops", [ + 'name' => $wn, + 'beschreibung' => 'Auto-generated', + 'max_teilnehmer' => $cap, + ]); + + $wid = intval($wpdb->insert_id); + if ($wid) { + $ws_ids[] = $wid; + $created['workshops']++; + for ($ph = 1; $ph <= $phases; $ph++) { + $wpdb->insert("{$prefix}kc_wahl_workshops", ['wahl_id' => $wahl_id, 'workshop_id' => $wid, 'phase' => $ph]); + $created['ww']++; + } + } + } + + $firsts = ['Lukas', 'Mia', 'Jonas', 'Emma', 'Noah', 'Hannah', 'Paul', 'Lea', 'Tim', 'Lina', 'Max', 'Sara']; + $lasts = ['Müller', 'Schmidt', 'Schneider', 'Fischer', 'Weber', 'Mayer', 'Wagner', 'Becker', 'Hoffmann', 'Schulz']; + + for ($n = 0; $n < $num_part_per_phase; $n++) { + for ($ph = 1; $ph <= $phases; $ph++) { + $fn = $firsts[array_rand($firsts)] . rand(1, 999); + $ln = $lasts[array_rand($lasts)]; + $w1 = $ws_ids[array_rand($ws_ids)]; + $w2 = $ws_ids[array_rand($ws_ids)]; + $w3 = $ws_ids[array_rand($ws_ids)]; + $wpdb->insert("{$prefix}kc_teilnehmer", [ + 'vorname' => $fn, + 'nachname' => $ln, + 'wahl_id' => $wahl_id, + 'phase' => $ph, + 'wunsch1' => intval($w1), + 'wunsch2' => intval($w2), + 'wunsch3' => intval($w3), + 'deleted' => 0, + ]); + if (intval($wpdb->insert_id)) $created['teilnehmer']++; + } + } + + return rest_ensure_response(['success' => true, 'created' => $created, 'wahl_id' => $wahl_id]); +} + +function kc_api_clear_testdata(WP_REST_Request $request) { + global $wpdb; + $prefix = $wpdb->prefix; + + $test_ws = $wpdb->get_col($wpdb->prepare("SELECT id FROM {$prefix}kc_workshops WHERE name LIKE %s", 'TEST - %')); + if (!empty($test_ws)) { + foreach ($test_ws as $tw) { + $wpdb->delete("{$prefix}kc_workshop_teamer", ['workshop_id' => $tw]); + } + $wpdb->query("DELETE FROM {$prefix}kc_wahl_workshops WHERE workshop_id IN (" . implode(',', array_map('intval', $test_ws)) . ")"); + $wpdb->query("DELETE FROM {$prefix}kc_workshops WHERE id IN (" . implode(',', array_map('intval', $test_ws)) . ")"); + } + + $test_wahlen = $wpdb->get_col($wpdb->prepare("SELECT id FROM {$prefix}kc_wahlen WHERE name LIKE %s", 'TEST - %')); + if (!empty($test_wahlen)) { + $ids = implode(',', array_map('intval', $test_wahlen)); + $wpdb->query("DELETE FROM {$prefix}kc_zuteilung WHERE wahl_id IN (" . $ids . ")"); + $wpdb->query("DELETE FROM {$prefix}kc_force_zuteilung WHERE wahl_id IN (" . $ids . ")"); + $wpdb->query("DELETE FROM {$prefix}kc_teilnehmer WHERE wahl_id IN (" . $ids . ")"); + $wpdb->query("DELETE FROM {$prefix}kc_wahl_workshops WHERE wahl_id IN (" . $ids . ")"); + $wpdb->query("DELETE FROM {$prefix}kc_wahlen WHERE id IN (" . $ids . ")"); + } + + return rest_ensure_response(['success' => true, 'message' => 'Alle TEST-Daten wurden entfernt.']); +} + +function kc_api_reset_plugin_data(WP_REST_Request $request) { + if (!function_exists('kc_uninstall_tables') || !function_exists('kc_install_tables')) { + return new WP_Error('kc_api_reset_unavailable', 'Reset-Funktionen nicht verfügbar.', ['status' => 500]); + } + + kc_uninstall_tables(); + kc_install_tables(); + + return rest_ensure_response(['success' => true, 'message' => 'Plugin-Daten zurückgesetzt (Tabellen neu erstellt).']); +} + add_action('rest_api_init', function() { register_rest_route('kc-internal/v1', '/status', [ 'methods' => WP_REST_Server::READABLE, @@ -120,22 +1190,245 @@ add_action('rest_api_init', function() { ]); register_rest_route('kc-internal/v1', '/wahlen', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => 'kc_api_get_wahlen', + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_wahlen', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_create_wahl', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/wahlen/(?P\d+)', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_wahl', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_update_wahl', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'kc_api_delete_wahl', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/wahlen/(?P\d+)/workshops', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_wahl_workshops', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_set_wahl_workshops', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/wahlen/(?P\d+)/anmeldung', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_submit_wahl_anmeldung', 'permission_callback' => 'kc_api_permission_check', ]); register_rest_route('kc-internal/v1', '/wahlen/(?P\d+)/zuteilungen', [ - 'methods' => WP_REST_Server::READABLE, - 'callback' => 'kc_api_get_wahl_zuteilungen', - 'permission_callback' => 'kc_api_permission_check', - 'args' => [ - 'id' => [ - 'validate_callback' => function($param) { - return is_numeric($param) && intval($param) > 0; - } - ] + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_wahl_zuteilungen', + 'permission_callback' => 'kc_api_permission_check', ], + [ + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'kc_api_delete_wahl_zuteilungen', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/wahlen/(?P\d+)/zuteilung/run', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_run_wahl_zuteilung', + 'permission_callback' => 'kc_api_permission_check', + ]); + + register_rest_route('kc-internal/v1', '/wahlen/(?P\d+)/ergebnis', [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_wahl_ergebnis', + 'permission_callback' => 'kc_api_permission_check', + ]); + + register_rest_route('kc-internal/v1', '/workshops', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_workshops', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_create_workshop', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/workshops/(?P\d+)', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_workshop', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_update_workshop', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'kc_api_delete_workshop', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/workshops/(?P\d+)/teamer', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_workshop_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_set_workshop_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/teamer', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_create_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/teamer/(?P\d+)', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_update_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'kc_api_delete_teamer', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/teamer/password', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_set_teamer_password', + 'permission_callback' => 'kc_api_permission_check', + ]); + + register_rest_route('kc-internal/v1', '/teilnehmer', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_teilnehmer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_create_teilnehmer', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/teilnehmer/(?P\d+)', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_teilnehmer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_update_teilnehmer', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'kc_api_delete_teilnehmer', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/force-zuteilungen', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_force_zuteilungen', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_create_force_zuteilung', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/force-zuteilungen/(?P\d+)', [ + [ + 'methods' => WP_REST_Server::READABLE, + 'callback' => 'kc_api_get_force_zuteilungen', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::EDITABLE, + 'callback' => 'kc_api_update_force_zuteilung', + 'permission_callback' => 'kc_api_permission_check', + ], + [ + 'methods' => WP_REST_Server::DELETABLE, + 'callback' => 'kc_api_delete_force_zuteilung', + 'permission_callback' => 'kc_api_permission_check', + ], + ]); + + register_rest_route('kc-internal/v1', '/teamer/workshop-create', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_create_workshop_by_teamer_password', + 'permission_callback' => 'kc_api_permission_check', + ]); + + register_rest_route('kc-internal/v1', '/data/test/generate', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_generate_testdata', + 'permission_callback' => 'kc_api_owner_permission_check', + ]); + + register_rest_route('kc-internal/v1', '/data/test/clear', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_clear_testdata', + 'permission_callback' => 'kc_api_owner_permission_check', + ]); + + register_rest_route('kc-internal/v1', '/data/reset', [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => 'kc_api_reset_plugin_data', + 'permission_callback' => 'kc_api_owner_permission_check', ]); });