From acb12a15bf7fc010c08341be81c996d3ed3dfc2b Mon Sep 17 00:00:00 2001
From: Git Manager GUI
Date: Mon, 11 May 2026 09:52:14 +0200
Subject: [PATCH] Upload via Git Manager GUI
---
Minecraft-Modern-Theme/functions.php | 7832 +++++++++++++-------------
1 file changed, 3938 insertions(+), 3894 deletions(-)
diff --git a/Minecraft-Modern-Theme/functions.php b/Minecraft-Modern-Theme/functions.php
index 2cc2936..bbbd3f8 100644
--- a/Minecraft-Modern-Theme/functions.php
+++ b/Minecraft-Modern-Theme/functions.php
@@ -1,3895 +1,3939 @@
- 9999,
- 'width' => 9999,
- 'flex-height' => true,
- 'flex-width' => true,
- 'header_text' => array( 'site-title', 'site-description' ),
- ) );
-
- add_theme_support( 'custom-background' );
-
- register_nav_menus( array(
- 'primary' => __( 'Hauptmenü', 'minecraft-modern-theme' ),
- 'footer' => __( 'Footer-Menü', 'minecraft-modern-theme' ),
- ) );
-
- // FIX: 'script' und 'style' ergänzt für sauberes HTML5-Output-Format
- add_theme_support( 'html5', array(
- 'search-form', 'comment-form', 'comment-list',
- 'gallery', 'caption', 'script', 'style',
- ) );
-}
-add_action( 'after_setup_theme', 'minecraft_modern_setup' );
-
-
-// === Styles & Scripts laden ===
-function minecraft_modern_scripts() {
- // Haupt-Stylesheet
- wp_enqueue_style( 'minecraft-modern-style', get_stylesheet_uri(), array(), filemtime( get_stylesheet_directory() . '/style.css' ) );
-
- // Swiper.js CSS
- wp_enqueue_style( 'swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.css' );
-
- // Header-Scroll + Suche Toggle
- wp_enqueue_script(
- 'minecraft-modern-header-script',
- get_template_directory_uri() . '/js/header-scroll.js',
- array(),
- filemtime( get_template_directory() . '/js/header-scroll.js' ),
- true
- );
-
- // Navigation (Dropdown + Außenklick-Schließen)
- wp_enqueue_script(
- 'minecraft-navigation',
- get_template_directory_uri() . '/js/navigation.js',
- array(),
- '1.1',
- true
- );
-
- // Swiper.js
- wp_enqueue_script(
- 'swiper-js',
- 'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.js',
- array(),
- '8.0.0',
- true
- );
-
- // Slider-Init
- wp_enqueue_script(
- 'minecraft-modern-slider-script',
- get_template_directory_uri() . '/js/slider-init.js',
- array( 'swiper-js' ),
- '1.1',
- true
- );
-
- // Theme Toggle
- wp_enqueue_script(
- 'theme-toggle-script',
- get_template_directory_uri() . '/js/theme-toggle.js',
- array(),
- '1.1',
- true
- );
-
- // BUG-FIX: post_type_exists('faq') prüft nur ob der CPT registriert ist, nicht ob wir
- // auf einer FAQ-Seite sind – das Script wurde damit seitenübergreifend geladen.
- // Jetzt: nur auf FAQ-Archiv-, Einzel- und Seiten mit FAQ-Template laden.
- if ( post_type_exists( 'faq' ) && ( is_post_type_archive( 'faq' ) || is_singular( 'faq' ) || is_page( 'FAQ' ) || is_page( 'faq' ) ) ) {
- wp_enqueue_script(
- 'faq-accordion-script',
- get_template_directory_uri() . '/js/faq-accordion.js',
- array(),
- '1.0',
- true
- );
- }
-
- // FIX: 'loop' ergänzt – war in slider-init.js als sliderSettings.loop referenziert, aber nie übergeben
- wp_localize_script(
- 'minecraft-modern-slider-script',
- 'sliderSettings',
- array(
- 'hideArrows' => get_theme_mod( 'slider_hide_arrows', false ) ? '1' : '0',
- 'hidePagination' => get_theme_mod( 'slider_hide_pagination', false ) ? '1' : '0',
- 'effect' => get_theme_mod( 'slider_effect', 'fade' ),
- 'direction' => get_theme_mod( 'slider_direction', 'horizontal' ),
- 'defaultMode' => get_theme_mod( 'default_theme_mode', 'dark' ),
- 'loop' => get_theme_mod( 'slider_loop', true ) ? '1' : '0',
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
- )
- );
-
- wp_localize_script(
- 'minecraft-modern-header-script',
- 'headerSettings',
- array(
- 'isCustomizer' => is_customize_preview(),
- )
- );
-}
-add_action( 'wp_enqueue_scripts', 'minecraft_modern_scripts' );
-
-
-// FIX: Scroll-to-Top via Customizer ein-/ausblendbar
-function minecraft_modern_scroll_to_top_css() {
- if ( ! get_theme_mod( 'show_scroll_to_top', true ) ) {
- wp_add_inline_style( 'minecraft-modern-style', '#scroll-to-top { display: none !important; }' );
- }
-}
-add_action( 'wp_enqueue_scripts', 'minecraft_modern_scroll_to_top_css', 25 );
-
-
-// === Customizer-Datei laden ===
-require get_template_directory() . '/inc/customizer.php';
-
-
-
-
-
-// =========================================================================
-// INTELLIGENTER SUPPORT-ASSISTENT
-// =========================================================================
-// Ausgelagert in /inc/assistant-widget.php
-require get_template_directory() . '/inc/assistant-widget.php';
-
-
-// === Theme-Updater-Datei laden ===
-require get_template_directory() . '/inc/theme-updater.php';
-
-
-// === Footer-Widgets registrieren ===
-function minecraft_modern_footer_widgets() {
- register_sidebar( array(
- 'name' => __( 'Footer Links', 'minecraft-modern-theme' ),
- 'id' => 'footer-left',
- 'description' => __( 'Widget-Bereich links im Footer.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
- for ( $i = 1; $i <= 3; $i++ ) {
- register_sidebar( array(
- 'name' => sprintf( __( 'Footer Spalte %d', 'minecraft-modern-theme' ), $i ),
- 'id' => 'footer-' . $i,
- 'description' => sprintf( __( 'Widget für die %d. Spalte im Footer.', 'minecraft-modern-theme' ), $i ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
- }
- register_sidebar( array(
- 'name' => __( 'Footer Rechts', 'minecraft-modern-theme' ),
- 'id' => 'footer-right',
- 'description' => __( 'Widget-Bereich rechts im Footer.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
-}
-add_action( 'widgets_init', 'minecraft_modern_footer_widgets' );
-
-
-// === Homepage Sidebar registrieren ===
-function minecraft_modern_homepage_sidebar() {
- register_sidebar( array(
- 'name' => __( 'Startseiten Sidebar - Oben', 'minecraft-modern-theme' ),
- 'id' => 'homepage-sidebar-top',
- 'description' => __( 'Widget-Bereich oben in der Sidebar (z.B. für wichtige Infos).', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
- register_sidebar( array(
- 'name' => __( 'Startseiten Sidebar - Mitte 1', 'minecraft-modern-theme' ),
- 'id' => 'homepage-sidebar-middle-1',
- 'description' => __( 'Widget-Bereich in der Mitte der Sidebar.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
- register_sidebar( array(
- 'name' => __( 'Startseiten Sidebar - Mitte 2', 'minecraft-modern-theme' ),
- 'id' => 'homepage-sidebar-middle-2',
- 'description' => __( 'Zweiter Widget-Bereich in der Mitte der Sidebar.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
- register_sidebar( array(
- 'name' => __( 'Startseiten Sidebar - Unten', 'minecraft-modern-theme' ),
- 'id' => 'homepage-sidebar-bottom',
- 'description' => __( 'Widget-Bereich unten in der Sidebar.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
- register_sidebar( array(
- 'name' => __( 'Startseiten Sidebar - Extra', 'minecraft-modern-theme' ),
- 'id' => 'homepage-sidebar-extra',
- 'description' => __( 'Zusätzlicher Widget-Bereich für spezielle Inhalte.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'after_widget' => '
',
- 'before_title' => '',
- ) );
-}
-add_action( 'widgets_init', 'minecraft_modern_homepage_sidebar' );
-
-
-/**
- * Rendert alle Sidebar-Sections der Homepage.
- * Ersetzt den doppelten Sidebar-Code in front-page.php.
- */
-function minecraft_modern_render_sidebar_sections() {
- $sections = array(
- 'homepage-sidebar-top',
- 'homepage-sidebar-middle-1',
- 'homepage-sidebar-middle-2',
- 'homepage-sidebar-bottom',
- 'homepage-sidebar-extra',
- );
- $classes = array(
- 'homepage-sidebar-top' => 'sidebar-top',
- 'homepage-sidebar-middle-1' => 'sidebar-middle-1',
- 'homepage-sidebar-middle-2' => 'sidebar-middle-2',
- 'homepage-sidebar-bottom' => 'sidebar-bottom',
- 'homepage-sidebar-extra' => 'sidebar-extra',
- );
- foreach ( $sections as $sidebar_id ) {
- if ( is_active_sidebar( $sidebar_id ) ) {
- $class = isset( $classes[ $sidebar_id ] ) ? ' ' . $classes[ $sidebar_id ] : '';
- echo '';
- }
- }
-}
-
-
-// === FAQ Custom Post Type & Taxonomy ===
-function create_faq_post_type() {
- if ( get_theme_mod( 'faq_enabled', true ) ) {
- register_post_type( 'faq', array(
- 'labels' => array(
- 'name' => __( 'FAQs', 'minecraft-modern-theme' ),
- 'singular_name' => __( 'FAQ', 'minecraft-modern-theme' ),
- 'add_new' => __( 'Neue FAQ hinzufügen', 'minecraft-modern-theme' ),
- 'add_new_item' => __( 'Neue FAQ hinzufügen', 'minecraft-modern-theme' ),
- 'edit_item' => __( 'FAQ bearbeiten', 'minecraft-modern-theme' ),
- 'new_item' => __( 'Neue FAQ', 'minecraft-modern-theme' ),
- 'view_item' => __( 'FAQ ansehen', 'minecraft-modern-theme' ),
- 'search_items' => __( 'FAQs durchsuchen', 'minecraft-modern-theme' ),
- 'not_found' => __( 'Keine FAQs gefunden', 'minecraft-modern-theme' ),
- 'not_found_in_trash' => __( 'Keine FAQs im Papierkorb gefunden', 'minecraft-modern-theme' ),
- 'all_items' => __( 'Alle FAQs', 'minecraft-modern-theme' ),
- ),
- 'public' => true,
- 'has_archive' => true,
- 'menu_icon' => 'dashicons-format-chat',
- 'supports' => array( 'title', 'editor', 'page-attributes' ),
- 'rewrite' => array( 'slug' => 'faq' ),
- 'show_in_rest' => true,
- ) );
- register_taxonomy( 'faq_category', 'faq', array(
- 'label' => __( 'FAQ Kategorien', 'minecraft-modern-theme' ),
- 'rewrite' => array( 'slug' => 'faq-kategorie' ),
- 'hierarchical' => true,
- 'show_in_rest' => true,
- ) );
- }
-}
-add_action( 'init', 'create_faq_post_type' );
-
-
-// =============================================================================
-// Automatische "Home" Seitenerstellung
-// =============================================================================
-
-function set_static_front_page_automatically() {
- if ( 'page' !== get_option( 'show_on_front' ) ) {
- // BUG-FIX: get_page_by_title() ist seit WP 6.2 deprecated. Ersetzt durch WP_Query.
- $home_query = new WP_Query( array(
- 'post_type' => 'page',
- 'title' => 'Home',
- 'post_status' => 'publish',
- 'posts_per_page' => 1,
- 'no_found_rows' => true,
- 'update_post_meta_cache' => false,
- 'update_post_term_cache' => false,
- ) );
- $home_page = $home_query->have_posts() ? $home_query->posts[0] : null;
- if ( ! $home_page ) {
- $home_page_id = wp_insert_post( array(
- 'post_title' => 'Home',
- 'post_content' => 'Diese Seite dient als statische Startseite.',
- 'post_status' => 'publish',
- 'post_type' => 'page',
- 'post_author' => 1,
- ) );
- } else {
- $home_page_id = $home_page->ID;
- }
- if ( $home_page_id ) {
- update_option( 'show_on_front', 'page' );
- update_option( 'page_on_front', $home_page_id );
- }
- }
-}
-add_action( 'after_switch_theme', 'set_static_front_page_automatically' );
-
-
-function add_home_body_class( $classes ) {
- if ( is_front_page() && ! get_theme_mod( 'show_home_title', true ) ) {
- $classes[] = 'home-title-hidden';
- }
- return $classes;
-}
-add_filter( 'body_class', 'add_home_body_class' );
-
-
-// =============================================================================
-// FAQ-Seitenerstellung
-// =============================================================================
-
-function create_faq_page_automatically() {
- if ( ! get_theme_mod( 'faq_enabled', true ) ) return;
-
- // BUG-FIX: get_page_by_title() ist seit WP 6.2 deprecated. Ersetzt durch WP_Query.
- $faq_query = new WP_Query( array(
- 'post_type' => 'page',
- 'title' => 'FAQ',
- 'post_status' => 'publish',
- 'posts_per_page' => 1,
- 'no_found_rows' => true,
- 'update_post_meta_cache' => false,
- 'update_post_term_cache' => false,
- ) );
- if ( $faq_query->have_posts() ) return;
-
- wp_insert_post( array(
- 'post_title' => 'FAQ',
- 'post_content' => 'Diese Seite zeigt alle FAQs an. Der Inhalt wird automatisch generiert.',
- 'post_status' => 'publish',
- 'post_type' => 'page',
- 'post_author' => 1,
- ) );
-}
-add_action( 'customize_save_after', 'create_faq_page_automatically' );
-
-
-function load_faq_page_template( $template ) {
- if ( get_theme_mod( 'faq_enabled', true ) && is_page() ) {
- global $post;
- if ( $post && $post->post_title === 'FAQ' ) {
- return get_template_directory() . '/archive-faq.php';
- }
- }
- return $template;
-}
-add_filter( 'template_include', 'load_faq_page_template' );
-
-
-// =========================================================================
-// CUSTOM LOGIN FUNCTIONS
-// =========================================================================
-
-function minecraft_modern_login_assets() {
- wp_enqueue_style( 'minecraft-modern-login-style', get_template_directory_uri() . '/css/login-style.css' );
- wp_enqueue_style( 'font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css' );
-
- // FIX: login-slider.js – erster Slide sofort sichtbar (fixiert)
- wp_enqueue_script( 'minecraft-avatar-slider-script', get_template_directory_uri() . '/js/login-slider.js', array( 'jquery' ), '1.1', true );
- wp_enqueue_script( 'minecraft-modern-login-script', get_template_directory_uri() . '/js/login-script.js', array( 'jquery' ), '1.0', true );
-
- wp_add_inline_script( 'minecraft-modern-login-script', "
- jQuery(document).ready(function($) {
- $('.forgetmenot, #nav').wrapAll('
');
- });
- " );
-
- $slider_speed = get_theme_mod( 'login_avatar_slider_speed', 4 );
- wp_localize_script( 'minecraft-avatar-slider-script', 'avatarSliderSettings', array(
- 'speed' => $slider_speed * 1000,
- ) );
-
- $login_bg_image = get_theme_mod( 'login_background_image' );
- if ( $login_bg_image ) {
- wp_add_inline_style( 'minecraft-modern-login-style', "body.login { background-image: url('" . esc_url( $login_bg_image ) . "') !important; }" );
- }
- $logo_url = get_theme_mod( 'login_logo' );
- if ( $logo_url ) {
- wp_add_inline_style( 'minecraft-modern-login-style', ".login h1 a { background-image: url('" . esc_url( $logo_url ) . "') !important; }" );
- }
-}
-add_action( 'login_enqueue_scripts', 'minecraft_modern_login_assets' );
-
-
-function add_minecraft_avatar_slider_to_login() {
- $avatar_html = '';
- $has_avatars = false;
-
- for ( $i = 1; $i <= 5; $i++ ) {
- $uuid = get_theme_mod( 'login_avatar_uuid_' . $i );
- if ( ! empty( $uuid ) ) {
- $has_avatars = true;
- $avatar_url = 'https://visage.surgeplay.com/full/' . $uuid . '.png';
- // FIX: Klasse wird hier gesetzt, JS überschreibt sie trotzdem (kompatibel)
- $active_class = ( $i === 1 ) ? 'avatar-slide avatar-slide-active' : 'avatar-slide';
- $avatar_html .= '
';
- }
- }
- $avatar_html .= '
';
-
- if ( $has_avatars ) {
- echo $avatar_html;
- }
-}
-add_action( 'login_form', 'add_minecraft_avatar_slider_to_login' );
-
-
-function add_post_login_links() {
- ?>
-
-
-
-
-
- '3.0',
- '_exported' => date( 'Y-m-d H:i:s' ),
- '_theme' => $theme_slug,
- );
-
- // 1. Alle Customizer-Einstellungen (Theme Mods)
- // Enthält: Farben, Slider, Hero, Social Links, Menü-Design, Login-Einstellungen, Sidebar-Einstellungen usw.
- $data['theme_mods'] = get_theme_mods();
-
- // 2. Announcement-Bar (gespeichert als wp_options, nicht als Theme Mod)
- $announcement_keys = array(
- 'mm_announcement_enabled', 'mm_announcement_text', 'mm_announcement_bg',
- 'mm_announcement_color', 'mm_announcement_font_size', 'mm_announcement_font_family',
- 'mm_announcement_position', 'mm_announcement_countdown_enabled',
- 'mm_announcement_countdown_label', 'mm_announcement_countdown_date',
- 'mm_announcement_countdown_expired_msg',
- );
- foreach ( $announcement_keys as $key ) {
- $data['announcement'][ $key ] = get_option( $key );
- }
-
- // 3. Widget-Konfigurationen aller Theme-Sidebars
- $theme_sidebars = array(
- 'single-post-sidebar',
- 'homepage-sidebar-top', 'homepage-sidebar-middle-1',
- 'homepage-sidebar-middle-2', 'homepage-sidebar-bottom',
- 'homepage-sidebar-extra', 'footer-1', 'footer-2', 'footer-3',
- );
- $all_sidebars = wp_get_sidebars_widgets();
- $widget_data = array();
- foreach ( $theme_sidebars as $sidebar_id ) {
- $widget_data[ $sidebar_id ] = array();
- if ( empty( $all_sidebars[ $sidebar_id ] ) ) continue;
- foreach ( $all_sidebars[ $sidebar_id ] as $widget_id ) {
- if ( ! preg_match( '/^(.+)-(\d+)$/', $widget_id, $m ) ) continue;
- $type = $m[1];
- $index = intval( $m[2] );
- $all = get_option( 'widget_' . $type, array() );
- $widget_data[ $sidebar_id ][] = array(
- 'type' => $type,
- 'index' => $index,
- 'settings' => isset( $all[ $index ] ) ? $all[ $index ] : array(),
- );
- }
- }
- $data['widgets'] = $widget_data;
-
- // 4. Team-Mitglieder (mit UUID, Banner, Thumbnail)
- $team_data = array();
- $team_query = new WP_Query( array(
- 'post_type' => 'team_member',
- 'posts_per_page' => -1,
- 'orderby' => 'menu_order',
- 'order' => 'ASC',
- ) );
- if ( $team_query->have_posts() ) {
- while ( $team_query->have_posts() ) {
- $team_query->the_post();
- $post_id = get_the_ID();
- $team_data[] = array(
- 'title' => get_the_title(),
- 'content' => get_the_content(),
- 'rank' => get_post_meta( $post_id, '_team_member_rank', true ),
- 'uuid' => get_post_meta( $post_id, '_team_member_uuid', true ),
- 'banner_id' => get_post_meta( $post_id, '_team_member_banner', true ),
- 'thumbnail_id' => get_post_thumbnail_id( $post_id ),
- 'menu_order' => get_post_field( 'menu_order', $post_id ),
- );
- }
- wp_reset_postdata();
- }
- $data['team'] = $team_data;
-
- // 5. FAQ-Einträge (theme-eigener Custom Post Type)
- $faq_data = array();
- $faq_query = new WP_Query( array(
- 'post_type' => 'faq',
- 'posts_per_page' => -1,
- 'orderby' => 'menu_order',
- 'order' => 'ASC',
- ) );
- if ( $faq_query->have_posts() ) {
- while ( $faq_query->have_posts() ) {
- $faq_query->the_post();
- $terms = wp_get_post_terms( get_the_ID(), 'faq_category', array( 'fields' => 'names' ) );
- $faq_data[] = array(
- 'title' => get_the_title(),
- 'content' => get_the_content(),
- 'menu_order' => get_post_field( 'menu_order', get_the_ID() ),
- 'categories' => is_wp_error( $terms ) ? array() : $terms,
- );
- }
- wp_reset_postdata();
- }
- $data['faqs'] = $faq_data;
-
- // 6. Custom CSS (Customizer → Zusätzliches CSS)
- $data['custom_css'] = wp_get_custom_css();
-
- // 7. Homepage-Seite (falls eingestellt)
- $homepage_id = get_option( 'page_on_front' );
- if ( $homepage_id ) {
- $homepage = get_post( $homepage_id );
- if ( $homepage ) {
- $data['homepage'] = array(
- 'title' => $homepage->post_title,
- 'content' => $homepage->post_content,
- 'excerpt' => $homepage->post_excerpt,
- 'featured_img' => get_post_thumbnail_id( $homepage_id ),
- );
- }
- }
-
- // 8. Navigation Menüs mit Items
- $nav_menus = array();
- $all_nav_menus = wp_get_nav_menus();
- foreach ( $all_nav_menus as $menu ) {
- $nav_menus[ $menu->slug ] = array(
- 'name' => $menu->name,
- 'description' => $menu->description,
- 'items' => array(),
- );
- $menu_items = wp_get_nav_menu_items( $menu->term_id );
- if ( $menu_items ) {
- foreach ( $menu_items as $item ) {
- $nav_menus[ $menu->slug ]['items'][] = array(
- 'title' => $item->title,
- 'url' => $item->url,
- 'description' => $item->description,
- 'type' => $item->type,
- 'type_label' => $item->type_label,
- 'object' => $item->object,
- 'object_id' => $item->object_id,
- 'parent' => $item->menu_item_parent,
- 'menu_order' => $item->menu_order,
- 'target' => get_post_meta( $item->ID, '_menu_item_target', true ),
- 'classes' => implode( ' ', (array) $item->classes ),
- 'xfn' => get_post_meta( $item->ID, '_menu_item_xfn', true ),
- );
- }
- }
- }
- $data['nav_menus'] = $nav_menus;
-
- // 9. Menü-Positionen (welches Menü ist welchem Theme-Location zugewiesen)
- $data['nav_menu_locations'] = get_theme_mod( 'nav_menu_locations', array() );
-
- $json = json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
- header( 'Content-Type: application/json; charset=utf-8' );
- header( 'Content-Disposition: attachment; filename=' . $theme_slug . '-theme-backup-' . date( 'Y-m-d' ) . '.json' );
- header( 'Pragma: no-cache' );
- header( 'Expires: 0' );
- echo $json;
- exit;
-}
-
-
-add_action( 'wp_ajax_import_theme_settings', 'handle_theme_settings_import' );
-
-function handle_theme_settings_import() {
- if ( ! current_user_can( 'edit_theme_options' ) ) {
- wp_send_json_error( __( 'Keine Berechtigung.', 'minecraft-modern-theme' ) );
- }
- check_ajax_referer( 'theme-import-nonce', 'nonce' );
-
- if ( empty( $_FILES['import_file']['tmp_name'] ) ) {
- wp_send_json_error( __( 'Keine Datei hochgeladen.', 'minecraft-modern-theme' ) );
- }
-
- $json_content = file_get_contents( $_FILES['import_file']['tmp_name'] );
- $data = json_decode( $json_content, true );
-
- if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $data ) ) {
- wp_send_json_error( __( 'Ungültige JSON-Datei.', 'minecraft-modern-theme' ) );
- }
-
- $imported = array();
- $version = isset( $data['_version'] ) ? $data['_version'] : '1.0';
-
- // -------------------------------------------------------------------------
- // 1. Customizer / Theme Mods
- // -------------------------------------------------------------------------
- $mods = array();
- if ( $version === '3.0' && isset( $data['theme_mods'] ) ) {
- $mods = $data['theme_mods'];
- } elseif ( $version === '2.0' && isset( $data['theme_mods'] ) ) {
- $mods = $data['theme_mods'];
- } else {
- // Altes v1-Format: alles auf oberster Ebene
- $skip = array( '_version', '_exported', '_theme', 'announcement', 'widgets', 'team', 'faqs', 'menus', 'wp_options', 'custom_css', 'team_data' );
- foreach ( $data as $k => $v ) {
- if ( ! in_array( $k, $skip ) ) $mods[ $k ] = $v;
- }
- }
- foreach ( $mods as $key => $value ) {
- set_theme_mod( $key, $value );
- }
- $imported[] = 'Customizer-Einstellungen';
-
- // -------------------------------------------------------------------------
- // 2. Announcement-Bar
- // -------------------------------------------------------------------------
- $announcement_keys = array(
- 'mm_announcement_enabled', 'mm_announcement_text', 'mm_announcement_bg',
- 'mm_announcement_color', 'mm_announcement_font_size', 'mm_announcement_font_family',
- 'mm_announcement_position', 'mm_announcement_countdown_enabled',
- 'mm_announcement_countdown_label', 'mm_announcement_countdown_date',
- 'mm_announcement_countdown_expired_msg',
- );
- $ann = isset( $data['announcement'] ) ? $data['announcement'] : $data;
- foreach ( $announcement_keys as $key ) {
- if ( array_key_exists( $key, $ann ) ) {
- update_option( $key, $ann[ $key ] );
- }
- }
- $imported[] = 'Ankündigung';
-
- // -------------------------------------------------------------------------
- // 3. Widgets
- // -------------------------------------------------------------------------
- if ( ! empty( $data['widgets'] ) ) {
- $current_sidebars = get_option( 'sidebars_widgets', array() );
- foreach ( $data['widgets'] as $sidebar_id => $widgets ) {
- $current_sidebars[ $sidebar_id ] = array();
- foreach ( $widgets as $widget ) {
- $type = sanitize_key( $widget['type'] );
- $index = intval( $widget['index'] );
- $all = get_option( 'widget_' . $type, array() );
- $all[ $index ] = $widget['settings'];
- update_option( 'widget_' . $type, $all );
- $current_sidebars[ $sidebar_id ][] = $type . '-' . $index;
- }
- }
- update_option( 'sidebars_widgets', $current_sidebars );
- $imported[] = 'Widgets';
- }
-
- // -------------------------------------------------------------------------
- // 4. Team-Mitglieder
- // -------------------------------------------------------------------------
- $team_data = isset( $data['team'] ) ? $data['team'] : ( isset( $data['team_data'] ) ? $data['team_data'] : array() );
- if ( ! empty( $team_data ) ) {
- $existing = new WP_Query( array( 'post_type' => 'team_member', 'posts_per_page' => -1, 'fields' => 'ids' ) );
- if ( $existing->have_posts() ) {
- foreach ( $existing->posts as $pid ) wp_delete_post( $pid, true );
- }
- wp_reset_postdata();
- foreach ( $team_data as $member ) {
- $id = wp_insert_post( array(
- 'post_title' => sanitize_text_field( $member['title'] ),
- 'post_content' => wp_kses_post( $member['content'] ),
- 'post_type' => 'team_member',
- 'post_status' => 'publish',
- 'menu_order' => intval( isset( $member['menu_order'] ) ? $member['menu_order'] : 0 ),
- ) );
- if ( $id && ! is_wp_error( $id ) ) {
- // Rank
- if ( ! empty( $member['rank'] ) ) {
- update_post_meta( $id, '_team_member_rank', sanitize_text_field( $member['rank'] ) );
- }
- // UUID
- if ( ! empty( $member['uuid'] ) ) {
- update_post_meta( $id, '_team_member_uuid', sanitize_text_field( $member['uuid'] ) );
- }
- // Thumbnail
- if ( ! empty( $member['thumbnail_id'] ) ) {
- set_post_thumbnail( $id, intval( $member['thumbnail_id'] ) );
- }
- // Banner
- if ( ! empty( $member['banner_id'] ) ) {
- update_post_meta( $id, '_team_member_banner', intval( $member['banner_id'] ) );
- }
- }
- }
- $imported[] = count( $team_data ) . ' Team-Mitglieder';
- }
-
- // -------------------------------------------------------------------------
- // 5. FAQs
- // -------------------------------------------------------------------------
- if ( ! empty( $data['faqs'] ) ) {
- $existing = new WP_Query( array( 'post_type' => 'faq', 'posts_per_page' => -1, 'fields' => 'ids' ) );
- if ( $existing->have_posts() ) {
- foreach ( $existing->posts as $pid ) wp_delete_post( $pid, true );
- }
- wp_reset_postdata();
- foreach ( $data['faqs'] as $faq ) {
- $id = wp_insert_post( array(
- 'post_title' => sanitize_text_field( $faq['title'] ),
- 'post_content' => wp_kses_post( $faq['content'] ),
- 'post_type' => 'faq',
- 'post_status' => 'publish',
- 'menu_order' => intval( isset( $faq['menu_order'] ) ? $faq['menu_order'] : 0 ),
- ) );
- if ( $id && ! is_wp_error( $id ) && ! empty( $faq['categories'] ) ) {
- foreach ( (array) $faq['categories'] as $cat_name ) {
- $term = term_exists( sanitize_text_field( $cat_name ), 'faq_category' );
- if ( ! $term ) {
- $term = wp_insert_term( sanitize_text_field( $cat_name ), 'faq_category' );
- }
- if ( ! is_wp_error( $term ) ) {
- wp_set_post_terms( $id, array( intval( $term['term_id'] ) ), 'faq_category', true );
- }
- }
- }
- }
- $imported[] = count( $data['faqs'] ) . ' FAQs';
- }
-
- // -------------------------------------------------------------------------
- // 6. Custom CSS
- // -------------------------------------------------------------------------
- if ( isset( $data['custom_css'] ) && $data['custom_css'] !== '' ) {
- wp_update_custom_css_post( $data['custom_css'] );
- $imported[] = 'Custom CSS';
- }
-
- // -------------------------------------------------------------------------
- // 7. Homepage-Seite
- // -------------------------------------------------------------------------
- if ( ! empty( $data['homepage'] ) ) {
- $homepage_id = get_option( 'page_on_front' );
- if ( $homepage_id ) {
- wp_update_post( array(
- 'ID' => $homepage_id,
- 'post_title' => sanitize_text_field( $data['homepage']['title'] ),
- 'post_content' => wp_kses_post( $data['homepage']['content'] ),
- 'post_excerpt' => sanitize_textarea_field( $data['homepage']['excerpt'] ),
- ) );
- if ( ! empty( $data['homepage']['featured_img'] ) ) {
- set_post_thumbnail( $homepage_id, intval( $data['homepage']['featured_img'] ) );
- }
- $imported[] = 'Homepage-Seite';
- }
- }
-
- // -------------------------------------------------------------------------
- // 8. Navigation Menüs mit Items
- // -------------------------------------------------------------------------
- if ( ! empty( $data['nav_menus'] ) ) {
- $menu_id_map = array(); // slug => term_id Mapping für Item-Parent-Zuordnung
-
- // Alle existierenden Menüs löschen
- $existing_menus = wp_get_nav_menus();
- foreach ( $existing_menus as $menu ) {
- wp_delete_nav_menu( $menu->term_id );
- }
-
- // Neue Menüs und Items importieren
- foreach ( $data['nav_menus'] as $menu_slug => $menu_data ) {
- $new_menu = wp_create_nav_menu( $menu_data['name'] );
- if ( ! is_wp_error( $new_menu ) ) {
- $menu_id_map[ $menu_slug ] = $new_menu;
-
- // Items hinzufügen
- if ( ! empty( $menu_data['items'] ) ) {
- $item_id_map = array(); // Altes Item ID => Neues Item ID Mapping
-
- // Erste Runde: Root-Items (parent = 0)
- foreach ( $menu_data['items'] as $idx => $item ) {
- if ( empty( $item['parent'] ) || $item['parent'] == 0 ) {
- $item_id = wp_update_nav_menu_item( $new_menu, 0, array(
- 'menu-item-title' => sanitize_text_field( $item['title'] ),
- 'menu-item-url' => esc_url_raw( $item['url'] ),
- 'menu-item-description' => sanitize_text_field( $item['description'] ),
- 'menu-item-type' => sanitize_key( $item['type'] ),
- 'menu-item-object' => sanitize_key( $item['object'] ),
- 'menu-item-object-id' => intval( $item['object_id'] ),
- 'menu-item-target' => sanitize_text_field( $item['target'] ),
- 'menu-item-classes' => sanitize_text_field( $item['classes'] ),
- 'menu-item-xfn' => sanitize_text_field( $item['xfn'] ),
- 'menu-item-status' => 'publish',
- ) );
- if ( $item_id && ! is_wp_error( $item_id ) ) {
- $item_id_map[ $idx ] = $item_id;
- }
- }
- }
-
- // Zweite Runde: Sub-Items (parent > 0)
- foreach ( $menu_data['items'] as $idx => $item ) {
- if ( ! empty( $item['parent'] ) && $item['parent'] > 0 ) {
- $parent_item_id = isset( $item_id_map[ $item['parent'] - 1 ] ) ? $item_id_map[ $item['parent'] - 1 ] : 0;
- $item_id = wp_update_nav_menu_item( $new_menu, 0, array(
- 'menu-item-title' => sanitize_text_field( $item['title'] ),
- 'menu-item-url' => esc_url_raw( $item['url'] ),
- 'menu-item-description' => sanitize_text_field( $item['description'] ),
- 'menu-item-type' => sanitize_key( $item['type'] ),
- 'menu-item-object' => sanitize_key( $item['object'] ),
- 'menu-item-object-id' => intval( $item['object_id'] ),
- 'menu-item-parent-id' => $parent_item_id,
- 'menu-item-target' => sanitize_text_field( $item['target'] ),
- 'menu-item-classes' => sanitize_text_field( $item['classes'] ),
- 'menu-item-xfn' => sanitize_text_field( $item['xfn'] ),
- 'menu-item-status' => 'publish',
- ) );
- if ( $item_id && ! is_wp_error( $item_id ) ) {
- $item_id_map[ $idx ] = $item_id;
- }
- }
- }
- }
- }
- }
- $imported[] = 'Navigation Menüs (' . count( $data['nav_menus'] ) . ')';
- }
-
- // -------------------------------------------------------------------------
- // 9. Menü-Positionen
- // -------------------------------------------------------------------------
- if ( ! empty( $data['nav_menu_locations'] ) ) {
- set_theme_mod( 'nav_menu_locations', $data['nav_menu_locations'] );
- $imported[] = 'Menü-Positionen';
- }
-
- wp_send_json_success( sprintf(
- __( 'Erfolgreich wiederhergestellt: %s', 'minecraft-modern-theme' ),
- implode( ', ', $imported )
- ) );
-}
-
-
-// =========================================================================
-// ANNOUNCEMENT BAR
-// =========================================================================
-
-function mm_announcement_get_font_list() {
- return array(
- 'inherit' => array( 'label' => 'Theme-Standard', 'css' => 'inherit', 'google' => false, 'google_name' => '' ),
- 'Arial' => array( 'label' => 'Arial', 'css' => 'Arial, Helvetica, sans-serif', 'google' => false, 'google_name' => '' ),
- 'Roboto' => array( 'label' => 'Roboto', 'css' => "'Roboto', sans-serif", 'google' => true, 'google_name' => 'Roboto' ),
- 'Montserrat' => array( 'label' => 'Montserrat', 'css' => "'Montserrat', sans-serif", 'google' => true, 'google_name' => 'Montserrat' ),
- 'Open Sans' => array( 'label' => 'Open Sans', 'css' => "'Open Sans', sans-serif", 'google' => true, 'google_name' => 'Open+Sans' ),
- 'Lato' => array( 'label' => 'Lato', 'css' => "'Lato', sans-serif", 'google' => true, 'google_name' => 'Lato' ),
- 'Poppins' => array( 'label' => 'Poppins', 'css' => "'Poppins', sans-serif", 'google' => true, 'google_name' => 'Poppins' ),
- 'Source Sans Pro' => array( 'label' => 'Source Sans Pro', 'css' => "'Source Sans Pro', sans-serif", 'google' => true, 'google_name' => 'Source+Sans+Pro' ),
- 'Noto Sans' => array( 'label' => 'Noto Sans', 'css' => "'Noto Sans', sans-serif", 'google' => true, 'google_name' => 'Noto+Sans' ),
- 'Raleway' => array( 'label' => 'Raleway', 'css' => "'Raleway', sans-serif", 'google' => true, 'google_name' => 'Raleway' ),
- 'Merriweather' => array( 'label' => 'Merriweather', 'css' => "'Merriweather', serif", 'google' => true, 'google_name' => 'Merriweather' ),
- 'Playfair Display' => array( 'label' => 'Playfair Display', 'css' => "'Playfair Display', serif", 'google' => true, 'google_name' => 'Playfair+Display' ),
- 'Oswald' => array( 'label' => 'Oswald', 'css' => "'Oswald', sans-serif", 'google' => true, 'google_name' => 'Oswald' ),
- 'Rubik' => array( 'label' => 'Rubik', 'css' => "'Rubik', sans-serif", 'google' => true, 'google_name' => 'Rubik' ),
- 'Inter' => array( 'label' => 'Inter', 'css' => "'Inter', sans-serif", 'google' => true, 'google_name' => 'Inter' ),
- 'Nunito' => array( 'label' => 'Nunito', 'css' => "'Nunito', sans-serif", 'google' => true, 'google_name' => 'Nunito' ),
- 'Ubuntu' => array( 'label' => 'Ubuntu', 'css' => "'Ubuntu', sans-serif", 'google' => true, 'google_name' => 'Ubuntu' ),
- 'PT Sans' => array( 'label' => 'PT Sans', 'css' => "'PT Sans', sans-serif", 'google' => true, 'google_name' => 'PT+Sans' ),
- 'Archivo' => array( 'label' => 'Archivo', 'css' => "'Archivo', sans-serif", 'google' => true, 'google_name' => 'Archivo' ),
- 'Fira Sans' => array( 'label' => 'Fira Sans', 'css' => "'Fira Sans', sans-serif", 'google' => true, 'google_name' => 'Fira+Sans' ),
- 'Work Sans' => array( 'label' => 'Work Sans', 'css' => "'Work Sans', sans-serif", 'google' => true, 'google_name' => 'Work+Sans' ),
- 'Quicksand' => array( 'label' => 'Quicksand', 'css' => "'Quicksand', sans-serif", 'google' => true, 'google_name' => 'Quicksand' ),
- 'Karla' => array( 'label' => 'Karla', 'css' => "'Karla', sans-serif", 'google' => true, 'google_name' => 'Karla' ),
- 'Dancing Script' => array( 'label' => 'Dancing Script', 'css' => "'Dancing Script', cursive", 'google' => true, 'google_name' => 'Dancing+Script' ),
- 'Pacifico' => array( 'label' => 'Pacifico', 'css' => "'Pacifico', cursive", 'google' => true, 'google_name' => 'Pacifico' ),
- 'Great Vibes' => array( 'label' => 'Great Vibes', 'css' => "'Great Vibes', cursive", 'google' => true, 'google_name' => 'Great+Vibes' ),
- 'Satisfy' => array( 'label' => 'Satisfy', 'css' => "'Satisfy', cursive", 'google' => true, 'google_name' => 'Satisfy' ),
- 'Allura' => array( 'label' => 'Allura', 'css' => "'Allura', cursive", 'google' => true, 'google_name' => 'Allura' ),
- 'Alex Brush' => array( 'label' => 'Alex Brush', 'css' => "'Alex Brush', cursive", 'google' => true, 'google_name' => 'Alex+Brush' ),
- 'Cookie' => array( 'label' => 'Cookie', 'css' => "'Cookie', cursive", 'google' => true, 'google_name' => 'Cookie' ),
- );
-}
-
-
-function mm_announcement_admin_init() {
- add_menu_page( 'Ankündigung', 'Ankündigung', 'manage_options', 'mm-announcement', 'mm_announcement_admin_page', 'dashicons-megaphone', 61 );
-
- register_setting( 'mm_announcement_group', 'mm_announcement_enabled' );
- register_setting( 'mm_announcement_group', 'mm_announcement_text' );
- register_setting( 'mm_announcement_group', 'mm_announcement_bg' );
- register_setting( 'mm_announcement_group', 'mm_announcement_color' );
- register_setting( 'mm_announcement_group', 'mm_announcement_font_size' );
- register_setting( 'mm_announcement_group', 'mm_announcement_font_family' );
- register_setting( 'mm_announcement_group', 'mm_announcement_position' );
- register_setting( 'mm_announcement_group', 'mm_announcement_countdown_enabled' );
- register_setting( 'mm_announcement_group', 'mm_announcement_countdown_label' );
- register_setting( 'mm_announcement_group', 'mm_announcement_countdown_date' );
- register_setting( 'mm_announcement_group', 'mm_announcement_countdown_expired_msg' );
-}
-add_action( 'admin_menu', 'mm_announcement_admin_init' );
-
-
-function mm_announcement_admin_page() {
- if ( ! current_user_can( 'manage_options' ) ) return;
-
- $fonts = mm_announcement_get_font_list();
- $selected_font = get_option( 'mm_announcement_font_family', 'inherit' );
- $selected_size = (int) get_option( 'mm_announcement_font_size', 16 );
- $bg = esc_attr( get_option( 'mm_announcement_bg', '#1e1e1e' ) );
- $color = esc_attr( get_option( 'mm_announcement_color', '#ffffff' ) );
- $text_sample = wp_strip_all_tags( get_option( 'mm_announcement_text' ) ) ?: 'Das ist eine Vorschau: Wie sieht die Schrift aus?';
- ?>
-
-
Header-Ankündigung
-
Diese Leiste wird auf allen Seiten angezeigt. Die Vorschau unten zeigt sofort, wie die Ankündigung aussieht — Änderungen wirken direkt in der Vorschau , erst nach Änderungen speichern werden sie im Frontend übernommen.
-
-
-
-
- ' . $countdown_label . '
- Laden...
- ';
- }
- ?>
-
- get_option( 'mm_announcement_font_family', 'inherit' ),
- 'size' => (int) get_option( 'mm_announcement_font_size', 16 ),
- 'bg' => get_option( 'mm_announcement_bg', '#1e1e1e' ),
- 'color' => get_option( 'mm_announcement_color', '#ffffff' ),
- 'text' => wp_kses_post( get_option( 'mm_announcement_text' ) ),
- ) );
-}
-add_action( 'admin_enqueue_scripts', 'mm_announcement_admin_assets' );
-
-
-// =========================================================================
-// TEAM MODUL
-// =========================================================================
-
-function create_team_post_type() {
- if ( get_theme_mod( 'team_enabled', true ) ) {
- register_post_type( 'team_member', array(
- 'labels' => array(
- 'name' => __( 'Team', 'minecraft-modern-theme' ),
- 'singular_name' => __( 'Teammitglied', 'minecraft-modern-theme' ),
- 'add_new' => __( 'Neues Mitglied', 'minecraft-modern-theme' ),
- 'menu_name' => __( 'Team', 'minecraft-modern-theme' ),
- ),
- 'public' => true,
- 'has_archive' => true,
- 'menu_icon' => 'dashicons-groups',
- 'supports' => array( 'title', 'thumbnail', 'page-attributes' ),
- 'rewrite' => array( 'slug' => 'team' ),
- 'show_in_rest' => true,
- 'show_in_menu' => false,
- ) );
- }
-}
-add_action( 'init', 'create_team_post_type' );
-
-
-function add_team_meta_boxes() {
- add_meta_box( 'team_member_rank_box', __( 'Rang & Position', 'minecraft-modern-theme' ), 'team_member_rank_callback', 'team_member', 'side', 'default' );
-}
-add_action( 'add_meta_boxes', 'add_team_meta_boxes' );
-
-function team_member_rank_callback( $post ) {
- wp_nonce_field( 'team_member_rank_save', 'team_member_rank_nonce' );
- $value = get_post_meta( $post->ID, '_team_member_rank', true );
- ?>
-
- Rang:
-
-
- id !== 'toplevel_page_mm-team-manager' ) return;
- echo '';
-}
-add_action( 'admin_head', 'mm_team_manager_admin_css' );
-
-function mm_team_manager_page_html() {
- ?>
-
-
Team Verwaltung
-
Hier kannst du Teammitglieder hinzufügen, sortieren und bearbeiten.
-
-
-
Neues Mitglied hinzufügen
-
-
Reihenfolge speichern
-
Reihenfolge gespeichert!
-
-
-
-
-
-
-
- Bild
- Name
- Rang
- Bio
- UUID / Bild
- Sort.
- Aktionen
-
-
-
- 'team_member', 'posts_per_page' => -1, 'orderby' => 'menu_order', 'order' => 'ASC' ) );
- if ( $team_query->have_posts() ) :
- while ( $team_query->have_posts() ) : $team_query->the_post();
- $id = get_the_ID();
- $name = get_the_title();
- $rank = get_post_meta( $id, '_team_member_rank', true );
- $bio = get_the_content();
- $uuid = get_post_meta( $id, '_team_member_uuid', true );
- $img_id = get_post_thumbnail_id( $id );
- $banner_id = get_post_meta( $id, '_team_member_banner', true );
- $banner_url = $banner_id ? wp_get_attachment_image_url( $banner_id, 'medium' ) : false;
-
- // Avatar anzeigen: UUID > Bild > Placeholder
- if ( $uuid ) {
- $avatar_src = 'https://visage.surgeplay.com/bust/' . esc_attr($uuid) . '.png';
- } elseif ( $img_id ) {
- $avatar_src = wp_get_attachment_image_url( $img_id, array(40,40) );
- } else {
- $avatar_src = false;
- }
- ?>
-
-
-
-
-
-
-
👤
-
-
-
-
-
-
-
-
-
-
-
- ▲
- ▼
-
-
- 💾
- 🗑️
-
-
- Noch keine Mitglieder vorhanden. ';
- endif;
- ?>
-
-
-
-
-
-
- $id, 'menu_order' => $pos ) );
- $pos++;
- }
- wp_send_json_success();
-}
-
-
-add_action( 'wp_ajax_mm_add_team_member', 'handle_mm_add_member' );
-function handle_mm_add_member() {
- check_ajax_referer( 'mm_team_nonce', 'nonce' );
- if ( ! current_user_can( 'publish_posts' ) ) wp_send_json_error( 'Keine Berechtigung' );
-
- $id = wp_insert_post( array(
- 'post_title' => sanitize_text_field( $_POST['name'] ),
- 'post_content' => sanitize_textarea_field( $_POST['bio'] ),
- 'post_type' => 'team_member',
- 'post_status' => 'publish',
- 'menu_order' => 999,
- ) );
-
- if ( ! $id || is_wp_error( $id ) ) {
- wp_send_json_error( 'Fehler beim Erstellen' );
- }
-
- update_post_meta( $id, '_team_member_rank', sanitize_text_field( $_POST['rank'] ) );
-
- // UUID speichern (kein externer Request hier – nur als Text speichern)
- if ( ! empty( $_POST['uuid'] ) ) {
- $uuid = sanitize_text_field( trim( $_POST['uuid'] ) );
- update_post_meta( $id, '_team_member_uuid', $uuid );
- } elseif ( ! empty( $_POST['img_id'] ) ) {
- set_post_thumbnail( $id, intval( $_POST['img_id'] ) );
- }
-
- // Banner-Bild speichern
- if ( ! empty( $_POST['banner_id'] ) ) {
- update_post_meta( $id, '_team_member_banner', intval( $_POST['banner_id'] ) );
- }
-
- wp_send_json_success( array( 'id' => $id, 'msg' => 'Hinzugefügt' ) );
-}
-
-add_action( 'wp_ajax_mm_update_team_member', 'handle_mm_update_team_member' );
-function handle_mm_update_team_member() {
- check_ajax_referer( 'mm_team_nonce', 'nonce' );
- if ( ! current_user_can( 'edit_posts' ) ) wp_send_json_error( 'Keine Berechtigung' );
- $id = intval( $_POST['id'] );
- wp_update_post( array(
- 'ID' => $id,
- 'post_title' => sanitize_text_field( $_POST['title'] ),
- 'post_content' => sanitize_textarea_field( $_POST['bio'] ),
- ) );
- update_post_meta( $id, '_team_member_rank', sanitize_text_field( $_POST['rank'] ) );
- // UUID speichern oder löschen
- if ( isset( $_POST['uuid'] ) ) {
- $uuid = sanitize_text_field( trim( $_POST['uuid'] ) );
- if ( $uuid ) {
- update_post_meta( $id, '_team_member_uuid', $uuid );
- } else {
- delete_post_meta( $id, '_team_member_uuid' );
- }
- }
- // Bild nur setzen wenn keine UUID
- if ( empty( $_POST['uuid'] ) && ! empty( $_POST['img_id'] ) ) {
- set_post_thumbnail( $id, intval( $_POST['img_id'] ) );
- }
- // Banner speichern
- if ( isset( $_POST['banner_id'] ) ) {
- if ( ! empty( $_POST['banner_id'] ) ) {
- update_post_meta( $id, '_team_member_banner', intval( $_POST['banner_id'] ) );
- }
- }
- wp_send_json_success( 'Gespeichert' );
-}
-
-add_action( 'wp_ajax_mm_delete_team_member', 'handle_mm_delete_team_member' );
-function handle_mm_delete_team_member() {
- check_ajax_referer( 'mm_team_nonce', 'nonce' );
- if ( ! current_user_can( 'delete_posts' ) ) wp_send_json_error( 'Keine Berechtigung' );
- wp_delete_post( intval( $_POST['id'] ), true );
- wp_send_json_success( 'Gelöscht' );
-}
-
-
-function create_team_page_automatically() {
- if ( get_theme_mod( 'team_enabled', true ) && get_page_by_title( 'Team' ) == null ) {
- wp_insert_post( array( 'post_title' => 'Team', 'post_status' => 'publish', 'post_type' => 'page', 'post_author' => 1 ) );
- }
-}
-add_action( 'customize_save_after', 'create_team_page_automatically' );
-
-
-function load_team_page_template( $template ) {
- if ( ! get_theme_mod( 'team_enabled', true ) ) return $template;
- if ( is_post_type_archive( 'team_member' ) ) return get_template_directory() . '/archive-team.php';
- if ( is_page() ) {
- $obj = get_queried_object();
- if ( $obj && $obj->post_name === 'team' ) return get_template_directory() . '/archive-team.php';
- }
- return $template;
-}
-add_filter( 'template_include', 'load_team_page_template' );
-
-
-// Doppelte team_customize_register entfernt – ist jetzt in inc/customizer.php
-
-// =========================================================================
-// MENÜ-LAYOUTS: Hilfsfunktionen für header.php
-// =========================================================================
-
-if ( ! function_exists('mm_branding') ) :
-function mm_branding( $show_title_with_logo = false ) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Panel –
- // der Hamburger wird dort nicht gebraucht und verursacht JS-Konflikte.
- if ( $menu_style !== 'sidebar' ) : ?>
-
-
- 'primary',
- 'container' => false,
- 'menu_class' => 'primary-menu',
- 'fallback_cb' => false,
- ) ); ?>
-
- 'fab fa-bluesky', 'discord' => 'fab fa-discord', 'youtube' => 'fab fa-youtube',
- 'twitter' => 'fab fa-x-twitter', 'facebook' => 'fab fa-facebook-f',
- 'instagram' => 'fab fa-instagram', 'tiktok' => 'fab fa-tiktok',
- 'twitch' => 'fab fa-twitch', 'steam' => 'fab fa-steam',
- 'github' => 'fab fa-github', 'linkedin' => 'fab fa-linkedin-in',
- 'pinterest' => 'fab fa-pinterest-p', 'reddit' => 'fab fa-reddit-alien',
- 'mastodon' => 'fab fa-mastodon', 'threads' => 'fab fa-threads',
- 'kickstarter' => 'fab fa-kickstarter', 'teamspeak' => 'fab fa-teamspeak', 'spotify' => 'fab fa-spotify',
- 'stoat' => 'fab fa-diaspora',
- ); ?>
-
-add_section( 'header_menu_style_section', array(
- 'title' => __( 'Menü-Design', 'minecraft-modern-theme' ),
- 'priority' => 30,
- ) );
- $wp_customize->add_setting( 'header_menu_style', array(
- 'default' => 'classic',
- 'sanitize_callback' => 'sanitize_text_field',
- 'transport' => 'refresh',
- ) );
- $wp_customize->add_control( 'header_menu_style', array(
- 'label' => __( 'Menü-Layout wählen', 'minecraft-modern-theme' ),
- 'section' => 'header_menu_style_section',
- 'type' => 'select',
- 'choices' => array(
- 'classic' => __( '① Classic – Logo links, Menü Mitte, Icons rechts', 'minecraft-modern-theme' ),
- 'centered' => __( '② Zentriert – Logo oben, Menü darunter', 'minecraft-modern-theme' ),
- 'sidebar' => __( '③ Sidebar – Menü als vertikale Leiste', 'minecraft-modern-theme' ),
- 'mega' => __( '④ Mega-Menü – breite Dropdown-Spalten', 'minecraft-modern-theme' ),
- ),
- ) );
-
- // Branding-Position (gilt für alle Layouts)
- $wp_customize->add_setting( 'sidebar_branding_position', array(
- 'default' => 'left',
- 'sanitize_callback' => 'sanitize_text_field',
- 'transport' => 'refresh',
- ) );
- $wp_customize->add_control( 'sidebar_branding_position', array(
- 'label' => __( 'Logo/Titel Position', 'minecraft-modern-theme' ),
- 'description' => __( 'Gilt für alle Menü-Layouts.', 'minecraft-modern-theme' ),
- 'section' => 'header_menu_style_section',
- 'type' => 'select',
- 'choices' => array(
- 'left' => __( 'Links', 'minecraft-modern-theme' ),
- 'center' => __( 'Mitte', 'minecraft-modern-theme' ),
- 'right' => __( 'Rechts', 'minecraft-modern-theme' ),
- ),
- ) );
-}
-add_action( 'customize_register', 'minecraft_modern_menu_style_customizer' );
-
-
-// === Sidebar-Menü JavaScript (nur wenn Sidebar-Layout aktiv) ===
-function minecraft_modern_sidebar_menu_script() {
- if ( get_theme_mod( 'header_menu_style', 'classic' ) !== 'sidebar' ) return;
- ?>
-
- __( 'Beitrag Sidebar', 'minecraft-modern-theme' ),
- 'id' => 'single-post-sidebar',
- 'description' => __( 'Widget-Bereich für die Sidebar auf Einzelbeitrags-Seiten.', 'minecraft-modern-theme' ),
- 'before_widget' => '',
- 'before_title' => '',
- ) );
-}
-add_action( 'widgets_init', 'minecraft_modern_single_sidebar' );
-
-
-// === Single-Post Sidebar Render-Funktion ===
-function minecraft_modern_render_single_sidebar() {
- if ( is_active_sidebar( 'single-post-sidebar' ) ) {
- dynamic_sidebar( 'single-post-sidebar' );
- } else {
- ?>
-
-
-
-
-
-
-
- add_section( 'single_sidebar_section', array(
- 'title' => __( 'Beitrag Sidebar', 'minecraft-modern-theme' ),
- 'priority' => 55,
- ) );
- $wp_customize->add_setting( 'single_sidebar_enabled', array(
- 'default' => true,
- 'sanitize_callback' => 'wp_validate_boolean',
- ) );
- $wp_customize->add_control( 'single_sidebar_enabled', array(
- 'label' => __( 'Sidebar auf Einzelbeiträgen anzeigen', 'minecraft-modern-theme' ),
- 'section' => 'single_sidebar_section',
- 'type' => 'checkbox',
- ) );
- $wp_customize->add_setting( 'single_sidebar_position', array(
- 'default' => 'right',
- 'sanitize_callback' => 'sanitize_text_field',
- ) );
- $wp_customize->add_control( 'single_sidebar_position', array(
- 'label' => __( 'Sidebar-Position', 'minecraft-modern-theme' ),
- 'section' => 'single_sidebar_section',
- 'type' => 'select',
- 'choices' => array(
- 'right' => __( 'Rechts', 'minecraft-modern-theme' ),
- 'left' => __( 'Links', 'minecraft-modern-theme' ),
- ),
- ) );
-}
-add_action( 'customize_register', 'minecraft_modern_single_sidebar_customizer' );
-
-
-// === Announcement-Bar Höhe als CSS-Variable + Body-Klasse ===
-function minecraft_modern_announcement_offset_script() {
- if ( ! get_option( 'mm_announcement_enabled' ) ) return;
- ?>
-
-
-
- add_section( 'mm_cookie_banner_section', array(
- 'title' => __( 'Cookie-Banner (DSGVO)', 'minecraft-modern-theme' ),
- 'priority' => 75,
- ) );
-
- $wp_customize->add_setting( 'mm_cookie_enabled', array( 'default' => true, 'sanitize_callback' => 'wp_validate_boolean', 'transport' => 'postMessage' ) );
- $wp_customize->add_control( 'mm_cookie_enabled', array( 'label' => __( 'Cookie-Banner aktivieren', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'checkbox' ) );
-
- $wp_customize->add_setting( 'mm_cookie_style', array( 'default' => 'bar', 'sanitize_callback' => 'sanitize_text_field', 'transport' => 'postMessage' ) );
- $wp_customize->add_control( 'mm_cookie_style', array(
- 'label' => __( 'Design-Variante', 'minecraft-modern-theme' ),
- 'section' => 'mm_cookie_banner_section',
- 'type' => 'select',
- 'choices' => array(
- 'bar' => __( 'Variante 1 – Schmale Bar (volle Breite)', 'minecraft-modern-theme' ),
- 'split' => __( 'Variante 2 – Zweispaltig (3A)', 'minecraft-modern-theme' ),
- 'slide' => __( 'Variante 3 – Slide-In von rechts (3B)', 'minecraft-modern-theme' ),
- 'stepper' => __( 'Variante 4 – Kompakt-Center mit Stepper (3C)', 'minecraft-modern-theme' ),
- ),
- ) );
-
- $wp_customize->add_setting( 'mm_cookie_text', array( 'default' => __( 'Wir nutzen Cookies und ähnliche Technologien. Einige sind essenziell, andere helfen uns diese Website zu verbessern. Du kannst deine Auswahl jederzeit anpassen.', 'minecraft-modern-theme' ), 'sanitize_callback' => 'wp_kses_post', 'transport' => 'postMessage' ) );
- $wp_customize->add_control( 'mm_cookie_text', array( 'label' => __( 'Banner-Text', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'textarea' ) );
-
- $wp_customize->add_setting( 'mm_cookie_privacy_url', array( 'default' => '', 'sanitize_callback' => 'esc_url_raw' ) );
- $wp_customize->add_control( 'mm_cookie_privacy_url', array( 'label' => __( 'URL Datenschutzerklärung', 'minecraft-modern-theme' ), 'description' => __( 'Leer lassen um den Link auszublenden.', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'url' ) );
-
- foreach ( array(
- 'necessary' => array( 'label' => __( 'Beschreibung: Notwendige', 'minecraft-modern-theme' ), 'default' => __( 'Grundlegende Funktionen der Website. Können nicht deaktiviert werden.', 'minecraft-modern-theme' ) ),
- 'statistics' => array( 'label' => __( 'Beschreibung: Statistik', 'minecraft-modern-theme' ), 'default' => __( 'Helfen uns zu verstehen wie Besucher mit der Website interagieren (z.B. Google Analytics).', 'minecraft-modern-theme' ) ),
- 'marketing' => array( 'label' => __( 'Beschreibung: Marketing', 'minecraft-modern-theme' ), 'default' => __( 'Werden genutzt um Werbung relevanter zu gestalten (z.B. YouTube, Facebook).', 'minecraft-modern-theme' ) ),
- ) as $key => $opts ) {
- $wp_customize->add_setting( 'mm_cookie_desc_' . $key, array( 'default' => $opts['default'], 'sanitize_callback' => 'sanitize_textarea_field' ) );
- $wp_customize->add_control( 'mm_cookie_desc_' . $key, array( 'label' => $opts['label'], 'section' => 'mm_cookie_banner_section', 'type' => 'textarea' ) );
- }
-
- $wp_customize->add_setting( 'mm_cookie_ga_id', array( 'default' => '', 'sanitize_callback' => 'sanitize_text_field' ) );
- $wp_customize->add_control( 'mm_cookie_ga_id', array( 'label' => __( 'Google Analytics ID (optional)', 'minecraft-modern-theme' ), 'description' => __( 'z.B. G-XXXXXXXXXX', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'text' ) );
-
- $wp_customize->add_setting( 'mm_cookie_lifetime', array( 'default' => 365, 'sanitize_callback' => 'absint' ) );
- $wp_customize->add_control( 'mm_cookie_lifetime', array( 'label' => __( 'Cookie-Laufzeit (Tage)', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'number', 'input_attrs' => array( 'min' => 1, 'max' => 730 ) ) );
-
- if ( class_exists( 'MM_Cookie_Preview_Control' ) ) {
- $wp_customize->add_setting( 'mm_cookie_preview_dummy', array( 'sanitize_callback' => 'sanitize_text_field' ) );
- $wp_customize->add_control( new MM_Cookie_Preview_Control( $wp_customize, 'mm_cookie_preview_dummy', array( 'section' => 'mm_cookie_banner_section', 'priority' => 200 ) ) );
- }
-}
-add_action( 'customize_register', 'mm_cookie_banner_customizer' );
-
-
-// --- 2. Banner HTML – ALLE 4 LAYOUTS gleichzeitig ausgeben ---
-// Im Customizer wechselt JS nur die Klasse auf #mm-cookie-banner.
-// CSS zeigt immer nur das passende .mmc-layout-* div.
-function mm_cookie_banner_render() {
- $is_preview = is_customize_preview();
- if ( ! $is_preview && ! get_theme_mod( 'mm_cookie_enabled', true ) ) return;
-
- $style = get_theme_mod( 'mm_cookie_style', 'bar' );
- $text = get_theme_mod( 'mm_cookie_text', __( 'Wir nutzen Cookies und ähnliche Technologien. Einige sind essenziell, andere helfen uns diese Website zu verbessern.', 'minecraft-modern-theme' ) );
- $priv_url = get_theme_mod( 'mm_cookie_privacy_url', '' );
- $lifetime = absint( get_theme_mod( 'mm_cookie_lifetime', 365 ) );
- $ga_id = get_theme_mod( 'mm_cookie_ga_id', '' );
- $desc_n = get_theme_mod( 'mm_cookie_desc_necessary', __( 'Grundlegende Funktionen der Website. Können nicht deaktiviert werden.', 'minecraft-modern-theme' ) );
- $desc_s = get_theme_mod( 'mm_cookie_desc_statistics', __( 'Helfen uns zu verstehen wie Besucher mit der Website interagieren (z.B. Google Analytics).', 'minecraft-modern-theme' ) );
- $desc_m = get_theme_mod( 'mm_cookie_desc_marketing', __( 'Werden genutzt um Werbung relevanter zu gestalten (z.B. YouTube, Facebook).', 'minecraft-modern-theme' ) );
-
- $priv_link = $priv_url ? '' . __( 'Datenschutzerklärung', 'minecraft-modern-theme' ) . ' ' : '';
- $preview_class = $is_preview ? ' mmc-visible mmc-preview-mode' : '';
- $inline_style = $is_preview ? '' : 'display:none;';
-
- // Kategorien-Block (identisch in allen Varianten mit Kategorien)
- $cats = '
-
' . __('Notwendige','minecraft-modern-theme') . '' . __('Immer aktiv','minecraft-modern-theme') . '
' . esc_html($desc_n) . '
-
-
-
';
-
- $btn_accept = ' ' . __('Alle akzeptieren','minecraft-modern-theme') . ' ';
- $btn_select = ' ' . __('Auswahl speichern','minecraft-modern-theme') . ' ';
- $btn_neces = '' . __('Nur notwendige','minecraft-modern-theme') . ' ';
-
- $text_esc = wp_kses_post($text);
- ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ' . $priv_link . ''; ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
- __('Cookie-Einstellungen','minecraft-modern-theme')),$atts);return ''.esc_html($a['text']).' ';}
-add_shortcode('cookie_settings','mm_cookie_settings_shortcode');
-
-
-// --- 4. Live-Vorschau: nur Klasse + Text wechseln, HTML ist bereits vollständig ---
-function mm_cookie_banner_preview_js(){
- if(!is_customize_preview())return; ?>
-
- array(
- 'name' => __( 'Videos', 'minecraft-modern-theme' ),
- 'singular_name' => __( 'Video', 'minecraft-modern-theme' ),
- 'add_new' => __( 'Neues Video', 'minecraft-modern-theme' ),
- 'add_new_item' => __( 'Neues Video hinzufügen', 'minecraft-modern-theme' ),
- 'edit_item' => __( 'Video bearbeiten', 'minecraft-modern-theme' ),
- 'all_items' => __( 'Alle Videos', 'minecraft-modern-theme' ),
- 'menu_name' => __( 'Videos', 'minecraft-modern-theme' ),
- ),
- 'public' => true,
- 'has_archive' => true,
- 'menu_icon' => 'dashicons-video-alt3',
- 'menu_position' => 7,
- 'supports' => array( 'title', 'thumbnail', 'excerpt', 'page-attributes' ),
- 'rewrite' => array( 'slug' => 'videos' ),
- 'show_in_rest' => true,
- ) );
-
- register_post_type( 'mm_livestream', array(
- 'labels' => array(
- 'name' => __( 'Livestreams', 'minecraft-modern-theme' ),
- 'singular_name' => __( 'Livestream', 'minecraft-modern-theme' ),
- 'add_new' => __( 'Neuer Livestream', 'minecraft-modern-theme' ),
- 'add_new_item' => __( 'Neuen Livestream hinzufügen', 'minecraft-modern-theme' ),
- 'edit_item' => __( 'Livestream bearbeiten', 'minecraft-modern-theme' ),
- 'all_items' => __( 'Alle Livestreams', 'minecraft-modern-theme' ),
- 'menu_name' => __( 'Livestreams', 'minecraft-modern-theme' ),
- ),
- 'public' => true,
- 'publicly_queryable' => false,
- 'exclude_from_search' => true,
- 'show_ui' => true,
- 'show_in_menu' => 'edit.php?post_type=mm_video',
- 'menu_position' => 8,
- 'supports' => array( 'title', 'excerpt', 'page-attributes' ),
- 'show_in_rest' => true,
- ) );
-}
-add_action( 'init', 'mm_register_video_post_type' );
-
-
-// --- 2. Meta-Box: Video URL + Kategorie ---
-function mm_video_meta_boxes() {
- add_meta_box(
- 'mm_video_data',
- __( 'Video-Einstellungen', 'minecraft-modern-theme' ),
- 'mm_video_meta_box_html',
- 'mm_video', 'normal', 'high'
- );
-
- add_meta_box(
- 'mm_livestream_data',
- __( 'Livestream-Einstellungen', 'minecraft-modern-theme' ),
- 'mm_livestream_meta_box_html',
- 'mm_livestream', 'normal', 'high'
- );
-}
-add_action( 'add_meta_boxes', 'mm_video_meta_boxes' );
-
-function mm_video_meta_box_html( $post ) {
- wp_nonce_field( 'mm_video_save', 'mm_video_nonce' );
- $url = get_post_meta( $post->ID, '_mm_video_url', true );
- $category = get_post_meta( $post->ID, '_mm_video_category', true );
- $post_id = $post->ID;
- ?>
-
- ID, '_mm_livestream_url', true );
- $player_url = get_post_meta( $post->ID, '_mm_livestream_player_url', true );
- $owner = get_post_meta( $post->ID, '_mm_livestream_owner', true );
- ?>
-
- Tag ausgegeben, nicht iframe
- }
-
- return false;
-}
-
-function mm_video_get_type( $url ) {
- if ( preg_match( '/youtube\.com|youtu\.be/', $url ) ) return 'youtube';
- if ( strpos( $url, 'vimeo.com' ) !== false ) return 'vimeo';
- if ( strpos( $url, 'twitch.tv' ) !== false ) return 'twitch';
- if ( preg_match( '/\.(mp4|webm|ogv|ogg)(\?.*)?$/i', $url ) ) return 'mp4';
- return 'unknown';
-}
-
-function mm_twitch_get_channel_from_url( $url ) {
- if ( preg_match( '/twitch\.tv\/([a-zA-Z0-9_]+)(?:\/|\?|$)/', (string) $url, $m ) ) {
- return strtolower( $m[1] );
- }
-
- return '';
-}
-
-function mm_twitch_get_app_token() {
- $client_id = get_theme_mod( 'twitch_client_id', '' );
- $client_secret = get_theme_mod( 'twitch_client_secret', '' );
-
- if ( empty( $client_id ) || empty( $client_secret ) ) {
- return false;
- }
-
- $cache_key = 'mm_twitch_app_token';
- $cached = get_transient( $cache_key );
- if ( ! empty( $cached ) ) {
- return $cached;
- }
-
- $response = wp_remote_post( 'https://id.twitch.tv/oauth2/token', array(
- 'timeout' => 8,
- 'body' => array(
- 'client_id' => $client_id,
- 'client_secret' => $client_secret,
- 'grant_type' => 'client_credentials',
- ),
- ) );
-
- if ( is_wp_error( $response ) ) {
- return false;
- }
-
- $data = json_decode( wp_remote_retrieve_body( $response ), true );
- if ( empty( $data['access_token'] ) || empty( $data['expires_in'] ) ) {
- return false;
- }
-
- $ttl = max( 60, (int) $data['expires_in'] - 60 );
- set_transient( $cache_key, $data['access_token'], $ttl );
-
- return $data['access_token'];
-}
-
-function mm_twitch_is_live( $channel ) {
- $channel = sanitize_text_field( (string) $channel );
- if ( $channel === '' ) {
- return false;
- }
-
- $cache_key = 'mm_twitch_live_' . $channel;
- $cached = get_transient( $cache_key );
- if ( false !== $cached ) {
- return $cached === 'live';
- }
-
- $client_id = get_theme_mod( 'twitch_client_id', '' );
- $token = mm_twitch_get_app_token();
- if ( empty( $client_id ) || empty( $token ) ) {
- return false;
- }
-
- $url = add_query_arg( array(
- 'user_login' => $channel,
- ), 'https://api.twitch.tv/helix/streams' );
-
- $response = wp_remote_get( $url, array(
- 'timeout' => 8,
- 'headers' => array(
- 'Client-ID' => $client_id,
- 'Authorization' => 'Bearer ' . $token,
- ),
- ) );
-
- if ( is_wp_error( $response ) ) {
- return false;
- }
-
- $data = json_decode( wp_remote_retrieve_body( $response ), true );
- $is_live = ! empty( $data['data'][0] );
- set_transient( $cache_key, $is_live ? 'live' : 'offline', 2 * MINUTE_IN_SECONDS );
-
- return $is_live;
-}
-
-function mm_video_get_youtube_player_url( $identifier, $mode = 'video' ) {
- $params = 'autoplay=0&rel=0&modestbranding=1';
-
- if ( $mode === 'channel' ) {
- return 'https://www.youtube.com/embed/live_stream?channel=' . rawurlencode( $identifier ) . '&' . $params;
- }
-
- if ( $mode === 'handle-live' ) {
- return 'https://www.youtube.com/embed/' . ltrim( $identifier ) . '/live?' . $params;
- }
-
- return 'https://www.youtube.com/embed/' . rawurlencode( $identifier ) . '?' . $params;
-}
-
-function mm_video_get_youtube_handle_from_url( $url ) {
- if ( preg_match( '~youtube\.com/(@[A-Za-z0-9._-]+)(?:/.*)?$~i', (string) $url, $matches ) ) {
- return sanitize_text_field( $matches[1] );
- }
-
- return '';
-}
-
-function mm_video_extract_youtube_live_video_id( $html ) {
- $patterns = array(
- '/"canonicalBaseUrl":"\\/watch\?v=([A-Za-z0-9_-]{11})"/i',
- '/"watchEndpoint":\{"videoId":"([A-Za-z0-9_-]{11})"/i',
- '/\/watch\?v=([A-Za-z0-9_-]{11})\\u0026/i',
- '/"videoId":"([A-Za-z0-9_-]{11})".{0,600}?"isLiveNow":true/is',
- '/"videoId":"([A-Za-z0-9_-]{11})".{0,600}?"style":"LIVE"/is',
- '/"videoId":"([A-Za-z0-9_-]{11})".{0,600}?LIVE_NOW/is',
- '/https:\/\/www\.youtube\.com\/watch\?v=([A-Za-z0-9_-]{11})/i',
- '/ 5 ) );
- if ( ! is_wp_error( $response ) ) {
- $data = json_decode( wp_remote_retrieve_body( $response ), true );
- if ( ! empty( $data['items'][0] ) ) {
- $state = $data['items'][0]['snippet']['liveBroadcastContent'] ?? '';
- $is_live = ( $state === 'live' );
- set_transient( $cache_key, $is_live ? 'live' : 'offline', 2 * MINUTE_IN_SECONDS );
- return $is_live;
- }
- }
- }
-
- // Fallback: Wenn keine API, gehen wir davon aus dass die video_id aktuell ist
- // weil sie von mm_video_resolve_youtube_live_video_id() kam
- set_transient( $cache_key, 'live', 2 * MINUTE_IN_SECONDS );
- return true;
-}
-
-function mm_video_resolve_youtube_live_video_id( $profile_url = '', $youtube_channel_id = '' ) {
- $profile_url = trim( (string) $profile_url );
- $youtube_channel_id = trim( (string) $youtube_channel_id );
- $cache_key = 'mm_yt_live_v2_' . md5( strtolower( $profile_url . '|' . $youtube_channel_id ) );
- $cached = get_transient( $cache_key );
-
- if ( is_array( $cached ) && array_key_exists( 'video_id', $cached ) ) {
- return $cached['video_id'];
- }
-
- $candidate_urls = array();
- $handle = mm_video_get_youtube_handle_from_url( $profile_url );
-
- if ( $youtube_channel_id && preg_match( '/^UC[a-zA-Z0-9_-]+$/', $youtube_channel_id ) ) {
- $candidate_urls[] = 'https://www.youtube.com/channel/' . rawurlencode( $youtube_channel_id ) . '/live';
- $candidate_urls[] = 'https://www.youtube.com/channel/' . rawurlencode( $youtube_channel_id ) . '/streams';
- }
-
- if ( $handle ) {
- $candidate_urls[] = 'https://www.youtube.com/' . rawurlencode( ltrim( $handle, '/' ) ) . '/live';
- $candidate_urls[] = 'https://www.youtube.com/' . rawurlencode( ltrim( $handle, '/' ) ) . '/streams';
- }
-
- if ( $profile_url ) {
- $candidate_urls[] = untrailingslashit( $profile_url ) . '/live';
- $candidate_urls[] = untrailingslashit( $profile_url ) . '/streams';
- }
-
- $candidate_urls = array_values( array_unique( array_filter( $candidate_urls ) ) );
- $request_args = array(
- 'timeout' => 10,
- 'redirection' => 5,
- 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
- 'headers' => array(
- 'Accept-Language' => 'en-US,en;q=0.9,de;q=0.8',
- ),
- );
-
- foreach ( $candidate_urls as $candidate_url ) {
- $response = wp_remote_get( $candidate_url, $request_args );
-
- if ( is_wp_error( $response ) ) {
- continue;
- }
-
- $body = wp_remote_retrieve_body( $response );
- if ( ! is_string( $body ) || $body === '' ) {
- continue;
- }
-
- $video_id = mm_video_extract_youtube_live_video_id( $body );
- if ( $video_id ) {
- set_transient( $cache_key, array( 'video_id' => $video_id ), 2 * MINUTE_IN_SECONDS );
- return $video_id;
- }
- }
-
- set_transient( $cache_key, array( 'video_id' => '' ), 60 );
- return '';
-}
-
-function mm_video_get_livestream_data( $profile_url = '', $player_url = '', $youtube_channel_id = '' ) {
- if ( empty( $profile_url ) && empty( $player_url ) && empty( $youtube_channel_id ) ) {
- return false;
- }
-
- $profile_url = trim( $profile_url );
- $player_url = trim( $player_url );
- $youtube_channel_id = trim( $youtube_channel_id );
- $source_url = $player_url ? $player_url : $profile_url;
- $parent_host = parse_url( home_url(), PHP_URL_HOST );
- $data = array(
- 'profile_url' => esc_url_raw( $profile_url ? $profile_url : $player_url ),
- 'platform' => 'unknown',
- 'label' => __( 'Livestream', 'minecraft-modern-theme' ),
- 'icon' => 'fas fa-tower-broadcast',
- 'color' => '#00d4ff',
- 'cta' => __( 'Profil öffnen', 'minecraft-modern-theme' ),
- 'embed_url' => '',
- 'video_id' => '',
- 'channel' => '',
- 'channel_display' => '',
- );
-
- if ( ! empty( $youtube_channel_id ) && preg_match( '/^UC[a-zA-Z0-9_-]+$/', $youtube_channel_id ) ) {
- $resolved_video_id = mm_video_resolve_youtube_live_video_id( $profile_url, $youtube_channel_id );
-
- $data['platform'] = 'youtube';
- $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-youtube';
- $data['color'] = '#ff0000';
- $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
- $data['channel'] = $youtube_channel_id;
- $data['channel_display'] = $youtube_channel_id;
- $data['video_id'] = $resolved_video_id;
- $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
-
- if ( preg_match( '~youtube\.com/(@[A-Za-z0-9._-]+)(?:/.*)?$~i', $profile_url, $handle_matches ) ) {
- $data['channel_display'] = sanitize_text_field( $handle_matches[1] );
- }
-
- if ( empty( $data['profile_url'] ) ) {
- $data['profile_url'] = 'https://www.youtube.com/channel/' . rawurlencode( $youtube_channel_id );
- }
-
- if ( empty( $player_url ) ) {
- return $data;
- }
- }
-
- if ( empty( $source_url ) ) {
- return false;
- }
-
- if ( preg_match( '/youtube\.com|youtu\.be/', $source_url ) && preg_match( '/(?:youtube\.com\/(?:watch\?v=|shorts\/|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/', $source_url ) ) {
- $data['platform'] = 'youtube';
- $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-youtube';
- $data['color'] = '#ff0000';
- $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
- preg_match( '/(?:youtube\.com\/(?:watch\?v=|shorts\/|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/', $source_url, $video_match );
- $data['video_id'] = ! empty( $video_match[1] ) ? $video_match[1] : '';
- $data['embed_url'] = ! empty( $video_match[1] ) ? mm_video_get_youtube_player_url( $video_match[1], 'video' ) : '';
- return $data;
- }
-
- if ( preg_match( '~twitch\.tv/([A-Za-z0-9_]+)(?:/)?(?:\?.*)?$~i', $source_url, $matches ) && strtolower( $matches[1] ) !== 'videos' ) {
- $channel = sanitize_key( $matches[1] );
- $data['platform'] = 'twitch';
- $data['label'] = __( 'Twitch Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-twitch';
- $data['color'] = '#9146ff';
- $data['cta'] = __( 'Zum Twitch-Kanal', 'minecraft-modern-theme' );
- $data['channel'] = $channel;
- $data['channel_display'] = '@' . $channel;
- $data['embed_url'] = 'https://player.twitch.tv/?channel=' . rawurlencode( $channel ) . '&parent=' . rawurlencode( $parent_host ) . '&autoplay=false';
- return $data;
- }
-
- if ( preg_match( '~kick\.com/([A-Za-z0-9_]+)(?:/)?(?:\?.*)?$~i', $source_url, $matches ) ) {
- $channel = sanitize_key( $matches[1] );
- $data['platform'] = 'kick';
- $data['label'] = __( 'Kick Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fas fa-satellite-dish';
- $data['color'] = '#53fc18';
- $data['cta'] = __( 'Zum Kick-Kanal', 'minecraft-modern-theme' );
- $data['channel'] = $channel;
- $data['channel_display'] = '@' . $channel;
- $data['embed_url'] = 'https://player.kick.com/' . rawurlencode( $channel );
- return $data;
- }
-
- if ( preg_match( '~youtube\.com/channel/(UC[a-zA-Z0-9_-]+)~i', $source_url, $matches ) ) {
- $channel_id = sanitize_text_field( $matches[1] );
- $resolved_video_id = mm_video_resolve_youtube_live_video_id( $source_url, $channel_id );
- $data['platform'] = 'youtube';
- $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-youtube';
- $data['color'] = '#ff0000';
- $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
- $data['channel'] = $channel_id;
- $data['channel_display'] = $channel_id;
- $data['video_id'] = $resolved_video_id;
- $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
- return $data;
- }
-
- if ( preg_match( '~youtube\.com/(@[A-Za-z0-9._-]+)(?:/.*)?$~i', $source_url, $matches ) ) {
- $handle = sanitize_text_field( $matches[1] );
- $resolved_video_id = mm_video_resolve_youtube_live_video_id( $source_url, '' );
- $data['platform'] = 'youtube';
- $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-youtube';
- $data['color'] = '#ff0000';
- $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
- $data['channel'] = ltrim( $handle, '@' );
- $data['channel_display'] = $handle;
- $data['video_id'] = $resolved_video_id;
- $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
- return $data;
- }
-
- if ( preg_match( '~youtube\.com/(?:c|user)/([A-Za-z0-9._-]+)(?:/.*)?$~i', $source_url, $matches ) ) {
- $channel_name = sanitize_text_field( $matches[1] );
- $resolved_video_id = mm_video_resolve_youtube_live_video_id( $source_url, '' );
- $data['platform'] = 'youtube';
- $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-youtube';
- $data['color'] = '#ff0000';
- $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
- $data['channel'] = $channel_name;
- $data['channel_display'] = '@' . ltrim( $channel_name, '@' );
- $data['video_id'] = $resolved_video_id;
- $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
- return $data;
- }
-
- if ( strpos( $source_url, 'youtube.com' ) !== false || strpos( $source_url, 'youtu.be' ) !== false ) {
- $data['platform'] = 'youtube';
- $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
- $data['icon'] = 'fab fa-youtube';
- $data['color'] = '#ff0000';
- $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
- }
-
- return $data;
-}
-
-function mm_video_get_livestream_item( $post_id ) {
- $stream_url = get_post_meta( $post_id, '_mm_livestream_url', true );
- $player_url = get_post_meta( $post_id, '_mm_livestream_player_url', true );
- $owner_meta = trim( get_post_meta( $post_id, '_mm_livestream_owner', true ) );
- $youtube_channel_id = get_post_meta( $post_id, '_mm_livestream_youtube_channel_id', true );
- $stream = mm_video_get_livestream_data( $stream_url, $player_url, $youtube_channel_id );
-
- if ( ! $stream || empty( $stream['profile_url'] ) ) {
- return false;
- }
-
- $stream_title = trim( get_the_title( $post_id ) );
- if ( empty( $stream_title ) || $stream_title === '(kein Titel)' || stripos( $stream_title, 'Automatisch gespeicherter Entwurf' ) !== false || stripos( $stream_title, 'Auto Draft' ) !== false ) {
- $stream_title = ! empty( $stream['channel_display'] ) ? $stream['channel_display'] : $stream['label'];
- }
-
- $stream_excerpt = has_excerpt( $post_id ) ? get_the_excerpt( $post_id ) : '';
- $owner = $owner_meta;
- if ( empty( $owner ) ) {
- $owner = ! empty( $stream['channel_display'] ) ? $stream['channel_display'] : $stream_title;
- }
-
- return array(
- 'post_id' => $post_id,
- 'owner' => $owner,
- 'title' => $stream_title,
- 'description' => $stream_excerpt,
- 'profile_url' => $stream['profile_url'],
- 'channel' => $stream['channel'],
- 'stream' => $stream,
- );
-}
-
-/**
- * Automatische YouTube-Live-Erkennung via @Handle (Optimiert)
- */
-
-/**
- * 1. Hilfsfunktion: Wandelt @Handle in eine Channel-ID um
- */
-function mm_get_channel_id_by_handle( $handle ) {
- $api_key = get_theme_mod( 'youtube_api_key' );
- if ( empty( $api_key ) || empty( $handle ) ) {
- return false;
- }
-
- // Handle normalisieren (ohne @)
- $handle = ltrim( $handle, '@' );
-
- // Cache für die Channel-ID (diese ändert sich nie, daher 30 Tage speichern)
- $cache_key = 'mm_id_for_' . $handle;
- $channel_id = get_transient( $cache_key );
- if ( false !== $channel_id ) {
- return $channel_id;
- }
-
- // Suche Kanal zum Handle
- $url = 'https://www.googleapis.com/youtube/v3/search?part=snippet&q=' . urlencode( '@' . $handle ) . '&type=channel&maxResults=1&key=' . $api_key;
-
- $response = wp_remote_get( $url );
- if ( is_wp_error( $response ) ) {
- return false;
- }
-
- $data = json_decode( wp_remote_retrieve_body( $response ) );
-
- if ( ! empty( $data->items[0]->snippet->channelId ) ) {
- $id = $data->items[0]->snippet->channelId;
- set_transient( $cache_key, $id, DAY_IN_SECONDS * 30 );
- return $id;
- }
-
- return false;
-}
-
-/**
- * 2. Hauptfunktion: Findet die Video-ID des aktuellen Livestreams
- */
-function mm_get_youtube_live_id_from_handle( $handle ) {
- $api_key = get_theme_mod( 'youtube_api_key' );
- if ( empty( $api_key ) ) {
- return false;
- }
-
- // 1. Kanal-ID zum Handle finden
- $channel_id = mm_get_channel_id_by_handle( $handle );
- if ( ! $channel_id ) {
- return false;
- }
-
- // 2. Aktuellen Livestream in diesem Kanal suchen
- $cache_key_live = 'mm_live_status_' . $channel_id;
- $live_id = get_transient( $cache_key_live );
- if ( false !== $live_id ) {
- return ( $live_id === 'none' ) ? false : $live_id;
- }
-
- // Wir suchen direkt nach dem Live-Event des Kanals
- $url = 'https://www.googleapis.com/youtube/v3/search?part=id&channelId=' . $channel_id . '&eventType=live&type=video&key=' . $api_key;
-
- $response = wp_remote_get( $url );
- if ( is_wp_error( $response ) ) {
- return false;
- }
-
- $data = json_decode( wp_remote_retrieve_body( $response ) );
-
- if ( ! empty( $data->items[0]->id->videoId ) ) {
- $video_id = $data->items[0]->id->videoId;
- set_transient( $cache_key_live, $video_id, 2 * MINUTE_IN_SECONDS ); // Nur 2 Min für schnellere Reaktion
- return $video_id;
- }
-
- // Wenn nicht live, 'none' speichern, um API-Anfragen zu drosseln
- set_transient( $cache_key_live, 'none', 2 * MINUTE_IN_SECONDS );
- return false;
-}
-
-/**
- * 3. Update für mm_video_get_livestream_groups - Hybrid-System
- * Zeigt ALLE konfigurierten Livestreams an:
- * - Livestream-Posts (Hauptmethode für mehrere Kanäle)
- * - Optional: Customizer @Handle (für einzelnen Hauptkanal)
- */
-function mm_video_get_livestream_groups() {
- $groups = array();
- $api_key = get_theme_mod( 'youtube_api_key', '' );
-
- // === METHODE 1: Livestream-Posts (IMMER abfragen) ===
- $posts = get_posts( array(
- 'post_type' => 'mm_livestream',
- 'post_status' => 'publish',
- 'posts_per_page' => -1,
- 'orderby' => 'ID',
- 'order' => 'ASC',
- ) );
-
- if ( ! empty( $posts ) ) {
- foreach ( $posts as $post ) {
- $profile_url = get_post_meta( $post->ID, '_mm_livestream_url', true );
- $stream_url = get_post_meta( $post->ID, '_mm_livestream_player_url', true );
- $owner = get_post_meta( $post->ID, '_mm_livestream_owner', true );
- $channel_id = '';
-
- // Versuche Channel-ID aus Profil-URL zu extrahieren (nur für YouTube)
- if ( ! empty( $profile_url ) && strpos( $profile_url, 'youtube.com' ) !== false ) {
- // Channel-URL mit UC-ID
- if ( preg_match( '~/channel/(UC[a-zA-Z0-9_-]+)~', $profile_url, $m ) ) {
- $channel_id = $m[1];
- }
- // @Handle -> über API auflösen
- elseif ( preg_match( '~/@([a-zA-Z0-9_.-]+)(?:/|$)~', $profile_url, $m ) && ! empty( $api_key ) ) {
- $channel_id = mm_get_channel_id_by_handle( $m[1] );
- }
- }
-
- // YouTube-Livestream: Prüfe ob live via API
- if ( ! empty( $channel_id ) && ! empty( $api_key ) ) {
- $cache_key_live = 'mm_live_status_' . $channel_id;
- $live_id = get_transient( $cache_key_live );
-
- if ( false === $live_id ) {
- $search_url = add_query_arg( array(
- 'part' => 'id',
- 'channelId' => $channel_id,
- 'eventType' => 'live',
- 'type' => 'video',
- 'key' => $api_key,
- ), 'https://www.googleapis.com/youtube/v3/search' );
-
- $response = wp_remote_get( $search_url, array( 'timeout' => 10 ) );
-
- if ( ! is_wp_error( $response ) ) {
- $data = json_decode( wp_remote_retrieve_body( $response ), true );
-
- if ( isset( $data['items'][0]['id']['videoId'] ) ) {
- $live_id = $data['items'][0]['id']['videoId'];
- set_transient( $cache_key_live, $live_id, 2 * MINUTE_IN_SECONDS );
- } else {
- set_transient( $cache_key_live, 'none', 2 * MINUTE_IN_SECONDS );
- $live_id = false;
- }
- } else {
- $live_id = false;
- }
- } elseif ( $live_id === 'none' ) {
- $live_id = false;
- }
-
- // Nur hinzufügen wenn live
- if ( $live_id && $live_id !== 'none' ) {
- $groups[] = array(
- 'title' => $post->post_title,
- 'platform' => 'youtube',
- 'yt_id' => $live_id,
- 'handle' => $profile_url,
- );
- }
- }
- // Andere Plattformen (Twitch, etc.): Prüfe zuerst direkte Stream-URL, sonst Profil-URL
- elseif ( ! empty( $stream_url ) || ! empty( $profile_url ) ) {
- $use_url = ! empty( $stream_url ) ? $stream_url : $profile_url;
- $embed_url = mm_video_get_embed_url( $use_url );
-
- // Nur Live-Streams anzeigen (Twitch muss live sein)
- if ( $embed_url ) {
- $platform = mm_video_get_type( $use_url );
-
- if ( $platform !== 'twitch' ) {
- continue;
- }
-
- $channel = mm_twitch_get_channel_from_url( $use_url );
- if ( ! mm_twitch_is_live( $channel ) ) {
- continue;
- }
-
- $groups[] = array(
- 'title' => $post->post_title,
- 'platform' => $platform,
- 'embed_url' => $embed_url,
- 'profile_url' => $profile_url,
- 'owner' => $owner,
- );
- }
- }
- }
- }
-
- return $groups;
-}
-
-/**
- * Debug-Informationen für Livestream-System
- */
-function mm_video_get_api_livestream_debug() {
- if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) {
- return array();
- }
-
- $api_key = get_theme_mod( 'youtube_api_key', '' );
-
- // Zähle Livestream-Posts
- $posts_count = wp_count_posts( 'mm_livestream' );
- $published_posts = isset( $posts_count->publish ) ? $posts_count->publish : 0;
-
- // === Fall 1: Keine Konfiguration ===
- if ( $published_posts === 0 ) {
- return array(
- 'status' => 'error',
- 'message' => 'Keine Livestream-Posts gefunden',
- 'hint' => 'Erstelle Livestream-Posts über "Livestreams → Neu"',
- );
- }
-
- // === Fall 2: API Key fehlt (nur Warnung) ===
- if ( empty( $api_key ) ) {
- return array(
- 'status' => 'warning',
- 'message' => 'Kein YouTube API Key',
- 'hint' => 'API Key wird für YouTube Live-Erkennung benötigt. Twitch-Streams funktionieren ohne.',
- 'posts_count' => $published_posts,
- );
- }
-
- // === Fall 3: Alles OK ===
- return array(
- 'status' => 'ok',
- 'posts_count' => $published_posts,
- 'api_key_length' => strlen( $api_key ),
- );
-}
-
-function mm_video_get_hidden_livestream_count() {
- // Für API-basiertes System nicht relevant
- return 0;
-}
-
-function mm_video_get_livestream_debug_rows() {
- // Ersetzt durch mm_video_get_api_livestream_debug()
- return array();
-}
-
-function mm_video_render_livestream_group( $group, $index = 0 ) {
- // Format 1: Neues API-YouTube-Format mit yt_id (Customizer oder Posts)
- if ( isset( $group['yt_id'] ) && isset( $group['platform'] ) && $group['platform'] === 'youtube' ) {
- $video_id = trim( (string) $group['yt_id'] );
- $title = isset( $group['title'] ) ? $group['title'] : __( 'Live', 'minecraft-modern-theme' );
- $handle = isset( $group['handle'] ) ? trim( (string) $group['handle'] ) : '';
-
- if ( empty( $video_id ) ) {
- return '';
- }
-
- $embed_url = 'https://www.youtube-nocookie.com/embed/' . rawurlencode( $video_id ) . '?autoplay=0&rel=0&modestbranding=1&playsinline=1';
-
- ob_start();
- ?>
-
- array( 'icon' => 'fab fa-twitch', 'color' => '#9146ff' ),
- 'vimeo' => array( 'icon' => 'fab fa-vimeo-v', 'color' => '#1ab7ea' ),
- 'youtube' => array( 'icon' => 'fab fa-youtube', 'color' => '#ff0000' ),
- );
- $icon = isset( $platform_icons[$platform] ) ? $platform_icons[$platform]['icon'] : 'fas fa-play';
-
- ob_start();
- ?>
-
- 1
- ? __( 'Mehrere aktive oder gespeicherte Streams dieses Streamers lassen sich hier direkt umschalten.', 'minecraft-modern-theme' )
- : __( 'Der Bereich wird automatisch aus deinem Profil-Link erzeugt und oberhalb der Videos eingebunden.', 'minecraft-modern-theme' ) );
-
- ob_start();
- ?>
-
-
-
-
-
-
-
-
- 1 ) : ?>
-
-
-
-
-
- 1 /
-
-
-
-
-
-
- $item ) : ?>
-
-
-
-
-
-
-
-
-
-
-
- ' . __('Ungültige Video-URL.', 'minecraft-modern-theme') . '
';
-
- $type = mm_video_get_type( $url );
- $title = isset($args['title']) ? esc_attr($args['title']) : __('Video', 'minecraft-modern-theme');
-
- if ( $type === 'mp4' ) {
- return ''
- . ''
- . ''
- . __('Dein Browser unterstützt kein HTML5-Video.', 'minecraft-modern-theme')
- . '
';
- }
-
- return ''
- . '
';
-}
-
-
-// --- 5. Shortcode: [mm_video url="..."] ---
-// Beispiele:
-// [mm_video url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
-// [mm_video url="https://vimeo.com/123456789" title="Mein Video"]
-function mm_video_shortcode( $atts ) {
- $a = shortcode_atts( array(
- 'url' => '',
- 'title' => __('Video', 'minecraft-modern-theme'),
- ), $atts );
-
- if ( empty($a['url']) ) return '';
- return mm_video_render_embed( $a['url'], $a );
-}
-add_shortcode( 'mm_video', 'mm_video_shortcode' );
-
-
-// --- 6. Template-Loader für Video-Archiv und Galerie-Seite ---
-function mm_video_template_loader( $template ) {
- // Archiv (domain.de/videos/)
- if ( is_post_type_archive('mm_video') ) {
- $t = get_template_directory() . '/archive-video.php';
- if ( file_exists($t) ) return $t;
- }
- // Seite mit dem Slug "videos"
- if ( is_page() ) {
- $obj = get_queried_object();
- if ( $obj && $obj->post_name === 'videos' ) {
- $t = get_template_directory() . '/archive-video.php';
- if ( file_exists($t) ) return $t;
- }
- }
- return $template;
-}
-add_filter( 'template_include', 'mm_video_template_loader' );
-
-
-// --- 7. "Videos"-Seite automatisch anlegen ---
-function mm_video_create_page() {
- if ( ! get_page_by_path('videos') ) {
- wp_insert_post( array(
- 'post_title' => __('Videos', 'minecraft-modern-theme'),
- 'post_name' => 'videos',
- 'post_status' => 'publish',
- 'post_type' => 'page',
- 'post_author' => 1,
- ) );
- }
-}
-add_action( 'after_switch_theme', 'mm_video_create_page' );
-// Auch beim Speichern im Customizer anlegen
-add_action( 'customize_save_after', 'mm_video_create_page' );
-
-
-// =============================================================================
-// === BEWERBUNGSFORMULAR ======================================================
-// =============================================================================
-
-// --- 1. Customizer: Ein/Ausschalten ---
-function mm_bewerbung_customizer( $wp_customize ) {
- $wp_customize->add_section( 'mm_bewerbung_section', array(
- 'title' => __( 'Bewerbungsformular', 'minecraft-modern-theme' ),
- 'priority' => 80,
- ) );
-
- $wp_customize->add_setting( 'mm_bewerbung_enabled', array(
- 'default' => false,
- 'sanitize_callback' => 'wp_validate_boolean',
- ) );
- $wp_customize->add_control( 'mm_bewerbung_enabled', array(
- 'label' => __( 'Bewerbungsformular aktivieren', 'minecraft-modern-theme' ),
- 'description' => __( 'Schaltet die Bewerbungsseite und das Formular ein.', 'minecraft-modern-theme' ),
- 'section' => 'mm_bewerbung_section',
- 'type' => 'checkbox',
- ) );
-
- $wp_customize->add_setting( 'mm_bewerbung_title', array(
- 'default' => __( 'Bewirb dich bei uns!', 'minecraft-modern-theme' ),
- 'sanitize_callback' => 'sanitize_text_field',
- ) );
- $wp_customize->add_control( 'mm_bewerbung_title', array(
- 'label' => __( 'Titel der Bewerbungsseite', 'minecraft-modern-theme' ),
- 'section' => 'mm_bewerbung_section',
- 'type' => 'text',
- ) );
-
- $wp_customize->add_setting( 'mm_bewerbung_desc', array(
- 'default' => __( 'Du möchtest Teil unseres Teams werden? Füll das Formular aus und wir melden uns bei dir.', 'minecraft-modern-theme' ),
- 'sanitize_callback' => 'sanitize_textarea_field',
- ) );
- $wp_customize->add_control( 'mm_bewerbung_desc', array(
- 'label' => __( 'Beschreibungstext', 'minecraft-modern-theme' ),
- 'section' => 'mm_bewerbung_section',
- 'type' => 'textarea',
- ) );
-
- $wp_customize->add_setting( 'mm_bewerbung_success_msg', array(
- 'default' => __( 'Deine Bewerbung wurde erfolgreich eingereicht! Wir melden uns so bald wie möglich bei dir.', 'minecraft-modern-theme' ),
- 'sanitize_callback' => 'sanitize_textarea_field',
- ) );
- $wp_customize->add_control( 'mm_bewerbung_success_msg', array(
- 'label' => __( 'Erfolgsmeldung nach dem Absenden', 'minecraft-modern-theme' ),
- 'section' => 'mm_bewerbung_section',
- 'type' => 'textarea',
- ) );
-
- $wp_customize->add_setting( 'mm_bewerbung_min_alter', array(
- 'default' => 14,
- 'sanitize_callback' => 'absint',
- ) );
- $wp_customize->add_control( 'mm_bewerbung_min_alter', array(
- 'label' => __( 'Mindestalter', 'minecraft-modern-theme' ),
- 'description' => __( 'Bewerbungen unter diesem Alter werden abgelehnt. 0 = kein Limit.', 'minecraft-modern-theme' ),
- 'section' => 'mm_bewerbung_section',
- 'type' => 'number',
- 'input_attrs' => array( 'min' => 0, 'max' => 99 ),
- ) );
-}
-add_action( 'customize_register', 'mm_bewerbung_customizer' );
-
-
-// --- 2. Custom Post Type: Bewerbung ---
-function mm_register_bewerbung_cpt() {
- if ( ! get_theme_mod( 'mm_bewerbung_enabled', false ) ) return;
-
- register_post_type( 'mm_bewerbung', array(
- 'labels' => array(
- 'name' => __( 'Bewerbungen', 'minecraft-modern-theme' ),
- 'singular_name' => __( 'Bewerbung', 'minecraft-modern-theme' ),
- 'all_items' => __( 'Alle Bewerbungen', 'minecraft-modern-theme' ),
- 'menu_name' => __( 'Bewerbungen', 'minecraft-modern-theme' ),
- ),
- 'public' => false,
- 'show_ui' => true,
- 'show_in_menu' => true,
- 'menu_icon' => 'dashicons-clipboard',
- 'menu_position' => 8,
- 'supports' => array( 'title' ),
- 'capabilities' => array(
- 'create_posts' => 'do_not_allow', // Nur über Frontend erstellbar
- ),
- 'map_meta_cap' => true,
- ) );
-}
-add_action( 'init', 'mm_register_bewerbung_cpt' );
-
-
-// --- 3. Admin: Meta-Box für Bewerbungs-Details ---
-function mm_bewerbung_meta_box() {
- add_meta_box(
- 'mm_bewerbung_details',
- __( 'Bewerbungs-Details', 'minecraft-modern-theme' ),
- 'mm_bewerbung_meta_box_html',
- 'mm_bewerbung', 'normal', 'high'
- );
- add_meta_box(
- 'mm_bewerbung_status_box',
- __( 'Status', 'minecraft-modern-theme' ),
- 'mm_bewerbung_status_box_html',
- 'mm_bewerbung', 'side', 'high'
- );
-}
-add_action( 'add_meta_boxes', 'mm_bewerbung_meta_box' );
-
-function mm_bewerbung_meta_box_html( $post ) {
- $fields = array(
- '_mm_bew_mc_name' => __( 'Minecraft Username', 'minecraft-modern-theme' ),
- '_mm_bew_discord' => __( 'Discord Username', 'minecraft-modern-theme' ),
- '_mm_bew_alter' => __( 'Alter', 'minecraft-modern-theme' ),
- '_mm_bew_warum' => __( 'Warum möchtest du mitspielen?', 'minecraft-modern-theme' ),
- '_mm_bew_erfahrung' => __( 'Erfahrung / Vorstellung', 'minecraft-modern-theme' ),
- '_mm_bew_datum' => __( 'Eingereicht am', 'minecraft-modern-theme' ),
- '_mm_bew_ip' => __( 'IP-Adresse', 'minecraft-modern-theme' ),
- );
- echo '';
-}
-
-function mm_bewerbung_status_box_html( $post ) {
- $status = get_post_meta( $post->ID, '_mm_bew_status', true ) ?: 'neu';
- wp_nonce_field( 'mm_bew_status_save', 'mm_bew_status_nonce' );
- $options = array(
- 'neu' => array( 'label' => __('Neu','minecraft-modern-theme'), 'color' => '#0073aa' ),
- 'in_pruef' => array( 'label' => __('In Prüfung','minecraft-modern-theme'), 'color' => '#f0ad4e' ),
- 'angenommen' => array( 'label' => __('Angenommen','minecraft-modern-theme'), 'color' => '#46b450' ),
- 'abgelehnt' => array( 'label' => __('Abgelehnt','minecraft-modern-theme'), 'color' => '#dc3232' ),
- );
- echo '';
- foreach ( $options as $val => $opt ) {
- $sel = selected( $status, $val, false );
- echo '' . esc_html($opt['label']) . ' ';
- }
- echo ' ';
- $cur = isset($options[$status]) ? $options[$status] : $options['neu'];
- echo '' . esc_html($cur['label']) . '
';
-
- // Admin-Notiz
- $notiz = get_post_meta( $post->ID, '_mm_bew_notiz', true );
- echo ' ';
- echo '' . __('Interne Notiz:', 'minecraft-modern-theme') . ' ';
- echo '' . esc_textarea($notiz) . ' ';
-}
-
-function mm_bewerbung_save_status( $post_id ) {
- if ( ! isset($_POST['mm_bew_status_nonce']) || ! wp_verify_nonce($_POST['mm_bew_status_nonce'], 'mm_bew_status_save') ) return;
- if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return;
- if ( ! current_user_can('edit_post', $post_id) ) return;
- if ( isset($_POST['mm_bew_status']) ) {
- update_post_meta( $post_id, '_mm_bew_status', sanitize_text_field($_POST['mm_bew_status']) );
- }
- if ( isset($_POST['mm_bew_notiz']) ) {
- update_post_meta( $post_id, '_mm_bew_notiz', sanitize_textarea_field($_POST['mm_bew_notiz']) );
- }
-}
-add_action( 'save_post', 'mm_bewerbung_save_status' );
-
-
-// --- 4. Admin-Spalten in der Bewerbungsliste ---
-function mm_bewerbung_columns( $cols ) {
- return array(
- 'cb' => $cols['cb'],
- 'title' => __( 'Name', 'minecraft-modern-theme' ),
- 'mc_name' => __( 'Minecraft', 'minecraft-modern-theme' ),
- 'discord' => __( 'Discord', 'minecraft-modern-theme' ),
- 'alter' => __( 'Alter', 'minecraft-modern-theme' ),
- 'status' => __( 'Status', 'minecraft-modern-theme' ),
- 'datum' => __( 'Eingereicht', 'minecraft-modern-theme' ),
- );
-}
-add_filter( 'manage_mm_bewerbung_posts_columns', 'mm_bewerbung_columns' );
-
-function mm_bewerbung_column_content( $col, $post_id ) {
- $status_colors = array(
- 'neu' => '#0073aa',
- 'in_pruef' => '#f0ad4e',
- 'angenommen' => '#46b450',
- 'abgelehnt' => '#dc3232',
- );
- $status_labels = array(
- 'neu' => __('Neu','minecraft-modern-theme'),
- 'in_pruef' => __('In Prüfung','minecraft-modern-theme'),
- 'angenommen' => __('Angenommen','minecraft-modern-theme'),
- 'abgelehnt' => __('Abgelehnt','minecraft-modern-theme'),
- );
- switch ($col) {
- case 'mc_name':
- $mc = get_post_meta($post_id, '_mm_bew_mc_name', true);
- if ($mc) {
- echo '';
- echo '
';
- echo esc_html($mc) . '
';
- }
- break;
- case 'discord':
- echo esc_html( get_post_meta($post_id, '_mm_bew_discord', true) );
- break;
- case 'alter':
- echo esc_html( get_post_meta($post_id, '_mm_bew_alter', true) );
- break;
- case 'status':
- $s = get_post_meta($post_id, '_mm_bew_status', true) ?: 'neu';
- $color = isset($status_colors[$s]) ? $status_colors[$s] : '#0073aa';
- $label = isset($status_labels[$s]) ? $status_labels[$s] : $s;
- echo '' . esc_html($label) . ' ';
- break;
- case 'datum':
- echo esc_html( get_post_meta($post_id, '_mm_bew_datum', true) );
- break;
- }
-}
-add_action( 'manage_mm_bewerbung_posts_custom_column', 'mm_bewerbung_column_content', 10, 2 );
-
-
-// --- 5. AJAX: Formular absenden ---
-add_action( 'wp_ajax_mm_submit_bewerbung', 'mm_submit_bewerbung' );
-add_action( 'wp_ajax_nopriv_mm_submit_bewerbung', 'mm_submit_bewerbung' );
-
-function mm_submit_bewerbung() {
- // Nonce prüfen
- if ( ! isset($_POST['nonce']) || ! wp_verify_nonce($_POST['nonce'], 'mm_bewerbung_nonce') ) {
- wp_send_json_error( array('msg' => __('Sicherheitsfehler. Bitte Seite neu laden.', 'minecraft-modern-theme')) );
- }
-
- // Felder validieren
- $mc_name = sanitize_text_field( $_POST['mc_name'] ?? '' );
- $discord = sanitize_text_field( $_POST['discord'] ?? '' );
- $alter_raw = absint( $_POST['alter'] ?? 0 );
- $warum = sanitize_textarea_field( $_POST['warum'] ?? '' );
- $erfahrung = sanitize_textarea_field( $_POST['erfahrung'] ?? '' );
-
- $errors = array();
- if ( empty($mc_name) ) $errors[] = __('Minecraft Username ist erforderlich.', 'minecraft-modern-theme');
- if ( empty($discord) ) $errors[] = __('Discord Username ist erforderlich.', 'minecraft-modern-theme');
- if ( $alter_raw < 1 ) $errors[] = __('Bitte gib dein Alter an.', 'minecraft-modern-theme');
- if ( empty($warum) ) $errors[] = __('Bitte erkläre warum du mitspielen möchtest.', 'minecraft-modern-theme');
- if ( empty($erfahrung) ) $errors[] = __('Bitte stell dich kurz vor.', 'minecraft-modern-theme');
-
- // Mindestalter prüfen
- $min_alter = absint( get_theme_mod('mm_bewerbung_min_alter', 14) );
- if ( $min_alter > 0 && $alter_raw < $min_alter ) {
- $errors[] = sprintf( __('Du musst mindestens %d Jahre alt sein.', 'minecraft-modern-theme'), $min_alter );
- }
-
- // Doppelbewerbung prüfen (gleicher MC-Name in den letzten 30 Tagen)
- $existing = get_posts( array(
- 'post_type' => 'mm_bewerbung',
- 'post_status' => 'publish',
- 'meta_query' => array(
- array( 'key' => '_mm_bew_mc_name', 'value' => $mc_name, 'compare' => '=' ),
- ),
- 'date_query' => array(
- array( 'after' => '30 days ago' ),
- ),
- 'numberposts' => 1,
- ) );
- if ( $existing ) {
- $errors[] = __('Mit diesem Minecraft-Namen wurde in den letzten 30 Tagen bereits eine Bewerbung eingereicht.', 'minecraft-modern-theme');
- }
-
- if ( ! empty($errors) ) {
- wp_send_json_error( array('msg' => implode(' ', $errors)) );
- }
-
- // Bewerbung als Post speichern
- $post_id = wp_insert_post( array(
- 'post_type' => 'mm_bewerbung',
- 'post_status' => 'publish',
- 'post_title' => $mc_name . ' – ' . date_i18n('d.m.Y H:i'),
- ) );
-
- if ( is_wp_error($post_id) ) {
- wp_send_json_error( array('msg' => __('Fehler beim Speichern. Bitte versuche es erneut.', 'minecraft-modern-theme')) );
- }
-
- update_post_meta( $post_id, '_mm_bew_mc_name', $mc_name );
- update_post_meta( $post_id, '_mm_bew_discord', $discord );
- update_post_meta( $post_id, '_mm_bew_alter', $alter_raw );
- update_post_meta( $post_id, '_mm_bew_warum', $warum );
- update_post_meta( $post_id, '_mm_bew_erfahrung', $erfahrung );
- update_post_meta( $post_id, '_mm_bew_status', 'neu' );
- update_post_meta( $post_id, '_mm_bew_datum', date_i18n('d.m.Y H:i:s') );
- update_post_meta( $post_id, '_mm_bew_ip', sanitize_text_field( $_SERVER['REMOTE_ADDR'] ?? '' ) );
-
- $success_msg = get_theme_mod( 'mm_bewerbung_success_msg', __('Deine Bewerbung wurde erfolgreich eingereicht! Wir melden uns so bald wie möglich bei dir.', 'minecraft-modern-theme') );
- wp_send_json_success( array('msg' => $success_msg) );
-}
-
-
-// --- 6. Template-Loader ---
-function mm_bewerbung_template_loader( $template ) {
- if ( ! get_theme_mod('mm_bewerbung_enabled', false) ) return $template;
- if ( is_page('bewerbung') ) {
- $t = get_template_directory() . '/page-bewerbung.php';
- if ( file_exists($t) ) return $t;
- }
- return $template;
-}
-add_filter( 'template_include', 'mm_bewerbung_template_loader' );
-
-
-// --- 7. Seite automatisch anlegen ---
-function mm_bewerbung_create_page() {
- if ( ! get_theme_mod('mm_bewerbung_enabled', false) ) return;
- if ( ! get_page_by_path('bewerbung') ) {
- wp_insert_post( array(
- 'post_title' => __('Bewerbung', 'minecraft-modern-theme'),
- 'post_name' => 'bewerbung',
- 'post_status' => 'publish',
- 'post_type' => 'page',
- 'post_author' => 1,
- ) );
- }
-}
+ 9999,
+ 'width' => 9999,
+ 'flex-height' => true,
+ 'flex-width' => true,
+ 'header_text' => array( 'site-title', 'site-description' ),
+ ) );
+
+ add_theme_support( 'custom-background' );
+
+ register_nav_menus( array(
+ 'primary' => __( 'Hauptmenü', 'minecraft-modern-theme' ),
+ 'footer' => __( 'Footer-Menü', 'minecraft-modern-theme' ),
+ ) );
+
+ // FIX: 'script' und 'style' ergänzt für sauberes HTML5-Output-Format
+ add_theme_support( 'html5', array(
+ 'search-form', 'comment-form', 'comment-list',
+ 'gallery', 'caption', 'script', 'style',
+ ) );
+}
+add_action( 'after_setup_theme', 'minecraft_modern_setup' );
+
+
+// === Styles & Scripts laden ===
+function minecraft_modern_scripts() {
+ // Haupt-Stylesheet
+ wp_enqueue_style( 'minecraft-modern-style', get_stylesheet_uri(), array(), filemtime( get_stylesheet_directory() . '/style.css' ) );
+
+ // Swiper.js CSS
+ wp_enqueue_style( 'swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.css' );
+
+ // Header-Scroll + Suche Toggle
+ wp_enqueue_script(
+ 'minecraft-modern-header-script',
+ get_template_directory_uri() . '/js/header-scroll.js',
+ array(),
+ filemtime( get_template_directory() . '/js/header-scroll.js' ),
+ true
+ );
+
+ // Navigation (Dropdown + Außenklick-Schließen)
+ wp_enqueue_script(
+ 'minecraft-navigation',
+ get_template_directory_uri() . '/js/navigation.js',
+ array(),
+ '1.1',
+ true
+ );
+
+ // Swiper.js
+ wp_enqueue_script(
+ 'swiper-js',
+ 'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.js',
+ array(),
+ '8.0.0',
+ true
+ );
+
+ // Slider-Init
+ wp_enqueue_script(
+ 'minecraft-modern-slider-script',
+ get_template_directory_uri() . '/js/slider-init.js',
+ array( 'swiper-js' ),
+ '1.1',
+ true
+ );
+
+ // Theme Toggle
+ wp_enqueue_script(
+ 'theme-toggle-script',
+ get_template_directory_uri() . '/js/theme-toggle.js',
+ array(),
+ '1.1',
+ true
+ );
+
+ // BUG-FIX: post_type_exists('faq') prüft nur ob der CPT registriert ist, nicht ob wir
+ // auf einer FAQ-Seite sind – das Script wurde damit seitenübergreifend geladen.
+ // Jetzt: nur auf FAQ-Archiv-, Einzel- und Seiten mit FAQ-Template laden.
+ if ( post_type_exists( 'faq' ) && ( is_post_type_archive( 'faq' ) || is_singular( 'faq' ) || is_page( 'FAQ' ) || is_page( 'faq' ) ) ) {
+ wp_enqueue_script(
+ 'faq-accordion-script',
+ get_template_directory_uri() . '/js/faq-accordion.js',
+ array(),
+ '1.0',
+ true
+ );
+ }
+
+ // FIX: 'loop' ergänzt – war in slider-init.js als sliderSettings.loop referenziert, aber nie übergeben
+ wp_localize_script(
+ 'minecraft-modern-slider-script',
+ 'sliderSettings',
+ array(
+ 'hideArrows' => get_theme_mod( 'slider_hide_arrows', false ) ? '1' : '0',
+ 'hidePagination' => get_theme_mod( 'slider_hide_pagination', false ) ? '1' : '0',
+ 'effect' => get_theme_mod( 'slider_effect', 'fade' ),
+ 'direction' => get_theme_mod( 'slider_direction', 'horizontal' ),
+ 'defaultMode' => get_theme_mod( 'default_theme_mode', 'dark' ),
+ 'loop' => get_theme_mod( 'slider_loop', true ) ? '1' : '0',
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
+ )
+ );
+
+ wp_localize_script(
+ 'minecraft-modern-header-script',
+ 'headerSettings',
+ array(
+ 'isCustomizer' => is_customize_preview(),
+ )
+ );
+}
+add_action( 'wp_enqueue_scripts', 'minecraft_modern_scripts' );
+
+
+// FIX: Scroll-to-Top via Customizer ein-/ausblendbar
+function minecraft_modern_scroll_to_top_css() {
+ if ( ! get_theme_mod( 'show_scroll_to_top', true ) ) {
+ wp_add_inline_style( 'minecraft-modern-style', '#scroll-to-top { display: none !important; }' );
+ }
+}
+add_action( 'wp_enqueue_scripts', 'minecraft_modern_scroll_to_top_css', 25 );
+
+
+// === Customizer-Datei laden ===
+require get_template_directory() . '/inc/customizer.php';
+
+
+
+
+
+// =========================================================================
+// INTELLIGENTER SUPPORT-ASSISTENT
+// =========================================================================
+// Ausgelagert in /inc/assistant-widget.php
+require get_template_directory() . '/inc/assistant-widget.php';
+
+
+// === Theme-Updater-Datei laden ===
+require get_template_directory() . '/inc/theme-updater.php';
+
+
+// === Footer-Widgets registrieren ===
+function minecraft_modern_footer_widgets() {
+ register_sidebar( array(
+ 'name' => __( 'Footer Links', 'minecraft-modern-theme' ),
+ 'id' => 'footer-left',
+ 'description' => __( 'Widget-Bereich links im Footer.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+ for ( $i = 1; $i <= 3; $i++ ) {
+ register_sidebar( array(
+ 'name' => sprintf( __( 'Footer Spalte %d', 'minecraft-modern-theme' ), $i ),
+ 'id' => 'footer-' . $i,
+ 'description' => sprintf( __( 'Widget für die %d. Spalte im Footer.', 'minecraft-modern-theme' ), $i ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+ }
+ register_sidebar( array(
+ 'name' => __( 'Footer Rechts', 'minecraft-modern-theme' ),
+ 'id' => 'footer-right',
+ 'description' => __( 'Widget-Bereich rechts im Footer.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+}
+add_action( 'widgets_init', 'minecraft_modern_footer_widgets' );
+
+
+// === Homepage Sidebar registrieren ===
+function minecraft_modern_homepage_sidebar() {
+ register_sidebar( array(
+ 'name' => __( 'Startseiten Sidebar - Oben', 'minecraft-modern-theme' ),
+ 'id' => 'homepage-sidebar-top',
+ 'description' => __( 'Widget-Bereich oben in der Sidebar (z.B. für wichtige Infos).', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+ register_sidebar( array(
+ 'name' => __( 'Startseiten Sidebar - Mitte 1', 'minecraft-modern-theme' ),
+ 'id' => 'homepage-sidebar-middle-1',
+ 'description' => __( 'Widget-Bereich in der Mitte der Sidebar.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+ register_sidebar( array(
+ 'name' => __( 'Startseiten Sidebar - Mitte 2', 'minecraft-modern-theme' ),
+ 'id' => 'homepage-sidebar-middle-2',
+ 'description' => __( 'Zweiter Widget-Bereich in der Mitte der Sidebar.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+ register_sidebar( array(
+ 'name' => __( 'Startseiten Sidebar - Unten', 'minecraft-modern-theme' ),
+ 'id' => 'homepage-sidebar-bottom',
+ 'description' => __( 'Widget-Bereich unten in der Sidebar.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+ register_sidebar( array(
+ 'name' => __( 'Startseiten Sidebar - Extra', 'minecraft-modern-theme' ),
+ 'id' => 'homepage-sidebar-extra',
+ 'description' => __( 'Zusätzlicher Widget-Bereich für spezielle Inhalte.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'after_widget' => '
',
+ 'before_title' => '',
+ ) );
+}
+add_action( 'widgets_init', 'minecraft_modern_homepage_sidebar' );
+
+
+/**
+ * Rendert alle Sidebar-Sections der Homepage.
+ * Ersetzt den doppelten Sidebar-Code in front-page.php.
+ */
+function minecraft_modern_render_sidebar_sections() {
+ $sections = array(
+ 'homepage-sidebar-top',
+ 'homepage-sidebar-middle-1',
+ 'homepage-sidebar-middle-2',
+ 'homepage-sidebar-bottom',
+ 'homepage-sidebar-extra',
+ );
+ $classes = array(
+ 'homepage-sidebar-top' => 'sidebar-top',
+ 'homepage-sidebar-middle-1' => 'sidebar-middle-1',
+ 'homepage-sidebar-middle-2' => 'sidebar-middle-2',
+ 'homepage-sidebar-bottom' => 'sidebar-bottom',
+ 'homepage-sidebar-extra' => 'sidebar-extra',
+ );
+ foreach ( $sections as $sidebar_id ) {
+ if ( is_active_sidebar( $sidebar_id ) ) {
+ $class = isset( $classes[ $sidebar_id ] ) ? ' ' . $classes[ $sidebar_id ] : '';
+ echo '';
+ }
+ }
+}
+
+
+// === FAQ Custom Post Type & Taxonomy ===
+function create_faq_post_type() {
+ if ( get_theme_mod( 'faq_enabled', true ) ) {
+ register_post_type( 'faq', array(
+ 'labels' => array(
+ 'name' => __( 'FAQs', 'minecraft-modern-theme' ),
+ 'singular_name' => __( 'FAQ', 'minecraft-modern-theme' ),
+ 'add_new' => __( 'Neue FAQ hinzufügen', 'minecraft-modern-theme' ),
+ 'add_new_item' => __( 'Neue FAQ hinzufügen', 'minecraft-modern-theme' ),
+ 'edit_item' => __( 'FAQ bearbeiten', 'minecraft-modern-theme' ),
+ 'new_item' => __( 'Neue FAQ', 'minecraft-modern-theme' ),
+ 'view_item' => __( 'FAQ ansehen', 'minecraft-modern-theme' ),
+ 'search_items' => __( 'FAQs durchsuchen', 'minecraft-modern-theme' ),
+ 'not_found' => __( 'Keine FAQs gefunden', 'minecraft-modern-theme' ),
+ 'not_found_in_trash' => __( 'Keine FAQs im Papierkorb gefunden', 'minecraft-modern-theme' ),
+ 'all_items' => __( 'Alle FAQs', 'minecraft-modern-theme' ),
+ ),
+ 'public' => true,
+ 'has_archive' => true,
+ 'menu_icon' => 'dashicons-format-chat',
+ 'supports' => array( 'title', 'editor', 'page-attributes' ),
+ 'rewrite' => array( 'slug' => 'faq' ),
+ 'show_in_rest' => true,
+ ) );
+ register_taxonomy( 'faq_category', 'faq', array(
+ 'label' => __( 'FAQ Kategorien', 'minecraft-modern-theme' ),
+ 'rewrite' => array( 'slug' => 'faq-kategorie' ),
+ 'hierarchical' => true,
+ 'show_in_rest' => true,
+ ) );
+ }
+}
+add_action( 'init', 'create_faq_post_type' );
+
+
+// =============================================================================
+// Automatische "Home" Seitenerstellung
+// =============================================================================
+
+function set_static_front_page_automatically() {
+ if ( 'page' !== get_option( 'show_on_front' ) ) {
+ // BUG-FIX: get_page_by_title() ist seit WP 6.2 deprecated. Ersetzt durch WP_Query.
+ $home_query = new WP_Query( array(
+ 'post_type' => 'page',
+ 'title' => 'Home',
+ 'post_status' => 'publish',
+ 'posts_per_page' => 1,
+ 'no_found_rows' => true,
+ 'update_post_meta_cache' => false,
+ 'update_post_term_cache' => false,
+ ) );
+ $home_page = $home_query->have_posts() ? $home_query->posts[0] : null;
+ if ( ! $home_page ) {
+ $home_page_id = wp_insert_post( array(
+ 'post_title' => 'Home',
+ 'post_content' => 'Diese Seite dient als statische Startseite.',
+ 'post_status' => 'publish',
+ 'post_type' => 'page',
+ 'post_author' => 1,
+ ) );
+ } else {
+ $home_page_id = $home_page->ID;
+ }
+ if ( $home_page_id ) {
+ update_option( 'show_on_front', 'page' );
+ update_option( 'page_on_front', $home_page_id );
+ }
+ }
+}
+add_action( 'after_switch_theme', 'set_static_front_page_automatically' );
+
+
+function add_home_body_class( $classes ) {
+ if ( is_front_page() && ! get_theme_mod( 'show_home_title', true ) ) {
+ $classes[] = 'home-title-hidden';
+ }
+ return $classes;
+}
+add_filter( 'body_class', 'add_home_body_class' );
+
+
+// =============================================================================
+// FAQ-Seitenerstellung
+// =============================================================================
+
+function create_faq_page_automatically() {
+ if ( ! get_theme_mod( 'faq_enabled', true ) ) return;
+
+ // BUG-FIX: get_page_by_title() ist seit WP 6.2 deprecated. Ersetzt durch WP_Query.
+ $faq_query = new WP_Query( array(
+ 'post_type' => 'page',
+ 'title' => 'FAQ',
+ 'post_status' => 'publish',
+ 'posts_per_page' => 1,
+ 'no_found_rows' => true,
+ 'update_post_meta_cache' => false,
+ 'update_post_term_cache' => false,
+ ) );
+ if ( $faq_query->have_posts() ) return;
+
+ wp_insert_post( array(
+ 'post_title' => 'FAQ',
+ 'post_content' => 'Diese Seite zeigt alle FAQs an. Der Inhalt wird automatisch generiert.',
+ 'post_status' => 'publish',
+ 'post_type' => 'page',
+ 'post_author' => 1,
+ ) );
+}
+add_action( 'customize_save_after', 'create_faq_page_automatically' );
+
+
+function load_faq_page_template( $template ) {
+ if ( get_theme_mod( 'faq_enabled', true ) && is_page() ) {
+ global $post;
+ if ( $post && $post->post_title === 'FAQ' ) {
+ return get_template_directory() . '/archive-faq.php';
+ }
+ }
+ return $template;
+}
+add_filter( 'template_include', 'load_faq_page_template' );
+
+
+// =========================================================================
+// CUSTOM LOGIN FUNCTIONS
+// =========================================================================
+
+function minecraft_modern_login_assets() {
+ wp_enqueue_style( 'minecraft-modern-login-style', get_template_directory_uri() . '/css/login-style.css' );
+ wp_enqueue_style( 'font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css' );
+
+ // FIX: login-slider.js – erster Slide sofort sichtbar (fixiert)
+ wp_enqueue_script( 'minecraft-avatar-slider-script', get_template_directory_uri() . '/js/login-slider.js', array( 'jquery' ), '1.1', true );
+ wp_enqueue_script( 'minecraft-modern-login-script', get_template_directory_uri() . '/js/login-script.js', array( 'jquery' ), '1.0', true );
+
+ wp_add_inline_script( 'minecraft-modern-login-script', "
+ jQuery(document).ready(function($) {
+ $('.forgetmenot, #nav').wrapAll('
');
+ });
+ " );
+
+ $slider_speed = get_theme_mod( 'login_avatar_slider_speed', 4 );
+ wp_localize_script( 'minecraft-avatar-slider-script', 'avatarSliderSettings', array(
+ 'speed' => $slider_speed * 1000,
+ ) );
+
+ $login_bg_image = get_theme_mod( 'login_background_image' );
+ if ( $login_bg_image ) {
+ wp_add_inline_style( 'minecraft-modern-login-style', "body.login { background-image: url('" . esc_url( $login_bg_image ) . "') !important; }" );
+ }
+ $logo_url = get_theme_mod( 'login_logo' );
+ if ( $logo_url ) {
+ wp_add_inline_style( 'minecraft-modern-login-style', ".login h1 a { background-image: url('" . esc_url( $logo_url ) . "') !important; }" );
+ }
+}
+add_action( 'login_enqueue_scripts', 'minecraft_modern_login_assets' );
+
+
+function add_minecraft_avatar_slider_to_login() {
+ $avatar_html = '';
+ $has_avatars = false;
+
+ for ( $i = 1; $i <= 5; $i++ ) {
+ $uuid = get_theme_mod( 'login_avatar_uuid_' . $i );
+ if ( ! empty( $uuid ) ) {
+ $has_avatars = true;
+ $avatar_url = 'https://visage.surgeplay.com/full/' . $uuid . '.png';
+ // FIX: Klasse wird hier gesetzt, JS überschreibt sie trotzdem (kompatibel)
+ $active_class = ( $i === 1 ) ? 'avatar-slide avatar-slide-active' : 'avatar-slide';
+ $avatar_html .= '
';
+ }
+ }
+ $avatar_html .= '
';
+
+ if ( $has_avatars ) {
+ echo $avatar_html;
+ }
+}
+add_action( 'login_form', 'add_minecraft_avatar_slider_to_login' );
+
+
+function add_post_login_links() {
+ ?>
+
+
+
+
+
+ '3.0',
+ '_exported' => date( 'Y-m-d H:i:s' ),
+ '_theme' => $theme_slug,
+ );
+
+ // 1. Alle Customizer-Einstellungen (Theme Mods)
+ // Enthält: Farben, Slider, Hero, Social Links, Menü-Design, Login-Einstellungen, Sidebar-Einstellungen usw.
+ $data['theme_mods'] = get_theme_mods();
+
+ // 2. Announcement-Bar (gespeichert als wp_options, nicht als Theme Mod)
+ $announcement_keys = array(
+ 'mm_announcement_enabled', 'mm_announcement_text', 'mm_announcement_bg',
+ 'mm_announcement_color', 'mm_announcement_font_size', 'mm_announcement_font_family',
+ 'mm_announcement_position', 'mm_announcement_countdown_enabled',
+ 'mm_announcement_countdown_label', 'mm_announcement_countdown_date',
+ 'mm_announcement_countdown_expired_msg',
+ );
+ foreach ( $announcement_keys as $key ) {
+ $data['announcement'][ $key ] = get_option( $key );
+ }
+
+ // 3. Widget-Konfigurationen aller Theme-Sidebars
+ $theme_sidebars = array(
+ 'single-post-sidebar',
+ 'homepage-sidebar-top', 'homepage-sidebar-middle-1',
+ 'homepage-sidebar-middle-2', 'homepage-sidebar-bottom',
+ 'homepage-sidebar-extra', 'footer-1', 'footer-2', 'footer-3',
+ );
+ $all_sidebars = wp_get_sidebars_widgets();
+ $widget_data = array();
+ foreach ( $theme_sidebars as $sidebar_id ) {
+ $widget_data[ $sidebar_id ] = array();
+ if ( empty( $all_sidebars[ $sidebar_id ] ) ) continue;
+ foreach ( $all_sidebars[ $sidebar_id ] as $widget_id ) {
+ if ( ! preg_match( '/^(.+)-(\d+)$/', $widget_id, $m ) ) continue;
+ $type = $m[1];
+ $index = intval( $m[2] );
+ $all = get_option( 'widget_' . $type, array() );
+ $widget_data[ $sidebar_id ][] = array(
+ 'type' => $type,
+ 'index' => $index,
+ 'settings' => isset( $all[ $index ] ) ? $all[ $index ] : array(),
+ );
+ }
+ }
+ $data['widgets'] = $widget_data;
+
+ // 4. Team-Mitglieder (mit UUID, Banner, Thumbnail)
+ $team_data = array();
+ $team_query = new WP_Query( array(
+ 'post_type' => 'team_member',
+ 'posts_per_page' => -1,
+ 'orderby' => 'menu_order',
+ 'order' => 'ASC',
+ ) );
+ if ( $team_query->have_posts() ) {
+ while ( $team_query->have_posts() ) {
+ $team_query->the_post();
+ $post_id = get_the_ID();
+ $team_data[] = array(
+ 'title' => get_the_title(),
+ 'content' => get_the_content(),
+ 'rank' => get_post_meta( $post_id, '_team_member_rank', true ),
+ 'uuid' => get_post_meta( $post_id, '_team_member_uuid', true ),
+ 'banner_id' => get_post_meta( $post_id, '_team_member_banner', true ),
+ 'thumbnail_id' => get_post_thumbnail_id( $post_id ),
+ 'menu_order' => get_post_field( 'menu_order', $post_id ),
+ );
+ }
+ wp_reset_postdata();
+ }
+ $data['team'] = $team_data;
+
+ // 5. FAQ-Einträge (theme-eigener Custom Post Type)
+ $faq_data = array();
+ $faq_query = new WP_Query( array(
+ 'post_type' => 'faq',
+ 'posts_per_page' => -1,
+ 'orderby' => 'menu_order',
+ 'order' => 'ASC',
+ ) );
+ if ( $faq_query->have_posts() ) {
+ while ( $faq_query->have_posts() ) {
+ $faq_query->the_post();
+ $terms = wp_get_post_terms( get_the_ID(), 'faq_category', array( 'fields' => 'names' ) );
+ $faq_data[] = array(
+ 'title' => get_the_title(),
+ 'content' => get_the_content(),
+ 'menu_order' => get_post_field( 'menu_order', get_the_ID() ),
+ 'categories' => is_wp_error( $terms ) ? array() : $terms,
+ );
+ }
+ wp_reset_postdata();
+ }
+ $data['faqs'] = $faq_data;
+
+ // 6. Custom CSS (Customizer → Zusätzliches CSS)
+ $data['custom_css'] = wp_get_custom_css();
+
+ // 7. Homepage-Seite (falls eingestellt)
+ $homepage_id = get_option( 'page_on_front' );
+ if ( $homepage_id ) {
+ $homepage = get_post( $homepage_id );
+ if ( $homepage ) {
+ $data['homepage'] = array(
+ 'title' => $homepage->post_title,
+ 'content' => $homepage->post_content,
+ 'excerpt' => $homepage->post_excerpt,
+ 'featured_img' => get_post_thumbnail_id( $homepage_id ),
+ );
+ }
+ }
+
+ // 8. Navigation Menüs mit Items
+ $nav_menus = array();
+ $all_nav_menus = wp_get_nav_menus();
+ foreach ( $all_nav_menus as $menu ) {
+ $nav_menus[ $menu->slug ] = array(
+ 'name' => $menu->name,
+ 'description' => $menu->description,
+ 'items' => array(),
+ );
+ $menu_items = wp_get_nav_menu_items( $menu->term_id );
+ if ( $menu_items ) {
+ foreach ( $menu_items as $item ) {
+ $nav_menus[ $menu->slug ]['items'][] = array(
+ 'title' => $item->title,
+ 'url' => $item->url,
+ 'description' => $item->description,
+ 'type' => $item->type,
+ 'type_label' => $item->type_label,
+ 'object' => $item->object,
+ 'object_id' => $item->object_id,
+ 'parent' => $item->menu_item_parent,
+ 'menu_order' => $item->menu_order,
+ 'target' => get_post_meta( $item->ID, '_menu_item_target', true ),
+ 'classes' => implode( ' ', (array) $item->classes ),
+ 'xfn' => get_post_meta( $item->ID, '_menu_item_xfn', true ),
+ );
+ }
+ }
+ }
+ $data['nav_menus'] = $nav_menus;
+
+ // 9. Menü-Positionen (welches Menü ist welchem Theme-Location zugewiesen)
+ $data['nav_menu_locations'] = get_theme_mod( 'nav_menu_locations', array() );
+
+ $json = json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
+ header( 'Content-Type: application/json; charset=utf-8' );
+ header( 'Content-Disposition: attachment; filename=' . $theme_slug . '-theme-backup-' . date( 'Y-m-d' ) . '.json' );
+ header( 'Pragma: no-cache' );
+ header( 'Expires: 0' );
+ echo $json;
+ exit;
+}
+
+
+add_action( 'wp_ajax_import_theme_settings', 'handle_theme_settings_import' );
+
+function handle_theme_settings_import() {
+ if ( ! current_user_can( 'edit_theme_options' ) ) {
+ wp_send_json_error( __( 'Keine Berechtigung.', 'minecraft-modern-theme' ) );
+ }
+ check_ajax_referer( 'theme-import-nonce', 'nonce' );
+
+ if ( empty( $_FILES['import_file']['tmp_name'] ) ) {
+ wp_send_json_error( __( 'Keine Datei hochgeladen.', 'minecraft-modern-theme' ) );
+ }
+
+ $json_content = file_get_contents( $_FILES['import_file']['tmp_name'] );
+ $data = json_decode( $json_content, true );
+
+ if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $data ) ) {
+ wp_send_json_error( __( 'Ungültige JSON-Datei.', 'minecraft-modern-theme' ) );
+ }
+
+ $imported = array();
+ $version = isset( $data['_version'] ) ? $data['_version'] : '1.0';
+
+ // -------------------------------------------------------------------------
+ // 1. Customizer / Theme Mods
+ // -------------------------------------------------------------------------
+ $mods = array();
+ if ( $version === '3.0' && isset( $data['theme_mods'] ) ) {
+ $mods = $data['theme_mods'];
+ } elseif ( $version === '2.0' && isset( $data['theme_mods'] ) ) {
+ $mods = $data['theme_mods'];
+ } else {
+ // Altes v1-Format: alles auf oberster Ebene
+ $skip = array( '_version', '_exported', '_theme', 'announcement', 'widgets', 'team', 'faqs', 'menus', 'wp_options', 'custom_css', 'team_data' );
+ foreach ( $data as $k => $v ) {
+ if ( ! in_array( $k, $skip ) ) $mods[ $k ] = $v;
+ }
+ }
+ foreach ( $mods as $key => $value ) {
+ set_theme_mod( $key, $value );
+ }
+ $imported[] = 'Customizer-Einstellungen';
+
+ // -------------------------------------------------------------------------
+ // 2. Announcement-Bar
+ // -------------------------------------------------------------------------
+ $announcement_keys = array(
+ 'mm_announcement_enabled', 'mm_announcement_text', 'mm_announcement_bg',
+ 'mm_announcement_color', 'mm_announcement_font_size', 'mm_announcement_font_family',
+ 'mm_announcement_position', 'mm_announcement_countdown_enabled',
+ 'mm_announcement_countdown_label', 'mm_announcement_countdown_date',
+ 'mm_announcement_countdown_expired_msg',
+ );
+ $ann = isset( $data['announcement'] ) ? $data['announcement'] : $data;
+ foreach ( $announcement_keys as $key ) {
+ if ( array_key_exists( $key, $ann ) ) {
+ update_option( $key, $ann[ $key ] );
+ }
+ }
+ $imported[] = 'Ankündigung';
+
+ // -------------------------------------------------------------------------
+ // 3. Widgets
+ // -------------------------------------------------------------------------
+ if ( ! empty( $data['widgets'] ) ) {
+ $current_sidebars = get_option( 'sidebars_widgets', array() );
+ foreach ( $data['widgets'] as $sidebar_id => $widgets ) {
+ $current_sidebars[ $sidebar_id ] = array();
+ foreach ( $widgets as $widget ) {
+ $type = sanitize_key( $widget['type'] );
+ $index = intval( $widget['index'] );
+ $all = get_option( 'widget_' . $type, array() );
+ $all[ $index ] = $widget['settings'];
+ update_option( 'widget_' . $type, $all );
+ $current_sidebars[ $sidebar_id ][] = $type . '-' . $index;
+ }
+ }
+ update_option( 'sidebars_widgets', $current_sidebars );
+ $imported[] = 'Widgets';
+ }
+
+ // -------------------------------------------------------------------------
+ // 4. Team-Mitglieder
+ // -------------------------------------------------------------------------
+ $team_data = isset( $data['team'] ) ? $data['team'] : ( isset( $data['team_data'] ) ? $data['team_data'] : array() );
+ if ( ! empty( $team_data ) ) {
+ $existing = new WP_Query( array( 'post_type' => 'team_member', 'posts_per_page' => -1, 'fields' => 'ids' ) );
+ if ( $existing->have_posts() ) {
+ foreach ( $existing->posts as $pid ) wp_delete_post( $pid, true );
+ }
+ wp_reset_postdata();
+ foreach ( $team_data as $member ) {
+ $id = wp_insert_post( array(
+ 'post_title' => sanitize_text_field( $member['title'] ),
+ 'post_content' => wp_kses_post( $member['content'] ),
+ 'post_type' => 'team_member',
+ 'post_status' => 'publish',
+ 'menu_order' => intval( isset( $member['menu_order'] ) ? $member['menu_order'] : 0 ),
+ ) );
+ if ( $id && ! is_wp_error( $id ) ) {
+ // Rank
+ if ( ! empty( $member['rank'] ) ) {
+ update_post_meta( $id, '_team_member_rank', sanitize_text_field( $member['rank'] ) );
+ }
+ // UUID
+ if ( ! empty( $member['uuid'] ) ) {
+ update_post_meta( $id, '_team_member_uuid', sanitize_text_field( $member['uuid'] ) );
+ }
+ // Thumbnail
+ if ( ! empty( $member['thumbnail_id'] ) ) {
+ set_post_thumbnail( $id, intval( $member['thumbnail_id'] ) );
+ }
+ // Banner
+ if ( ! empty( $member['banner_id'] ) ) {
+ update_post_meta( $id, '_team_member_banner', intval( $member['banner_id'] ) );
+ }
+ }
+ }
+ $imported[] = count( $team_data ) . ' Team-Mitglieder';
+ }
+
+ // -------------------------------------------------------------------------
+ // 5. FAQs
+ // -------------------------------------------------------------------------
+ if ( ! empty( $data['faqs'] ) ) {
+ $existing = new WP_Query( array( 'post_type' => 'faq', 'posts_per_page' => -1, 'fields' => 'ids' ) );
+ if ( $existing->have_posts() ) {
+ foreach ( $existing->posts as $pid ) wp_delete_post( $pid, true );
+ }
+ wp_reset_postdata();
+ foreach ( $data['faqs'] as $faq ) {
+ $id = wp_insert_post( array(
+ 'post_title' => sanitize_text_field( $faq['title'] ),
+ 'post_content' => wp_kses_post( $faq['content'] ),
+ 'post_type' => 'faq',
+ 'post_status' => 'publish',
+ 'menu_order' => intval( isset( $faq['menu_order'] ) ? $faq['menu_order'] : 0 ),
+ ) );
+ if ( $id && ! is_wp_error( $id ) && ! empty( $faq['categories'] ) ) {
+ foreach ( (array) $faq['categories'] as $cat_name ) {
+ $term = term_exists( sanitize_text_field( $cat_name ), 'faq_category' );
+ if ( ! $term ) {
+ $term = wp_insert_term( sanitize_text_field( $cat_name ), 'faq_category' );
+ }
+ if ( ! is_wp_error( $term ) ) {
+ wp_set_post_terms( $id, array( intval( $term['term_id'] ) ), 'faq_category', true );
+ }
+ }
+ }
+ }
+ $imported[] = count( $data['faqs'] ) . ' FAQs';
+ }
+
+ // -------------------------------------------------------------------------
+ // 6. Custom CSS
+ // -------------------------------------------------------------------------
+ if ( isset( $data['custom_css'] ) && $data['custom_css'] !== '' ) {
+ wp_update_custom_css_post( $data['custom_css'] );
+ $imported[] = 'Custom CSS';
+ }
+
+ // -------------------------------------------------------------------------
+ // 7. Homepage-Seite
+ // -------------------------------------------------------------------------
+ if ( ! empty( $data['homepage'] ) ) {
+ $homepage_id = get_option( 'page_on_front' );
+ if ( $homepage_id ) {
+ wp_update_post( array(
+ 'ID' => $homepage_id,
+ 'post_title' => sanitize_text_field( $data['homepage']['title'] ),
+ 'post_content' => wp_kses_post( $data['homepage']['content'] ),
+ 'post_excerpt' => sanitize_textarea_field( $data['homepage']['excerpt'] ),
+ ) );
+ if ( ! empty( $data['homepage']['featured_img'] ) ) {
+ set_post_thumbnail( $homepage_id, intval( $data['homepage']['featured_img'] ) );
+ }
+ $imported[] = 'Homepage-Seite';
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // 8. Navigation Menüs mit Items
+ // -------------------------------------------------------------------------
+ if ( ! empty( $data['nav_menus'] ) ) {
+ $menu_id_map = array(); // slug => term_id Mapping für Item-Parent-Zuordnung
+
+ // Alle existierenden Menüs löschen
+ $existing_menus = wp_get_nav_menus();
+ foreach ( $existing_menus as $menu ) {
+ wp_delete_nav_menu( $menu->term_id );
+ }
+
+ // Neue Menüs und Items importieren
+ foreach ( $data['nav_menus'] as $menu_slug => $menu_data ) {
+ $new_menu = wp_create_nav_menu( $menu_data['name'] );
+ if ( ! is_wp_error( $new_menu ) ) {
+ $menu_id_map[ $menu_slug ] = $new_menu;
+
+ // Items hinzufügen
+ if ( ! empty( $menu_data['items'] ) ) {
+ $item_id_map = array(); // Altes Item ID => Neues Item ID Mapping
+
+ // Erste Runde: Root-Items (parent = 0)
+ foreach ( $menu_data['items'] as $idx => $item ) {
+ if ( empty( $item['parent'] ) || $item['parent'] == 0 ) {
+ $item_id = wp_update_nav_menu_item( $new_menu, 0, array(
+ 'menu-item-title' => sanitize_text_field( $item['title'] ),
+ 'menu-item-url' => esc_url_raw( $item['url'] ),
+ 'menu-item-description' => sanitize_text_field( $item['description'] ),
+ 'menu-item-type' => sanitize_key( $item['type'] ),
+ 'menu-item-object' => sanitize_key( $item['object'] ),
+ 'menu-item-object-id' => intval( $item['object_id'] ),
+ 'menu-item-target' => sanitize_text_field( $item['target'] ),
+ 'menu-item-classes' => sanitize_text_field( $item['classes'] ),
+ 'menu-item-xfn' => sanitize_text_field( $item['xfn'] ),
+ 'menu-item-status' => 'publish',
+ ) );
+ if ( $item_id && ! is_wp_error( $item_id ) ) {
+ $item_id_map[ $idx ] = $item_id;
+ }
+ }
+ }
+
+ // Zweite Runde: Sub-Items (parent > 0)
+ foreach ( $menu_data['items'] as $idx => $item ) {
+ if ( ! empty( $item['parent'] ) && $item['parent'] > 0 ) {
+ $parent_item_id = isset( $item_id_map[ $item['parent'] - 1 ] ) ? $item_id_map[ $item['parent'] - 1 ] : 0;
+ $item_id = wp_update_nav_menu_item( $new_menu, 0, array(
+ 'menu-item-title' => sanitize_text_field( $item['title'] ),
+ 'menu-item-url' => esc_url_raw( $item['url'] ),
+ 'menu-item-description' => sanitize_text_field( $item['description'] ),
+ 'menu-item-type' => sanitize_key( $item['type'] ),
+ 'menu-item-object' => sanitize_key( $item['object'] ),
+ 'menu-item-object-id' => intval( $item['object_id'] ),
+ 'menu-item-parent-id' => $parent_item_id,
+ 'menu-item-target' => sanitize_text_field( $item['target'] ),
+ 'menu-item-classes' => sanitize_text_field( $item['classes'] ),
+ 'menu-item-xfn' => sanitize_text_field( $item['xfn'] ),
+ 'menu-item-status' => 'publish',
+ ) );
+ if ( $item_id && ! is_wp_error( $item_id ) ) {
+ $item_id_map[ $idx ] = $item_id;
+ }
+ }
+ }
+ }
+ }
+ }
+ $imported[] = 'Navigation Menüs (' . count( $data['nav_menus'] ) . ')';
+ }
+
+ // -------------------------------------------------------------------------
+ // 9. Menü-Positionen
+ // -------------------------------------------------------------------------
+ if ( ! empty( $data['nav_menu_locations'] ) ) {
+ set_theme_mod( 'nav_menu_locations', $data['nav_menu_locations'] );
+ $imported[] = 'Menü-Positionen';
+ }
+
+ wp_send_json_success( sprintf(
+ __( 'Erfolgreich wiederhergestellt: %s', 'minecraft-modern-theme' ),
+ implode( ', ', $imported )
+ ) );
+}
+
+
+// =========================================================================
+// ANNOUNCEMENT BAR
+// =========================================================================
+
+function mm_announcement_get_font_list() {
+ return array(
+ 'inherit' => array( 'label' => 'Theme-Standard', 'css' => 'inherit', 'google' => false, 'google_name' => '' ),
+ 'Arial' => array( 'label' => 'Arial', 'css' => 'Arial, Helvetica, sans-serif', 'google' => false, 'google_name' => '' ),
+ 'Roboto' => array( 'label' => 'Roboto', 'css' => "'Roboto', sans-serif", 'google' => true, 'google_name' => 'Roboto' ),
+ 'Montserrat' => array( 'label' => 'Montserrat', 'css' => "'Montserrat', sans-serif", 'google' => true, 'google_name' => 'Montserrat' ),
+ 'Open Sans' => array( 'label' => 'Open Sans', 'css' => "'Open Sans', sans-serif", 'google' => true, 'google_name' => 'Open+Sans' ),
+ 'Lato' => array( 'label' => 'Lato', 'css' => "'Lato', sans-serif", 'google' => true, 'google_name' => 'Lato' ),
+ 'Poppins' => array( 'label' => 'Poppins', 'css' => "'Poppins', sans-serif", 'google' => true, 'google_name' => 'Poppins' ),
+ 'Source Sans Pro' => array( 'label' => 'Source Sans Pro', 'css' => "'Source Sans Pro', sans-serif", 'google' => true, 'google_name' => 'Source+Sans+Pro' ),
+ 'Noto Sans' => array( 'label' => 'Noto Sans', 'css' => "'Noto Sans', sans-serif", 'google' => true, 'google_name' => 'Noto+Sans' ),
+ 'Raleway' => array( 'label' => 'Raleway', 'css' => "'Raleway', sans-serif", 'google' => true, 'google_name' => 'Raleway' ),
+ 'Merriweather' => array( 'label' => 'Merriweather', 'css' => "'Merriweather', serif", 'google' => true, 'google_name' => 'Merriweather' ),
+ 'Playfair Display' => array( 'label' => 'Playfair Display', 'css' => "'Playfair Display', serif", 'google' => true, 'google_name' => 'Playfair+Display' ),
+ 'Oswald' => array( 'label' => 'Oswald', 'css' => "'Oswald', sans-serif", 'google' => true, 'google_name' => 'Oswald' ),
+ 'Rubik' => array( 'label' => 'Rubik', 'css' => "'Rubik', sans-serif", 'google' => true, 'google_name' => 'Rubik' ),
+ 'Inter' => array( 'label' => 'Inter', 'css' => "'Inter', sans-serif", 'google' => true, 'google_name' => 'Inter' ),
+ 'Nunito' => array( 'label' => 'Nunito', 'css' => "'Nunito', sans-serif", 'google' => true, 'google_name' => 'Nunito' ),
+ 'Ubuntu' => array( 'label' => 'Ubuntu', 'css' => "'Ubuntu', sans-serif", 'google' => true, 'google_name' => 'Ubuntu' ),
+ 'PT Sans' => array( 'label' => 'PT Sans', 'css' => "'PT Sans', sans-serif", 'google' => true, 'google_name' => 'PT+Sans' ),
+ 'Archivo' => array( 'label' => 'Archivo', 'css' => "'Archivo', sans-serif", 'google' => true, 'google_name' => 'Archivo' ),
+ 'Fira Sans' => array( 'label' => 'Fira Sans', 'css' => "'Fira Sans', sans-serif", 'google' => true, 'google_name' => 'Fira+Sans' ),
+ 'Work Sans' => array( 'label' => 'Work Sans', 'css' => "'Work Sans', sans-serif", 'google' => true, 'google_name' => 'Work+Sans' ),
+ 'Quicksand' => array( 'label' => 'Quicksand', 'css' => "'Quicksand', sans-serif", 'google' => true, 'google_name' => 'Quicksand' ),
+ 'Karla' => array( 'label' => 'Karla', 'css' => "'Karla', sans-serif", 'google' => true, 'google_name' => 'Karla' ),
+ 'Dancing Script' => array( 'label' => 'Dancing Script', 'css' => "'Dancing Script', cursive", 'google' => true, 'google_name' => 'Dancing+Script' ),
+ 'Pacifico' => array( 'label' => 'Pacifico', 'css' => "'Pacifico', cursive", 'google' => true, 'google_name' => 'Pacifico' ),
+ 'Great Vibes' => array( 'label' => 'Great Vibes', 'css' => "'Great Vibes', cursive", 'google' => true, 'google_name' => 'Great+Vibes' ),
+ 'Satisfy' => array( 'label' => 'Satisfy', 'css' => "'Satisfy', cursive", 'google' => true, 'google_name' => 'Satisfy' ),
+ 'Allura' => array( 'label' => 'Allura', 'css' => "'Allura', cursive", 'google' => true, 'google_name' => 'Allura' ),
+ 'Alex Brush' => array( 'label' => 'Alex Brush', 'css' => "'Alex Brush', cursive", 'google' => true, 'google_name' => 'Alex+Brush' ),
+ 'Cookie' => array( 'label' => 'Cookie', 'css' => "'Cookie', cursive", 'google' => true, 'google_name' => 'Cookie' ),
+ );
+}
+
+
+function mm_announcement_admin_init() {
+ add_menu_page( 'Ankündigung', 'Ankündigung', 'manage_options', 'mm-announcement', 'mm_announcement_admin_page', 'dashicons-megaphone', 61 );
+
+ register_setting( 'mm_announcement_group', 'mm_announcement_enabled' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_text' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_bg' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_color' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_font_size' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_font_family' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_position' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_countdown_enabled' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_countdown_label' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_countdown_date' );
+ register_setting( 'mm_announcement_group', 'mm_announcement_countdown_expired_msg' );
+}
+add_action( 'admin_menu', 'mm_announcement_admin_init' );
+
+
+function mm_announcement_admin_page() {
+ if ( ! current_user_can( 'manage_options' ) ) return;
+
+ $fonts = mm_announcement_get_font_list();
+ $selected_font = get_option( 'mm_announcement_font_family', 'inherit' );
+ $selected_size = (int) get_option( 'mm_announcement_font_size', 16 );
+ $bg = esc_attr( get_option( 'mm_announcement_bg', '#1e1e1e' ) );
+ $color = esc_attr( get_option( 'mm_announcement_color', '#ffffff' ) );
+ $text_sample = wp_strip_all_tags( get_option( 'mm_announcement_text' ) ) ?: 'Das ist eine Vorschau: Wie sieht die Schrift aus?';
+ ?>
+
+
Header-Ankündigung
+
Diese Leiste wird auf allen Seiten angezeigt. Die Vorschau unten zeigt sofort, wie die Ankündigung aussieht — Änderungen wirken direkt in der Vorschau , erst nach Änderungen speichern werden sie im Frontend übernommen.
+
+
+
+
+ Allgemein
+
+
+ Inhalt
+
+
+ Position
+
+
+ Design
+
+
+ Countdown Timer
+
+
+
+
+
+
+ ' . $countdown_label . '
+ Laden...
+ ';
+ }
+ ?>
+
+ get_option( 'mm_announcement_font_family', 'inherit' ),
+ 'size' => (int) get_option( 'mm_announcement_font_size', 16 ),
+ 'bg' => get_option( 'mm_announcement_bg', '#1e1e1e' ),
+ 'color' => get_option( 'mm_announcement_color', '#ffffff' ),
+ 'text' => wp_kses_post( get_option( 'mm_announcement_text' ) ),
+ ) );
+}
+add_action( 'admin_enqueue_scripts', 'mm_announcement_admin_assets' );
+
+
+// =========================================================================
+// TEAM MODUL
+// =========================================================================
+
+function create_team_post_type() {
+ if ( get_theme_mod( 'team_enabled', true ) ) {
+ register_post_type( 'team_member', array(
+ 'labels' => array(
+ 'name' => __( 'Team', 'minecraft-modern-theme' ),
+ 'singular_name' => __( 'Teammitglied', 'minecraft-modern-theme' ),
+ 'add_new' => __( 'Neues Mitglied', 'minecraft-modern-theme' ),
+ 'menu_name' => __( 'Team', 'minecraft-modern-theme' ),
+ ),
+ 'public' => true,
+ 'has_archive' => true,
+ 'menu_icon' => 'dashicons-groups',
+ 'supports' => array( 'title', 'thumbnail', 'page-attributes' ),
+ 'rewrite' => array( 'slug' => 'team' ),
+ 'show_in_rest' => true,
+ 'show_in_menu' => false,
+ ) );
+ }
+}
+add_action( 'init', 'create_team_post_type' );
+
+
+function add_team_meta_boxes() {
+ add_meta_box( 'team_member_rank_box', __( 'Rang & Position', 'minecraft-modern-theme' ), 'team_member_rank_callback', 'team_member', 'side', 'default' );
+}
+add_action( 'add_meta_boxes', 'add_team_meta_boxes' );
+
+function team_member_rank_callback( $post ) {
+ wp_nonce_field( 'team_member_rank_save', 'team_member_rank_nonce' );
+ $value = get_post_meta( $post->ID, '_team_member_rank', true );
+ ?>
+
+ Rang:
+
+
+ id !== 'toplevel_page_mm-team-manager' ) return;
+ echo '';
+}
+add_action( 'admin_head', 'mm_team_manager_admin_css' );
+
+function mm_team_manager_page_html() {
+ ?>
+
+
Team Verwaltung
+
Hier kannst du Teammitglieder hinzufügen, sortieren und bearbeiten.
+
+
+
Neues Mitglied hinzufügen
+
+
+ Name:
+
+
+
+ Rang:
+
+
+
+ Kurzbeschreibung:
+
+
+
+
+
+
+
Minecraft UUID:
+
+
UUID eingeben → Avatar wird automatisch geladen
+
+
+
+
Oder: Avatar-Bild:
+
+
+ Bild auswählen
+ Kein Bild gewählt
+
+
Wird ignoriert wenn UUID gesetzt ist
+
+
+
+
Banner-Bild:
+
+
+ Banner auswählen
+ Kein Banner gewählt
+
+
Hintergrundbild der Card (optional)
+
+
+
+
+
+
+
+ Hinzufügen
+
+
+
Reihenfolge speichern
+
Reihenfolge gespeichert!
+
+
+
+
+
+
+
+ Bild
+ Name
+ Rang
+ Bio
+ UUID / Bild
+ Sort.
+ Aktionen
+
+
+
+ 'team_member', 'posts_per_page' => -1, 'orderby' => 'menu_order', 'order' => 'ASC' ) );
+ if ( $team_query->have_posts() ) :
+ while ( $team_query->have_posts() ) : $team_query->the_post();
+ $id = get_the_ID();
+ $name = get_the_title();
+ $rank = get_post_meta( $id, '_team_member_rank', true );
+ $bio = get_the_content();
+ $uuid = get_post_meta( $id, '_team_member_uuid', true );
+ $img_id = get_post_thumbnail_id( $id );
+ $banner_id = get_post_meta( $id, '_team_member_banner', true );
+ $banner_url = $banner_id ? wp_get_attachment_image_url( $banner_id, 'medium' ) : false;
+
+ // Avatar anzeigen: UUID > Bild > Placeholder
+ if ( $uuid ) {
+ $avatar_src = 'https://visage.surgeplay.com/bust/' . esc_attr($uuid) . '.png';
+ } elseif ( $img_id ) {
+ $avatar_src = wp_get_attachment_image_url( $img_id, array(40,40) );
+ } else {
+ $avatar_src = false;
+ }
+ ?>
+
+
+
+
+
+
+
👤
+
+
+
+
+
+
+
+
+
+
+
+ ▲
+ ▼
+
+
+ 💾
+ 🗑️
+
+
+ Noch keine Mitglieder vorhanden. ';
+ endif;
+ ?>
+
+
+
+
+
+
+ $id, 'menu_order' => $pos ) );
+ $pos++;
+ }
+ wp_send_json_success();
+}
+
+
+add_action( 'wp_ajax_mm_add_team_member', 'handle_mm_add_member' );
+function handle_mm_add_member() {
+ check_ajax_referer( 'mm_team_nonce', 'nonce' );
+ if ( ! current_user_can( 'publish_posts' ) ) wp_send_json_error( 'Keine Berechtigung' );
+
+ $id = wp_insert_post( array(
+ 'post_title' => sanitize_text_field( $_POST['name'] ),
+ 'post_content' => sanitize_textarea_field( $_POST['bio'] ),
+ 'post_type' => 'team_member',
+ 'post_status' => 'publish',
+ 'menu_order' => 999,
+ ) );
+
+ if ( ! $id || is_wp_error( $id ) ) {
+ wp_send_json_error( 'Fehler beim Erstellen' );
+ }
+
+ update_post_meta( $id, '_team_member_rank', sanitize_text_field( $_POST['rank'] ) );
+
+ // UUID speichern (kein externer Request hier – nur als Text speichern)
+ if ( ! empty( $_POST['uuid'] ) ) {
+ $uuid = sanitize_text_field( trim( $_POST['uuid'] ) );
+ update_post_meta( $id, '_team_member_uuid', $uuid );
+ } elseif ( ! empty( $_POST['img_id'] ) ) {
+ set_post_thumbnail( $id, intval( $_POST['img_id'] ) );
+ }
+
+ // Banner-Bild speichern
+ if ( ! empty( $_POST['banner_id'] ) ) {
+ update_post_meta( $id, '_team_member_banner', intval( $_POST['banner_id'] ) );
+ }
+
+ wp_send_json_success( array( 'id' => $id, 'msg' => 'Hinzugefügt' ) );
+}
+
+add_action( 'wp_ajax_mm_update_team_member', 'handle_mm_update_team_member' );
+function handle_mm_update_team_member() {
+ check_ajax_referer( 'mm_team_nonce', 'nonce' );
+ if ( ! current_user_can( 'edit_posts' ) ) wp_send_json_error( 'Keine Berechtigung' );
+ $id = intval( $_POST['id'] );
+ wp_update_post( array(
+ 'ID' => $id,
+ 'post_title' => sanitize_text_field( $_POST['title'] ),
+ 'post_content' => sanitize_textarea_field( $_POST['bio'] ),
+ ) );
+ update_post_meta( $id, '_team_member_rank', sanitize_text_field( $_POST['rank'] ) );
+ // UUID speichern oder löschen
+ if ( isset( $_POST['uuid'] ) ) {
+ $uuid = sanitize_text_field( trim( $_POST['uuid'] ) );
+ if ( $uuid ) {
+ update_post_meta( $id, '_team_member_uuid', $uuid );
+ } else {
+ delete_post_meta( $id, '_team_member_uuid' );
+ }
+ }
+ // Bild nur setzen wenn keine UUID
+ if ( empty( $_POST['uuid'] ) && ! empty( $_POST['img_id'] ) ) {
+ set_post_thumbnail( $id, intval( $_POST['img_id'] ) );
+ }
+ // Banner speichern
+ if ( isset( $_POST['banner_id'] ) ) {
+ if ( ! empty( $_POST['banner_id'] ) ) {
+ update_post_meta( $id, '_team_member_banner', intval( $_POST['banner_id'] ) );
+ }
+ }
+ wp_send_json_success( 'Gespeichert' );
+}
+
+add_action( 'wp_ajax_mm_delete_team_member', 'handle_mm_delete_team_member' );
+function handle_mm_delete_team_member() {
+ check_ajax_referer( 'mm_team_nonce', 'nonce' );
+ if ( ! current_user_can( 'delete_posts' ) ) wp_send_json_error( 'Keine Berechtigung' );
+ wp_delete_post( intval( $_POST['id'] ), true );
+ wp_send_json_success( 'Gelöscht' );
+}
+
+
+function create_team_page_automatically() {
+ if ( get_theme_mod( 'team_enabled', true ) && get_page_by_title( 'Team' ) == null ) {
+ wp_insert_post( array( 'post_title' => 'Team', 'post_status' => 'publish', 'post_type' => 'page', 'post_author' => 1 ) );
+ }
+}
+add_action( 'customize_save_after', 'create_team_page_automatically' );
+
+
+function load_team_page_template( $template ) {
+ if ( ! get_theme_mod( 'team_enabled', true ) ) return $template;
+ if ( is_post_type_archive( 'team_member' ) ) return get_template_directory() . '/archive-team.php';
+ if ( is_page() ) {
+ $obj = get_queried_object();
+ if ( $obj && $obj->post_name === 'team' ) return get_template_directory() . '/archive-team.php';
+ }
+ return $template;
+}
+add_filter( 'template_include', 'load_team_page_template' );
+
+
+// Doppelte team_customize_register entfernt – ist jetzt in inc/customizer.php
+
+// =========================================================================
+// MENÜ-LAYOUTS: Hilfsfunktionen für header.php
+// =========================================================================
+
+if ( ! function_exists('mm_branding') ) :
+function mm_branding( $show_title_with_logo = false ) { ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Panel –
+ // der Hamburger wird dort nicht gebraucht und verursacht JS-Konflikte.
+ if ( $menu_style !== 'sidebar' ) : ?>
+
+
+ 'primary',
+ 'container' => false,
+ 'menu_class' => 'primary-menu',
+ 'fallback_cb' => false,
+ ) ); ?>
+
+ 'fab fa-bluesky', 'discord' => 'fab fa-discord', 'youtube' => 'fab fa-youtube',
+ 'twitter' => 'fab fa-x-twitter', 'facebook' => 'fab fa-facebook-f',
+ 'instagram' => 'fab fa-instagram', 'tiktok' => 'fab fa-tiktok',
+ 'twitch' => 'fab fa-twitch', 'steam' => 'fab fa-steam',
+ 'github' => 'fab fa-github', 'linkedin' => 'fab fa-linkedin-in',
+ 'pinterest' => 'fab fa-pinterest-p', 'reddit' => 'fab fa-reddit-alien',
+ 'mastodon' => 'fab fa-mastodon', 'threads' => 'fab fa-threads',
+ 'kickstarter' => 'fab fa-kickstarter', 'teamspeak' => 'fab fa-teamspeak', 'spotify' => 'fab fa-spotify',
+ 'stoat' => 'fab fa-diaspora',
+ ); ?>
+
+add_section( 'header_menu_style_section', array(
+ 'title' => __( 'Menü-Design', 'minecraft-modern-theme' ),
+ 'priority' => 30,
+ ) );
+ $wp_customize->add_setting( 'header_menu_style', array(
+ 'default' => 'classic',
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'transport' => 'refresh',
+ ) );
+ $wp_customize->add_control( 'header_menu_style', array(
+ 'label' => __( 'Menü-Layout wählen', 'minecraft-modern-theme' ),
+ 'section' => 'header_menu_style_section',
+ 'type' => 'select',
+ 'choices' => array(
+ 'classic' => __( '① Classic – Logo links, Menü Mitte, Icons rechts', 'minecraft-modern-theme' ),
+ 'centered' => __( '② Zentriert – Logo oben, Menü darunter', 'minecraft-modern-theme' ),
+ 'sidebar' => __( '③ Sidebar – Menü als vertikale Leiste', 'minecraft-modern-theme' ),
+ 'mega' => __( '④ Mega-Menü – breite Dropdown-Spalten', 'minecraft-modern-theme' ),
+ ),
+ ) );
+
+ // Branding-Position (gilt für alle Layouts)
+ $wp_customize->add_setting( 'sidebar_branding_position', array(
+ 'default' => 'left',
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'transport' => 'refresh',
+ ) );
+ $wp_customize->add_control( 'sidebar_branding_position', array(
+ 'label' => __( 'Logo/Titel Position', 'minecraft-modern-theme' ),
+ 'description' => __( 'Gilt für alle Menü-Layouts.', 'minecraft-modern-theme' ),
+ 'section' => 'header_menu_style_section',
+ 'type' => 'select',
+ 'choices' => array(
+ 'left' => __( 'Links', 'minecraft-modern-theme' ),
+ 'center' => __( 'Mitte', 'minecraft-modern-theme' ),
+ 'right' => __( 'Rechts', 'minecraft-modern-theme' ),
+ ),
+ ) );
+}
+add_action( 'customize_register', 'minecraft_modern_menu_style_customizer' );
+
+
+// === Sidebar-Menü JavaScript (nur wenn Sidebar-Layout aktiv) ===
+function minecraft_modern_sidebar_menu_script() {
+ if ( get_theme_mod( 'header_menu_style', 'classic' ) !== 'sidebar' ) return;
+ ?>
+
+ __( 'Beitrag Sidebar', 'minecraft-modern-theme' ),
+ 'id' => 'single-post-sidebar',
+ 'description' => __( 'Widget-Bereich für die Sidebar auf Einzelbeitrags-Seiten.', 'minecraft-modern-theme' ),
+ 'before_widget' => '',
+ 'before_title' => '',
+ ) );
+}
+add_action( 'widgets_init', 'minecraft_modern_single_sidebar' );
+
+
+// === Single-Post Sidebar Render-Funktion ===
+function minecraft_modern_render_single_sidebar() {
+ if ( is_active_sidebar( 'single-post-sidebar' ) ) {
+ dynamic_sidebar( 'single-post-sidebar' );
+ } else {
+ ?>
+
+
+
+
+
+
+
+ add_section( 'single_sidebar_section', array(
+ 'title' => __( 'Beitrag Sidebar', 'minecraft-modern-theme' ),
+ 'priority' => 55,
+ ) );
+ $wp_customize->add_setting( 'single_sidebar_enabled', array(
+ 'default' => true,
+ 'sanitize_callback' => 'wp_validate_boolean',
+ ) );
+ $wp_customize->add_control( 'single_sidebar_enabled', array(
+ 'label' => __( 'Sidebar auf Einzelbeiträgen anzeigen', 'minecraft-modern-theme' ),
+ 'section' => 'single_sidebar_section',
+ 'type' => 'checkbox',
+ ) );
+ $wp_customize->add_setting( 'single_sidebar_position', array(
+ 'default' => 'right',
+ 'sanitize_callback' => 'sanitize_text_field',
+ ) );
+ $wp_customize->add_control( 'single_sidebar_position', array(
+ 'label' => __( 'Sidebar-Position', 'minecraft-modern-theme' ),
+ 'section' => 'single_sidebar_section',
+ 'type' => 'select',
+ 'choices' => array(
+ 'right' => __( 'Rechts', 'minecraft-modern-theme' ),
+ 'left' => __( 'Links', 'minecraft-modern-theme' ),
+ ),
+ ) );
+}
+add_action( 'customize_register', 'minecraft_modern_single_sidebar_customizer' );
+
+
+// === Announcement-Bar Höhe als CSS-Variable + Body-Klasse ===
+function minecraft_modern_announcement_offset_script() {
+ if ( ! get_option( 'mm_announcement_enabled' ) ) return;
+ ?>
+
+
+
+ add_section( 'mm_cookie_banner_section', array(
+ 'title' => __( 'Cookie-Banner (DSGVO)', 'minecraft-modern-theme' ),
+ 'priority' => 75,
+ ) );
+
+ $wp_customize->add_setting( 'mm_cookie_enabled', array( 'default' => true, 'sanitize_callback' => 'wp_validate_boolean', 'transport' => 'postMessage' ) );
+ $wp_customize->add_control( 'mm_cookie_enabled', array( 'label' => __( 'Cookie-Banner aktivieren', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'checkbox' ) );
+
+ $wp_customize->add_setting( 'mm_cookie_style', array( 'default' => 'bar', 'sanitize_callback' => 'sanitize_text_field', 'transport' => 'postMessage' ) );
+ $wp_customize->add_control( 'mm_cookie_style', array(
+ 'label' => __( 'Design-Variante', 'minecraft-modern-theme' ),
+ 'section' => 'mm_cookie_banner_section',
+ 'type' => 'select',
+ 'choices' => array(
+ 'bar' => __( 'Variante 1 – Schmale Bar (volle Breite)', 'minecraft-modern-theme' ),
+ 'split' => __( 'Variante 2 – Zweispaltig (3A)', 'minecraft-modern-theme' ),
+ 'slide' => __( 'Variante 3 – Slide-In von rechts (3B)', 'minecraft-modern-theme' ),
+ 'stepper' => __( 'Variante 4 – Kompakt-Center mit Stepper (3C)', 'minecraft-modern-theme' ),
+ ),
+ ) );
+
+ $wp_customize->add_setting( 'mm_cookie_text', array( 'default' => __( 'Wir nutzen Cookies und ähnliche Technologien. Einige sind essenziell, andere helfen uns diese Website zu verbessern. Du kannst deine Auswahl jederzeit anpassen.', 'minecraft-modern-theme' ), 'sanitize_callback' => 'wp_kses_post', 'transport' => 'postMessage' ) );
+ $wp_customize->add_control( 'mm_cookie_text', array( 'label' => __( 'Banner-Text', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'textarea' ) );
+
+ $wp_customize->add_setting( 'mm_cookie_privacy_url', array( 'default' => '', 'sanitize_callback' => 'esc_url_raw' ) );
+ $wp_customize->add_control( 'mm_cookie_privacy_url', array( 'label' => __( 'URL Datenschutzerklärung', 'minecraft-modern-theme' ), 'description' => __( 'Leer lassen um den Link auszublenden.', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'url' ) );
+
+ foreach ( array(
+ 'necessary' => array( 'label' => __( 'Beschreibung: Notwendige', 'minecraft-modern-theme' ), 'default' => __( 'Grundlegende Funktionen der Website. Können nicht deaktiviert werden.', 'minecraft-modern-theme' ) ),
+ 'statistics' => array( 'label' => __( 'Beschreibung: Statistik', 'minecraft-modern-theme' ), 'default' => __( 'Helfen uns zu verstehen wie Besucher mit der Website interagieren (z.B. Google Analytics).', 'minecraft-modern-theme' ) ),
+ 'marketing' => array( 'label' => __( 'Beschreibung: Marketing', 'minecraft-modern-theme' ), 'default' => __( 'Werden genutzt um Werbung relevanter zu gestalten (z.B. YouTube, Facebook).', 'minecraft-modern-theme' ) ),
+ ) as $key => $opts ) {
+ $wp_customize->add_setting( 'mm_cookie_desc_' . $key, array( 'default' => $opts['default'], 'sanitize_callback' => 'sanitize_textarea_field' ) );
+ $wp_customize->add_control( 'mm_cookie_desc_' . $key, array( 'label' => $opts['label'], 'section' => 'mm_cookie_banner_section', 'type' => 'textarea' ) );
+ }
+
+ $wp_customize->add_setting( 'mm_cookie_ga_id', array( 'default' => '', 'sanitize_callback' => 'sanitize_text_field' ) );
+ $wp_customize->add_control( 'mm_cookie_ga_id', array( 'label' => __( 'Google Analytics ID (optional)', 'minecraft-modern-theme' ), 'description' => __( 'z.B. G-XXXXXXXXXX', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'text' ) );
+
+ $wp_customize->add_setting( 'mm_cookie_lifetime', array( 'default' => 365, 'sanitize_callback' => 'absint' ) );
+ $wp_customize->add_control( 'mm_cookie_lifetime', array( 'label' => __( 'Cookie-Laufzeit (Tage)', 'minecraft-modern-theme' ), 'section' => 'mm_cookie_banner_section', 'type' => 'number', 'input_attrs' => array( 'min' => 1, 'max' => 730 ) ) );
+
+ if ( class_exists( 'MM_Cookie_Preview_Control' ) ) {
+ $wp_customize->add_setting( 'mm_cookie_preview_dummy', array( 'sanitize_callback' => 'sanitize_text_field' ) );
+ $wp_customize->add_control( new MM_Cookie_Preview_Control( $wp_customize, 'mm_cookie_preview_dummy', array( 'section' => 'mm_cookie_banner_section', 'priority' => 200 ) ) );
+ }
+}
+add_action( 'customize_register', 'mm_cookie_banner_customizer' );
+
+
+// --- 2. Banner HTML – ALLE 4 LAYOUTS gleichzeitig ausgeben ---
+// Im Customizer wechselt JS nur die Klasse auf #mm-cookie-banner.
+// CSS zeigt immer nur das passende .mmc-layout-* div.
+function mm_cookie_banner_render() {
+ $is_preview = is_customize_preview();
+ if ( ! $is_preview && ! get_theme_mod( 'mm_cookie_enabled', true ) ) return;
+
+ $style = get_theme_mod( 'mm_cookie_style', 'bar' );
+ $text = get_theme_mod( 'mm_cookie_text', __( 'Wir nutzen Cookies und ähnliche Technologien. Einige sind essenziell, andere helfen uns diese Website zu verbessern.', 'minecraft-modern-theme' ) );
+ $priv_url = get_theme_mod( 'mm_cookie_privacy_url', '' );
+ $lifetime = absint( get_theme_mod( 'mm_cookie_lifetime', 365 ) );
+ $ga_id = get_theme_mod( 'mm_cookie_ga_id', '' );
+ $desc_n = get_theme_mod( 'mm_cookie_desc_necessary', __( 'Grundlegende Funktionen der Website. Können nicht deaktiviert werden.', 'minecraft-modern-theme' ) );
+ $desc_s = get_theme_mod( 'mm_cookie_desc_statistics', __( 'Helfen uns zu verstehen wie Besucher mit der Website interagieren (z.B. Google Analytics).', 'minecraft-modern-theme' ) );
+ $desc_m = get_theme_mod( 'mm_cookie_desc_marketing', __( 'Werden genutzt um Werbung relevanter zu gestalten (z.B. YouTube, Facebook).', 'minecraft-modern-theme' ) );
+
+ $priv_link = $priv_url ? '' . __( 'Datenschutzerklärung', 'minecraft-modern-theme' ) . ' ' : '';
+ $preview_class = $is_preview ? ' mmc-visible mmc-preview-mode' : '';
+ $inline_style = $is_preview ? '' : 'display:none;';
+
+ // Kategorien-Block (identisch in allen Varianten mit Kategorien)
+ $cats = '
+
' . __('Notwendige','minecraft-modern-theme') . '' . __('Immer aktiv','minecraft-modern-theme') . '
' . esc_html($desc_n) . '
+
+
+
';
+
+ $btn_accept = ' ' . __('Alle akzeptieren','minecraft-modern-theme') . ' ';
+ $btn_select = ' ' . __('Auswahl speichern','minecraft-modern-theme') . ' ';
+ $btn_neces = '' . __('Nur notwendige','minecraft-modern-theme') . ' ';
+
+ $text_esc = wp_kses_post($text);
+ ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . $priv_link . ''; ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __('Cookie-Einstellungen','minecraft-modern-theme')),$atts);return ''.esc_html($a['text']).' ';}
+add_shortcode('cookie_settings','mm_cookie_settings_shortcode');
+
+
+// --- 4. Live-Vorschau: nur Klasse + Text wechseln, HTML ist bereits vollständig ---
+function mm_cookie_banner_preview_js(){
+ if(!is_customize_preview())return; ?>
+
+ array(
+ 'name' => __( 'Videos', 'minecraft-modern-theme' ),
+ 'singular_name' => __( 'Video', 'minecraft-modern-theme' ),
+ 'add_new' => __( 'Neues Video', 'minecraft-modern-theme' ),
+ 'add_new_item' => __( 'Neues Video hinzufügen', 'minecraft-modern-theme' ),
+ 'edit_item' => __( 'Video bearbeiten', 'minecraft-modern-theme' ),
+ 'all_items' => __( 'Alle Videos', 'minecraft-modern-theme' ),
+ 'menu_name' => __( 'Videos', 'minecraft-modern-theme' ),
+ ),
+ 'public' => true,
+ 'has_archive' => true,
+ 'menu_icon' => 'dashicons-video-alt3',
+ 'menu_position' => 7,
+ 'supports' => array( 'title', 'thumbnail', 'excerpt', 'page-attributes' ),
+ 'rewrite' => array( 'slug' => 'videos' ),
+ 'show_in_rest' => true,
+ ) );
+
+ register_post_type( 'mm_livestream', array(
+ 'labels' => array(
+ 'name' => __( 'Livestreams', 'minecraft-modern-theme' ),
+ 'singular_name' => __( 'Livestream', 'minecraft-modern-theme' ),
+ 'add_new' => __( 'Neuer Livestream', 'minecraft-modern-theme' ),
+ 'add_new_item' => __( 'Neuen Livestream hinzufügen', 'minecraft-modern-theme' ),
+ 'edit_item' => __( 'Livestream bearbeiten', 'minecraft-modern-theme' ),
+ 'all_items' => __( 'Alle Livestreams', 'minecraft-modern-theme' ),
+ 'menu_name' => __( 'Livestreams', 'minecraft-modern-theme' ),
+ ),
+ 'public' => true,
+ 'publicly_queryable' => false,
+ 'exclude_from_search' => true,
+ 'show_ui' => true,
+ 'show_in_menu' => 'edit.php?post_type=mm_video',
+ 'menu_position' => 8,
+ 'supports' => array( 'title', 'excerpt', 'page-attributes' ),
+ 'show_in_rest' => true,
+ ) );
+}
+add_action( 'init', 'mm_register_video_post_type' );
+
+
+// --- 2. Meta-Box: Video URL + Kategorie ---
+function mm_video_meta_boxes() {
+ add_meta_box(
+ 'mm_video_data',
+ __( 'Video-Einstellungen', 'minecraft-modern-theme' ),
+ 'mm_video_meta_box_html',
+ 'mm_video', 'normal', 'high'
+ );
+
+ add_meta_box(
+ 'mm_livestream_data',
+ __( 'Livestream-Einstellungen', 'minecraft-modern-theme' ),
+ 'mm_livestream_meta_box_html',
+ 'mm_livestream', 'normal', 'high'
+ );
+}
+add_action( 'add_meta_boxes', 'mm_video_meta_boxes' );
+
+function mm_video_meta_box_html( $post ) {
+ wp_nonce_field( 'mm_video_save', 'mm_video_nonce' );
+ $url = get_post_meta( $post->ID, '_mm_video_url', true );
+ $category = get_post_meta( $post->ID, '_mm_video_category', true );
+ $post_id = $post->ID;
+ ?>
+
+ ID, '_mm_livestream_url', true );
+ $player_url = get_post_meta( $post->ID, '_mm_livestream_player_url', true );
+ $owner = get_post_meta( $post->ID, '_mm_livestream_owner', true );
+ ?>
+
+ Tag ausgegeben, nicht iframe
+ }
+
+ return false;
+}
+
+function mm_video_get_type( $url ) {
+ if ( preg_match( '/youtube\.com|youtu\.be/', $url ) ) return 'youtube';
+ if ( strpos( $url, 'vimeo.com' ) !== false ) return 'vimeo';
+ if ( strpos( $url, 'twitch.tv' ) !== false ) return 'twitch';
+ if ( preg_match( '/\.(mp4|webm|ogv|ogg)(\?.*)?$/i', $url ) ) return 'mp4';
+ return 'unknown';
+}
+
+function mm_twitch_get_channel_from_url( $url ) {
+ if ( preg_match( '/twitch\.tv\/([a-zA-Z0-9_]+)(?:\/|\?|$)/', (string) $url, $m ) ) {
+ return strtolower( $m[1] );
+ }
+
+ return '';
+}
+
+function mm_twitch_get_app_token() {
+ $client_id = get_theme_mod( 'twitch_client_id', '' );
+ $client_secret = get_theme_mod( 'twitch_client_secret', '' );
+
+ if ( empty( $client_id ) || empty( $client_secret ) ) {
+ return false;
+ }
+
+ $cache_key = 'mm_twitch_app_token';
+ $cached = get_transient( $cache_key );
+ if ( ! empty( $cached ) ) {
+ return $cached;
+ }
+
+ $response = wp_remote_post( 'https://id.twitch.tv/oauth2/token', array(
+ 'timeout' => 8,
+ 'body' => array(
+ 'client_id' => $client_id,
+ 'client_secret' => $client_secret,
+ 'grant_type' => 'client_credentials',
+ ),
+ ) );
+
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ $data = json_decode( wp_remote_retrieve_body( $response ), true );
+ if ( empty( $data['access_token'] ) || empty( $data['expires_in'] ) ) {
+ return false;
+ }
+
+ $ttl = max( 60, (int) $data['expires_in'] - 60 );
+ set_transient( $cache_key, $data['access_token'], $ttl );
+
+ return $data['access_token'];
+}
+
+function mm_twitch_is_live( $channel ) {
+ $channel = sanitize_text_field( (string) $channel );
+ if ( $channel === '' ) {
+ return false;
+ }
+
+ $cache_key = 'mm_twitch_live_' . $channel;
+ $cached = get_transient( $cache_key );
+ if ( false !== $cached ) {
+ return $cached === 'live';
+ }
+
+ $client_id = get_theme_mod( 'twitch_client_id', '' );
+ $token = mm_twitch_get_app_token();
+ if ( empty( $client_id ) || empty( $token ) ) {
+ return false;
+ }
+
+ $url = add_query_arg( array(
+ 'user_login' => $channel,
+ ), 'https://api.twitch.tv/helix/streams' );
+
+ $response = wp_remote_get( $url, array(
+ 'timeout' => 8,
+ 'headers' => array(
+ 'Client-ID' => $client_id,
+ 'Authorization' => 'Bearer ' . $token,
+ ),
+ ) );
+
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ $data = json_decode( wp_remote_retrieve_body( $response ), true );
+ $is_live = ! empty( $data['data'][0] );
+ set_transient( $cache_key, $is_live ? 'live' : 'offline', 2 * MINUTE_IN_SECONDS );
+
+ return $is_live;
+}
+
+function mm_video_get_youtube_player_url( $identifier, $mode = 'video' ) {
+ $params = 'autoplay=0&rel=0&modestbranding=1';
+
+ if ( $mode === 'channel' ) {
+ return 'https://www.youtube.com/embed/live_stream?channel=' . rawurlencode( $identifier ) . '&' . $params;
+ }
+
+ if ( $mode === 'handle-live' ) {
+ return 'https://www.youtube.com/embed/' . ltrim( $identifier ) . '/live?' . $params;
+ }
+
+ return 'https://www.youtube.com/embed/' . rawurlencode( $identifier ) . '?' . $params;
+}
+
+function mm_video_get_youtube_handle_from_url( $url ) {
+ if ( preg_match( '~youtube\.com/(@[A-Za-z0-9._-]+)(?:/.*)?$~i', (string) $url, $matches ) ) {
+ return sanitize_text_field( $matches[1] );
+ }
+
+ return '';
+}
+
+function mm_video_extract_youtube_live_video_id( $html ) {
+ $patterns = array(
+ '/"canonicalBaseUrl":"\\/watch\?v=([A-Za-z0-9_-]{11})"/i',
+ '/"watchEndpoint":\{"videoId":"([A-Za-z0-9_-]{11})"/i',
+ '/\/watch\?v=([A-Za-z0-9_-]{11})\\u0026/i',
+ '/"videoId":"([A-Za-z0-9_-]{11})".{0,600}?"isLiveNow":true/is',
+ '/"videoId":"([A-Za-z0-9_-]{11})".{0,600}?"style":"LIVE"/is',
+ '/"videoId":"([A-Za-z0-9_-]{11})".{0,600}?LIVE_NOW/is',
+ '/https:\/\/www\.youtube\.com\/watch\?v=([A-Za-z0-9_-]{11})/i',
+ '/ 5 ) );
+ if ( ! is_wp_error( $response ) ) {
+ $data = json_decode( wp_remote_retrieve_body( $response ), true );
+ if ( ! empty( $data['items'][0] ) ) {
+ $state = $data['items'][0]['snippet']['liveBroadcastContent'] ?? '';
+ $is_live = ( $state === 'live' );
+ set_transient( $cache_key, $is_live ? 'live' : 'offline', 2 * MINUTE_IN_SECONDS );
+ return $is_live;
+ }
+ }
+ }
+
+ // Fallback: Wenn keine API, gehen wir davon aus dass die video_id aktuell ist
+ // weil sie von mm_video_resolve_youtube_live_video_id() kam
+ set_transient( $cache_key, 'live', 2 * MINUTE_IN_SECONDS );
+ return true;
+}
+
+function mm_video_resolve_youtube_live_video_id( $profile_url = '', $youtube_channel_id = '' ) {
+ $profile_url = trim( (string) $profile_url );
+ $youtube_channel_id = trim( (string) $youtube_channel_id );
+ $cache_key = 'mm_yt_live_v2_' . md5( strtolower( $profile_url . '|' . $youtube_channel_id ) );
+ $cached = get_transient( $cache_key );
+
+ if ( is_array( $cached ) && array_key_exists( 'video_id', $cached ) ) {
+ return $cached['video_id'];
+ }
+
+ $candidate_urls = array();
+ $handle = mm_video_get_youtube_handle_from_url( $profile_url );
+
+ if ( $youtube_channel_id && preg_match( '/^UC[a-zA-Z0-9_-]+$/', $youtube_channel_id ) ) {
+ $candidate_urls[] = 'https://www.youtube.com/channel/' . rawurlencode( $youtube_channel_id ) . '/live';
+ $candidate_urls[] = 'https://www.youtube.com/channel/' . rawurlencode( $youtube_channel_id ) . '/streams';
+ }
+
+ if ( $handle ) {
+ $candidate_urls[] = 'https://www.youtube.com/' . rawurlencode( ltrim( $handle, '/' ) ) . '/live';
+ $candidate_urls[] = 'https://www.youtube.com/' . rawurlencode( ltrim( $handle, '/' ) ) . '/streams';
+ }
+
+ if ( $profile_url ) {
+ $candidate_urls[] = untrailingslashit( $profile_url ) . '/live';
+ $candidate_urls[] = untrailingslashit( $profile_url ) . '/streams';
+ }
+
+ $candidate_urls = array_values( array_unique( array_filter( $candidate_urls ) ) );
+ $request_args = array(
+ 'timeout' => 10,
+ 'redirection' => 5,
+ 'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
+ 'headers' => array(
+ 'Accept-Language' => 'en-US,en;q=0.9,de;q=0.8',
+ ),
+ );
+
+ foreach ( $candidate_urls as $candidate_url ) {
+ $response = wp_remote_get( $candidate_url, $request_args );
+
+ if ( is_wp_error( $response ) ) {
+ continue;
+ }
+
+ $body = wp_remote_retrieve_body( $response );
+ if ( ! is_string( $body ) || $body === '' ) {
+ continue;
+ }
+
+ $video_id = mm_video_extract_youtube_live_video_id( $body );
+ if ( $video_id ) {
+ set_transient( $cache_key, array( 'video_id' => $video_id ), 2 * MINUTE_IN_SECONDS );
+ return $video_id;
+ }
+ }
+
+ set_transient( $cache_key, array( 'video_id' => '' ), 60 );
+ return '';
+}
+
+function mm_video_get_livestream_data( $profile_url = '', $player_url = '', $youtube_channel_id = '' ) {
+ if ( empty( $profile_url ) && empty( $player_url ) && empty( $youtube_channel_id ) ) {
+ return false;
+ }
+
+ $profile_url = trim( $profile_url );
+ $player_url = trim( $player_url );
+ $youtube_channel_id = trim( $youtube_channel_id );
+ $source_url = $player_url ? $player_url : $profile_url;
+ $parent_host = parse_url( home_url(), PHP_URL_HOST );
+ $data = array(
+ 'profile_url' => esc_url_raw( $profile_url ? $profile_url : $player_url ),
+ 'platform' => 'unknown',
+ 'label' => __( 'Livestream', 'minecraft-modern-theme' ),
+ 'icon' => 'fas fa-tower-broadcast',
+ 'color' => '#00d4ff',
+ 'cta' => __( 'Profil öffnen', 'minecraft-modern-theme' ),
+ 'embed_url' => '',
+ 'video_id' => '',
+ 'channel' => '',
+ 'channel_display' => '',
+ );
+
+ if ( ! empty( $youtube_channel_id ) && preg_match( '/^UC[a-zA-Z0-9_-]+$/', $youtube_channel_id ) ) {
+ $resolved_video_id = mm_video_resolve_youtube_live_video_id( $profile_url, $youtube_channel_id );
+
+ $data['platform'] = 'youtube';
+ $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-youtube';
+ $data['color'] = '#ff0000';
+ $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
+ $data['channel'] = $youtube_channel_id;
+ $data['channel_display'] = $youtube_channel_id;
+ $data['video_id'] = $resolved_video_id;
+ $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
+
+ if ( preg_match( '~youtube\.com/(@[A-Za-z0-9._-]+)(?:/.*)?$~i', $profile_url, $handle_matches ) ) {
+ $data['channel_display'] = sanitize_text_field( $handle_matches[1] );
+ }
+
+ if ( empty( $data['profile_url'] ) ) {
+ $data['profile_url'] = 'https://www.youtube.com/channel/' . rawurlencode( $youtube_channel_id );
+ }
+
+ if ( empty( $player_url ) ) {
+ return $data;
+ }
+ }
+
+ if ( empty( $source_url ) ) {
+ return false;
+ }
+
+ if ( preg_match( '/youtube\.com|youtu\.be/', $source_url ) && preg_match( '/(?:youtube\.com\/(?:watch\?v=|shorts\/|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/', $source_url ) ) {
+ $data['platform'] = 'youtube';
+ $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-youtube';
+ $data['color'] = '#ff0000';
+ $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
+ preg_match( '/(?:youtube\.com\/(?:watch\?v=|shorts\/|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/', $source_url, $video_match );
+ $data['video_id'] = ! empty( $video_match[1] ) ? $video_match[1] : '';
+ $data['embed_url'] = ! empty( $video_match[1] ) ? mm_video_get_youtube_player_url( $video_match[1], 'video' ) : '';
+ return $data;
+ }
+
+ if ( preg_match( '~twitch\.tv/([A-Za-z0-9_]+)(?:/)?(?:\?.*)?$~i', $source_url, $matches ) && strtolower( $matches[1] ) !== 'videos' ) {
+ $channel = sanitize_key( $matches[1] );
+ $data['platform'] = 'twitch';
+ $data['label'] = __( 'Twitch Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-twitch';
+ $data['color'] = '#9146ff';
+ $data['cta'] = __( 'Zum Twitch-Kanal', 'minecraft-modern-theme' );
+ $data['channel'] = $channel;
+ $data['channel_display'] = '@' . $channel;
+ $data['embed_url'] = 'https://player.twitch.tv/?channel=' . rawurlencode( $channel ) . '&parent=' . rawurlencode( $parent_host ) . '&autoplay=false';
+ return $data;
+ }
+
+ if ( preg_match( '~kick\.com/([A-Za-z0-9_]+)(?:/)?(?:\?.*)?$~i', $source_url, $matches ) ) {
+ $channel = sanitize_key( $matches[1] );
+ $data['platform'] = 'kick';
+ $data['label'] = __( 'Kick Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fas fa-satellite-dish';
+ $data['color'] = '#53fc18';
+ $data['cta'] = __( 'Zum Kick-Kanal', 'minecraft-modern-theme' );
+ $data['channel'] = $channel;
+ $data['channel_display'] = '@' . $channel;
+ $data['embed_url'] = 'https://player.kick.com/' . rawurlencode( $channel );
+ return $data;
+ }
+
+ if ( preg_match( '~youtube\.com/channel/(UC[a-zA-Z0-9_-]+)~i', $source_url, $matches ) ) {
+ $channel_id = sanitize_text_field( $matches[1] );
+ $resolved_video_id = mm_video_resolve_youtube_live_video_id( $source_url, $channel_id );
+ $data['platform'] = 'youtube';
+ $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-youtube';
+ $data['color'] = '#ff0000';
+ $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
+ $data['channel'] = $channel_id;
+ $data['channel_display'] = $channel_id;
+ $data['video_id'] = $resolved_video_id;
+ $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
+ return $data;
+ }
+
+ if ( preg_match( '~youtube\.com/(@[A-Za-z0-9._-]+)(?:/.*)?$~i', $source_url, $matches ) ) {
+ $handle = sanitize_text_field( $matches[1] );
+ $resolved_video_id = mm_video_resolve_youtube_live_video_id( $source_url, '' );
+ $data['platform'] = 'youtube';
+ $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-youtube';
+ $data['color'] = '#ff0000';
+ $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
+ $data['channel'] = ltrim( $handle, '@' );
+ $data['channel_display'] = $handle;
+ $data['video_id'] = $resolved_video_id;
+ $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
+ return $data;
+ }
+
+ if ( preg_match( '~youtube\.com/(?:c|user)/([A-Za-z0-9._-]+)(?:/.*)?$~i', $source_url, $matches ) ) {
+ $channel_name = sanitize_text_field( $matches[1] );
+ $resolved_video_id = mm_video_resolve_youtube_live_video_id( $source_url, '' );
+ $data['platform'] = 'youtube';
+ $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-youtube';
+ $data['color'] = '#ff0000';
+ $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
+ $data['channel'] = $channel_name;
+ $data['channel_display'] = '@' . ltrim( $channel_name, '@' );
+ $data['video_id'] = $resolved_video_id;
+ $data['embed_url'] = $resolved_video_id ? mm_video_get_youtube_player_url( $resolved_video_id, 'video' ) : '';
+ return $data;
+ }
+
+ if ( strpos( $source_url, 'youtube.com' ) !== false || strpos( $source_url, 'youtu.be' ) !== false ) {
+ $data['platform'] = 'youtube';
+ $data['label'] = __( 'YouTube Livestream', 'minecraft-modern-theme' );
+ $data['icon'] = 'fab fa-youtube';
+ $data['color'] = '#ff0000';
+ $data['cta'] = __( 'Zum YouTube-Kanal', 'minecraft-modern-theme' );
+ }
+
+ return $data;
+}
+
+function mm_video_get_livestream_item( $post_id ) {
+ $stream_url = get_post_meta( $post_id, '_mm_livestream_url', true );
+ $player_url = get_post_meta( $post_id, '_mm_livestream_player_url', true );
+ $owner_meta = trim( get_post_meta( $post_id, '_mm_livestream_owner', true ) );
+ $youtube_channel_id = get_post_meta( $post_id, '_mm_livestream_youtube_channel_id', true );
+ $stream = mm_video_get_livestream_data( $stream_url, $player_url, $youtube_channel_id );
+
+ if ( ! $stream || empty( $stream['profile_url'] ) ) {
+ return false;
+ }
+
+ $stream_title = trim( get_the_title( $post_id ) );
+ if ( empty( $stream_title ) || $stream_title === '(kein Titel)' || stripos( $stream_title, 'Automatisch gespeicherter Entwurf' ) !== false || stripos( $stream_title, 'Auto Draft' ) !== false ) {
+ $stream_title = ! empty( $stream['channel_display'] ) ? $stream['channel_display'] : $stream['label'];
+ }
+
+ $stream_excerpt = has_excerpt( $post_id ) ? get_the_excerpt( $post_id ) : '';
+ $owner = $owner_meta;
+ if ( empty( $owner ) ) {
+ $owner = ! empty( $stream['channel_display'] ) ? $stream['channel_display'] : $stream_title;
+ }
+
+ return array(
+ 'post_id' => $post_id,
+ 'owner' => $owner,
+ 'title' => $stream_title,
+ 'description' => $stream_excerpt,
+ 'profile_url' => $stream['profile_url'],
+ 'channel' => $stream['channel'],
+ 'stream' => $stream,
+ );
+}
+
+/**
+ * Automatische YouTube-Live-Erkennung via @Handle (Optimiert)
+ */
+
+/**
+ * 1. Hilfsfunktion: Wandelt @Handle in eine Channel-ID um
+ */
+function mm_get_channel_id_by_handle( $handle ) {
+ $api_key = get_theme_mod( 'youtube_api_key' );
+ if ( empty( $api_key ) || empty( $handle ) ) {
+ return false;
+ }
+
+ // Handle normalisieren (ohne @)
+ $handle = ltrim( $handle, '@' );
+
+ // Cache für die Channel-ID (diese ändert sich nie, daher 30 Tage speichern)
+ $cache_key = 'mm_id_for_' . $handle;
+ $channel_id = get_transient( $cache_key );
+ if ( false !== $channel_id ) {
+ return $channel_id;
+ }
+
+ // Suche Kanal zum Handle
+ $url = 'https://www.googleapis.com/youtube/v3/search?part=snippet&q=' . urlencode( '@' . $handle ) . '&type=channel&maxResults=1&key=' . $api_key;
+
+ $response = wp_remote_get( $url );
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ $data = json_decode( wp_remote_retrieve_body( $response ) );
+
+ if ( ! empty( $data->items[0]->snippet->channelId ) ) {
+ $id = $data->items[0]->snippet->channelId;
+ set_transient( $cache_key, $id, DAY_IN_SECONDS * 30 );
+ return $id;
+ }
+
+ return false;
+}
+
+/**
+ * 2. Hauptfunktion: Findet die Video-ID des aktuellen Livestreams
+ */
+function mm_get_youtube_live_id_from_handle( $handle ) {
+ $api_key = get_theme_mod( 'youtube_api_key' );
+ if ( empty( $api_key ) ) {
+ return false;
+ }
+
+ // 1. Kanal-ID zum Handle finden
+ $channel_id = mm_get_channel_id_by_handle( $handle );
+ if ( ! $channel_id ) {
+ return false;
+ }
+
+ // 2. Aktuellen Livestream in diesem Kanal suchen
+ $cache_key_live = 'mm_live_status_' . $channel_id;
+ $live_id = get_transient( $cache_key_live );
+ if ( false !== $live_id ) {
+ return ( $live_id === 'none' ) ? false : $live_id;
+ }
+
+ // Wir suchen direkt nach dem Live-Event des Kanals
+ $url = 'https://www.googleapis.com/youtube/v3/search?part=id&channelId=' . $channel_id . '&eventType=live&type=video&key=' . $api_key;
+
+ $response = wp_remote_get( $url );
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ $data = json_decode( wp_remote_retrieve_body( $response ) );
+
+ if ( ! empty( $data->items[0]->id->videoId ) ) {
+ $video_id = $data->items[0]->id->videoId;
+ set_transient( $cache_key_live, $video_id, 2 * MINUTE_IN_SECONDS ); // Nur 2 Min für schnellere Reaktion
+ return $video_id;
+ }
+
+ // Wenn nicht live, 'none' speichern, um API-Anfragen zu drosseln
+ set_transient( $cache_key_live, 'none', 2 * MINUTE_IN_SECONDS );
+ return false;
+}
+
+/**
+ * 3. Update für mm_video_get_livestream_groups - Hybrid-System
+ * Zeigt ALLE konfigurierten Livestreams an:
+ * - Livestream-Posts (Hauptmethode für mehrere Kanäle)
+ * - Optional: Customizer @Handle (für einzelnen Hauptkanal)
+ */
+function mm_video_get_livestream_groups() {
+ $groups = array();
+ $api_key = get_theme_mod( 'youtube_api_key', '' );
+
+ // === METHODE 1: Livestream-Posts (IMMER abfragen) ===
+ $posts = get_posts( array(
+ 'post_type' => 'mm_livestream',
+ 'post_status' => 'publish',
+ 'posts_per_page' => -1,
+ 'orderby' => 'ID',
+ 'order' => 'ASC',
+ ) );
+
+ if ( ! empty( $posts ) ) {
+ foreach ( $posts as $post ) {
+ $profile_url = get_post_meta( $post->ID, '_mm_livestream_url', true );
+ $stream_url = get_post_meta( $post->ID, '_mm_livestream_player_url', true );
+ $owner = get_post_meta( $post->ID, '_mm_livestream_owner', true );
+ $channel_id = '';
+
+ // Versuche Channel-ID aus Profil-URL zu extrahieren (nur für YouTube)
+ if ( ! empty( $profile_url ) && strpos( $profile_url, 'youtube.com' ) !== false ) {
+ // Channel-URL mit UC-ID
+ if ( preg_match( '~/channel/(UC[a-zA-Z0-9_-]+)~', $profile_url, $m ) ) {
+ $channel_id = $m[1];
+ }
+ // @Handle -> über API auflösen
+ elseif ( preg_match( '~/@([a-zA-Z0-9_.-]+)(?:/|$)~', $profile_url, $m ) && ! empty( $api_key ) ) {
+ $channel_id = mm_get_channel_id_by_handle( $m[1] );
+ }
+ }
+
+ // YouTube-Livestream: Prüfe ob live via API
+ if ( ! empty( $channel_id ) && ! empty( $api_key ) ) {
+ $cache_key_live = 'mm_live_status_' . $channel_id;
+ $live_id = get_transient( $cache_key_live );
+
+ if ( false === $live_id ) {
+ $search_url = add_query_arg( array(
+ 'part' => 'id',
+ 'channelId' => $channel_id,
+ 'eventType' => 'live',
+ 'type' => 'video',
+ 'key' => $api_key,
+ ), 'https://www.googleapis.com/youtube/v3/search' );
+
+ $response = wp_remote_get( $search_url, array( 'timeout' => 10 ) );
+
+ if ( ! is_wp_error( $response ) ) {
+ $data = json_decode( wp_remote_retrieve_body( $response ), true );
+
+ if ( isset( $data['items'][0]['id']['videoId'] ) ) {
+ $live_id = $data['items'][0]['id']['videoId'];
+ set_transient( $cache_key_live, $live_id, 2 * MINUTE_IN_SECONDS );
+ } else {
+ set_transient( $cache_key_live, 'none', 2 * MINUTE_IN_SECONDS );
+ $live_id = false;
+ }
+ } else {
+ $live_id = false;
+ }
+ } elseif ( $live_id === 'none' ) {
+ $live_id = false;
+ }
+
+ // Nur hinzufügen wenn live
+ if ( $live_id && $live_id !== 'none' ) {
+ $groups[] = array(
+ 'title' => $post->post_title,
+ 'platform' => 'youtube',
+ 'yt_id' => $live_id,
+ 'handle' => $profile_url,
+ );
+ }
+ }
+ // Andere Plattformen (Twitch, etc.): Prüfe zuerst direkte Stream-URL, sonst Profil-URL
+ elseif ( ! empty( $stream_url ) || ! empty( $profile_url ) ) {
+ $use_url = ! empty( $stream_url ) ? $stream_url : $profile_url;
+ $embed_url = mm_video_get_embed_url( $use_url );
+
+ // Nur Live-Streams anzeigen (Twitch muss live sein)
+ if ( $embed_url ) {
+ $platform = mm_video_get_type( $use_url );
+
+ if ( $platform !== 'twitch' ) {
+ continue;
+ }
+
+ $channel = mm_twitch_get_channel_from_url( $use_url );
+ if ( ! mm_twitch_is_live( $channel ) ) {
+ continue;
+ }
+
+ $groups[] = array(
+ 'title' => $post->post_title,
+ 'platform' => $platform,
+ 'embed_url' => $embed_url,
+ 'profile_url' => $profile_url,
+ 'owner' => $owner,
+ );
+ }
+ }
+ }
+ }
+
+ return $groups;
+}
+
+/**
+ * Debug-Informationen für Livestream-System
+ */
+function mm_video_get_api_livestream_debug() {
+ if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) {
+ return array();
+ }
+
+ $api_key = get_theme_mod( 'youtube_api_key', '' );
+
+ // Zähle Livestream-Posts
+ $posts_count = wp_count_posts( 'mm_livestream' );
+ $published_posts = isset( $posts_count->publish ) ? $posts_count->publish : 0;
+
+ // === Fall 1: Keine Konfiguration ===
+ if ( $published_posts === 0 ) {
+ return array(
+ 'status' => 'error',
+ 'message' => 'Keine Livestream-Posts gefunden',
+ 'hint' => 'Erstelle Livestream-Posts über "Livestreams → Neu"',
+ );
+ }
+
+ // === Fall 2: API Key fehlt (nur Warnung) ===
+ if ( empty( $api_key ) ) {
+ return array(
+ 'status' => 'warning',
+ 'message' => 'Kein YouTube API Key',
+ 'hint' => 'API Key wird für YouTube Live-Erkennung benötigt. Twitch-Streams funktionieren ohne.',
+ 'posts_count' => $published_posts,
+ );
+ }
+
+ // === Fall 3: Alles OK ===
+ return array(
+ 'status' => 'ok',
+ 'posts_count' => $published_posts,
+ 'api_key_length' => strlen( $api_key ),
+ );
+}
+
+function mm_video_get_hidden_livestream_count() {
+ // Für API-basiertes System nicht relevant
+ return 0;
+}
+
+function mm_video_get_livestream_debug_rows() {
+ // Ersetzt durch mm_video_get_api_livestream_debug()
+ return array();
+}
+
+function mm_video_render_livestream_group( $group, $index = 0 ) {
+ // Format 1: Neues API-YouTube-Format mit yt_id (Customizer oder Posts)
+ if ( isset( $group['yt_id'] ) && isset( $group['platform'] ) && $group['platform'] === 'youtube' ) {
+ $video_id = trim( (string) $group['yt_id'] );
+ $title = isset( $group['title'] ) ? $group['title'] : __( 'Live', 'minecraft-modern-theme' );
+ $handle = isset( $group['handle'] ) ? trim( (string) $group['handle'] ) : '';
+
+ if ( empty( $video_id ) ) {
+ return '';
+ }
+
+ $embed_url = 'https://www.youtube-nocookie.com/embed/' . rawurlencode( $video_id ) . '?autoplay=0&rel=0&modestbranding=1&playsinline=1';
+
+ ob_start();
+ ?>
+
+ array( 'icon' => 'fab fa-twitch', 'color' => '#9146ff' ),
+ 'vimeo' => array( 'icon' => 'fab fa-vimeo-v', 'color' => '#1ab7ea' ),
+ 'youtube' => array( 'icon' => 'fab fa-youtube', 'color' => '#ff0000' ),
+ );
+ $icon = isset( $platform_icons[$platform] ) ? $platform_icons[$platform]['icon'] : 'fas fa-play';
+
+ ob_start();
+ ?>
+
+ 1
+ ? __( 'Mehrere aktive oder gespeicherte Streams dieses Streamers lassen sich hier direkt umschalten.', 'minecraft-modern-theme' )
+ : __( 'Der Bereich wird automatisch aus deinem Profil-Link erzeugt und oberhalb der Videos eingebunden.', 'minecraft-modern-theme' ) );
+
+ ob_start();
+ ?>
+
+
+
+
+
+
+
+
+ 1 ) : ?>
+
+
+
+
+
+ 1 /
+
+
+
+
+
+
+ $item ) : ?>
+
+
+
+
+
+
+
+
+
+
+
+ ' . __('Ungültige Video-URL.', 'minecraft-modern-theme') . '';
+
+ $type = mm_video_get_type( $url );
+ $title = isset($args['title']) ? esc_attr($args['title']) : __('Video', 'minecraft-modern-theme');
+
+ if ( $type === 'mp4' ) {
+ return ''
+ . ''
+ . ''
+ . __('Dein Browser unterstützt kein HTML5-Video.', 'minecraft-modern-theme')
+ . '
';
+ }
+
+ return ''
+ . '
';
+}
+
+
+// --- 5. Shortcode: [mm_video url="..."] ---
+// Beispiele:
+// [mm_video url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
+// [mm_video url="https://vimeo.com/123456789" title="Mein Video"]
+function mm_video_shortcode( $atts ) {
+ $a = shortcode_atts( array(
+ 'url' => '',
+ 'title' => __('Video', 'minecraft-modern-theme'),
+ ), $atts );
+
+ if ( empty($a['url']) ) return '';
+ return mm_video_render_embed( $a['url'], $a );
+}
+add_shortcode( 'mm_video', 'mm_video_shortcode' );
+
+
+// --- 6. Template-Loader für Video-Archiv und Galerie-Seite ---
+function mm_video_template_loader( $template ) {
+ // Archiv (domain.de/videos/)
+ if ( is_post_type_archive('mm_video') ) {
+ $t = get_template_directory() . '/archive-video.php';
+ if ( file_exists($t) ) return $t;
+ }
+ // Seite mit dem Slug "videos"
+ if ( is_page() ) {
+ $obj = get_queried_object();
+ if ( $obj && $obj->post_name === 'videos' ) {
+ $t = get_template_directory() . '/archive-video.php';
+ if ( file_exists($t) ) return $t;
+ }
+ }
+ return $template;
+}
+add_filter( 'template_include', 'mm_video_template_loader' );
+
+
+// --- 7. "Videos"-Seite automatisch anlegen ---
+function mm_video_create_page() {
+ if ( ! get_page_by_path('videos') ) {
+ wp_insert_post( array(
+ 'post_title' => __('Videos', 'minecraft-modern-theme'),
+ 'post_name' => 'videos',
+ 'post_status' => 'publish',
+ 'post_type' => 'page',
+ 'post_author' => 1,
+ ) );
+ }
+}
+add_action( 'after_switch_theme', 'mm_video_create_page' );
+// Auch beim Speichern im Customizer anlegen
+add_action( 'customize_save_after', 'mm_video_create_page' );
+
+
+// =============================================================================
+// === BEWERBUNGSFORMULAR ======================================================
+// =============================================================================
+
+// --- 1. Customizer: Ein/Ausschalten ---
+function mm_bewerbung_customizer( $wp_customize ) {
+ $wp_customize->add_section( 'mm_bewerbung_section', array(
+ 'title' => __( 'Bewerbungsformular', 'minecraft-modern-theme' ),
+ 'priority' => 80,
+ ) );
+
+ $wp_customize->add_setting( 'mm_bewerbung_enabled', array(
+ 'default' => false,
+ 'sanitize_callback' => 'wp_validate_boolean',
+ ) );
+ $wp_customize->add_control( 'mm_bewerbung_enabled', array(
+ 'label' => __( 'Bewerbungsformular aktivieren', 'minecraft-modern-theme' ),
+ 'description' => __( 'Schaltet die Bewerbungsseite und das Formular ein.', 'minecraft-modern-theme' ),
+ 'section' => 'mm_bewerbung_section',
+ 'type' => 'checkbox',
+ ) );
+
+ $wp_customize->add_setting( 'mm_bewerbung_title', array(
+ 'default' => __( 'Bewirb dich bei uns!', 'minecraft-modern-theme' ),
+ 'sanitize_callback' => 'sanitize_text_field',
+ ) );
+ $wp_customize->add_control( 'mm_bewerbung_title', array(
+ 'label' => __( 'Titel der Bewerbungsseite', 'minecraft-modern-theme' ),
+ 'section' => 'mm_bewerbung_section',
+ 'type' => 'text',
+ ) );
+
+ $wp_customize->add_setting( 'mm_bewerbung_desc', array(
+ 'default' => __( 'Du möchtest Teil unseres Teams werden? Füll das Formular aus und wir melden uns bei dir.', 'minecraft-modern-theme' ),
+ 'sanitize_callback' => 'sanitize_textarea_field',
+ ) );
+ $wp_customize->add_control( 'mm_bewerbung_desc', array(
+ 'label' => __( 'Beschreibungstext', 'minecraft-modern-theme' ),
+ 'section' => 'mm_bewerbung_section',
+ 'type' => 'textarea',
+ ) );
+
+ $wp_customize->add_setting( 'mm_bewerbung_success_msg', array(
+ 'default' => __( 'Deine Bewerbung wurde erfolgreich eingereicht! Wir melden uns so bald wie möglich bei dir.', 'minecraft-modern-theme' ),
+ 'sanitize_callback' => 'sanitize_textarea_field',
+ ) );
+ $wp_customize->add_control( 'mm_bewerbung_success_msg', array(
+ 'label' => __( 'Erfolgsmeldung nach dem Absenden', 'minecraft-modern-theme' ),
+ 'section' => 'mm_bewerbung_section',
+ 'type' => 'textarea',
+ ) );
+
+ $wp_customize->add_setting( 'mm_bewerbung_min_alter', array(
+ 'default' => 14,
+ 'sanitize_callback' => 'absint',
+ ) );
+ $wp_customize->add_control( 'mm_bewerbung_min_alter', array(
+ 'label' => __( 'Mindestalter', 'minecraft-modern-theme' ),
+ 'description' => __( 'Bewerbungen unter diesem Alter werden abgelehnt. 0 = kein Limit.', 'minecraft-modern-theme' ),
+ 'section' => 'mm_bewerbung_section',
+ 'type' => 'number',
+ 'input_attrs' => array( 'min' => 0, 'max' => 99 ),
+ ) );
+
+ // ── Positionen ──
+ $wp_customize->add_setting( 'mm_bewerbung_positionen', array(
+ 'default' => "Moderator
+Supporter
+Builder
+Developer",
+ 'sanitize_callback' => 'sanitize_textarea_field',
+ ) );
+ $wp_customize->add_control( 'mm_bewerbung_positionen', array(
+ 'label' => __( 'Bewerbungs-Positionen', 'minecraft-modern-theme' ),
+ 'description' => __( 'Eine Position pro Zeile. Der Bewerber wählt genau eine davon aus.', 'minecraft-modern-theme' ),
+ 'section' => 'mm_bewerbung_section',
+ 'type' => 'textarea',
+ ) );
+}
+add_action( 'customize_register', 'mm_bewerbung_customizer' );
+
+// Hilfsfunktion: Positions-Liste als Array zurückgeben
+function mm_bewerbung_get_positionen() {
+ $raw = get_theme_mod( 'mm_bewerbung_positionen', "Moderator
+Supporter
+Builder
+Developer" );
+ $list = array_filter( array_map( 'trim', explode( "
+", $raw ) ) );
+ return array_values( $list );
+}
+
+
+// --- 2. Custom Post Type: Bewerbung ---
+function mm_register_bewerbung_cpt() {
+ if ( ! get_theme_mod( 'mm_bewerbung_enabled', false ) ) return;
+
+ register_post_type( 'mm_bewerbung', array(
+ 'labels' => array(
+ 'name' => __( 'Bewerbungen', 'minecraft-modern-theme' ),
+ 'singular_name' => __( 'Bewerbung', 'minecraft-modern-theme' ),
+ 'all_items' => __( 'Alle Bewerbungen', 'minecraft-modern-theme' ),
+ 'menu_name' => __( 'Bewerbungen', 'minecraft-modern-theme' ),
+ ),
+ 'public' => false,
+ 'show_ui' => true,
+ 'show_in_menu' => true,
+ 'menu_icon' => 'dashicons-clipboard',
+ 'menu_position' => 8,
+ 'supports' => array( 'title' ),
+ 'capabilities' => array(
+ 'create_posts' => 'do_not_allow', // Nur über Frontend erstellbar
+ ),
+ 'map_meta_cap' => true,
+ ) );
+}
+add_action( 'init', 'mm_register_bewerbung_cpt' );
+
+
+// --- 3. Admin: Meta-Box für Bewerbungs-Details ---
+function mm_bewerbung_meta_box() {
+ add_meta_box(
+ 'mm_bewerbung_details',
+ __( 'Bewerbungs-Details', 'minecraft-modern-theme' ),
+ 'mm_bewerbung_meta_box_html',
+ 'mm_bewerbung', 'normal', 'high'
+ );
+ add_meta_box(
+ 'mm_bewerbung_status_box',
+ __( 'Status', 'minecraft-modern-theme' ),
+ 'mm_bewerbung_status_box_html',
+ 'mm_bewerbung', 'side', 'high'
+ );
+}
+add_action( 'add_meta_boxes', 'mm_bewerbung_meta_box' );
+
+function mm_bewerbung_meta_box_html( $post ) {
+ $fields = array(
+ '_mm_bew_mc_name' => __( 'Minecraft Username', 'minecraft-modern-theme' ),
+ '_mm_bew_discord' => __( 'Discord Username', 'minecraft-modern-theme' ),
+ '_mm_bew_alter' => __( 'Alter', 'minecraft-modern-theme' ),
+ '_mm_bew_position' => __( 'Bewirbt sich als', 'minecraft-modern-theme' ),
+ '_mm_bew_warum' => __( 'Warum möchtest du mitspielen?', 'minecraft-modern-theme' ),
+ '_mm_bew_erfahrung' => __( 'Erfahrung / Vorstellung', 'minecraft-modern-theme' ),
+ '_mm_bew_datum' => __( 'Eingereicht am', 'minecraft-modern-theme' ),
+ '_mm_bew_ip' => __( 'IP-Adresse', 'minecraft-modern-theme' ),
+ );
+ echo '';
+}
+
+function mm_bewerbung_status_box_html( $post ) {
+ $status = get_post_meta( $post->ID, '_mm_bew_status', true ) ?: 'neu';
+ wp_nonce_field( 'mm_bew_status_save', 'mm_bew_status_nonce' );
+ $options = array(
+ 'neu' => array( 'label' => __('Neu','minecraft-modern-theme'), 'color' => '#0073aa' ),
+ 'in_pruef' => array( 'label' => __('In Prüfung','minecraft-modern-theme'), 'color' => '#f0ad4e' ),
+ 'angenommen' => array( 'label' => __('Angenommen','minecraft-modern-theme'), 'color' => '#46b450' ),
+ 'abgelehnt' => array( 'label' => __('Abgelehnt','minecraft-modern-theme'), 'color' => '#dc3232' ),
+ );
+ echo '';
+ foreach ( $options as $val => $opt ) {
+ $sel = selected( $status, $val, false );
+ echo '' . esc_html($opt['label']) . ' ';
+ }
+ echo ' ';
+ $cur = isset($options[$status]) ? $options[$status] : $options['neu'];
+ echo '' . esc_html($cur['label']) . '
';
+
+ // Admin-Notiz
+ $notiz = get_post_meta( $post->ID, '_mm_bew_notiz', true );
+ echo ' ';
+ echo '' . __('Interne Notiz:', 'minecraft-modern-theme') . ' ';
+ echo '' . esc_textarea($notiz) . ' ';
+}
+
+function mm_bewerbung_save_status( $post_id ) {
+ if ( ! isset($_POST['mm_bew_status_nonce']) || ! wp_verify_nonce($_POST['mm_bew_status_nonce'], 'mm_bew_status_save') ) return;
+ if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return;
+ if ( ! current_user_can('edit_post', $post_id) ) return;
+ if ( isset($_POST['mm_bew_status']) ) {
+ update_post_meta( $post_id, '_mm_bew_status', sanitize_text_field($_POST['mm_bew_status']) );
+ }
+ if ( isset($_POST['mm_bew_notiz']) ) {
+ update_post_meta( $post_id, '_mm_bew_notiz', sanitize_textarea_field($_POST['mm_bew_notiz']) );
+ }
+}
+add_action( 'save_post', 'mm_bewerbung_save_status' );
+
+
+// --- 4. Admin-Spalten in der Bewerbungsliste ---
+function mm_bewerbung_columns( $cols ) {
+ return array(
+ 'cb' => $cols['cb'],
+ 'title' => __( 'Name', 'minecraft-modern-theme' ),
+ 'mc_name' => __( 'Minecraft', 'minecraft-modern-theme' ),
+ 'discord' => __( 'Discord', 'minecraft-modern-theme' ),
+ 'alter' => __( 'Alter', 'minecraft-modern-theme' ),
+ 'position' => __( 'Position', 'minecraft-modern-theme' ),
+ 'status' => __( 'Status', 'minecraft-modern-theme' ),
+ 'datum' => __( 'Eingereicht', 'minecraft-modern-theme' ),
+ );
+}
+add_filter( 'manage_mm_bewerbung_posts_columns', 'mm_bewerbung_columns' );
+
+function mm_bewerbung_column_content( $col, $post_id ) {
+ $status_colors = array(
+ 'neu' => '#0073aa',
+ 'in_pruef' => '#f0ad4e',
+ 'angenommen' => '#46b450',
+ 'abgelehnt' => '#dc3232',
+ );
+ $status_labels = array(
+ 'neu' => __('Neu','minecraft-modern-theme'),
+ 'in_pruef' => __('In Prüfung','minecraft-modern-theme'),
+ 'angenommen' => __('Angenommen','minecraft-modern-theme'),
+ 'abgelehnt' => __('Abgelehnt','minecraft-modern-theme'),
+ );
+ switch ($col) {
+ case 'mc_name':
+ $mc = get_post_meta($post_id, '_mm_bew_mc_name', true);
+ if ($mc) {
+ echo '';
+ echo '
';
+ echo esc_html($mc) . '
';
+ }
+ break;
+ case 'discord':
+ echo esc_html( get_post_meta($post_id, '_mm_bew_discord', true) );
+ break;
+ case 'alter':
+ echo esc_html( get_post_meta($post_id, '_mm_bew_alter', true) );
+ break;
+ case 'position':
+ $pos = get_post_meta($post_id, '_mm_bew_position', true);
+ if ( $pos ) {
+ echo ''
+ . esc_html($pos) . ' ';
+ } else {
+ echo '— ';
+ }
+ break;
+ case 'status':
+ $s = get_post_meta($post_id, '_mm_bew_status', true) ?: 'neu';
+ $color = isset($status_colors[$s]) ? $status_colors[$s] : '#0073aa';
+ $label = isset($status_labels[$s]) ? $status_labels[$s] : $s;
+ echo '' . esc_html($label) . ' ';
+ break;
+ case 'datum':
+ echo esc_html( get_post_meta($post_id, '_mm_bew_datum', true) );
+ break;
+ }
+}
+add_action( 'manage_mm_bewerbung_posts_custom_column', 'mm_bewerbung_column_content', 10, 2 );
+
+
+// --- 5. AJAX: Formular absenden ---
+add_action( 'wp_ajax_mm_submit_bewerbung', 'mm_submit_bewerbung' );
+add_action( 'wp_ajax_nopriv_mm_submit_bewerbung', 'mm_submit_bewerbung' );
+
+function mm_submit_bewerbung() {
+ // Nonce prüfen
+ if ( ! isset($_POST['nonce']) || ! wp_verify_nonce($_POST['nonce'], 'mm_bewerbung_nonce') ) {
+ wp_send_json_error( array('msg' => __('Sicherheitsfehler. Bitte Seite neu laden.', 'minecraft-modern-theme')) );
+ }
+
+ // Felder validieren
+ $mc_name = sanitize_text_field( $_POST['mc_name'] ?? '' );
+ $discord = sanitize_text_field( $_POST['discord'] ?? '' );
+ $alter_raw = absint( $_POST['alter'] ?? 0 );
+ $warum = sanitize_textarea_field( $_POST['warum'] ?? '' );
+ $erfahrung = sanitize_textarea_field( $_POST['erfahrung'] ?? '' );
+
+ $position = sanitize_text_field( $_POST['position'] ?? '' );
+ $positionen = mm_bewerbung_get_positionen();
+
+ $errors = array();
+ if ( empty($mc_name) ) $errors[] = __('Minecraft Username ist erforderlich.', 'minecraft-modern-theme');
+ if ( empty($discord) ) $errors[] = __('Discord Username ist erforderlich.', 'minecraft-modern-theme');
+ if ( $alter_raw < 1 ) $errors[] = __('Bitte gib dein Alter an.', 'minecraft-modern-theme');
+ if ( ! empty($positionen) && ! in_array($position, $positionen, true) ) {
+ $errors[] = __('Bitte wähle eine gültige Position aus.', 'minecraft-modern-theme');
+ }
+ if ( empty($warum) ) $errors[] = __('Bitte erkläre warum du mitspielen möchtest.', 'minecraft-modern-theme');
+ if ( empty($erfahrung) ) $errors[] = __('Bitte stell dich kurz vor.', 'minecraft-modern-theme');
+
+ // Mindestalter prüfen
+ $min_alter = absint( get_theme_mod('mm_bewerbung_min_alter', 14) );
+ if ( $min_alter > 0 && $alter_raw < $min_alter ) {
+ $errors[] = sprintf( __('Du musst mindestens %d Jahre alt sein.', 'minecraft-modern-theme'), $min_alter );
+ }
+
+ // Doppelbewerbung prüfen (gleicher MC-Name in den letzten 30 Tagen)
+ $existing = get_posts( array(
+ 'post_type' => 'mm_bewerbung',
+ 'post_status' => 'publish',
+ 'meta_query' => array(
+ array( 'key' => '_mm_bew_mc_name', 'value' => $mc_name, 'compare' => '=' ),
+ ),
+ 'date_query' => array(
+ array( 'after' => '30 days ago' ),
+ ),
+ 'numberposts' => 1,
+ ) );
+ if ( $existing ) {
+ $errors[] = __('Mit diesem Minecraft-Namen wurde in den letzten 30 Tagen bereits eine Bewerbung eingereicht.', 'minecraft-modern-theme');
+ }
+
+ if ( ! empty($errors) ) {
+ wp_send_json_error( array('msg' => implode(' ', $errors)) );
+ }
+
+ // Bewerbung als Post speichern
+ $post_id = wp_insert_post( array(
+ 'post_type' => 'mm_bewerbung',
+ 'post_status' => 'publish',
+ 'post_title' => $mc_name . ' – ' . date_i18n('d.m.Y H:i'),
+ ) );
+
+ if ( is_wp_error($post_id) ) {
+ wp_send_json_error( array('msg' => __('Fehler beim Speichern. Bitte versuche es erneut.', 'minecraft-modern-theme')) );
+ }
+
+ update_post_meta( $post_id, '_mm_bew_mc_name', $mc_name );
+ update_post_meta( $post_id, '_mm_bew_discord', $discord );
+ update_post_meta( $post_id, '_mm_bew_alter', $alter_raw );
+ update_post_meta( $post_id, '_mm_bew_position', $position );
+ update_post_meta( $post_id, '_mm_bew_warum', $warum );
+ update_post_meta( $post_id, '_mm_bew_erfahrung', $erfahrung );
+ update_post_meta( $post_id, '_mm_bew_status', 'neu' );
+ update_post_meta( $post_id, '_mm_bew_datum', date_i18n('d.m.Y H:i:s') );
+ update_post_meta( $post_id, '_mm_bew_ip', sanitize_text_field( $_SERVER['REMOTE_ADDR'] ?? '' ) );
+
+ $success_msg = get_theme_mod( 'mm_bewerbung_success_msg', __('Deine Bewerbung wurde erfolgreich eingereicht! Wir melden uns so bald wie möglich bei dir.', 'minecraft-modern-theme') );
+ wp_send_json_success( array('msg' => $success_msg) );
+}
+
+
+// --- 6. Template-Loader ---
+function mm_bewerbung_template_loader( $template ) {
+ if ( ! get_theme_mod('mm_bewerbung_enabled', false) ) return $template;
+ if ( is_page('bewerbung') ) {
+ $t = get_template_directory() . '/page-bewerbung.php';
+ if ( file_exists($t) ) return $t;
+ }
+ return $template;
+}
+add_filter( 'template_include', 'mm_bewerbung_template_loader' );
+
+
+// --- 7. Seite automatisch anlegen ---
+function mm_bewerbung_create_page() {
+ if ( ! get_theme_mod('mm_bewerbung_enabled', false) ) return;
+ if ( ! get_page_by_path('bewerbung') ) {
+ wp_insert_post( array(
+ 'post_title' => __('Bewerbung', 'minecraft-modern-theme'),
+ 'post_name' => 'bewerbung',
+ 'post_status' => 'publish',
+ 'post_type' => 'page',
+ 'post_author' => 1,
+ ) );
+ }
+}
add_action( 'customize_save_after', 'mm_bewerbung_create_page' );
\ No newline at end of file