From 4a593677dd3253915476f1058b7ba835f0ab21ab Mon Sep 17 00:00:00 2001 From: M_Viper Date: Sun, 29 Mar 2026 22:25:37 +0200 Subject: [PATCH] Update from Git Manager GUI --- includes/class-forum-ajax.php | 293 +++++++++++++++++++++++++++- includes/class-forum-bbcode.php | 19 +- includes/class-forum-db.php | 63 ++++++ includes/class-forum-export.php | 2 +- includes/class-forum-roles.php | 33 +++- includes/class-forum-shortcodes.php | 194 ++++++++++++++++-- 6 files changed, 579 insertions(+), 25 deletions(-) diff --git a/includes/class-forum-ajax.php b/includes/class-forum-ajax.php index fda76e1..175dd00 100644 --- a/includes/class-forum-ajax.php +++ b/includes/class-forum-ajax.php @@ -2,12 +2,72 @@ if ( ! defined( 'ABSPATH' ) ) exit; class WBF_Ajax { + // ── Discord-Rollen-Sync manuell anstoßen ──────────────────────────────── + public static function handle_manual_discord_sync() { + self::verify(); + $user = WBF_Auth::get_current_user(); + if (!$user || WBF_Roles::level($user->role) < 80) { + wp_send_json_error(['message' => 'Keine Berechtigung.']); + } + if (!function_exists('wbf_run_discord_role_sync')) { + wp_send_json_error(['message' => 'Sync-Funktion nicht gefunden.']); + } + // Sync anstoßen (läuft synchron, kann bei vielen Usern etwas dauern) + wbf_run_discord_role_sync(); + wp_send_json_success(['message' => 'Discord-Rollen-Sync wurde ausgeführt.']); + } + + // ── Discord-Rollen-Sync für einzelnen Nutzer ───────────────────────────── + public static function handle_discord_sync_user() { + self::verify(); + $admin = WBF_Auth::get_current_user(); + if ( ! $admin || WBF_Roles::level( $admin->role ) < 80 ) { + wp_send_json_error( [ 'message' => 'Keine Berechtigung.' ] ); + } + + $target_id = (int) ( $_POST['user_id'] ?? 0 ); + if ( ! $target_id ) { + wp_send_json_error( [ 'message' => 'Keine Nutzer-ID.' ] ); + } + + $s = function_exists( 'wbf_get_settings' ) ? wbf_get_settings() : []; + $token = trim( $s['discord_bot_token'] ?? '' ); + $guild = trim( $s['discord_guild_id'] ?? '' ); + $role_map = json_decode( $s['discord_role_map'] ?? '{}', true ) ?: []; + + if ( ! $token || ! $guild || empty( $role_map ) ) { + wp_send_json_error( [ 'message' => 'Discord nicht konfiguriert.' ] ); + } + + global $wpdb; + $discord_uid = $wpdb->get_var( $wpdb->prepare( + "SELECT meta_value FROM {$wpdb->prefix}forum_user_meta + WHERE user_id = %d AND meta_key = 'discord_user_id'", + $target_id + ) ); + + if ( ! $discord_uid ) { + wp_send_json_error( [ 'message' => 'Nutzer hat kein verknüpftes Discord-Konto.' ] ); + } + + // Beide Richtungen: Discord → Forum + if ( function_exists( 'wbf_sync_discord_role_for_user' ) ) { + wbf_sync_discord_role_for_user( $target_id, $discord_uid, $token, $guild, $role_map ); + } + + // Frisch geladene Rolle zurückgeben damit die UI sofort aktualisiert werden kann + $updated = WBF_DB::get_user( $target_id ); + wp_send_json_success( [ + 'message' => 'Sync abgeschlossen.', + 'new_role' => $updated ? $updated->role : '', + ] ); + } public static function init() { $actions = [ 'wbf_login', 'wbf_register', 'wbf_logout', 'wbf_new_thread', 'wbf_new_post', 'wbf_toggle_like', - 'wbf_update_profile', 'wbf_upload_avatar', 'wbf_upload_post_image', + 'wbf_update_profile', 'wbf_upload_avatar', 'wbf_upload_banner', 'wbf_upload_post_image', 'wbf_forgot_password', 'wbf_reset_password', 'wbf_load_more_messages', 'wbf_create_invite', 'wbf_delete_invite', 'wbf_toggle_subscribe', 'wbf_restore_content', 'wbf_toggle_profile_visibility', @@ -21,6 +81,11 @@ class WBF_Ajax { 'wbf_toggle_ignore', 'wbf_change_email', 'wbf_save_notification_prefs', + 'wbf_save_discord', + 'wbf_discord_send_code', + 'wbf_discord_verify_code', + 'wbf_manual_discord_sync', + 'wbf_discord_sync_user', ]; foreach ($actions as $action) { add_action('wp_ajax_nopriv_' . $action, [__CLASS__, str_replace('wbf_','handle_',$action)]); @@ -507,6 +572,56 @@ class WBF_Ajax { wp_send_json_success(['avatar_url'=>$url]); } + // ── Banner Upload ──────────────────────────────────────────────────────── + + public static function handle_upload_banner() { + self::verify(); + $user = WBF_Auth::get_current_user(); + if (!$user) wp_send_json_error(['message'=>'Nicht eingeloggt.']); + if (empty($_FILES['banner'])) wp_send_json_error(['message'=>'Keine Datei.']); + + $allowed_types = ['image/jpeg','image/png','image/gif','image/webp']; + + // Max 4 MB für Banner (größer als Avatar) + if ( $_FILES['banner']['size'] > 4 * 1024 * 1024 ) { + wp_send_json_error(['message'=>'Maximale Dateigröße: 4 MB.']); + } + + // Server-seitige MIME-Typ-Prüfung + $tmp = $_FILES['banner']['tmp_name'] ?? ''; + if ( ! $tmp || ! is_uploaded_file( $tmp ) ) { + wp_send_json_error(['message'=>'Ungültiger Datei-Upload.']); + } + if ( function_exists('finfo_open') ) { + $finfo = finfo_open( FILEINFO_MIME_TYPE ); + $real_mime = finfo_file( $finfo, $tmp ); + finfo_close( $finfo ); + } else { + $et_map = [ + IMAGETYPE_JPEG => 'image/jpeg', + IMAGETYPE_PNG => 'image/png', + IMAGETYPE_GIF => 'image/gif', + IMAGETYPE_WEBP => 'image/webp', + ]; + $et = @exif_imagetype( $tmp ); + $real_mime = $et_map[$et] ?? ''; + } + if ( ! in_array( $real_mime, $allowed_types, true ) ) { + wp_send_json_error(['message'=>'Nur JPG, PNG, GIF und WebP erlaubt.']); + } + + require_once ABSPATH . 'wp-admin/includes/image.php'; + require_once ABSPATH . 'wp-admin/includes/file.php'; + require_once ABSPATH . 'wp-admin/includes/media.php'; + + $id = media_handle_upload('banner', 0); + if (is_wp_error($id)) wp_send_json_error(['message'=>$id->get_error_message()]); + + $url = wp_get_attachment_url($id); + WBF_DB::update_user($user->id, ['banner_url'=>$url]); + wp_send_json_success(['banner_url'=>$url]); + } + // ── Report ──────────────────────────────────────────────────────────────── public static function handle_report_post() { @@ -1447,6 +1562,182 @@ class WBF_Ajax { ] ); } + // ── Discord: Verifikations-Code per Bot-DM senden ───────────────────────── + + public static function handle_discord_send_code() { + self::verify(); + $user = WBF_Auth::get_current_user(); + if ( ! $user ) wp_send_json_error(['message' => 'Nicht eingeloggt.']); + + $s = wbf_get_settings(); + $token = trim($s['discord_bot_token'] ?? ''); + $guild = trim($s['discord_guild_id'] ?? ''); + + if ( ! $token ) { + wp_send_json_error(['message' => 'Discord-Bot ist noch nicht konfiguriert. Bitte wende dich an einen Admin.']); + } + + $username_input = sanitize_text_field($_POST['discord_username'] ?? ''); + if ( ! $username_input ) { + wp_send_json_error(['message' => 'Bitte Discord-Benutzername eingeben.']); + } + + // Nutzer auf dem Guild suchen (nach Username oder per Search) + $discord_user_id = self::discord_find_user_id($username_input, $token, $guild); + if ( ! $discord_user_id ) { + wp_send_json_error(['message' => 'Discord-Nutzer nicht auf dem Server gefunden. Stelle sicher, dass du Mitglied des Servers bist.']); + } + + // Verifikations-Code generieren (6-stellig) + $code = strtoupper(substr(md5(uniqid(mt_rand(), true)), 0, 6)); + $expires = time() + 600; // 10 Minuten + + // Code + Discord-User-ID temporär speichern + WBF_DB::set_user_meta($user->id, 'discord_verify_code', $code); + WBF_DB::set_user_meta($user->id, 'discord_verify_expires', (string)$expires); + WBF_DB::set_user_meta($user->id, 'discord_verify_pending_id', $discord_user_id); + + // DM senden + $sent = self::discord_send_dm($discord_user_id, $token, + "🔐 **Dein Verifikationscode für " . get_bloginfo('name') . ":**\n\n" . + "```" . $code . "```\n" . + "Gib diesen Code im Forum ein. Er ist **10 Minuten** gültig.\n" . + "_Falls du diese Nachricht nicht erwartet hast, ignoriere sie einfach._" + ); + + if ( ! $sent ) { + wp_send_json_error(['message' => 'DM konnte nicht gesendet werden. Stelle sicher, dass du DMs von Server-Mitgliedern zulässt.']); + } + + wp_send_json_success(['message' => '✅ Code gesendet! Prüfe deine Discord-DMs und gib den 6-stelligen Code ein.', 'step' => 'enter_code']); + } + + // ── Discord: Code überprüfen + Verbindung herstellen ───────────────────── + + public static function handle_discord_verify_code() { + self::verify(); + $user = WBF_Auth::get_current_user(); + if ( ! $user ) wp_send_json_error(['message' => 'Nicht eingeloggt.']); + + $code_input = strtoupper(sanitize_text_field($_POST['verify_code'] ?? '')); + $meta = WBF_DB::get_user_meta($user->id); + + $stored_code = strtoupper($meta['discord_verify_code'] ?? ''); + $expires = (int)($meta['discord_verify_expires'] ?? 0); + $discord_uid = $meta['discord_verify_pending_id'] ?? ''; + + if ( ! $stored_code || ! $discord_uid ) { + wp_send_json_error(['message' => 'Kein offener Verifizierungs-Vorgang. Bitte erneut starten.']); + } + if ( time() > $expires ) { + wp_send_json_error(['message' => 'Code abgelaufen. Bitte erneut einen Code anfordern.']); + } + if ( ! hash_equals($stored_code, $code_input) ) { + wp_send_json_error(['message' => 'Falscher Code. Bitte erneut versuchen.']); + } + + // Discord-Username abrufen (für Anzeige) + $s = wbf_get_settings(); + $token = trim($s['discord_bot_token'] ?? ''); + $display_name = $discord_uid; + if ( $token ) { + $res = wp_remote_get("https://discord.com/api/v10/users/{$discord_uid}", [ + 'timeout' => 5, + 'headers' => ['Authorization' => 'Bot ' . $token], + ]); + if ( ! is_wp_error($res) && wp_remote_retrieve_response_code($res) === 200 ) { + $d = json_decode(wp_remote_retrieve_body($res), true); + $display_name = $d['global_name'] ?? $d['username'] ?? $discord_uid; + } + } + + // Speichern + WBF_DB::set_user_meta($user->id, 'discord_user_id', $discord_uid); + WBF_DB::set_user_meta($user->id, 'discord_username', $display_name); + // Temp-Daten löschen + WBF_DB::set_user_meta($user->id, 'discord_verify_code', ''); + WBF_DB::set_user_meta($user->id, 'discord_verify_expires', ''); + WBF_DB::set_user_meta($user->id, 'discord_verify_pending_id', ''); + + // Rollen-Sync direkt nach Verifikation + $guild = trim($s['discord_guild_id'] ?? ''); + $role_map = json_decode($s['discord_role_map'] ?? '{}', true) ?: []; + if ( ($s['discord_role_sync'] ?? '0') === '1' && $token && $guild && $role_map ) { + wbf_sync_discord_role_for_user($user->id, $discord_uid, $token, $guild, $role_map); + } + + wp_send_json_success([ + 'message' => '🎉 Discord erfolgreich verknüpft!', + 'connected' => true, + 'display_name' => esc_html($display_name), + ]); + } + + // ── Discord: Verbindung trennen ─────────────────────────────────────────── + + public static function handle_save_discord() { + self::verify(); + $user = WBF_Auth::get_current_user(); + if ( ! $user ) wp_send_json_error(['message' => 'Nicht eingeloggt.']); + + $action = sanitize_key( $_POST['sub_action'] ?? 'save' ); + + if ( $action === 'disconnect' ) { + WBF_DB::set_user_meta($user->id, 'discord_username', ''); + WBF_DB::set_user_meta($user->id, 'discord_user_id', ''); + wp_send_json_success(['message' => 'Discord-Verbindung getrennt.', 'connected' => false]); + } + + wp_send_json_error(['message' => 'Unbekannte Aktion.']); + } + + // ── Discord Hilfsmethoden ───────────────────────────────────────────────── + + private static function discord_find_user_id($username_input, $token, $guild) { + if ( ! $guild ) return null; + // Guild-Member-Search (max. 1 Treffer) + $search = rawurlencode($username_input); + $res = wp_remote_get("https://discord.com/api/v10/guilds/{$guild}/members/search?query={$search}&limit=5", [ + 'timeout' => 6, + 'headers' => ['Authorization' => 'Bot ' . $token], + ]); + if ( is_wp_error($res) || wp_remote_retrieve_response_code($res) !== 200 ) return null; + $members = json_decode(wp_remote_retrieve_body($res), true); + if ( empty($members) ) return null; + // Exakten Treffer bevorzugen + $input_lower = strtolower($username_input); + foreach ( $members as $m ) { + $uname = strtolower($m['user']['username'] ?? ''); + $global = strtolower($m['user']['global_name'] ?? ''); + if ( $uname === $input_lower || $global === $input_lower ) { + return $m['user']['id']; + } + } + // Erster Treffer als Fallback + return $members[0]['user']['id'] ?? null; + } + + private static function discord_send_dm($user_id, $token, $message) { + // DM-Channel erstellen + $ch_res = wp_remote_post('https://discord.com/api/v10/users/@me/channels', [ + 'timeout' => 6, + 'headers' => ['Authorization' => 'Bot ' . $token, 'Content-Type' => 'application/json'], + 'body' => json_encode(['recipient_id' => $user_id]), + ]); + if ( is_wp_error($ch_res) || wp_remote_retrieve_response_code($ch_res) !== 200 ) return false; + $channel = json_decode(wp_remote_retrieve_body($ch_res), true); + $ch_id = $channel['id'] ?? ''; + if ( ! $ch_id ) return false; + + // Nachricht senden + $msg_res = wp_remote_post("https://discord.com/api/v10/channels/{$ch_id}/messages", [ + 'timeout' => 6, + 'headers' => ['Authorization' => 'Bot ' . $token, 'Content-Type' => 'application/json'], + 'body' => json_encode(['content' => $message]), + ]); + return ( ! is_wp_error($msg_res) && wp_remote_retrieve_response_code($msg_res) === 200 ); + } + } add_action( 'init', [ 'WBF_Ajax', 'init' ] ); \ No newline at end of file diff --git a/includes/class-forum-bbcode.php b/includes/class-forum-bbcode.php index acdaa9b..4e62be7 100644 --- a/includes/class-forum-bbcode.php +++ b/includes/class-forum-bbcode.php @@ -123,12 +123,23 @@ class WBF_BBCode { $s ); - // [size=small|large|xlarge] + // [size=small|large|xlarge] oder [size=1–7] (klassisches BBCode) $s = preg_replace_callback( - '/\[size=(small|large|xlarge)\](.*?)\[\/size\]/is', + '/\[size=([a-zA-Z0-9]+)\](.*?)\[\/size\]/is', function ( $m ) { - $map = [ 'small' => '.8em', 'large' => '1.2em', 'xlarge' => '1.5em' ]; - return '' . $m[2] . ''; + $val = strtolower( $m[1] ); + // Benannte Größen + $named = [ 'small' => '.8em', 'large' => '1.2em', 'xlarge' => '1.5em' ]; + if ( isset( $named[ $val ] ) ) { + $size = $named[ $val ]; + // Numerische Größen 1–7 (klassisches BBCode-Schema) + } elseif ( ctype_digit( $val ) && (int)$val >= 1 && (int)$val <= 7 ) { + $num_map = [ 1 => '.7em', 2 => '.85em', 3 => '1em', 4 => '1.2em', 5 => '1.4em', 6 => '1.6em', 7 => '2em' ]; + $size = $num_map[ (int)$val ]; + } else { + return $m[2]; // Unbekannter Wert → nur Text + } + return '' . $m[2] . ''; }, $s ); diff --git a/includes/class-forum-db.php b/includes/class-forum-db.php index 65100ab..c9d128a 100644 --- a/includes/class-forum-db.php +++ b/includes/class-forum-db.php @@ -174,6 +174,8 @@ class WBF_DB { // Zeitlich begrenzte Sperren self::maybe_add_column("{$wpdb->prefix}forum_users", 'ban_until', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN ban_until DATETIME DEFAULT NULL"); self::maybe_add_column("{$wpdb->prefix}forum_users", 'pre_ban_role', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN pre_ban_role VARCHAR(20) DEFAULT 'member'"); + // Profilbanner + self::maybe_add_column("{$wpdb->prefix}forum_users", 'banner_url', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN banner_url VARCHAR(255) DEFAULT ''"); // Thread-Abonnements $sql_subscriptions = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_subscriptions ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, @@ -354,6 +356,67 @@ class WBF_DB { public static function update_user( $id, $data ) { global $wpdb; $wpdb->update("{$wpdb->prefix}forum_users", $data, ['id' => $id]); + + // --- Discord-Rollen-Sync nach Rollenänderung --- + if (isset($data['role'])) { + // Discord-User-ID holen + $discord_user_id = $wpdb->get_var($wpdb->prepare( + "SELECT meta_value FROM {$wpdb->prefix}forum_user_meta WHERE user_id = %d AND meta_key = 'discord_user_id'", + $id + )); + if ($discord_user_id) { + // Einstellungen laden + $s = function_exists('wbf_get_settings') ? wbf_get_settings() : []; + $token = trim($s['discord_bot_token'] ?? ''); + $guild = trim($s['discord_guild_id'] ?? ''); + $role_map = json_decode($s['discord_role_map'] ?? '{}', true) ?: []; + if ($token && $guild && !empty($role_map)) { + // Ziel-Discord-Rolle anhand Mapping finden + $target_discord_role = null; + foreach ($role_map as $dc_role_id => $forum_role) { + if ($forum_role === $data['role']) { + $target_discord_role = (string)$dc_role_id; + break; + } + } + if ($target_discord_role) { + // Aktuelle Rollen des Users abrufen + $res = wp_remote_get("https://discord.com/api/v10/guilds/{$guild}/members/{$discord_user_id}", [ + 'timeout' => 6, + 'headers' => ['Authorization' => 'Bot ' . $token], + ]); + if (!is_wp_error($res) && wp_remote_retrieve_response_code($res) === 200) { + $member = json_decode(wp_remote_retrieve_body($res), true); + $user_roles = $member['roles'] ?? []; + // Alle gemappten Discord-Rollen entfernen, außer Zielrolle + $remove_roles = []; + foreach ($role_map as $dc_role_id => $forum_role) { + if ((string)$dc_role_id !== $target_discord_role && in_array((string)$dc_role_id, $user_roles, true)) { + $remove_roles[] = (string)$dc_role_id; + } + } + // Zielrolle hinzufügen, falls nicht vorhanden + if (!in_array($target_discord_role, $user_roles, true)) { + $user_roles[] = $target_discord_role; + } + // Entfernte Rollen rausnehmen + $user_roles = array_values(array_diff($user_roles, $remove_roles)); + // PATCH an Discord senden + $body = json_encode(['roles' => array_values($user_roles)]); + wp_remote_request("https://discord.com/api/v10/guilds/{$guild}/members/{$discord_user_id}", [ + 'method' => 'PATCH', + 'timeout' => 6, + 'headers' => [ + 'Authorization' => 'Bot ' . $token, + 'Content-Type' => 'application/json', + ], + 'body' => $body, + ]); + } + } + } + } + } } public static function get_all_users( $limit = 100, $offset = 0 ) { diff --git a/includes/class-forum-export.php b/includes/class-forum-export.php index 7d789a3..a8cad38 100644 --- a/includes/class-forum-export.php +++ b/includes/class-forum-export.php @@ -130,7 +130,7 @@ class WBF_Export { case 'users': $data['users'] = $wpdb->get_results( - "SELECT id, username, email, password, display_name, avatar_url, + "SELECT id, username, email, password, display_name, avatar_url, banner_url, bio, signature, role, pre_ban_role, ban_reason, ban_until, post_count, registered, last_active, profile_public, reset_token, reset_token_expires diff --git a/includes/class-forum-roles.php b/includes/class-forum-roles.php index 3d74c93..d4abadb 100644 --- a/includes/class-forum-roles.php +++ b/includes/class-forum-roles.php @@ -192,20 +192,37 @@ class WBF_Roles { ]; } - /** Ist der aktuelle WP-User der Seiteninhaber (Superadmin)? */ - public static function is_wp_superadmin() { - return current_user_can('administrator') || (is_multisite() && is_super_admin()); + /** + * Gibt die WP-User-ID des echten Superadmins zurück. + * Das ist immer der bei der WordPress-Installation angelegte erste Nutzer (ID 1). + * Alle anderen WP-Administratoren sind KEINE Forum-Superadmins. + */ + public static function get_wp_superadmin_id() { + // Primär: gespeicherte ID aus den Plugin-Einstellungen (falls manuell überschrieben) + $saved_id = (int) get_option( 'wbf_superadmin_wp_id', 0 ); + if ( $saved_id > 0 ) return $saved_id; + // Fallback: WP-Installations-User (ID 1) + return 1; } - /** Superadmin-Status erzwingen: Forum-User des WP-Admins immer auf superadmin setzen */ + /** Ist der aktuelle eingeloggte WP-User der echte Superadmin (nur ID 1 bzw. gespeicherte ID)? */ + public static function is_wp_superadmin() { + if ( ! is_user_logged_in() ) return false; + return get_current_user_id() === self::get_wp_superadmin_id(); + } + + /** + * Superadmin-Status erzwingen — aber NUR für den einen echten WP-Superadmin (ID 1). + * Alle anderen WP-Admins können normale Forum-Rollen haben und behalten diese auch. + */ public static function sync_superadmin() { if ( ! is_user_logged_in() ) return; - if ( ! self::is_wp_superadmin() ) return; + if ( ! self::is_wp_superadmin() ) return; // nur ID 1 kommt durch - $wp_user = wp_get_current_user(); - $forum_user = WBF_DB::get_user_by('email', $wp_user->user_email); + $wp_user = wp_get_current_user(); + $forum_user = WBF_DB::get_user_by( 'email', $wp_user->user_email ); if ( $forum_user && $forum_user->role !== self::SUPERADMIN ) { - WBF_DB::update_user($forum_user->id, ['role' => self::SUPERADMIN]); + WBF_DB::update_user( $forum_user->id, [ 'role' => self::SUPERADMIN ] ); } } } \ No newline at end of file diff --git a/includes/class-forum-shortcodes.php b/includes/class-forum-shortcodes.php index b0c6808..94b277e 100644 --- a/includes/class-forum-shortcodes.php +++ b/includes/class-forum-shortcodes.php @@ -707,7 +707,7 @@ class WBF_Shortcodes { signature)): ?> -
signature)); ?>
+
signature); ?>
@@ -844,7 +844,7 @@ class WBF_Shortcodes {
signature)): ?> -
signature)); ?>
+
signature); ?>
@@ -994,13 +994,13 @@ class WBF_Shortcodes { bio)): ?>
-

