Update from Git Manager GUI

This commit is contained in:
2026-03-21 18:47:28 +01:00
parent 5d7a4743d7
commit 6fff4f9dc2
4 changed files with 1710 additions and 37 deletions

View File

@@ -30,6 +30,14 @@ class WBF_Shortcodes {
return WBF_Roles::badge( $role );
}
public static function render_prefix( $thread ) {
if ( empty($thread->prefix_label) ) return '';
$label = esc_html($thread->prefix_label);
$color = esc_attr($thread->prefix_color ?? '#fff');
$bg = esc_attr($thread->prefix_bg ?? '#475569');
return "<span class=\"wbf-prefix-badge\" style=\"color:{$color};background:{$bg}\">{$label}</span>";
}
public static function render_tags( $tags, $small = false ) {
if ( empty($tags) ) return '';
$cls = $small ? 'wbf-tag wbf-tag--sm' : 'wbf-tag';
@@ -54,7 +62,7 @@ class WBF_Shortcodes {
}
private static function reaction_bar( $object_id, $object_type, $current_user ) {
$emojis = ['👍','❤️','😂','😮','😢','😡'];
$emojis = WBF_DB::get_allowed_reactions();
$user_id = $current_user ? (int)$current_user->id : 0;
$data = WBF_DB::get_reactions($object_id, $object_type, $user_id);
$counts = $data['counts'];
@@ -147,8 +155,39 @@ class WBF_Shortcodes {
wp_redirect( wbf_get_forum_url() );
exit;
}
// ── Wartungsmodus — zentraler Check vor allem anderen ────────────────
$wbf_current_user = WBF_Auth::get_current_user();
$wbf_maint = wbf_get_settings()['maintenance_mode'] ?? '0';
if ( $wbf_maint === '1' ) {
$is_staff = $wbf_current_user && WBF_Roles::level($wbf_current_user->role) >= 50;
if ( ! $is_staff ) {
return self::view_maintenance();
}
}
if (isset($_GET['forum_members'])) return self::view_members();
// Einladungscode aus URL vorausfüllen
if (isset($_GET['wbf_invite'])) {
$inv_code = strtoupper(sanitize_text_field($_GET['wbf_invite']));
if (!WBF_DB::verify_invite($inv_code)) {
// Ungültiger Code — zeige Meldung
ob_start(); ?>
<div class="wbf-wrap"><?php self::render_topbar(null); ?>
<div class="wbf-container wbf-mt">
<div class="wbf-notice wbf-notice--warning">
<i class="fas fa-exclamation-triangle"></i>
Dieser Einladungslink ist ungültig oder bereits abgelaufen.
</div>
<div style="margin-top:1rem">
<a href="<?php echo esc_url(wbf_get_forum_url()); ?>" class="wbf-btn wbf-btn--sm">← Zurück zum Forum</a>
</div>
</div></div>
<?php return ob_get_clean();
}
}
if (isset($_GET['wbf_reset_token'])) return self::view_reset_password();
if (isset($_GET['forum_rules'])) return self::view_rules();
if (isset($_GET['forum_thread'])) return self::view_thread();
if (isset($_GET['forum_cat'])) return self::view_category();
if (isset($_GET['forum_profile'])) return self::view_profile();
@@ -158,6 +197,19 @@ class WBF_Shortcodes {
return self::view_home();
}
/** Darf der Nutzer diese Kategorie sehen? */
private static function can_see_category( $user, $cat ) {
// Gäste: guest_visible prüfen
if ( ! $user && (int)($cat->guest_visible ?? 1) === 0 ) return false;
// Min-Rolle: Nutzer muss mindestens diese Rolle haben um die Kategorie zu sehen
if ( $cat->min_role && $cat->min_role !== 'member' ) {
if ( ! $user ) return false; // nicht eingeloggt → versteckt wenn min_role > member
if ( WBF_Roles::level($user->role) < WBF_Roles::level($cat->min_role) ) return false;
}
return true;
}
// ── HOME ──────────────────────────────────────────────────────────────────
private static function view_home() {
@@ -195,11 +247,15 @@ class WBF_Shortcodes {
<div class="wbf-section-header">
<h2><?php echo esc_html(wbf_get_settings()['section_cats']); ?></h2>
<?php if (WBF_DB::can($current,'create_thread')): ?>
<button class="wbf-btn wbf-btn--primary" onclick="wbfShowNewThread()"><i class="fas fa-plus"></i> <?php echo esc_html(wbf_get_settings()['btn_new_thread']); ?></button>
<div style="display:flex;gap:.5rem;flex-wrap:wrap">
<button class="wbf-btn wbf-btn--outline-poll" onclick="wbfShowNewPoll()"><i class="fas fa-chart-bar"></i> Neue Umfrage</button>
<button class="wbf-btn wbf-btn--primary" onclick="wbfShowNewThread()"><i class="fas fa-plus"></i> <?php echo esc_html(wbf_get_settings()['btn_new_thread']); ?></button>
</div>
<?php endif; ?>
</div>
<div class="wbf-cat-group-list">
<?php foreach ($tree as $parent): ?>
<?php foreach ($tree as $parent):
if (!self::can_see_category($current, $parent)) continue; ?>
<div class="wbf-cat-group">
<div class="wbf-cat-parent">
<div class="wbf-cat-parent__icon"><i class="<?php echo esc_attr($parent->icon); ?>"></i></div>
@@ -217,7 +273,8 @@ class WBF_Shortcodes {
</div>
<?php if (!empty($parent->children)): ?>
<div class="wbf-cat-children">
<?php foreach ($parent->children as $child): ?>
<?php foreach ($parent->children as $child):
if (!self::can_see_category($current, $child)) continue; ?>
<div class="wbf-cat-child">
<div class="wbf-cat-child__icon"><i class="<?php echo esc_attr($child->icon); ?>"></i></div>
<div class="wbf-cat-child__body">
@@ -286,6 +343,7 @@ class WBF_Shortcodes {
</div>
</div>
<?php self::render_new_thread_modal(WBF_DB::get_categories_flat(), $current); ?>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
</div>
<?php return ob_get_clean();
@@ -298,11 +356,30 @@ class WBF_Shortcodes {
$cat = WBF_DB::get_category($slug);
if (!$cat) return '<p class="wbf-notice">Kategorie nicht gefunden.</p>';
$current = WBF_Auth::get_current_user();
// Zugang prüfen — Gäste + Min-Rolle
if (!self::can_see_category($current, $cat)) {
ob_start(); ?>
<div class="wbf-wrap"><?php self::render_topbar($current); ?>
<div class="wbf-container wbf-mt">
<div class="wbf-notice wbf-notice--warning">
<i class="fas fa-lock"></i>
<?php if (!$current): ?>
Diese Kategorie ist nur für eingeloggte Mitglieder sichtbar.
<a href="#" class="wbf-login-link" style="margin-left:.5rem;font-weight:700">Jetzt einloggen</a>
<?php else: ?>
Du hast keine Berechtigung um diese Kategorie zu sehen.
<?php endif; ?>
</div>
</div></div>
<?php return ob_get_clean();
}
$page = max(1,(int)($_GET['fp']??1));
$threads = WBF_DB::get_threads($cat->id, $page);
$total = WBF_DB::count_threads($cat->id);
$pages = ceil($total / 20) ?: 1;
$current = WBF_Auth::get_current_user();
$children = WBF_DB::get_child_categories($cat->id);
$crumbs = WBF_DB::get_category_breadcrumb($cat);
@@ -322,12 +399,16 @@ class WBF_Shortcodes {
<div><h2><i class="<?php echo esc_attr($cat->icon); ?>"></i> <?php echo esc_html($cat->name); ?></h2>
<p class="wbf-muted"><?php echo esc_html($cat->description); ?></p></div>
<?php if (WBF_DB::can($current,'create_thread') && WBF_DB::can_post_in($current,$cat)): ?>
<button class="wbf-btn wbf-btn--primary" onclick="wbfShowNewThread(<?php echo $cat->id; ?>)"><i class="fas fa-plus"></i> <?php echo esc_html(wbf_get_settings()['btn_new_thread']); ?></button>
<div style="display:flex;gap:.5rem;flex-wrap:wrap">
<button class="wbf-btn wbf-btn--outline-poll" onclick="wbfShowNewPoll(<?php echo $cat->id; ?>)"><i class="fas fa-chart-bar"></i> Neue Umfrage</button>
<button class="wbf-btn wbf-btn--primary" onclick="wbfShowNewThread(<?php echo $cat->id; ?>)"><i class="fas fa-plus"></i> <?php echo esc_html(wbf_get_settings()['btn_new_thread']); ?></button>
</div>
<?php endif; ?>
</div>
<?php if (!empty($children)): ?>
<div class="wbf-subcat-list">
<?php foreach ($children as $child): ?>
<?php foreach ($children as $child):
if (!self::can_see_category($current, $child)) continue; ?>
<a href="?forum_cat=<?php echo esc_attr($child->slug); ?>" class="wbf-subcat-card">
<div class="wbf-subcat-card__icon"><i class="<?php echo esc_attr($child->icon); ?>"></i></div>
<div class="wbf-subcat-card__body">
@@ -347,12 +428,14 @@ class WBF_Shortcodes {
$liked = $current ? WBF_DB::has_liked($current->id,$t->id,'thread') : false; ?>
<div class="wbf-thread-row<?php echo $t->pinned?' wbf-thread-row--pinned':''; ?>"
data-thread-id="<?php echo (int)$t->id; ?>"
data-last-reply="<?php echo esc_attr($t->last_reply_at); ?>">
data-last-reply="<?php echo esc_attr($t->last_reply_at); ?>"
data-preview="<?php echo esc_attr(mb_substr(strip_tags(WBF_BBCode::render($t->content)),0,160)); ?>">
<div class="wbf-thread-row__avatar"><?php echo self::avatar($t->avatar_url,$t->display_name); ?></div>
<div class="wbf-thread-row__body">
<div class="wbf-thread-row__top">
<?php if ($t->pinned): ?><span class="wbf-pin"><i class="fas fa-thumbtack"></i></span><?php endif; ?>
<?php if ($t->status==='closed'): ?><span class="wbf-badge wbf-badge--closed"><i class="fas fa-lock"></i> Geschlossen</span><?php endif; ?>
<?php echo self::render_prefix($t); ?>
<a href="?forum_thread=<?php echo (int)$t->id; ?>" class="wbf-thread-row__title"><?php echo esc_html($t->title); ?></a>
<span class="wbf-new-badge" style="display:none"><i class="fas fa-circle-dot"></i> Neu</span>
</div>
@@ -419,6 +502,7 @@ class WBF_Shortcodes {
<?php endif; endif; ?>
<?php self::render_new_thread_modal(WBF_DB::get_categories_flat(),$current,$cat->id); ?>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
</div>
<?php return ob_get_clean();
@@ -427,9 +511,33 @@ class WBF_Shortcodes {
// ── THREAD ────────────────────────────────────────────────────────────────
private static function view_thread() {
// Wartungsmodus
$maint6 = wbf_get_settings()['maintenance_mode'] ?? '0';
$cur6 = WBF_Auth::get_current_user();
if ( $maint6 === '1' && ( !$cur6 || WBF_Roles::level($cur6->role) < 50 ) ) {
return self::view_maintenance();
}
$id = (int)($_GET['forum_thread'] ?? 0);
$thread = WBF_DB::get_thread($id);
if (!$thread) return '<p class="wbf-notice">Thread nicht gefunden.</p>';
// Kategorie-Zugang prüfen (Gäste + Min-Rolle)
$cat6 = WBF_DB::get_category($thread->category_id);
if ($cat6 && !self::can_see_category($cur6, $cat6)) {
ob_start(); ?>
<div class="wbf-wrap"><?php self::render_topbar($cur6); ?>
<div class="wbf-container wbf-mt">
<div class="wbf-notice wbf-notice--warning">
<i class="fas fa-lock"></i>
<?php if (!$cur6): ?>
Dieser Thread ist nur für eingeloggte Mitglieder sichtbar.
<a href="#" class="wbf-login-link" style="margin-left:.5rem;font-weight:700">Jetzt einloggen</a>
<?php else: ?>
Du hast keine Berechtigung um diesen Thread zu sehen.
<?php endif; ?>
</div>
</div></div>
<?php return ob_get_clean();
}
global $wpdb;
$wpdb->query($wpdb->prepare("UPDATE {$wpdb->prefix}forum_threads SET views=views+1 WHERE id=%d",$id));
@@ -462,18 +570,101 @@ class WBF_Shortcodes {
<?php if ($thread->pinned): ?><i class="fas fa-thumbtack wbf-pin-icon"></i><?php endif; ?>
<?php if ($thread->status==='closed'): ?><i class="fas fa-lock" style="color:var(--c-muted);font-size:1rem;margin-right:.35rem"></i><?php endif; ?>
<?php if ($thread->status==='archived'): ?><span class="wbf-badge wbf-badge--archived"><i class="fas fa-box-archive"></i> Archiviert</span><?php endif; ?>
<?php echo self::render_prefix($thread); ?>
<?php echo esc_html($thread->title); ?>
</h1>
<div class="wbf-thread-header-meta">
<?php echo self::like_btn($id,'thread',$thread->like_count,$t_liked); ?>
<?php if ($current): $is_bm = WBF_DB::is_bookmarked($current->id,$id); ?>
<button class="wbf-bookmark-btn<?php echo $is_bm?' wbf-bookmarked':''; ?>" data-thread="<?php echo $id; ?>" title="<?php echo $is_bm?'Lesezeichen entfernen':'Lesezeichen hinzufügen'; ?>">
<i class="fa<?php echo $is_bm?'s':'r'; ?> fa-bookmark"></i>
</button>
<?php endif; ?>
<span><i class="fas fa-comment-dots"></i> <?php echo (int)$thread->reply_count; ?> Antworten</span>
<span><i class="fas fa-eye"></i> <?php echo (int)$thread->views; ?> Views</span>
</div>
<?php $thread_tags = WBF_DB::get_thread_tags($id); echo self::render_tags($thread_tags); ?>
</div>
</div>
<div class="wbf-posts" id="wbfPosts">
<!-- OP -->
<!-- Poll (optional) -->
<?php
$poll = WBF_DB::get_poll($id);
$can_add_poll = $current && (int)$current->id === (int)$thread->user_id && !$poll && $thread->status === 'open';
if ($can_add_poll): ?>
<div style="margin-bottom:1rem">
<button class="wbf-btn wbf-btn--sm wbf-btn--outline" id="wbfOpenPollModal">
<i class="fas fa-chart-bar"></i> Umfrage hinzufügen
</button>
</div>
<?php endif;
if ($poll):
$results = WBF_DB::get_poll_results($poll->id);
$my_votes = $current ? WBF_DB::get_user_votes($poll->id, $current->id) : [];
$total = array_sum($results);
$voted = !empty($my_votes);
$expired = $poll->ends_at && strtotime($poll->ends_at) < time();
$show_results = $voted || $expired || !$current;
?>
<div class="wbf-poll" id="wbfPoll-<?php echo (int)$poll->id; ?>" data-poll-id="<?php echo (int)$poll->id; ?>" data-multi="<?php echo (int)$poll->multi; ?>">
<div class="wbf-poll__header">
<i class="fas fa-chart-bar"></i>
<span class="wbf-poll__title"><?php echo esc_html($poll->question); ?></span>
<?php if ($poll->multi): ?><span class="wbf-poll__badge">Mehrfachauswahl</span><?php endif; ?>
<?php if ($expired): ?><span class="wbf-poll__badge wbf-poll__badge--ended">Beendet</span><?php endif; ?>
</div>
<div class="wbf-poll__body">
<?php if ($show_results): ?>
<!-- Ergebnisse -->
<?php foreach ($poll->options as $i => $opt):
$votes = $results[$i] ?? 0;
$pct = $total > 0 ? round($votes / $total * 100) : 0;
$mine = in_array($i, $my_votes);
?>
<div class="wbf-poll__result<?php echo $mine?' wbf-poll__result--mine':''; ?>">
<div class="wbf-poll__result-bar" style="width:<?php echo $pct; ?>%"></div>
<div class="wbf-poll__result-content">
<span class="wbf-poll__result-label"><?php if($mine): ?><i class="fas fa-check-circle" style="color:var(--c-primary)"></i> <?php endif; ?><?php echo esc_html($opt); ?></span>
<span class="wbf-poll__result-pct"><?php echo $pct; ?>% <span style="color:var(--c-muted);font-size:.75em">(<?php echo $votes; ?>)</span></span>
</div>
</div>
<?php endforeach; ?>
<div class="wbf-poll__footer">
<i class="fas fa-users"></i> <?php echo $total; ?> Stimme<?php echo $total!=1?'n':''; ?>
<?php if ($poll->ends_at && !$expired): ?>
· <i class="fas fa-clock"></i> Endet <?php echo esc_html(date_i18n('d.m.Y \u\m H:i \U\h\r', strtotime($poll->ends_at))); ?>
<?php elseif ($expired): ?>
· <i class="fas fa-flag-checkered"></i> Abgestimmt
<?php endif; ?>
</div>
<?php else: ?>
<!-- Abstimmung -->
<form class="wbf-poll__form" data-poll-id="<?php echo (int)$poll->id; ?>">
<?php foreach ($poll->options as $i => $opt): ?>
<label class="wbf-poll__option">
<input type="<?php echo $poll->multi?'checkbox':'radio'; ?>"
name="wbf_poll_option" value="<?php echo $i; ?>"
style="accent-color:var(--c-primary)">
<?php echo esc_html($opt); ?>
</label>
<?php endforeach; ?>
<div style="display:flex;align-items:center;gap:.75rem;margin-top:.75rem">
<button type="submit" class="wbf-btn wbf-btn--sm wbf-btn--primary">
<i class="fas fa-vote-yea"></i> Abstimmen
</button>
<span class="wbf-poll__msg wbf-msg"></span>
</div>
</form>
<?php if ($poll->ends_at): ?>
<div class="wbf-poll__footer">
<i class="fas fa-clock"></i> Endet <?php echo esc_html(date_i18n('d.m.Y \u\m H:i \U\h\r', strtotime($poll->ends_at))); ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php
$op_reported = $current ? WBF_DB::has_reported($current->id, $id, 'thread') : false;
$op_can_edit = $current && ((int)$current->id === (int)$thread->user_id || WBF_DB::can($current,'delete_post'));
@@ -508,7 +699,16 @@ class WBF_Shortcodes {
<div class="wbf-post__footer">
<span class="wbf-post__date"><?php echo self::time_ago($thread->created_at); ?></span>
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap">
<?php if ($current && WBF_DB::can($current,'post') && $thread->status !== 'closed'): ?>
<?php if ($current): ?>
<?php $is_subbed = WBF_DB::is_subscribed($current->id, $id); ?>
<button class="wbf-subscribe-btn wbf-btn wbf-btn--sm<?php echo $is_subbed?' wbf-btn--primary':''; ?>"
data-thread="<?php echo (int)$id; ?>"
title="<?php echo $is_subbed?'Abonnement entfernen':'Thread abonnieren'; ?>">
<i class="fas fa-bell<?php echo $is_subbed?'':'-slash'; ?>"></i>
<?php echo $is_subbed?'Abonniert':'Abonnieren'; ?>
</button>
<?php endif; ?>
<?php if ($current && WBF_DB::can($current,'post') && $thread->status !== 'closed'): ?>
<button class="wbf-quote-btn"
data-source="wbf-thread-content-<?php echo (int)$id; ?>"
data-author="<?php echo esc_attr($thread->display_name); ?>"
@@ -575,9 +775,11 @@ class WBF_Shortcodes {
<div class="wbf-notice wbf-notice--warning"><i class="fas fa-lock"></i> Dieser Thread ist geschlossen.</div>
<?php endif; ?>
</div>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
<?php self::render_report_modal(); ?>
<?php if (WBF_DB::can($current,'manage_cats')): self::render_move_modal(WBF_DB::get_categories_flat(), $id); endif; ?>
<?php if ($can_add_poll): self::render_poll_modal($id); endif; ?>
</div>
<?php return ob_get_clean();
}
@@ -656,6 +858,18 @@ class WBF_Shortcodes {
$profile = $profile_id ? WBF_DB::get_user($profile_id) : $current;
if (!$profile) return '<p class="wbf-notice">Profil nicht gefunden.</p>';
$is_own = $current && $current->id == $profile->id;
$is_staff = $current && WBF_Roles::level($current->role) >= 50;
// Profil-Sichtbarkeit prüfen
if (!$is_own && !$is_staff && (int)($profile->profile_public ?? 1) === 0) {
ob_start(); ?>
<div class="wbf-wrap"><?php self::render_topbar($current); ?>
<div class="wbf-container wbf-mt">
<div class="wbf-notice wbf-notice--warning">
<i class="fas fa-user-lock"></i> Dieses Profil ist nicht öffentlich.
</div>
</div></div>
<?php return ob_get_clean();
}
$user_posts = WBF_DB::get_user_posts( $profile->id, 50 );
ob_start(); ?>
@@ -727,6 +941,33 @@ class WBF_Shortcodes {
</div>
<?php endif; ?>
<!-- Benutzerdefinierte Profilfelder (öffentliche) -->
<?php
$cf_defs_pub = WBF_DB::get_profile_field_defs();
$cf_vals_pub = WBF_DB::get_user_meta( $profile->id );
foreach ( $cf_defs_pub as $def ):
if ( ! $is_own && empty($def['public']) ) continue;
$val = trim( $cf_vals_pub[ $def['key'] ] ?? '' );
if ( $val === '' ) continue;
?>
<div class="wbf-profile-sidebar__section">
<span class="wbf-profile-sidebar__section-label">
<i class="fas fa-<?php echo $def['type']==='url'?'link':($def['type']==='number'?'hashtag':'tag'); ?>"></i>
<?php echo esc_html($def['label']); ?>
</span>
<?php if ( $def['type'] === 'url' ): ?>
<a href="<?php echo esc_url($val); ?>" target="_blank" rel="noopener noreferrer"
style="color:var(--c-primary);font-size:.85rem;word-break:break-all">
<?php echo esc_html( mb_strtolower( preg_replace('#^https?://#i','',$val) ) ); ?>
</a>
<?php elseif ( $def['type'] === 'textarea' ): ?>
<p style="font-size:.85rem"><?php echo nl2br(esc_html($val)); ?></p>
<?php else: ?>
<p style="font-size:.85rem"><?php echo esc_html($val); ?></p>
<?php endif; ?>
</div>
<?php endforeach; ?>
</aside>
<!-- ── MAIN ────────────────────────────────────────────── -->
@@ -754,7 +995,17 @@ class WBF_Shortcodes {
</div>
<div class="wbf-form-row">
<label>Signatur <small>(max. 300 Zeichen)</small></label>
<textarea id="wbfEditSignature" rows="2" maxlength="300" placeholder="Deine Signatur…"><?php echo esc_textarea($profile->signature ?? ''); ?></textarea>
<div class="wbf-form-row" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.75rem">
<label style="font-size:.82rem;color:var(--c-muted)">Profil öffentlich sichtbar</label>
<?php $pub = (int)($profile->profile_public ?? 1); ?>
<button type="button" id="wbfToggleProfileVis"
class="wbf-btn wbf-btn--sm<?php echo $pub?' wbf-btn--primary':''; ?>"
data-state="<?php echo $pub; ?>">
<i class="fas fa-<?php echo $pub?'eye':'eye-slash'; ?>"></i>
<?php echo $pub?'Öffentlich':'Privat'; ?>
</button>
</div>
<textarea id="wbfEditSignature" rows="2" maxlength="300" placeholder="Deine Signatur…"><?php echo esc_textarea($profile->signature ?? ''); ?></textarea>
<div class="wbf-sig-counter"><span id="wbfSigCount"><?php echo mb_strlen($profile->signature??''); ?></span>/300</div>
</div>
<div class="wbf-profile-card__footer">
@@ -765,8 +1016,104 @@ class WBF_Shortcodes {
</div>
</div>
</div>
<!-- ── Benutzerdefinierte Profilfelder ──────────────── -->
<?php
$cf_defs = WBF_DB::get_profile_field_defs();
$cf_vals = WBF_DB::get_user_meta( $profile->id );
if ( ! empty( $cf_defs ) ):
?>
<div class="wbf-profile-card">
<div class="wbf-profile-card__header">
<i class="fas fa-sliders"></i> Weitere Profilangaben
</div>
<div class="wbf-profile-card__body">
<div class="wbf-profile-edit-grid">
<?php foreach ( $cf_defs as $def ):
$k = esc_attr( $def['key'] );
$lbl = esc_html( $def['label'] );
$ph = esc_attr( $def['placeholder'] ?? '' );
$val = esc_attr( $cf_vals[ $def['key'] ] ?? '' );
$req = ! empty($def['required']) ? 'required' : '';
?>
<div class="wbf-form-row">
<label><?php echo $lbl; ?><?php if($req): ?> <span style="color:var(--c-danger)">*</span><?php endif; ?></label>
<?php if ( $def['type'] === 'textarea' ): ?>
<textarea class="wbf-cf-input" data-field="cf_<?php echo $k; ?>"
rows="2" placeholder="<?php echo $ph; ?>"
<?php echo $req; ?>><?php echo esc_textarea( $cf_vals[$def['key']] ?? '' ); ?></textarea>
<?php elseif ( $def['type'] === 'select' ):
$opts = array_filter( array_map('trim', explode("\n", $def['options'] ?? '')) );
?>
<select class="wbf-cf-input" data-field="cf_<?php echo $k; ?>">
<option value="">— Bitte wählen —</option>
<?php foreach ( $opts as $opt ): ?>
<option value="<?php echo esc_attr($opt); ?>"
<?php selected( $cf_vals[$def['key']] ?? '', $opt ); ?>><?php echo esc_html($opt); ?></option>
<?php endforeach; ?>
</select>
<?php else: ?>
<input type="<?php echo $def['type'] === 'url' ? 'url' : ($def['type'] === 'number' ? 'number' : 'text'); ?>"
class="wbf-cf-input"
data-field="cf_<?php echo $k; ?>"
value="<?php echo $val; ?>"
placeholder="<?php echo $ph; ?>"
<?php echo $req; ?>>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div class="wbf-profile-card__footer">
<button class="wbf-btn wbf-btn--primary" id="wbfSaveProfileCf">
<i class="fas fa-save"></i> Speichern
</button>
<span class="wbf-msg" id="wbfProfileCfMsg"></span>
</div>
</div>
</div>
<?php endif; ?>
<!-- ── DSGVO: Konto löschen ──────────────────────────── -->
<div class="wbf-profile-card" style="border-color:rgba(240,82,82,.25)">
<div class="wbf-profile-card__header" style="color:var(--c-danger);background:rgba(240,82,82,.06);border-bottom-color:rgba(240,82,82,.15)">
<i class="fas fa-shield-halved"></i> Datenschutz & Konto löschen
</div>
<div class="wbf-profile-card__body">
<p style="font-size:.85rem;color:var(--c-text-dim);margin-bottom:1rem;line-height:1.6">
Gemäß <strong>DSGVO Art. 17</strong> (Recht auf Vergessenwerden) kannst du die vollständige Löschung deines Kontos und aller personenbezogenen Daten beantragen.<br>
<span style="color:var(--c-muted);font-size:.8rem">Deine Beiträge bleiben anonymisiert sichtbar. Direktnachrichten, Likes, Profilinformationen und alle persönlichen Daten werden dauerhaft gelöscht.</span>
</p>
<div id="wbfGdprBox" style="background:rgba(240,82,82,.06);border:1px solid rgba(240,82,82,.2);border-radius:var(--radius-sm);padding:1.1rem;display:none">
<p style="font-size:.82rem;font-weight:700;color:var(--c-danger);margin-bottom:.9rem"><i class="fas fa-triangle-exclamation"></i> Diese Aktion ist unwiderruflich.</p>
<div class="wbf-form-row">
<label style="font-size:.72rem">Passwort zur Bestätigung</label>
<input type="password" id="wbfGdprPassword" placeholder="Dein aktuelles Passwort" autocomplete="current-password">
</div>
<label style="display:flex;align-items:center;gap:.6rem;font-size:.82rem;color:var(--c-text-dim);cursor:pointer;margin-bottom:1rem">
<input type="checkbox" id="wbfGdprConfirm" style="width:15px;height:15px;accent-color:var(--c-danger);cursor:pointer">
Ich verstehe, dass mein Konto und alle persönlichen Daten unwiderruflich gelöscht werden.
</label>
<div style="display:flex;gap:.75rem;align-items:center;flex-wrap:wrap">
<button class="wbf-btn wbf-btn--sm" id="wbfGdprCancel" onclick="document.getElementById('wbfGdprBox').style.display='none';document.getElementById('wbfGdprToggle').style.display=''">
<i class="fas fa-xmark"></i> Abbrechen
</button>
<button class="wbf-btn wbf-btn--sm" id="wbfGdprSubmit"
style="background:rgba(240,82,82,.15);color:var(--c-danger);border-color:rgba(240,82,82,.4)">
<i class="fas fa-trash-can"></i> Konto endgültig löschen
</button>
<span class="wbf-msg" id="wbfGdprMsg"></span>
</div>
</div>
<button class="wbf-btn wbf-btn--sm" id="wbfGdprToggle"
style="background:rgba(240,82,82,.08);color:var(--c-danger);border-color:rgba(240,82,82,.3)"
onclick="document.getElementById('wbfGdprBox').style.display='';document.getElementById('wbfGdprToggle').style.display='none'">
<i class="fas fa-trash-can"></i> Konto löschen (DSGVO Art. 17)
</button>
</div>
</div>
<?php endif; /* end $is_own */ ?>
<!-- Beiträge -->
<div class="wbf-profile-card">
<div class="wbf-profile-card__header">
@@ -812,6 +1159,33 @@ class WBF_Shortcodes {
</div>
</div>
<!-- Lesezeichen (nur eigenes Profil) -->
<?php if ($is_own):
$bookmarks = WBF_DB::get_user_bookmarks($current->id, 50); ?>
<div class="wbf-profile-card">
<div class="wbf-profile-card__header">
<i class="fas fa-bookmark"></i> Lesezeichen
<span class="wbf-profile-card__count"><?php echo count($bookmarks); ?></span>
</div>
<div class="wbf-profile-card__body wbf-profile-card__body--posts">
<?php if (empty($bookmarks)): ?>
<p class="wbf-profile-empty">Noch keine Lesezeichen.</p>
<?php else: foreach ($bookmarks as $bm): ?>
<div class="wbf-profile-post-item">
<div class="wbf-profile-post-item__top">
<?php echo self::render_prefix($bm); ?>
<a href="?forum_thread=<?php echo (int)$bm->id; ?>" class="wbf-profile-post-item__title">
<?php echo esc_html(mb_substr($bm->title,0,60)); ?>
</a>
<span class="wbf-profile-post-item__cat"><i class="fas fa-folder"></i> <?php echo esc_html($bm->cat_name); ?></span>
<span class="wbf-profile-post-item__time"><i class="fas fa-bookmark" style="font-size:.65rem"></i> <?php echo self::time_ago($bm->bookmarked_at); ?></span>
</div>
</div>
<?php endforeach; endif; ?>
</div>
</div>
<?php endif; ?>
</div><!-- /.wbf-profile-main -->
</div><!-- /.wbf-profile-layout -->
</div>
@@ -901,6 +1275,7 @@ class WBF_Shortcodes {
</div>
<?php endif; ?>
</div>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
</div>
<?php return ob_get_clean();
@@ -912,8 +1287,20 @@ class WBF_Shortcodes {
$current = WBF_Auth::get_current_user();
if (!$current) {
ob_start(); ?>
<div class="wbf-wrap"><?php self::render_topbar(null); ?>
<div class="wbf-container wbf-mt"><div class="wbf-notice"><i class="fas fa-lock"></i> Bitte <a href="#" class="wbf-login-link">einloggen</a> um Nachrichten zu lesen.</div></div></div>
<div class="wbf-wrap">
<?php self::render_topbar(null); ?>
<div class="wbf-container wbf-mt" style="display:flex;justify-content:center;padding:3rem 1rem">
<div style="width:100%;max-width:460px;background:var(--c-surface);border:1px solid rgba(0,180,216,.25);border-radius:var(--radius);padding:2rem;position:relative;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.6)">
<div style="position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,#00b4d8,transparent)"></div>
<div style="text-align:center;margin-bottom:1.5rem">
<div style="font-size:2.2rem;margin-bottom:.6rem">🔒</div>
<h2 style="font-size:1.1rem;font-weight:700;color:var(--c-text);margin-bottom:.3rem">Bitte einloggen</h2>
<p style="font-size:.85rem;color:var(--c-muted)">Um Nachrichten lesen zu können, musst du eingeloggt sein.</p>
</div>
<?php self::render_auth_forms(); ?>
</div>
</div>
</div>
<?php return ob_get_clean();
}
$partner_id = (int)($_GET['with'] ?? 0);
@@ -969,6 +1356,7 @@ class WBF_Shortcodes {
</div>
</div>
</div>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
<?php self::render_dm_compose_modal(); ?>
</div>
@@ -1006,6 +1394,9 @@ class WBF_Shortcodes {
// ── SEARCH ────────────────────────────────────────────────────────────────
private static function view_search() {
$cur_s = WBF_Auth::get_current_user();
$maint_s = wbf_get_settings()['maintenance_mode'] ?? '0';
if ($maint_s === '1' && (!$cur_s || WBF_Roles::level($cur_s->role) < 50)) return self::view_maintenance();
$query = sanitize_text_field($_GET['q'] ?? '');
$current = WBF_Auth::get_current_user();
$results = mb_strlen($query) >= 2 ? WBF_DB::search($query, 40) : [];
@@ -1053,6 +1444,7 @@ class WBF_Shortcodes {
<p style="color:var(--c-muted);font-size:.82rem;margin-top:1rem"><?php echo count($results); ?> Ergebnis(se) gefunden.</p>
<?php endif; ?>
</div>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
</div>
<?php return ob_get_clean();
@@ -1063,7 +1455,7 @@ class WBF_Shortcodes {
?>
<div class="wbf-topbar">
<div class="wbf-topbar__inner">
<a href="<?php echo esc_url(remove_query_arg(['forum_cat','forum_thread','forum_profile','forum_search','forum_tag','fp','tp'])); ?>" class="wbf-topbar__brand">
<a href="<?php echo esc_url(wbf_get_forum_url()); ?>" class="wbf-topbar__brand">
<i class="fas fa-comments"></i> <?php echo esc_html(wbf_get_settings()['topbar_brand']); ?>
</a>
<!-- Suchfeld -->
@@ -1120,11 +1512,18 @@ class WBF_Shortcodes {
</div>
<?php }
private static function render_auth_forms() { ?>
private static function render_auth_forms() {
$reg_mode = wbf_get_settings()['registration_mode'] ?? 'open';
$invite_msg = wbf_get_settings()['invite_message'] ?? 'Registrierung ist aktuell nur auf Einladung möglich.';
?>
<div class="wbf-auth-box">
<div class="wbf-auth-tabs">
<button class="wbf-auth-tab active" data-tab="login">Login</button>
<?php if ($reg_mode === 'open'): ?>
<button class="wbf-auth-tab" data-tab="register">Registrieren</button>
<?php elseif ($reg_mode === 'invite'): ?>
<button class="wbf-auth-tab wbf-auth-tab--muted" disabled title="<?php echo esc_attr($invite_msg); ?>">Registrieren <i class="fas fa-lock" style="font-size:.7em"></i></button>
<?php endif; ?>
</div>
<div class="wbf-auth-panel active" data-panel="login">
<div class="wbf-form-row"><input type="text" class="wbf-field-username" placeholder="Benutzername oder E-Mail"></div>
@@ -1137,6 +1536,20 @@ class WBF_Shortcodes {
<div style="text-align:right;margin-top:.4rem"><a href="#" class="wbf-forgot-link" style="font-size:.78rem;color:var(--c-muted)">Passwort vergessen?</a></div>
<span class="wbf-login-msg wbf-msg"></span>
</div>
<!-- Registrierung gesperrt/invite -->
<?php if ($reg_mode === 'invite'): ?>
<div class="wbf-auth-panel" data-panel="register">
<div class="wbf-notice" style="margin:.5rem 0;font-size:.85rem;text-align:center">
<i class="fas fa-lock"></i> <?php echo esc_html($invite_msg); ?>
</div>
</div>
<?php elseif ($reg_mode === 'disabled'): ?>
<div class="wbf-auth-panel" data-panel="register">
<div class="wbf-notice wbf-notice--warning" style="margin:.5rem 0;font-size:.85rem;text-align:center">
<i class="fas fa-ban"></i> Registrierung ist deaktiviert.
</div>
</div>
<?php endif; ?>
<!-- Passwort vergessen -->
<div class="wbf-auth-panel" data-panel="forgot">
<p style="font-size:.82rem;color:var(--c-text-dim);margin-bottom:.75rem">Gib deine E-Mail ein — wir schicken dir einen Reset-Link.</p>
@@ -1146,10 +1559,37 @@ class WBF_Shortcodes {
<span class="wbf-forgot-msg wbf-msg"></span>
</div>
<div class="wbf-auth-panel" data-panel="register">
<?php
$inv_code_prefill = '';
if (isset($_GET['wbf_invite'])) $inv_code_prefill = strtoupper(sanitize_text_field($_GET['wbf_invite']));
$reg_mode_now = wbf_get_settings()['registration_mode'] ?? 'open';
if ($reg_mode_now === 'invite'): ?>
<div class="wbf-form-row">
<input type="text" class="wbf-field-invite-code"
placeholder="Einladungscode"
value="<?php echo esc_attr($inv_code_prefill); ?>"
style="text-transform:uppercase;letter-spacing:.1em;font-weight:700">
</div>
<?php endif; ?>
<!-- Spam: Honeypot (versteckt) + Zeitstempel -->
<input type="text" name="wbf_website" class="wbf-hp-field" tabindex="-1" autocomplete="off" style="display:none!important;visibility:hidden;position:absolute;left:-9999px">
<input type="hidden" class="wbf-field-form-time" value="<?php echo time(); ?>">
<div class="wbf-form-row"><input type="text" class="wbf-field-reg-user" placeholder="Benutzername"></div>
<div class="wbf-form-row"><input type="text" class="wbf-field-reg-name" placeholder="Anzeigename"></div>
<div class="wbf-form-row"><input type="email" class="wbf-field-reg-email" placeholder="E-Mail"></div>
<div class="wbf-form-row"><input type="password" class="wbf-field-reg-pass" placeholder="Passwort (min. 6 Zeichen)"></div>
<?php
$rules_required = ( wbf_get_settings()['rules_accept_required'] ?? '1' ) === '1';
$rules_enabled = ( wbf_get_settings()['rules_enabled'] ?? '1' ) === '1';
if ( $rules_enabled ):
?>
<label style="display:flex;align-items:center;gap:.5rem;font-size:.82rem;color:var(--c-text-dim);cursor:pointer;margin-bottom:.75rem;line-height:1.4;flex-wrap:wrap">
<input type="checkbox" class="wbf-field-rules-accept"
style="width:15px;height:15px;accent-color:var(--c-primary);cursor:pointer;flex-shrink:0"
<?php echo $rules_required ? 'required' : ''; ?>>
<span>Ich akzeptiere die <a href="<?php echo esc_url(wbf_get_forum_url().'?forum_rules=1'); ?>" target="_blank" style="color:var(--c-primary);font-weight:600;white-space:nowrap">Forum-Regeln</a><?php echo $rules_required ? ' <span style="color:var(--c-danger)">*</span>' : ''; ?></span>
</label>
<?php endif; ?>
<button class="wbf-btn wbf-btn--primary wbf-btn--full wbf-reg-submit-btn"><i class="fas fa-user-plus"></i> Konto erstellen</button>
<span class="wbf-reg-msg wbf-msg"></span>
</div>
@@ -1322,6 +1762,58 @@ class WBF_Shortcodes {
<?php
}
// ── UMFRAGE-MODAL ─────────────────────────────────────────────────────────
private static function render_poll_modal( $thread_id ) { ?>
<div class="wbf-modal" id="wbfPollModal">
<div class="wbf-modal__box">
<button class="wbf-modal__close" onclick="document.getElementById('wbfPollModal').classList.remove('active')">&times;</button>
<h3 style="margin-bottom:1.25rem"><i class="fas fa-chart-bar" style="color:var(--c-primary)"></i> Umfrage erstellen</h3>
<input type="hidden" id="wbfPollThreadId" value="<?php echo (int)$thread_id; ?>">
<div class="wbf-form-row">
<label>Frage <span style="color:var(--c-danger)">*</span></label>
<input type="text" id="wbfPollQuestion" placeholder="Was ist deine Meinung zu…?" maxlength="200">
</div>
<div class="wbf-form-row">
<label>Antwortmöglichkeiten <small>(min. 2, max. 10)</small></label>
<div id="wbfPollOptions">
<div class="wbf-poll-opt-row">
<input type="text" class="wbf-poll-opt" placeholder="Option 1" maxlength="100">
<button type="button" class="wbf-btn wbf-btn--sm" style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);min-width:32px;flex-shrink:0" onclick="wbfRemovePollOpt(this)">✕</button>
</div>
<div class="wbf-poll-opt-row">
<input type="text" class="wbf-poll-opt" placeholder="Option 2" maxlength="100">
<button type="button" class="wbf-btn wbf-btn--sm" style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);min-width:32px;flex-shrink:0" onclick="wbfRemovePollOpt(this)">✕</button>
</div>
</div>
<button type="button" id="wbfPollAddOpt" class="wbf-btn wbf-btn--sm" style="margin-top:.5rem">
<i class="fas fa-plus"></i> Option hinzufügen
</button>
</div>
<div style="display:flex;gap:1.25rem;flex-wrap:wrap;align-items:flex-end;margin-bottom:1rem">
<label style="display:flex;align-items:center;gap:.4rem;font-size:.82rem;color:var(--c-text-dim);cursor:pointer">
<input type="checkbox" id="wbfPollMulti" style="accent-color:var(--c-primary);width:15px;height:15px">
Mehrfachauswahl erlauben
</label>
<div class="wbf-form-row" style="margin:0;flex:1;min-width:180px">
<label style="font-size:.72rem">Endet am <small style="color:var(--c-muted)">(optional)</small></label>
<input type="datetime-local" id="wbfPollEndsAt" min="<?php echo date('Y-m-d\TH:i'); ?>">
</div>
</div>
<div style="display:flex;gap:.75rem;align-items:center">
<button class="wbf-btn wbf-btn--primary" id="wbfSubmitPoll">
<i class="fas fa-chart-bar"></i> Umfrage erstellen
</button>
<span class="wbf-msg" id="wbfPollMsg"></span>
</div>
</div>
</div>
<?php }
private static function render_new_thread_modal( $categories, $current, $preselect = 0 ) {
if (!$current) return;
$parents = array_filter($categories, fn($c) => (int)$c->parent_id === 0);
@@ -1331,7 +1823,7 @@ class WBF_Shortcodes {
<div class="wbf-modal" id="wbfNewThreadModal">
<div class="wbf-modal__box wbf-modal__box--lg">
<button class="wbf-modal__close" onclick="document.getElementById('wbfNewThreadModal').classList.remove('active')">&times;</button>
<h3 style="margin-bottom:1.2rem"><i class="fas fa-plus-circle"></i> Neuen Thread erstellen</h3>
<h3 id="wbfModalTitle" style="margin-bottom:1.2rem"><i class="fas fa-plus-circle"></i> Neuen Thread erstellen</h3>
<div class="wbf-form-row"><label>Kategorie</label>
<select id="wbfThreadCat">
<?php foreach ($parents as $p): ?>
@@ -1344,13 +1836,28 @@ class WBF_Shortcodes {
<?php endforeach; ?>
</select>
</div>
<div class="wbf-form-row"><label>Titel</label><input type="text" id="wbfThreadTitle" placeholder="Titel deines Threads"></div>
<div class="wbf-form-row">
<div class="wbf-form-row"><label id="wbfTitleLabel">Titel</label><input type="text" id="wbfThreadTitle" placeholder="Titel deines Threads"></div>
<?php $prefixes = WBF_DB::get_prefixes(); if (!empty($prefixes)): ?>
<div class="wbf-form-row" id="wbfPrefixRow">
<label>Präfix <small>(optional)</small></label>
<select id="wbfThreadPrefix">
<option value="">— Kein Präfix —</option>
<?php foreach ($prefixes as $px): ?>
<option value="<?php echo (int)$px->id; ?>"
data-color="<?php echo esc_attr($px->color); ?>"
data-bg="<?php echo esc_attr($px->bg_color); ?>">
<?php echo esc_html($px->label); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<?php endif; ?>
<div class="wbf-form-row" id="wbfContentRow">
<label>Inhalt</label>
<?php self::render_editor_toolbar('wbfThreadContent'); ?>
<textarea id="wbfThreadContent" rows="7" placeholder="Was möchtest du besprechen?"></textarea>
</div>
<div class="wbf-form-row">
<div class="wbf-form-row" id="wbfTagsRow">
<label>Tags <small>(kommagetrennt, max. 10 — z.B. php, wordpress, tipps)</small></label>
<div class="wbf-tag-input-wrap" id="wbfTagInputWrap">
<div class="wbf-tag-pills" id="wbfTagPills"></div>
@@ -1359,10 +1866,55 @@ class WBF_Shortcodes {
<div class="wbf-tag-suggest" id="wbfTagSuggest" style="display:none"></div>
</div>
</div>
<div style="display:flex;gap:1rem;align-items:center">
<!-- Thread Submit Row -->
<div id="wbfThreadSubmitRow" style="display:flex;gap:1rem;align-items:center;margin-top:.25rem">
<button class="wbf-btn wbf-btn--primary" id="wbfSubmitThread"><i class="fas fa-paper-plane"></i> Thread erstellen</button>
<button type="button" id="wbfShowPollSection" class="wbf-btn wbf-btn--sm wbf-btn--outline-poll">
<i class="fas fa-chart-bar"></i> Umfrage hinzufügen
</button>
<span class="wbf-msg" id="wbfThreadMsg"></span>
</div>
<!-- Poll Section -->
<div id="wbfPollSection" style="margin-top:1.25rem;border-top:1px solid var(--c-border);padding-top:1.1rem;display:none">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:.9rem">
<span style="font-size:.85rem;font-weight:700;color:#fbbf24"><i class="fas fa-chart-bar"></i> Umfrage</span>
<button type="button" id="wbfRemovePollSection" class="wbf-btn wbf-btn--sm" style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);padding:.25rem .6rem;font-size:.72rem"><i class="fas fa-xmark"></i> Entfernen</button>
</div>
<div class="wbf-form-row">
<label>Frage <span style="color:var(--c-danger)">*</span></label>
<input type="text" id="wbfNewThreadPollQuestion" placeholder="Was ist deine Meinung zu…?" maxlength="200">
</div>
<div class="wbf-form-row">
<label>Antwortmöglichkeiten <small>(min. 2, max. 10)</small></label>
<div id="wbfNewThreadPollOptions">
<div class="wbf-poll-opt-row" style="display:flex;gap:.5rem;margin-bottom:.4rem">
<input type="text" class="wbf-nt-poll-opt" placeholder="Option 1" maxlength="100">
<button type="button" class="wbf-btn wbf-btn--sm" style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);min-width:32px;flex-shrink:0" onclick="wbfRemoveNTPollOpt(this)">✕</button>
</div>
<div class="wbf-poll-opt-row" style="display:flex;gap:.5rem;margin-bottom:.4rem">
<input type="text" class="wbf-nt-poll-opt" placeholder="Option 2" maxlength="100">
<button type="button" class="wbf-btn wbf-btn--sm" style="background:rgba(240,82,82,.1);color:var(--c-danger);border-color:rgba(240,82,82,.3);min-width:32px;flex-shrink:0" onclick="wbfRemoveNTPollOpt(this)">✕</button>
</div>
</div>
<button type="button" id="wbfNTPollAddOpt" class="wbf-btn wbf-btn--sm" style="margin-top:.5rem">
<i class="fas fa-plus"></i> Option hinzufügen
</button>
</div>
<label style="display:flex;align-items:center;gap:.4rem;font-size:.82rem;color:var(--c-text-dim);cursor:pointer;margin-bottom:.75rem">
<input type="checkbox" id="wbfNTPollMulti" style="accent-color:var(--c-primary);width:15px;height:15px">
Mehrfachauswahl erlauben
</label>
<div class="wbf-form-row" style="margin-bottom:1rem">
<label style="font-size:.78rem;font-weight:600;color:var(--c-text-dim)">Endet am <small style="font-weight:400;color:var(--c-muted)">(optional)</small></label>
<input type="datetime-local" id="wbfNTPollEndsAt" min="<?php echo date('Y-m-d\TH:i'); ?>" style="width:100%;box-sizing:border-box">
</div>
<div style="display:flex;gap:1rem;align-items:center">
<button class="wbf-btn wbf-btn--outline-poll" id="wbfSubmitPollThread"><i class="fas fa-chart-bar"></i> Umfrage erstellen</button>
<span class="wbf-msg" id="wbfPollThreadMsg"></span>
</div>
</div>
</div>
</div>
<?php }
@@ -1415,6 +1967,8 @@ class WBF_Shortcodes {
private static function view_members() {
$current = WBF_Auth::get_current_user();
$maint_m = wbf_get_settings()['maintenance_mode'] ?? '0';
if ($maint_m === '1' && (!$current || WBF_Roles::level($current->role) < 50)) return self::view_maintenance();
if ( ! $current ) {
ob_start(); ?>
<div class="wbf-wrap"><?php self::render_topbar(null); ?>
@@ -1527,11 +2081,133 @@ class WBF_Shortcodes {
<?php endif; ?>
</div>
<?php self::render_forum_footer(); ?>
<?php self::render_auth_modal(); ?>
</div>
<?php return ob_get_clean();
}
// ── Wartungsmodus ─────────────────────────────────────────────────────────
private static function view_maintenance() {
$s = wbf_get_settings();
$title = esc_html($s['maintenance_title'] ?? 'Wartungsarbeiten');
$msg = esc_html($s['maintenance_message'] ?? 'Das Forum wird gerade gewartet. Bitte versuche es später erneut.');
ob_start(); ?>
<div class="wbf-wrap">
<div style="min-height:60vh;display:flex;align-items:center;justify-content:center;text-align:center;padding:2rem">
<div style="max-width:480px">
<div style="font-size:4rem;margin-bottom:1.5rem">🔧</div>
<h1 style="font-size:1.6rem;font-weight:800;color:var(--c-text);margin-bottom:1rem"><?php echo $title; ?></h1>
<p style="color:var(--c-text-dim);font-size:1rem;line-height:1.7"><?php echo $msg; ?></p>
<p style="margin-top:2rem">
<a href="#" class="wbf-login-link wbf-btn wbf-btn--sm wbf-btn--outline" style="font-size:.85rem">
<i class="fas fa-lock"></i> Als Admin einloggen
</a>
</p>
</div>
</div>
<?php self::render_auth_modal(); ?>
</div>
<?php return ob_get_clean();
}
// ── FORUM-FOOTER ──────────────────────────────────────────────────────────
private static function render_forum_footer() {
if ( ( wbf_get_settings()['rules_enabled'] ?? '1' ) !== '1' ) return;
$rules_url = esc_url( wbf_get_forum_url() . '?forum_rules=1' );
?>
<div style="border-top:1px solid var(--c-border);margin-top:3rem;padding:1.25rem 1.5rem;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;background:var(--c-bg2)">
<span style="font-size:.78rem;color:var(--c-muted)">
<i class="fas fa-shield-halved" style="color:var(--c-primary);margin-right:.35rem"></i>
Durch die Nutzung des Forums stimmst du unseren Regeln zu.
</span>
<a href="<?php echo $rules_url; ?>"
style="display:inline-flex;align-items:center;gap:.4rem;font-size:.78rem;font-weight:700;color:#0d1117;background:var(--c-primary);padding:.35rem .9rem;border-radius:6px;text-decoration:none;transition:opacity .15s"
onmouseover="this.style.opacity='.85'" onmouseout="this.style.opacity='1'">
<i class="fas fa-scroll"></i> Forum-Regeln lesen
</a>
</div>
<?php
}
// ── FORUM-REGELN ──────────────────────────────────────────────────────────
private static function view_rules() {
$current = WBF_Auth::get_current_user();
$s = wbf_get_settings();
if ( ( $s['rules_enabled'] ?? '1' ) !== '1' ) {
return '<p class="wbf-notice">Diese Seite ist nicht verfügbar.</p>';
}
$title = esc_html( $s['rules_title'] ?? 'Forum-Regeln & Nutzungsbedingungen' );
$raw = $s['rules_content'] ?? '';
ob_start(); ?>
<div class="wbf-wrap">
<?php self::render_topbar($current); ?>
<div class="wbf-container wbf-mt" style="max-width:820px">
<nav class="wbf-breadcrumb">
<a href="<?php echo esc_url(wbf_get_forum_url()); ?>"><i class="fas fa-home"></i> Forum</a>
<span>/</span><span><i class="fas fa-shield-halved"></i> Regeln</span>
</nav>
<div style="background:var(--c-surface);border:1px solid var(--c-border);border-radius:var(--radius);overflow:hidden;margin-bottom:2rem">
<div style="background:linear-gradient(135deg,rgba(0,180,216,.12),rgba(99,102,241,.08));border-bottom:1px solid var(--c-border);padding:1.75rem 2rem;display:flex;align-items:center;gap:1rem">
<div style="width:48px;height:48px;border-radius:12px;background:rgba(0,180,216,.15);border:1px solid rgba(0,180,216,.25);display:flex;align-items:center;justify-content:center;font-size:1.3rem;flex-shrink:0">📜</div>
<div>
<h1 style="font-size:1.3rem;font-weight:800;color:var(--c-text);margin:0 0 .2rem"><?php echo $title; ?></h1>
<p style="font-size:.8rem;color:var(--c-muted);margin:0">
<i class="fas fa-clock"></i> Zuletzt aktualisiert: <?php echo date_i18n('d. F Y'); ?>
&nbsp;·&nbsp;<i class="fas fa-eye"></i> Bitte sorgfältig lesen
</p>
</div>
</div>
<div class="wbf-rules-body" style="padding:2rem">
<?php echo self::render_rules_content( $raw ); ?>
</div>
<div style="border-top:1px solid var(--c-border);padding:1.25rem 2rem;background:rgba(0,0,0,.12);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem">
<span style="font-size:.82rem;color:var(--c-muted)">
<i class="fas fa-info-circle"></i> Mit der Nutzung des Forums stimmst du diesen Regeln zu.
</span>
<a href="<?php echo esc_url(wbf_get_forum_url()); ?>" class="wbf-btn wbf-btn--sm wbf-btn--primary" style="color:#0d1117;font-weight:700">
<i class="fas fa-arrow-left"></i> Zurück zum Forum
</a>
</div>
</div>
</div>
</div>
<?php return ob_get_clean();
}
private static function render_rules_content( $raw ) {
if ( empty( $raw ) ) {
return '<p style="color:var(--c-muted)">Noch keine Regeln hinterlegt.</p>';
}
$paragraphs = preg_split( '/\n{2,}/', trim( $raw ) );
$out = '';
foreach ( $paragraphs as $para ) {
$para = trim( $para );
if ( $para === '' ) continue;
if ( preg_match( '/^\*\*(\d+\.\s*.+?)\*\*$/', $para, $m ) ) {
preg_match('/^(\d+)\.\s*(.+)$/', $m[1], $parts);
$num = esc_html($parts[1] ?? '');
$text = esc_html($parts[2] ?? $m[1]);
$out .= '<div class="wbf-rules-section">'
. '<span class="wbf-rules-num">' . $num . '</span>'
. '<h2 class="wbf-rules-heading">' . $text . '</h2>'
. '</div>';
} else {
$para = htmlspecialchars( $para, ENT_QUOTES, 'UTF-8' );
$para = preg_replace( '/\*\*(.+?)\*\*/s', '<strong>$1</strong>', $para );
$para = nl2br( $para );
$out .= '<p class="wbf-rules-para">' . $para . '</p>';
}
}
return $out;
}
}
WBF_Shortcodes::init();