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' => '

', - 'after_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' => '

', - 'after_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' => '

', - 'after_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' => '

', - 'after_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' => '

', - 'after_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' => '

', - 'after_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' => '

', - 'after_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' => '

', - 'after_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 .= 'Minecraft Avatar ' . $i . ''; - } - } - $avatar_html .= '
'; - - if ( $has_avatars ) { - echo $avatar_html; - } -} -add_action( 'login_form', 'add_minecraft_avatar_slider_to_login' ); - - -function add_post_login_links() { - ?> -
- ← Zu -
- - - - - '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

- - - - - -
Aktivieren - -
- -

Inhalt

- - - - - -
Text - 6, 'media_buttons' => false, 'tinymce' => true, 'quicktags' => true ) ); ?> - -

Verfügbare Icons

-

Klicke auf ein Icon, um es in den Editor einzufügen.

-
- ' . $icon . ''; - } - ?> -
- - -

HTML erlaubt (z. B. <a>-Links).

- -
-
- -
-

Live Vorschau

-
-
- -

Position

- - - - - -
Anzeigeort - -
- -

Design

- - - - - - - - - - - - - - - - - -
Hintergrundfarbe
Textfarbe
Schriftgröße (px)
Schriftfamilie - -
- -

Countdown Timer

- - - - - - - - - - - - - - - - - -
Countdown aktivieren
Label (Text vor Timer)
Zieldatum & Uhrzeit
Nachricht nach Ablauf
- - -
-
- - ' . $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 ); - ?> -

- - -

- 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

-
-
-
- -
-
-
- -
-
-
- -
- -
- -
-
- -

UUID eingeben → Avatar wird automatisch geladen

-
- -
-
-
- - - Kein Bild gewählt -
-

Wird ignoriert wenn UUID gesetzt ist

-
- -
-
-
- - - Kein Banner gewählt -
-

Hintergrundbild der Card (optional)

-
-
- -
-
-
- -
-
-
- -
- -
-
- - -
-
- -
- - - - - - - - - - - - - - '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; - } - ?> - - - - - - - - - - '; - endif; - ?> - -
BildNameRangBioUUID / BildSort.Aktionen
-
- - - - 👤 - -
-
- -
- - - -
- -
-
- - - - - -
Noch keine Mitglieder vorhanden.
-
-
- - - $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 ) { ?> -
- - - -

- -

- - - -

- -

- - -
- - - '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' => '

', - 'after_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) . '

-

' . esc_html($desc_s) . '

-

' . esc_html($desc_m) . '

-
'; - - $btn_accept = ''; - $btn_select = ''; - $btn_neces = ''; - - $text_esc = wp_kses_post($text); - ?> - - - - __('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 '
' - . '
'; - } - - 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 ''; - foreach ( $fields as $key => $label ) { - $val = get_post_meta( $post->ID, $key, true ); - if ( ! $val ) continue; - $is_long = in_array( $key, array( '_mm_bew_warum', '_mm_bew_erfahrung' ) ); - echo ''; - echo ''; - echo ''; - } - echo '
' . esc_html($label) . ''; - if ( $is_long ) { - echo '
' . esc_html($val) . '
'; - } else { - echo '' . esc_html($val) . ''; - } - 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 ''; - $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 ''; - echo ''; -} - -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' => '

', + 'after_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' => '

', + 'after_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' => '

', + 'after_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' => '

', + 'after_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' => '

', + 'after_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' => '

', + 'after_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' => '

', + 'after_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' => '

', + 'after_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 .= 'Minecraft Avatar ' . $i . ''; + } + } + $avatar_html .= '
'; + + if ( $has_avatars ) { + echo $avatar_html; + } +} +add_action( 'login_form', 'add_minecraft_avatar_slider_to_login' ); + + +function add_post_login_links() { + ?> +
+ ← Zu +
+ + + + + '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

+ + + + + +
Aktivieren + +
+ +

Inhalt

+ + + + + +
Text + 6, 'media_buttons' => false, 'tinymce' => true, 'quicktags' => true ) ); ?> + +

Verfügbare Icons

+

Klicke auf ein Icon, um es in den Editor einzufügen.

+
+ ' . $icon . ''; + } + ?> +
+ + +

HTML erlaubt (z. B. <a>-Links).

+ +
+
+ +
+

Live Vorschau

+
+
+ +

Position

+ + + + + +
Anzeigeort + +
+ +

Design

+ + + + + + + + + + + + + + + + + +
Hintergrundfarbe
Textfarbe
Schriftgröße (px)
Schriftfamilie + +
+ +

Countdown Timer

+ + + + + + + + + + + + + + + + + +
Countdown aktivieren
Label (Text vor Timer)
Zieldatum & Uhrzeit
Nachricht nach Ablauf
+ + +
+
+ + ' . $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 ); + ?> +

+ + +

+ 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

+
+
+
+ +
+
+
+ +
+
+
+ +
+ +
+ +
+
+ +

UUID eingeben → Avatar wird automatisch geladen

+
+ +
+
+
+ + + Kein Bild gewählt +
+

Wird ignoriert wenn UUID gesetzt ist

+
+ +
+
+
+ + + Kein Banner gewählt +
+

Hintergrundbild der Card (optional)

+
+
+ +
+
+
+ +
+
+
+ +
+ +
+
+ + +
+
+ +
+ + + + + + + + + + + + + + '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; + } + ?> + + + + + + + + + + '; + endif; + ?> + +
BildNameRangBioUUID / BildSort.Aktionen
+
+ + + + 👤 + +
+
+ +
+ + + +
+ +
+
+ + + + + +
Noch keine Mitglieder vorhanden.
+
+
+ + + $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 ) { ?> +
+ + + +

+ +

+ + + +

+ +

+ + +
+ + + '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' => '

', + 'after_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) . '

+

' . esc_html($desc_s) . '

+

' . esc_html($desc_m) . '

+
'; + + $btn_accept = ''; + $btn_select = ''; + $btn_neces = ''; + + $text_esc = wp_kses_post($text); + ?> + + + + __('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 '
' + . '
'; + } + + 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 ''; + foreach ( $fields as $key => $label ) { + $val = get_post_meta( $post->ID, $key, true ); + if ( ! $val ) continue; + $is_long = in_array( $key, array( '_mm_bew_warum', '_mm_bew_erfahrung' ) ); + echo ''; + echo ''; + echo ''; + } + echo '
' . esc_html($label) . ''; + if ( $is_long ) { + echo '
' . esc_html($val) . '
'; + } else { + echo '' . esc_html($val) . ''; + } + 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 ''; + $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 ''; + echo ''; +} + +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