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 );
?>
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'])
);
?>
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();
?>
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 = 'Spieler Status Grund Datum Aktion ';
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 .= ''.$this->get_avatar($playerName);
$html .= ''.esc_html($playerName).'
';
$html .= ''.$this->get_badge($r, $t).' ';
$html .= ''.$this->clean($r->reason).' ';
$html .= ''.$this->millis_to_date($r->time).' ';
// BUTTON LOGIK: nur für bans/mutes und nur wenn aktiv
$html .= '';
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 .= ' ';
$html .= ' ';
}
return $html.'
';
};
ob_start();
?>
Bans
Mutes
Warnings
Kicks
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");
?>
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 '
';
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 '' . esc_html( ucfirst( $t ) ) . ' ';
}
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();