diff --git a/wp-litebans-manager.php b/wp-litebans-manager.php index f9428d5..6a689aa 100644 --- a/wp-litebans-manager.php +++ b/wp-litebans-manager.php @@ -1,998 +1,1006 @@ -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 ''; - - echo ''; - } - - public function hide_default_editor() { - global $post; - if ( $post && $post->post_type === 'unban_request' ) { - echo ''; - } - } - - // --- 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 ''.esc_html($label).''; - } - } - - 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 ); - - ?> -
-
-
- Skin -
-

- Eingereicht am: -
-
-
- - - - - -
- Gesperrt von: -
- -

- ⏳ Ausstehend = In Bearbeitung, kann noch geändert werden
- ✅ Annehmen = Bann/Mute wird aufgehoben, Admin-Notiz gesperrt
- ❌ Ablehnen = Status bleibt bestehen, Admin-Notiz gesperrt -

-
-
-
-
-
Banngrund
-
-
-
-
Spieler Nachricht
-
-
-
- -
- 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 '
'; - if ( $locked ) { - echo '

Gesperrt von: ' . esc_html( $admin ) . '

'; - echo '
'; - echo nl2br( esc_html( $note ) ); - echo '
'; - echo '

Diese Notiz wurde gesperrt und kann nicht mehr verändert werden.

'; - } else { - echo '

Admin Notiz (wird beim Annehmen/Ablehnen gesperrt)

'; - echo ''; - echo '

Bitte begründe kurz deine Entscheidung. Nach dem Speichern bei Annahme/Ablehnung wird diese Notiz dauerhaft gesperrt.

'; - } - echo '
'; - } - - /** - * 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 '

Einstellungen gespeichert!

'; - } - - $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']) - ); - ?> -
-

LiteBans Einstellungen

-
- - - $f): ?> - - - - - -
- - - - - -
- -
-
- 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 ""; - } - - 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 "Aufgehoben"; - } - - // Warnings & Kicks - keine Ablauf/Perm-Logik nötig - if ($type === 'warnings') { - return "Verwarnt"; - } - if ($type === 'kicks') { - return "Gekickt"; - } - - // Bans / Mutes: prüfen Ablauf - if (!empty($r->until) && (int)$r->until > 0 && $now > (int)$r->until) { - return "Abgelaufen"; - } - - if ( empty($r->until) || (int)$r->until <= 0 ) { - $label = ($type === 'mutes') ? "Permanent Mute" : "Permanent Ban"; - return "".esc_html($label).""; - } - - // aktiv - $label = ($type === 'mutes') ? "Gemutet" : "Gebannt"; - $class = ($type === 'mutes') ? "muted" : "active"; - return "".esc_html($label).""; - } - - private function get_theme_script($default_theme) { - ob_start(); - ?> - - '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 = '
Antrag erfolgreich eingereicht!
'; - $player = ''; $reason = ''; - } - } - - ob_start(); - ?> -
-
-
-

Entbannungsantrag

-
- - -
- - -
- - -
- -
- - -
- -
- - -
- - -
-
-
- 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 '
Datenbank Fehler: Bitte Einstellungen überprüfen.
'; - - $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 "
Keine Daten
"; - - $html = ''; - - 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 .= ''; - $html .= ''; - - $html .= ''; - - $html .= ''; - - $html .= ''; - - // BUTTON LOGIK: nur für bans/mutes und nur wenn aktiv - $html .= ''; - $html .= ''; - } - return $html.'
SpielerStatusGrundDatumAktion
'.$this->get_avatar($playerName); - $html .= ''.esc_html($playerName).'
'.$this->get_badge($r, $t).''.$this->clean($r->reason).''.$this->millis_to_date($r->time).''; - if ($is_active && ($t === 'bans' || $t === 'mutes')) { - $unban_link = add_query_arg( array('lb_player' => $playerName, 'lb_reason' => rawurlencode($r->reason)), $current_url ); - $html .= 'Antrag'; - } else { - $html .= '-'; - } - $html .= '
'; - }; - - ob_start(); - ?> -
-
-
-

Sanktionen

-
- -
- -
-
-
- -
-
-
-
-
- - get_theme_script($default_theme); ?> -
- 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 '

' . esc_html( $lbdb->get_error_message() ) . '

'; - 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"); - - ?> -
-

LiteBans Manager

- - - - - - - - - - - - - - '; ?> - active) && $row->active == 1) && empty($row->removed_by_name); - } - ?> - - - - - - - - - - -
IDSpielerGrundVonStatusAktion
Keine Einträge.
id); ?>name ?? $row->banned_by_name); ?>clean($row->reason); ?>banned_by_name); ?> - Verwarnung'; - } elseif ($current_tab === 'kicks') { - echo 'Kick'; - } else { - echo ''; - echo $active ? 'Aktiv' : 'Inaktiv'; - echo ''; - } - ?> - -
- id ); ?> - - - - - - - - -
-
- - -
- max(1, ceil($total/$limit)), - 'current' => $paged, - 'base' => add_query_arg('paged', '%#%') - )); - ?> -
-
- 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 '
'; - echo ''; - echo '
'; - } - - 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; - } -} - +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 ''; + + echo ''; + } + + public function hide_default_editor() { + global $post; + if ( $post && $post->post_type === 'unban_request' ) { + echo ''; + } + } + + // --- 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 ''.esc_html($label).''; + } + } + + 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 ); + + ?> +
+
+
+ Skin +
+

