Update from Git Manager GUI

This commit is contained in:
2026-03-29 13:41:29 +02:00
parent 1c229ab72b
commit 43fcc6cb95
2 changed files with 289 additions and 93 deletions

View File

@@ -553,7 +553,6 @@ function wbf_admin_page() {
['wbf-trash', 'fas fa-trash-can', 'Papierkorb', $deleted_count>0?$deleted_count:0, true],
['wbf-export', 'fas fa-database', 'Export / Import'],
['wbf-settings', 'fas fa-gear', 'Einstellungen'],
['wbf-updates', 'fas fa-arrow-up-from-bracket', '🔔 Updates', $update_info ? 1 : 0],
];
foreach ($nav as $n):
if ($n===null) { echo '<div class="wbf-nav__sep"></div>'; continue; }
@@ -1079,6 +1078,10 @@ function wbf_admin_categories() {
foreach ( $p->children as $c ) {
$cd = wp_nonce_url( "?page=wbf-categories&delete_cat={$c->id}", "delete_cat_{$c->id}" );
$crole = WBF_Roles::get( $c->min_role ?? 'member' );
$clbadge = ( $c->min_role ?? 'member' ) !== 'member'
? " <span style='color:" . esc_attr( $crole['color'] ) . ";font-size:.8em'>🔒 " . esc_html( $crole['label'] ) . "</span>" : '';
$cguestbadge = ( (int)($c->guest_visible ?? 1) === 0 )
? " <span style='color:#f59e0b;font-size:.8em'>👁 Nur eingeloggt</span>" : '';
$reorder_c =
'<form method="post" style="display:inline-flex;gap:2px">'
. wp_nonce_field( 'wbf_reorder_nonce', '_wpnonce', true, false )
@@ -1097,7 +1100,7 @@ function wbf_admin_categories() {
echo "<tr>
<td style='padding-left:2.5rem'>↳ " . esc_html( $c->name ) . "</td>
<td>" . esc_html( $c->thread_count ) . "</td>
<td>" . esc_html( $crole['label'] ) . "$clockbadge$cguestbadge</td>
<td>" . esc_html( $crole['label'] ) . "$clbadge$cguestbadge</td>
<td style='white-space:nowrap'>{$reorder_c} <a href='?page=wbf-categories&edit={$c->id}'>Bearbeiten</a> | <a href='" . esc_url( $cd ) . "' onclick='return confirm(\"Löschen?\")'>Löschen</a></td>
</tr>";
}
@@ -1428,6 +1431,7 @@ function wbf_admin_members() {
<td>
<strong><?php echo esc_html( $m->display_name ); ?></strong><br>
<small style="color:#999">@<?php echo esc_html( $m->username ); ?></small>
<?php do_action( 'wbf_admin_user_row_extra', $m ); ?>
</td>
<td><?php echo esc_html( $m->email ); ?></td>
<td>
@@ -3039,151 +3043,328 @@ function wbf_admin_export() {
function wbf_admin_profile_fields() {
// ── Speichern ────────────────────────────────────────────────────────────
// ── Kategorien speichern ─────────────────────────────────────────────────
if ( isset($_POST['wbf_save_profile_fields']) && check_admin_referer('wbf_profile_fields_nonce') ) {
$keys = $_POST['field_key'] ?? [];
$labels = $_POST['field_label'] ?? [];
$types = $_POST['field_type'] ?? [];
$placeholders = $_POST['field_placeholder'] ?? [];
$required = $_POST['field_required'] ?? [];
$public = $_POST['field_public'] ?? [];
$options = $_POST['field_options'] ?? [];
// Kategorien
$cat_ids = $_POST['cat_id'] ?? [];
$cat_names = $_POST['cat_name'] ?? [];
$cat_icons = $_POST['cat_icon'] ?? [];
$cats = [];
foreach ( $cat_ids as $ci => $cid ) {
$cid = sanitize_key( $cid );
if ( ! $cid ) continue;
$cats[] = [
'id' => $cid,
'name' => sanitize_text_field( $cat_names[$ci] ?? $cid ),
'icon' => sanitize_text_field( $cat_icons[$ci] ?? '📋' ),
];
}
// Neue Kategorie
if ( ! empty( trim( $_POST['new_cat_name'] ?? '' ) ) ) {
$new_name = sanitize_text_field( $_POST['new_cat_name'] );
$new_icon = sanitize_text_field( $_POST['new_cat_icon'] ?? '📋' );
$new_id = 'cat_' . sanitize_key( $new_name ) . '_' . time();
$cats[] = [ 'id' => $new_id, 'name' => $new_name, 'icon' => $new_icon ];
}
WBF_DB::save_profile_field_categories( $cats );
// Felder
$keys = $_POST['field_key'] ?? [];
$labels = $_POST['field_label'] ?? [];
$types = $_POST['field_type'] ?? [];
$phs = $_POST['field_placeholder'] ?? [];
$req = $_POST['field_required'] ?? [];
$pub = $_POST['field_public'] ?? [];
$opts = $_POST['field_options'] ?? [];
$cats_f = $_POST['field_category'] ?? [];
$valid_cat_ids = array_column( $cats, 'id' );
$fields = [];
foreach ( $keys as $i => $raw_key ) {
$key = sanitize_key( $raw_key );
if ( ! $key ) continue;
// Reservierte Keys sperren
if ( in_array($key, ['username','email','password','display_name','bio','signature','avatar_url','role']) ) continue;
$cat_id = sanitize_key( $cats_f[$i] ?? '' );
if ( ! in_array( $cat_id, $valid_cat_ids ) ) $cat_id = '';
$fields[] = [
'key' => $key,
'label' => sanitize_text_field( $labels[$i] ?? $key ),
'type' => in_array($types[$i] ?? '', ['text','url','textarea','select','number']) ? $types[$i] : 'text',
'placeholder' => sanitize_text_field( $placeholders[$i] ?? '' ),
'required' => ! empty( $required[$i] ) ? 1 : 0,
'public' => ! empty( $public[$i] ) ? 1 : 0,
'options' => sanitize_textarea_field( $options[$i] ?? '' ),
'type' => in_array($types[$i] ?? '', ['text','url','textarea','select','number','date']) ? $types[$i] : 'text',
'placeholder' => sanitize_text_field( $phs[$i] ?? '' ),
'required' => ! empty( $req[$i] ) ? 1 : 0,
'public' => ! empty( $pub[$i] ) ? 1 : 0,
'options' => sanitize_textarea_field( $opts[$i] ?? '' ),
'category_id' => $cat_id,
];
}
WBF_DB::save_profile_field_defs( $fields );
echo '<div class="notice notice-success is-dismissible"><p>✅ Profilfelder gespeichert!</p></div>';
echo '<div class="notice notice-success is-dismissible"><p>✅ Profilfelder & Kategorien gespeichert!</p></div>';
}
$fields = WBF_DB::get_profile_field_defs();
$types = ['text'=>'Text','url'=>'URL/Link','textarea'=>'Mehrzeiliger Text','select'=>'Auswahlliste','number'=>'Zahl'];
// Kategorie löschen
if ( isset($_GET['wbf_del_cat']) && check_admin_referer('wbf_del_cat') ) {
$del_id = sanitize_key( $_GET['wbf_del_cat'] );
$cats = WBF_DB::get_profile_field_categories();
$cats = array_values( array_filter( $cats, fn($c) => $c['id'] !== $del_id ) );
WBF_DB::save_profile_field_categories( $cats );
// Felder dieser Kategorie auf "keine Kategorie" setzen
$fields = WBF_DB::get_profile_field_defs();
foreach ( $fields as &$f ) {
if ( ($f['category_id'] ?? '') === $del_id ) $f['category_id'] = '';
}
WBF_DB::save_profile_field_defs( $fields );
wp_safe_redirect( remove_query_arg(['wbf_del_cat','_wpnonce']) );
exit;
}
$fields = WBF_DB::get_profile_field_defs();
$cats = WBF_DB::get_profile_field_categories();
$cat_map = array_column( $cats, null, 'id' );
$type_opts = ['text'=>'Text','url'=>'URL/Link','textarea'=>'Mehrzeiliger Text','select'=>'Auswahlliste','number'=>'Zahl','date'=>'Datum (Alter)'];
// Felder nach Kategorie gruppieren
$by_cat = [];
foreach ( $fields as $f ) {
$cid = $f['category_id'] ?? '';
if ( ! $cid || ! isset($cat_map[$cid]) ) $cid = '__none__';
$by_cat[$cid][] = $f;
}
?>
<div class="wrap">
<h1>👤 Benutzerdefinierte Profilfelder</h1>
<p style="color:#666;margin-bottom:1.5rem">Felder die Nutzer in ihrem Profil ausfüllen können — z.B. Website, Ort, Discord-Name, etc.</p>
<h1>👤 Benutzerdefinierte Profilfelder</h1>
<p style="color:#666;margin-bottom:1.5rem">Felder die Nutzer in ihrem Profil ausfüllen können — z.B. Website, Ort, Discord-Name, etc.</p>
<form method="post" id="wbfFieldsForm">
<?php wp_nonce_field('wbf_profile_fields_nonce'); ?>
<form method="post" id="wbfFieldsForm">
<?php wp_nonce_field('wbf_profile_fields_nonce'); ?>
<table class="widefat" style="margin-bottom:1rem">
<!-- ── Kategorien-Verwaltung ──────────────────────────────── -->
<div style="background:#f0f4ff;border:1px solid #c7d3f8;border-radius:10px;padding:18px 20px;margin-bottom:22px">
<h3 style="margin:0 0 14px;font-size:1rem;display:flex;align-items:center;gap:8px">
🗂️ Kategorien verwalten
</h3>
<table class="widefat" style="margin-bottom:12px;background:#fff;border-radius:7px;overflow:hidden">
<thead>
<tr>
<th style="width:140px">Schlüssel</th>
<th style="width:160px">Bezeichnung</th>
<th style="width:110px">Typ</th>
<th>Platzhalter</th>
<th style="width:110px">Optionen <small>(bei Auswahl)</small></th>
<th style="width:70px;text-align:center">Pflicht</th>
<th style="width:70px;text-align:center">Öffentlich</th>
<tr style="background:#eef2ff">
<th style="width:56px;padding:8px 10px">Icon</th>
<th style="padding:8px 10px">Kategorie-Name</th>
<th style="width:50px"></th>
</tr>
</thead>
<tbody id="wbfFieldRows">
<tbody>
<?php foreach ( $cats as $ci => $cat ): ?>
<tr>
<td style="padding:6px 10px">
<input type="hidden" name="cat_id[]" value="<?php echo esc_attr($cat['id']); ?>">
<input type="text" name="cat_icon[]" value="<?php echo esc_attr($cat['icon']); ?>"
style="width:46px;text-align:center;font-size:1.1rem;padding:4px 2px;border:1px solid #d0d9f7;border-radius:5px">
</td>
<td style="padding:6px 10px">
<input type="text" name="cat_name[]" value="<?php echo esc_attr($cat['name']); ?>"
style="width:100%;max-width:340px;border:1px solid #d0d9f7;border-radius:5px;padding:5px 8px">
</td>
<td style="padding:6px 10px;text-align:center">
<?php
$del_url = wp_nonce_url(
add_query_arg(['page'=>'wbf-profile-fields','wbf_del_cat'=>$cat['id']], admin_url('admin.php')),
'wbf_del_cat'
); ?>
<a href="<?php echo esc_url($del_url); ?>"
onclick="return confirm('Kategorie löschen? Felder dieser Kategorie bleiben erhalten (ohne Kategorie).')"
style="color:#dc2626;text-decoration:none;font-size:1.1rem;font-weight:700" title="Kategorie löschen">×</a>
</td>
</tr>
<?php endforeach; ?>
<tr style="background:#f8faff">
<td style="padding:6px 10px">
<input type="text" name="new_cat_icon" value="" placeholder="📋"
style="width:46px;text-align:center;font-size:1.1rem;padding:4px 2px;border:1px dashed #a5b4f8;border-radius:5px">
</td>
<td style="padding:6px 10px" colspan="2">
<input type="text" name="new_cat_name" value="" placeholder="+ Neue Kategorie (Namen eingeben und speichern)"
style="width:100%;max-width:380px;border:1px dashed #a5b4f8;border-radius:5px;padding:5px 8px;color:#555">
</td>
</tr>
</tbody>
</table>
</div>
<!-- ── Felder je Kategorie ───────────────────────────────── -->
<?php
// Alle Kategorien + "Ohne Kategorie" am Ende ausgeben
$all_sections = $cats;
if ( isset($by_cat['__none__']) ) {
$all_sections[] = ['id'=>'__none__','name'=>'Ohne Kategorie','icon'=>'📋'];
}
foreach ( $all_sections as $cat ):
$cid = $cat['id'];
$c_fields = $by_cat[$cid] ?? [];
?>
<div class="wbf-cat-section" style="border:1px solid #e0e6f8;border-radius:10px;margin-bottom:16px;overflow:hidden">
<div style="background:#eef2ff;padding:10px 16px;display:flex;align-items:center;gap:8px;cursor:pointer"
onclick="this.nextElementSibling.style.display=this.nextElementSibling.style.display==='none'?'':'none'">
<span style="font-size:1.1rem"><?php echo esc_html($cat['icon']); ?></span>
<strong style="font-size:.95rem"><?php echo esc_html($cat['name']); ?></strong>
<span style="color:#94a3b8;font-size:.8rem;margin-left:4px"><?php echo count($c_fields); ?> Feld<?php echo count($c_fields)!==1?'er':''; ?></span>
</div>
<div class="wbf-cat-body">
<table class="widefat" style="border:none">
<thead>
<tr style="background:#f8faff">
<th style="width:130px">Schlüssel</th>
<th style="width:150px">Bezeichnung</th>
<th style="width:105px">Typ</th>
<th>Platzhalter</th>
<th style="width:105px">Optionen</th>
<th style="width:65px;text-align:center">Pflicht</th>
<th style="width:70px;text-align:center">Öffentlich</th>
<th style="width:130px">Kategorie</th>
<th style="width:44px"></th>
</tr>
</thead>
<tbody class="wbf-field-rows">
<?php
$rows = $fields;
// Mindestens eine leere Zeile wenn noch keine Felder
if ( empty($rows) ) $rows[] = ['key'=>'','label'=>'','type'=>'text','placeholder'=>'','required'=>0,'public'=>1,'options'=>''];
foreach ( $rows as $i => $f ):
if ( empty($c_fields) ):
?>
<tr class="wbf-no-fields-hint" style="background:#fafafa">
<td colspan="9" style="color:#aaa;font-style:italic;font-size:.85rem;padding:10px 14px">
Noch keine Felder in dieser Kategorie.
</td>
</tr>
<?php
endif;
foreach ( $c_fields as $i_f => $f ):
$fi = 'fi_' . $f['key'];
?>
<tr class="wbf-field-row" style="background:#fff">
<td>
<td style="padding:6px 8px">
<input type="text" name="field_key[]"
value="<?php echo esc_attr($f['key']); ?>"
placeholder="z.b. website"
style="width:100%;font-family:monospace;font-size:.82rem"
<?php echo $f['key'] ? 'readonly' : ''; ?>>
<?php if ($f['key']): ?>
<p style="font-size:.7rem;color:#999;margin:2px 0 0">Schlüssel kann nach Erstellung nicht geändert werden.</p>
<p style="font-size:.68rem;color:#999;margin:2px 0 0">Nicht änderbar</p>
<?php endif; ?>
</td>
<td><input type="text" name="field_label[]" value="<?php echo esc_attr($f['label']); ?>" placeholder="Website" style="width:100%"></td>
<td>
<td style="padding:6px 8px">
<input type="text" name="field_label[]" value="<?php echo esc_attr($f['label']); ?>" placeholder="Website" style="width:100%">
</td>
<td style="padding:6px 8px">
<select name="field_type[]" style="width:100%" onchange="wbfToggleOptions(this)">
<?php foreach ($types as $val=>$lbl): ?>
<?php foreach ($type_opts as $val=>$lbl): ?>
<option value="<?php echo $val; ?>" <?php selected($f['type']??'text',$val); ?>><?php echo $lbl; ?></option>
<?php endforeach; ?>
</select>
</td>
<td><input type="text" name="field_placeholder[]" value="<?php echo esc_attr($f['placeholder']??''); ?>" placeholder="https://..." style="width:100%"></td>
<td>
<td style="padding:6px 8px">
<input type="text" name="field_placeholder[]" value="<?php echo esc_attr($f['placeholder']??''); ?>" placeholder="https://..." style="width:100%">
</td>
<td style="padding:6px 8px">
<textarea name="field_options[]" rows="2"
placeholder="Option 1&#10;Option 2&#10;Option 3"
placeholder="Option 1&#10;Option 2"
style="width:100%;font-size:.78rem;<?php echo ($f['type']??'text')==='select'?'':'display:none'; ?>"
class="wbf-options-field"><?php echo esc_textarea($f['options']??''); ?></textarea>
<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem;<?php echo ($f['type']??'text')==='select'?'display:none':''; ?>">—</span>
</td>
<td style="text-align:center">
<input type="checkbox" name="field_required[<?php echo $i; ?>]" value="1" <?php checked($f['required']??0,1); ?>>
<td style="text-align:center;padding:6px 8px">
<input type="checkbox" name="field_required[<?php echo $fi; ?>]" value="1" <?php checked($f['required']??0,1); ?>>
</td>
<td style="text-align:center">
<input type="checkbox" name="field_public[<?php echo $i; ?>]" value="1" <?php checked($f['public']??1,1); ?>>
<td style="text-align:center;padding:6px 8px">
<input type="checkbox" name="field_public[<?php echo $fi; ?>]" value="1" <?php checked($f['public']??1,1); ?>>
</td>
<td>
<button type="button" class="button" onclick="this.closest('tr').remove()" style="color:#dc2626;border-color:#fca5a5">✕</button>
<td style="padding:6px 8px">
<select name="field_category[]" style="width:100%;font-size:.82rem">
<option value="">— Ohne —</option>
<?php foreach ($cats as $co): ?>
<option value="<?php echo esc_attr($co['id']); ?>" <?php selected($f['category_id']??'',$co['id']); ?>>
<?php echo esc_html($co['icon'].' '.$co['name']); ?>
</option>
<?php endforeach; ?>
</select>
</td>
<td style="padding:6px 8px">
<button type="button" class="button" onclick="wbfRemoveRow(this)" style="color:#dc2626;border-color:#fca5a5;padding:2px 7px">✕</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div style="display:flex;gap:.75rem;align-items:center;margin-bottom:1.5rem">
<button type="button" class="button" id="wbfAddField">+ Feld hinzufügen</button>
<?php submit_button('💾 Felder speichern','primary','wbf_save_profile_fields',false); ?>
<div style="padding:10px 14px">
<button type="button" class="button" onclick="wbfAddField(this,'<?php echo esc_attr($cid); ?>')" style="font-size:.82rem">
+ Feld hinzufügen
</button>
</div>
</form>
</div>
</div>
<?php endforeach; ?>
<hr>
<h3> Hinweise</h3>
<ul style="color:#555;line-height:1.8;font-size:.9rem">
<li><strong>Schlüssel</strong>: Einmalig, nur Kleinbuchstaben/Zahlen/Unterstrich. Kann nach dem ersten Speichern nicht mehr geändert werden.</li>
<li><strong>Typ URL</strong>: Wird automatisch als klickbarer Link dargestellt.</li>
<li><strong>Typ Auswahlliste</strong>: Trage die Optionen zeilenweise ins Optionen-Feld ein.</li>
<li><strong>Öffentlich</strong>: Wenn deaktiviert, sieht nur der Nutzer selbst das Feld in seinem Profil.</li>
<li><strong>Pflicht</strong>: Verhindert das Speichern wenn das Feld leer ist.</li>
</ul>
<div style="display:flex;gap:.75rem;align-items:center;margin:1rem 0 1.5rem">
<?php submit_button('💾 Felder & Kategorien speichern','primary','wbf_save_profile_fields',false); ?>
</div>
</form>
<hr>
<h3> Hinweise</h3>
<ul style="color:#555;line-height:1.9;font-size:.9rem">
<li><strong>Schlüssel</strong>: Einmalig, nur Kleinbuchstaben/Zahlen/Unterstrich. Kann nach dem ersten Speichern nicht mehr geändert werden.</li>
<li><strong>Typ URL</strong>: Wird automatisch als klickbarer Link dargestellt.</li>
<li><strong>Typ Datum (Alter)</strong>: Speichert das Geburtsdatum und zeigt nur das Alter an.</li>
<li><strong>Typ Auswahlliste</strong>: Trage die Optionen zeilenweise ins Optionen-Feld ein.</li>
<li><strong>Öffentlich</strong>: Wenn deaktiviert, sieht nur der Nutzer selbst das Feld in seinem Profil.</li>
<li><strong>Pflicht</strong>: Verhindert das Speichern wenn das Feld leer ist.</li>
<li><strong>Kategorie</strong>: Felder werden im Profil nach Kategorie gruppiert dargestellt.</li>
</ul>
</div>
<script>
var wbfRowCount = <?php echo count($fields) ?: 1; ?>;
document.getElementById('wbfAddField').addEventListener('click', function() {
var wbfRowCount = <?php echo count($fields) + 100; ?>;
function wbfRemoveRow(btn) {
var tr = btn.closest('tr');
tr.remove();
}
function wbfAddField(btn, catId) {
var i = wbfRowCount++;
var tbody = btn.closest('.wbf-cat-body').querySelector('.wbf-field-rows');
// Remove "no fields" hint row if present
var hint = tbody.querySelector('.wbf-no-fields-hint');
if (hint) hint.remove();
// Build category <options>
var cats = <?php echo json_encode( array_values($cats) ); ?>;
var catOpts = '<option value="">— Ohne —</option>';
cats.forEach(function(c) {
var sel = (c.id === catId) ? ' selected' : '';
catOpts += '<option value="' + c.id + '"' + sel + '>' + c.icon + ' ' + c.name + '</option>';
});
var tr = document.createElement('tr');
tr.className = 'wbf-field-row';
tr.style.background = '#fff';
tr.innerHTML = `
<td><input type="text" name="field_key[]" placeholder="mein_feld" style="width:100%;font-family:monospace;font-size:.82rem"></td>
<td><input type="text" name="field_label[]" placeholder="Mein Feld" style="width:100%"></td>
<td><select name="field_type[]" style="width:100%" onchange="wbfToggleOptions(this)">
<option value="text">Text</option>
<option value="url">URL/Link</option>
<option value="textarea">Mehrzeiliger Text</option>
<option value="select">Auswahlliste</option>
<option value="number">Zahl</option>
</select></td>
<td><input type="text" name="field_placeholder[]" placeholder="..." style="width:100%"></td>
<td>
<textarea name="field_options[]" rows="2" placeholder="Option 1&#10;Option 2" style="width:100%;font-size:.78rem;display:none" class="wbf-options-field"></textarea>
<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem"></span>
</td>
<td style="text-align:center"><input type="checkbox" name="field_required[${i}]" value="1"></td>
<td style="text-align:center"><input type="checkbox" name="field_public[${i}]" value="1" checked></td>
<td><button type="button" class="button" onclick="this.closest('tr').remove()" style="color:#dc2626;border-color:#fca5a5">✕</button></td>`;
document.getElementById('wbfFieldRows').appendChild(tr);
});
tr.innerHTML =
'<td style="padding:6px 8px"><input type="text" name="field_key[]" placeholder="mein_feld" style="width:100%;font-family:monospace;font-size:.82rem"></td>' +
'<td style="padding:6px 8px"><input type="text" name="field_label[]" placeholder="Mein Feld" style="width:100%"></td>' +
'<td style="padding:6px 8px"><select name="field_type[]" style="width:100%" onchange="wbfToggleOptions(this)">' +
'<option value="text">Text</option>' +
'<option value="url">URL/Link</option>' +
'<option value="textarea">Mehrzeiliger Text</option>' +
'<option value="select">Auswahlliste</option>' +
'<option value="number">Zahl</option>' +
'<option value="date">Datum (Alter)</option>' +
'</select></td>' +
'<td style="padding:6px 8px"><input type="text" name="field_placeholder[]" placeholder="..." style="width:100%"></td>' +
'<td style="padding:6px 8px">' +
'<textarea name="field_options[]" rows="2" placeholder="Option 1\nOption 2" style="width:100%;font-size:.78rem;display:none" class="wbf-options-field"></textarea>' +
'<span class="wbf-options-placeholder" style="color:#ccc;font-size:.75rem">—</span>' +
'</td>' +
'<td style="text-align:center;padding:6px 8px"><input type="checkbox" name="field_required[new_' + i + ']" value="1"></td>' +
'<td style="text-align:center;padding:6px 8px"><input type="checkbox" name="field_public[new_' + i + ']" value="1" checked></td>' +
'<td style="padding:6px 8px"><select name="field_category[]" style="width:100%;font-size:.82rem">' + catOpts + '</select></td>' +
'<td style="padding:6px 8px"><button type="button" class="button" onclick="wbfRemoveRow(this)" style="color:#dc2626;border-color:#fca5a5;padding:2px 7px">✕</button></td>';
tbody.appendChild(tr);
}
function wbfToggleOptions(sel) {
var tr = sel.closest('tr');
@@ -3550,26 +3731,34 @@ function wbf_admin_prefixes() {
<!-- Formular -->
<div style="background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:18px">
<h3 style="margin:0 0 14px;font-size:.95rem"><?php echo $edit ? 'Bearbeiten: '.esc_html($edit->label) : 'Neuer Präfix'; ?></h3>
<?php
// PHP 8 safe: extract values once — avoids null->property warnings
$px_label = $edit ? $edit->label : '';
$px_color = $edit ? $edit->color : '#ffffff';
$px_bg = $edit ? $edit->bg_color : '#475569';
$px_order = $edit ? (int) $edit->sort_order : 0;
$px_id = $edit ? (int) $edit->id : 0;
?>
<h3 style="margin:0 0 14px;font-size:.95rem"><?php echo $edit ? 'Bearbeiten: '.esc_html($px_label) : 'Neuer Präfix'; ?></h3>
<form method="post">
<?php wp_nonce_field('wbf_prefix_nonce'); ?>
<?php if ($edit): ?><input type="hidden" name="prefix_id" value="<?php echo (int)$edit->id; ?>"><?php endif; ?>
<?php if ($edit): ?><input type="hidden" name="prefix_id" value="<?php echo $px_id; ?>"><?php endif; ?>
<table class="form-table" style="margin:0">
<tr>
<th style="padding:6px 10px 6px 0;width:110px">Label *</th>
<td><input type="text" name="label" value="<?php echo esc_attr($edit->label ?? ''); ?>" class="regular-text" required placeholder="z.B. GELÖST"></td>
<td><input type="text" name="label" value="<?php echo esc_attr($px_label); ?>" style="width:100%;box-sizing:border-box" required placeholder="z.B. GELÖST"></td>
</tr>
<tr>
<th style="padding:6px 10px 6px 0">Textfarbe</th>
<td><input type="color" name="color" value="<?php echo esc_attr($edit->color ?? '#ffffff'); ?>"></td>
<td><input type="color" name="color" value="<?php echo esc_attr($px_color); ?>"></td>
</tr>
<tr>
<th style="padding:6px 10px 6px 0">Hintergrund</th>
<td><input type="color" name="bg_color" value="<?php echo esc_attr($edit->bg_color ?? '#475569'); ?>"></td>
<td><input type="color" name="bg_color" value="<?php echo esc_attr($px_bg); ?>"></td>
</tr>
<tr>
<th style="padding:6px 10px 6px 0">Reihenfolge</th>
<td><input type="number" name="sort_order" value="<?php echo (int)($edit->sort_order ?? 0); ?>" class="small-text"></td>
<td><input type="number" name="sort_order" value="<?php echo $px_order; ?>" class="small-text"></td>
</tr>
</table>
<div style="margin-top:12px;display:flex;gap:8px">

View File

@@ -130,6 +130,13 @@ function wbf_admin_settings() {
// rules_content separat (nicht in $fields, da textarea mit eigener Behandlung)
$settings['rules_content'] = sanitize_textarea_field( $_POST['rules_content'] ?? '' );
// Checkbox-Felder explizit als '0' speichern wenn nicht angehakt,
// damit array_filter(...,'strlen') sie nicht wegwirft und der Default '1' greift.
$checkbox_fields = ['maintenance_mode', 'rules_enabled', 'rules_accept_required'];
foreach ( $checkbox_fields as $cb ) {
$settings[$cb] = isset($_POST[$cb]) && $_POST[$cb] === '1' ? '1' : '0';
}
// ignore_blocked_roles: kommagetrennte Liste der gewählten Rollen-Keys
$all_role_keys = array_keys( WBF_Roles::get_all() );
$checked_roles = array_intersect(