Update from Git Manager GUI

This commit is contained in:
2026-03-21 18:47:45 +01:00
parent 28e574d04c
commit 73e124d090
2 changed files with 658 additions and 37 deletions

View File

@@ -6,8 +6,10 @@
data.action = action;
data.nonce = WBF.nonce;
$.post(WBF.ajax_url, data, function (res) {
if (res.success) cb(res.data);
else if (errCb) errCb(res.data);
if (res && res.success) cb(res.data);
else if (errCb) errCb(res ? res.data : {message: 'Server-Fehler'});
}, 'json').fail(function(xhr) {
if (errCb) errCb({message: 'Verbindungsfehler (' + xhr.status + ')'});
});
}
@@ -51,10 +53,12 @@
$(document).on('click', '.wbf-reg-submit-btn', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_register', {
username: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-user').val(),
display_name: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-name').val(),
email: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-email').val(),
password: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-pass').val()
username: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-user').val(),
display_name: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-name').val(),
email: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-email').val(),
password: $(this).closest('.wbf-auth-box').find('.wbf-field-reg-pass').val(),
invite_code: $(this).closest('.wbf-auth-box').find('.wbf-field-invite-code').val().toUpperCase().trim(),
rules_accepted: $(this).closest('.wbf-auth-box').find('.wbf-field-rules-accept').is(':checked') ? '1' : ''
}, function () {
location.reload();
}, function (d) {
@@ -68,22 +72,85 @@
/* ── Neuer Thread Modal ─────────────────────────────────────── */
window.wbfShowNewThread = function (catId) {
if (WBF.logged_in !== 'yes') {
$('#wbfAuthModal').addClass('active');
return;
}
if (WBF.logged_in !== 'yes') { $('#wbfAuthModal').addClass('active'); return; }
if (catId) $('#wbfThreadCat').val(catId);
// Thread mode: alle Felder sichtbar
$('#wbfModalTitle').html('<i class="fas fa-plus-circle"></i> Neuen Thread erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel deines Threads');
$('#wbfContentRow, #wbfTagsRow').show();
$('#wbfPollSection').hide();
$('#wbfThreadSubmitRow').show();
$('#wbfNewThreadModal').addClass('active');
};
window.wbfShowNewPoll = function (catId) {
if (WBF.logged_in !== 'yes') { $('#wbfAuthModal').addClass('active'); return; }
if (catId) $('#wbfThreadCat').val(catId);
// Poll mode: Inhalt + Tags ausblenden
$('#wbfModalTitle').html('<i class="fas fa-chart-bar" style="color:#fbbf24"></i> Neue Umfrage erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel der Umfrage');
$('#wbfContentRow, #wbfTagsRow').hide();
$('#wbfThreadSubmitRow').hide();
$('#wbfPollSection').show();
$('#wbfNewThreadModal').addClass('active');
setTimeout(function() {
$('#wbfPollSection')[0].scrollIntoView({behavior:'smooth', block:'nearest'});
}, 150);
};
// Show poll section → Inhalt+Tags ausblenden
$(document).on('click', '#wbfShowPollSection', function () {
$('#wbfModalTitle').html('<i class="fas fa-chart-bar" style="color:#fbbf24"></i> Neue Umfrage erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel der Umfrage');
$('#wbfContentRow, #wbfTagsRow').hide();
$('#wbfThreadSubmitRow').hide();
$('#wbfPollSection').show();
});
// Entfernen → zurück zu Thread-Modus
$(document).on('click', '#wbfRemovePollSection', function () {
$('#wbfModalTitle').html('<i class="fas fa-plus-circle"></i> Neuen Thread erstellen');
$('#wbfThreadTitle').attr('placeholder', 'Titel deines Threads');
$('#wbfContentRow, #wbfTagsRow').show();
$('#wbfPollSection').hide();
$('#wbfThreadSubmitRow').show();
// Poll-Felder zurücksetzen
$('#wbfNewThreadPollQuestion').val('');
$('#wbfNTPollEndsAt').val('');
$('#wbfNTPollMulti').prop('checked', false);
$('#wbfNewThreadPollOptions .wbf-poll-opt-row').slice(2).remove();
$('#wbfNewThreadPollOptions .wbf-nt-poll-opt').val('');
});
// Poll option add/remove for new-thread poll
window.wbfRemoveNTPollOpt = function(btn) {
var $rows = $('#wbfNewThreadPollOptions .wbf-poll-opt-row');
if ($rows.length <= 2) return;
$(btn).closest('.wbf-poll-opt-row').remove();
};
$(document).on('click', '#wbfNTPollAddOpt', function () {
var count = $('#wbfNewThreadPollOptions .wbf-poll-opt-row').length;
if (count >= 10) return;
var n = count + 1;
$('#wbfNewThreadPollOptions').append(
'<div class="wbf-poll-opt-row">' +
'<input type="text" class="wbf-nt-poll-opt" placeholder="Option ' + n + '" 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>'
);
});
$(document).on('click', '#wbfSubmitThread', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
wbfPost('wbf_new_thread', {
var data = {
category_id: $('#wbfThreadCat').val(),
prefix_id: $('#wbfThreadPrefix').val() || '',
title: $('#wbfThreadTitle').val(),
content: $('#wbfThreadContent').val(),
tags: $('#wbfThreadTags').val()
}, function (d) {
};
wbfPost('wbf_new_thread', data, function (d) {
var base = WBF.forum_url || window.location.href.split('?')[0];
var sep = base.indexOf('?') !== -1 ? '&' : '?';
window.location.href = base + sep + 'forum_thread=' + d.thread_id;
@@ -93,6 +160,148 @@
});
});
/* ── Umfrage erstellen (eigener Submit-Button) ──────────────────────── */
$(document).on('click', '#wbfSubmitPollThread', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $msg = $('#wbfPollThreadMsg');
var data = {
category_id: $('#wbfThreadCat').val(),
prefix_id: $('#wbfThreadPrefix').val() || '',
title: $('#wbfThreadTitle').val(),
content: $('#wbfThreadContent').val(),
tags: $('#wbfThreadTags').val(),
poll_question: $('#wbfNewThreadPollQuestion').val().trim(),
poll_multi: $('#wbfNTPollMulti').is(':checked') ? '1' : '',
poll_ends_at: $('#wbfNTPollEndsAt').val()
};
var optIdx = 0;
$('#wbfNewThreadPollOptions .wbf-nt-poll-opt').each(function () {
var v = $(this).val().trim();
if (v) { data['poll_options[' + optIdx + ']'] = v; optIdx++; }
});
if (!data.poll_question) {
showMsg($msg, 'Bitte gib eine Umfrage-Frage ein.', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
return;
}
if (optIdx < 2) {
showMsg($msg, 'Mindestens 2 Antwortmöglichkeiten erforderlich.', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
return;
}
wbfPost('wbf_new_thread', data, function (d) {
var base = WBF.forum_url || window.location.href.split('?')[0];
var sep = base.indexOf('?') !== -1 ? '&' : '?';
window.location.href = base + sep + 'forum_thread=' + d.thread_id;
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
});
});
/* ── Poll Option hinzufügen / entfernen ─────────────────────────── */
window.wbfRemovePollOpt = function(btn) {
var $rows = $('#wbfPollOptions .wbf-poll-opt-row');
if ($rows.length <= 2) return; // min. 2 bleiben
$(btn).closest('.wbf-poll-opt-row').remove();
};
$(document).on('click', '#wbfPollAddOpt', function () {
var count = $('#wbfPollOptions .wbf-poll-opt-row').length;
if (count >= 10) return;
var n = count + 1;
$('#wbfPollOptions').append(
'<div class="wbf-poll-opt-row">' +
'<input type="text" class="wbf-poll-opt" placeholder="Option ' + n + '" 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>'
);
});
/* ── Poll erstellen (separates Modal) ───────────────────────────── */
$(document).on('click', '#wbfSubmitPoll', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $msg = $('#wbfPollMsg');
var opts = [];
$('.wbf-poll-opt').each(function () {
var v = $(this).val().trim();
if (v) opts.push(v);
});
var data = {
thread_id: $('#wbfPollThreadId').val(),
poll_question: $('#wbfPollQuestion').val(),
poll_multi: $('#wbfPollMulti').is(':checked') ? '1' : '',
poll_ends_at: $('#wbfPollEndsAt').val()
};
$.each(opts, function (i, v) { data['poll_options[' + i + ']'] = v; });
wbfPost('wbf_create_poll', data, function (d) {
showMsg($msg, d.message, true);
setTimeout(function () { location.reload(); }, 1200);
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-chart-bar"></i> Umfrage erstellen');
});
});
/* ── Poll Abstimmen ─────────────────────────────────────────────── */
$(document).on('submit', '.wbf-poll__form', function (e) {
e.preventDefault();
var $form = $(this);
var pollId = $form.data('poll-id');
var multi = $form.closest('.wbf-poll').data('multi');
var $btn = $form.find('button[type="submit"]').prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $msg = $form.find('.wbf-poll__msg');
var selected = [];
if (multi) {
$form.find('input[type="checkbox"]:checked').each(function () { selected.push($(this).val()); });
} else {
var val = $form.find('input[type="radio"]:checked').val();
if (val !== undefined) selected.push(val);
}
if (!selected.length) {
showMsg($msg, 'Bitte eine Option wählen.', false);
$btn.prop('disabled', false).html('<i class="fas fa-vote-yea"></i> Abstimmen');
return;
}
var postData = { poll_id: pollId };
$.each(selected, function (i, v) { postData['options[' + i + ']'] = v; });
wbfPost('wbf_vote_poll', postData, function (d) {
// Formular durch Ergebnisse ersetzen
var html = wbfRenderPollResults(d.results, d.my_votes, d.total, $form.closest('.wbf-poll').find('.wbf-poll__header'));
$form.replaceWith(html);
}, function (d) {
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-vote-yea"></i> Abstimmen');
});
});
function wbfRenderPollResults(results, myVotes, total, $header) {
var options = [];
$header.closest('.wbf-poll').find('.wbf-poll__form label').each(function () {
options.push($(this).text().trim());
});
var html = '';
$.each(options, function (i, label) {
var votes = results[i] || 0;
var pct = total > 0 ? Math.round(votes / total * 100) : 0;
var mine = myVotes.indexOf(i) !== -1 || myVotes.indexOf('' + i) !== -1;
html += '<div class="wbf-poll__result' + (mine ? ' wbf-poll__result--mine' : '') + '">' +
'<div class="wbf-poll__result-bar" style="width:' + pct + '%"></div>' +
'<div class="wbf-poll__result-content">' +
'<span class="wbf-poll__result-label">' + (mine ? '<i class="fas fa-check-circle" style="color:var(--c-primary)"></i> ' : '') + label + '</span>' +
'<span class="wbf-poll__result-pct">' + pct + '% <span style="color:var(--c-muted);font-size:.75em">(' + votes + ')</span></span>' +
'</div></div>';
});
html += '<div class="wbf-poll__footer"><i class="fas fa-users"></i> ' + total + ' Stimme' + (total !== 1 ? 'n' : '') + ' · <i class="fas fa-flag-checkered"></i> Abgestimmt</div>';
return html;
}
/* ══════════════════════════════════════════════════════════
FEATURE: Tag-Input Widget
══════════════════════════════════════════════════════════ */
@@ -302,18 +511,24 @@
});
/* ── Profil speichern ───────────────────────────────────────── */
$(document).on('click', '#wbfSaveProfile', function () {
$(document).on('click', '#wbfSaveProfile, #wbfSaveProfileCf', function () {
var $btn = $(this).prop('disabled', true);
wbfPost('wbf_update_profile', {
var $msg = $(this).siblings('.wbf-msg').length ? $(this).siblings('.wbf-msg') : $('#wbfProfileMsg');
var data = {
display_name: $('#wbfEditName').val(),
bio: $('#wbfEditBio').val(),
signature: $('#wbfEditSignature').val(),
new_password: $('#wbfNewPassword').val()
}, function (d) {
showMsg($('#wbfProfileMsg'), d.message, true);
};
// Benutzerdefinierte Profilfelder einsammeln
$('.wbf-cf-input').each(function () {
data[$(this).data('field')] = $(this).val();
});
wbfPost('wbf_update_profile', data, function (d) {
showMsg($msg, d.message, true);
$btn.prop('disabled', false);
}, function (d) {
showMsg($('#wbfProfileMsg'), d.message || 'Fehler', false);
showMsg($msg, d.message || 'Fehler', false);
$btn.prop('disabled', false);
});
});
@@ -1139,17 +1354,17 @@
/* ── Login: Remember-Me ─────────────────────────────────── */
$(document).on('click', '.wbf-login-submit-btn', function () {
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $btn = $(this).prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i>');
var $box = $(this).closest('.wbf-auth-box');
wbfPost('wbf_login', {
username: $(this).closest('.wbf-auth-box').find('.wbf-field-username').val(),
password: $(this).closest('.wbf-auth-box').find('.wbf-field-password').val(),
remember_me: $(this).closest('.wbf-auth-box').find('.wbf-field-remember').is(':checked') ? '1' : ''
username: $box.find('.wbf-field-username').val(),
password: $box.find('.wbf-field-password').val(),
remember_me: $box.find('.wbf-field-remember').is(':checked') ? '1' : ''
}, function () {
location.reload();
}, function (d) {
showMsg($btn.closest('.wbf-auth-box').find('.wbf-login-msg'), d.message || 'Fehler', false);
showMsg($box.find('.wbf-login-msg'), d.message || 'Fehler', false);
$btn.prop('disabled', false).html('<i class="fas fa-sign-in-alt"></i> Einloggen');
});
});
@@ -1180,7 +1395,7 @@
var $summary = $bar.find('.wbf-reaction-summary');
var $picker = $bar.find('.wbf-reaction-picker');
// Rebuild summary
var order = ['👍','❤️','😂','😮','😢','😡'];
var order = (WBF.reactions && WBF.reactions.length) ? WBF.reactions : ['👍','❤️','😂','😮','😢','😡'];
var html = '';
order.forEach(function(e) {
if (d.counts[e]) {
@@ -1567,4 +1782,181 @@
});
/* ══════════════════════════════════════════════════════════
FEATURE: Thread-Vorschau beim Hover
══════════════════════════════════════════════════════════ */
var $wbfPreviewTip = $('<div class="wbf-thread-preview-tip"></div>').appendTo('body');
var wbfPreviewTimer = null;
$(document).on('mouseenter', '.wbf-thread-row__title', function(e) {
var $row = $(this).closest('.wbf-thread-row');
var preview = $row.data('preview');
if (!preview || preview.length < 10) return;
clearTimeout(wbfPreviewTimer);
wbfPreviewTimer = setTimeout(function() {
$wbfPreviewTip.text(preview + (preview.length >= 160 ? '…' : '')).addClass('visible');
positionTip(e);
}, 280);
});
$(document).on('mousemove', '.wbf-thread-row__title', function(e) {
if ($wbfPreviewTip.hasClass('visible')) positionTip(e);
});
$(document).on('mouseleave', '.wbf-thread-row__title', function() {
clearTimeout(wbfPreviewTimer);
$wbfPreviewTip.removeClass('visible');
});
function positionTip(e) {
var tipW = $wbfPreviewTip.outerWidth();
var left = Math.min(e.pageX + 12, $(window).width() - tipW - 16);
var top = e.pageY + 18;
$wbfPreviewTip.css({ left: left, top: top });
}
/* ══════════════════════════════════════════════════════════
FEATURE: Ungelesene Threads markieren
══════════════════════════════════════════════════════════ */
/* ══════════════════════════════════════════════════════════
FEATURE: Thread-Abonnement
══════════════════════════════════════════════════════════ */
$(document).on('click', '.wbf-subscribe-btn', function(e) {
e.preventDefault();
var $btn = $(this).prop('disabled', true);
var threadId = $btn.data('thread');
wbfPost('wbf_toggle_subscribe', { thread_id: threadId }, function(d) {
$btn.prop('disabled', false);
if (d.subscribed) {
$btn.addClass('wbf-btn--primary')
.html('<i class="fas fa-bell"></i> Abonniert');
$btn.attr('title','Abonnement entfernen');
} else {
$btn.removeClass('wbf-btn--primary')
.html('<i class="fas fa-bell-slash"></i> Abonnieren');
$btn.attr('title','Thread abonnieren');
}
if (d.msg) { var $n = $('<div class="wbf-toast">'+d.msg+'</div>').appendTo('body'); setTimeout(function(){$n.remove();},3000); }
}, function() {
$btn.prop('disabled', false);
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Profil-Sichtbarkeit
══════════════════════════════════════════════════════════ */
$(document).on('click', '#wbfToggleProfileVis', function() {
var $btn = $(this).prop('disabled', true);
wbfPost('wbf_toggle_profile_visibility', {}, function(d) {
$btn.prop('disabled', false).data('state', d.public);
if (d.public) {
$btn.addClass('wbf-btn--primary').html('<i class="fas fa-eye"></i> Öffentlich');
} else {
$btn.removeClass('wbf-btn--primary').html('<i class="fas fa-eye-slash"></i> Privat');
}
if (d.msg) { var $n = $('<div class="wbf-toast">'+d.msg+'</div>').appendTo('body'); setTimeout(function(){$n.remove();},3000); }
}, function() { $btn.prop('disabled', false); });
});
/* ══════════════════════════════════════════════════════════
FEATURE: DSGVO Konto-Löschung
══════════════════════════════════════════════════════════ */
$(document).on('click', '#wbfGdprSubmit', function () {
var $btn = $(this);
var password = $('#wbfGdprPassword').val();
var confirmed = $('#wbfGdprConfirm').is(':checked');
var $msg = $('#wbfGdprMsg');
if (!password) {
showMsg($msg, 'Bitte Passwort eingeben.', false);
return;
}
if (!confirmed) {
showMsg($msg, 'Bitte Bestätigungs-Checkbox anhaken.', false);
return;
}
if (!confirm('Letzter Schritt: Konto wirklich unwiderruflich löschen?')) return;
$btn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin"></i> Wird gelöscht…');
wbfPost('wbf_delete_account', {
password: password,
confirm: '1'
}, function (d) {
// Erfolgreich — kurze Meldung, dann Weiterleitung
$('#wbfGdprBox').html(
'<p style="color:var(--c-green);font-size:.88rem"><i class="fas fa-circle-check"></i> ' +
(d.message || 'Konto gelöscht.') + '</p>'
);
setTimeout(function () {
window.location.href = d.redirect || WBF.forum_url || '/';
}, 2500);
}, function (d) {
showMsg($msg, d.message || 'Fehler.', false);
$btn.prop('disabled', false).html('<i class="fas fa-trash-can"></i> Konto endgültig löschen');
});
});
/* ══════════════════════════════════════════════════════════
FEATURE: Spam-Schutz — Formzeit + Honeypot
══════════════════════════════════════════════════════════ */
// Sende Honeypot + Zeitstempel mit Registrierung
$(document).on('click', '.wbf-reg-submit-btn', function() {
var $box = $(this).closest('.wbf-auth-box');
$box.find('[name="wbf_website"]').val(''); // Honeypot immer leer lassen
});
/* ══════════════════════════════════════════════════════════
FIX: Modal Teleport — verschiebe alle Modals zu <body>
damit kein Stacking-Context (sticky/transform) das
position:fixed Overlay blockieren kann.
══════════════════════════════════════════════════════════ */
$(function() {
// CSS-Variablen aus .wbf-wrap auf die teleportierten Modals übertragen
var wrapStyle = document.querySelector('.wbf-wrap');
$('.wbf-modal').each(function() {
var $modal = $(this);
// Klone den computed style für CSS-Variablen
if (wrapStyle) {
var vars = [
'--c-surface','--c-primary','--c-primary-l','--c-accent',
'--c-text','--c-text-dim','--c-muted','--c-border',
'--c-danger','--c-green','--c-warning','--radius','--radius-sm',
'--c-bg','--c-bg2','--c-surface2'
];
vars.forEach(function(v) {
var val = getComputedStyle(wrapStyle).getPropertyValue(v);
if (val) $modal[0].style.setProperty(v, val.trim());
});
}
$modal.appendTo('body');
});
});
/* ── Lesezeichen ────────────────────────────────────────────────────── */
$(document).on('click', '.wbf-bookmark-btn', function () {
var $btn = $(this);
var threadId = $btn.data('thread');
wbfPost('wbf_toggle_bookmark', { thread_id: threadId }, function (d) {
if (d.bookmarked) {
$btn.addClass('wbf-bookmarked').attr('title', 'Lesezeichen entfernen');
$btn.find('i').removeClass('far').addClass('fas');
} else {
$btn.removeClass('wbf-bookmarked').attr('title', 'Lesezeichen hinzufügen');
$btn.find('i').removeClass('fas').addClass('far');
}
});
});
}(jQuery));