Dateien nach "/" hochladen

This commit is contained in:
2026-01-10 16:21:26 +00:00
parent 05ff9951d5
commit eb40b2e360

998
wp-litebans-manager.php Normal file
View File

@@ -0,0 +1,998 @@
<?php
/*
Plugin Name: LiteBans Manager
Description: Die ultimative Lösung, um deine LiteBans Datenbank nahtlos in WordPress zu integrieren. Verwalte Bans, Mutes, Warnings und Kicks direkt im WordPress Admin-Panel und biete deinen Spielern ein modernes Frontend-Dashboard.
Version: 1.0.0
Author: M_Viper
*/
if ( ! defined( 'ABSPATH' ) ) { exit; }
class WP_LiteBans_Pro {
private $option_name = 'wp_litebans_pro_settings';
private $db = null;
public function __construct() {
// 1. CPT für Entbannungsanträge
add_action( 'init', array( $this, 'register_cpt_unban_request' ) );
add_action( 'add_meta_boxes', array( $this, 'add_unban_meta_box' ) );
add_action( 'save_post_unban_request', array( $this, 'save_unban_meta_box' ), 10, 2 );
// Spalten im CPT Listing anpassen
add_filter( 'manage_unban_request_posts_columns', array( $this, 'set_unban_columns' ) );
add_action( 'manage_unban_request_posts_custom_column', array( $this, 'fill_unban_columns' ), 10, 2 );
// 2. Admin Menü & Assets
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'admin_init', array( $this, 'handle_admin_actions' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
// Tabs in CPT-Übersicht einfügen + optionaler Filter
add_action( 'all_admin_notices', array( $this, 'inject_tabs_into_unban_cpt' ) );
add_action( 'pre_get_posts', array( $this, 'filter_unban_cpt_by_tab' ) );
// Schutz: Entferne Quick-Edit wenn Notiz / Status gesperrt
add_filter( 'post_row_actions', array( $this, 'filter_post_row_actions' ), 10, 2 );
// Ausgrauen gesperrter Zeilen in der CPT-Übersicht
add_action( 'admin_head-edit.php', array( $this, 'inject_locked_row_styles' ) );
// Verstecke Standard Editor für CPT
add_action( 'admin_head-post.php', array( $this, 'hide_default_editor' ) );
add_action( 'admin_head-post-new.php', array( $this, 'hide_default_editor' ) );
// 3. Frontend Assets & Shortcodes
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_shortcode( 'litebans_dashboard', array( $this, 'render_dashboard' ) );
add_shortcode( 'litebans_unban', array( $this, 'render_unban_shortcode' ) );
}
// --- Admin assets ---
public function enqueue_admin_assets( $hook ) {
$load = false;
if ( strpos( (string)$hook, 'litebans' ) !== false || strpos( (string)$hook, 'litebans-manager' ) !== false ) {
$load = true;
}
if ( function_exists( 'get_current_screen' ) ) {
$screen = get_current_screen();
if ( $screen && isset( $screen->post_type ) && $screen->post_type === 'unban_request' ) {
$load = true;
}
}
if ( $load ) {
wp_enqueue_style( 'litebans-pro-admin', plugins_url( 'css/admin.css', __FILE__ ), array(), '6.4.7' );
wp_enqueue_style( 'litebans-pro-frontend-css', plugins_url( 'css/style.css', __FILE__ ), array(), '6.4.7' );
}
}
/**
* Inject CSS/JS to visually mark locked rows in the CPT list (edit.php?post_type=unban_request)
* We collect locked post IDs server-side and output a small script that adds a CSS class and badge.
*/
public function inject_locked_row_styles() {
if ( ! is_admin() ) return;
if ( ! function_exists( 'get_current_screen' ) ) return;
$screen = get_current_screen();
if ( ! $screen ) return;
if ( $screen->id !== 'edit-unban_request' ) return;
// Query for locked posts (status or admin note locked)
$locked_posts = get_posts(array(
'post_type' => 'unban_request',
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_lb_status_locked',
'value' => '1',
'compare' => '='
),
array(
'key' => '_lb_admin_note_locked',
'value' => '1',
'compare' => '='
)
)
));
if ( empty( $locked_posts ) ) return;
// Prepare JSON array for JS
$ids_json = wp_json_encode( array_map( 'intval', $locked_posts ) );
// Output inline CSS + JS
echo '<style>
/* LiteBans: locked list-row styling */
.lb-locked-row td { opacity: 0.65; background: #fbfbfb; color: #6b6b6b; }
.lb-locked-badge { margin-left:6px; color:#a00; font-weight:700; font-size:12px; }
</style>';
echo '<script type="text/javascript">(function(){ var locked = ' . $ids_json . '; if(!locked || !locked.length) return; locked.forEach(function(id){ var row = document.getElementById("post-"+id); if(!row) return; row.classList.add("lb-locked-row"); try{ var title = row.querySelector(".row-title"); if(title && !row.querySelector(".lb-locked-badge")){ var span = document.createElement("span"); span.className = "lb-locked-badge"; span.textContent = " [Gesperrt]"; title.appendChild(span); } } catch(e){} });})();</script>';
}
public function hide_default_editor() {
global $post;
if ( $post && $post->post_type === 'unban_request' ) {
echo '<style>
#titlediv, #titlewrap, #post-body-content, #screen-meta-links, #submitdiv { display: none !important; }
#poststuff #post-body { margin-top: 20px; }
</style>';
}
}
// --- 1. CPT SYSTEM ---
public function register_cpt_unban_request() {
$labels = array(
'name' => 'Entbannungsanträge',
'singular_name' => 'Entbannungsantrag',
'menu_name' => 'Entbannungen',
'add_new' => 'Neu erstellen',
'edit_item' => 'Antrag bearbeiten',
);
$args = array(
'labels' => $labels,
'public' => false,
'show_ui' => true,
'show_in_menu' => 'litebans-manager',
'capability_type' => 'post',
'supports' => array(''),
'menu_icon' => 'dashicons-forms',
);
register_post_type( 'unban_request', $args );
}
public function set_unban_columns($columns) {
if ( isset($columns['title']) ) unset($columns['title']);
if ( isset($columns['date']) ) unset($columns['date']);
$columns['player'] = 'Spieler';
$columns['status'] = 'Status';
$columns['date'] = 'Datum';
return $columns;
}
public function fill_unban_columns($column, $post_id) {
if ($column == 'player') {
echo esc_html(get_post_meta($post_id, '_lb_player', true));
}
if ($column == 'status') {
$s = get_post_meta($post_id, '_lb_status', true);
$label = $s === 'approved' ? 'Angenommen' : ($s === 'rejected' ? 'Abgelehnt' : 'Wartend');
echo '<span class="lb-status-label lb-status-'.esc_attr($s).'">'.esc_html($label).'</span>';
}
}
public function add_unban_meta_box() {
// Primary ticket box
add_meta_box(
'lb_unban_ticket', 'Unban Ticket Verwaltung',
array( $this, 'render_unban_meta_box' ),
'unban_request', 'normal', 'high'
);
// Admin Note sidebox
add_meta_box(
'lb_admin_note_box', 'Admin Notiz',
array( $this, 'render_admin_note_box' ),
'unban_request', 'side', 'high'
);
}
public function render_unban_meta_box( $post ) {
wp_nonce_field( 'lb_unban_save', 'lb_unban_nonce' );
$player = get_post_meta( $post->ID, '_lb_player', true );
$reason = get_post_meta( $post->ID, '_lb_reason', true );
$message = $post->post_content;
$status = get_post_meta( $post->ID, '_lb_status', true );
if ( empty($status) ) $status = 'pending';
$status_locked = get_post_meta( $post->ID, '_lb_status_locked', true );
$admin_name = get_post_meta( $post->ID, '_lb_admin_name', true );
?>
<div class="lb-ticket-container">
<div class="lb-ticket-header">
<div class="lb-player-badge">
<img src="https://minotar.net/avatar/<?php echo rawurlencode($player); ?>/50" alt="Skin" class="lb-avatar-large">
<div>
<h3><?php echo esc_html($player); ?></h3>
<span class="lb-date-label">Eingereicht am: <?php echo get_the_date('d.m.Y H:i'); ?></span>
</div>
</div>
<div class="lb-status-select-wrapper">
<label>Entscheidung:</label>
<?php
// Wenn Status gesperrt ist, machen wir das Select disabled und fügen ein verstecktes Feld ein,
// damit der Wert beim POST gesendet wird (wird serverseitig aber ignoriert, falls gesperrt).
$disabled = ! empty( $status_locked ) ? 'disabled' : '';
?>
<select name="lb_status" class="lb-status-select" <?php echo $disabled; ?>>
<option value="pending" <?php selected($status, 'pending'); ?>>⏳ Ausstehend</option>
<option value="approved" <?php selected($status, 'approved'); ?>>✅ Annehmen</option>
<option value="rejected" <?php selected($status, 'rejected'); ?>>❌ Ablehnen</option>
</select>
<?php if ( ! empty( $status_locked ) ): ?>
<input type="hidden" name="lb_status" value="<?php echo esc_attr( $status ); ?>">
<div style="margin-left:10px;font-size:12px;color:#6b7280;">
Gesperrt von: <?php echo esc_html( $admin_name ?: '-' ); ?>
</div>
<?php endif; ?>
<p class="description" style="margin-top:4px;font-size:12px;">
⏳ Ausstehend = In Bearbeitung, kann noch geändert werden<br>
✅ Annehmen = Bann/Mute wird aufgehoben, Admin-Notiz gesperrt<br>
❌ Ablehnen = Status bleibt bestehen, Admin-Notiz gesperrt
</p>
</div>
</div>
<div class="lb-ticket-body">
<div class="lb-card lb-card-ban">
<div class="lb-card-title">Banngrund</div>
<div class="lb-card-content"><?php echo esc_textarea($reason); ?></div>
</div>
<div class="lb-card lb-card-msg">
<div class="lb-card-title">Spieler Nachricht</div>
<div class="lb-card-content"><?php echo wpautop(esc_textarea($message)); ?></div>
</div>
</div>
<div class="lb-ticket-footer">
<div class="lb-actions">
<button type="submit" class="button button-primary button-large">Entscheidung speichern</button>
</div>
</div>
</div>
<?php
}
/**
* Render the Admin Note sidebox.
* If locked, show read-only note with admin and timestamp.
* Otherwise show textarea for admin to add a note before approving/rejecting.
*/
public function render_admin_note_box( $post ) {
wp_nonce_field( 'lb_unban_save', 'lb_unban_nonce' ); // same nonce as main meta box
$note = get_post_meta( $post->ID, '_lb_admin_note', true );
$admin = get_post_meta( $post->ID, '_lb_admin_name', true );
$locked = get_post_meta( $post->ID, '_lb_admin_note_locked', true );
echo '<div style="margin-top:6px;">';
if ( $locked ) {
echo '<p style="margin:0 0 6px 0;"><strong>Gesperrt von:</strong> ' . esc_html( $admin ) . '</p>';
echo '<div style="background:#fafafa;border:1px solid #e6e9ec;padding:8px;border-radius:4px;white-space:pre-wrap;">';
echo nl2br( esc_html( $note ) );
echo '</div>';
echo '<p style="color:#8c8f94;margin-top:8px;">Diese Notiz wurde gesperrt und kann nicht mehr verändert werden.</p>';
} else {
echo '<p style="margin:0 0 6px 0;"><strong>Admin Notiz (wird beim Annehmen/Ablehnen gesperrt)</strong></p>';
echo '<textarea name="lb_admin_note" rows="6" style="width:100%;">' . esc_textarea( $note ) . '</textarea>';
echo '<p style="color:#8c8f94;margin-top:6px;">Bitte begründe kurz deine Entscheidung. Nach dem Speichern bei Annahme/Ablehnung wird diese Notiz dauerhaft gesperrt.</p>';
}
echo '</div>';
}
/**
* save_unban_meta_box
* - speichert Meta
* - sperrt Status und Admin-Notiz bei finaler Entscheidung (approved/rejected)
* - verhindert nachträgliche Änderungen am Status/Notiz, wenn gesperrt
*/
public function save_unban_meta_box( $post_id, $post ) {
// Nonce + capability checks
if ( ! isset( $_POST['lb_unban_nonce'] ) || ! wp_verify_nonce( $_POST['lb_unban_nonce'], 'lb_unban_save' ) ) return;
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
// Existing flags
$old_status = get_post_meta( $post_id, '_lb_status', true );
$status_locked = get_post_meta( $post_id, '_lb_status_locked', true );
$note_locked = get_post_meta( $post_id, '_lb_admin_note_locked', true );
// Incoming sanitized values
$incoming_status = isset( $_POST['lb_status'] ) ? sanitize_text_field( $_POST['lb_status'] ) : $old_status;
$incoming_note = isset( $_POST['lb_admin_note'] ) ? sanitize_textarea_field( wp_kses_post( $_POST['lb_admin_note'] ) ) : '';
$current_user = wp_get_current_user();
$timestamp = current_time( 'd.m.Y H:i' );
// ---- STATUS HANDLING ----
// If status is already locked, deny changing it.
if ( ! empty( $status_locked ) ) {
// keep old status; ignore incoming value
$status_to_store = $old_status;
} else {
// Accept incoming status and if it's final, lock it.
$status_to_store = $incoming_status;
if ( in_array( $status_to_store, array( 'approved', 'rejected' ), true ) ) {
update_post_meta( $post_id, '_lb_status_locked', 1 );
}
}
// Always store the status meta (either unchanged or new allowed value)
update_post_meta( $post_id, '_lb_status', $status_to_store );
// ---- ADMIN NOTE HANDLING ----
// If note already locked, do not overwrite.
if ( ! empty( $note_locked ) ) {
// do nothing for admin note
} else {
// If note is not locked yet:
if ( in_array( $status_to_store, array( 'approved', 'rejected' ), true ) ) {
// Final status: write note + admin name + lock (even if note is empty)
$admin_label = ( $current_user->display_name ? $current_user->display_name : $current_user->user_login ) . ' · ' . $timestamp;
update_post_meta( $post_id, '_lb_admin_note', $incoming_note );
update_post_meta( $post_id, '_lb_admin_name', $admin_label );
update_post_meta( $post_id, '_lb_admin_note_locked', 1 );
} else {
// Non-final: save incoming note (editable)
if ( isset( $_POST['lb_admin_note'] ) ) {
update_post_meta( $post_id, '_lb_admin_note', $incoming_note );
}
}
}
// ---- ACTION: wenn gerade auf approved gesetzt (und war nicht approved vorher) -> unban ingame ----
if ( $old_status !== 'approved' && $status_to_store === 'approved' ) {
$player = get_post_meta( $post_id, '_lb_player', true );
if ( $player ) {
$lbdb = $this->get_litebans_db();
if ( is_wp_error( $lbdb ) ) {
add_settings_error( 'lb', 'db_error', 'LiteBans DB nicht konfiguriert: Unban nicht ausgeführt.', 'error' );
} else {
$res = $this->unban_by_player( $lbdb, $player, $current_user );
if ( $res['updated'] > 0 ) {
add_settings_error( 'lb', 'unban_ok', sprintf( '%d Eintrag(e) für %s wurden ingame aufgehoben.', $res['updated'], esc_html($player) ), 'updated' );
} else {
add_settings_error( 'lb', 'unban_none', 'Keine aktive Strafe in LiteBans gefunden für ' . esc_html($player) . '.', 'notice' );
}
}
}
}
// If status changed to rejected and was not rejected previously, add a notice
if ( $old_status !== 'rejected' && $status_to_store === 'rejected' ) {
add_settings_error( 'lb', 'rejected', 'Antrag abgelehnt. Notiz gesperrt.', 'updated' );
}
}
// --- 2. ADMIN MENÜ ---
public function add_admin_menu() {
add_menu_page(
'LiteBans Manager',
'LiteBans Manager',
'manage_options',
'litebans-manager',
array( $this, 'render_admin_dashboard' ),
'dashicons-dismiss',
30
);
add_submenu_page( 'litebans-manager', 'Einstellungen', 'Einstellungen', 'manage_options', 'litebans-settings', array( $this, 'render_settings_page' ) );
}
public function register_settings() {
register_setting( $this->option_name, $this->option_name );
}
public function handle_admin_actions() {
// Actions handled in dashboard
}
public function render_settings_page() {
if ( isset( $_POST['save_settings'] ) && check_admin_referer( 'lb_save_settings' ) ) {
update_option( $this->option_name, $_POST['lb_settings'] );
echo '<div class="updated"><p>Einstellungen gespeichert!</p></div>';
}
$settings = get_option( $this->option_name );
$fields = array(
'db_host' => array('label'=>'Host', 'type'=>'text', 'default'=>'localhost'),
'db_name' => array('label'=>'Datenbank Name', 'type'=>'text', 'default'=>'litebans'),
'db_user' => array('label'=>'User', 'type'=>'text'),
'db_pass' => array('label'=>'Passwort', 'type'=>'password'),
'table_prefix' => array('label'=>'Prefix', 'type'=>'text', 'default'=>'litebans_'),
'theme_mode' => array('label'=>'Frontend Theme', 'type'=>'select', 'default'=>'light', 'options'=>['light'=>'Light Mode', 'dark'=>'Dark Mode'])
);
?>
<div class="wrap">
<h1>LiteBans Einstellungen</h1>
<form method="post">
<?php wp_nonce_field( 'lb_save_settings' ); ?>
<table class="form-table">
<?php foreach($fields as $key => $f): ?>
<tr>
<th scope="row"><label for="<?php echo $key; ?>"><?php echo $f['label']; ?></label></th>
<td>
<?php if($f['type'] === 'select'): ?>
<select id="<?php echo $key; ?>" name="lb_settings[<?php echo $key; ?>]" class="regular-text">
<?php foreach($f['options'] as $val => $label): ?>
<option value="<?php echo esc_attr($val); ?>" <?php selected( ($settings[$key] ?? $f['default']), $val ); ?>><?php echo esc_html($label); ?></option>
<?php endforeach; ?>
</select>
<?php else: ?>
<input type="<?php echo esc_attr($f['type']); ?>" id="<?php echo esc_attr($key); ?>" name="lb_settings[<?php echo esc_attr($key); ?>]"
value="<?php echo esc_attr( $settings[$key] ?? $f['default'] ); ?>" class="regular-text">
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
<input type="submit" name="save_settings" class="button button-primary" value="Speichern">
</form>
</div>
<?php
}
// --- 3. FRONTEND LOGIC ---
public function enqueue_scripts() {
wp_enqueue_style( 'litebans-pro-css', plugins_url( 'css/style.css', __FILE__ ), array(), '6.4.7' );
}
private function get_litebans_db() {
if ($this->db) return $this->db;
$s = get_option( $this->option_name );
if ( empty( $s['db_name'] ) ) return new WP_Error( 'no_config', 'LiteBans nicht konfiguriert.' );
$this->db = new wpdb( $s['db_user'], $s['db_pass'], $s['db_name'], $s['db_host'] );
if ( $this->db->last_error ) return new WP_Error( 'db_error', 'DB Fehler: ' . $this->db->last_error );
$this->db->prefix = isset($s['table_prefix']) ? $s['table_prefix'] : 'litebans_';
return $this->db;
}
private function get_avatar($name) {
return "<img src='https://minotar.net/avatar/".rawurlencode($name)."/32' class='litebans-avatar' loading='lazy'>";
}
private function clean($t) {
if(!$t) return "-";
$t = preg_replace("/(?i)(\x{00a7}|&)[0-9A-FK-OR]/u", "", $t);
return esc_html($t);
}
private function millis_to_date($m) { return $m ? date("d.m.Y H:i", $m/1000) : "-"; }
// KORRIGIERTE BADGE LOGIK
private function get_badge($r, $type) {
$now = (int) ( microtime(true) * 1000 );
// entfernte Einträge (aufgehoben)
if (!empty($r->removed_by_name) && $r->removed_by_name !== "#expired") {
return "<span class='litebans-badge expired'>Aufgehoben</span>";
}
// Warnings & Kicks - keine Ablauf/Perm-Logik nötig
if ($type === 'warnings') {
return "<span class='litebans-badge warn'>Verwarnt</span>";
}
if ($type === 'kicks') {
return "<span class='litebans-badge kick'>Gekickt</span>";
}
// Bans / Mutes: prüfen Ablauf
if (!empty($r->until) && (int)$r->until > 0 && $now > (int)$r->until) {
return "<span class='litebans-badge expired'>Abgelaufen</span>";
}
if ( empty($r->until) || (int)$r->until <= 0 ) {
$label = ($type === 'mutes') ? "Permanent Mute" : "Permanent Ban";
return "<span class='litebans-badge permanent'>".esc_html($label)."</span>";
}
// aktiv
$label = ($type === 'mutes') ? "Gemutet" : "Gebannt";
$class = ($type === 'mutes') ? "muted" : "active";
return "<span class='litebans-badge ".esc_attr($class)."'>".esc_html($label)."</span>";
}
private function get_theme_script($default_theme) {
ob_start();
?>
<script>
(function() {
const wrapper = document.querySelector('.litebans-wrapper');
const toggleBtn = document.getElementById('lb-theme-toggle');
if(!wrapper || !toggleBtn) return;
const sunIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>';
const moonIcon = '<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>';
const savedTheme = localStorage.getItem('lb_theme') || '<?php echo esc_js($default_theme); ?>';
function applyTheme(theme) {
if(theme === 'dark') {
wrapper.classList.add('dark-mode');
toggleBtn.innerHTML = sunIcon;
} else {
wrapper.classList.remove('dark-mode');
toggleBtn.innerHTML = moonIcon;
}
}
applyTheme(savedTheme);
toggleBtn.addEventListener('click', function() {
const isDark = wrapper.classList.contains('dark-mode');
const newTheme = isDark ? 'light' : 'dark';
localStorage.setItem('lb_theme', newTheme);
applyTheme(newTheme);
});
})();
</script>
<?php
return ob_get_clean();
}
private function render_unban_logic($player, $reason) {
$msg = '';
if ( isset($_POST['lb_submit_unban']) && check_admin_referer( 'lb_unban_form' ) ) {
$post_data = array(
'post_title' => 'Unban: ' . sanitize_text_field($_POST['lb_player']),
'post_content' => sanitize_textarea_field($_POST['lb_message']),
'post_status' => 'pending',
'post_type' => 'unban_request',
);
$post_id = wp_insert_post( $post_data );
if( $post_id ) {
update_post_meta( $post_id, '_lb_player', sanitize_text_field($_POST['lb_player']) );
update_post_meta( $post_id, '_lb_reason', sanitize_text_field($_POST['lb_reason']) );
update_post_meta( $post_id, '_lb_status', 'pending' );
$msg = '<div class="lb-success">Antrag erfolgreich eingereicht!</div>';
$player = ''; $reason = '';
}
}
ob_start();
?>
<div class="litebans-wrapper">
<div class="litebans-unban-form-container">
<div class="litebans-unban-header">
<h2>Entbannungsantrag</h2>
</div>
<?php echo $msg; ?>
<form method="post">
<?php wp_nonce_field( 'lb_unban_form' ); ?>
<div class="litebans-form-group">
<label class="litebans-form-label">Spielername</label>
<input type="text" name="lb_player" class="litebans-form-control" value="<?php echo esc_attr($player); ?>" readonly required>
</div>
<div class="litebans-form-group">
<label class="litebans-form-label">Grund</label>
<input type="text" name="lb_reason" class="litebans-form-control" value="<?php echo esc_attr($reason); ?>" readonly required>
</div>
<div class="litebans-form-group">
<label class="litebans-form-label">Warum sollten wir dich entbannen?</label>
<textarea name="lb_message" class="litebans-form-control" rows="6" required placeholder="Erkläre uns kurz dein Anliegen..."></textarea>
</div>
<div class="litebans-form-footer">
<a class="litebans-backlink" href="<?php echo esc_url( remove_query_arg(array('lb_player', 'lb_reason')) ); ?>">&larr; Zurück zur Liste</a>
<button type="submit" name="lb_submit_unban" class="litebans-btn">Antrag senden</button>
</div>
</form>
</div>
</div>
<?php
return ob_get_clean();
}
public function render_dashboard() {
$settings = get_option( $this->option_name );
$default_theme = $settings['theme_mode'] ?? 'light';
if ( isset($_GET['lb_player']) ) {
$player = sanitize_text_field($_GET['lb_player']);
$reason = isset($_GET['lb_reason']) ? sanitize_textarea_field($_GET['lb_reason']) : '';
$content = $this->render_unban_logic($player, $reason);
$content .= $this->get_theme_script($default_theme);
return $content;
}
$lbdb = $this->get_litebans_db();
if(is_wp_error($lbdb)) return '<div class="error">Datenbank Fehler: Bitte Einstellungen überprüfen.</div>';
$current_url = get_permalink();
// robustes render_tab: COALESCE für optionale Felder, stellt sicher dass kicks/warnings sichtbar sind
$render_tab = function($t) use($lbdb, $current_url) {
$table = esc_sql($lbdb->prefix . $t);
$limit = 20;
// FIX: Kicks haben keine removed_by_name Spalte in der DB
$select_removed = ($t === 'kicks') ? "''" : "COALESCE(l.removed_by_name, '')";
$q = $lbdb->prepare(
"SELECT l.id, l.uuid, l.reason, l.banned_by_name, l.time,
COALESCE(l.until, 0) as until,
COALESCE(l.active, 0) as active,
" . $select_removed . " as removed_by_name,
h.name
FROM {$table} l
LEFT JOIN {$lbdb->prefix}history h ON l.uuid = h.uuid
WHERE (l.uuid IS NOT NULL OR h.name IS NOT NULL)
GROUP BY l.id
ORDER BY l.time DESC LIMIT %d",
$limit
);
$rows = $lbdb->get_results($q);
if(!$rows) return "<div class='litebans-empty'>Keine Daten</div>";
$html = '<table class="litebans-table"><thead><tr><th>Spieler</th><th>Status</th><th>Grund</th><th>Datum</th><th>Aktion</th></tr></thead><tbody>';
foreach($rows as $r) {
$playerName = !empty($r->name) ? $r->name : (!empty($r->banned_by_name) ? $r->banned_by_name : '-');
// active kann 0/1 oder NULL sein -> COALESCE stellt sicher: 1=aktiv
$is_active = (isset($r->active) && intval($r->active) == 1) && empty($r->removed_by_name);
$html .= '<tr>';
$html .= '<td data-label="Spieler"><div class="litebans-avatar-wrapper">'.$this->get_avatar($playerName);
$html .= '<span class="litebans-player-name">'.esc_html($playerName).'</span></div></td>';
$html .= '<td data-label="Status">'.$this->get_badge($r, $t).'</td>';
$html .= '<td data-label="Grund">'.$this->clean($r->reason).'</td>';
$html .= '<td data-label="Datum">'.$this->millis_to_date($r->time).'</td>';
// BUTTON LOGIK: nur für bans/mutes und nur wenn aktiv
$html .= '<td data-label="Aktion">';
if ($is_active && ($t === 'bans' || $t === 'mutes')) {
$unban_link = add_query_arg( array('lb_player' => $playerName, 'lb_reason' => rawurlencode($r->reason)), $current_url );
$html .= '<a href="'.esc_url($unban_link).'" class="litebans-unban-link">Antrag</a>';
} else {
$html .= '<span style="color:#aaa; font-size:12px;">-</span>';
}
$html .= '</td>';
$html .= '</tr>';
}
return $html.'</tbody></table>';
};
ob_start();
?>
<div class="litebans-wrapper">
<div class="litebans-card">
<div class="litebans-header">
<h2 class="litebans-title">Sanktionen</h2>
<div class="litebans-controls">
<button id="lb-theme-toggle" class="litebans-theme-toggle" title="Theme wechseln"></button>
<form method="post" class="litebans-search-form">
<!-- Suche könnte hier erweitert werden -->
</form>
</div>
</div>
<ul class="litebans-tabs">
<li><button class="litebans-tab-btn active" onclick="openTab(event,'bans')">Bans</button></li>
<li><button class="litebans-tab-btn" onclick="openTab(event,'mutes')">Mutes</button></li>
<li><button class="litebans-tab-btn" onclick="openTab(event,'warnings')">Warnings</button></li>
<li><button class="litebans-tab-btn" onclick="openTab(event,'kicks')">Kicks</button></li>
</ul>
<div id="bans" class="litebans-tab-content active"><?php echo $render_tab('bans'); ?></div>
<div id="mutes" class="litebans-tab-content"><?php echo $render_tab('mutes'); ?></div>
<div id="warnings" class="litebans-tab-content"><?php echo $render_tab('warnings'); ?></div>
<div id="kicks" class="litebans-tab-content"><?php echo $render_tab('kicks'); ?></div>
</div>
<script>
function openTab(e,n){
var c=document.getElementsByClassName("litebans-tab-content"), t=document.getElementsByClassName("litebans-tab-btn");
for(i=0;i<c.length;i++){ c[i].style.display="none"; c[i].classList.remove("active"); }
for(i=0;i<t.length;i++){ t[i].classList.remove("active"); }
document.getElementById(n).style.display="block";
document.getElementById(n).classList.add("active");
e.currentTarget.classList.add("active");
}
</script>
<?php echo $this->get_theme_script($default_theme); ?>
</div>
<?php
return ob_get_clean();
}
public function render_unban_shortcode() {
return $this->render_unban_logic('', '');
}
// --- 4. ADMIN BACKEND MIT TABS ---
public function render_admin_dashboard() {
// Tabs
$current_tab = isset($_GET['lb_tab']) ? sanitize_text_field($_GET['lb_tab']) : 'bans';
$valid_tabs = array('bans', 'mutes', 'warnings', 'kicks');
if(!in_array($current_tab, $valid_tabs, true)) $current_tab = 'bans';
$lbdb = $this->get_litebans_db();
if ( is_wp_error( $lbdb ) ) {
echo '<div class="notice notice-error"><p>' . esc_html( $lbdb->get_error_message() ) . '</p></div>';
return;
}
// Actions verarbeiten (Unban/Delete)
if ( isset( $_POST['lb_action'] ) && isset( $_POST['lb_id'] ) && check_admin_referer( 'lb_admin_action_' . $_POST['lb_id'] ) ) {
$this->process_admin_action($lbdb, sanitize_text_field($_POST['lb_type']));
}
settings_errors( 'lb' );
// Pagination
$search = isset( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '';
$limit = 20;
$paged = isset( $_GET['paged'] ) ? max(1, intval($_GET['paged'])) : 1;
$offset = ( $paged - 1 ) * $limit;
$table = esc_sql($lbdb->prefix . $current_tab);
$search_sql = $search ? $lbdb->prepare(" AND h.name LIKE %s", "%$search%") : "";
// FIX: Kicks haben keine removed_by_name Spalte in der DB
$select_removed = ($current_tab === 'kicks') ? "''" : "COALESCE(l.removed_by_name, '')";
// Robust select for admin listing as well
$query = "SELECT l.id, l.uuid, l.reason, l.banned_by_name, l.time,
COALESCE(l.until,0) AS until,
COALESCE(l.active,0) AS active,
" . $select_removed . " AS removed_by_name,
h.name
FROM $table l LEFT JOIN {$lbdb->prefix}history h ON l.uuid = h.uuid
WHERE (l.uuid IS NOT NULL OR h.name IS NOT NULL) $search_sql GROUP BY l.id ORDER BY l.time DESC LIMIT %d OFFSET %d";
$results = $lbdb->get_results( $lbdb->prepare( $query, $limit, $offset ) );
$total = $lbdb->get_var("SELECT COUNT(*) FROM $table l WHERE l.uuid IS NOT NULL");
?>
<div class="wrap">
<h1>LiteBans Manager</h1>
<!-- TAB NAVIGATION -->
<ul class="lb-admin-tabs">
<?php foreach($valid_tabs as $t): ?>
<li class="lb-admin-tab <?php echo $current_tab == $t ? 'active' : ''; ?>">
<a href="<?php echo esc_url(admin_url('admin.php?page=litebans-manager&lb_tab='.$t)); ?>"><?php echo esc_html(ucfirst($t)); ?></a>
</li>
<?php endforeach; ?>
</ul>
<!-- SEARCH -->
<form method="get" class="search-box" style="margin-bottom: 20px;">
<input type="hidden" name="page" value="litebans-manager">
<input type="hidden" name="lb_tab" value="<?php echo esc_attr($current_tab); ?>">
<input type="search" name="s" value="<?php echo esc_attr($search); ?>" placeholder="Spieler suchen...">
<button type="submit" class="button">Suchen</button>
</form>
<!-- TABLE -->
<table class="wp-list-table widefat fixed striped table-view-list">
<thead>
<tr><th>ID</th><th>Spieler</th><th>Grund</th><th>Von</th><th>Status</th><th>Aktion</th></tr>
</thead>
<tbody>
<?php if(!$results) echo '<tr><td colspan="6">Keine Einträge.</td></tr>'; ?>
<?php foreach($results as $row):
$active = false;
if ( in_array($current_tab, array('bans','mutes'), true) ) {
$active = (isset($row->active) && $row->active == 1) && empty($row->removed_by_name);
}
?>
<tr>
<td><?php echo esc_html($row->id); ?></td>
<td><?php echo esc_html($row->name ?? $row->banned_by_name); ?></td>
<td><?php echo $this->clean($row->reason); ?></td>
<td><?php echo esc_html($row->banned_by_name); ?></td>
<td>
<?php
if ($current_tab === 'warnings') {
echo '<span class="lb-status label-warning">Verwarnung</span>';
} elseif ($current_tab === 'kicks') {
echo '<span class="lb-status label-kick">Kick</span>';
} else {
echo '<span class="lb-status '.($active?'label-active':'label-inactive').'">';
echo $active ? 'Aktiv' : 'Inaktiv';
echo '</span>';
}
?>
</td>
<td>
<form method="post" style="display:inline;">
<?php wp_nonce_field( 'lb_admin_action_' . $row->id ); ?>
<input type="hidden" name="lb_id" value="<?php echo esc_attr($row->id); ?>">
<input type="hidden" name="lb_type" value="<?php echo esc_attr($current_tab); ?>">
<?php if($active && in_array($current_tab, array('bans','mutes'), true)): ?>
<button type="submit" name="lb_action" value="unban"
onclick="return confirm('Möchtest du diesen Eintrag wirklich aufheben?');" class="button button-secondary button-small">Aufheben</button>
<?php endif; ?>
<button type="submit" name="lb_action" value="delete"
onclick="return confirm('Möchtest du diesen Eintrag löschen?');" class="button button-link button-link-delete delete" style="color:#a00;">Löschen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- PAGINATION -->
<div class="tablenav bottom">
<?php
echo paginate_links( array(
'total' => max(1, ceil($total/$limit)),
'current' => $paged,
'base' => add_query_arg('paged', '%#%')
));
?>
</div>
</div>
<?php
}
/**
* process_admin_action
* - verarbeitet 'unban' und 'delete' Aktionen aus dem Admin-Dashboard
* - unban aktualisiert LiteBans DB so, dass ingame aufgehoben wird
*/
private function process_admin_action($lbdb, $type) {
$action = sanitize_text_field( $_POST['lb_action'] ?? '' );
$id = intval( $_POST['lb_id'] ?? 0 );
$current_user = wp_get_current_user();
$table = esc_sql($lbdb->prefix . $type);
$now = (int) ( microtime(true) * 1000 );
// Disallow unban action for warnings/kicks
if ( ($type === 'warnings' || $type === 'kicks') && $action === 'unban' ) {
add_settings_error( 'lb', 'invalid_action', 'Diese Sanktion kann nicht aufgehoben werden.', 'error' );
return;
}
if ( $action === 'unban' ) {
// Nur Bans / Mutes
if ( ! in_array( $type, array('bans','mutes'), true ) ) {
add_settings_error( 'lb', 'invalid_type', 'Ungültiger Typ für Aufhebung.', 'error' );
return;
}
$data = array(
'active' => 0,
'removed_by_name' => 'WebAdmin: ' . $current_user->display_name,
'removed_by_uuid' => 'CONSOLE',
'removed_by_date' => $now
);
$where = array( 'id' => $id );
$formats = array( '%d', '%s', '%s', '%d' );
$where_formats = array( '%d' );
$result = $lbdb->update( $table, $data, $where, $formats, $where_formats );
if ( $result !== false ) {
// additionally, try to update any other rows with same uuid (defensive)
$uuid = $lbdb->get_var( $lbdb->prepare( "SELECT uuid FROM {$table} WHERE id = %d LIMIT 1", $id ) );
if ( $uuid ) {
$lbdb->update( esc_sql($lbdb->prefix . 'bans'), $data, array('uuid' => $uuid, 'active' => 1), $formats, array('%s','%d'));
$lbdb->update( esc_sql($lbdb->prefix . 'mutes'), $data, array('uuid' => $uuid, 'active' => 1), $formats, array('%s','%d'));
}
add_settings_error( 'lb', 'success', "Eintrag #$id erfolgreich aufgehoben und ingame entfernt.", 'updated' );
} else {
add_settings_error( 'lb', 'dbfail', "Datenbank Update fehlgeschlagen.", 'error' );
}
} elseif ( $action === 'delete' ) {
$result = $lbdb->delete( $table, array( 'id' => $id ), array( '%d' ) );
if ( $result ) add_settings_error( 'lb', 'success', "Eintrag #$id gelöscht.", 'updated' );
else add_settings_error( 'lb', 'dbfail', "Löschen fehlgeschlagen.", 'error' );
}
}
/**
* unban_by_player
* - versucht UUID aus history zu holen und active Bans/Mutes aufzuheben
* - returns array('updated' => int)
*/
private function unban_by_player( $lbdb, $player, $current_user ) {
$updated = 0;
$now = (int) ( microtime(true) * 1000 );
// find latest uuid from history
$uuid = $lbdb->get_var( $lbdb->prepare( "SELECT uuid FROM {$lbdb->prefix}history WHERE name = %s ORDER BY id DESC LIMIT 1", $player ) );
if ( ! $uuid ) {
// Kein Eintrag in history gefunden => nicht zuverlässig aufhebbar
return array('updated' => 0);
}
$data = array(
'active' => 0,
'removed_by_name' => 'WebAdmin: ' . $current_user->display_name,
'removed_by_uuid' => 'CONSOLE',
'removed_by_date' => $now
);
$formats = array('%d','%s','%s','%d');
foreach ( array('bans','mutes') as $t ) {
$table = esc_sql( $lbdb->prefix . $t );
// Update only active entries with this uuid
$where = array( 'uuid' => $uuid, 'active' => 1 );
$where_formats = array('%s','%d');
$res = $lbdb->update( $table, $data, $where, $formats, $where_formats );
if ( $res !== false ) {
$updated += (int) $res;
}
}
return array('updated' => $updated);
}
/**
* Inject tabs into the CPT listing screen (edit.php?post_type=unban_request)
* This displays the same tabs as the plugin dashboard and links to the plugin page.
*/
public function inject_tabs_into_unban_cpt() {
if ( ! is_admin() ) return;
if ( ! function_exists( 'get_current_screen' ) ) return;
$screen = get_current_screen();
if ( ! $screen ) return;
// Only show on the CPT list screen for unban_request
if ( $screen->id !== 'edit-unban_request' ) return;
$valid_tabs = array( 'bans', 'mutes', 'warnings', 'kicks' );
$current = isset( $_GET['lb_tab'] ) ? sanitize_text_field( $_GET['lb_tab'] ) : 'bans';
if ( ! in_array( $current, $valid_tabs, true ) ) $current = 'bans';
echo '<div style="margin-top:10px;margin-bottom:8px;">';
echo '<ul class="lb-admin-tabs">';
foreach ( $valid_tabs as $t ) {
$active = $current === $t ? 'active' : '';
$url = add_query_arg( array( 'page' => 'litebans-manager', 'lb_tab' => $t ), admin_url( 'admin.php' ) );
echo '<li class="lb-admin-tab ' . esc_attr( $active ) . '"><a href="' . esc_url( $url ) . '">' . esc_html( ucfirst( $t ) ) . '</a></li>';
}
echo '</ul>';
echo '</div>';
}
public function filter_unban_cpt_by_tab( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) return;
if ( $query->get('post_type') !== 'unban_request' ) return;
if ( isset( $_GET['lb_tab'] ) ) {
$tab = sanitize_text_field( $_GET['lb_tab'] );
$allowed = array( 'bans', 'mutes', 'warnings', 'kicks' );
if ( in_array( $tab, $allowed, true ) ) {
// apply meta_query only if meta exists; harmless otherwise
$query->set( 'meta_query', array(
array(
'key' => '_lb_type',
'value' => $tab,
'compare' => '='
)
) );
}
}
}
/**
* Remove inline Quick Edit when admin note is locked (prevents sneaky quick edits).
*/
public function filter_post_row_actions( $actions, $post ) {
if ( $post->post_type !== 'unban_request' ) return $actions;
$locked_note = get_post_meta( $post->ID, '_lb_admin_note_locked', true );
$locked_status = get_post_meta( $post->ID, '_lb_status_locked', true );
if ( $locked_note || $locked_status ) {
if ( isset( $actions['inline hide-if-no-js'] ) ) {
unset( $actions['inline hide-if-no-js'] );
}
if ( isset( $actions['inline'] ) ) {
unset( $actions['inline'] );
}
}
return $actions;
}
}
new WP_LiteBans_Pro();