+ Eingereicht am: +
+
+
+ + + + + +
+ Gesperrt von: +
+ +

+ ⏳ Ausstehend = In Bearbeitung, kann noch geändert werden
+ ✅ Annehmen = Bann/Mute wird aufgehoben, Admin-Notiz gesperrt
+ ❌ Ablehnen = Status bleibt bestehen, Admin-Notiz gesperrt +

+
+
+
+
+
Banngrund
+
+
+
+
Spieler Nachricht
+
+
+
+ +
+ 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 '
'; + if ( $locked ) { + echo '

Gesperrt von: ' . esc_html( $admin ) . '

'; + echo '
'; + echo nl2br( esc_html( $note ) ); + echo '
'; + echo '

Diese Notiz wurde gesperrt und kann nicht mehr verändert werden.

'; + } else { + echo '

Admin Notiz (wird beim Annehmen/Ablehnen gesperrt)

'; + echo ''; + echo '

Bitte begründe kurz deine Entscheidung. Nach dem Speichern bei Annahme/Ablehnung wird diese Notiz dauerhaft gesperrt.

'; + } + echo '
'; + } + + /** + * 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 '

Einstellungen gespeichert!

'; + } + + $settings = get_option( $this->option_name ); + $fields = array( + 'db_host' => array('label'=>'Host', 'type'=>'text', 'default'=>'localhost'), + 'db_port' => array('label'=>'Port', 'type'=>'number', 'default'=>'3306'), + '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']) + ); + ?> +
+

LiteBans Einstellungen

+
+ + + $f): ?> + + + + + +
+ + + + + +
+ +
+
+ db) return $this->db; + $s = get_option( $this->option_name ); + if ( empty( $s['db_name'] ) ) return new WP_Error( 'no_config', 'LiteBans nicht konfiguriert.' ); + + // Host und Port zusammenfügen + $db_host = $s['db_host']; + if ( ! empty( $s['db_port'] ) ) { + $db_host .= ':' . intval( $s['db_port'] ); + } + + $this->db = new wpdb( $s['db_user'], $s['db_pass'], $s['db_name'], $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 ""; + } + + 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 "Aufgehoben"; + } + + // Warnings & Kicks - keine Ablauf/Perm-Logik nötig + if ($type === 'warnings') { + return "Verwarnt"; + } + if ($type === 'kicks') { + return "Gekickt"; + } + + // Bans / Mutes: prüfen Ablauf + if (!empty($r->until) && (int)$r->until > 0 && $now > (int)$r->until) { + return "Abgelaufen"; + } + + if ( empty($r->until) || (int)$r->until <= 0 ) { + $label = ($type === 'mutes') ? "Permanent Mute" : "Permanent Ban"; + return "".esc_html($label).""; + } + + // aktiv + $label = ($type === 'mutes') ? "Gemutet" : "Gebannt"; + $class = ($type === 'mutes') ? "muted" : "active"; + return "".esc_html($label).""; + } + + private function get_theme_script($default_theme) { + ob_start(); + ?> + + '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 = '
Antrag erfolgreich eingereicht!
'; + $player = ''; $reason = ''; + } + } + + ob_start(); + ?> +
+
+
+

Entbannungsantrag

+
+ + +
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+
+ 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 '
Datenbank Fehler: Bitte Einstellungen überprüfen.
'; + + $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 "
Keine Daten
"; + + $html = ''; + + 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 .= ''; + $html .= ''; + + $html .= ''; + + $html .= ''; + + $html .= ''; + + // BUTTON LOGIK: nur für bans/mutes und nur wenn aktiv + $html .= ''; + $html .= ''; + } + return $html.'
SpielerStatusGrundDatumAktion
'.$this->get_avatar($playerName); + $html .= ''.esc_html($playerName).'
'.$this->get_badge($r, $t).''.$this->clean($r->reason).''.$this->millis_to_date($r->time).''; + if ($is_active && ($t === 'bans' || $t === 'mutes')) { + $unban_link = add_query_arg( array('lb_player' => $playerName, 'lb_reason' => rawurlencode($r->reason)), $current_url ); + $html .= 'Antrag'; + } else { + $html .= '-'; + } + $html .= '
'; + }; + + ob_start(); + ?> +
+
+
+

Sanktionen

+
+ +
+ +
+
+
+ +
+
+
+
+
+ + get_theme_script($default_theme); ?> +
+ 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 '

' . esc_html( $lbdb->get_error_message() ) . '

'; + 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"); + + ?> +
+

LiteBans Manager

+ + + + + + + + + + + + + + '; ?> + active) && $row->active == 1) && empty($row->removed_by_name); + } + ?> + + + + + + + + + + +
IDSpielerGrundVonStatusAktion
Keine Einträge.
id); ?>name ?? $row->banned_by_name); ?>clean($row->reason); ?>banned_by_name); ?> + Verwarnung'; + } elseif ($current_tab === 'kicks') { + echo 'Kick'; + } else { + echo ''; + echo $active ? 'Aktiv' : 'Inaktiv'; + echo ''; + } + ?> + +
+ id ); ?> + + + + + + + + +
+
+ + +
+ max(1, ceil($total/$limit)), + 'current' => $paged, + 'base' => add_query_arg('paged', '%#%') + )); + ?> +
+
+ 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 '
'; + echo ''; + echo '
'; + } + + 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(); \ No newline at end of file