Update from Git Manager GUI
This commit is contained in:
@@ -30,6 +30,7 @@ add_action( 'admin_menu', function() {
|
|||||||
add_submenu_page( 'wbf-admin', 'Thread-Präfixe','Thread-Präfixe','manage_options', 'wbf-prefixes', 'wbf_admin_prefixes' );
|
add_submenu_page( 'wbf-admin', 'Thread-Präfixe','Thread-Präfixe','manage_options', 'wbf-prefixes', 'wbf_admin_prefixes' );
|
||||||
add_submenu_page( 'wbf-admin', 'Wortfilter', 'Wortfilter', 'manage_options', 'wbf-wordfilter', 'wbf_admin_wordfilter' );
|
add_submenu_page( 'wbf-admin', 'Wortfilter', 'Wortfilter', 'manage_options', 'wbf-wordfilter', 'wbf_admin_wordfilter' );
|
||||||
add_submenu_page( 'wbf-admin', 'Export / Import','Export / Import','manage_options', 'wbf-export', 'wbf_admin_export' );
|
add_submenu_page( 'wbf-admin', 'Export / Import','Export / Import','manage_options', 'wbf-export', 'wbf_admin_export' );
|
||||||
|
add_submenu_page( 'wbf-admin', '🎮 Discord', '🎮 Discord', 'manage_options', 'wbf-discord', 'wbf_admin_discord' );
|
||||||
add_submenu_page( 'wbf-admin', '⚠️ Deinstallieren', '⚠️ Deinstallieren', 'manage_options', 'wbf-uninstall', 'wbf_admin_uninstall' );
|
add_submenu_page( 'wbf-admin', '⚠️ Deinstallieren', '⚠️ Deinstallieren', 'manage_options', 'wbf-uninstall', 'wbf_admin_uninstall' );
|
||||||
add_submenu_page( 'wbf-admin', '🔔 Updates', '🔔 Updates', 'manage_options', 'wbf-updates', 'wbf_admin_updates' );
|
add_submenu_page( 'wbf-admin', '🔔 Updates', '🔔 Updates', 'manage_options', 'wbf-updates', 'wbf_admin_updates' );
|
||||||
}, 10 );
|
}, 10 );
|
||||||
@@ -1346,14 +1347,46 @@ function wbf_admin_members() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$members = WBF_DB::get_all_users( 200 );
|
$members = WBF_DB::get_all_users( 200 );
|
||||||
|
$s_discord = wbf_get_settings();
|
||||||
|
$dc_sync_on = ( $s_discord['discord_role_sync'] ?? '0' ) === '1' && trim( $s_discord['discord_bot_token'] ?? '' );
|
||||||
|
|
||||||
|
// Discord-Meta aller User vorladen (1 Query statt N)
|
||||||
|
$dc_meta = [];
|
||||||
|
if ( $dc_sync_on ) {
|
||||||
|
global $wpdb;
|
||||||
|
$rows = $wpdb->get_results(
|
||||||
|
"SELECT user_id,
|
||||||
|
MAX(CASE WHEN meta_key='discord_user_id' THEN meta_value END) AS discord_uid,
|
||||||
|
MAX(CASE WHEN meta_key='discord_username' THEN meta_value END) AS discord_name
|
||||||
|
FROM {$wpdb->prefix}forum_user_meta
|
||||||
|
WHERE meta_key IN ('discord_user_id','discord_username')
|
||||||
|
GROUP BY user_id"
|
||||||
|
);
|
||||||
|
foreach ( $rows as $r ) {
|
||||||
|
$dc_meta[ (int)$r->user_id ] = $r;
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<div class="wrap">
|
<div class="wrap">
|
||||||
<h1 style="display:flex;align-items:center;justify-content:space-between">
|
<h1 style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px">
|
||||||
<span>Mitglieder</span>
|
<span>Mitglieder</span>
|
||||||
<button type="button" class="button button-primary" onclick="document.getElementById('wbf-create-user-box').style.display=document.getElementById('wbf-create-user-box').style.display==='none'?'block':'none'">
|
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
|
||||||
+ Neuen Nutzer anlegen
|
<?php if ( $dc_sync_on ): ?>
|
||||||
</button>
|
<button type="button" id="wbf-discord-sync-all-btn" class="button"
|
||||||
|
style="background:#5865f2;color:#fff;border-color:#4752c4;display:inline-flex;align-items:center;gap:5px"
|
||||||
|
title="Synchronisiert Discord-Rollen aller verknüpften Nutzer (Discord → Forum)">
|
||||||
|
<span class="dashicons dashicons-update" id="wbf-sync-icon" style="margin-top:3px"></span>
|
||||||
|
Discord-Rollen synchronisieren
|
||||||
|
</button>
|
||||||
|
<span id="wbf-discord-sync-result" style="font-weight:600;font-size:.85rem"></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<button type="button" class="button button-primary"
|
||||||
|
onclick="document.getElementById('wbf-create-user-box').style.display=
|
||||||
|
document.getElementById('wbf-create-user-box').style.display==='none'?'block':'none'">
|
||||||
|
+ Neuen Nutzer anlegen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<!-- Neuen Nutzer anlegen -->
|
<!-- Neuen Nutzer anlegen -->
|
||||||
@@ -1409,6 +1442,7 @@ function wbf_admin_members() {
|
|||||||
<th>#</th><th>Nutzer</th><th>E-Mail</th>
|
<th>#</th><th>Nutzer</th><th>E-Mail</th>
|
||||||
<th>Aktuelle Rolle</th><th>Beiträge</th>
|
<th>Aktuelle Rolle</th><th>Beiträge</th>
|
||||||
<th>Registriert</th><th>Rolle ändern</th>
|
<th>Registriert</th><th>Rolle ändern</th>
|
||||||
|
<?php if ( $dc_sync_on ): ?><th style="color:#5865f2"><i class="fab fa-discord"></i> Discord</th><?php endif; ?>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -1417,9 +1451,17 @@ function wbf_admin_members() {
|
|||||||
$color = esc_attr( $role['color'] );
|
$color = esc_attr( $role['color'] );
|
||||||
$bg = esc_attr( $role['bg_color'] );
|
$bg = esc_attr( $role['bg_color'] );
|
||||||
$icon = esc_attr( $role['icon'] ?? 'fas fa-user' );
|
$icon = esc_attr( $role['icon'] ?? 'fas fa-user' );
|
||||||
$is_sa = ( $m->role === WBF_Roles::SUPERADMIN );
|
// Nur sperren wenn dieser Forum-User wirklich dem WP-Superadmin (ID 1) entspricht.
|
||||||
|
// Reine Rollen-Prüfung reicht nicht — sonst kann man versehentlich
|
||||||
|
// zugewiesene superadmin-Rollen nicht mehr korrigieren.
|
||||||
|
$wp_sa_data = get_userdata( WBF_Roles::get_wp_superadmin_id() );
|
||||||
|
$is_sa = ( $m->role === WBF_Roles::SUPERADMIN )
|
||||||
|
&& $wp_sa_data
|
||||||
|
&& ( strtolower($m->email) === strtolower($wp_sa_data->user_email) );
|
||||||
$ban_reason = esc_attr( $m->ban_reason ?? '' );
|
$ban_reason = esc_attr( $m->ban_reason ?? '' );
|
||||||
$opts = '';
|
$opts = '';
|
||||||
|
$dc_user = $dc_meta[ (int)$m->id ] ?? null;
|
||||||
|
$has_dc = $dc_sync_on && $dc_user && ! empty( $dc_user->discord_uid );
|
||||||
foreach ( $roles as $k => $r ) {
|
foreach ( $roles as $k => $r ) {
|
||||||
if ( $k === WBF_Roles::SUPERADMIN ) continue;
|
if ( $k === WBF_Roles::SUPERADMIN ) continue;
|
||||||
$sel = $m->role === $k ? ' selected' : '';
|
$sel = $m->role === $k ? ' selected' : '';
|
||||||
@@ -1438,13 +1480,13 @@ function wbf_admin_members() {
|
|||||||
<span class="wbf-role-preview" style="color:<?php echo $color; ?>;background:<?php echo $bg; ?>;border-color:<?php echo $color; ?>">
|
<span class="wbf-role-preview" style="color:<?php echo $color; ?>;background:<?php echo $bg; ?>;border-color:<?php echo $color; ?>">
|
||||||
<i class="<?php echo $icon; ?>"></i> <?php echo esc_html( $role['label'] ); ?>
|
<i class="<?php echo $icon; ?>"></i> <?php echo esc_html( $role['label'] ); ?>
|
||||||
</span>
|
</span>
|
||||||
<?php if ( $is_sa ) : ?><em style="color:#999;font-size:.8em">(WP-Admin)</em><?php endif; ?>
|
<?php if ( $is_sa ) : ?><em style="color:#999;font-size:.8em">(Haupt-Admin)</em><?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?php echo esc_html( $m->post_count ); ?></td>
|
<td><?php echo esc_html( $m->post_count ); ?></td>
|
||||||
<td><?php echo esc_html( date( 'd.m.Y', strtotime( $m->registered ) ) ); ?></td>
|
<td><?php echo esc_html( date( 'd.m.Y', strtotime( $m->registered ) ) ); ?></td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ( $is_sa ) : ?>
|
<?php if ( $is_sa ) : ?>
|
||||||
<em style="color:#999">Automatisch (WP-Admin)</em>
|
<em style="color:#999">Gesperrt — Haupt-Superadmin (WP User ID <?php echo (int) WBF_Roles::get_wp_superadmin_id(); ?>)</em>
|
||||||
<?php else : ?>
|
<?php else : ?>
|
||||||
<form method="post" style="display:flex;flex-direction:column;gap:5px">
|
<form method="post" style="display:flex;flex-direction:column;gap:5px">
|
||||||
<?php wp_nonce_field( 'wbf_member_role_nonce' ); ?>
|
<?php wp_nonce_field( 'wbf_member_role_nonce' ); ?>
|
||||||
@@ -1711,11 +1753,127 @@ function wbf_admin_members() {
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<?php if ( $dc_sync_on ) : ?>
|
||||||
|
<td style="white-space:nowrap;min-width:140px;vertical-align:top;padding-top:8px">
|
||||||
|
<?php if ( $has_dc ) : ?>
|
||||||
|
<div style="display:flex;flex-direction:column;gap:5px">
|
||||||
|
<span style="font-size:.8rem;color:#5865f2;font-weight:600">
|
||||||
|
<i class="fab fa-discord"></i>
|
||||||
|
<?php echo esc_html( $dc_user->discord_name ?: $dc_user->discord_uid ); ?>
|
||||||
|
</span>
|
||||||
|
<button type="button"
|
||||||
|
class="button button-small wbf-dc-sync-user"
|
||||||
|
data-uid="<?php echo (int)$m->id; ?>"
|
||||||
|
data-nonce="<?php echo wp_create_nonce('wbf_nonce'); ?>"
|
||||||
|
style="color:#5865f2;border-color:#5865f2;font-size:.75rem;height:24px;line-height:22px">
|
||||||
|
<span class="dashicons dashicons-update" style="font-size:12px;width:12px;height:12px;margin-top:5px"></span>
|
||||||
|
Sync
|
||||||
|
</button>
|
||||||
|
<span class="wbf-dc-user-result" style="font-size:.75rem;font-weight:600"></span>
|
||||||
|
</div>
|
||||||
|
<?php else : ?>
|
||||||
|
<span style="font-size:.78rem;color:#9ca3af">Nicht verknüpft</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if ( $dc_sync_on ) : ?>
|
||||||
|
<style>
|
||||||
|
@keyframes wbf-spin { from { transform:rotate(0deg); } to { transform:rotate(360deg); } }
|
||||||
|
.wbf-spinning { animation: wbf-spin .8s linear infinite; display:inline-block; }
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
var nonce = '<?php echo wp_create_nonce("wbf_nonce"); ?>';
|
||||||
|
|
||||||
|
// ── Bulk-Sync ──────────────────────────────────────────────────────────
|
||||||
|
var allBtn = document.getElementById('wbf-discord-sync-all-btn');
|
||||||
|
var allRes = document.getElementById('wbf-discord-sync-result');
|
||||||
|
var allIcon = document.getElementById('wbf-sync-icon');
|
||||||
|
|
||||||
|
if (allBtn) {
|
||||||
|
allBtn.addEventListener('click', function() {
|
||||||
|
allBtn.disabled = true;
|
||||||
|
if (allIcon) allIcon.classList.add('wbf-spinning');
|
||||||
|
allRes.style.color = '#374151';
|
||||||
|
allRes.textContent = '⏳ Sync läuft…';
|
||||||
|
|
||||||
|
fetch(ajaxurl, {
|
||||||
|
method : 'POST',
|
||||||
|
headers : {'Content-Type':'application/x-www-form-urlencoded'},
|
||||||
|
body : 'action=wbf_manual_discord_sync&nonce=' + nonce
|
||||||
|
})
|
||||||
|
.then(function(r){ return r.json(); })
|
||||||
|
.then(function(d) {
|
||||||
|
allBtn.disabled = false;
|
||||||
|
if (allIcon) allIcon.classList.remove('wbf-spinning');
|
||||||
|
if (d.success) {
|
||||||
|
allRes.style.color = '#16a34a';
|
||||||
|
allRes.textContent = '✅ ' + (d.data.message || 'Fertig!');
|
||||||
|
// Seite neu laden damit neue Rollen sichtbar werden
|
||||||
|
setTimeout(function(){ location.reload(); }, 1800);
|
||||||
|
} else {
|
||||||
|
allRes.style.color = '#dc2626';
|
||||||
|
allRes.textContent = '❌ ' + ((d.data && d.data.message) || 'Fehler');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
allBtn.disabled = false;
|
||||||
|
if (allIcon) allIcon.classList.remove('wbf-spinning');
|
||||||
|
allRes.style.color = '#dc2626';
|
||||||
|
allRes.textContent = '❌ Netzwerkfehler';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Pro-Nutzer-Sync ───────────────────────────────────────────────────
|
||||||
|
document.querySelectorAll('.wbf-dc-sync-user').forEach(function(btn) {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
var uid = btn.dataset.uid;
|
||||||
|
var icon = btn.querySelector('.dashicons');
|
||||||
|
var result = btn.closest('div').querySelector('.wbf-dc-user-result');
|
||||||
|
|
||||||
|
btn.disabled = true;
|
||||||
|
if (icon) icon.classList.add('wbf-spinning');
|
||||||
|
if (result) { result.style.color='#374151'; result.textContent='⏳'; }
|
||||||
|
|
||||||
|
fetch(ajaxurl, {
|
||||||
|
method : 'POST',
|
||||||
|
headers : {'Content-Type':'application/x-www-form-urlencoded'},
|
||||||
|
body : 'action=wbf_discord_sync_user&nonce=' + nonce + '&user_id=' + uid
|
||||||
|
})
|
||||||
|
.then(function(r){ return r.json(); })
|
||||||
|
.then(function(d) {
|
||||||
|
btn.disabled = false;
|
||||||
|
if (icon) icon.classList.remove('wbf-spinning');
|
||||||
|
if (d.success) {
|
||||||
|
if (result) { result.style.color='#16a34a'; result.textContent='✅ OK'; }
|
||||||
|
// Rollenbadge in dieser Zeile nach 1s aktualisieren (Seitenreload)
|
||||||
|
setTimeout(function(){ location.reload(); }, 1200);
|
||||||
|
} else {
|
||||||
|
if (result) {
|
||||||
|
result.style.color = '#dc2626';
|
||||||
|
result.textContent = '❌ ' + ((d.data && d.data.message) || 'Fehler');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
btn.disabled = false;
|
||||||
|
if (icon) icon.classList.remove('wbf-spinning');
|
||||||
|
if (result) { result.style.color='#dc2626'; result.textContent='❌ Netzwerkfehler'; }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3191,6 +3349,8 @@ function wbf_admin_profile_fields() {
|
|||||||
|
|
||||||
<!-- ── Felder je Kategorie ───────────────────────────────── -->
|
<!-- ── Felder je Kategorie ───────────────────────────────── -->
|
||||||
<?php
|
<?php
|
||||||
|
// Globaler Feld-Index — synchronisiert Checkboxen mit den sequentiellen []‑Arrays
|
||||||
|
$wbf_fidx = 0;
|
||||||
// Alle Kategorien + "Ohne Kategorie" am Ende ausgeben
|
// Alle Kategorien + "Ohne Kategorie" am Ende ausgeben
|
||||||
$all_sections = $cats;
|
$all_sections = $cats;
|
||||||
if ( isset($by_cat['__none__']) ) {
|
if ( isset($by_cat['__none__']) ) {
|
||||||
@@ -3234,7 +3394,8 @@ function wbf_admin_profile_fields() {
|
|||||||
<?php
|
<?php
|
||||||
endif;
|
endif;
|
||||||
foreach ( $c_fields as $i_f => $f ):
|
foreach ( $c_fields as $i_f => $f ):
|
||||||
$fi = 'fi_' . $f['key'];
|
$fi = $wbf_fidx;
|
||||||
|
$wbf_fidx++;
|
||||||
?>
|
?>
|
||||||
<tr class="wbf-field-row" style="background:#fff">
|
<tr class="wbf-field-row" style="background:#fff">
|
||||||
<td style="padding:6px 8px">
|
<td style="padding:6px 8px">
|
||||||
@@ -3268,9 +3429,11 @@ function wbf_admin_profile_fields() {
|
|||||||
<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem;<?php echo ($f['type']??'text')==='select'?'display:none':''; ?>">—</span>
|
<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem;<?php echo ($f['type']??'text')==='select'?'display:none':''; ?>">—</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align:center;padding:6px 8px">
|
<td style="text-align:center;padding:6px 8px">
|
||||||
|
<input type="hidden" name="field_required[<?php echo $fi; ?>]" value="0">
|
||||||
<input type="checkbox" name="field_required[<?php echo $fi; ?>]" value="1" <?php checked($f['required']??0,1); ?>>
|
<input type="checkbox" name="field_required[<?php echo $fi; ?>]" value="1" <?php checked($f['required']??0,1); ?>>
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align:center;padding:6px 8px">
|
<td style="text-align:center;padding:6px 8px">
|
||||||
|
<input type="hidden" name="field_public[<?php echo $fi; ?>]" value="0">
|
||||||
<input type="checkbox" name="field_public[<?php echo $fi; ?>]" value="1" <?php checked($f['public']??1,1); ?>>
|
<input type="checkbox" name="field_public[<?php echo $fi; ?>]" value="1" <?php checked($f['public']??1,1); ?>>
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:6px 8px">
|
<td style="padding:6px 8px">
|
||||||
@@ -3318,7 +3481,7 @@ function wbf_admin_profile_fields() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var wbfRowCount = <?php echo count($fields) + 100; ?>;
|
var wbfRowCount = <?php echo $wbf_fidx; ?>;
|
||||||
|
|
||||||
function wbfRemoveRow(btn) {
|
function wbfRemoveRow(btn) {
|
||||||
var tr = btn.closest('tr');
|
var tr = btn.closest('tr');
|
||||||
@@ -3359,8 +3522,8 @@ function wbf_admin_profile_fields() {
|
|||||||
'<textarea name="field_options[]" rows="2" placeholder="Option 1\nOption 2" style="width:100%;font-size:.78rem;display:none" class="wbf-options-field"></textarea>' +
|
'<textarea name="field_options[]" rows="2" placeholder="Option 1\nOption 2" style="width:100%;font-size:.78rem;display:none" class="wbf-options-field"></textarea>' +
|
||||||
'<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem">—</span>' +
|
'<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem">—</span>' +
|
||||||
'</td>' +
|
'</td>' +
|
||||||
'<td style="text-align:center;padding:6px 8px"><input type="checkbox" name="field_required[new_' + i + ']" value="1"></td>' +
|
'<td style="text-align:center;padding:6px 8px"><input type="hidden" name="field_required[' + i + ']" value="0"><input type="checkbox" name="field_required[' + i + ']" value="1"></td>' +
|
||||||
'<td style="text-align:center;padding:6px 8px"><input type="checkbox" name="field_public[new_' + i + ']" value="1" checked></td>' +
|
'<td style="text-align:center;padding:6px 8px"><input type="hidden" name="field_public[' + i + ']" value="0"><input type="checkbox" name="field_public[' + i + ']" value="1" checked></td>' +
|
||||||
'<td style="padding:6px 8px"><select name="field_category[]" style="width:100%;font-size:.82rem">' + catOpts + '</select></td>' +
|
'<td style="padding:6px 8px"><select name="field_category[]" style="width:100%;font-size:.82rem">' + catOpts + '</select></td>' +
|
||||||
'<td style="padding:6px 8px"><button type="button" class="button" onclick="wbfRemoveRow(this)" style="color:#dc2626;border-color:#fca5a5;padding:2px 7px">✕</button></td>';
|
'<td style="padding:6px 8px"><button type="button" class="button" onclick="wbfRemoveRow(this)" style="color:#dc2626;border-color:#fca5a5;padding:2px 7px">✕</button></td>';
|
||||||
tbody.appendChild(tr);
|
tbody.appendChild(tr);
|
||||||
@@ -3838,3 +4001,231 @@ function wbf_admin_wordfilter() {
|
|||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Discord-Bot-Verbindungstest (Admin AJAX) ──────────────────────────────────
|
||||||
|
add_action('wp_ajax_wbf_discord_test', function() {
|
||||||
|
if ( ! current_user_can('manage_options') ) wp_send_json_error(['message' => 'Keine Berechtigung.']);
|
||||||
|
check_ajax_referer('wbf_discord_test', 'nonce');
|
||||||
|
|
||||||
|
$s = wbf_get_settings();
|
||||||
|
$token = trim($s['discord_bot_token'] ?? '');
|
||||||
|
$guild = trim($s['discord_guild_id'] ?? '');
|
||||||
|
|
||||||
|
if ( ! $token ) {
|
||||||
|
wp_send_json_error(['message' => 'Kein Bot-Token gespeichert.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bot-Info abrufen (@me)
|
||||||
|
$res = wp_remote_get('https://discord.com/api/v10/users/@me', [
|
||||||
|
'timeout' => 8,
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => 'Bot ' . $token,
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ( is_wp_error($res) ) {
|
||||||
|
wp_send_json_error(['message' => 'HTTP-Fehler: ' . $res->get_error_message()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = wp_remote_retrieve_response_code($res);
|
||||||
|
$body = json_decode(wp_remote_retrieve_body($res), true);
|
||||||
|
|
||||||
|
if ( $code !== 200 || empty($body['id']) ) {
|
||||||
|
$err = $body['message'] ?? 'Unbekannter Fehler (HTTP ' . $code . ')';
|
||||||
|
wp_send_json_error(['message' => 'Discord API: ' . $err]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bot_name = ($body['username'] ?? 'Unbekannt') . '#' . ($body['discriminator'] ?? '0');
|
||||||
|
|
||||||
|
// Guild-Prüfung falls Guild-ID angegeben
|
||||||
|
$guild_info = '';
|
||||||
|
if ( $guild ) {
|
||||||
|
$gr = wp_remote_get("https://discord.com/api/v10/guilds/{$guild}", [
|
||||||
|
'timeout' => 6,
|
||||||
|
'headers' => ['Authorization' => 'Bot ' . $token],
|
||||||
|
]);
|
||||||
|
if ( ! is_wp_error($gr) && wp_remote_retrieve_response_code($gr) === 200 ) {
|
||||||
|
$gd = json_decode(wp_remote_retrieve_body($gr), true);
|
||||||
|
$guild_info = ' | Server: ' . ($gd['name'] ?? $guild);
|
||||||
|
} else {
|
||||||
|
$guild_info = ' | ⚠️ Server nicht gefunden oder Bot kein Mitglied';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json_success(['message' => 'Bot: ' . $bot_name . $guild_info]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Discord-Cron: Rollen synchronisieren ──────────────────────────────────────
|
||||||
|
add_action('wbf_discord_role_sync', 'wbf_run_discord_role_sync');
|
||||||
|
|
||||||
|
if ( ! wp_next_scheduled('wbf_discord_role_sync') ) {
|
||||||
|
wp_schedule_event(time(), 'hourly', 'wbf_discord_role_sync');
|
||||||
|
}
|
||||||
|
|
||||||
|
function wbf_run_discord_role_sync() {
|
||||||
|
$s = wbf_get_settings();
|
||||||
|
if ( ($s['discord_role_sync'] ?? '0') !== '1' ) return;
|
||||||
|
$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) ) return;
|
||||||
|
|
||||||
|
global $wpdb;
|
||||||
|
// Alle verifizierten Discord-User holen (discord_user_id in user_meta gesetzt)
|
||||||
|
$rows = $wpdb->get_results(
|
||||||
|
"SELECT um.user_id, um.meta_value AS discord_user_id
|
||||||
|
FROM {$wpdb->prefix}forum_user_meta um
|
||||||
|
WHERE um.meta_key = 'discord_user_id' AND um.meta_value != ''"
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $rows as $row ) {
|
||||||
|
wbf_sync_discord_role_for_user((int)$row->user_id, $row->discord_user_id, $token, $guild, $role_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronisiert die Discord-Serverrolle eines einzelnen Nutzers mit der Forum-Rolle.
|
||||||
|
*/
|
||||||
|
function wbf_sync_discord_role_for_user($forum_user_id, $discord_user_id, $token, $guild, $role_map) {
|
||||||
|
// Guild-Member-Info 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 ) return;
|
||||||
|
|
||||||
|
$member = json_decode(wp_remote_retrieve_body($res), true);
|
||||||
|
$user_roles = $member['roles'] ?? [];
|
||||||
|
|
||||||
|
// Rollen-Map prüfen — erster Treffer gewinnt (Reihenfolge = Priorität)
|
||||||
|
foreach ( $role_map as $dc_role_id => $forum_role ) {
|
||||||
|
if ( in_array((string)$dc_role_id, array_map('strval', $user_roles), true) ) {
|
||||||
|
$forum_user = WBF_DB::get_user($forum_user_id);
|
||||||
|
if ( $forum_user && $forum_user->role !== 'superadmin' && $forum_user->role !== $forum_role ) {
|
||||||
|
WBF_DB::update_user($forum_user_id, ['role' => $forum_role]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ── Discord-Admin-Seite ───────────────────────────────────────────────────────
|
||||||
|
if ( ! function_exists('wbf_admin_discord') ) {
|
||||||
|
function wbf_admin_discord() {
|
||||||
|
if ( ! current_user_can('manage_options') ) return;
|
||||||
|
$s = wbf_get_settings();
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>🎮 Discord-Integration</h1>
|
||||||
|
<p>Konfiguriere den Discord-Bot und die Rollen-Synchronisation.
|
||||||
|
Einstellungen werden in <a href="admin.php?page=wbf-settings">Einstellungen → Discord-Integration</a> gespeichert.</p>
|
||||||
|
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:900px;margin-top:1.5rem">
|
||||||
|
|
||||||
|
<!-- Status-Panel -->
|
||||||
|
<div style="background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px">
|
||||||
|
<h3 style="margin-top:0">🔌 Bot-Status</h3>
|
||||||
|
<?php
|
||||||
|
$token = trim($s['discord_bot_token'] ?? '');
|
||||||
|
$guild = trim($s['discord_guild_id'] ?? '');
|
||||||
|
if (!$token): ?>
|
||||||
|
<p style="color:#dc2626"><i class="dashicons dashicons-warning"></i> Kein Bot-Token konfiguriert.</p>
|
||||||
|
<a href="admin.php?page=wbf-settings#discord" class="button button-primary">Jetzt einrichten</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<p style="color:#16a34a;font-weight:600">✅ Bot-Token gespeichert</p>
|
||||||
|
<p style="color:<?php echo $guild ? '#16a34a' : '#f59e0b'; ?>">
|
||||||
|
<?php echo $guild ? '✅ Guild-ID: <code>' . esc_html($guild) . '</code>' : '⚠️ Keine Guild-ID gesetzt'; ?>
|
||||||
|
</p>
|
||||||
|
<p style="color:<?php echo ($s['discord_role_sync']??'0')==='1' ? '#16a34a' : '#9ca3af'; ?>">
|
||||||
|
Rollen-Sync: <strong><?php echo ($s['discord_role_sync']??'0')==='1' ? 'Aktiv' : 'Deaktiviert'; ?></strong>
|
||||||
|
</p>
|
||||||
|
<button type="button" class="button button-secondary" id="wbf-discord-test-btn2">
|
||||||
|
🔌 Verbindung testen
|
||||||
|
</button>
|
||||||
|
<span id="wbf-discord-test-result2" style="margin-left:10px;font-weight:600"></span>
|
||||||
|
<script>
|
||||||
|
document.getElementById('wbf-discord-test-btn2').addEventListener('click', function(){
|
||||||
|
var btn = this, res = document.getElementById('wbf-discord-test-result2');
|
||||||
|
btn.disabled = true; res.textContent = '⏳ Teste…';
|
||||||
|
fetch(ajaxurl,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},
|
||||||
|
body:'action=wbf_discord_test&nonce=<?php echo wp_create_nonce("wbf_discord_test"); ?>'
|
||||||
|
}).then(r=>r.json()).then(function(d){
|
||||||
|
res.style.color = d.success ? '#16a34a' : '#dc2626';
|
||||||
|
res.textContent = d.success ? '✅ '+(d.data.message||'OK') : '❌ '+((d.data&&d.data.message)||'Fehler');
|
||||||
|
btn.disabled = false;
|
||||||
|
}).catch(function(){ res.style.color='#dc2626'; res.textContent='❌ Netzwerkfehler'; btn.disabled=false; });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Rollen-Map-Übersicht -->
|
||||||
|
<div style="background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px">
|
||||||
|
<h3 style="margin-top:0">🔗 Aktive Rollen-Zuordnungen</h3>
|
||||||
|
<?php
|
||||||
|
$role_map = json_decode($s['discord_role_map'] ?? '{}', true) ?: [];
|
||||||
|
$all_roles = WBF_Roles::get_all();
|
||||||
|
if (empty($role_map)): ?>
|
||||||
|
<p style="color:#9ca3af">Keine Zuordnungen konfiguriert.</p>
|
||||||
|
<a href="admin.php?page=wbf-settings" class="button">Jetzt einrichten</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<table class="widefat striped" style="font-size:.85rem">
|
||||||
|
<thead><tr><th>Discord Rollen-ID</th><th>Forum-Rolle</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($role_map as $dc_id => $fr_key):
|
||||||
|
$fr_label = $all_roles[$fr_key]['label'] ?? $fr_key; ?>
|
||||||
|
<tr>
|
||||||
|
<td><code><?php echo esc_html($dc_id); ?></code></td>
|
||||||
|
<td><?php echo WBF_Roles::badge($fr_key); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<a href="admin.php?page=wbf-settings" class="button" style="margin-top:.75rem">Bearbeiten</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Verknüpfte Discord-Nutzer -->
|
||||||
|
<div style="background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:20px;max-width:900px;margin-top:20px">
|
||||||
|
<h3 style="margin-top:0">👥 Verknüpfte Forum-Nutzer</h3>
|
||||||
|
<?php
|
||||||
|
global $wpdb;
|
||||||
|
$linked = $wpdb->get_results(
|
||||||
|
"SELECT fu.id, fu.username, fu.display_name, fu.role,
|
||||||
|
MAX(CASE WHEN um.meta_key='discord_username' THEN um.meta_value END) AS discord_name,
|
||||||
|
MAX(CASE WHEN um.meta_key='discord_user_id' THEN um.meta_value END) AS discord_uid
|
||||||
|
FROM {$wpdb->prefix}forum_users fu
|
||||||
|
JOIN {$wpdb->prefix}forum_user_meta um ON um.user_id = fu.id
|
||||||
|
WHERE um.meta_key IN ('discord_username','discord_user_id')
|
||||||
|
GROUP BY fu.id
|
||||||
|
HAVING discord_name != '' AND discord_name IS NOT NULL
|
||||||
|
ORDER BY fu.username"
|
||||||
|
);
|
||||||
|
if (empty($linked)): ?>
|
||||||
|
<p style="color:#9ca3af">Noch keine verknüpften Nutzer.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<table class="widefat striped" style="font-size:.85rem">
|
||||||
|
<thead><tr><th>Forum-Nutzer</th><th>Rolle</th><th>Discord-Name</th><th>Discord-ID</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($linked as $u): ?>
|
||||||
|
<tr>
|
||||||
|
<td><strong><?php echo esc_html($u->display_name); ?></strong>
|
||||||
|
<span style="color:#9ca3af"> @<?php echo esc_html($u->username); ?></span></td>
|
||||||
|
<td><?php echo WBF_Roles::badge($u->role); ?></td>
|
||||||
|
<td><i class="fab fa-discord" style="color:#5865f2"></i> <?php echo esc_html($u->discord_name ?: '–'); ?></td>
|
||||||
|
<td><code style="font-size:.78rem"><?php echo esc_html($u->discord_uid ?: '–'); ?></code></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,13 @@ if ( ! function_exists('wbf_get_settings') ) {
|
|||||||
'rules_content' => "**1. Respektvoller Umgang**\nBehandle alle Mitglieder freundlich und respektvoll. Beleidigungen, Mobbing und Diskriminierung sind nicht toleriert.\n\n**2. Keine Spam-Inhalte**\nWerbung, Spam und irrelevante Links sind verboten.\n\n**3. Keine illegalen Inhalte**\nJegliche Inhalte, die gegen geltendes Recht verstoßen, sind streng verboten.\n\n**4. Themenrelevanz**\nBeiträge sollten zur jeweiligen Kategorie passen.\n\n**5. Urheberrecht**\nVeröffentliche keine Inhalte, an denen du keine Rechte besitzt.\n\n**6. Datenschutz**\nTeile keine persönlichen Daten anderer Personen ohne deren Zustimmung.\n\n**7. Moderations-Entscheidungen**\nEntscheidungen der Moderatoren sind zu respektieren. Bei Fragen wende dich direkt ans Team.\n\nVerstöße können zur Verwarnung oder dauerhaften Sperrung führen.",
|
'rules_content' => "**1. Respektvoller Umgang**\nBehandle alle Mitglieder freundlich und respektvoll. Beleidigungen, Mobbing und Diskriminierung sind nicht toleriert.\n\n**2. Keine Spam-Inhalte**\nWerbung, Spam und irrelevante Links sind verboten.\n\n**3. Keine illegalen Inhalte**\nJegliche Inhalte, die gegen geltendes Recht verstoßen, sind streng verboten.\n\n**4. Themenrelevanz**\nBeiträge sollten zur jeweiligen Kategorie passen.\n\n**5. Urheberrecht**\nVeröffentliche keine Inhalte, an denen du keine Rechte besitzt.\n\n**6. Datenschutz**\nTeile keine persönlichen Daten anderer Personen ohne deren Zustimmung.\n\n**7. Moderations-Entscheidungen**\nEntscheidungen der Moderatoren sind zu respektieren. Bei Fragen wende dich direkt ans Team.\n\nVerstöße können zur Verwarnung oder dauerhaften Sperrung führen.",
|
||||||
// Ignore/Block-System: Rollen die nicht geblockt werden können (kommagetrennte Schlüssel)
|
// Ignore/Block-System: Rollen die nicht geblockt werden können (kommagetrennte Schlüssel)
|
||||||
'ignore_blocked_roles' => 'superadmin,admin,moderator',
|
'ignore_blocked_roles' => 'superadmin,admin,moderator',
|
||||||
|
// Discord-Integration
|
||||||
|
'discord_bot_token' => '',
|
||||||
|
'discord_guild_id' => '',
|
||||||
|
'discord_client_id' => '',
|
||||||
|
'discord_client_secret' => '',
|
||||||
|
'discord_role_sync' => '0', // Rollen-Sync aktiviert?
|
||||||
|
'discord_role_map' => '', // JSON: {"discord_role_id":"forum_role_key"}
|
||||||
];
|
];
|
||||||
|
|
||||||
$saved = get_option( 'wbf_settings', [] );
|
$saved = get_option( 'wbf_settings', [] );
|
||||||
@@ -130,6 +137,27 @@ function wbf_admin_settings() {
|
|||||||
// rules_content separat (nicht in $fields, da textarea mit eigener Behandlung)
|
// rules_content separat (nicht in $fields, da textarea mit eigener Behandlung)
|
||||||
$settings['rules_content'] = sanitize_textarea_field( $_POST['rules_content'] ?? '' );
|
$settings['rules_content'] = sanitize_textarea_field( $_POST['rules_content'] ?? '' );
|
||||||
|
|
||||||
|
// Discord-Einstellungen gesondert speichern (sensitiv — niemals in wbf_settings öffentlich)
|
||||||
|
$discord_fields = ['discord_bot_token', 'discord_guild_id', 'discord_client_id', 'discord_client_secret'];
|
||||||
|
foreach ( $discord_fields as $df ) {
|
||||||
|
$settings[$df] = sanitize_text_field( $_POST[$df] ?? '' );
|
||||||
|
}
|
||||||
|
$settings['discord_role_sync'] = isset($_POST['discord_role_sync']) && $_POST['discord_role_sync'] === '1' ? '1' : '0';
|
||||||
|
|
||||||
|
// Discord-Rollen-Map: Array von discord_role_id => forum_role_key
|
||||||
|
$role_map = [];
|
||||||
|
$dc_ids = array_map('sanitize_text_field', (array)($_POST['discord_role_id'] ?? []));
|
||||||
|
$fr_keys = array_map('sanitize_key', (array)($_POST['discord_forum_role'] ?? []));
|
||||||
|
$valid_roles = array_keys(WBF_Roles::get_all());
|
||||||
|
foreach ( $dc_ids as $i => $dc_id ) {
|
||||||
|
$dc_id = trim($dc_id);
|
||||||
|
$fr_key = $fr_keys[$i] ?? '';
|
||||||
|
if ( $dc_id !== '' && in_array($fr_key, $valid_roles, true) ) {
|
||||||
|
$role_map[$dc_id] = $fr_key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$settings['discord_role_map'] = json_encode($role_map);
|
||||||
|
|
||||||
// Checkbox-Felder explizit als '0' speichern wenn nicht angehakt,
|
// Checkbox-Felder explizit als '0' speichern wenn nicht angehakt,
|
||||||
// damit array_filter(...,'strlen') sie nicht wegwirft und der Default '1' greift.
|
// damit array_filter(...,'strlen') sie nicht wegwirft und der Default '1' greift.
|
||||||
$checkbox_fields = ['maintenance_mode', 'rules_enabled', 'rules_accept_required'];
|
$checkbox_fields = ['maintenance_mode', 'rules_enabled', 'rules_accept_required'];
|
||||||
@@ -150,6 +178,12 @@ function wbf_admin_settings() {
|
|||||||
$settings['ignore_blocked_roles'] = implode( ',', $checked_roles );
|
$settings['ignore_blocked_roles'] = implode( ',', $checked_roles );
|
||||||
|
|
||||||
update_option( 'wbf_settings', $settings );
|
update_option( 'wbf_settings', $settings );
|
||||||
|
|
||||||
|
// Superadmin WP-User-ID separat speichern (außerhalb von wbf_settings)
|
||||||
|
$sa_wp_id = (int) ( $_POST['superadmin_wp_id'] ?? 1 );
|
||||||
|
if ( $sa_wp_id < 1 ) $sa_wp_id = 1;
|
||||||
|
update_option( 'wbf_superadmin_wp_id', $sa_wp_id );
|
||||||
|
|
||||||
echo '<div class="notice notice-success is-dismissible"><p>✅ Einstellungen gespeichert!</p></div>';
|
echo '<div class="notice notice-success is-dismissible"><p>✅ Einstellungen gespeichert!</p></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +285,40 @@ function wbf_admin_settings() {
|
|||||||
🔒 Sicherheit
|
🔒 Sicherheit
|
||||||
</h2>
|
</h2>
|
||||||
<table class="form-table" role="presentation">
|
<table class="form-table" role="presentation">
|
||||||
|
|
||||||
|
<!-- ── Superadmin WP-User-ID ─────────────────── -->
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
<label for="wbf_superadmin_wp_id">Superadmin WordPress-User-ID</label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$sa_id = (int) get_option( 'wbf_superadmin_wp_id', 1 );
|
||||||
|
$sa_wpuser = get_userdata( $sa_id );
|
||||||
|
?>
|
||||||
|
<input type="number" id="wbf_superadmin_wp_id" name="superadmin_wp_id"
|
||||||
|
value="<?php echo $sa_id; ?>"
|
||||||
|
min="1" step="1"
|
||||||
|
style="width:80px">
|
||||||
|
<?php if ( $sa_wpuser ) : ?>
|
||||||
|
<span style="margin-left:10px;color:#16a34a;font-weight:600">
|
||||||
|
✅ <?php echo esc_html( $sa_wpuser->display_name ); ?>
|
||||||
|
<<?php echo esc_html( $sa_wpuser->user_email ); ?>>
|
||||||
|
</span>
|
||||||
|
<?php else : ?>
|
||||||
|
<span style="margin-left:10px;color:#dc2626;font-weight:600">
|
||||||
|
⚠️ Kein WordPress-User mit dieser ID gefunden!
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<p class="description">
|
||||||
|
Nur dieser WordPress-User erhält automatisch die Forum-Rolle <strong>Superadmin</strong>
|
||||||
|
und kann sie nicht verlieren. Alle anderen WordPress-Admins können normale Forum-Rollen
|
||||||
|
haben und im Mitglieder-Bereich frei zugewiesen werden.<br>
|
||||||
|
<em>Standard: 1 (erster bei der WP-Installation angelegter User)</em>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">
|
<th scope="row">
|
||||||
<label for="wbf_auto_logout_minutes">Auto-Logout nach Inaktivität</label>
|
<label for="wbf_auto_logout_minutes">Auto-Logout nach Inaktivität</label>
|
||||||
@@ -499,6 +567,170 @@ function wbf_admin_settings() {
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════
|
||||||
|
DISCORD-INTEGRATION
|
||||||
|
══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 style="border-bottom:1px solid #ddd;padding-bottom:.4rem;margin-top:2rem">
|
||||||
|
<span style="color:#5865f2">🎮</span> Discord-Integration
|
||||||
|
</h2>
|
||||||
|
<p class="description" style="margin-bottom:1rem">
|
||||||
|
Bot-Token und Guild-ID findest du im <a href="https://discord.com/developers/applications" target="_blank">Discord Developer Portal</a>.
|
||||||
|
Der Bot muss Mitglied deines Servers sein und die Berechtigung <strong>Direct Messages lesen/senden</strong> sowie
|
||||||
|
<strong>Server-Mitglieder verwalten</strong> besitzen.
|
||||||
|
</p>
|
||||||
|
<table class="form-table" role="presentation">
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="wbf_discord_bot_token">Bot-Token</label></th>
|
||||||
|
<td>
|
||||||
|
<input type="password" id="wbf_discord_bot_token" name="discord_bot_token"
|
||||||
|
value="<?php echo esc_attr($s['discord_bot_token']); ?>"
|
||||||
|
class="regular-text" autocomplete="off" placeholder="Bot-Token aus dem Developer Portal">
|
||||||
|
<p class="description">Niemals öffentlich teilen! Wird verschlüsselt in der Datenbank gespeichert.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="wbf_discord_guild_id">Server-ID (Guild ID)</label></th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="wbf_discord_guild_id" name="discord_guild_id"
|
||||||
|
value="<?php echo esc_attr($s['discord_guild_id']); ?>"
|
||||||
|
class="regular-text" placeholder="z. B. 123456789012345678">
|
||||||
|
<p class="description">Rechtsklick auf deinen Server → ID kopieren (Entwicklermodus muss aktiv sein).</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="wbf_discord_client_id">Client ID (optional)</label></th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="wbf_discord_client_id" name="discord_client_id"
|
||||||
|
value="<?php echo esc_attr($s['discord_client_id']); ?>"
|
||||||
|
class="regular-text" placeholder="Application ID">
|
||||||
|
<p class="description">Für zukünftige OAuth2-Unterstützung. Aktuell optional.</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row"><label for="wbf_discord_client_secret">Client Secret (optional)</label></th>
|
||||||
|
<td>
|
||||||
|
<input type="password" id="wbf_discord_client_secret" name="discord_client_secret"
|
||||||
|
value="<?php echo esc_attr($s['discord_client_secret']); ?>"
|
||||||
|
class="regular-text" autocomplete="off" placeholder="Client Secret">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Rollen-Sync aktivieren</th>
|
||||||
|
<td>
|
||||||
|
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
|
||||||
|
<input type="checkbox" name="discord_role_sync" value="1"
|
||||||
|
<?php checked('1', $s['discord_role_sync'] ?? '0'); ?>>
|
||||||
|
Discord-Serverrollen automatisch auf Forum-Rollen mappen
|
||||||
|
</label>
|
||||||
|
<p class="description">
|
||||||
|
Wenn aktiviert, wird bei jedem Login und stündlich per Cron die Discord-Rolle des Nutzers
|
||||||
|
geprüft und die Forum-Rolle entsprechend der unten definierten Zuordnung aktualisiert.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Discord Rollen-Map -->
|
||||||
|
<h3 style="margin-top:1.5rem">🔗 Discord-Rollen → Forum-Rollen Zuordnung</h3>
|
||||||
|
<p class="description" style="margin-bottom:.75rem">
|
||||||
|
Trage die Discord-Rollen-ID und die gewünschte Forum-Rolle ein.
|
||||||
|
Mehrere Einträge werden der Reihe nach geprüft — der erste Treffer gewinnt.
|
||||||
|
</p>
|
||||||
|
<?php
|
||||||
|
$role_map_raw = $s['discord_role_map'] ?? '{}';
|
||||||
|
$role_map = json_decode($role_map_raw, true) ?: [];
|
||||||
|
$forum_roles = WBF_Roles::get_sorted();
|
||||||
|
// Sicherstellen dass mindestens eine leere Zeile zum Hinzufügen da ist
|
||||||
|
if ( empty($role_map) ) $role_map[''] = '';
|
||||||
|
?>
|
||||||
|
<table class="widefat" id="wbf-discord-role-map" style="max-width:680px;margin-bottom:.75rem">
|
||||||
|
<thead><tr>
|
||||||
|
<th style="width:50%">Discord Rollen-ID</th>
|
||||||
|
<th style="width:40%">Forum-Rolle</th>
|
||||||
|
<th style="width:10%"></th>
|
||||||
|
</tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ( $role_map as $dc_id => $fr_key ) : ?>
|
||||||
|
<tr class="wbf-role-map-row">
|
||||||
|
<td><input type="text" name="discord_role_id[]"
|
||||||
|
value="<?php echo esc_attr($dc_id); ?>"
|
||||||
|
placeholder="Discord Rollen-ID"
|
||||||
|
class="widefat" style="font-family:monospace"></td>
|
||||||
|
<td>
|
||||||
|
<select name="discord_forum_role[]" class="widefat">
|
||||||
|
<option value="">— wählen —</option>
|
||||||
|
<?php foreach ( $forum_roles as $rk => $role ) :
|
||||||
|
if ( $rk === 'superadmin' ) continue; ?>
|
||||||
|
<option value="<?php echo esc_attr($rk); ?>"
|
||||||
|
<?php selected($rk, $fr_key); ?>>
|
||||||
|
<?php echo esc_html($role['label']); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td><button type="button" class="button button-small wbf-rm-role-row"
|
||||||
|
style="color:#c00">✕</button></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button type="button" class="button" id="wbf-add-role-row">+ Zeile hinzufügen</button>
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
document.getElementById('wbf-add-role-row').addEventListener('click', function(){
|
||||||
|
var tbody = document.querySelector('#wbf-discord-role-map tbody');
|
||||||
|
var row = document.querySelector('.wbf-role-map-row').cloneNode(true);
|
||||||
|
row.querySelectorAll('input').forEach(function(i){i.value='';});
|
||||||
|
row.querySelectorAll('select').forEach(function(s){s.selectedIndex=0;});
|
||||||
|
tbody.appendChild(row);
|
||||||
|
});
|
||||||
|
document.addEventListener('click', function(e){
|
||||||
|
if (e.target.classList.contains('wbf-rm-role-row')) {
|
||||||
|
var rows = document.querySelectorAll('.wbf-role-map-row');
|
||||||
|
if (rows.length > 1) e.target.closest('tr').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Test-Verbindung -->
|
||||||
|
<div style="margin-top:1.25rem;padding:1rem;background:#f0f7ff;border:1px solid #c3dafe;border-radius:6px;max-width:680px">
|
||||||
|
<strong>🔌 Verbindungstest</strong><br>
|
||||||
|
<p style="margin:.4rem 0 .75rem;color:#374151;font-size:.9rem">
|
||||||
|
Speichere zuerst die Einstellungen, dann klicke „Testen" um zu prüfen ob der Bot erreichbar ist.
|
||||||
|
</p>
|
||||||
|
<button type="button" class="button button-secondary" id="wbf-discord-test-btn">
|
||||||
|
🔌 Discord-Verbindung testen
|
||||||
|
</button>
|
||||||
|
<span id="wbf-discord-test-result" style="margin-left:10px;font-weight:600"></span>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
document.getElementById('wbf-discord-test-btn').addEventListener('click', function(){
|
||||||
|
var btn = this;
|
||||||
|
var res = document.getElementById('wbf-discord-test-result');
|
||||||
|
btn.disabled = true;
|
||||||
|
res.textContent = '⏳ Teste…';
|
||||||
|
fetch(ajaxurl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {'Content-Type':'application/x-www-form-urlencoded'},
|
||||||
|
body: 'action=wbf_discord_test&nonce=<?php echo wp_create_nonce("wbf_discord_test"); ?>'
|
||||||
|
})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(function(d){
|
||||||
|
if (d.success) {
|
||||||
|
res.style.color = '#16a34a';
|
||||||
|
res.textContent = '✅ ' + (d.data.message || 'Verbunden!');
|
||||||
|
} else {
|
||||||
|
res.style.color = '#dc2626';
|
||||||
|
res.textContent = '❌ ' + ((d.data && d.data.message) || 'Fehler');
|
||||||
|
}
|
||||||
|
btn.disabled = false;
|
||||||
|
})
|
||||||
|
.catch(function(){ res.style.color='#dc2626'; res.textContent='❌ Netzwerkfehler'; btn.disabled=false; });
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<?php submit_button(
|
<?php submit_button(
|
||||||
'💾 Einstellungen speichern',
|
'💾 Einstellungen speichern',
|
||||||
'primary',
|
'primary',
|
||||||
@@ -506,7 +738,6 @@ function wbf_admin_settings() {
|
|||||||
true,
|
true,
|
||||||
[ 'style' => 'margin-top:1rem' ]
|
[ 'style' => 'margin-top:1rem' ]
|
||||||
); ?>
|
); ?>
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- ── Vorschau-Tabelle ──────────────────────────────── -->
|
<!-- ── Vorschau-Tabelle ──────────────────────────────── -->
|
||||||
<hr style="margin-top:2.5rem">
|
<hr style="margin-top:2.5rem">
|
||||||
|
|||||||
Reference in New Issue
Block a user