Update from Git Manager GUI
This commit is contained in:
@@ -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' ] );
|
||||
@@ -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 '<span style="font-size:' . $map[$m[1]] . '">' . $m[2] . '</span>';
|
||||
$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 '<span style="font-size:' . $size . '">' . $m[2] . '</span>';
|
||||
},
|
||||
$s
|
||||
);
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -707,7 +707,7 @@ class WBF_Shortcodes {
|
||||
</div>
|
||||
</div>
|
||||
<?php if (!empty($thread->signature)): ?>
|
||||
<div class="wbf-signature"><div class="wbf-signature__divider"></div><?php echo nl2br(esc_html($thread->signature)); ?></div>
|
||||
<div class="wbf-signature"><div class="wbf-signature__divider"></div><?php echo WBF_BBCode::render($thread->signature); ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="wbf-post__footer">
|
||||
<span class="wbf-post__date"><?php echo self::time_ago($thread->created_at); ?></span>
|
||||
@@ -844,7 +844,7 @@ class WBF_Shortcodes {
|
||||
</div>
|
||||
</div>
|
||||
<?php if (!empty($post->signature)): ?>
|
||||
<div class="wbf-signature"><div class="wbf-signature__divider"></div><?php echo nl2br(esc_html($post->signature)); ?></div>
|
||||
<div class="wbf-signature"><div class="wbf-signature__divider"></div><?php echo WBF_BBCode::render($post->signature); ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="wbf-post__footer">
|
||||
<span class="wbf-post__date"><?php echo self::time_ago($post->created_at); ?></span>
|
||||
@@ -994,13 +994,13 @@ class WBF_Shortcodes {
|
||||
<?php if (!empty($profile->bio)): ?>
|
||||
<div class="wbf-profile-sidebar__section">
|
||||
<span class="wbf-profile-sidebar__section-label"><i class="fas fa-align-left"></i> Bio</span>
|
||||
<p><?php echo nl2br(esc_html($profile->bio)); ?></p>
|
||||
<p><?php echo WBF_BBCode::render($profile->bio); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($profile->signature)): ?>
|
||||
<div class="wbf-profile-sidebar__section">
|
||||
<span class="wbf-profile-sidebar__section-label"><i class="fas fa-pen-nib"></i> Signatur</span>
|
||||
<p class="wbf-profile-sidebar__sig"><?php echo nl2br(esc_html($profile->signature)); ?></p>
|
||||
<p class="wbf-profile-sidebar__sig"><?php echo WBF_BBCode::render($profile->signature); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!-- Öffentliche Custom Fields — nach Kategorie gruppiert -->
|
||||
@@ -1113,10 +1113,13 @@ class WBF_Shortcodes {
|
||||
class="wbf-profile-tab<?php echo $active_tab===4?' active':''; ?>">
|
||||
<i class="fas fa-lock"></i> Sicherheit
|
||||
</a>
|
||||
<?php if ( class_exists('MC_Gallery_Forum_Bridge') ) : ?>
|
||||
<?php
|
||||
// „Verbindungen" Tab — immer sichtbar (Discord eingebaut, MC optional)
|
||||
$wbf_has_connections = true;
|
||||
if ( $wbf_has_connections ) : ?>
|
||||
<a href="?forum_profile=<?php echo (int)$profile->id; ?>&ptab=mc"
|
||||
class="wbf-profile-tab<?php echo $active_tab==='mc'?' active':''; ?>">
|
||||
<i class="fas fa-cubes"></i> Minecraft
|
||||
<i class="fas fa-plug"></i> Verbindungen
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -1139,10 +1142,12 @@ class WBF_Shortcodes {
|
||||
</div>
|
||||
<div class="wbf-form-row">
|
||||
<label>Bio</label>
|
||||
<?php self::render_editor_toolbar('wbfEditBio'); ?>
|
||||
<textarea id="wbfEditBio" rows="2"><?php echo esc_textarea($profile->bio); ?></textarea>
|
||||
</div>
|
||||
<div class="wbf-form-row">
|
||||
<label>Signatur <small>(max. 300 Zeichen)</small></label>
|
||||
<?php self::render_editor_toolbar('wbfEditSignature'); ?>
|
||||
<textarea id="wbfEditSignature" rows="2" maxlength="300" placeholder="Deine Signatur…"><?php echo esc_textarea($profile->signature ?? ''); ?></textarea>
|
||||
<div class="wbf-sig-counter"><span id="wbfSigCount"><?php echo mb_strlen($profile->signature??''); ?></span>/300</div>
|
||||
</div>
|
||||
@@ -1535,12 +1540,118 @@ class WBF_Shortcodes {
|
||||
<?php endif; /* end Tab 4 */ ?>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════
|
||||
TAB MC — Minecraft-Konto verknüpfen (Bridge)
|
||||
Wird nur gerendert wenn MC Gallery Forum Bridge aktiv ist.
|
||||
TAB MC — Verbindungen (Externe Dienste verknüpfen)
|
||||
Wird nur gerendert wenn mind. eine Integration aktiv ist.
|
||||
Neue Integrationen: einfach weiteres .wbf-connection-card-Block
|
||||
via apply_filters('wbf_profile_connections', ...) hinzufügen.
|
||||
══════════════════════════════════════════════════ -->
|
||||
<?php if ( $is_own && $active_tab === 'mc' && class_exists('MC_Gallery_Forum_Bridge') ) :
|
||||
echo apply_filters('wbf_profile_tab_content', '', 'minecraft', $profile);
|
||||
endif; /* end Tab MC */ ?>
|
||||
<?php if ( $is_own && $active_tab === 'mc' ) : ?>
|
||||
|
||||
<div class="wbf-profile-card">
|
||||
<div class="wbf-profile-card__header">
|
||||
<i class="fas fa-plug"></i> Verbundene Dienste
|
||||
</div>
|
||||
<div class="wbf-profile-card__body" style="padding:0">
|
||||
|
||||
<?php if ( class_exists('MC_Gallery_Forum_Bridge') ) :
|
||||
$mc_content = apply_filters('wbf_profile_tab_content', '', 'minecraft', $profile);
|
||||
?>
|
||||
<div class="wbf-connection-card">
|
||||
<div class="wbf-connection-card__icon" style="background:rgba(101,163,13,.15);border-color:rgba(101,163,13,.3)">
|
||||
<i class="fas fa-cubes" style="color:#65a30d"></i>
|
||||
</div>
|
||||
<div class="wbf-connection-card__head">
|
||||
<span class="wbf-connection-card__title">Minecraft</span>
|
||||
</div>
|
||||
<div class="wbf-connection-card__content">
|
||||
<?php echo $mc_content; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
// ── Discord-Card (eingebaut, kein extra Plugin nötig) ──────────────
|
||||
$discord_meta = WBF_DB::get_user_meta( $profile->id );
|
||||
$discord_current = trim( $discord_meta['discord_username'] ?? '' );
|
||||
$discord_connected = $discord_current !== '';
|
||||
?>
|
||||
<?php
|
||||
$s = wbf_get_settings();
|
||||
$discord_bot_configured = ! empty( trim( $s['discord_bot_token'] ?? '' ) );
|
||||
?>
|
||||
<div class="wbf-connection-card wbf-connection-card--discord">
|
||||
<div class="wbf-connection-card__icon">
|
||||
<i class="fab fa-discord"></i>
|
||||
</div>
|
||||
<div class="wbf-connection-card__head">
|
||||
<span class="wbf-connection-card__title">Discord</span>
|
||||
<?php if ( $discord_connected ) : ?>
|
||||
<span class="wbf-connection-badge wbf-connection-badge--connected">
|
||||
<i class="fas fa-check-circle"></i> Verbunden
|
||||
</span>
|
||||
<?php else : ?>
|
||||
<span class="wbf-connection-badge wbf-connection-badge--disconnected">
|
||||
<i class="fas fa-circle-xmark"></i> Nicht verbunden
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="wbf-connection-card__content">
|
||||
|
||||
<?php if ( $discord_connected ) : ?>
|
||||
<!-- ── Bereits verbunden ── -->
|
||||
<div class="wbf-discord-connected-info">
|
||||
<span class="wbf-discord-linked-name">
|
||||
<i class="fab fa-discord" style="color:#5865f2"></i>
|
||||
<?php echo esc_html( $discord_current ); ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="wbf-connect-row" style="margin-top:.75rem">
|
||||
<button type="button" class="wbf-btn wbf-btn--primary" id="wbf-discord-relink">
|
||||
<i class="fas fa-rotate"></i> Neu verknüpfen
|
||||
</button>
|
||||
<button type="button" class="wbf-btn wbf-btn--ghost" id="wbf-discord-disconnect">
|
||||
<i class="fas fa-unlink"></i> Trennen
|
||||
</button>
|
||||
</div>
|
||||
<div id="wbf-discord-msg" style="margin-top:.5rem;font-size:.82rem"></div>
|
||||
|
||||
<!-- Formular (standardmäßig ausgeblendet, bei "Neu verknüpfen" sichtbar) -->
|
||||
<div id="wbf-discord-form" style="display:none;margin-top:1rem">
|
||||
<?php self::render_discord_form( $discord_bot_configured ); ?>
|
||||
</div>
|
||||
|
||||
<?php else : ?>
|
||||
<!-- ── Noch nicht verbunden ── -->
|
||||
<p class="wbf-connection-card__desc">
|
||||
Verknüpfe deinen Discord-Account mit deinem Profil.
|
||||
<?php if ( $discord_bot_configured ) : ?>
|
||||
Ein Bestätigungs-Code wird dir per Discord-DM zugeschickt.
|
||||
<?php else : ?>
|
||||
<em style="color:var(--c-muted)">(Bot noch nicht konfiguriert – wende dich an einen Admin.)</em>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<div id="wbf-discord-msg" style="margin-top:.3rem;font-size:.82rem"></div>
|
||||
<div id="wbf-discord-form">
|
||||
<?php self::render_discord_form( $discord_bot_configured ); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
// Hook für weitere Verbindungen (z.B. Steam, Twitch, …)
|
||||
// Nutzung: add_filter('wbf_profile_connections', function($html, $profile) {
|
||||
// return $html . '<div class="wbf-connection-card">…</div>';
|
||||
// }, 10, 2);
|
||||
echo apply_filters('wbf_profile_connections', '', $profile);
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endif; /* end Tab MC */ ?>
|
||||
|
||||
</div><!-- /.wbf-profile-main -->
|
||||
</div><!-- /.wbf-profile-layout -->
|
||||
@@ -1552,6 +1663,58 @@ class WBF_Shortcodes {
|
||||
|
||||
// ── TAG PAGE ─────────────────────────────────────────────────────────────
|
||||
|
||||
// ── Discord Verifikations-Formular (3-Schritt) ────────────────────────────
|
||||
|
||||
private static function render_discord_form( $bot_configured ) { ?>
|
||||
<?php if ( ! $bot_configured ) : ?>
|
||||
<p style="color:var(--c-muted);font-size:.83rem;margin:0">
|
||||
<i class="fas fa-triangle-exclamation"></i>
|
||||
Discord-Bot noch nicht eingerichtet. Bitte Admin kontaktieren.
|
||||
</p>
|
||||
<?php else : ?>
|
||||
<!-- Schritt 1: Benutzername eingeben -->
|
||||
<div id="wbf-dc-step1">
|
||||
<label>DISCORD-BENUTZERNAME</label>
|
||||
<div class="wbf-connect-row">
|
||||
<input type="text"
|
||||
id="wbf-discord-input"
|
||||
placeholder="z. B. MvViper"
|
||||
maxlength="40"
|
||||
autocomplete="off">
|
||||
<button type="button" class="wbf-btn wbf-btn--primary" id="wbf-discord-send-code">
|
||||
<i class="fab fa-discord"></i> Code senden
|
||||
</button>
|
||||
</div>
|
||||
<p style="font-size:.78rem;color:var(--c-muted);margin:.45rem 0 0">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
Du musst Mitglied unseres Discord-Servers sein und DMs erlauben.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Schritt 2: Code eingeben (zunächst ausgeblendet) -->
|
||||
<div id="wbf-dc-step2" style="display:none;margin-top:.9rem">
|
||||
<label>BESTÄTIGUNGS-CODE (aus Discord-DM)</label>
|
||||
<div class="wbf-connect-row">
|
||||
<input type="text"
|
||||
id="wbf-discord-code-input"
|
||||
placeholder="A1B2C3"
|
||||
maxlength="6"
|
||||
autocomplete="off"
|
||||
style="font-family:monospace;letter-spacing:.15em;text-transform:uppercase;max-width:140px">
|
||||
<button type="button" class="wbf-btn wbf-btn--primary" id="wbf-discord-verify">
|
||||
<i class="fas fa-check"></i> Bestätigen
|
||||
</button>
|
||||
<button type="button" class="wbf-btn wbf-btn--ghost" id="wbf-discord-code-back">
|
||||
<i class="fas fa-arrow-left"></i> Zurück
|
||||
</button>
|
||||
</div>
|
||||
<p style="font-size:.78rem;color:var(--c-muted);margin:.45rem 0 0">
|
||||
<i class="fas fa-clock"></i> Code ist 10 Minuten gültig.
|
||||
</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php }
|
||||
|
||||
private static function view_tag() {
|
||||
$slug = sanitize_title( $_GET['forum_tag'] ?? '' );
|
||||
$tag = WBF_DB::get_tag($slug);
|
||||
@@ -2416,7 +2579,16 @@ class WBF_Shortcodes {
|
||||
</div>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php if (WBF_Roles::level($current->role) >= 80): // Nur Admins/Mods ?>
|
||||
<div style="margin-top:2.5rem;text-align:center">
|
||||
<button class="wbf-btn wbf-btn--primary" id="wbf-discord-sync-btn" type="button">
|
||||
<i class="fab fa-discord"></i> Discord-Rollen-Sync manuell anstoßen
|
||||
</button>
|
||||
<span id="wbf-discord-sync-msg" style="margin-left:1rem;font-size:.95em;display:none"></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (empty($members)): ?>
|
||||
<div class="wbf-empty" style="grid-column:1/-1">
|
||||
<i class="fas fa-users-slash"></i>
|
||||
|
||||
Reference in New Issue
Block a user