4 Commits
2.5.4 ... 2.5.5

Author SHA1 Message Date
b0bec83593 Update from Git Manager GUI 2026-03-29 13:35:46 +02:00
7ef72641f6 Update from Git Manager GUI 2026-03-29 13:35:44 +02:00
c4c02c1b9d Upload via Git Manager GUI - mc-multiserver-gallery-pro.php 2026-03-29 11:35:43 +00:00
0ab87297ac README.md aktualisiert 2026-03-01 11:21:10 +00:00
9 changed files with 2785 additions and 411 deletions

View File

@@ -144,4 +144,10 @@ Die StatusAPI muss auf deinem Minecraft-Bungeecord laufen, um die Kommunikation
A: Du musst sicherstellen, dass auf deinem Minecraft-Bungeecord die [StatusAPI](https://git.viper.ipv64.net/M_Viper/StatusAPI) läuft, die mit diesem WordPress-Plugin kommunizieren kann. Der Server muss den Befehl `/verify` entgegennehmen und an deine WordPress-Website melden.
**F: Wo finde ich die Server ID?**
A: Gehe im WordPress Dashboard auf "MC Gallery PRO" > "MC Server". Die ID ist die Zahl, die in der URL steht, wenn du einen Server bearbeitest (z.B. `post.php?post=15&action=edit` → ID ist 15).
A: Gehe im WordPress Dashboard auf "MC Gallery PRO" > "MC Server". Die ID ist die Zahl, die in der URL steht, wenn du einen Server bearbeitest (z.B. `post.php?post=15&action=edit` → ID ist 15).
---
**Copyright © 2026 - M_Viper - Alle Rechte vorbehalten**
Die unbefugte Vervielfältigung, Verbreitung oder Weitergabe dieses Plugins ist strafbar und wird rechtlich verfolgt.

View File

@@ -45,6 +45,19 @@
background-color: var(--mc-primary-hover);
}
.mc-upload-wrapper .mc-btn-secondary,
.mc-combined-wrapper .mc-btn-secondary {
background-color: rgba(255,255,255,.07);
color: #94a3b8;
border: 1px solid rgba(100,116,139,.3);
}
.mc-upload-wrapper .mc-btn-secondary:hover,
.mc-combined-wrapper .mc-btn-secondary:hover {
background-color: rgba(255,255,255,.12);
color: #cbd5e1;
border-color: rgba(100,116,139,.5);
}
/* =================================================================== */
/* === 2. MODAL SPECIFIC CSS (Fix für das Popup) ================== */
@@ -739,6 +752,37 @@
color: white;
}
/* Like-Button in Lightbox */
.mc-lightbox-like-wrap {
margin-top: 14px;
display: flex;
justify-content: center;
}
.mc-like-btn--lightbox {
position: static !important;
display: inline-flex !important;
align-items: center;
gap: 8px;
padding: 9px 20px !important;
border-radius: 24px !important;
font-size: 14px !important;
background: rgba(255,255,255,.1) !important;
border: 1px solid rgba(255,255,255,.2) !important;
color: #fff !important;
cursor: pointer;
transition: background .15s, border-color .15s !important;
}
.mc-like-btn--lightbox:hover {
background: rgba(239,68,68,.25) !important;
border-color: rgba(239,68,68,.4) !important;
}
.mc-like-btn--lightbox.mc-like-btn--active {
background: rgba(239,68,68,.2) !important;
border-color: rgba(239,68,68,.5) !important;
}
.mc-like-btn--lightbox .mc-like-icon { width: 18px; height: 18px; }
.mc-like-btn--lightbox .mc-like-label { font-size: 13px; opacity: .85; }
.mc-lightbox-close {
position: absolute;
top: -40px;
@@ -782,4 +826,314 @@
max-width: 90%;
padding: 20px;
}
}
/* =================================================================== */
/* === LIKE / VOTE BUTTON =========================================== */
/* =================================================================== */
.mc-like-btn {
position: absolute;
bottom: 8px;
right: 8px;
display: flex;
align-items: center;
gap: 5px;
padding: 5px 10px;
background: rgba(0,0,0,0.65);
border: 1px solid rgba(255,255,255,0.15);
border-radius: 20px;
color: #fff;
font-size: 12px;
font-weight: 600;
cursor: pointer;
z-index: 10;
transition: background 0.15s, border-color 0.15s, transform 0.1s;
backdrop-filter: blur(4px);
}
.mc-like-btn:hover {
background: rgba(239,68,68,0.3);
border-color: rgba(239,68,68,0.5);
transform: scale(1.08);
}
.mc-like-btn:active { transform: scale(0.96); }
.mc-like-btn:disabled { opacity: 0.5; cursor: wait; }
.mc-like-btn--active {
background: rgba(239,68,68,0.25);
border-color: rgba(239,68,68,0.6);
color: #fca5a5;
}
.mc-like-btn--active .mc-like-icon { stroke: #ef4444; fill: rgba(239,68,68,0.3); }
.mc-like-icon {
width: 14px;
height: 14px;
stroke: currentColor;
flex-shrink: 0;
transition: stroke 0.15s, fill 0.15s;
}
.mc-like-count { min-width: 12px; text-align: center; }
/* =================================================================== */
/* === KOMBINIERTER UPLOAD SHORTCODE ================================ */
/* =================================================================== */
.mc-combined-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
}
.mc-combined-two-col {
display: flex;
flex-direction: column;
gap: 0;
}
.mc-combined-upload-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10px;
width: 100%;
padding: 13px 20px;
background: var(--mc-primary);
color: #fff;
border: none;
border-radius: var(--mc-radius);
font-size: 15px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
position: relative;
}
.mc-combined-upload-btn:hover { background: var(--mc-primary-hover); }
.mc-combined-method-tag {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
font-size: 10px;
font-weight: 600;
letter-spacing: .05em;
text-transform: uppercase;
padding: 2px 8px;
border-radius: 20px;
background: rgba(255,255,255,0.18);
color: #fff;
white-space: nowrap;
}
.mc-combined-divider {
display: flex;
align-items: center;
gap: 10px;
margin: 14px 0;
color: #64748b;
font-size: .75rem;
text-transform: uppercase;
letter-spacing: .06em;
}
.mc-combined-divider::before,
.mc-combined-divider::after {
content: '';
flex: 1;
height: 1px;
background: rgba(100,116,139,.25);
}
.mc-combined-verified-row {
display: flex;
align-items: center;
gap: 7px;
margin-top: 10px;
padding: 8px 14px;
background: rgba(34,197,94,.08);
border: 1px solid rgba(34,197,94,.2);
border-radius: 8px;
color: #22c55e;
font-size: .83rem;
font-weight: 500;
}
.mc-combined-verified-row svg {
flex-shrink: 0;
stroke: #22c55e;
}
.mc-combined-verified-row strong { font-weight: 700; }
/* Vote Board */
.mc-vote-board .mc-player-header h2 { font-size: 1.3rem; }
/* =========================================================================
VOTE BUTTONS (Daumen hoch / runter)
========================================================================= */
.mc-vote-wrap {
position: absolute;
bottom: 8px;
right: 8px;
display: flex;
gap: 4px;
z-index: 10;
}
.mc-vote-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 9px;
background: rgba(0,0,0,.65);
border: 1px solid rgba(255,255,255,.15);
border-radius: 16px;
color: #fff;
font-size: 11px;
font-weight: 600;
cursor: pointer;
transition: background .15s, border-color .15s, transform .1s;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
line-height: 1;
}
.mc-vote-btn svg {
width: 13px;
height: 13px;
flex-shrink: 0;
stroke: #fff;
transition: stroke .15s, fill .15s;
}
.mc-vote-btn:hover { transform: scale(1.08); }
.mc-vote-btn:active { transform: scale(.94); }
.mc-vote-btn:disabled { opacity: .5; cursor: wait; }
/* Daumen hoch aktiv */
.mc-vote-up.mc-vote-btn--active {
background: rgba(34,197,94,.25);
border-color: rgba(34,197,94,.5);
color: #86efac;
}
.mc-vote-up.mc-vote-btn--active svg { stroke: #22c55e; fill: #22c55e; }
.mc-vote-up:not(.mc-vote-btn--active):hover { background: rgba(34,197,94,.2); border-color: rgba(34,197,94,.4); }
/* Daumen runter aktiv */
.mc-vote-down.mc-vote-btn--active {
background: rgba(239,68,68,.25);
border-color: rgba(239,68,68,.5);
color: #fca5a5;
}
.mc-vote-down.mc-vote-btn--active svg { stroke: #ef4444; fill: #ef4444; }
.mc-vote-down:not(.mc-vote-btn--active):hover { background: rgba(239,68,68,.2); border-color: rgba(239,68,68,.4); }
.mc-vote-count { min-width: 10px; text-align: center; }
/* In der Lightbox: größer und zentriert */
.mc-vote-wrap--lightbox {
position: static;
justify-content: center;
margin-top: 14px;
}
.mc-vote-wrap--lightbox .mc-vote-btn {
padding: 8px 18px;
font-size: 13px;
border-radius: 24px;
}
.mc-vote-wrap--lightbox .mc-vote-btn svg { width: 16px; height: 16px; }
/* =========================================================================
COMBINED UPLOAD WRAPPER
========================================================================= */
/* Alle Buttons im Combined-Wrapper: gleiche Basis wie Upload-Button */
.mc-combined-wrapper .mc-btn,
.mc-combined-wrapper a.mc-btn,
.mc-combined-upload-btn {
display: flex !important;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
padding: 12px 20px;
border: none;
border-radius: var(--mc-radius);
font-weight: 600;
font-size: 15px;
cursor: pointer;
transition: background-color 0.2s ease, opacity 0.2s ease;
white-space: nowrap;
position: relative;
text-decoration: none;
box-sizing: border-box;
}
.mc-combined-upload-btn {
margin-top: 0;
}
.mc-combined-method-tag {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
font-size: 10px;
font-weight: 600;
letter-spacing: .05em;
text-transform: uppercase;
background: rgba(255,255,255,.15);
padding: 2px 8px;
border-radius: 20px;
color: rgba(255,255,255,.85);
}
.mc-combined-two-col {
display: flex;
flex-direction: column;
gap: 0;
}
.mc-combined-divider {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 0;
color: rgba(148,163,184,.6);
font-size: 11px;
text-transform: uppercase;
letter-spacing: .06em;
}
.mc-combined-divider::before,
.mc-combined-divider::after {
content: '';
flex: 1;
height: 1px;
background: rgba(100,116,139,.2);
}
.mc-combined-verified-row {
display: flex;
align-items: center;
gap: 7px;
margin-top: 8px;
padding: 7px 12px;
background: rgba(34,197,94,.08);
border: 1px solid rgba(34,197,94,.2);
border-radius: 8px;
color: #22c55e;
font-size: 13px;
}
.mc-combined-verified-row svg {
flex-shrink: 0;
stroke: #22c55e;
}
/* =========================================================================
VOTE BOARD
========================================================================= */
.mc-vote-board .mc-player-avatar { font-size: 24px; }
/* Forum-Button — blaue Variante */
.mc-combined-forum-btn {
background-color: #2563eb !important;
color: #fff !important;
}
.mc-combined-forum-btn:hover {
background-color: #1d4ed8 !important;
opacity: 1 !important;
}

View File

@@ -0,0 +1,405 @@
/*
* MC Gallery PRO — Forum Bridge Styles
* Pfad: assets/css/mc-gallery-forum-bridge.css
*/
/* ── Verify-Panel unter der Gallery ──────────────────────────────────────── */
.mc-bridge-verify-wrap {
margin-top: 16px;
}
.mc-bridge-divider {
display: flex;
align-items: center;
gap: 10px;
margin: 14px 0;
color: var(--wbf-text-muted, #94a3b8);
font-size: .8rem;
text-transform: uppercase;
letter-spacing: .06em;
}
.mc-bridge-divider::before,
.mc-bridge-divider::after {
content: '';
flex: 1;
height: 1px;
background: var(--wbf-border, rgba(100,116,139,.25));
}
/* ── Forum-Card — gemeinsame Hülle ───────────────────────────────────────── */
.mc-bridge-card {
border-radius: 12px;
border: 1px solid rgba(56,189,248,.15);
background: rgba(15,23,42,.55);
overflow: hidden;
}
.mc-bridge-card--warn {
border-color: rgba(245,158,11,.22);
}
.mc-bridge-card--ok {
border-color: rgba(34,197,94,.22);
}
.mc-bridge-card-inner {
display: flex;
align-items: center;
gap: 16px;
padding: 16px 20px;
flex-wrap: wrap;
}
/* Quadratisches Icon-Kästchen */
.mc-bridge-card-icon {
width: 44px;
height: 44px;
border-radius: 10px;
background: rgba(56,189,248,.09);
border: 1px solid rgba(56,189,248,.18);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.mc-bridge-card-icon i {
font-size: 1.15rem;
color: #38bdf8;
}
/* Text-Block */
.mc-bridge-card-text {
flex: 1;
min-width: 0;
}
.mc-bridge-card-label {
font-size: .7rem;
text-transform: uppercase;
letter-spacing: .07em;
color: #475569;
margin-bottom: 3px;
}
.mc-bridge-card-desc {
font-size: .84rem;
color: #94a3b8;
line-height: 1.4;
}
.mc-bridge-card-desc strong {
color: #cbd5e1;
font-weight: 500;
}
/* Action-Spalte */
.mc-bridge-card-actions {
display: flex;
flex-direction: column;
gap: 7px;
flex-shrink: 0;
}
/* Haupt-Button */
.mc-bridge-action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
padding: 8px 16px;
border-radius: 8px;
border: none;
cursor: pointer;
background: #38bdf8;
color: #0c1222;
font-size: .82rem;
font-weight: 700;
letter-spacing: .01em;
white-space: nowrap;
text-decoration: none;
transition: opacity .15s;
}
.mc-bridge-action-btn:hover { opacity: .85; text-decoration: none; color: #0c1222; }
.mc-bridge-action-btn--warn {
background: #f59e0b;
color: #1c1008;
}
/* Sekundär-Button */
.mc-bridge-action-btn-ghost {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
padding: 7px 16px;
border-radius: 8px;
cursor: pointer;
background: transparent;
border: 1px solid rgba(100,116,139,.3);
color: #64748b;
font-size: .78rem;
font-weight: 500;
white-space: nowrap;
text-decoration: none;
transition: border-color .15s, color .15s;
}
.mc-bridge-action-btn-ghost:hover {
border-color: rgba(100,116,139,.5);
color: #94a3b8;
text-decoration: none;
}
.mc-bridge-action-btn-ghost i { font-size: .82rem; }
/* Verifiziert-Badge */
.mc-bridge-verified-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 7px 16px;
border-radius: 20px;
background: rgba(34,197,94,.1);
border: 1px solid rgba(34,197,94,.25);
color: #22c55e;
font-size: .8rem;
font-weight: 600;
flex-shrink: 0;
}
/* Nicht eingeloggt / nicht verknüpft — jetzt via .mc-bridge-card */
.mc-bridge-not-logged,
.mc-bridge-not-linked {
display: none; /* HTML-Ersatz durch mc-bridge-card */
}
.mc-bridge-btn-inline {
color: #38bdf8;
text-decoration: none;
font-weight: 600;
}
.mc-bridge-btn-inline:hover { text-decoration: underline; }
/* Eingeloggt & verknüpft — jetzt via .mc-bridge-card */
.mc-bridge-forum-linked { display: none; }
.mc-bridge-head-sm {
width: 44px;
height: 44px;
border-radius: 8px;
flex-shrink: 0;
image-rendering: pixelated;
}
.mc-bridge-forum-name { font-weight: 500; color: #e2e8f0; }
.mc-bridge-arrow { color: #475569; margin: 0 2px; }
.mc-bridge-mc-name-sm { font-weight: 600; color: #38bdf8; }
.mc-bridge-unverified-hint {
display: block;
font-size: .75rem;
color: #f59e0b;
margin-top: 2px;
}
.mc-bridge-verify-trigger {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 8px 16px;
background: #f59e0b;
color: #1c1008;
border: none;
border-radius: 8px;
font-size: .82rem;
font-weight: 700;
cursor: pointer;
transition: opacity .15s;
white-space: nowrap;
flex-shrink: 0;
}
.mc-bridge-verify-trigger:hover { opacity: .85; }
.mc-bridge-verify-trigger:disabled { opacity: .5; cursor: wait; }
.mc-bridge-success {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 18px;
background: rgba(34,197,94,.08);
border: 1px solid rgba(34,197,94,.22);
border-radius: 12px;
color: #22c55e;
font-size: .88rem;
font-weight: 500;
}
.mc-bridge-success i { font-size: 1.1rem; flex-shrink: 0; }
.mc-bridge-error {
margin-top: 8px;
padding: 10px 14px;
background: rgba(239,68,68,.08);
border: 1px solid rgba(239,68,68,.2);
border-radius: 8px;
color: #f87171;
font-size: .83rem;
}
.mc-bridge-error i { margin-right: 5px; }
/* ── Profil-Tab: Minecraft ────────────────────────────────────────────────── */
.mc-bridge-profile-tab {
padding: 4px 0;
}
.mc-bridge-intro {
color: var(--wbf-text-muted, #94a3b8);
margin-bottom: 14px;
font-size: .9rem;
}
/* Verknüpfte Karte */
.mc-bridge-linked-card {
display: flex;
align-items: center;
gap: 14px;
padding: 14px 16px;
border-radius: 10px;
border: 1px solid rgba(100,116,139,.2);
background: rgba(30,41,59,.5);
flex-wrap: wrap;
}
.mc-bridge-linked-card.is-verified {
border-color: rgba(34,197,94,.3);
background: rgba(34,197,94,.05);
}
.mc-bridge-linked-card.is-pending {
border-color: rgba(245,158,11,.25);
background: rgba(245,158,11,.04);
}
.mc-bridge-head {
width: 48px;
height: 48px;
border-radius: 6px;
image-rendering: pixelated;
flex-shrink: 0;
}
.mc-bridge-linked-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.mc-bridge-mc-name {
font-size: 1.05rem;
font-weight: 700;
color: var(--wbf-text, #e2e8f0);
}
.mc-bridge-server-hint {
font-size: .78rem;
color: var(--wbf-text-muted, #64748b);
}
.mc-bridge-server-hint i { margin-right: 3px; }
/* Badges */
.mc-bridge-badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 2px 9px;
border-radius: 20px;
font-size: .78rem;
font-weight: 600;
}
.mc-bridge-badge--ok { background: rgba(34,197,94,.15); color: #22c55e; }
.mc-bridge-badge--wait { background: rgba(245,158,11,.15); color: #f59e0b; }
/* Buttons */
.mc-bridge-actions {
display: flex;
gap: 8px;
margin-left: auto;
}
.mc-bridge-btn {
padding: 6px 12px;
border: none;
border-radius: 6px;
font-size: .82rem;
font-weight: 600;
cursor: pointer;
transition: opacity .15s;
}
.mc-bridge-btn:disabled { opacity: .5; cursor: wait; }
.mc-bridge-btn--link {
background: var(--wbf-accent, #38bdf8);
color: #0f172a;
}
.mc-bridge-btn--link:hover { opacity: .85; }
.mc-bridge-btn--approve {
background: rgba(34,197,94,.2);
color: #22c55e;
}
.mc-bridge-btn--approve:hover { background: rgba(34,197,94,.35); }
.mc-bridge-btn--unlink {
background: rgba(239,68,68,.15);
color: #f87171;
}
.mc-bridge-btn--unlink:hover { background: rgba(239,68,68,.28); }
/* Pending-Hinweis */
.mc-bridge-pending-hint {
margin-top: 10px;
padding: 9px 13px;
background: rgba(245,158,11,.07);
border-left: 3px solid #f59e0b;
border-radius: 0 6px 6px 0;
font-size: .85rem;
color: #fcd34d;
line-height: 1.5;
}
.mc-bridge-pending-hint i { margin-right: 5px; }
/* Verknüpfungs-Formular */
.mc-bridge-link-form {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: flex-end;
margin-bottom: 10px;
}
.mc-bridge-link-form label {
display: flex;
flex-direction: column;
gap: 4px;
font-size: .82rem;
color: var(--wbf-text-muted, #94a3b8);
}
.mc-bridge-link-form input,
.mc-bridge-link-form select {
padding: 7px 11px;
background: rgba(15,23,42,.6);
border: 1px solid rgba(100,116,139,.3);
border-radius: 6px;
color: var(--wbf-text, #e2e8f0);
font-size: .9rem;
min-width: 180px;
}
.mc-bridge-link-form input:focus,
.mc-bridge-link-form select:focus {
outline: none;
border-color: var(--wbf-accent, #38bdf8);
}
/* ── Admin-Badge in User-Tabelle ──────────────────────────────────────────── */
.mc-bridge-admin-mc-badge {
display: inline-flex;
align-items: center;
gap: 5px;
padding: 2px 7px;
background: rgba(56,189,248,.1);
border: 1px solid rgba(56,189,248,.25);
border-radius: 20px;
font-size: .78rem;
color: #38bdf8;
vertical-align: middle;
}
.mc-bridge-admin-mc-badge img {
width: 20px;
height: 20px;
border-radius: 3px;
image-rendering: pixelated;
}

41
assets/js/forum-bridge.js Normal file
View File

@@ -0,0 +1,41 @@
(function($){
$(document).on('click', '.mc-bridge-verify-trigger', function(){
console.log('[MC-Gallery] Forum-Login Button geklickt!');
if (typeof $ === 'undefined') { alert('jQuery nicht geladen!'); return; }
var $btn = $(this).prop('disabled', true);
var ajax = $btn.data('ajax');
var nonce = $btn.data('nonce');
var server = $btn.data('server') || $btn.closest('[data-server]').data('server') || '';
$btn.html('<i class="fas fa-spinner fa-spin"></i> Prüfe…');
$.post(ajax, {
action: 'mc_forum_verify_upload',
nonce: nonce,
server_id: server
})
.done(function(r){
if ( r.success ) {
$(document).trigger('mc_gallery_forum_verified', [ r.data ]);
if ( typeof window.mcGalleryOnVerified === 'function' ) {
window.mcGalleryOnVerified( r.data );
}
$btn.closest('.mc-bridge-verify-wrap')
.html('<div class="mc-bridge-success">'
+ '<i class="fas fa-check-circle"></i> Verifiziert als <strong>'
+ $('<span>').text(r.data.mc_username).html()
+ '</strong></div>');
} else {
var msg = (r.data && r.data.message) ? r.data.message : 'Fehler bei der Verifikation.';
$btn.closest('.mc-bridge-verify-wrap').find('.mc-bridge-error').remove();
$btn.closest('.mc-bridge-forum-linked, .mc-bridge-not-linked')
.after('<p class="mc-bridge-error"><i class="fas fa-times-circle"></i> ' + msg + '</p>');
$btn.prop('disabled', false).html('<i class="fas fa-sign-in-alt"></i> Mit Forum-Login verifizieren');
}
})
.fail(function(){
$btn.prop('disabled', false).html('<i class="fas fa-sign-in-alt"></i> Mit Forum-Login verifizieren');
alert('Netzwerkfehler. Bitte erneut versuchen.');
});
});
})(jQuery);

View File

@@ -119,16 +119,15 @@
function loadAlbums() {
const $select = $('#mc-upload-album');
$select.html('<option value="">Kein Album</option>').prop('disabled', true);
if (!sessionData.token || !sessionData.verified) return;
// Auch ohne Token laden, wenn Forum-Session aktiv
if ((!sessionData.token && !sessionData.verified) || !sessionData.username) return;
fetch(api + '/albums', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: sessionData.token,
username: sessionData.username,
server_id: sessionData.serverId
token: sessionData.token || '',
username: sessionData.username,
server_id: sessionData.serverId || ''
})
})
.then(response => response.json())
@@ -235,6 +234,23 @@
resetModal();
loadServers();
// Wenn User bereits forum-verifiziert ist: direkt zu Schritt 3 springen
if (mcGalleryPro.forumVerified) {
const fv = mcGalleryPro.forumVerified;
sessionData.verified = true;
sessionData.username = fv.mc_username;
sessionData.serverId = fv.server_id;
sessionData.token = null;
$('#mc-session-user').text(fv.display_name || fv.mc_username);
$('#mc_form_username').val(fv.mc_username);
$('#mc_form_server').val(fv.server_id);
$('#mc_form_token').val('');
switchStep(3);
loadAlbums();
}
}
function closeModal() {
@@ -245,6 +261,30 @@
setTimeout(resetModal, 300);
}
// Forum-Login: Auf globales Event reagieren
$(document).on('mc_gallery_forum_verified', function(e, data) {
if (!data || !data.mc_username || !data.server_id) {
showFeedback('Forum-Login: Ungültige Antwort vom Server.', 'error');
return;
}
sessionData.verified = true;
sessionData.username = data.mc_username;
sessionData.serverId = data.server_id;
sessionData.token = null; // Kein Token nötig
$('#mc-session-user').text(data.display_name || data.mc_username);
$('#mc_form_username').val(data.mc_username);
$('#mc_form_server').val(data.server_id);
$('#mc_form_token').val('');
showFeedback('✓ Erfolgreich mit Forum-Login verifiziert!', 'success');
setTimeout(() => {
switchStep(3);
console.log('[MC-Gallery] mc_gallery_forum_verified: switchStep(3) ausgeführt, Upload-Schritt sollte sichtbar sein.');
loadAlbums();
}, 800);
});
$(document).ready(function () {
loadServers();
@@ -264,6 +304,53 @@
}
});
// Forum-Login-Button im Modal (Schritt 1) — einmalig binden, NICHT inside btn-generate
$(document).on('click', '#mc-btn-forum-login', function () {
const serverId = $('#mc-upload-server').val();
if (!serverId || serverId === '') {
showFeedback('Bitte wähle einen Server aus.', 'error');
return;
}
const $btn = $(this);
const originalText = $btn.html();
$btn.prop('disabled', true).html('<span class="mc-loading"></span> Prüfe Forum-Login...');
$.ajax({
url: ajaxUrl,
method: 'POST',
dataType: 'json',
data: {
action: 'mc_forum_verify_upload',
nonce: mcGalleryPro.forumNonce || mcGalleryPro.nonce,
server_id: serverId
},
success: function (data) {
$btn.prop('disabled', false).html(originalText);
if (data.success && data.data && data.data.mc_username) {
sessionData.verified = true;
sessionData.username = data.data.mc_username;
sessionData.serverId = data.data.server_id;
sessionData.token = null;
$('#mc-session-user').text(data.data.display_name || data.data.mc_username);
$('#mc_form_username').val(data.data.mc_username);
$('#mc_form_server').val(data.data.server_id);
$('#mc_form_token').val('');
showFeedback('✓ Erfolgreich mit Forum-Login verifiziert!', 'success');
setTimeout(() => { switchStep(3); loadAlbums(); }, 800);
} else {
const msg = (data.data && data.data.message) ? data.data.message : (data.message || 'Verifizierung fehlgeschlagen.');
showFeedback(msg, 'error');
}
},
error: function () {
$btn.prop('disabled', false).html(originalText);
showFeedback('Netzwerkfehler bei der Forum-Login-Prüfung', 'error');
}
});
});
$('#mc-btn-generate').on('click', function () {
const username = $('#mc-upload-username').val().trim();
const serverId = $('#mc-upload-server').val();
@@ -432,9 +519,9 @@
const formData = new FormData();
formData.append('action', 'mc_gallery_create_album');
formData.append('mc_upload_nonce', mcGalleryPro.nonce);
formData.append('mc_token', sessionData.token);
formData.append('mc_token', sessionData.token || '');
formData.append('mc_username', sessionData.username);
formData.append('mc_server_id', sessionData.serverId);
formData.append('mc_server_id', sessionData.serverId || '');
formData.append('album_name', albumName);
fetch(ajaxUrl, {
@@ -477,10 +564,16 @@
return;
}
// Auch ohne Token erlauben, wenn Forum-Session aktiv
if (!sessionData.verified || !sessionData.username) {
showFeedback('Bitte verifiziere dich zuerst.', 'error');
return;
}
const formData = new FormData();
formData.append('action', 'mc_gallery_upload');
formData.append('mc_upload_nonce', mcGalleryPro.nonce);
formData.append('mc_token', sessionData.token);
formData.append('mc_token', sessionData.token || '');
formData.append('mc_username', sessionData.username);
formData.append('mc_server_id', sessionData.serverId);
formData.append('mc_album_id', $('#mc-upload-album').val() || '');
@@ -546,6 +639,77 @@
$('#mc-upload-file').click();
});
// === VOTE BUTTONS (Daumen hoch / runter) ===
if (mcGalleryPro.votingEnabled) {
// Cookie-Zustand initialisieren
function initVoteBtns() {
$('.mc-vote-wrap').each(function() {
const aid = $(this).data('attach-id');
const up = document.cookie.split(';').some(c => c.trim() === 'mc_vote_' + aid + '=up');
const down = document.cookie.split(';').some(c => c.trim() === 'mc_vote_' + aid + '=down');
if (up) $(this).find('.mc-vote-up').addClass('mc-vote-btn--active');
if (down) $(this).find('.mc-vote-down').addClass('mc-vote-btn--active');
});
}
initVoteBtns();
$(document).on('click', '.mc-vote-btn', function(e) {
e.preventDefault();
e.stopPropagation();
const $btn = $(this);
const $wrap = $btn.closest('.mc-vote-wrap');
const aid = $wrap.data('attach-id');
const nonce = $wrap.data('nonce');
const type = $btn.data('type'); // 'up' oder 'down'
const ck = 'mc_vote_' + aid;
const current = document.cookie.split(';').find(c => c.trim().startsWith(ck + '='));
const curVal = current ? current.trim().split('=')[1] : null;
if ($btn.prop('disabled')) return;
$wrap.find('.mc-vote-btn').prop('disabled', true);
// Toggle-Logik: nochmal klicken = entfernen
const isActive = $btn.hasClass('mc-vote-btn--active');
const action = isActive ? 'remove' : 'add';
$.post(ajaxUrl, {
action: 'mc_gallery_vote',
attach_id: aid,
vote_type: type,
vote_action: action,
nonce: nonce
})
.done(function(r) {
if (r.success) {
$wrap.find('.mc-vote-up-count').text(r.data.votes_up);
$wrap.find('.mc-vote-down-count').text(r.data.votes_down);
if (action === 'add') {
// Gegenpart deaktivieren falls aktiv
const other = type === 'up' ? 'down' : 'up';
$wrap.find('.mc-vote-' + other).removeClass('mc-vote-btn--active');
$btn.addClass('mc-vote-btn--active');
const exp = new Date(); exp.setDate(exp.getDate() + 30);
document.cookie = ck + '=' + type + '; expires=' + exp.toUTCString() + '; path=/; SameSite=Lax';
} else {
$btn.removeClass('mc-vote-btn--active');
document.cookie = ck + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/';
}
// Lightbox-Zähler synchronisieren
$('.mc-vote-wrap[data-attach-id="' + aid + '"] .mc-vote-up-count').text(r.data.votes_up);
$('.mc-vote-wrap[data-attach-id="' + aid + '"] .mc-vote-down-count').text(r.data.votes_down);
}
$wrap.find('.mc-vote-btn').prop('disabled', false);
})
.fail(function() {
$wrap.find('.mc-vote-btn').prop('disabled', false);
});
});
}
// === LIGHTBOX & VIEW COUNT ===
$(document).on('click', '.mc-gallery-item', function (e) {
e.preventDefault();
@@ -566,31 +730,40 @@
}
const player = $item.data('player') || $item.find('.mc-watermark').text().trim();
const date = $item.data('date') || '';
const album = $item.data('album') || '';
const views = $item.data('views') || 0;
const date = $item.data('date') || '';
const album = $item.data('album') || '';
const $lightbox = $('<div class="mc-lightbox active"></div>');
const $content = $('<div class="mc-lightbox-content"></div>');
const $img = $('<img>').attr({
'src': href,
'alt': player
});
const $close = $('<button class="mc-lightbox-close" aria-label="Schließen">&times;</button>');
const $content = $('<div class="mc-lightbox-content"></div>');
const $img = $('<img>').attr({ src: href, alt: player });
const $close = $('<button class="mc-lightbox-close" aria-label="Schließen">&times;</button>');
// Info-Zeile
let infoHtml = '';
if (player) infoHtml += `👤 ${player}`;
if (date && player) infoHtml += ' • ';
if (date) infoHtml += `📅 ${date}`;
if (album) infoHtml += ` • 📁 ${album}`;
// Views auch in Lightbox zeigen
if (views) infoHtml += ' • 👁️ ' + views;
if (infoHtml) {
const $info = $('<div class="mc-lightbox-info"></div>').html(infoHtml);
$content.append($info);
}
// Vote-Buttons in Lightbox
if (attachId) {
const $voteWrap = $item.find('.mc-vote-wrap').clone();
if ($voteWrap.length) {
$voteWrap.addClass('mc-vote-wrap--lightbox');
$content.append($voteWrap);
// Cookie-Zustand wiederherstellen
const ck = 'mc_vote_' + attachId;
const cur = document.cookie.split(';').find(c => c.trim().startsWith(ck + '='));
const curV = cur ? cur.trim().split('=')[1] : null;
if (curV === 'up') $voteWrap.find('.mc-vote-up').addClass('mc-vote-btn--active');
if (curV === 'down') $voteWrap.find('.mc-vote-down').addClass('mc-vote-btn--active');
}
}
$content.append($close).append($img);
$lightbox.append($content).appendTo('body');

View File

@@ -1,8 +1,13 @@
<?php
if (!defined('ABSPATH')) exit;
class MC_Gallery_Core {
const OPTION_FORUM_LOGIN = 'mc_gallery_forum_login';
const OPTION_VOTING = 'mc_gallery_voting_enabled';
private static $forum_plugin_active = false;
const OPTION_THUMB_H = 'mc_gallery_thumb_h';
const OPTION_RESIZE_PCT = 'mc_gallery_resize_pct';
const OPTION_SHOW_DATE = 'mc_gallery_show_date';
@@ -10,6 +15,8 @@ class MC_Gallery_Core {
const SESSION_TTL = 3600; // 1 Stunde Session
public static function init() {
// Prüfen, ob das Forum-Plugin aktiv ist
self::$forum_plugin_active = class_exists('WBF_Auth');
add_action('init', [__CLASS__, 'register_post_types']);
add_action('rest_api_init', [__CLASS__, 'register_rest_routes']);
@@ -22,6 +29,9 @@ class MC_Gallery_Core {
add_action('wp_ajax_mc_gallery_create_album', [__CLASS__, 'handle_create_album']);
add_action('wp_ajax_nopriv_mc_gallery_create_album', [__CLASS__, 'handle_create_album']);
add_action('wp_ajax_mc_gallery_vote', [__CLASS__, 'handle_vote']);
add_action('wp_ajax_nopriv_mc_gallery_vote', [__CLASS__, 'handle_vote']);
add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_assets']);
// Meta Boxen & UI
@@ -202,7 +212,7 @@ class MC_Gallery_Core {
register_setting('mc_gallery_pro_group', self::OPTION_RESIZE_PCT, ['type' => 'integer', 'sanitize_callback' => 'absint', 'default' => 100]);
register_setting('mc_gallery_pro_group', self::OPTION_SHOW_DATE, [
'type' => 'boolean',
'sanitize_callback' => function($input) { return $input === '1'; },
'sanitize_callback' => function($input) { return $input == '1' || $input === true || $input === 'on'; },
'default' => true
]);
register_setting('mc_gallery_pro_group', self::OPTION_MAX_UPLOADS, [
@@ -210,13 +220,39 @@ class MC_Gallery_Core {
'sanitize_callback' => 'absint',
'default' => 5
]);
register_setting('mc_gallery_pro_group', self::OPTION_FORUM_LOGIN, [
'type' => 'boolean',
'sanitize_callback' => function($input) { return !empty($input) && $input !== '0'; },
'default' => false
]);
register_setting('mc_gallery_pro_group', self::OPTION_VOTING, [
'type' => 'boolean',
'sanitize_callback' => function($input) { return !empty($input) && $input !== '0'; },
'default' => true
]);
}
public static function settings_page() {
$thumb_h = get_option(self::OPTION_THUMB_H, 200);
$resize_pct = get_option(self::OPTION_RESIZE_PCT, 100);
$show_date = get_option(self::OPTION_SHOW_DATE, true);
$thumb_h = get_option(self::OPTION_THUMB_H, 200);
$resize_pct = get_option(self::OPTION_RESIZE_PCT, 100);
$show_date = get_option(self::OPTION_SHOW_DATE, true);
$max_uploads = get_option(self::OPTION_MAX_UPLOADS, 5);
$forum_login = get_option(self::OPTION_FORUM_LOGIN, false);
$forum_plugin_active = class_exists('WBF_Auth');
// Prüfen ob das Forum-Plugin zwar existiert (Datei vorhanden) aber nicht aktiv ist
$forum_plugin_installed = false;
if ( ! $forum_plugin_active ) {
$all_plugins = get_plugins();
foreach ( $all_plugins as $plugin_file => $plugin_data ) {
if ( stripos( $plugin_data['Name'], 'WP Business Forum' ) !== false
|| stripos( $plugin_data['TextDomain'], 'wp-business-forum' ) !== false
|| stripos( $plugin_file, 'wp-business-forum' ) !== false ) {
$forum_plugin_installed = true;
break;
}
}
}
?>
<div class="wrap">
<h1>MC Gallery PRO Settings</h1>
@@ -250,8 +286,84 @@ class MC_Gallery_Core {
<p class="description">How many images can a user upload at once? (Default: 5)</p>
</td>
</tr>
<tr>
<th scope="row">
Forum-Login für Upload-Verifizierung
<br><span style="font-weight:400;color:#646970;font-size:12px;">Benötigt: WP Business Forum</span>
</th>
<td>
<?php if ( $forum_plugin_active ) : ?>
<?php /* ── Plugin aktiv & bereit ── */ ?>
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px;">
<span style="display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;background:#edfaef;border:1px solid #c3e6cb;color:#1a7431;font-size:12px;font-weight:600;">
<span style="width:7px;height:7px;border-radius:50%;background:#22c55e;display:inline-block;"></span>
WP Business Forum aktiv
</span>
</div>
<label>
<input type="checkbox" name="<?php echo esc_attr(self::OPTION_FORUM_LOGIN); ?>" value="1" <?php checked($forum_login, '1'); ?> />
Forum-Login für Upload-Verifizierung erlauben
</label>
<p class="description" style="margin-top:6px;">
Wenn aktiviert, können sich Nutzer die ihren Minecraft-Account im Forum-Profil verknüpft haben, direkt ohne Ingame-Token hochladen.<br>
<strong>Der Shortcode:</strong> <code>[mc_gallery_combined]</code> <strong>ermöglicht eine kombinierte Verifizierung für Token und Forum.</strong>
</p>
<?php elseif ( $forum_plugin_installed ) : ?>
<?php /* ── Plugin installiert aber nicht aktiviert ── */ ?>
<div style="padding:12px 14px;background:#fff8e6;border:1px solid #f0c040;border-left:4px solid #f0c040;border-radius:0 6px 6px 0;margin-bottom:12px;">
<strong style="color:#7a5400;">⚠ WP Business Forum ist installiert, aber nicht aktiviert.</strong><br>
<span style="color:#7a5400;font-size:13px;">Aktiviere das Plugin unter <a href="<?php echo esc_url(admin_url('plugins.php')); ?>" style="color:#7a5400;text-decoration:underline;">Plugins → Installierte Plugins</a>, um diese Funktion zu nutzen.</span>
</div>
<label style="opacity:.5;pointer-events:none;">
<input type="checkbox" disabled />
Forum-Login für Upload-Verifizierung erlauben
</label>
<?php else : ?>
<?php /* ── Plugin nicht vorhanden ── */ ?>
<div style="padding:12px 14px;background:#fef2f2;border:1px solid #fca5a5;border-left:4px solid #ef4444;border-radius:0 6px 6px 0;margin-bottom:12px;">
<strong style="color:#991b1b;">✗ WP Business Forum ist nicht installiert.</strong><br>
<span style="color:#991b1b;font-size:13px;">
Dieses Feature benötigt das Plugin <em>WP Business Forum</em> von M_Viper.<br>
<a href="https://git.viper.ipv64.net/M_Viper/WP-Business-Forum/releases" target="_blank" rel="noopener" style="color:#b91c1c;font-weight:600;text-decoration:underline;">
↓ Jetzt herunterladen (Gitea)
</a>
&nbsp;·&nbsp;
<a href="<?php echo esc_url(admin_url('plugin-install.php')); ?>" style="color:#b91c1c;text-decoration:underline;">
Plugins → Neu hinzufügen
</a>
</span>
</div>
<label style="opacity:.5;pointer-events:none;">
<input type="checkbox" disabled />
Forum-Login für Upload-Verifizierung erlauben
</label>
<p class="description" style="margin-top:6px;color:#888;">
Nach der Installation und Aktivierung von WP Business Forum wird diese Option freigeschaltet.
</p>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row">Abstimmung (Daumen hoch/runter)</th>
<td>
<label>
<input type="checkbox" name="<?php echo esc_attr(self::OPTION_VOTING); ?>" value="1" <?php checked(get_option(self::OPTION_VOTING, true), true); ?> />
Abstimmung auf Bilder aktivieren
</label>
<p class="description" style="margin-top:6px;">
Besucher können Bilder mit 👍 oder 👎 bewerten. Jeder kann abstimmen (kein Login nötig).<br>
Shortcode für die Bestenliste: <code>[mc_gallery_vote]</code>
</p>
</td>
</tr>
</table>
<?php submit_button(); ?>
<?php submit_button('Änderungen speichern'); ?>
</form>
</div>
<?php
@@ -268,16 +380,43 @@ class MC_Gallery_Core {
}
public static function enqueue_assets() {
$js_version = time(); // Zeitstempel für Cache-Busting
wp_register_style('mc-gallery-pro-css', MCGALLERY_PRO_URL . 'assets/css/gallery-pro.css', [], MCGALLERY_PRO_VERSION);
wp_register_script('mc-gallery-pro-js', MCGALLERY_PRO_URL . 'assets/js/gallery-pro.js', ['jquery'], MCGALLERY_PRO_VERSION, true);
wp_register_script('mc-gallery-pro-js', MCGALLERY_PRO_URL . 'assets/js/gallery-pro.js?v=' . $js_version, ['jquery'], null, true);
wp_enqueue_style('mc-gallery-pro-css');
wp_enqueue_script('mc-gallery-pro-js');
// Forum-Verified-Status für JS ermitteln
$forum_verified_data = false;
if ( class_exists('WBF_Auth') && class_exists('MC_Gallery_Forum_Bridge') ) {
$forum_user = WBF_Auth::get_current_user();
if ( $forum_user ) {
$mc_user = MC_Gallery_Forum_Bridge::get_mc_username( $forum_user->id );
$verified = MC_Gallery_Forum_Bridge::is_verified( $forum_user->id );
if ( $mc_user && $verified ) {
$forum_verified_data = [
'mc_username' => $mc_user,
'server_id' => MC_Gallery_Forum_Bridge::get_mc_server( $forum_user->id ),
'display_name' => $forum_user->display_name,
];
}
}
}
wp_localize_script('mc-gallery-pro-js', 'mcGalleryPro', [
'restBase' => esc_url_raw(rest_url('mc-gallery/v1')),
'uploadUrl' => esc_url_raw(admin_url('admin-ajax.php')),
'nonce' => wp_create_nonce('mc_gallery_upload_action'),
'maxUploads' => intval(get_option(self::OPTION_MAX_UPLOADS, 5))
'restBase' => esc_url_raw(rest_url('mc-gallery/v1')),
'uploadUrl' => esc_url_raw(admin_url('admin-ajax.php')),
'nonce' => wp_create_nonce('mc_gallery_upload_action'),
'forumNonce' => wp_create_nonce('mc_gallery_forum_bridge'),
'maxUploads' => intval(get_option(self::OPTION_MAX_UPLOADS, 5)),
'forumVerified' => $forum_verified_data,
'votingEnabled' => get_option(self::OPTION_VOTING, true) ? true : false,
]);
// Forum-Bridge-JS nur laden, wenn Option aktiv und Forum-Plugin vorhanden
$forum_login = get_option(self::OPTION_FORUM_LOGIN, false);
if ($forum_login && class_exists('WBF_Auth')) {
wp_enqueue_script('mc-gallery-forum-bridge', MCGALLERY_PRO_URL . 'assets/js/forum-bridge.js?v=' . $js_version, ['jquery'], null, true);
}
}
public static function register_rest_routes() {
@@ -435,38 +574,60 @@ class MC_Gallery_Core {
}
public static function rest_get_albums($req) {
$params = $req->get_json_params();
$token = sanitize_text_field($params['token'] ?? '');
$username = sanitize_text_field($params['username'] ?? '');
$server_id = intval($params['server_id'] ?? 0);
if (!$token || !$username || !$server_id) {
return new WP_REST_Response(['success'=>false,'message'=>'Missing data'],400);
$params = $req->get_json_params();
$token = sanitize_text_field($params['token'] ?? '');
$username = sanitize_text_field($params['username'] ?? '');
$server_id = sanitize_text_field($params['server_id'] ?? '');
if (!$username) {
return new WP_REST_Response(['success'=>false,'message'=>'Missing username'],400);
}
$tokens = MC_Gallery_Helpers::get_tokens();
if (!isset($tokens[$token]) || !$tokens[$token]['claimed'] || $tokens[$token]['claimed_by'] !== $username) {
// ── Autorisierung: Token-Session ODER Forum-Login ─────────────────────
$authorized = false;
if ($token) {
$tokens = MC_Gallery_Helpers::get_tokens();
if (isset($tokens[$token]) && $tokens[$token]['claimed'] && $tokens[$token]['claimed_by'] === $username) {
if (!$server_id) $server_id = $tokens[$token]['server_id'] ?? '';
$authorized = true;
}
} elseif (class_exists('WBF_Auth') && class_exists('MC_Gallery_Forum_Bridge')) {
$forum_user = WBF_Auth::get_current_user();
if ($forum_user) {
$linked = MC_Gallery_Forum_Bridge::get_mc_username($forum_user->id);
if ($linked && MC_Gallery_Forum_Bridge::is_verified($forum_user->id)
&& strtolower($linked) === strtolower($username)) {
if (!$server_id) $server_id = MC_Gallery_Forum_Bridge::get_mc_server($forum_user->id);
$authorized = true;
}
}
}
if (!$authorized) {
return new WP_REST_Response(['success'=>false,'message'=>'Invalid session'],401);
}
$gallery = MC_Gallery_Helpers::find_or_create_gallery_post($username, $server_id);
$server_id_val = is_numeric($server_id) ? intval($server_id) : $server_id;
$gallery = MC_Gallery_Helpers::find_or_create_gallery_post($username, $server_id_val);
if (!$gallery) {
return MC_Gallery_Helpers::rest_response_success(['albums' => []]);
}
$albums = get_posts([
'post_type' => 'mc_album',
'post_type' => 'mc_album',
'posts_per_page' => -1,
'meta_key' => 'mc_gallery_id',
'meta_value' => $gallery->ID,
'orderby' => 'date',
'order' => 'DESC'
'meta_key' => 'mc_gallery_id',
'meta_value' => $gallery->ID,
'orderby' => 'date',
'order' => 'DESC'
]);
$out = [];
foreach ($albums as $album) {
$out[] = [
'id' => $album->ID,
'id' => $album->ID,
'title' => $album->post_title
];
}
@@ -495,30 +656,87 @@ class MC_Gallery_Core {
wp_send_json_success(['views' => $count]);
}
// NEU: Vote/Like Handler
public static function handle_vote() {
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mc_gallery_upload_action')) {
wp_send_json_error(['message' => 'Security check failed.']);
}
if (!isset($_POST['attach_id']) || !is_numeric($_POST['attach_id'])) {
wp_send_json_error(['message' => 'Invalid ID.']);
}
// Voting deaktiviert?
if (!get_option(self::OPTION_VOTING, true)) {
wp_send_json_error(['message' => 'Voting ist deaktiviert.']);
}
$attach_id = intval($_POST['attach_id']);
$vote_type = sanitize_text_field($_POST['vote_type'] ?? 'up'); // 'up' oder 'down'
$vote_action = sanitize_text_field($_POST['vote_action'] ?? 'add'); // 'add' oder 'remove'
$post = get_post($attach_id);
if (!$post || $post->post_type !== 'attachment' || strpos($post->post_mime_type, 'image/') !== 0) {
wp_send_json_error(['message' => 'Not a valid image.']);
}
$meta_key = ($vote_type === 'down') ? 'mc_votes_down' : 'mc_votes_up';
$count = (int) get_post_meta($attach_id, $meta_key, true);
$count = ($vote_action === 'remove') ? max(0, $count - 1) : $count + 1;
update_post_meta($attach_id, $meta_key, $count);
wp_send_json_success([
'votes_up' => (int) get_post_meta($attach_id, 'mc_votes_up', true),
'votes_down' => (int) get_post_meta($attach_id, 'mc_votes_down', true),
'vote_type' => $vote_type,
'vote_action'=> $vote_action,
]);
}
public static function handle_create_album() {
if (!isset($_POST['mc_upload_nonce']) || !wp_verify_nonce($_POST['mc_upload_nonce'], 'mc_gallery_upload_action')) {
wp_send_json_error(['message' => 'Security check failed.']);
}
$token = sanitize_text_field($_POST['mc_token'] ?? '');
$username = sanitize_text_field($_POST['mc_username'] ?? '');
$server_id = intval($_POST['mc_server_id'] ?? 0);
$album_name = sanitize_text_field($_POST['album_name'] ?? '');
if (!$token || !$username || !$server_id || !$album_name) {
$token = sanitize_text_field($_POST['mc_token'] ?? '');
$username = sanitize_text_field($_POST['mc_username'] ?? '');
$server_id = sanitize_text_field($_POST['mc_server_id'] ?? '');
$album_name = sanitize_text_field($_POST['album_name'] ?? '');
if (!$username || !$album_name) {
wp_send_json_error(['message' => 'Missing data']);
}
$tokens = MC_Gallery_Helpers::get_tokens();
if (!isset($tokens[$token]) || !$tokens[$token]['claimed'] || $tokens[$token]['claimed_by'] !== $username) {
wp_send_json_error(['message' => 'Invalid session']);
// ── Autorisierung: Token-Session ODER Forum-Login ─────────────────────
$authorized = false;
if ($token) {
$tokens = MC_Gallery_Helpers::get_tokens();
if (isset($tokens[$token]) && $tokens[$token]['claimed'] && $tokens[$token]['claimed_by'] === $username && $tokens[$token]['expires'] >= time()) {
if (!$server_id) $server_id = $tokens[$token]['server_id'] ?? '';
$authorized = true;
} else {
wp_send_json_error(['message' => 'Invalid or expired session']);
}
} elseif (class_exists('WBF_Auth') && class_exists('MC_Gallery_Forum_Bridge')) {
$forum_user = WBF_Auth::get_current_user();
if ($forum_user) {
$linked_mc = MC_Gallery_Forum_Bridge::get_mc_username($forum_user->id);
$verified = MC_Gallery_Forum_Bridge::is_verified($forum_user->id);
if ($linked_mc && $verified && strtolower($linked_mc) === strtolower($username)) {
if (!$server_id) $server_id = MC_Gallery_Forum_Bridge::get_mc_server($forum_user->id);
$authorized = true;
}
}
}
if ($tokens[$token]['expires'] < time()) {
wp_send_json_error(['message' => 'Session expired']);
if (!$authorized) {
wp_send_json_error(['message' => 'Nicht autorisiert.']);
}
$server_id_val = is_numeric($server_id) ? intval($server_id) : $server_id;
$gallery = MC_Gallery_Helpers::find_or_create_gallery_post($username, $server_id);
$gallery = MC_Gallery_Helpers::find_or_create_gallery_post($username, $server_id_val);
if (!$gallery) {
wp_send_json_error(['message' => 'Gallery not found']);
}
@@ -551,27 +769,60 @@ class MC_Gallery_Core {
wp_send_json_error(['message' => 'Security check failed.']);
}
$token = sanitize_text_field($_POST['mc_token'] ?? '');
$username = sanitize_text_field($_POST['mc_username'] ?? '');
$server_id = intval($_POST['mc_server_id'] ?? 0);
$album_id = intval($_POST['mc_album_id'] ?? 0);
if (!$token || !$username || !$server_id) {
wp_send_json_error(['message' => 'Token or data missing.']);
$token = sanitize_text_field($_POST['mc_token'] ?? '');
$username = sanitize_text_field($_POST['mc_username'] ?? '');
$server_id = sanitize_text_field($_POST['mc_server_id'] ?? '');
$album_id = intval($_POST['mc_album_id'] ?? 0);
if (!$username) {
wp_send_json_error(['message' => 'Benutzername fehlt.']);
}
$tokens = MC_Gallery_Helpers::get_tokens();
if (!isset($tokens[$token])) {
wp_send_json_error(['message' => 'Invalid token.']);
// ── Autorisierung: Token-Session ODER Forum-Login ─────────────────────
$authorized = false;
if ($token) {
// Standard-Weg: Ingame-Token verifizieren
$tokens = MC_Gallery_Helpers::get_tokens();
if (!isset($tokens[$token])) {
wp_send_json_error(['message' => 'Invalid token.']);
}
$t = $tokens[$token];
if ($t['expires'] < time()) {
wp_send_json_error(['message' => 'Session expired.']);
}
if (!$t['claimed'] || $t['claimed_by'] !== $username) {
wp_send_json_error(['message' => 'Verification failed. Please go back to step 2 and use /verify']);
}
// server_id aus Token übernehmen falls nicht per POST übergeben
if (!$server_id) {
$server_id = $t['server_id'] ?? '';
}
$authorized = true;
} elseif (class_exists('WBF_Auth') && class_exists('MC_Gallery_Forum_Bridge')) {
// Forum-Login-Weg: eingeloggten Forum-User prüfen
$forum_user = WBF_Auth::get_current_user();
if ($forum_user) {
$linked_mc = MC_Gallery_Forum_Bridge::get_mc_username($forum_user->id);
$verified = MC_Gallery_Forum_Bridge::is_verified($forum_user->id);
if ($linked_mc && $verified && strtolower($linked_mc) === strtolower($username)) {
// server_id aus Profil holen falls nicht übergeben
if (!$server_id) {
$server_id = MC_Gallery_Forum_Bridge::get_mc_server($forum_user->id);
}
$authorized = true;
}
}
}
$t = $tokens[$token];
if ($t['expires'] < time()) {
wp_send_json_error(['message' => 'Session expired.']);
if (!$authorized) {
wp_send_json_error(['message' => 'Nicht autorisiert. Bitte zuerst verifizieren.']);
}
if (!$t['claimed'] || $t['claimed_by'] !== $username) {
wp_send_json_error(['message' => 'Verification failed. Please go back to step 2 and use /verify']);
// server_id als Integer für wp_query (falls numeric), sonst als String belassen
$server_id_val = is_numeric($server_id) ? intval($server_id) : $server_id;
if (!$server_id_val) {
wp_send_json_error(['message' => 'Server-ID fehlt.']);
}
if (empty($_FILES['mc_images']) || !is_array($_FILES['mc_images']['name'])) {
@@ -596,7 +847,7 @@ class MC_Gallery_Core {
$errors = [];
try {
$gallery_post = MC_Gallery_Helpers::find_or_create_gallery_post($username, $server_id);
$gallery_post = MC_Gallery_Helpers::find_or_create_gallery_post($username, $server_id_val);
if (!$gallery_post) throw new Exception('Gallery not found.');
if (empty($_FILES['mc_images']) || !is_array($_FILES['mc_images']['name'])) {

View File

@@ -0,0 +1,691 @@
<?php
if ( ! defined( 'ABSPATH' ) ) exit;
/**
* MC_Gallery_Forum_Bridge
*
* Optionale Integration zwischen MC MultiServer Gallery PRO und WP Business Forum.
* Wird nur initialisiert wenn beide Plugins aktiv sind (class_exists-Check).
*
* Features:
* - Forum-User können ihren Minecraft-Account im Profil verknüpfen
* - Verknüpfte + verifizierte User können in der Galerie ohne Ingame-Token hochladen
* - Admin kann Verknüpfungen in der Forum-Benutzerverwaltung einsehen & bestätigen
* - Automatische Verifikation wenn ein gültiger Ingame-Token für diesen MC-Namen vorliegt
*
* Datenspeicherung: forum_user_meta (bereits vorhanden im Forum-Schema)
*/
class MC_Gallery_Forum_Bridge {
// Meta-Keys (gespeichert in forum_user_meta)
const META_MC_USER = 'mc_linked_username';
const META_MC_SERVER = 'mc_linked_server';
const META_VERIFIED = 'mc_link_verified';
const NONCE_ACTION = 'mc_gallery_forum_bridge';
// ── Bootstrap ─────────────────────────────────────────────────────────────
public static function init() {
// Nur laden wenn beide Plugins aktiv sind
if ( ! class_exists( 'WBF_Auth' ) || ! class_exists( 'MC_Gallery_Helpers' ) ) {
return;
}
// AJAX-Handler
add_action( 'wp_ajax_mc_forum_link_account', [ __CLASS__, 'ajax_link_account' ] );
add_action( 'wp_ajax_mc_forum_unlink_account', [ __CLASS__, 'ajax_unlink_account' ] );
add_action( 'wp_ajax_mc_forum_verify_upload', [ __CLASS__, 'ajax_verify_upload' ] );
add_action( 'wp_ajax_nopriv_mc_forum_verify_upload', [ __CLASS__, 'ajax_verify_upload' ] );
add_action( 'wp_ajax_mc_forum_admin_approve', [ __CLASS__, 'ajax_admin_approve' ] );
// Hooks in die Gallery-Ausgabe (sofern vorhanden)
add_filter( 'mc_gallery_verify_ui_extra', [ __CLASS__, 'render_verify_panel' ], 10, 2 );
add_filter( 'mc_gallery_can_upload', [ __CLASS__, 'filter_upload_permission' ], 10, 3 );
// Hooks ins Forum
add_filter( 'wbf_profile_tabs', [ __CLASS__, 'add_profile_tab' ], 10, 2 );
add_filter( 'wbf_profile_tab_content', [ __CLASS__, 'render_profile_tab' ], 10, 3 );
add_action( 'wbf_admin_user_row_extra',[ __CLASS__, 'render_admin_user_col' ] );
// CSS laden
add_action( 'wp_enqueue_scripts', [ __CLASS__, 'enqueue_assets' ] );
add_action( 'admin_enqueue_scripts', [ __CLASS__, 'enqueue_assets' ] );
}
public static function enqueue_assets() {
wp_enqueue_style(
'mc-gallery-forum-bridge',
MCGALLERY_PRO_URL . 'assets/css/mc-gallery-forum-bridge.css',
[],
MCGALLERY_PRO_VERSION
);
}
// ── Forum-Profil: Minecraft-Tab ───────────────────────────────────────────
/**
* Neuen Tab "Minecraft" zum Forum-Profil hinzufügen.
* Erwartet dass WBF einen `wbf_profile_tabs`-Filter anbietet.
* Falls nicht: Tab wird ignoriert, kein Fehler.
*/
public static function add_profile_tab( $tabs, $profile_user ) {
$tabs['minecraft'] = [
'label' => '<i class="fas fa-cubes"></i> Minecraft',
'order' => 50,
];
return $tabs;
}
public static function render_profile_tab( $content, $tab, $profile_user ) {
if ( $tab !== 'minecraft' ) return $content;
$current = WBF_Auth::get_current_user();
$is_own = $current && (int) $current->id === (int) $profile_user->id;
$is_mod = $current && WBF_Roles::can( $current, 'manage_users' );
$mc_user = self::get_mc_username( $profile_user->id );
$verified = self::is_verified( $profile_user->id );
$servers = self::get_server_list();
ob_start();
?>
<div class="mc-bridge-profile-tab">
<?php if ( $mc_user ) : ?>
<div class="mc-bridge-linked-card <?php echo $verified ? 'is-verified' : 'is-pending'; ?>">
<img class="mc-bridge-head"
src="https://mc-heads.net/avatar/<?php echo esc_attr( $mc_user ); ?>/48"
alt="" loading="lazy">
<div class="mc-bridge-linked-info">
<span class="mc-bridge-mc-name"><?php echo esc_html( $mc_user ); ?></span>
<?php if ( $verified ) : ?>
<span class="mc-bridge-badge mc-bridge-badge--ok">
<i class="fas fa-check-circle"></i> Verifiziert
</span>
<?php else : ?>
<span class="mc-bridge-badge mc-bridge-badge--wait">
<i class="fas fa-clock"></i> Ausstehend
</span>
<?php endif; ?>
<?php
$sv = self::get_mc_server( $profile_user->id );
if ( $sv ) echo '<span class="mc-bridge-server-hint"><i class="fas fa-server"></i> ' . esc_html( $sv ) . '</span>';
?>
</div>
<?php if ( $is_own || $is_mod ) : ?>
<div class="mc-bridge-actions">
<?php if ( ! $verified && $is_mod ) : ?>
<button type="button" class="mc-bridge-btn mc-bridge-btn--approve"
data-uid="<?php echo (int) $profile_user->id; ?>"
data-nonce="<?php echo wp_create_nonce( self::NONCE_ACTION ); ?>">
<i class="fas fa-check"></i> Bestätigen
</button>
<?php endif; ?>
<?php if ( $is_own ) : ?>
<button type="button" class="mc-bridge-btn mc-bridge-btn--unlink"
data-nonce="<?php echo wp_create_nonce( self::NONCE_ACTION ); ?>">
<i class="fas fa-unlink"></i> Trennen
</button>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php if ( $is_own && ! $verified ) : ?>
<div class="mc-bridge-pending-hint">
<i class="fas fa-info-circle"></i>
Noch nicht verifiziert. Du kannst entweder auf Admin-Bestätigung warten
oder deinen Ingame-Token normal in der Galerie eingeben — dann wird
die Verknüpfung automatisch bestätigt.
</div>
<?php endif; ?>
<?php elseif ( $is_own ) : ?>
<p class="mc-bridge-intro">
Verknüpfe deinen Minecraft-Account mit deinem Forum-Profil.
Danach kannst du Bilder in der Galerie ohne Ingame-Token hochladen.
</p>
<div class="mc-bridge-link-form">
<label>
<span>Minecraft-Benutzername</span>
<input type="text" id="mc-bridge-username"
placeholder="z. B. MvViper123" maxlength="32" autocomplete="off">
</label>
<?php if ( count( $servers ) > 1 ) : ?>
<label>
<span>Server</span>
<select id="mc-bridge-server">
<?php foreach ( $servers as $sid => $sname ) : ?>
<option value="<?php echo esc_attr( $sid ); ?>">
<?php echo esc_html( $sname ); ?>
</option>
<?php endforeach; ?>
</select>
</label>
<?php else :
$first_key = array_key_first( $servers );
echo '<input type="hidden" id="mc-bridge-server" value="' . esc_attr( $first_key ) . '">';
endif; ?>
<button type="button" class="mc-bridge-btn mc-bridge-btn--link"
data-nonce="<?php echo wp_create_nonce( self::NONCE_ACTION ); ?>">
<i class="fas fa-link"></i> Verknüpfen
</button>
</div>
<?php else : ?>
<p class="mc-bridge-intro">Kein Minecraft-Account verknüpft.</p>
<?php endif; ?>
</div>
<script>
(function($){
// Account verknüpfen
$('.mc-bridge-btn--link').on('click', function(){
var mc = $('#mc-bridge-username').val().trim();
var sv = $('#mc-bridge-server').val() || '';
if ( ! mc ) { alert('Bitte einen Minecraft-Namen eingeben.'); return; }
var $btn = $(this).prop('disabled', true).text('Bitte warten…');
$.post(<?php echo json_encode( admin_url('admin-ajax.php') ); ?>, {
action: 'mc_forum_link_account',
mc_username: mc,
server_id: sv,
nonce: $btn.data('nonce')
})
.done(function(r){
if ( r.success ) location.reload();
else { alert(r.data || 'Fehler beim Verknüpfen.'); $btn.prop('disabled', false).html('<i class="fas fa-link"></i> Verknüpfen'); }
})
.fail(function(){ alert('Netzwerkfehler.'); $btn.prop('disabled', false).html('<i class="fas fa-link"></i> Verknüpfen'); });
});
// Trennen
$('.mc-bridge-btn--unlink').on('click', function(){
if ( ! confirm('Minecraft-Konto wirklich trennen?') ) return;
$.post(<?php echo json_encode( admin_url('admin-ajax.php') ); ?>, {
action: 'mc_forum_unlink_account',
nonce: $(this).data('nonce')
})
.done(function(r){
if ( r.success ) location.reload();
else alert(r.data || 'Fehler.');
});
});
// Admin: Bestätigen
$('.mc-bridge-btn--approve').on('click', function(){
var uid = $(this).data('uid');
var nonce = $(this).data('nonce');
var $btn = $(this).prop('disabled', true);
$.post(<?php echo json_encode( admin_url('admin-ajax.php') ); ?>, {
action: 'mc_forum_admin_approve',
user_id: uid,
nonce: nonce
})
.done(function(r){
if ( r.success ) location.reload();
else { alert(r.data || 'Fehler.'); $btn.prop('disabled', false); }
});
});
})(jQuery);
</script>
<?php
return ob_get_clean();
}
// ── AJAX-Handler ──────────────────────────────────────────────────────────
/** Account verknüpfen (eigenes Profil) */
public static function ajax_link_account() {
check_ajax_referer( self::NONCE_ACTION, 'nonce' );
$user = WBF_Auth::get_current_user();
if ( ! $user ) wp_send_json_error( 'Nicht eingeloggt.' );
$mc_user = sanitize_text_field( $_POST['mc_username'] ?? '' );
$server = sanitize_text_field( $_POST['server_id'] ?? '' );
// Validierung
if ( strlen( $mc_user ) < 2 || strlen( $mc_user ) > 32 ) {
wp_send_json_error( 'Ungültiger Minecraft-Name (232 Zeichen).' );
}
if ( ! preg_match( '/^[a-zA-Z0-9_]+$/', $mc_user ) ) {
wp_send_json_error( 'Nur Buchstaben, Zahlen und _ erlaubt.' );
}
// Doppelte Verknüpfung verhindern
if ( self::mc_user_taken( $mc_user, $user->id ) ) {
wp_send_json_error( 'Dieser Minecraft-Name ist bereits mit einem anderen Forum-Konto verknüpft.' );
}
self::set_mc_username( $user->id, $mc_user );
self::set_mc_server( $user->id, $server );
// Verifizierung: automatisch wenn gültiger Ingame-Token vorliegt
$auto_verified = self::check_active_token_for( $mc_user );
self::set_verified( $user->id, $auto_verified );
wp_send_json_success( [
'mc_username' => $mc_user,
'verified' => $auto_verified,
] );
}
/** Account-Verknüpfung trennen */
public static function ajax_unlink_account() {
check_ajax_referer( self::NONCE_ACTION, 'nonce' );
$user = WBF_Auth::get_current_user();
if ( ! $user ) wp_send_json_error( 'Nicht eingeloggt.' );
self::delete_mc_link( $user->id );
wp_send_json_success();
}
/** Admin bestätigt eine ausstehende Verknüpfung */
public static function ajax_admin_approve() {
check_ajax_referer( self::NONCE_ACTION, 'nonce' );
$current = WBF_Auth::get_current_user();
if ( ! $current || ! WBF_Roles::can( $current, 'manage_users' ) ) {
wp_send_json_error( 'Keine Berechtigung.' );
}
$target_id = (int) ( $_POST['user_id'] ?? 0 );
if ( ! $target_id ) wp_send_json_error( 'Ungültige Benutzer-ID.' );
self::set_verified( $target_id, true );
wp_send_json_success();
}
/**
* Galerie-Verify via Forum-Login.
* Wird vom Frontend aufgerufen wenn der User auf "Mit Forum-Login verifizieren" klickt.
* Gibt bei Erfolg MC-Username + Server zurück, die das Gallery-JS dann als würden
* sie aus einem Token-Verify stammen weiterverarbeiten kann.
*/
public static function ajax_verify_upload() {
check_ajax_referer( self::NONCE_ACTION, 'nonce' );
$forum_user = WBF_Auth::get_current_user();
if ( ! $forum_user ) {
wp_send_json_error( [
'code' => 'not_logged_in',
'message' => 'Du musst im Forum eingeloggt sein.',
] );
}
$mc_user = self::get_mc_username( $forum_user->id );
$verified = self::is_verified( $forum_user->id );
if ( ! $mc_user ) {
wp_send_json_error( [
'code' => 'no_mc_link',
'message' => 'Kein Minecraft-Konto verknüpft. Bitte zuerst im Profil verknüpfen.',
] );
}
// Noch nicht verifiziert → Ingame-Token als letzten Ausweg prüfen
if ( ! $verified ) {
if ( self::check_active_token_for( $mc_user ) ) {
self::set_verified( $forum_user->id, true );
$verified = true;
}
}
if ( ! $verified ) {
wp_send_json_error( [
'code' => 'not_verified',
'message' => 'Dein Account ist noch nicht verifiziert. Bitte einen Admin um Bestätigung oder nutze einmalig das Ingame-Token.',
] );
}
$server_id = self::get_mc_server( $forum_user->id )
?: sanitize_text_field( $_POST['server_id'] ?? '' );
wp_send_json_success( [
'mc_username' => $mc_user,
'server_id' => $server_id,
'display_name' => $forum_user->display_name,
'verify_type' => 'forum_login',
] );
}
// ── Filter: Gallery-Upload-Berechtigung ───────────────────────────────────
/**
* Gibt true zurück wenn ein eingeloggter Forum-User einen verifizierten
* Link zu $mc_username hat — ersetzt dann die normale Token-Prüfung.
* Wird nur aufgerufen wenn die Gallery `apply_filters('mc_gallery_can_upload', …)` nutzt.
*/
public static function filter_upload_permission( $allowed, $mc_username, $server_id ) {
if ( $allowed ) return true; // schon erlaubt, nicht überschreiben
$forum_user = WBF_Auth::get_current_user();
if ( ! $forum_user ) return false;
$linked = self::get_mc_username( $forum_user->id );
if ( ! $linked ) return false;
if ( strtolower( $linked ) !== strtolower( $mc_username ) ) return false;
return self::is_verified( $forum_user->id );
}
// ── Filter: Zusätzliche Verify-UI in der Gallery ──────────────────────────
/**
* Hängt unter das normale Verify-Formular einen "Mit Forum-Login"-Bereich.
* Funktioniert wenn die Gallery `apply_filters('mc_gallery_verify_ui_extra', '', $server_id)` aufruft.
*
* Falls der Hook nicht existiert: eigenen Shortcode [mc_gallery_forum_login] nutzen.
*/
public static function render_verify_panel( $html, $server_id ) {
return $html . self::build_verify_panel( $server_id );
}
/**
* Shortcode [mc_gallery_forum_login server_id="survival"]
* Kann manuell auf der Upload-Seite eingebettet werden falls der Hook-Ansatz
* nicht greift.
*/
public static function shortcode_verify_panel( $atts ) {
$atts = shortcode_atts( [ 'server_id' => '' ], $atts );
return self::build_verify_panel( sanitize_text_field( $atts['server_id'] ) );
}
/** Eigentliches HTML für das Verify-Panel */
public static function build_verify_panel( $server_id ) {
$forum_user = WBF_Auth::get_current_user();
$nonce = wp_create_nonce( self::NONCE_ACTION );
$ajax_url = admin_url( 'admin-ajax.php' );
ob_start();
?>
<div class="mc-bridge-verify-wrap" data-server="<?php echo esc_attr( $server_id ); ?>">
<div class="mc-bridge-divider"><span>oder</span></div>
<?php if ( ! $forum_user ) :
$forum_url = function_exists('wbf_get_forum_url') ? wbf_get_forum_url() : '#';
$register_url = function_exists('wbf_get_forum_url') ? wbf_get_forum_url() . '?register=1' : '#';
?>
<div class="mc-bridge-card">
<div class="mc-bridge-card-inner">
<div class="mc-bridge-card-icon">
<i class="fas fa-comments"></i>
</div>
<div class="mc-bridge-card-text">
<div class="mc-bridge-card-label">Forum-Login</div>
<div class="mc-bridge-card-desc">
Direkt hochladen ohne Ingame-Token wenn dein <strong>Minecraft-Account</strong> mit dem Forum verknüpft ist.
</div>
</div>
<div class="mc-bridge-card-actions">
<a href="<?php echo esc_url( $forum_url ); ?>" class="mc-bridge-action-btn">
<i class="fas fa-sign-in-alt"></i> Einloggen
</a>
<a href="<?php echo esc_url( $register_url ); ?>" class="mc-bridge-action-btn-ghost">
<i class="fas fa-external-link-alt"></i> Konto erstellen
</a>
</div>
</div>
</div>
<?php else :
$mc_user = self::get_mc_username( $forum_user->id );
$verified = self::is_verified( $forum_user->id );
$forum_url = function_exists('wbf_get_forum_url')
? wbf_get_forum_url() . '?forum_profile=' . (int) $forum_user->id . '#tab-minecraft'
: '#';
?>
<?php if ( $mc_user && $verified ) : ?>
<div class="mc-bridge-success">
<i class="fas fa-check-circle"></i>
Verifiziert als <strong><?php echo esc_html( $mc_user ); ?></strong>
</div>
<?php elseif ( $mc_user ) : ?>
<div class="mc-bridge-card mc-bridge-card--warn">
<div class="mc-bridge-card-inner">
<img class="mc-bridge-head-sm"
src="https://mc-heads.net/avatar/<?php echo esc_attr( $mc_user ); ?>/44"
alt="" loading="lazy">
<div class="mc-bridge-card-text">
<div class="mc-bridge-card-label">Verknüpft · ausstehend</div>
<div class="mc-bridge-card-desc">
<strong><?php echo esc_html( $mc_user ); ?></strong>
· Noch nicht bestätigt
</div>
</div>
<button type="button"
class="mc-bridge-verify-trigger"
data-ajax="<?php echo esc_attr( $ajax_url ); ?>"
data-nonce="<?php echo esc_attr( $nonce ); ?>"
data-server="<?php echo esc_attr( $server_id ); ?>">
<i class="fas fa-sign-in-alt"></i> Mit Forum verifizieren
</button>
</div>
</div>
<?php else : ?>
<div class="mc-bridge-card">
<div class="mc-bridge-card-inner">
<div class="mc-bridge-card-icon">
<i class="fas fa-user-circle"></i>
</div>
<div class="mc-bridge-card-text">
<div class="mc-bridge-card-label">Eingeloggt als <?php echo esc_html( $forum_user->display_name ); ?></div>
<div class="mc-bridge-card-desc">
Noch kein Minecraft-Konto verknüpft.
</div>
</div>
<div class="mc-bridge-card-actions">
<a href="<?php echo esc_url( $forum_url ); ?>" class="mc-bridge-action-btn">
<i class="fas fa-link"></i> Konto verknüpfen
</a>
</div>
</div>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
<script>
(function($){
$(document).on('click', '.mc-bridge-verify-trigger', function(){
console.log('[MC-Gallery] Forum-Login Button geklickt!');
if (typeof $ === 'undefined') { alert('jQuery nicht geladen!'); return; }
var $btn = $(this).prop('disabled', true);
var ajax = $btn.data('ajax');
var nonce = $btn.data('nonce');
var server = $btn.data('server') || $btn.closest('[data-server]').data('server') || '';
$btn.html('<i class="fas fa-spinner fa-spin"></i> Prüfe…');
$.post(ajax, {
action: 'mc_forum_verify_upload',
nonce: nonce,
server_id: server
})
.done(function(r){
if ( r.success ) {
// Galerie-JS über erfolgreiche Verify informieren
// Standard-Event — Gallery-JS kann darauf lauschen
$(document).trigger('mc_gallery_forum_verified', [ r.data ]);
// Falls Gallery eigenes globales Callback hat
if ( typeof window.mcGalleryOnVerified === 'function' ) {
window.mcGalleryOnVerified( r.data );
}
// Statusanzeige aktualisieren
$btn.closest('.mc-bridge-verify-wrap')
.html('<div class="mc-bridge-success">'
+ '<i class="fas fa-check-circle"></i> Verifiziert als <strong>'
+ $('<span>').text(r.data.mc_username).html()
+ '</strong></div>');
} else {
var msg = (r.data && r.data.message) ? r.data.message : 'Fehler bei der Verifikation.';
$btn.closest('.mc-bridge-verify-wrap').find('.mc-bridge-error').remove();
$btn.closest('.mc-bridge-forum-linked, .mc-bridge-not-linked')
.after('<p class="mc-bridge-error"><i class="fas fa-times-circle"></i> ' + msg + '</p>');
$btn.prop('disabled', false).html('<i class="fas fa-sign-in-alt"></i> Mit Forum-Login verifizieren');
}
})
.fail(function(){
$btn.prop('disabled', false).html('<i class="fas fa-sign-in-alt"></i> Mit Forum-Login verifizieren');
alert('Netzwerkfehler. Bitte erneut versuchen.');
});
});
})(jQuery);
</script>
<?php
return ob_get_clean();
}
// ── Admin: Spalte in Forum-Benutzertabelle ────────────────────────────────
/**
* Zeigt verknüpfte MC-Accounts in der Forum-Admin-Usertabelle.
* Voraussetzung: Forum rendert `do_action('wbf_admin_user_row_extra', $user)`.
*/
public static function render_admin_user_col( $user ) {
$mc_user = self::get_mc_username( $user->id );
if ( ! $mc_user ) return;
$verified = self::is_verified( $user->id );
$nonce = wp_create_nonce( self::NONCE_ACTION );
?>
<span class="mc-bridge-admin-mc-badge"
title="Verknüpfter Minecraft-Account<?php echo $verified ? ' (verifiziert)' : ' (ausstehend)'; ?>">
<img src="https://mc-heads.net/avatar/<?php echo esc_attr( $mc_user ); ?>/20" alt="">
<?php echo esc_html( $mc_user ); ?>
<?php if ( $verified ) : ?>
<i class="fas fa-check" style="color:#22c55e"></i>
<?php else : ?>
<a href="#"
class="mc-bridge-btn--approve"
style="color:#f59e0b;margin-left:4px"
data-uid="<?php echo (int) $user->id; ?>"
data-nonce="<?php echo esc_attr( $nonce ); ?>"
title="Verknüpfung bestätigen">
<i class="fas fa-check-circle"></i>
</a>
<script>
(function($){
$(document).on('click', '.mc-bridge-btn--approve[data-uid]', function(e){
e.preventDefault();
var $a = $(this);
var uid = $a.data('uid');
var nonce = $a.data('nonce');
$.post(<?php echo json_encode( admin_url('admin-ajax.php') ); ?>, {
action: 'mc_forum_admin_approve',
user_id: uid,
nonce: nonce
}).done(function(r){
if ( r.success ) location.reload();
else alert(r.data || 'Fehler.');
});
});
})(jQuery);
</script>
<?php endif; ?>
</span>
<?php
}
// ── Hilfsmethoden: forum_user_meta Zugriff ────────────────────────────────
private static function get_meta( $user_id, $key ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare(
"SELECT meta_value FROM {$wpdb->prefix}forum_user_meta
WHERE user_id = %d AND meta_key = %s
LIMIT 1",
(int) $user_id, $key
) );
}
private static function set_meta( $user_id, $key, $value ) {
global $wpdb;
$table = $wpdb->prefix . 'forum_user_meta';
$exists = $wpdb->get_var( $wpdb->prepare(
"SELECT id FROM {$table} WHERE user_id = %d AND meta_key = %s",
(int) $user_id, $key
) );
if ( $exists ) {
$wpdb->update( $table,
[ 'meta_value' => $value ],
[ 'user_id' => (int) $user_id, 'meta_key' => $key ]
);
} else {
$wpdb->insert( $table, [
'user_id' => (int) $user_id,
'meta_key' => $key,
'meta_value' => $value,
] );
}
}
private static function delete_meta( $user_id, $key ) {
global $wpdb;
$wpdb->delete( $wpdb->prefix . 'forum_user_meta',
[ 'user_id' => (int) $user_id, 'meta_key' => $key ]
);
}
// Öffentliche Getter/Setter
public static function get_mc_username( $user_id ) { return self::get_meta( $user_id, self::META_MC_USER ); }
public static function get_mc_server ( $user_id ) { return self::get_meta( $user_id, self::META_MC_SERVER ); }
public static function is_verified ( $user_id ) { return '1' === self::get_meta( $user_id, self::META_VERIFIED ); }
public static function set_mc_username( $user_id, $val ) { self::set_meta( $user_id, self::META_MC_USER, $val ); }
public static function set_mc_server ( $user_id, $val ) { self::set_meta( $user_id, self::META_MC_SERVER, $val ); }
public static function set_verified ( $user_id, $bool ) { self::set_meta( $user_id, self::META_VERIFIED, $bool ? '1' : '0' ); }
public static function delete_mc_link( $user_id ) {
self::delete_meta( $user_id, self::META_MC_USER );
self::delete_meta( $user_id, self::META_MC_SERVER );
self::delete_meta( $user_id, self::META_VERIFIED );
}
// Prüft ob ein MC-Name schon einem anderen Forum-User gehört
private static function mc_user_taken( $mc_user, $exclude_user_id ) {
global $wpdb;
return (bool) $wpdb->get_var( $wpdb->prepare(
"SELECT m.id FROM {$wpdb->prefix}forum_user_meta m
WHERE m.meta_key = %s
AND m.meta_value = %s
AND m.user_id != %d
LIMIT 1",
self::META_MC_USER, $mc_user, (int) $exclude_user_id
) );
}
// Gibt true zurück wenn gerade ein gültiger Ingame-Token für diesen MC-Namen existiert
private static function check_active_token_for( $mc_username ) {
if ( ! class_exists( 'MC_Gallery_Helpers' ) ) return false;
$tokens = MC_Gallery_Helpers::get_tokens();
$now = time();
foreach ( $tokens as $token => $data ) {
if ( ! is_array( $data ) ) continue;
if ( ( $data['expires'] ?? 0 ) < $now ) continue;
if ( strtolower( $data['player'] ?? '' ) === strtolower( $mc_username ) ) {
return true;
}
}
return false;
}
// Server-Liste aus Gallery-Config laden
private static function get_server_list() {
$servers = get_option( 'mc_gallery_servers', [] );
if ( ! empty( $servers ) && is_array( $servers ) ) return $servers;
return [ 'default' => 'Standard-Server' ];
}
}

View File

@@ -3,12 +3,16 @@ if (!defined('ABSPATH')) exit;
class MC_Gallery_Shortcodes {
private static $modal_rendered = false;
public static function init() {
add_shortcode('mc_gallery_overview', [__CLASS__, 'shortcode_overview']);
add_shortcode('mc_gallery_player', [__CLASS__, 'shortcode_player']);
add_shortcode('mc_gallery_upload', [__CLASS__, 'shortcode_upload']);
add_shortcode('mc_gallery_combined', [__CLASS__, 'shortcode_combined']);
add_shortcode('mc_gallery_all_albums', [__CLASS__, 'shortcode_all_albums_overview']);
add_shortcode('mc_gallery_vote', [__CLASS__, 'shortcode_vote_board']);
add_shortcode('mc_gallery', [__CLASS__, 'shortcode_overview']);
}
@@ -110,13 +114,15 @@ class MC_Gallery_Shortcodes {
$albums = [];
if (!empty($player_gallery_ids)) {
$albums = get_posts([
'post_type' => 'mc_album',
'post_type' => 'mc_album',
'posts_per_page' => -1,
'meta_key' => 'mc_gallery_id',
'value' => $player_gallery_ids,
'compare' => 'IN',
'orderby' => 'date',
'order' => 'DESC'
'meta_query' => [[
'key' => 'mc_gallery_id',
'value' => $player_gallery_ids,
'compare' => 'IN',
]],
'orderby' => 'date',
'order' => 'DESC'
]);
}
@@ -128,12 +134,13 @@ class MC_Gallery_Shortcodes {
foreach ($albums as $album) {
$album_images = get_posts([
'post_type' => 'attachment',
'post_type' => 'attachment',
'post_status' => 'inherit',
'posts_per_page' => 1,
'post_mime_type' => 'image',
'post_parent' => get_post_meta($album->ID, 'mc_gallery_id', true),
'orderby' => 'date',
'order' => 'DESC'
'post_parent' => get_post_meta($album->ID, 'mc_gallery_id', true),
'orderby' => 'date',
'order' => 'DESC'
]);
$cover_url = '';
@@ -226,6 +233,7 @@ class MC_Gallery_Shortcodes {
$out .= '<div class="mc-date-badge">'.$upload_date.'</div>';
}
$out .= '<img src="'.esc_url($thumb).'" alt="'.esc_attr($q_player).'" loading="lazy" style="aspect-ratio: '.$aspect_ratio.';">';
$out .= self::render_like_btn($img->ID);
$out .= '<div class="mc-gallery-overlay">';
$out .= '<div class="mc-gallery-info">';
$out .= '<div class="mc-watermark">'.esc_html($q_player).'</div>';
@@ -382,6 +390,7 @@ class MC_Gallery_Shortcodes {
$out .= '<div class="mc-date-badge">'.$upload_date.'</div>';
}
$out .= '<img src="'.esc_url($thumb).'" alt="'.esc_attr($player_name).'" loading="lazy" style="aspect-ratio: '.$aspect_ratio.';">';
$out .= self::render_like_btn($img->ID);
$out .= '<div class="mc-gallery-overlay">';
$out .= '<div class="mc-gallery-info">';
$out .= '<div class="mc-watermark">'.$player_name.'</div>';
@@ -539,6 +548,7 @@ class MC_Gallery_Shortcodes {
$out .= '<div class="mc-date-badge">'.$upload_date.'</div>';
}
$out .= '<img src="'.esc_url($thumb).'" alt="'.esc_attr($player).'" loading="lazy" style="aspect-ratio: '.$aspect_ratio.';">';
$out .= self::render_like_btn($img->ID);
$out .= '<div class="mc-gallery-overlay">';
$out .= '<div class="mc-gallery-info">';
$out .= '<div class="mc-watermark">'.esc_html($player).'</div>';
@@ -560,6 +570,12 @@ class MC_Gallery_Shortcodes {
public static function shortcode_all_albums_overview($atts) {
$show_date = get_option(MC_Gallery_Core::OPTION_SHOW_DATE, true);
$current_url = remove_query_arg(['player', 'server', 'album']);
// Album-Ansicht: wenn ?album=ID in der URL steht, direkt anzeigen
$q_album = isset($_GET['album']) ? intval($_GET['album']) : 0;
if ($q_album) {
return self::shortcode_album_view(['album_id' => $q_album]);
}
$albums = get_posts([
'post_type' => 'mc_album',
@@ -586,13 +602,14 @@ class MC_Gallery_Shortcodes {
$album_count = get_post_meta($album->ID, 'mc_image_count', true) ?: 0;
$cover_images = get_posts([
'post_type' => 'attachment',
'post_type' => 'attachment',
'post_status' => 'inherit',
'posts_per_page' => 1,
'post_mime_type' => 'image',
'meta_key' => 'mc_album_id',
'meta_value' => $album->ID,
'orderby' => 'date',
'order' => 'DESC'
'meta_key' => 'mc_album_id',
'meta_value' => $album->ID,
'orderby' => 'date',
'order' => 'DESC'
]);
$cover_url = '';
@@ -645,13 +662,14 @@ class MC_Gallery_Shortcodes {
}
$images = get_posts([
'post_type' => 'attachment',
'post_type' => 'attachment',
'post_status' => 'inherit',
'posts_per_page' => -1,
'post_mime_type' => 'image',
'meta_key' => 'mc_album_id',
'meta_value' => $album_id,
'orderby' => 'date',
'order' => 'DESC'
'meta_key' => 'mc_album_id',
'meta_value' => $album_id,
'orderby' => 'date',
'order' => 'DESC'
]);
$gallery_id = get_post_meta($album_id, 'mc_gallery_id', true);
@@ -700,6 +718,7 @@ class MC_Gallery_Shortcodes {
$out .= '<div class="mc-date-badge">'.$upload_date.'</div>';
}
$out .= '<img src="'.esc_url($thumb).'" alt="" loading="lazy" style="aspect-ratio: '.$aspect_ratio.';">';
$out .= self::render_like_btn($img->ID);
$out .= '</div>';
$out .= '</a>';
}
@@ -716,9 +735,12 @@ class MC_Gallery_Shortcodes {
public static function shortcode_upload($atts) {
$atts = shortcode_atts(['show' => '0'], $atts);
$show_default = ($atts['show'] === '1');
// Option als String vergleichen, damit auch '1' als String erkannt wird
$forum_login_enabled = get_option(MC_Gallery_Core::OPTION_FORUM_LOGIN, false) == '1';
$forum_plugin = class_exists('WBF_Auth');
ob_start();
?>
<!-- DEBUG: Forum-Login Option: <?php echo $forum_login_enabled ? 'aktiv' : 'inaktiv'; ?>, Forum-Plugin: <?php echo $forum_plugin ? 'geladen' : 'NICHT geladen'; ?> -->
<div class="mc-upload-wrapper">
<button id="mc-open-upload-modal" class="mc-btn mc-btn-primary">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -728,6 +750,7 @@ class MC_Gallery_Shortcodes {
</button>
</div>
<?php if (!self::$modal_rendered): self::$modal_rendered = true; ?>
<div id="mc-upload-modal" class="mc-modal" aria-hidden="true" role="dialog">
<div class="mc-modal-backdrop"></div>
<div class="mc-modal-content">
@@ -757,6 +780,18 @@ class MC_Gallery_Shortcodes {
</svg>
Session starten
</button>
<?php if ($forum_login_enabled && $forum_plugin): ?>
<div style="margin-top:16px; text-align:center;">
<button id="mc-btn-forum-login" class="mc-btn mc-btn-secondary mc-full-width" type="button">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke-width="2"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14a4 4 0 100-8 4 4 0 000 8zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</svg>
Mit Forum-Login verifizieren
</button>
<p class="mc-small" style="margin-top:8px;">Optional: Wenn du im Forum eingeloggt bist und dein Minecraft-Account verknüpft ist, kannst du dich direkt verifizieren.</p>
</div>
<?php endif; ?>
</div>
</div>
@@ -866,6 +901,7 @@ class MC_Gallery_Shortcodes {
</div>
</div>
</div>
<?php endif; // modal_rendered ?>
<?php if ($show_default): ?>
<script>
@@ -889,4 +925,397 @@ class MC_Gallery_Shortcodes {
<?php
return ob_get_clean();
}
// =========================================================================
// === KOMBINIERTER SHORTCODE: Upload-Button + Forum-Panel ================
// =========================================================================
// Verwendung: [mc_gallery_combined] oder [mc_gallery_combined show="1"]
// Ersetzt [mc_gallery_upload] + [mc_gallery_forum_login] auf einer Seite.
public static function shortcode_combined($atts) {
$atts = shortcode_atts(['show' => '0', 'server_id' => ''], $atts);
$show_default = ($atts['show'] === '1');
$forum_login_enabled = get_option(MC_Gallery_Core::OPTION_FORUM_LOGIN, false) == '1';
$forum_plugin = class_exists('WBF_Auth');
$server_id = sanitize_text_field($atts['server_id']);
// Prüfen ob Forum-User bereits verifiziert ist (für Button-Zustand)
$forum_verified = false;
$forum_mc_name = '';
if ($forum_plugin && class_exists('MC_Gallery_Forum_Bridge')) {
$fu = WBF_Auth::get_current_user();
if ($fu) {
$mc = MC_Gallery_Forum_Bridge::get_mc_username($fu->id);
$ver = MC_Gallery_Forum_Bridge::is_verified($fu->id);
if ($mc && $ver) {
$forum_verified = true;
$forum_mc_name = $mc;
}
}
}
ob_start();
?>
<div class="mc-combined-wrapper">
<?php if ($forum_verified): ?>
<!-- Bereits verifiziert: Upload-Button + grüner Badge -->
<button id="mc-open-upload-modal" class="mc-btn mc-btn-primary mc-combined-upload-btn">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
</svg>
Bilder hochladen
</button>
<div class="mc-combined-verified-row">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M5 13l4 4L19 7"/>
</svg>
Verifiziert als <strong><?php echo esc_html($forum_mc_name); ?></strong>
</div>
<?php elseif ($forum_login_enabled && $forum_plugin): ?>
<!-- Zwei-Wege: Token-Upload + Forum-Login -->
<!-- Upload-Button (Token-Weg) -->
<button id="mc-open-upload-modal" class="mc-btn mc-btn-primary mc-combined-upload-btn">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
</svg>
Bilder hochladen
<span class="mc-combined-method-tag">Ingame-Token</span>
</button>
<div class="mc-combined-divider"><span>oder</span></div>
<!-- Forum-Card direkt inline (ohne extra Divider aus build_verify_panel) -->
<?php
if (class_exists('MC_Gallery_Forum_Bridge')) {
$fu = WBF_Auth::get_current_user();
$nonce = wp_create_nonce('mc_gallery_forum_bridge');
$ajax_url = admin_url('admin-ajax.php');
if (!$fu) :
$forum_url = function_exists('wbf_get_forum_url') ? wbf_get_forum_url() : '#';
$register_url = function_exists('wbf_get_forum_url') ? wbf_get_forum_url() . '?register=1' : '#';
?>
<a href="<?php echo esc_url($forum_url); ?>" class="mc-btn mc-combined-upload-btn mc-combined-forum-btn" style="text-decoration:none;display:flex;align-items:center;justify-content:center;gap:8px;">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"/>
</svg>
Mit Forum-Login verifizieren
<span class="mc-combined-method-tag" style="background:rgba(0,0,0,.2);">Forum</span>
</a>
<a href="<?php echo esc_url($register_url); ?>" class="mc-btn mc-btn-secondary mc-combined-upload-btn" style="">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"/>
</svg>
Noch kein Konto? Jetzt registrieren
</a>
<?php
else:
$mc_user = MC_Gallery_Forum_Bridge::get_mc_username($fu->id);
$verified = MC_Gallery_Forum_Bridge::is_verified($fu->id);
$prof_url = function_exists('wbf_get_forum_url') ? wbf_get_forum_url() . '?forum_profile=' . (int)$fu->id . '#tab-minecraft' : '#';
if ($mc_user && $verified):
?>
<div class="mc-bridge-success">
<i class="fas fa-check-circle"></i> Verifiziert als <strong><?php echo esc_html($mc_user); ?></strong>
</div>
<?php
elseif ($mc_user):
?>
<button type="button" class="mc-bridge-verify-trigger mc-btn mc-combined-upload-btn"
style="width:100%;justify-content:center;gap:8px;background:#f59e0b;border:none;color:#1c1008;padding:12px 20px;font-size:15px;font-weight:600;cursor:pointer;"
data-ajax="<?php echo esc_attr($ajax_url); ?>"
data-nonce="<?php echo esc_attr($nonce); ?>"
data-server="<?php echo esc_attr($server_id); ?>">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Mit Forum verifizieren <?php echo esc_html($mc_user); ?>
<span class="mc-combined-method-tag" style="background:rgba(0,0,0,.2);">Ausstehend</span>
</button>
<?php
else:
?>
<a href="<?php echo esc_url($prof_url); ?>" class="mc-btn mc-combined-upload-btn mc-combined-forum-btn" style="text-decoration:none;display:flex;align-items:center;justify-content:center;gap:8px;">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/>
</svg>
Minecraft-Konto verknüpfen
<span class="mc-combined-method-tag">Forum-Profil</span>
</a>
<?php
endif;
endif; // $fu
} // class_exists
?>
<?php else: ?>
<!-- Nur Upload-Button -->
<button id="mc-open-upload-modal" class="mc-btn mc-btn-primary mc-combined-upload-btn">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
</svg>
Bilder hochladen
</button>
<?php endif; ?>
</div>
<?php
// Modal HTML — nur einmal pro Seite ausgeben
$forum_login_for_modal = $forum_login_enabled && $forum_plugin;
if (!self::$modal_rendered): self::$modal_rendered = true;
?>
<div id="mc-upload-modal" class="mc-modal" aria-hidden="true" role="dialog">
<div class="mc-modal-backdrop"></div>
<div class="mc-modal-content">
<button class="mc-modal-close" aria-label="Schließen">&times;</button>
<div class="mc-upload-wizard">
<div class="mc-step active" data-step="1">
<div class="mc-step-header"><span class="mc-step-num">1</span><h3>Identifikation</h3></div>
<div class="mc-step-body">
<label for="mc-upload-username">Dein Minecraft Name</label>
<input type="text" id="mc-upload-username" placeholder="z.B. Steve" autocomplete="off">
<label for="mc-upload-server">Server wählen</label>
<select id="mc-upload-server"><option>Lade Server...</option></select>
<button id="mc-btn-generate" class="mc-btn mc-btn-primary mc-full-width">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/>
</svg>
Session starten
</button>
<?php if ($forum_login_for_modal): ?>
<div style="margin-top:16px; text-align:center;">
<button id="mc-btn-forum-login" class="mc-btn mc-btn-secondary mc-full-width" type="button">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke-width="2"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14a4 4 0 100-8 4 4 0 000 8zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/>
</svg>
Mit Forum-Login verifizieren
</button>
<p class="mc-small" style="margin-top:8px;">Optional: Wenn du im Forum eingeloggt bist und dein Minecraft-Account verknüpft ist, kannst du dich direkt verifizieren.</p>
</div>
<?php endif; ?>
</div>
</div>
<div class="mc-step" data-step="2">
<div class="mc-step-header"><span class="mc-step-num">2</span><h3>Verifizierung</h3></div>
<div class="mc-step-body">
<div class="mc-info-box">🎮 Gehe auf den Server und führe den folgenden Befehl aus:</div>
<div class="mc-token-box">
<code id="mc-token-text">Waiting...</code>
<button class="mc-copy-btn" title="Kopieren">📋</button>
</div>
<p class="mc-small">💡 Gib im Chat ein: <strong>/verify [dein-token]</strong></p>
<button id="mc-btn-check" class="mc-btn mc-btn-primary mc-full-width">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Verifizierung prüfen
</button>
</div>
</div>
<div class="mc-step" data-step="3">
<div class="mc-step-header"><span class="mc-step-num">3</span><h3>Bilder hochladen</h3></div>
<div class="mc-step-body">
<div class="mc-session-info">
<span class="mc-session-badge">✓ Session aktiv</span>
<span id="mc-session-user"></span>
</div>
<label>Album wählen (optional)</label>
<div class="mc-album-selector">
<select id="mc-upload-album"><option value="">Kein Album</option></select>
<button id="mc-btn-new-album" class="mc-btn mc-btn-ghost mc-btn-small" type="button">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
Neues Album
</button>
</div>
<div id="mc-new-album-form" style="display:none;">
<label>Album-Name</label>
<input type="text" id="mc-new-album-name" placeholder="z.B. Meine Bauten">
<button id="mc-btn-create-album" class="mc-btn mc-btn-primary mc-btn-small mc-full-width">Album erstellen</button>
</div>
<label>Bilder auswählen (mehrere möglich)</label>
<div class="mc-file-drop">
<input type="file" id="mc-upload-file" accept="image/*" multiple>
<svg width="48" height="48" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="margin:0 auto 12px;display:block;color:var(--mc-primary);">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
</svg>
<span id="mc-file-name">Keine Bilder gewählt</span>
</div>
<div id="mc-file-preview" class="mc-file-preview"></div>
<form id="mc-upload-form" style="display:none;">
<?php wp_nonce_field('mc_gallery_upload_action', 'mc_upload_nonce'); ?>
<input type="hidden" name="mc_token" id="mc_form_token">
<input type="hidden" name="mc_username" id="mc_form_username">
<input type="hidden" name="mc_server_id" id="mc_form_server">
<input type="hidden" name="mc_album_id" id="mc_form_album">
</form>
<div id="mc-upload-progress" style="display:none;">
<div class="mc-progress-bar"><div class="mc-progress-fill" id="mc-progress-fill"></div></div>
<p id="mc-progress-text">Uploading...</p>
</div>
<button id="mc-btn-final-upload" class="mc-btn mc-btn-success mc-full-width">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
</svg>
<span id="mc-upload-btn-text">Bilder hochladen</span>
</button>
<button id="mc-btn-upload-more" class="mc-btn mc-btn-ghost mc-full-width" style="display:none;margin-top:12px;">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
Weitere Bilder hochladen
</button>
</div>
</div>
<div id="mc-feedback-msg"></div>
</div>
</div>
</div>
<?php endif; // modal_rendered ?>
<?php if ($show_default): ?>
<script>(function(){ setTimeout(function(){ var b=document.getElementById('mc-open-upload-modal'); if(b) b.click(); }, 500); })();</script>
<?php endif; ?>
<?php
return ob_get_clean();
}
// =========================================================================
// === ABSTIMMUNG / VOTE BOARD =============================================
// =========================================================================
// Verwendung: [mc_gallery_vote] — Zeigt alle Bilder sortiert nach Likes
public static function shortcode_vote_board($atts) {
$atts = shortcode_atts(['limit' => 50, 'server_ids' => ''], $atts);
$server_ids = [];
if (!empty($atts['server_ids'])) {
foreach (explode(',', $atts['server_ids']) as $id) {
$int = intval(trim($id));
if ($int > 0) $server_ids[] = $int;
}
}
// Gallery-Posts ermitteln
$gallery_args = ['post_type' => 'mc_gallery', 'posts_per_page' => -1, 'fields' => 'ids', 'post_status' => 'publish'];
if (!empty($server_ids)) {
$gallery_args['meta_query'] = [['key' => 'mc_server', 'value' => $server_ids, 'compare' => 'IN']];
}
$gallery_ids = get_posts($gallery_args);
if (empty($gallery_ids)) {
return '<div class="mc-gallery-container"><div class="mc-empty-state">📷 Noch keine Bilder vorhanden.</div></div>';
}
// Alle Bilder laden, nach Likes sortiert
$images = get_posts([
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_mime_type' => 'image',
'post_parent__in'=> $gallery_ids,
'posts_per_page' => intval($atts['limit']),
'meta_key' => 'mc_votes_up',
'orderby' => 'meta_value_num',
'order' => 'DESC',
]);
// Auch Bilder ohne Votes holen
if (count($images) < intval($atts['limit'])) {
$voted_ids = array_map(fn($i) => $i->ID, $images);
$rest_args = [
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_mime_type' => 'image',
'post_parent__in'=> $gallery_ids,
'posts_per_page' => intval($atts['limit']) - count($images),
'orderby' => 'date',
'order' => 'DESC',
];
if (!empty($voted_ids)) $rest_args['post__not_in'] = $voted_ids;
$images = array_merge($images, get_posts($rest_args));
}
if (empty($images)) {
return '<div class="mc-gallery-container"><div class="mc-empty-state">📷 Noch keine Bilder vorhanden.</div></div>';
}
$show_date = get_option(MC_Gallery_Core::OPTION_SHOW_DATE, true);
$out = '<div class="mc-gallery-container mc-vote-board">';
$out .= '<div class="mc-player-header">';
$out .= '<div class="mc-player-title"><div class="mc-player-avatar">👍</div><h2>Abstimmung</h2></div>';
$out .= '</div>';
$out .= '<div class="mc-gallery-grid">';
foreach ($images as $img) {
$full = wp_get_attachment_url($img->ID);
$thumb_arr = wp_get_attachment_image_src($img->ID, 'medium');
$thumb = $thumb_arr ? $thumb_arr[0] : $full;
$views = (int) get_post_meta($img->ID, 'mc_views', true);
$upload_date = date_i18n('d.m.Y', strtotime($img->post_date));
$gallery_post = get_post($img->post_parent);
$player_name = $gallery_post ? get_post_meta($gallery_post->ID, 'mc_player', true) : '';
$metadata = wp_get_attachment_metadata($img->ID);
$ratio = (($metadata['width'] ?? 0) && ($metadata['height'] ?? 0))
? ($metadata['width'] / $metadata['height']) : 1;
$data_date = $show_date ? 'data-date="'.esc_attr($upload_date).'"' : '';
$out .= '<a href="'.esc_url($full).'" class="mc-gallery-item" data-player="'.esc_attr($player_name).'" '.$data_date.' data-attach-id="'.$img->ID.'">';
$out .= '<div class="mc-gallery-item-inner">';
$out .= '<div class="mc-views-badge">👁️ <span class="mc-views-count">'.$views.'</span></div>';
if ($show_date) $out .= '<div class="mc-date-badge">'.$upload_date.'</div>';
$out .= '<img src="'.esc_url($thumb).'" alt="'.esc_attr($player_name).'" loading="lazy" style="aspect-ratio:'.$ratio.';">';
$out .= self::render_vote_btns($img->ID);
$out .= '<div class="mc-gallery-overlay"><div class="mc-gallery-info"><div class="mc-watermark">'.esc_html($player_name).'</div></div></div>';
$out .= '</div>';
$out .= '</a>';
}
$out .= '</div></div>';
return $out;
}
// Hilfsfunktion: Like-Button HTML für normale Galerie-Items
public static function render_vote_btns($attach_id) {
if (!get_option(MC_Gallery_Core::OPTION_VOTING, true)) return '';
$up = (int) get_post_meta($attach_id, 'mc_votes_up', true);
$down = (int) get_post_meta($attach_id, 'mc_votes_down', true);
$nonce = wp_create_nonce('mc_gallery_upload_action');
$id = esc_attr($attach_id);
$n = esc_attr($nonce);
return
'<div class="mc-vote-wrap" data-attach-id="'.$id.'" data-nonce="'.$n.'">'
// Daumen hoch
.'<button class="mc-vote-btn mc-vote-up" data-type="up" title="Daumen hoch">'
.'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5"/></svg>'
.'<span class="mc-vote-count mc-vote-up-count">'.$up.'</span>'
.'</button>'
// Daumen runter
.'<button class="mc-vote-btn mc-vote-down" data-type="down" title="Daumen runter">'
.'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.736 3h4.018c.163 0 .326.02.485.06L17 4m-7 10v2a2 2 0 002 2h.095c.5 0 .905-.405.905-.905 0-.714.211-1.412.608-2.006L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5"/></svg>'
.'<span class="mc-vote-count mc-vote-down-count">'.$down.'</span>'
.'</button>'
.'</div>';
}
// Rückwärtskompatibilität
public static function render_like_btn($attach_id) {
return self::render_vote_btns($attach_id);
}
public static function ajax_vote() {
// Delegiert an MC_Gallery_Core::handle_vote()
if (class_exists('MC_Gallery_Core')) {
MC_Gallery_Core::handle_vote();
} else {
wp_send_json_error(['message' => 'Core nicht geladen.']);
}
}
}

View File

@@ -1,306 +1,330 @@
<?php
/*
Plugin Name: MC MultiServer Gallery PRO
Description: Professionelle Minecraft-Galerie mit Ingame-Verification, modernem UI, Lightbox und AJAX-Upload (multi-server fähig).
Version: 2.5.4
Author: M_Viper
Text Domain: mc-multiserver-gallery-pro
Domain Path: /languages
*/
if (!defined('ABSPATH')) exit;
// ===========================================
// MC GALLERY PRO - UPDATE NOTICE SYSTEM
// ===========================================
// Plugin-Version aus Header lesen (NICHT aus Konstante)
function mc_gallery_get_plugin_version() {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$plugin_data = get_plugin_data( __FILE__ );
return $plugin_data['Version'] ?? '0.0.0';
}
// Cache manuell leeren
function mc_gallery_clear_update_cache() {
if ( isset($_GET['mc_gallery_clear_cache']) && current_user_can('manage_options') ) {
check_admin_referer('mc_gallery_clear_cache_action');
delete_transient('mc_gallery_latest_release');
wp_redirect( admin_url('plugins.php') );
exit;
}
}
add_action('admin_init', 'mc_gallery_clear_update_cache');
// Neueste Release-Infos von Gitea holen
function mc_gallery_get_latest_release_info( $force_refresh = false ) {
$transient_key = 'mc_gallery_latest_release';
if ( $force_refresh ) {
delete_transient( $transient_key );
}
$release_info = get_transient( $transient_key );
if ( false === $release_info ) {
$response = wp_remote_get(
'https://git.viper.ipv64.net/api/v1/repos/M_Viper/Wordpress-MC-Gallery/releases/latest',
['timeout' => 10]
);
if ( ! is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response) ) {
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if ( $data && isset($data['tag_name']) ) {
$tag = ltrim( $data['tag_name'], 'vV' );
$release_info = [
'version' => $tag,
'download_url' => $data['zipball_url'] ?? '',
'notes' => $data['body'] ?? '',
'published_at' => $data['published_at'] ?? '',
];
set_transient( $transient_key, $release_info, 6 * HOUR_IN_SECONDS );
} else {
set_transient( $transient_key, [], HOUR_IN_SECONDS );
}
} else {
set_transient( $transient_key, [], HOUR_IN_SECONDS );
}
}
return $release_info;
}
// Admin-Update-Hinweis anzeigen
function mc_gallery_show_update_notice() {
if ( ! current_user_can('manage_options') ) {
return;
}
$current_version = mc_gallery_get_plugin_version();
$latest_release = mc_gallery_get_latest_release_info();
if ( ! empty($latest_release['version']) && version_compare($current_version, $latest_release['version'], '<') ) {
$refresh_url = wp_nonce_url(
admin_url('plugins.php?mc_gallery_clear_cache=1'),
'mc_gallery_clear_cache_action'
);
?>
<div class="notice notice-warning is-dismissible">
<h3>MC MultiServer Gallery PRO Update verfügbar</h3>
<p>
Installiert: <strong><?php echo esc_html($current_version); ?></strong><br>
Neueste Version: <strong><?php echo esc_html($latest_release['version']); ?></strong>
</p>
<p>
<a href="<?php echo esc_url($latest_release['download_url']); ?>" class="button button-primary" target="_blank">
Update herunterladen
</a>
<a href="https://git.viper.ipv64.net/M_Viper/Wordpress-MC-Gallery/releases" class="button" target="_blank">
Release Notes
</a>
<a href="<?php echo esc_url($refresh_url); ?>" class="button">
Jetzt neu prüfen
</a>
</p>
</div>
<?php
}
}
add_action('admin_notices', 'mc_gallery_show_update_notice');
if (version_compare(PHP_VERSION, '7.0', '<')) {
if (is_admin()) {
add_action('admin_notices', function() {
echo '<div class="error"><p><strong>MC Gallery PRO:</strong> Dieses Plugin benötigt PHP 7.0 oder höher.</p></div>';
});
}
return;
}
if (defined('MCGALLERY_PRO_VERSION')) { return; }
define('MCGALLERY_PRO_VERSION', '2.5.2');
define('MCGALLERY_PRO_DIR', plugin_dir_path(__FILE__));
define('MCGALLERY_PRO_URL', plugin_dir_url(__FILE__));
define('MCGALLERY_TOKEN_TTL', 300);
define('MCGALLERY_OPTION_KEY', 'mc_gallery_tokens_store');
function mc_gallery_pro_safe_require($path) {
if (!file_exists($path)) {
if (is_admin()) {
add_action('admin_notices', function() use ($path) {
echo '<div class="error"><p><strong>MC Gallery PRO Fehler:</strong> Datei nicht gefunden: <code>' . esc_html($path) . '</code>.</p></div>';
});
}
return false;
}
require_once $path;
return true;
}
function mc_gallery_pro_load_textdomain() {
load_plugin_textdomain('mc-multiserver-gallery-pro', false, basename(dirname(__FILE__)) . '/languages');
}
add_action('plugins_loaded', 'mc_gallery_pro_load_textdomain');
$core_files = [
'class-mc-gallery-helpers.php',
'class-mc-gallery-core.php',
'class-mc-gallery-shortcodes.php'
];
if (!is_dir(MCGALLERY_PRO_DIR . 'includes')) {
add_action('admin_notices', function() {
echo '<div class="error"><p><strong>MC Gallery PRO Fehler:</strong> Der Ordner <code>includes/</code> fehlt.</p></div>';
});
} else {
foreach ($core_files as $file) {
if (!mc_gallery_pro_safe_require(MCGALLERY_PRO_DIR . 'includes/' . $file)) {
return;
}
}
}
function mc_gallery_pro_activate_callback() {
if (class_exists('MC_Gallery_Core')) {
if (!get_option(MC_Gallery_Core::OPTION_THUMB_H)) {
update_option(MC_Gallery_Core::OPTION_THUMB_H, 200);
}
}
if (get_option(MCGALLERY_OPTION_KEY) === false) {
update_option(MCGALLERY_OPTION_KEY, []);
}
}
register_activation_hook(__FILE__, 'mc_gallery_pro_activate_callback');
function mc_gallery_pro_uninstall_callback() {
delete_option(MCGALLERY_OPTION_KEY);
if (class_exists('MC_Gallery_Core')) {
delete_option(MC_Gallery_Core::OPTION_THUMB_H);
}
}
register_uninstall_hook(__FILE__, 'mc_gallery_pro_uninstall_callback');
if (class_exists('MC_Gallery_Core')) {
add_action('plugins_loaded', ['MC_Gallery_Core', 'init']);
}
if (class_exists('MC_Gallery_Shortcodes')) {
add_action('plugins_loaded', ['MC_Gallery_Shortcodes', 'init']);
}
/* ================= SIDEBAR WIDGET: BILD DES TAGES ================= */
class MC_Daily_Image_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'mc_daily_image_widget',
'MC Bild des Tages',
array( 'description' => 'Zeigt jeden Tag ein anderes, zufälliges Bild aus allen MC Galerien an.' )
);
}
// Frontend-Ausgabe
public function widget( $args, $instance ) {
$title = ! empty( $instance['title'] ) ? $instance['title'] : 'Bild des Tages';
echo $args['before_widget'];
if ( ! empty( $title ) ) {
echo $args['before_title'] . apply_filters( 'widget_title', $title ) . $args['after_title'];
}
// 1. Eindeutiger Key für heute (z.B. mc_daily_image_2023-10-27)
$today_key = 'mc_daily_image_' . date('Y-m-d');
// 2. Prüfen, ob wir heute schon ein Bild ausgewählt haben
$image_id = get_transient( $today_key );
// Falls kein Bild für heute im Cache ist:
if ( false === $image_id ) {
// Alle MC Galerien finden
$galleries = get_posts([
'post_type' => 'mc_gallery',
'numberposts' => -1,
'fields' => 'ids',
'post_status' => 'publish'
]);
if ( ! empty( $galleries ) ) {
// Ein zufälliges Bild aus diesen Galerien holen
$random_image = get_posts([
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_parent__in'=> $galleries,
'numberposts' => 1,
'orderby' => 'rand', // Zufällig wählen
'suppress_filters' => false
]);
if ( ! empty( $random_image ) ) {
$image_id = $random_image[0]->ID;
// Das Bild für 24 Stunden (bis morgen) speichern
set_transient( $today_key, $image_id, DAY_IN_SECONDS );
}
}
}
// 3. Bild anzeigen (wird aus Cache oder neu geladen)
if ( $image_id && wp_attachment_is_image( $image_id ) ) {
$img_src = wp_get_attachment_image_src( $image_id, 'medium' );
$full_src = wp_get_attachment_image_src( $image_id, 'full' );
if ( $img_src ) {
?>
<div class="mc-daily-image-container" style="text-align: center;">
<a href="<?php echo esc_url( $full_src[0] ); ?>" target="_blank" title="Vollbild anzeigen">
<img src="<?php echo esc_url( $img_src[0] ); ?>" alt="MC Bild des Tages" style="max-width: 100%; height: auto; border-radius: 4px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);" />
</a>
</div>
<?php
}
} else {
echo '<p style="text-align:center; color:#888;">Keine Bilder verfügbar.</p>';
}
echo $args['after_widget'];
}
// Backend Formular
public function form( $instance ) {
$title = ! empty( $instance['title'] ) ? $instance['title'] : 'Bild des Tages';
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>">Titel:</label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>"
name="<?php echo $this->get_field_name( 'title' ); ?>" type="text"
value="<?php echo esc_attr( $title ); ?>">
</p>
<p class="description" style="font-size: 0.9em; color: #666;">
Dieses Widget wählt einmal pro Tag (um 00:00 Uhr) ein zufälliges Bild aus allen Spieler-Galerien aus und zeigt es an. Der Bildtext ist ausgeblendet.
</p>
<?php
}
// Speichern der Einstellungen
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? sanitize_text_field( $new_instance['title'] ) : 'Bild des Tages';
return $instance;
}
}
// Widget registrieren
function mc_register_daily_image_widget() {
register_widget( 'MC_Daily_Image_Widget' );
}
<?php
/*
Plugin Name: MC MultiServer Gallery PRO
Description: Professionelle Minecraft-Galerie mit Ingame-Verification, modernem UI, Lightbox und AJAX-Upload (multi-server fähig).
Version: 2.5.5
Author: M_Viper
Plugin URI: https://git.viper.ipv64.net/M_Viper/Wordpress-MC-Gallery/releases
Author URI: https://m-viper.de
Text Domain: mc-multiserver-gallery-pro
Domain Path: /languages
*/
if (!defined('ABSPATH')) exit;
// ===========================================
// MC GALLERY PRO - UPDATE NOTICE SYSTEM
// ===========================================
// Plugin-Version aus Header lesen (NICHT aus Konstante)
function mc_gallery_get_plugin_version() {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$plugin_data = get_plugin_data( __FILE__ );
return $plugin_data['Version'] ?? '0.0.0';
}
// Cache manuell leeren
function mc_gallery_clear_update_cache() {
if ( isset($_GET['mc_gallery_clear_cache']) && current_user_can('manage_options') ) {
check_admin_referer('mc_gallery_clear_cache_action');
delete_transient('mc_gallery_latest_release');
wp_redirect( admin_url('plugins.php') );
exit;
}
}
add_action('admin_init', 'mc_gallery_clear_update_cache');
// Neueste Release-Infos von Gitea holen
function mc_gallery_get_latest_release_info( $force_refresh = false ) {
$transient_key = 'mc_gallery_latest_release';
if ( $force_refresh ) {
delete_transient( $transient_key );
}
$release_info = get_transient( $transient_key );
if ( false === $release_info ) {
$response = wp_remote_get(
'https://git.viper.ipv64.net/api/v1/repos/M_Viper/Wordpress-MC-Gallery/releases/latest',
['timeout' => 10]
);
if ( ! is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response) ) {
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if ( $data && isset($data['tag_name']) ) {
$tag = ltrim( $data['tag_name'], 'vV' );
$release_info = [
'version' => $tag,
'download_url' => $data['zipball_url'] ?? '',
'notes' => $data['body'] ?? '',
'published_at' => $data['published_at'] ?? '',
];
set_transient( $transient_key, $release_info, 6 * HOUR_IN_SECONDS );
} else {
set_transient( $transient_key, [], HOUR_IN_SECONDS );
}
} else {
set_transient( $transient_key, [], HOUR_IN_SECONDS );
}
}
return $release_info;
}
// Admin-Update-Hinweis anzeigen
function mc_gallery_show_update_notice() {
if ( ! current_user_can('manage_options') ) {
return;
}
$current_version = mc_gallery_get_plugin_version();
$latest_release = mc_gallery_get_latest_release_info();
if ( ! empty($latest_release['version']) && version_compare($current_version, $latest_release['version'], '<') ) {
$refresh_url = wp_nonce_url(
admin_url('plugins.php?mc_gallery_clear_cache=1'),
'mc_gallery_clear_cache_action'
);
?>
<div class="notice notice-warning is-dismissible">
<h3>MC MultiServer Gallery PRO Update verfügbar</h3>
<p>
Installiert: <strong><?php echo esc_html($current_version); ?></strong><br>
Neueste Version: <strong><?php echo esc_html($latest_release['version']); ?></strong>
</p>
<p>
<a href="<?php echo esc_url($latest_release['download_url']); ?>" class="button button-primary" target="_blank">
Update herunterladen
</a>
<a href="https://git.viper.ipv64.net/M_Viper/Wordpress-MC-Gallery/releases" class="button" target="_blank">
Release Notes
</a>
<a href="<?php echo esc_url($refresh_url); ?>" class="button">
Jetzt neu prüfen
</a>
</p>
</div>
<?php
}
}
add_action('admin_notices', 'mc_gallery_show_update_notice');
if (version_compare(PHP_VERSION, '7.0', '<')) {
if (is_admin()) {
add_action('admin_notices', function() {
echo '<div class="error"><p><strong>MC Gallery PRO:</strong> Dieses Plugin benötigt PHP 7.0 oder höher.</p></div>';
});
}
return;
}
if (defined('MCGALLERY_PRO_VERSION')) { return; }
define('MCGALLERY_PRO_VERSION', '2.5.4');
define('MCGALLERY_PRO_DIR', plugin_dir_path(__FILE__));
define('MCGALLERY_PRO_URL', plugin_dir_url(__FILE__));
define('MCGALLERY_TOKEN_TTL', 300);
define('MCGALLERY_OPTION_KEY', 'mc_gallery_tokens_store');
function mc_gallery_pro_safe_require($path) {
if (!file_exists($path)) {
if (is_admin()) {
add_action('admin_notices', function() use ($path) {
echo '<div class="error"><p><strong>MC Gallery PRO Fehler:</strong> Datei nicht gefunden: <code>' . esc_html($path) . '</code>.</p></div>';
});
}
return false;
}
require_once $path;
return true;
}
function mc_gallery_pro_load_textdomain() {
load_plugin_textdomain('mc-multiserver-gallery-pro', false, basename(dirname(__FILE__)) . '/languages');
}
add_action('plugins_loaded', 'mc_gallery_pro_load_textdomain');
$core_files = [
'class-mc-gallery-helpers.php',
'class-mc-gallery-core.php',
'class-mc-gallery-shortcodes.php',
];
if (!is_dir(MCGALLERY_PRO_DIR . 'includes')) {
add_action('admin_notices', function() {
echo '<div class="error"><p><strong>MC Gallery PRO Fehler:</strong> Der Ordner <code>includes/</code> fehlt.</p></div>';
});
} else {
foreach ($core_files as $file) {
if (!mc_gallery_pro_safe_require(MCGALLERY_PRO_DIR . 'includes/' . $file)) {
return;
}
}
// ── WP Business Forum — optionale Bridge ─────────────────────────────────
// Wird nur geladen wenn das Forum-Plugin ebenfalls aktiv ist.
// Keine harte Abhängigkeit — Gallery funktioniert ohne Forum genauso wie vorher.
add_action('plugins_loaded', function() {
$bridge_file = MCGALLERY_PRO_DIR . 'includes/class-mc-gallery-forum-bridge.php';
if ( file_exists($bridge_file) && class_exists('WBF_Auth') ) {
require_once $bridge_file;
MC_Gallery_Forum_Bridge::init();
// Shortcode für manuelles Einbetten des Verify-Panels auf beliebigen Seiten:
// [mc_gallery_forum_login server_id="survival"]
add_shortcode('mc_gallery_forum_login', ['MC_Gallery_Forum_Bridge', 'shortcode_verify_panel']);
}
}, 20); // Prio 20 → nach Forum (Prio 1) und Gallery-Core (Prio 10)
}
// ── Aktivierungs- / Deinstallations-Hooks ────────────────────────────────────
function mc_gallery_pro_activate_callback() {
if (class_exists('MC_Gallery_Core')) {
if (!get_option(MC_Gallery_Core::OPTION_THUMB_H)) {
update_option(MC_Gallery_Core::OPTION_THUMB_H, 200);
}
}
if (get_option(MCGALLERY_OPTION_KEY) === false) {
update_option(MCGALLERY_OPTION_KEY, []);
}
}
register_activation_hook(__FILE__, 'mc_gallery_pro_activate_callback');
function mc_gallery_pro_uninstall_callback() {
delete_option(MCGALLERY_OPTION_KEY);
if (class_exists('MC_Gallery_Core')) {
delete_option(MC_Gallery_Core::OPTION_THUMB_H);
}
}
register_uninstall_hook(__FILE__, 'mc_gallery_pro_uninstall_callback');
// ── Core-Klassen initialisieren ───────────────────────────────────────────────
if (class_exists('MC_Gallery_Core')) {
add_action('plugins_loaded', ['MC_Gallery_Core', 'init']);
}
if (class_exists('MC_Gallery_Shortcodes')) {
add_action('plugins_loaded', ['MC_Gallery_Shortcodes', 'init']);
}
/* ================= SIDEBAR WIDGET: BILD DES TAGES ================= */
class MC_Daily_Image_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'mc_daily_image_widget',
'MC Bild des Tages',
array( 'description' => 'Zeigt jeden Tag ein anderes, zufälliges Bild aus allen MC Galerien an.' )
);
}
// Frontend-Ausgabe
public function widget( $args, $instance ) {
$title = ! empty( $instance['title'] ) ? $instance['title'] : 'Bild des Tages';
echo $args['before_widget'];
if ( ! empty( $title ) ) {
echo $args['before_title'] . apply_filters( 'widget_title', $title ) . $args['after_title'];
}
// 1. Eindeutiger Key für heute (z.B. mc_daily_image_2023-10-27)
$today_key = 'mc_daily_image_' . date('Y-m-d');
// 2. Prüfen, ob wir heute schon ein Bild ausgewählt haben
$image_id = get_transient( $today_key );
// Falls kein Bild für heute im Cache ist:
if ( false === $image_id ) {
// Alle MC Galerien finden
$galleries = get_posts([
'post_type' => 'mc_gallery',
'numberposts' => -1,
'fields' => 'ids',
'post_status' => 'publish',
]);
if ( ! empty( $galleries ) ) {
// Ein zufälliges Bild aus diesen Galerien holen
$random_image = get_posts([
'post_type' => 'attachment',
'post_mime_type' => 'image',
'post_parent__in' => $galleries,
'numberposts' => 1,
'orderby' => 'rand',
'suppress_filters' => false,
]);
if ( ! empty( $random_image ) ) {
$image_id = $random_image[0]->ID;
// Das Bild für 24 Stunden (bis morgen) speichern
set_transient( $today_key, $image_id, DAY_IN_SECONDS );
}
}
}
// 3. Bild anzeigen (wird aus Cache oder neu geladen)
if ( $image_id && wp_attachment_is_image( $image_id ) ) {
$img_src = wp_get_attachment_image_src( $image_id, 'medium' );
$full_src = wp_get_attachment_image_src( $image_id, 'full' );
if ( $img_src ) {
?>
<div class="mc-daily-image-container" style="text-align: center;">
<a href="<?php echo esc_url( $full_src[0] ); ?>" target="_blank" title="Vollbild anzeigen">
<img src="<?php echo esc_url( $img_src[0] ); ?>" alt="MC Bild des Tages"
style="max-width: 100%; height: auto; border-radius: 4px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);" />
</a>
</div>
<?php
}
} else {
echo '<p style="text-align:center; color:#888;">Keine Bilder verfügbar.</p>';
}
echo $args['after_widget'];
}
// Backend Formular
public function form( $instance ) {
$title = ! empty( $instance['title'] ) ? $instance['title'] : 'Bild des Tages';
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>">Titel:</label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>"
name="<?php echo $this->get_field_name( 'title' ); ?>" type="text"
value="<?php echo esc_attr( $title ); ?>">
</p>
<p class="description" style="font-size: 0.9em; color: #666;">
Dieses Widget wählt einmal pro Tag (um 00:00 Uhr) ein zufälliges Bild aus allen Spieler-Galerien aus und zeigt es an. Der Bildtext ist ausgeblendet.
</p>
<?php
}
// Speichern der Einstellungen
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) )
? sanitize_text_field( $new_instance['title'] )
: 'Bild des Tages';
return $instance;
}
}
// Widget registrieren
function mc_register_daily_image_widget() {
register_widget( 'MC_Daily_Image_Widget' );
}
add_action( 'widgets_init', 'mc_register_daily_image_widget' );