bio)); ?>

+

bio); ?>

signature)): ?>
-

signature)); ?>

+

signature); ?>

@@ -1113,10 +1113,13 @@ class WBF_Shortcodes { class="wbf-profile-tab"> Sicherheit - + - Minecraft + Verbindungen
@@ -1139,10 +1142,12 @@ class WBF_Shortcodes {
+
+
signature??''); ?>/300
@@ -1535,12 +1540,118 @@ class WBF_Shortcodes { - + + +
+
+ Verbundene Dienste +
+
+ + +
+
+ +
+
+ Minecraft +
+
+ +
+
+ + + id ); + $discord_current = trim( $discord_meta['discord_username'] ?? '' ); + $discord_connected = $discord_current !== ''; + ?> + +
+
+ +
+
+ Discord + + + Verbunden + + + + Nicht verbunden + + +
+ +
+ + + +
+ + + + +
+
+ + +
+
+ + + + + + +

+ Verknüpfe deinen Discord-Account mit deinem Profil. + + Ein Bestätigungs-Code wird dir per Discord-DM zugeschickt. + + (Bot noch nicht konfiguriert – wende dich an einen Admin.) + +

+
+
+ +
+ + +
+
+ +
'; + // }, 10, 2); + echo apply_filters('wbf_profile_connections', '', $profile); + ?> + +
+ + + @@ -1552,6 +1663,58 @@ class WBF_Shortcodes { // ── TAG PAGE ───────────────────────────────────────────────────────────── + // ── Discord Verifikations-Formular (3-Schritt) ──────────────────────────── + + private static function render_discord_form( $bot_configured ) { ?> + +

+ + Discord-Bot noch nicht eingerichtet. Bitte Admin kontaktieren. +

+ + +
+ +
+ + +
+

+ + Du musst Mitglied unseres Discord-Servers sein und DMs erlauben. +

+
+ + + + + + + role) >= 80): // Nur Admins/Mods ?> +
+ + +
+