Update from Git Manager GUI

This commit is contained in:
2026-03-29 22:30:22 +02:00
parent 8be73b9764
commit 94975c41fb
4 changed files with 2973 additions and 33 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,854 @@
<?php
/**
* Assistant Widget Virtueller Support-Assistent
*
* Vollständig integriert mit allen Plugins:
* → LiteBans Manager
* → MC Player History
* → BungeeCord Status
* → Multi Rules
* → WP Multi Wiki
* → WP Ingame Shop Pro
* → WP Multi Ticket Pro
* → WP Business Forum
* → MC MultiServer Gallery PRO
* → FAQ (Custom Post Type)
*
* @version 3.1
* @author M_Viper
*
* WICHTIG: Die eigentliche Antwort-Logik liegt in assistant-ajax.php.
* Diese Datei enthält nur: Admin-Backend, Widget-Frontend, JS.
*/
if ( ! defined( 'ABSPATH' ) ) exit;
// =========================================================================
// AJAX-Handler laden (assistant-ajax.php)
// =========================================================================
$mm_ajax_file = get_template_directory() . '/inc/assistant-ajax.php';
if ( file_exists( $mm_ajax_file ) ) {
require_once $mm_ajax_file;
}
// =========================================================================
// 1. ADMIN-BACKEND BOT-ZENTRALE
// =========================================================================
add_action( 'admin_menu', function () {
add_menu_page(
'Bot Setup',
'Bot Setup',
'manage_options',
'bot-setup',
'mm_render_bot_admin',
'dashicons-robot',
65
);
} );
add_action( 'admin_init', function () {
register_setting( 'mm_bot_settings', 'mm_bot_data', [
'sanitize_callback' => 'mm_bot_sanitize_settings',
] );
} );
function mm_bot_sanitize_settings( $input ) {
$clean = [];
$text_fields = [ 'server_ip', 'server_ver', 'server_specs', 'bot_name', 'welcome' ];
$url_fields = [
'url_wiki', 'url_rules', 'url_tickets', 'url_faq',
'url_team', 'url_shop', 'url_gallery', 'url_player_history',
'url_forum', 'link_discord', 'litebans_dashboard_url',
];
foreach ( $text_fields as $f ) {
$clean[ $f ] = isset( $input[ $f ] ) ? sanitize_text_field( $input[ $f ] ) : '';
}
foreach ( $url_fields as $f ) {
$clean[ $f ] = isset( $input[ $f ] ) ? esc_url_raw( $input[ $f ] ) : '';
}
$clean['qa'] = [];
if ( ! empty( $input['qa'] ) && is_array( $input['qa'] ) ) {
foreach ( $input['qa'] as $item ) {
if ( empty( $item['keys'] ) ) continue;
$clean['qa'][] = [
'keys' => sanitize_text_field( $item['keys'] ),
'val' => wp_kses_post( $item['val'] ),
];
}
}
return $clean;
}
// =========================================================================
// 2. ADMIN-SEITE
// =========================================================================
function mm_render_bot_admin() {
$data = get_option( 'mm_bot_data', [] );
?>
<div class="wrap">
<h1><span class="dashicons dashicons-robot"></span> Bot-Zentrale</h1>
<style>
.bot-card { background:#fff; padding:20px; border:1px solid #ccd0d4; border-radius:8px; margin-top:20px; box-shadow:0 2px 4px rgba(0,0,0,.05); }
.bot-card h2 { margin-top:0; color:#0073aa; border-bottom:1px solid #eee; padding-bottom:12px; }
.bot-card p.description { color:#666; font-style:italic; margin-top:4px; }
.badge { display:inline-block; background:#0099ff; color:#fff; font-size:11px; padding:1px 7px; border-radius:10px; margin-left:6px; vertical-align:middle; }
.badge.inactive { background:#999; }
</style>
<form method="post" action="options.php">
<?php settings_fields( 'mm_bot_settings' ); ?>
<!-- KARTE 1: Server -->
<div class="bot-card">
<h2>1. Server-Infos</h2>
<table class="form-table">
<tr>
<th><label>Server-IP / Adresse</label></th>
<td>
<input type="text" name="mm_bot_data[server_ip]" value="<?php echo esc_attr( $data['server_ip'] ?? '' ); ?>" class="regular-text" placeholder="play.example.net">
<p class="description">Wird bei Fragen nach der Server-IP angezeigt.</p>
</td>
</tr>
<tr>
<th><label>Minecraft-Version</label></th>
<td><input type="text" name="mm_bot_data[server_ver]" value="<?php echo esc_attr( $data['server_ver'] ?? '' ); ?>" class="small-text" placeholder="1.21.1"></td>
</tr>
<tr>
<th><label>Hardware / Specs</label></th>
<td>
<input type="text" name="mm_bot_data[server_specs]" value="<?php echo esc_attr( $data['server_specs'] ?? '' ); ?>" class="large-text" placeholder="z.B. Ryzen 9, 64 GB RAM, NVMe SSD">
<p class="description">Wird bei Fragen nach der Server-Hardware angezeigt.</p>
</td>
</tr>
</table>
</div>
<!-- KARTE 2: Bot-Einstellungen -->
<div class="bot-card">
<h2>2. Bot-Einstellungen</h2>
<table class="form-table">
<tr>
<th><label>Bot-Name</label></th>
<td><input type="text" name="mm_bot_data[bot_name]" value="<?php echo esc_attr( $data['bot_name'] ?? '' ); ?>" class="regular-text" placeholder="Viper-Bot"></td>
</tr>
<tr>
<th><label>Minecraft-UUID / Name (Avatar)</label></th>
<td>
<?php
$uuid = get_theme_mod( 'assistant_minecraft_uuid', 'Steve' );
?>
<input type="text" name="assistant_mc_uuid_preview" value="<?php echo esc_attr( $uuid ); ?>" class="regular-text" disabled>
<p class="description">UUID / Name wird im Theme-Customizer unter "Assistent" gesetzt.</p>
</td>
</tr>
<tr>
<th><label>Begrüßungstext</label></th>
<td>
<textarea name="mm_bot_data[welcome]" rows="3" class="large-text"><?php echo esc_textarea( $data['welcome'] ?? '' ); ?></textarea>
<p class="description">Erster Text der beim Öffnen des Assistenten angezeigt wird.</p>
</td>
</tr>
</table>
</div>
<!-- KARTE 3: Links -->
<div class="bot-card">
<h2>3. Wichtige Links</h2>
<p class="description" style="margin-bottom:12px;">
💡 Wenn eine Seite mit dem passenden Shortcode gefunden wird, erscheint ein <strong>„Vorschlag übernehmen"</strong>-Button.
</p>
<?php
// ── Auto-Detect: Seiten mit bekannten Shortcodes suchen ──────────────
function mm_find_page_by_shortcode( $shortcodes ) {
global $wpdb;
$conditions = [];
foreach ( (array) $shortcodes as $sc ) {
$conditions[] = $wpdb->prepare( 'post_content LIKE %s', '%[' . $wpdb->esc_like( $sc ) . '%' );
}
if ( empty( $conditions ) ) return '';
$where = implode( ' OR ', $conditions );
$page = $wpdb->get_row(
"SELECT ID FROM {$wpdb->posts}
WHERE post_status = 'publish'
AND post_type IN ('page','post')
AND ({$where})
LIMIT 1"
);
return $page ? get_permalink( $page->ID ) : '';
}
$autodetect = [
'url_wiki' => mm_find_page_by_shortcode( ['wmw_wiki', 'wmw_search', 'wmw_article'] ),
'url_rules' => mm_find_page_by_shortcode( ['mrp_rules', 'mc_rules', 'multi_rules'] ),
'url_tickets' => mm_find_page_by_shortcode( ['wmtp_tickets', 'wm_tickets', 'multi_ticket'] ),
'url_shop' => mm_find_page_by_shortcode( ['wis_shop', 'ingame_shop', 'wis_items'] ),
'url_gallery' => mm_find_page_by_shortcode( ['mc_gallery', 'mc_gallery_overview', 'mc_gallery_upload', 'mc_gallery_all_albums'] ),
'url_faq' => mm_find_page_by_shortcode( ['faq_list', 'faq', 'faq_page'] ),
'url_player_history' => mm_find_page_by_shortcode( ['mc_player_history', 'mc_players', 'player_history'] ),
'url_forum' => mm_find_page_by_shortcode( ['business_forum'] ),
'litebans_dashboard_url' => mm_find_page_by_shortcode( ['litebans_dashboard', 'litebans', 'wp_litebans'] ),
];
$links = [
'url_wiki' => [ 'label' => '📖 Wiki-URL', 'plugin' => 'WP Multi Wiki', 'active' => post_type_exists( 'wmw_article' ) ],
'url_rules' => [ 'label' => '📜 Regelwerk-URL', 'plugin' => 'Multi Rules', 'active' => function_exists( 'mrp_get_plugin_version' ) ],
'url_tickets' => [ 'label' => '🎫 Ticket/Support-URL', 'plugin' => 'WP Multi Ticket Pro', 'active' => class_exists( 'WP_Multi_Ticket_Pro' ) ],
'url_shop' => [ 'label' => '🛒 Shop-URL', 'plugin' => 'WP Ingame Shop Pro', 'active' => class_exists( 'WIS_Activator' ) ],
'url_gallery' => [ 'label' => '📷 Galerie-URL', 'plugin' => 'MC MultiServer Gallery PRO', 'active' => post_type_exists( 'mc_gallery' ) ],
'url_faq' => [ 'label' => '❓ FAQ-URL', 'plugin' => 'FAQ Post Type', 'active' => post_type_exists( 'faq' ) ],
'url_player_history' => [ 'label' => '👤 Spieler-History-URL', 'plugin' => 'MC Player History', 'active' => function_exists( 'mcph_get_plugin_version' ) ],
'url_forum' => [ 'label' => '💬 Forum-URL', 'plugin' => 'WP Business Forum', 'active' => class_exists( 'WBF_DB' ) ],
'url_team' => [ 'label' => '👥 Team-URL', 'plugin' => '', 'active' => true ],
'link_discord' => [ 'label' => '💬 Discord-Einladung', 'plugin' => '', 'active' => true ],
'litebans_dashboard_url' => [ 'label' => '🔨 LiteBans Dashboard-URL', 'plugin' => 'LiteBans Manager', 'active' => class_exists( 'WP_LiteBans_Pro' ) ],
];
?>
<style>
.mm-url-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mm-url-row input.large-text { flex: 1; min-width: 200px; }
.mm-suggest-btn {
background: #e8f5e9; color: #2e7d32; border: 1px solid #a5d6a7;
border-radius: 4px; padding: 5px 10px; font-size: 12px;
cursor: pointer; white-space: nowrap; line-height: 1.4;
transition: background .2s;
}
.mm-suggest-btn:hover { background: #c8e6c9; }
.mm-suggest-url { font-size: 11px; color: #666; max-width: 260px;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
display: inline-block; vertical-align: middle; }
.mm-autofill-all {
margin-bottom: 12px; background: #0073aa; color: #fff;
border: none; border-radius: 4px; padding: 7px 16px;
cursor: pointer; font-size: 13px;
}
.mm-autofill-all:hover { background: #005f8d; }
</style>
<?php
$has_any_suggestion = ! empty( array_filter( $autodetect ) );
if ( $has_any_suggestion ) : ?>
<button type="button" class="mm-autofill-all" id="mm-autofill-all">
⚡ Alle erkannten URLs automatisch übernehmen
</button>
<?php endif; ?>
<table class="form-table">
<?php foreach ( $links as $key => $cfg ) :
$badge = '';
if ( $cfg['plugin'] ) {
$cls = $cfg['active'] ? 'badge' : 'badge inactive';
$txt = $cfg['active'] ? 'aktiv' : 'inaktiv';
$badge = '<span class="' . $cls . '">' . esc_html( $cfg['plugin'] ) . ' ' . $txt . '</span>';
}
$saved = $data[ $key ] ?? '';
$suggested = $autodetect[ $key ] ?? '';
$show_suggest = $suggested && $suggested !== $saved;
?>
<tr>
<th><label for="mm_link_<?php echo esc_attr( $key ); ?>"><?php echo $cfg['label'] . $badge; ?></label></th>
<td>
<div class="mm-url-row">
<input type="url"
id="mm_link_<?php echo esc_attr( $key ); ?>"
name="mm_bot_data[<?php echo esc_attr( $key ); ?>]"
value="<?php echo esc_url( $saved ); ?>"
class="large-text"
data-key="<?php echo esc_attr( $key ); ?>">
<?php if ( $show_suggest ) : ?>
<button type="button"
class="mm-suggest-btn"
data-target="mm_link_<?php echo esc_attr( $key ); ?>"
data-url="<?php echo esc_url( $suggested ); ?>">
✓ Vorschlag übernehmen
</button>
<span class="mm-suggest-url" title="<?php echo esc_attr( $suggested ); ?>">
<?php echo esc_html( $suggested ); ?>
</span>
<?php elseif ( $saved && $suggested && $suggested === $saved ) : ?>
<span style="color:#2e7d32;font-size:12px;">✔ Erkannt &amp; gesetzt</span>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
<!-- KARTE 4: Individuelle Q&A -->
<div class="bot-card">
<h2>4. Individuelle Q&A</h2>
<p class="description">
Schlüsselwörter (kommagetrennt) → Antwort. Hat höchste Priorität vor allen Plugin-Abfragen.
</p>
<table class="form-table" style="width:100%;">
<tr>
<th style="width:200px;">Schlüsselwörter</th>
<th>Antwort (HTML erlaubt)</th>
<th style="width:50px;"></th>
</tr>
</table>
<div id="mm-qa-rows">
<?php
$qa = $data['qa'] ?? [];
foreach ( $qa as $i => $item ) :
if ( empty( $item['keys'] ) ) continue;
?>
<div class="qa-item" style="display:flex;gap:10px;margin-bottom:10px;align-items:center;">
<input type="text" name="mm_bot_data[qa][<?php echo $i; ?>][keys]"
value="<?php echo esc_attr( $item['keys'] ); ?>"
style="flex:1;" placeholder="discord, invite, join discord">
<input type="text" name="mm_bot_data[qa][<?php echo $i; ?>][val]"
value="<?php echo esc_attr( $item['val'] ); ?>"
style="flex:2;" placeholder="Antworttext oder HTML">
<button type="button" class="button remove-qa" title="Entfernen">✕</button>
</div>
<?php endforeach; ?>
</div>
<button type="button" id="add-qa" class="button button-primary">+ Neue Q&amp;A-Zeile</button>
</div>
<?php submit_button( 'Einstellungen speichern' ); ?>
</form>
</div>
<script>
jQuery(function($){
// Q&A Zeilen
$('#add-qa').on('click', function(){
var i = Date.now();
$('#mm-qa-rows').append(
'<div class="qa-item" style="display:flex;gap:10px;margin-bottom:10px;align-items:center;">' +
'<input type="text" name="mm_bot_data[qa]['+i+'][keys]" style="flex:1;" placeholder="discord, invite">' +
'<input type="text" name="mm_bot_data[qa]['+i+'][val]" style="flex:2;" placeholder="Antwort...">' +
'<button type="button" class="button remove-qa" title="Entfernen">✕</button>' +
'</div>'
);
});
$(document).on('click', '.remove-qa', function(){
$(this).closest('.qa-item').remove();
});
// Einzelnen Vorschlag übernehmen
$(document).on('click', '.mm-suggest-btn', function(){
var $btn = $(this);
var target = $btn.data('target');
var url = $btn.data('url');
$('#' + target).val(url);
$btn.replaceWith('<span style="color:#2e7d32;font-size:12px;">✔ Übernommen</span>');
$(this).siblings('.mm-suggest-url').remove();
});
// Alle auf einmal übernehmen
$('#mm-autofill-all').on('click', function(){
$('.mm-suggest-btn').each(function(){
var $btn = $(this);
var target = $btn.data('target');
var url = $btn.data('url');
if ( url && ! $('#' + target).val() ) {
$('#' + target).val(url);
$btn.closest('.mm-url-row').find('.mm-suggest-url').remove();
$btn.replaceWith('<span style="color:#2e7d32;font-size:12px;">✔ Übernommen</span>');
}
});
$(this).prop('disabled', true).text('✔ Fertig bitte speichern');
});
});
</script>
<?php
}
// =========================================================================
// 3. ASSETS LADEN
// =========================================================================
add_action( 'wp_enqueue_scripts', function () {
// CSS aus Theme-Ordner (falls vorhanden), sonst Inline-Fallback
$css_file = get_template_directory() . '/css/assistant-widget.css';
if ( file_exists( $css_file ) ) {
wp_enqueue_style(
'mm-assistant-widget',
get_template_directory_uri() . '/css/assistant-widget.css',
[],
'3.1'
);
}
} );
// =========================================================================
// 4. FRONTEND WIDGET
// =========================================================================
add_action( 'wp_footer', 'mm_bot_render_widget', 50 );
function mm_bot_render_widget() {
$data = get_option( 'mm_bot_data', [] );
$uuid = sanitize_text_field( trim( get_theme_mod( 'assistant_minecraft_uuid', 'Steve' ) ) );
$name = ! empty( $data['bot_name'] ) ? esc_html( $data['bot_name'] ) : 'Viper-Bot';
$welcome = ! empty( $data['welcome'] ) ? $data['welcome'] : 'Hallo! Wie kann ich dir helfen? 👋';
$nonce = wp_create_nonce( 'mm_bot_nonce' );
$ajax = admin_url( 'admin-ajax.php' );
// ── Quick-Buttons: nur anzeigen wenn Plugin aktiv + URL gesetzt ──────
$quick = [];
// Server-Status (immer wenn IP konfiguriert oder BungeeCord-Plugin aktiv)
$servers = get_option( 'mcss_servers', [] );
if ( ! empty( $data['server_ip'] ) || ! empty( $servers ) ) {
$quick[] = [ 'label' => '🖥️ Server-Status', 'q' => 'server status' ];
}
// Regeln nur wenn URL im Backend gesetzt
if ( ! empty( $data['url_rules'] ) ) {
$quick[] = [ 'label' => '📜 Regelwerk', 'q' => 'regeln' ];
}
// Wiki nur wenn URL im Backend gesetzt
if ( ! empty( $data['url_wiki'] ) ) {
$quick[] = [ 'label' => '📖 Wiki', 'q' => 'wiki' ];
}
// Shop nur wenn URL im Backend gesetzt
global $wpdb;
if ( ! empty( $data['url_shop'] ) ) {
$quick[] = [ 'label' => '🛒 Shop', 'q' => 'shop' ];
}
// Ticket / Support nur wenn URL im Backend gesetzt
if ( ! empty( $data['url_tickets'] ) ) {
$quick[] = [ 'label' => '🎫 Support-Ticket', 'q' => 'ticket erstellen' ];
}
// Forum
if ( class_exists( 'WBF_DB' ) && ! empty( $data['url_forum'] ) ) {
$quick[] = [ 'label' => '💬 Forum', 'q' => 'forum' ];
}
// Galerie nur wenn URL im Backend gesetzt
if ( ! empty( $data['url_gallery'] ) ) {
$quick[] = [ 'label' => '📷 Galerie', 'q' => 'galerie' ];
}
// Ban-Status
$lb = get_option( 'wp_litebans_pro_settings', [] );
if ( ! empty( $lb['db_name'] ) ) {
$quick[] = [ 'label' => '🔨 Strafen prüfen', 'q' => 'meine strafen' ];
}
// Spieler-History nur wenn URL im Backend gesetzt
if ( ! empty( $data['url_player_history'] ) ) {
$quick[] = [ 'label' => '⏱️ Spielzeit', 'q' => 'spielzeit' ];
}
// Discord
if ( ! empty( $data['link_discord'] ) ) {
$quick[] = [ 'label' => '💬 Discord', 'q' => 'discord' ];
}
?>
<div id="mm-bot-root">
<!-- Chat-Fenster -->
<div id="mm-bot-chat" style="display:none;" aria-label="Assistent" role="dialog">
<!-- Header -->
<div class="mm-bot-header">
<div class="mm-bot-status" title="Online"></div>
<img class="mm-bot-avatar-small"
src="https://mc-heads.net/avatar/<?php echo esc_attr( $uuid ); ?>/32"
alt="<?php echo $name; ?>">
<span class="mm-bot-title"><?php echo $name; ?></span>
<button id="mm-bot-close" aria-label="Schließen">&times;</button>
</div>
<!-- Nachrichten -->
<div id="mm-bot-content" role="log" aria-live="polite">
<div class="mm-msg bot">
<?php echo nl2br( wp_kses_post( $welcome ) ); ?>
</div>
<?php if ( ! empty( $quick ) ) : ?>
<div class="mm-bot-quick">
<?php foreach ( $quick as $btn ) : ?>
<button class="mm-quick-btn"
data-q="<?php echo esc_attr( $btn['q'] ); ?>"
type="button">
<?php echo esc_html( $btn['label'] ); ?>
</button>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- Eingabe -->
<div class="mm-bot-input-area">
<input type="text"
id="mm-bot-field"
placeholder="Deine Frage eingeben…"
autocomplete="off"
maxlength="300"
aria-label="Nachricht eingeben">
<button id="mm-bot-send" type="button" aria-label="Senden">➤</button>
</div>
</div>
<!-- Launcher-Button -->
<button id="mm-bot-launcher" type="button" aria-label="Assistenten öffnen" title="<?php echo $name; ?>">
<img src="https://mc-heads.net/avatar/<?php echo esc_attr( $uuid ); ?>/60"
alt="<?php echo $name; ?>">
</button>
</div>
<script>
(function($){
'use strict';
var $chat = $('#mm-bot-chat');
var $field = $('#mm-bot-field');
var $content = $('#mm-bot-content');
var nonce = <?php echo wp_json_encode( $nonce ); ?>;
var ajaxUrl = <?php echo wp_json_encode( $ajax ); ?>;
var isLoading = false;
// ── Öffnen / Schließen ────────────────────────────────────
$('#mm-bot-launcher').on('click', function(){
$chat.fadeToggle(200);
if ($chat.is(':visible')) {
$field.trigger('focus');
scrollBottom();
}
});
$('#mm-bot-close').on('click', function(){
$chat.fadeOut(200);
});
// ── Quick-Buttons ─────────────────────────────────────────
$(document).on('click', '.mm-quick-btn', function(){
sendMessage( $(this).data('q') );
});
// ── Senden ────────────────────────────────────────────────
$('#mm-bot-send').on('click', function(){ sendMessage(); });
$field.on('keydown', function(e){
if (e.which === 13 && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
function sendMessage(forceText) {
if (isLoading) return;
var val = forceText !== undefined
? String(forceText).trim()
: $field.val().trim();
if (!val) return;
// Nutzernachricht anzeigen
appendMsg('user', $('<span>').text(val).html());
if (forceText === undefined) $field.val('');
// Loader
isLoading = true;
var $loader = $('<div class="mm-msg bot mm-loading" aria-label="Lädt">···</div>');
$content.append($loader);
scrollBottom();
$.ajax({
url: ajaxUrl,
method: 'POST',
dataType: 'json',
data: {
action: 'mm_assistant_query', // ← Neuer Action-Name
q: val,
nonce: nonce
},
success: function(res){
$loader.remove();
isLoading = false;
var text;
if (res && res.success && res.data && res.data.reply) {
// Neues Format: {reply: '...', parts: [...]}
text = res.data.reply;
} else if (res && res.success && typeof res.data === 'string') {
// Altes Format: direkter String (Fallback)
text = res.data;
} else {
text = '⚠️ Keine Antwort erhalten.';
}
appendMsg('bot', text);
},
error: function(){
$loader.remove();
isLoading = false;
appendMsg('bot', '⚠️ Verbindungsfehler. Bitte versuche es erneut.');
}
});
}
function appendMsg(type, html) {
var $msg = $('<div class="mm-msg ' + type + '">').html(html);
$content.append($msg);
scrollBottom();
}
function scrollBottom() {
$content.scrollTop($content[0].scrollHeight);
}
})(jQuery);
</script>
<style>
/* ── Wrapper ── */
#mm-bot-root {
position: fixed;
bottom: 30px;
right: 30px;
z-index: 999999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
/* ── Launcher ── */
#mm-bot-launcher {
background: #0099ff;
border: none;
border-radius: 50%;
width: 62px;
height: 62px;
cursor: pointer;
box-shadow: 0 4px 16px rgba(0,153,255,.45);
transition: transform .25s, box-shadow .25s;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
#mm-bot-launcher:hover {
transform: scale(1.1);
box-shadow: 0 6px 22px rgba(0,153,255,.6);
}
#mm-bot-launcher img {
width: 44px;
height: 44px;
border-radius: 4px;
}
/* ── Chat-Fenster ── */
#mm-bot-chat {
position: absolute;
bottom: 78px;
right: 0;
width: 350px;
background: #1e2124;
border-radius: 16px;
box-shadow: 0 16px 48px rgba(0,0,0,.55);
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid #2a2d31;
}
/* ── Header ── */
.mm-bot-header {
background: #2f3136;
color: #fff;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 10px;
border-bottom: 1px solid #1a1a1a;
}
.mm-bot-status {
width: 10px;
height: 10px;
background: #43b581;
border-radius: 50%;
box-shadow: 0 0 8px #43b581;
flex-shrink: 0;
}
.mm-bot-avatar-small {
width: 28px;
height: 28px;
border-radius: 4px;
}
.mm-bot-title {
font-weight: 700;
font-size: 15px;
flex: 1;
}
#mm-bot-close {
background: none;
border: none;
color: #888;
font-size: 22px;
cursor: pointer;
line-height: 1;
padding: 0 2px;
transition: color .2s;
}
#mm-bot-close:hover { color: #fff; }
/* ── Nachrichten ── */
#mm-bot-content {
height: 340px;
padding: 14px 14px 6px;
overflow-y: auto;
background: #36393f;
display: flex;
flex-direction: column;
gap: 10px;
scroll-behavior: smooth;
}
.mm-msg {
padding: 10px 14px;
border-radius: 14px;
font-size: 13.5px;
line-height: 1.55;
max-width: 90%;
word-break: break-word;
}
.mm-msg.bot {
background: #40444b;
color: #dcddde;
align-self: flex-start;
border-bottom-left-radius: 4px;
}
.mm-msg.user {
background: #0099ff;
color: #fff;
align-self: flex-end;
border-bottom-right-radius: 4px;
}
.mm-msg a {
color: #5bc0eb;
text-decoration: none;
font-weight: 600;
}
.mm-msg a:hover { text-decoration: underline; }
.mm-msg code {
background: #1a1a1a;
color: #ffa500;
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: 12.5px;
}
.mm-msg hr {
border: 0;
border-top: 1px solid rgba(255,255,255,.12);
margin: 8px 0;
}
.mm-msg img {
max-width: 100%;
border-radius: 6px;
margin-top: 6px;
display: block;
}
.mm-msg small { opacity: .75; font-size: 12px; }
.mm-msg b { color: #fff; }
.mm-msg.bot b { color: #e3e4e6; }
/* TinyMCE Regelwerk-Inhalt im Chat */
.mm-msg p { margin: 4px 0; }
.mm-msg ul, .mm-msg ol {
margin: 4px 0 4px 16px;
padding: 0;
}
.mm-msg li { margin-bottom: 2px; list-style: disc; }
.mm-msg ol li { list-style: decimal; }
.mm-msg strong, .mm-msg b { font-weight: 700; }
.mm-msg em { font-style: italic; }
.mm-msg h1, .mm-msg h2, .mm-msg h3,
.mm-msg h4, .mm-msg h5, .mm-msg h6 {
margin: 6px 0 4px;
font-size: 14px;
color: #e3e4e6;
}
.mm-loading {
opacity: .55;
font-size: 22px;
letter-spacing: 5px;
padding: 6px 14px;
}
/* ── Quick-Buttons ── */
.mm-bot-quick {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 2px;
}
.mm-quick-btn {
background: #2f3136;
color: #b9bbbe;
border: 1px solid #3f4147;
border-radius: 20px;
padding: 5px 12px;
font-size: 12px;
cursor: pointer;
transition: background .2s, color .2s, border-color .2s;
white-space: nowrap;
}
.mm-quick-btn:hover {
background: #0099ff;
color: #fff;
border-color: #0099ff;
}
/* ── Eingabe ── */
.mm-bot-input-area {
padding: 10px 12px;
background: #2f3136;
display: flex;
gap: 8px;
border-top: 1px solid #1a1a1a;
}
#mm-bot-field {
flex: 1;
background: #40444b;
border: 1px solid #1a1a1a;
border-radius: 8px;
color: #fff;
padding: 9px 12px;
outline: none;
font-size: 13.5px;
transition: border-color .2s;
}
#mm-bot-field::placeholder { color: #72767d; }
#mm-bot-field:focus { border-color: #0099ff; }
#mm-bot-send {
background: #0099ff;
border: none;
color: #fff;
border-radius: 8px;
padding: 0 16px;
cursor: pointer;
font-size: 16px;
transition: background .2s;
flex-shrink: 0;
}
#mm-bot-send:hover { background: #00b0f4; }
/* ── Responsive ── */
@media (max-width: 420px) {
#mm-bot-root { bottom: 16px; right: 12px; }
#mm-bot-chat { width: calc(100vw - 24px); right: -12px; }
}
</style>
<?php
}

View File

@@ -23,9 +23,22 @@ class MM_Import_Export_Control extends WP_Customize_Control {
<div class="mm-import-export-wrapper"> <div class="mm-import-export-wrapper">
<p class="description" style="margin-bottom:16px;"> <p class="description" style="margin-bottom:16px;">
<strong><?php _e('Hinweis:', 'minecraft-modern-theme'); ?></strong> <strong><?php _e('Hinweis:', 'minecraft-modern-theme'); ?></strong>
<?php _e('Hier kannst du alle Theme-Einstellungen sichern und wiederherstellen.', 'minecraft-modern-theme'); ?> <?php _e('Hier kannst du alle Theme-Einstellungen und Inhalte sichern und wiederherstellen.', 'minecraft-modern-theme'); ?>
</p> </p>
<div style="background:#e7f3ff;border:1px solid #b3d9ff;border-radius:4px;padding:12px;margin-bottom:16px;font-size:12px;line-height:1.5;">
<strong>📦 Was wird gesichert:</strong><br>
✓ Customizer-Einstellungen (Farben, Social Links, Menü-Design, etc.)<br>
✓ Livestream API Keys (YouTube, Twitch)<br>
✓ Homepage-Seite (Titel, Inhalt, Highlight-Bild)<br>
✓ Navigation Menüs inkl. aller Items & Struktur<br>
✓ Widget-Konfigurationen<br>
✓ Team-Mitglieder (mit UUID, Avatar, Banner)<br>
✓ FAQ-Einträge & Kategorien<br>
✓ Custom CSS<br>
✓ Announcement-Bar Einstellungen
</div>
<a href="<?php echo esc_url($export_url); ?>" class="button button-primary" style="display:inline-flex;align-items:center;gap:6px;margin-bottom:20px;"> <a href="<?php echo esc_url($export_url); ?>" class="button button-primary" style="display:inline-flex;align-items:center;gap:6px;margin-bottom:20px;">
<span class="dashicons dashicons-download"></span> <span class="dashicons dashicons-download"></span>
<?php _e('Einstellungen exportieren', 'minecraft-modern-theme'); ?> <?php _e('Einstellungen exportieren', 'minecraft-modern-theme'); ?>
@@ -87,6 +100,33 @@ endif;
// Customizer Register // Customizer Register
// ========================================================================= // =========================================================================
function minecraft_modern_customize_register( $wp_customize ) { function minecraft_modern_customize_register( $wp_customize ) {
// =========================================================================
// 9. Virtueller Assistent
// =========================================================================
$wp_customize->add_section( 'assistant_settings', array(
'title' => __('Virtueller Assistent', 'minecraft-modern-theme'),
'priority' => 80,
'description' => __('Steuert den virtuellen Assistenten im Frontend. Avatar basiert auf Minecraft-UUID.', 'minecraft-modern-theme'),
) );
$wp_customize->add_setting( 'assistant_enabled', array(
'default' => false,
'sanitize_callback' => 'wp_validate_boolean',
) );
$wp_customize->add_control( 'assistant_enabled', array(
'label' => __('Virtuellen Assistenten aktivieren', 'minecraft-modern-theme'),
'section' => 'assistant_settings',
'type' => 'checkbox',
) );
$wp_customize->add_setting( 'assistant_minecraft_uuid', array(
'default' => '',
'sanitize_callback' => 'sanitize_text_field',
) );
$wp_customize->add_control( 'assistant_minecraft_uuid', array(
'label' => __('Minecraft UUID für Avatar', 'minecraft-modern-theme'),
'section' => 'assistant_settings',
'type' => 'text',
'description' => __('Gib die Minecraft-UUID für den Avatar des Assistenten ein.', 'minecraft-modern-theme'),
) );
// ========================================================================= // =========================================================================
// 1. HEADER SLIDER // 1. HEADER SLIDER
@@ -235,18 +275,18 @@ function minecraft_modern_customize_register( $wp_customize ) {
// ========================================================================= // =========================================================================
$wp_customize->add_section( 'social_links', array( 'title' => __('Social Media Links', 'minecraft-modern-theme'), 'priority' => 40 ) ); $wp_customize->add_section( 'social_links', array( 'title' => __('Social Media Links', 'minecraft-modern-theme'), 'priority' => 40 ) );
$social_platforms = array( $social_platforms = array(
'discord' => 'Discord', 'youtube' => 'YouTube', 'twitter' => 'Twitter (X)', 'bluesky' => 'BlueSky', 'discord' => 'Discord', 'youtube' => 'YouTube', 'twitter' => 'Twitter (X)',
'facebook' => 'Facebook', 'instagram' => 'Instagram', 'tiktok' => 'TikTok', 'facebook' => 'Facebook', 'instagram' => 'Instagram', 'tiktok' => 'TikTok',
'twitch' => 'Twitch', 'steam' => 'Steam', 'github' => 'GitHub', 'twitch' => 'Twitch', 'steam' => 'Steam', 'github' => 'GitHub',
'linkedin' => 'LinkedIn', 'pinterest' => 'Pinterest', 'reddit' => 'Reddit', 'linkedin' => 'LinkedIn', 'pinterest' => 'Pinterest', 'reddit' => 'Reddit',
'teamspeak' => 'Teamspeak', 'spotify' => 'Spotify', 'mastodon' => 'Mastodon', 'threads' => 'Threads', 'kickstarter' => 'Kickstarter',
'teamspeak' => 'Teamspeak', 'spotify' => 'Spotify', 'stoat' => 'Stoat',
); );
foreach ( $social_platforms as $key => $label ) { foreach ( $social_platforms as $key => $label ) {
$wp_customize->add_setting( 'social_' . $key, array( 'sanitize_callback' => 'esc_url_raw' ) ); $wp_customize->add_setting( 'social_' . $key, array( 'sanitize_callback' => 'esc_url_raw' ) );
$wp_customize->add_control( 'social_' . $key, array( 'label' => $label . ' URL', 'section' => 'social_links', 'type' => 'url' ) ); $wp_customize->add_control( 'social_' . $key, array( 'label' => $label . ' URL', 'section' => 'social_links', 'type' => 'url' ) );
} }
// ========================================================================= // =========================================================================
// 6. FOOTER // 6. FOOTER
// ========================================================================= // =========================================================================
@@ -311,6 +351,60 @@ function minecraft_modern_customize_register( $wp_customize ) {
) ); ) );
// =========================================================================
// 8.5. VIDEO / LIVESTREAM EINSTELLUNGEN
// =========================================================================
$wp_customize->add_section( 'video_livestream_settings', array(
'title' => __('Video & Livestream', 'minecraft-modern-theme'),
'description' => __('Einstellungen für YouTube Livestream-Erkennung. Hauptmethode: Livestream-Posts unter "Livestreams" erstellen. Optional: Zusätzlichen Hauptkanal hier eintragen.', 'minecraft-modern-theme'),
'priority' => 75,
) );
$wp_customize->add_setting( 'youtube_api_key', array(
'default' => '',
'sanitize_callback' => 'sanitize_text_field',
'transport' => 'refresh',
) );
$wp_customize->add_control( 'youtube_api_key', array(
'label' => __('YouTube API Key', 'minecraft-modern-theme'),
'description' => __('Erforderlich für automatische YouTube Live-Erkennung. <a href="https://console.cloud.google.com/" target="_blank">Hier API Key erstellen</a>', 'minecraft-modern-theme'),
'section' => 'video_livestream_settings',
'type' => 'text',
'input_attrs' => array(
'placeholder' => 'AIzaSyD...',
),
) );
$wp_customize->add_setting( 'twitch_client_id', array(
'default' => '',
'sanitize_callback' => 'sanitize_text_field',
'transport' => 'refresh',
) );
$wp_customize->add_control( 'twitch_client_id', array(
'label' => __('Twitch Client ID', 'minecraft-modern-theme'),
'description' => __('Erforderlich für Twitch Live-Erkennung. <a href="https://dev.twitch.tv/console/apps" target="_blank">Hier App erstellen</a>', 'minecraft-modern-theme'),
'section' => 'video_livestream_settings',
'type' => 'text',
'input_attrs' => array(
'placeholder' => 'xxxxxxxxxxxxxx',
),
) );
$wp_customize->add_setting( 'twitch_client_secret', array(
'default' => '',
'sanitize_callback' => 'sanitize_text_field',
'transport' => 'refresh',
) );
$wp_customize->add_control( 'twitch_client_secret', array(
'label' => __('Twitch Client Secret', 'minecraft-modern-theme'),
'description' => __('Nur für Live-Prüfung. Wird serverseitig genutzt.', 'minecraft-modern-theme'),
'section' => 'video_livestream_settings',
'type' => 'text',
'input_attrs' => array(
'placeholder' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
),
) );
// ========================================================================= // =========================================================================
// 9. EXPORT / IMPORT // 9. EXPORT / IMPORT
// ========================================================================= // =========================================================================

View File

@@ -1,7 +1,8 @@
<?php <?php
/** /**
* Minecraft Modern Theme - Updater & Dashboard Status * Minecraft Modern Theme - Updater & Dashboard Status
* * Diese Datei prüft auf neue Versionen via Gitea API und zeigt den Status im Dashboard an. *
* Diese Datei prüft auf neue Versionen via Gitea API und zeigt den Status im Dashboard an.
* Aus Sicherheitsgründen ist das automatische Update deaktiviert, um Datenverlust zu vermeiden. * Aus Sicherheitsgründen ist das automatische Update deaktiviert, um Datenverlust zu vermeiden.
*/ */
@@ -11,29 +12,29 @@ if ( ! defined( 'ABSPATH' ) ) {
class Minecraft_Modern_Theme_Manager { class Minecraft_Modern_Theme_Manager {
private $theme_slug = 'Minecraft-Modern-Theme'; // BUG-FIX: Hardcoded 'Minecraft-Modern-Theme' schlug auf Linux-Servern
private $repo = 'M_Viper/Minecraft-Modern-Theme'; // (case-sensitive Dateisystem) fehl, wenn das Verzeichnis kleingeschrieben ist.
// get_template() liefert immer den echten Verzeichnisnamen.
private $theme_slug;
private $repo = 'M_Viper/Minecraft-Modern-Theme';
private $transient_key = 'mm_theme_update_check'; private $transient_key = 'mm_theme_update_check';
public function __construct() { public function __construct() {
// Update-Prüfung und Benachrichtigung $this->theme_slug = get_template();
add_action( 'admin_notices', [ $this, 'display_update_notice' ] );
// Dashboard Widget add_action( 'admin_notices', [ $this, 'display_update_notice' ] );
add_action( 'wp_dashboard_setup', [ $this, 'add_dashboard_widget' ] ); add_action( 'wp_dashboard_setup', [ $this, 'add_dashboard_widget' ] );
add_action( 'admin_init', [ $this, 'handle_refresh_request' ] );
// Refresh Logik
add_action( 'admin_init', [ $this, 'handle_refresh_request' ] );
} }
/** /**
* Holt die API-Daten von Gitea * Holt die API-Daten von Gitea (mit Transient-Cache).
*/ */
private function get_latest_release() { private function get_latest_release() {
$update_data = get_transient( $this->transient_key ); $update_data = get_transient( $this->transient_key );
if ( false === $update_data ) { if ( false === $update_data ) {
$api_url = "https://git.viper.ipv64.net/api/v1/repos/{$this->repo}/releases/latest"; $api_url = "https://git.viper.ipv64.net/api/v1/repos/{$this->repo}/releases/latest";
$response = wp_remote_get( $api_url, [ 'timeout' => 10 ] ); $response = wp_remote_get( $api_url, [ 'timeout' => 10 ] );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) { if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
@@ -41,12 +42,12 @@ class Minecraft_Modern_Theme_Manager {
return [ 'error' => true ]; return [ 'error' => true ];
} }
$data = json_decode( wp_remote_retrieve_body( $response ) ); $data = json_decode( wp_remote_retrieve_body( $response ) );
$new_version = ltrim( $data->tag_name, 'vV' ); $new_version = ltrim( $data->tag_name, 'vV' );
$update_data = [ $update_data = [
'version' => $new_version, 'version' => $new_version,
'url' => "https://git.viper.ipv64.net/{$this->repo}/releases" 'url' => "https://git.viper.ipv64.net/{$this->repo}/releases",
]; ];
set_transient( $this->transient_key, $update_data, 12 * HOUR_IN_SECONDS ); set_transient( $this->transient_key, $update_data, 12 * HOUR_IN_SECONDS );
@@ -56,7 +57,7 @@ class Minecraft_Modern_Theme_Manager {
} }
/** /**
* Zeigt die gelbe Info-Box oben im Admin-Bereich * Zeigt die gelbe Info-Box oben im Admin-Bereich an, wenn ein Update verfügbar ist.
*/ */
public function display_update_notice() { public function display_update_notice() {
if ( ! current_user_can( 'update_themes' ) ) return; if ( ! current_user_can( 'update_themes' ) ) return;
@@ -65,14 +66,18 @@ class Minecraft_Modern_Theme_Manager {
if ( isset( $latest['error'] ) ) return; if ( isset( $latest['error'] ) ) return;
$current_version = wp_get_theme( $this->theme_slug )->get( 'Version' ); $current_version = wp_get_theme( $this->theme_slug )->get( 'Version' );
if ( ! $current_version ) return;
if ( version_compare( $current_version, $latest['version'], '<' ) ) { if ( version_compare( $current_version, $latest['version'], '<' ) ) {
?> ?>
<div class="notice notice-warning is-dismissible"> <div class="notice notice-warning is-dismissible">
<p> <p>
<span class="dashicons dashicons-update" style="color: #dba617; margin-right: 5px;"></span> <span class="dashicons dashicons-update" style="color: #dba617; margin-right: 5px;"></span>
<strong>Minecraft Modern Update verfügbar:</strong> Version <strong><?php echo esc_html( $latest['version'] ); ?></strong> ist bereit zum Download. <strong>Minecraft Modern Update verfügbar:</strong>
<a href="<?php echo esc_url( $latest['url'] ); ?>" target="_blank" style="margin-left: 10px; font-weight: bold;">Zum Download →</a> Version <strong><?php echo esc_html( $latest['version'] ); ?></strong> ist bereit zum Download.
<a href="<?php echo esc_url( $latest['url'] ); ?>" target="_blank" style="margin-left: 10px; font-weight: bold;">
Zum Download &rarr;
</a>
</p> </p>
</div> </div>
<?php <?php
@@ -80,7 +85,7 @@ class Minecraft_Modern_Theme_Manager {
} }
/** /**
* Fügt das Widget zum Dashboard hinzu * Fügt das Widget zum WordPress-Dashboard hinzu.
*/ */
public function add_dashboard_widget() { public function add_dashboard_widget() {
wp_add_dashboard_widget( wp_add_dashboard_widget(
@@ -91,19 +96,19 @@ class Minecraft_Modern_Theme_Manager {
} }
/** /**
* HTML-Inhalt des Widgets * HTML-Inhalt des Dashboard-Widgets.
*/ */
public function render_widget_content() { public function render_widget_content() {
$current_version = wp_get_theme( $this->theme_slug )->get( 'Version' ); $current_version = wp_get_theme( $this->theme_slug )->get( 'Version' );
$latest = $this->get_latest_release(); $latest = $this->get_latest_release();
echo '<div class="mm-status-widget">'; echo '<div class="mm-status-widget">';
echo '<p><span class="dashicons dashicons-admin-appearance"></span> <strong>Installiert:</strong> ' . esc_html( $current_version ) . '</p>'; echo '<p><span class="dashicons dashicons-admin-appearance"></span> <strong>Installiert:</strong> ' . esc_html( $current_version ?: '' ) . '</p>';
if ( isset( $latest['version'] ) ) { if ( isset( $latest['version'] ) ) {
echo '<p><span class="dashicons dashicons-cloud"></span> <strong>Aktuellste:</strong> ' . esc_html( $latest['version'] ) . '</p>'; echo '<p><span class="dashicons dashicons-cloud"></span> <strong>Aktuellste:</strong> ' . esc_html( $latest['version'] ) . '</p>';
if ( version_compare( $current_version, $latest['version'], '<' ) ) { if ( $current_version && version_compare( $current_version, $latest['version'], '<' ) ) {
echo '<div style="background: #fff8e5; border-left: 4px solid #ffb900; padding: 12px; margin: 15px 0;">'; echo '<div style="background: #fff8e5; border-left: 4px solid #ffb900; padding: 12px; margin: 15px 0;">';
echo '<p style="margin: 0 0 10px; color: #856404;"><strong>Update verfügbar!</strong></p>'; echo '<p style="margin: 0 0 10px; color: #856404;"><strong>Update verfügbar!</strong></p>';
echo '<a href="' . esc_url( $latest['url'] ) . '" class="button button-primary" target="_blank">Download ZIP von Gitea</a>'; echo '<a href="' . esc_url( $latest['url'] ) . '" class="button button-primary" target="_blank">Download ZIP von Gitea</a>';
@@ -122,7 +127,7 @@ class Minecraft_Modern_Theme_Manager {
} }
/** /**
* Verarbeitet den Klick auf "Jetzt prüfen" * Verarbeitet den Klick auf "Update-Cache jetzt leeren".
*/ */
public function handle_refresh_request() { public function handle_refresh_request() {
if ( isset( $_GET['mm_refresh_check'] ) && check_admin_referer( 'mm_refresh_action' ) ) { if ( isset( $_GET['mm_refresh_check'] ) && check_admin_referer( 'mm_refresh_action' ) ) {
@@ -133,5 +138,4 @@ class Minecraft_Modern_Theme_Manager {
} }
} }
// Initialisierung
new Minecraft_Modern_Theme_Manager(); new Minecraft_Modern_Theme_Manager();