Update from Git Manager GUI

This commit is contained in:
2026-03-29 22:25:37 +02:00
parent 43fcc6cb95
commit 4a593677dd
6 changed files with 579 additions and 25 deletions

View File

@@ -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' ] );