Update from Git Manager GUI
This commit is contained in:
@@ -96,10 +96,10 @@ class WBF_DB {
|
||||
$sql_tags = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_tags (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(60) NOT NULL,
|
||||
slug VARCHAR(60) NOT NULL UNIQUE,
|
||||
slug VARCHAR(60) NOT NULL,
|
||||
use_count INT DEFAULT 0,
|
||||
PRIMARY KEY (id),
|
||||
KEY slug (slug)
|
||||
UNIQUE KEY slug (slug)
|
||||
) $charset;";
|
||||
|
||||
$sql_thread_tags = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_thread_tags (
|
||||
@@ -163,8 +163,28 @@ class WBF_DB {
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_users", 'ban_reason', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN ban_reason TEXT DEFAULT '' AFTER role");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_categories", 'parent_id', "ALTER TABLE {$wpdb->prefix}forum_categories ADD COLUMN parent_id BIGINT UNSIGNED DEFAULT 0 AFTER id");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_categories", 'min_role', "ALTER TABLE {$wpdb->prefix}forum_categories ADD COLUMN min_role VARCHAR(20) DEFAULT 'member' AFTER post_count");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_categories", 'guest_visible', "ALTER TABLE {$wpdb->prefix}forum_categories ADD COLUMN guest_visible TINYINT(1) DEFAULT 1 AFTER min_role");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_users", 'reset_token', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN reset_token VARCHAR(64) DEFAULT NULL");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_users", 'reset_token_expires', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN reset_token_expires DATETIME DEFAULT NULL");
|
||||
// Soft-Delete
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_threads", 'deleted_at', "ALTER TABLE {$wpdb->prefix}forum_threads ADD COLUMN deleted_at DATETIME DEFAULT NULL");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_posts", 'deleted_at', "ALTER TABLE {$wpdb->prefix}forum_posts ADD COLUMN deleted_at DATETIME DEFAULT NULL");
|
||||
// Profil-Sichtbarkeit
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_users", 'profile_public', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN profile_public TINYINT(1) DEFAULT 1");
|
||||
// Zeitlich begrenzte Sperren
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_users", 'ban_until', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN ban_until DATETIME DEFAULT NULL");
|
||||
self::maybe_add_column("{$wpdb->prefix}forum_users", 'pre_ban_role', "ALTER TABLE {$wpdb->prefix}forum_users ADD COLUMN pre_ban_role VARCHAR(20) DEFAULT 'member'");
|
||||
// Thread-Abonnements
|
||||
$sql_subscriptions = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_subscriptions (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
thread_id BIGINT UNSIGNED NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY user_thread (user_id, thread_id),
|
||||
KEY thread_id (thread_id)
|
||||
) $charset;";
|
||||
dbDelta( $sql_subscriptions );
|
||||
$sql_notifications = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_notifications (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
@@ -182,7 +202,87 @@ class WBF_DB {
|
||||
dbDelta( $sql_reports );
|
||||
dbDelta( $sql_notifications );
|
||||
|
||||
// Default categories
|
||||
// Einladungs-Tabelle
|
||||
$sql_invites = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_invites (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
code VARCHAR(64) NOT NULL UNIQUE,
|
||||
created_by BIGINT UNSIGNED NOT NULL,
|
||||
used_by BIGINT UNSIGNED DEFAULT NULL,
|
||||
max_uses SMALLINT UNSIGNED DEFAULT 1,
|
||||
use_count SMALLINT UNSIGNED DEFAULT 0,
|
||||
note VARCHAR(255) DEFAULT '',
|
||||
expires_at DATETIME DEFAULT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY code (code),
|
||||
KEY created_by (created_by)
|
||||
) $charset;";
|
||||
dbDelta( $sql_invites );
|
||||
|
||||
// Benutzerdefinierte Profilfelder — Meta-Tabelle
|
||||
$sql_user_meta = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_user_meta (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
meta_key VARCHAR(60) NOT NULL,
|
||||
meta_value TEXT DEFAULT '',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY user_key (user_id, meta_key),
|
||||
KEY user_id (user_id)
|
||||
) $charset;";
|
||||
dbDelta( $sql_user_meta );
|
||||
|
||||
// Umfragen (Polls)
|
||||
$sql_polls = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_polls (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
thread_id BIGINT UNSIGNED NOT NULL,
|
||||
question VARCHAR(255) NOT NULL DEFAULT '',
|
||||
options TEXT NOT NULL DEFAULT '',
|
||||
multi TINYINT(1) DEFAULT 0,
|
||||
ends_at DATETIME DEFAULT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY thread_id (thread_id)
|
||||
) $charset;";
|
||||
dbDelta( $sql_polls );
|
||||
|
||||
$sql_poll_votes = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_poll_votes (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
poll_id BIGINT UNSIGNED NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
option_idx TINYINT UNSIGNED NOT NULL,
|
||||
voted_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY poll_user_option (poll_id, user_id, option_idx),
|
||||
KEY poll_id (poll_id)
|
||||
) $charset;";
|
||||
dbDelta( $sql_poll_votes );
|
||||
|
||||
// ── Thread-Präfixe ────────────────────────────────────────────────────
|
||||
$sql_prefixes = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_prefixes (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
label VARCHAR(60) NOT NULL,
|
||||
color VARCHAR(30) DEFAULT '#ffffff',
|
||||
bg_color VARCHAR(30) DEFAULT '#475569',
|
||||
sort_order INT DEFAULT 0,
|
||||
PRIMARY KEY (id)
|
||||
) $charset;";
|
||||
dbDelta( $sql_prefixes );
|
||||
|
||||
// ── Lesezeichen ───────────────────────────────────────────────────────
|
||||
$sql_bookmarks = "CREATE TABLE IF NOT EXISTS {$wpdb->prefix}forum_bookmarks (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
thread_id BIGINT UNSIGNED NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY user_thread (user_id, thread_id)
|
||||
) $charset;";
|
||||
dbDelta( $sql_bookmarks );
|
||||
|
||||
// ── prefix_id zu threads ──────────────────────────────────────────────
|
||||
self::maybe_add_column( "{$wpdb->prefix}forum_threads", 'prefix_id',
|
||||
"ALTER TABLE {$wpdb->prefix}forum_threads ADD COLUMN prefix_id BIGINT UNSIGNED DEFAULT NULL" );
|
||||
|
||||
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}forum_categories");
|
||||
if ( (int)$count === 0 ) {
|
||||
$wpdb->insert("{$wpdb->prefix}forum_categories", ['parent_id'=>0,'name'=>'Allgemein', 'slug'=>'allgemein', 'description'=>'Allgemeine Diskussionen','icon'=>'fas fa-home', 'sort_order'=>1]);
|
||||
@@ -311,10 +411,12 @@ class WBF_DB {
|
||||
$offset = ($page - 1) * $per_page;
|
||||
$status_sql = $include_archived ? '' : "AND t.status != 'archived'";
|
||||
return $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT t.*, u.display_name, u.avatar_url, u.username, u.role as author_role
|
||||
"SELECT t.*, u.display_name, u.avatar_url, u.username, u.role as author_role,
|
||||
p.label as prefix_label, p.color as prefix_color, p.bg_color as prefix_bg
|
||||
FROM {$wpdb->prefix}forum_threads t
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id = t.user_id
|
||||
WHERE t.category_id = %d $status_sql
|
||||
LEFT JOIN {$wpdb->prefix}forum_prefixes p ON p.id = t.prefix_id
|
||||
WHERE t.category_id = %d AND t.deleted_at IS NULL $status_sql
|
||||
ORDER BY t.pinned DESC, t.last_reply_at DESC
|
||||
LIMIT %d OFFSET %d",
|
||||
$category_id, $per_page, $offset
|
||||
@@ -351,7 +453,7 @@ class WBF_DB {
|
||||
public static function count_threads( $category_id ) {
|
||||
global $wpdb;
|
||||
return (int)$wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}forum_threads WHERE category_id=%d AND status != 'archived'",
|
||||
"SELECT COUNT(*) FROM {$wpdb->prefix}forum_threads WHERE category_id=%d AND status != 'archived' AND deleted_at IS NULL",
|
||||
$category_id
|
||||
));
|
||||
}
|
||||
@@ -393,9 +495,11 @@ class WBF_DB {
|
||||
global $wpdb;
|
||||
return $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT t.*, u.display_name, u.avatar_url, u.username, u.signature,
|
||||
u.post_count as author_posts, u.registered as author_registered, u.role as author_role
|
||||
u.post_count as author_posts, u.registered as author_registered, u.role as author_role,
|
||||
p.label as prefix_label, p.color as prefix_color, p.bg_color as prefix_bg
|
||||
FROM {$wpdb->prefix}forum_threads t
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id = t.user_id
|
||||
LEFT JOIN {$wpdb->prefix}forum_prefixes p ON p.id = t.prefix_id
|
||||
WHERE t.id = %d", $id
|
||||
));
|
||||
}
|
||||
@@ -447,7 +551,7 @@ class WBF_DB {
|
||||
u.post_count as author_posts, u.role as author_role, u.registered as author_registered
|
||||
FROM {$wpdb->prefix}forum_posts p
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id = p.user_id
|
||||
WHERE p.thread_id = %d
|
||||
WHERE p.thread_id = %d AND p.deleted_at IS NULL
|
||||
ORDER BY p.created_at ASC
|
||||
LIMIT %d OFFSET %d",
|
||||
$thread_id, $per_page, $offset
|
||||
@@ -885,9 +989,16 @@ class WBF_DB {
|
||||
|
||||
// ── Reaktionen ────────────────────────────────────────────────────────────
|
||||
|
||||
/** Erlaubte Reaktionen aus den Einstellungen holen */
|
||||
public static function get_allowed_reactions() {
|
||||
$saved = get_option('wbf_reactions', null);
|
||||
if ( $saved !== null && is_array($saved) && count($saved) > 0 ) return $saved;
|
||||
return ['👍','❤️','😂','😮','😢','😡']; // Defaults
|
||||
}
|
||||
|
||||
public static function set_reaction( $user_id, $object_id, $object_type, $reaction ) {
|
||||
global $wpdb;
|
||||
$allowed = ['👍','❤️','😂','😮','😢','😡'];
|
||||
$allowed = self::get_allowed_reactions();
|
||||
if ( ! in_array($reaction, $allowed, true) ) return false;
|
||||
|
||||
$existing = $wpdb->get_row( $wpdb->prepare(
|
||||
@@ -1134,4 +1245,491 @@ class WBF_DB {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ── Einladungen ───────────────────────────────────────────────────────────
|
||||
|
||||
public static function create_invite( $created_by, $max_uses = 1, $note = '', $expires_at = null ) {
|
||||
global $wpdb;
|
||||
$code = strtoupper( substr( bin2hex( random_bytes(6) ), 0, 10 ) );
|
||||
$wpdb->insert( "{$wpdb->prefix}forum_invites", [
|
||||
'code' => $code,
|
||||
'created_by' => (int) $created_by,
|
||||
'max_uses' => (int) $max_uses,
|
||||
'note' => sanitize_text_field( $note ),
|
||||
'expires_at' => $expires_at,
|
||||
] );
|
||||
return $code;
|
||||
}
|
||||
|
||||
public static function get_invite( $code ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}forum_invites WHERE code = %s",
|
||||
strtoupper( trim($code) )
|
||||
) );
|
||||
}
|
||||
|
||||
public static function verify_invite( $code ) {
|
||||
$inv = self::get_invite( $code );
|
||||
if ( ! $inv ) return false;
|
||||
if ( $inv->use_count >= $inv->max_uses ) return false;
|
||||
if ( $inv->expires_at && strtotime($inv->expires_at) < time() ) return false;
|
||||
return $inv;
|
||||
}
|
||||
|
||||
public static function use_invite( $code, $user_id ) {
|
||||
global $wpdb;
|
||||
$inv = self::verify_invite( $code );
|
||||
if ( ! $inv ) return false;
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"UPDATE {$wpdb->prefix}forum_invites
|
||||
SET use_count = use_count + 1, used_by = %d
|
||||
WHERE code = %s",
|
||||
(int) $user_id, strtoupper($code)
|
||||
) );
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function get_all_invites( $limit = 100 ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT i.*, u.display_name AS creator_name,
|
||||
uu.display_name AS used_name
|
||||
FROM {$wpdb->prefix}forum_invites i
|
||||
LEFT JOIN {$wpdb->prefix}forum_users u ON u.id = i.created_by
|
||||
LEFT JOIN {$wpdb->prefix}forum_users uu ON uu.id = i.used_by
|
||||
ORDER BY i.created_at DESC
|
||||
LIMIT %d",
|
||||
$limit
|
||||
) );
|
||||
}
|
||||
|
||||
public static function delete_invite( $id ) {
|
||||
global $wpdb;
|
||||
$wpdb->delete( "{$wpdb->prefix}forum_invites", ['id' => (int)$id] );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ── Thread-Abonnements ────────────────────────────────────────────────────
|
||||
|
||||
public static function subscribe( $user_id, $thread_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->replace("{$wpdb->prefix}forum_subscriptions", [
|
||||
'user_id' => (int)$user_id,
|
||||
'thread_id' => (int)$thread_id,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function unsubscribe( $user_id, $thread_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->delete("{$wpdb->prefix}forum_subscriptions", [
|
||||
'user_id' => (int)$user_id,
|
||||
'thread_id' => (int)$thread_id,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function is_subscribed( $user_id, $thread_id ) {
|
||||
global $wpdb;
|
||||
return (bool)$wpdb->get_var($wpdb->prepare(
|
||||
"SELECT id FROM {$wpdb->prefix}forum_subscriptions WHERE user_id=%d AND thread_id=%d",
|
||||
(int)$user_id, (int)$thread_id
|
||||
));
|
||||
}
|
||||
|
||||
public static function get_thread_subscribers( $thread_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT u.id, u.email, u.display_name
|
||||
FROM {$wpdb->prefix}forum_subscriptions s
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id = s.user_id
|
||||
WHERE s.thread_id = %d",
|
||||
(int)$thread_id
|
||||
));
|
||||
}
|
||||
|
||||
// ── Soft-Delete ───────────────────────────────────────────────────────────
|
||||
|
||||
public static function soft_delete_thread( $thread_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->update(
|
||||
"{$wpdb->prefix}forum_threads",
|
||||
['deleted_at' => current_time('mysql')],
|
||||
['id' => (int)$thread_id]
|
||||
);
|
||||
}
|
||||
|
||||
public static function soft_delete_post( $post_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->update(
|
||||
"{$wpdb->prefix}forum_posts",
|
||||
['deleted_at' => current_time('mysql')],
|
||||
['id' => (int)$post_id]
|
||||
);
|
||||
}
|
||||
|
||||
public static function restore_thread( $thread_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->update("{$wpdb->prefix}forum_threads", ['deleted_at'=>null], ['id'=>(int)$thread_id]);
|
||||
}
|
||||
|
||||
public static function restore_post( $post_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->update("{$wpdb->prefix}forum_posts", ['deleted_at'=>null], ['id'=>(int)$post_id]);
|
||||
}
|
||||
|
||||
public static function get_deleted_content( $limit = 50 ) {
|
||||
global $wpdb;
|
||||
$threads = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT 'thread' as type, t.id, t.title as content_preview, t.deleted_at,
|
||||
u.display_name, c.name as cat_name
|
||||
FROM {$wpdb->prefix}forum_threads t
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id=t.user_id
|
||||
LEFT JOIN {$wpdb->prefix}forum_categories c ON c.id=t.category_id
|
||||
WHERE t.deleted_at IS NOT NULL
|
||||
ORDER BY t.deleted_at DESC LIMIT %d", $limit
|
||||
));
|
||||
$posts = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT 'post' as type, p.id, LEFT(p.content,80) as content_preview, p.deleted_at,
|
||||
u.display_name, t.title as cat_name
|
||||
FROM {$wpdb->prefix}forum_posts p
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id=p.user_id
|
||||
LEFT JOIN {$wpdb->prefix}forum_threads t ON t.id=p.thread_id
|
||||
WHERE p.deleted_at IS NOT NULL
|
||||
ORDER BY p.deleted_at DESC LIMIT %d", $limit
|
||||
));
|
||||
return array_merge($threads, $posts);
|
||||
}
|
||||
|
||||
// ── Nutzungs-Statistiken ──────────────────────────────────────────────────
|
||||
|
||||
public static function get_activity_stats( $days = 30 ) {
|
||||
global $wpdb;
|
||||
$since = date('Y-m-d', strtotime("-{$days} days"));
|
||||
|
||||
$posts_per_day = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT DATE(created_at) as day, COUNT(*) as count
|
||||
FROM {$wpdb->prefix}forum_posts
|
||||
WHERE created_at >= %s AND deleted_at IS NULL
|
||||
GROUP BY DATE(created_at) ORDER BY day ASC",
|
||||
$since
|
||||
));
|
||||
$threads_per_day = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT DATE(created_at) as day, COUNT(*) as count
|
||||
FROM {$wpdb->prefix}forum_threads
|
||||
WHERE created_at >= %s AND deleted_at IS NULL
|
||||
GROUP BY DATE(created_at) ORDER BY day ASC",
|
||||
$since
|
||||
));
|
||||
$registrations = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT DATE(registered) as day, COUNT(*) as count
|
||||
FROM {$wpdb->prefix}forum_users
|
||||
WHERE registered >= %s
|
||||
GROUP BY DATE(registered) ORDER BY day ASC",
|
||||
$since
|
||||
));
|
||||
$top_posters = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT u.display_name, u.role, COUNT(p.id) as post_count
|
||||
FROM {$wpdb->prefix}forum_posts p
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id=p.user_id
|
||||
WHERE p.created_at >= %s AND p.deleted_at IS NULL
|
||||
GROUP BY u.id ORDER BY post_count DESC LIMIT 10",
|
||||
$since
|
||||
));
|
||||
$active_hours = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT HOUR(created_at) as hour, COUNT(*) as count
|
||||
FROM {$wpdb->prefix}forum_posts
|
||||
WHERE created_at >= %s AND deleted_at IS NULL
|
||||
GROUP BY HOUR(created_at) ORDER BY hour ASC",
|
||||
$since
|
||||
));
|
||||
|
||||
return compact('posts_per_day','threads_per_day','registrations','top_posters','active_hours');
|
||||
}
|
||||
|
||||
// ── Benutzerdefinierte Profilfelder ───────────────────────────────────────
|
||||
|
||||
public static function get_profile_field_defs() {
|
||||
$fields = get_option( 'wbf_profile_fields', [] );
|
||||
return is_array( $fields ) ? $fields : [];
|
||||
}
|
||||
|
||||
public static function save_profile_field_defs( $fields ) {
|
||||
update_option( 'wbf_profile_fields', $fields );
|
||||
}
|
||||
|
||||
public static function get_user_meta( $user_id ) {
|
||||
global $wpdb;
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT meta_key, meta_value FROM {$wpdb->prefix}forum_user_meta WHERE user_id = %d",
|
||||
(int) $user_id
|
||||
) );
|
||||
$out = [];
|
||||
foreach ( $rows as $r ) $out[ $r->meta_key ] = $r->meta_value;
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function set_user_meta( $user_id, $key, $value ) {
|
||||
global $wpdb;
|
||||
$wpdb->replace(
|
||||
"{$wpdb->prefix}forum_user_meta",
|
||||
[ 'user_id' => (int) $user_id, 'meta_key' => $key, 'meta_value' => $value ],
|
||||
[ '%d', '%s', '%s' ]
|
||||
);
|
||||
}
|
||||
|
||||
public static function delete_user_meta_all( $user_id ) {
|
||||
global $wpdb;
|
||||
$wpdb->delete( "{$wpdb->prefix}forum_user_meta", [ 'user_id' => (int) $user_id ] );
|
||||
}
|
||||
|
||||
// ── Zeitlich begrenzte Sperren ────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Setzt eine zeitlich begrenzte Sperre für einen User.
|
||||
* Speichert die vorherige Rolle in pre_ban_role.
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param string $until MySQL DATETIME z.B. '2025-12-31 23:59:00'
|
||||
* @param string $reason Sperrgrund
|
||||
*/
|
||||
public static function temp_ban( $user_id, $until, $reason = '' ) {
|
||||
global $wpdb;
|
||||
$user = self::get_user( (int) $user_id );
|
||||
if ( ! $user || $user->role === 'superadmin' ) return false;
|
||||
|
||||
$wpdb->update(
|
||||
"{$wpdb->prefix}forum_users",
|
||||
[
|
||||
'pre_ban_role' => $user->role !== 'banned' ? $user->role : ( $user->pre_ban_role ?: 'member' ),
|
||||
'role' => 'banned',
|
||||
'ban_reason' => $reason,
|
||||
'ban_until' => $until,
|
||||
],
|
||||
[ 'id' => (int) $user_id ],
|
||||
[ '%s', '%s', '%s', '%s' ],
|
||||
[ '%d' ]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hebt abgelaufene Sperren auf — läuft per WP-Cron täglich.
|
||||
* Gibt Anzahl entsperrter User zurück.
|
||||
*/
|
||||
public static function check_expired_bans() {
|
||||
global $wpdb;
|
||||
$expired = $wpdb->get_results(
|
||||
"SELECT id, pre_ban_role FROM {$wpdb->prefix}forum_users
|
||||
WHERE role = 'banned'
|
||||
AND ban_until IS NOT NULL
|
||||
AND ban_until <= NOW()"
|
||||
);
|
||||
$count = 0;
|
||||
foreach ( $expired as $u ) {
|
||||
$restore = ! empty( $u->pre_ban_role ) ? $u->pre_ban_role : 'member';
|
||||
$wpdb->update(
|
||||
"{$wpdb->prefix}forum_users",
|
||||
[ 'role' => $restore, 'ban_reason' => '', 'ban_until' => null, 'pre_ban_role' => '' ],
|
||||
[ 'id' => (int) $u->id ],
|
||||
[ '%s', '%s', null, '%s' ],
|
||||
[ '%d' ]
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
// ── Umfragen (Polls) ──────────────────────────────────────────────────────
|
||||
|
||||
public static function create_poll( $thread_id, $question, $options, $multi = false, $ends_at = null ) {
|
||||
global $wpdb;
|
||||
$wpdb->insert( "{$wpdb->prefix}forum_polls", [
|
||||
'thread_id' => (int) $thread_id,
|
||||
'question' => sanitize_text_field( $question ),
|
||||
'options' => wp_json_encode( array_values( $options ) ),
|
||||
'multi' => $multi ? 1 : 0,
|
||||
'ends_at' => $ends_at,
|
||||
]);
|
||||
return $wpdb->insert_id;
|
||||
}
|
||||
|
||||
public static function get_poll( $thread_id ) {
|
||||
global $wpdb;
|
||||
$row = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}forum_polls WHERE thread_id = %d", (int) $thread_id
|
||||
) );
|
||||
if ( ! $row ) return null;
|
||||
$row->options = json_decode( $row->options, true ) ?: [];
|
||||
return $row;
|
||||
}
|
||||
|
||||
public static function get_poll_results( $poll_id ) {
|
||||
global $wpdb;
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT option_idx, COUNT(*) AS votes FROM {$wpdb->prefix}forum_poll_votes
|
||||
WHERE poll_id = %d GROUP BY option_idx", (int) $poll_id
|
||||
) );
|
||||
$out = [];
|
||||
foreach ( $rows as $r ) $out[(int)$r->option_idx] = (int)$r->votes;
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function get_user_votes( $poll_id, $user_id ) {
|
||||
global $wpdb;
|
||||
return array_map( fn($r) => (int)$r->option_idx,
|
||||
$wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT option_idx FROM {$wpdb->prefix}forum_poll_votes WHERE poll_id=%d AND user_id=%d",
|
||||
(int) $poll_id, (int) $user_id
|
||||
) )
|
||||
);
|
||||
}
|
||||
|
||||
public static function vote_poll( $poll_id, $user_id, $option_idxs ) {
|
||||
global $wpdb;
|
||||
$poll = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT ends_at, multi FROM {$wpdb->prefix}forum_polls WHERE id=%d", (int) $poll_id
|
||||
) );
|
||||
if ( ! $poll ) return false;
|
||||
if ( $poll->ends_at && strtotime( $poll->ends_at ) < time() ) return false;
|
||||
if ( ! empty( self::get_user_votes( $poll_id, $user_id ) ) ) return false;
|
||||
if ( ! $poll->multi ) $option_idxs = [ (int)$option_idxs[0] ];
|
||||
foreach ( $option_idxs as $idx ) {
|
||||
$wpdb->insert( "{$wpdb->prefix}forum_poll_votes", [
|
||||
'poll_id' => (int)$poll_id, 'user_id' => (int)$user_id, 'option_idx' => (int)$idx,
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function delete_poll( $thread_id ) {
|
||||
global $wpdb;
|
||||
$poll = self::get_poll( $thread_id );
|
||||
if ( ! $poll ) return;
|
||||
$wpdb->delete( "{$wpdb->prefix}forum_poll_votes", [ 'poll_id' => $poll->id ] );
|
||||
$wpdb->delete( "{$wpdb->prefix}forum_polls", [ 'id' => $poll->id ] );
|
||||
}
|
||||
|
||||
// ── Thread-Präfixe ────────────────────────────────────────────────────────
|
||||
|
||||
public static function get_prefixes() {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results(
|
||||
"SELECT * FROM {$wpdb->prefix}forum_prefixes ORDER BY sort_order ASC, id ASC"
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_prefix( $id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM {$wpdb->prefix}forum_prefixes WHERE id=%d", (int)$id
|
||||
));
|
||||
}
|
||||
|
||||
public static function create_prefix( $data ) {
|
||||
global $wpdb;
|
||||
$wpdb->insert( "{$wpdb->prefix}forum_prefixes", $data );
|
||||
return $wpdb->insert_id;
|
||||
}
|
||||
|
||||
public static function update_prefix( $id, $data ) {
|
||||
global $wpdb;
|
||||
$wpdb->update( "{$wpdb->prefix}forum_prefixes", $data, ['id' => (int)$id] );
|
||||
}
|
||||
|
||||
public static function delete_prefix( $id ) {
|
||||
global $wpdb;
|
||||
// Präfix bei betroffenen Threads entfernen
|
||||
$wpdb->update( "{$wpdb->prefix}forum_threads", ['prefix_id' => null], ['prefix_id' => (int)$id] );
|
||||
$wpdb->delete( "{$wpdb->prefix}forum_prefixes", ['id' => (int)$id] );
|
||||
}
|
||||
|
||||
// ── Lesezeichen ───────────────────────────────────────────────────────────
|
||||
|
||||
public static function is_bookmarked( $user_id, $thread_id ) {
|
||||
global $wpdb;
|
||||
return (bool)$wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT id FROM {$wpdb->prefix}forum_bookmarks WHERE user_id=%d AND thread_id=%d",
|
||||
(int)$user_id, (int)$thread_id
|
||||
));
|
||||
}
|
||||
|
||||
public static function toggle_bookmark( $user_id, $thread_id ) {
|
||||
global $wpdb;
|
||||
if ( self::is_bookmarked( $user_id, $thread_id ) ) {
|
||||
$wpdb->delete( "{$wpdb->prefix}forum_bookmarks", [
|
||||
'user_id' => (int)$user_id,
|
||||
'thread_id' => (int)$thread_id,
|
||||
]);
|
||||
return false; // removed
|
||||
}
|
||||
$wpdb->insert( "{$wpdb->prefix}forum_bookmarks", [
|
||||
'user_id' => (int)$user_id,
|
||||
'thread_id' => (int)$thread_id,
|
||||
]);
|
||||
return true; // added
|
||||
}
|
||||
|
||||
public static function get_user_bookmarks( $user_id, $limit = 50 ) {
|
||||
global $wpdb;
|
||||
return $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT t.id, t.title, t.reply_count, t.views, t.created_at, t.last_reply_at,
|
||||
t.prefix_id, t.status, t.pinned,
|
||||
u.display_name, u.avatar_url, u.role as author_role,
|
||||
c.name as cat_name, c.slug as cat_slug,
|
||||
b.created_at as bookmarked_at
|
||||
FROM {$wpdb->prefix}forum_bookmarks b
|
||||
JOIN {$wpdb->prefix}forum_threads t ON t.id = b.thread_id
|
||||
JOIN {$wpdb->prefix}forum_users u ON u.id = t.user_id
|
||||
JOIN {$wpdb->prefix}forum_categories c ON c.id = t.category_id
|
||||
WHERE b.user_id = %d AND t.deleted_at IS NULL
|
||||
ORDER BY b.created_at DESC
|
||||
LIMIT %d",
|
||||
(int)$user_id, $limit
|
||||
));
|
||||
}
|
||||
|
||||
// ── Wortfilter ────────────────────────────────────────────────────────────
|
||||
|
||||
public static function get_word_filter() {
|
||||
$raw = get_option( 'wbf_word_filter', '' );
|
||||
if ( empty( $raw ) ) return [];
|
||||
return array_values( array_filter( array_map( 'trim', explode( "\n", $raw ) ) ) );
|
||||
}
|
||||
|
||||
public static function apply_word_filter( $text ) {
|
||||
$words = self::get_word_filter();
|
||||
if ( empty( $words ) ) return $text;
|
||||
foreach ( $words as $word ) {
|
||||
if ( empty($word) ) continue;
|
||||
$replacement = str_repeat( '*', mb_strlen($word) );
|
||||
$text = preg_replace( '/\b' . preg_quote($word, '/') . '\b/iu', $replacement, $text );
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
// ── Flood Control ─────────────────────────────────────────────────────────
|
||||
|
||||
public static function check_flood( $user_id ) {
|
||||
$interval = (int)( wbf_get_settings()['flood_interval'] ?? 0 );
|
||||
if ( $interval <= 0 ) return true; // deaktiviert
|
||||
$key = 'wbf_flood_' . (int)$user_id;
|
||||
$last = get_transient( $key );
|
||||
if ( $last !== false ) {
|
||||
return false; // noch gesperrt
|
||||
}
|
||||
set_transient( $key, time(), $interval );
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function flood_remaining( $user_id ) {
|
||||
$interval = (int)( wbf_get_settings()['flood_interval'] ?? 0 );
|
||||
if ( $interval <= 0 ) return 0;
|
||||
$key = 'wbf_flood_' . (int)$user_id;
|
||||
$last = get_transient( $key );
|
||||
if ( $last === false ) return 0;
|
||||
// Transients speichern keine genaue Restzeit — wir schätzen über $interval
|
||||
return $interval;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user