Files
Minecraft-Modern-Theme/Minecraft-Modern-Theme/functions.php
2026-02-10 22:25:55 +00:00

1413 lines
67 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// === Theme Setup ===
function minecraft_modern_setup() {
add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' );
// Logo-Unterstützung aktivieren (Maximale Flexibilität)
add_theme_support( 'custom-logo', array(
'height' => 9999, // Sehr hohe Werte, um den Crop-Dialog zu umgehen
'width' => 9999, // Sehr hohe Werte, um den Crop-Dialog zu umgehen
'flex-height' => true,
'flex-width' => true,
'header_text' => array( 'site-title', 'site-description' ),
) );
// Benutzerdefinierten Hintergrund aktivieren
add_theme_support( 'custom-background' );
register_nav_menus( array(
'primary' => __( 'Hauptmenü', 'minecraft-modern-theme' ),
'footer' => __( 'Footer-Menü', 'minecraft-modern-theme' ),
) );
add_theme_support( 'html5', array( 'search-form', 'comment-form', 'comment-list', 'gallery', 'caption' ) );
}
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() );
// Swiper.js CSS (von CDN)
wp_enqueue_style( 'swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.css' );
// JavaScript für den Header-Scroll-Effekt
wp_enqueue_script(
'minecraft-modern-header-script',
get_template_directory_uri() . '/js/header-scroll.js',
array(),
'1.0',
true
);
// NEU: Navigation Script für Dropdown Menü laden
wp_enqueue_script(
'minecraft-navigation',
get_template_directory_uri() . '/js/navigation.js',
array(), // Keine Abhängigkeiten
'1.0',
true
);
// NEU: Ankündigungs-Skript laden
wp_enqueue_script(
'announcement-script',
get_template_directory_uri() . '/js/announcement.js',
array(), // Keine Abhängigkeiten
'1.3', // Version angehoben für Countdown Update
true
);
// Swiper.js JS (von CDN)
wp_enqueue_script(
'swiper-js',
'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.min.js',
array(),
'8.0.0',
true
);
// Unsere eigene Slider-Initialisierungs-Datei
wp_enqueue_script(
'minecraft-modern-slider-script',
get_template_directory_uri() . '/js/slider-init.js',
array('swiper-js'), // Hängt von Swiper.js ab
'1.0',
true
);
// NEU: Theme-Toggle-Skript laden
wp_enqueue_script(
'theme-toggle-script',
get_template_directory_uri() . '/js/theme-toggle.js',
array(), // Keine Abhängigkeiten
'1.0',
true
);
// FAQ Skript laden, wenn der Post-Type aktiv ist
if ( post_type_exists('faq') ) {
wp_enqueue_script(
'faq-accordion-script',
get_template_directory_uri() . '/js/faq-accordion.js',
array(),
'1.0',
true
);
}
// Übergebe ALLE Theme-Einstellungen an das JavaScript
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' ),
'ajax_url' => admin_url('admin-ajax.php')
)
);
// Dies verhindert das Flackern im Customizer.
wp_localize_script(
'minecraft-modern-header-script', // <-- Richtiges Skript-Handle!
'headerSettings', // <-- Neuer Objektname für Klarheit
array(
'isCustomizer' => is_customize_preview()
)
);
}
add_action( 'wp_enqueue_scripts', 'minecraft_modern_scripts' );
// === Customizer-Datei laden ===
require get_template_directory() . '/inc/customizer.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' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>',
) );
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' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>',
) );
}
register_sidebar( array(
'name' => __( 'Footer Rechts', 'minecraft-modern-theme' ),
'id' => 'footer-right',
'description' => __( 'Widget-Bereich rechts im Footer.', 'minecraft-modern-theme' ),
'before_widget' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4 class="widget-title">',
'after_title' => '</h4>',
) );
}
add_action( 'widgets_init', 'minecraft_modern_footer_widgets' );
// === Homepage Sidebar registrieren ===
function minecraft_modern_homepage_sidebar() {
// Hauptbereich oben
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' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
) );
// Mittlerer Bereich 1
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' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
) );
// Mittlerer Bereich 2
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' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
) );
// Unterer Bereich
register_sidebar( array(
'name' => __( 'Startseiten Sidebar - Unten', 'minecraft-modern-theme' ),
'id' => 'homepage-sidebar-bottom',
'description' => __( 'Widget-Bereich unten in der Sidebar (z.B. für Social Media).', 'minecraft-modern-theme' ),
'before_widget' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
) );
// Zusätzlicher flexibler Bereich
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' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="widget-title">',
'after_title' => '</h3>',
) );
}
add_action( 'widgets_init', 'minecraft_modern_homepage_sidebar' );
// === FAQ Custom Post Type & Taxonomy ===
function create_faq_post_type() {
// Nur registrieren, wenn im Customizer aktiviert
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, // Archiv-Seite /faq/ bleibt als Fallback
'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 und Zuweisung (kombiniert) =======
// =============================================================================
/**
* Erstellt die "Home" Seite und weist sie automatisch als statische Startseite zu,
* wenn das Theme aktiviert wird und noch keine Seite festgelegt ist.
*/
function set_static_front_page_automatically() {
// Nur ausführen, wenn noch keine statische Seite als Startseite festgelegt ist
if ( 'page' !== get_option( 'show_on_front' ) ) {
// Finde die "Home" Seite (oder erstelle sie, falls sie nicht existiert)
$home_page = get_page_by_title( 'Home' );
if ( ! $home_page ) {
// Seite erstellen, falls sie nicht existiert
$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;
}
// Setze die Seite als statische Startseite
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' );
/**
* Fügt eine Body-Klasse hinzu, um den Home-Titel per CSS auszublenden.
*/
function add_home_body_class( $classes ) {
// Prüfen, ob wir auf der Startseite sind und die Einstellung zum Ausblenden aktiv ist
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' );
// =============================================================================
// Automatische FAQ-Seitenerstellung und Template-Zuweisung
// =============================================================================
/**
* Erstellt automatisch eine "FAQ" Seite, wenn die FAQ-Funktion aktiviert wird.
*/
function create_faq_page_automatically() {
// Prüfen, ob die FAQ-Funktion aktiv ist
if ( get_theme_mod( 'faq_enabled', true ) ) {
// Prüfen, ob die Seite bereits existiert
if ( get_page_by_title( 'FAQ' ) == null ) {
// Seite erstellen
$new_page = 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,
);
// Seite in die Datenbank einfügen
wp_insert_post( $new_page );
}
}
}
// Diese Funktion wird ausgeführt, wenn der Customizer gespeichert wird.
add_action( 'customize_save_after', 'create_faq_page_automatically' );
/**
* Leitet Anfragen für die "FAQ" Seite auf unser spezielles Template um.
*/
function load_faq_page_template( $template ) {
// Prüfen, ob die FAQ-Funktion aktiv ist
if ( get_theme_mod( 'faq_enabled', true ) ) {
// Prüfen, ob wir uns auf einer Seite befinden
if ( is_page() ) {
global $post;
// Prüfen, ob der Titel der Seite "FAQ" ist
if ( $post && $post->post_title == 'FAQ' ) {
// Pfad zu unserem Template zurückgeben
return get_template_directory() . '/archive-faq.php';
}
}
}
// Standard-Template in allen anderen Fällen
return $template;
}
add_filter( 'template_include', 'load_faq_page_template' );
// =========================================================================
// === CUSTOM LOGIN FUNCTIONS =============================================
// =========================================================================
// Lädt alle notwendigen Styles und Scripts nur für die Login-Seite
function minecraft_modern_login_assets() {
// Lade die Login-spezifische CSS
wp_enqueue_style('minecraft-modern-login-style', get_template_directory_uri() . '/css/login-style.css');
// Lade Font Awesome für Icons
wp_enqueue_style('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css');
// Lade das JavaScript für den Avatar-Slider
wp_enqueue_script('minecraft-avatar-slider-script', get_template_directory_uri() . '/js/login-slider.js', array('jquery'), '1.0', true);
// Lade das JavaScript, das die HTML-Struktur anpasst
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('<div class=\"login-options-container\"></div>');
});
");
// Übergebe die Slider-Geschwindigkeit aus dem Customizer an das JavaScript
$slider_speed = get_theme_mod('login_avatar_slider_speed', 4); // Standard: 4 Sekunden
wp_localize_script('minecraft-avatar-slider-script', 'avatarSliderSettings', array(
'speed' => $slider_speed * 1000 // Umwandlung in Millisekunden für JS
));
// Hintergrundbild und Logo als Inline-CSS hinzufügen
$login_bg_image = get_theme_mod('login_background_image');
if ($login_bg_image) {
$custom_css = "body.login { background-image: url('{$login_bg_image}') !important; }";
wp_add_inline_style('minecraft-modern-login-style', $custom_css);
}
$logo_url = get_theme_mod('login_logo');
if ($logo_url) {
$logo_css = ".login h1 a { background-image: url('{$logo_url}') !important; }";
wp_add_inline_style('minecraft-modern-login-style', $logo_css);
}
}
add_action('login_enqueue_scripts', 'minecraft_modern_login_assets');
// Erstellt die HTML-Struktur für den Avatar-Slider
function add_minecraft_avatar_slider_to_login() {
$avatar_html = '<div id="minecraft-avatar-slider">';
$has_avatars = false;
// Durchlaufe alle 5 möglichen Avatar-Plätze
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";
// Das erste Bild wird direkt angezeigt
$active_class = ($i === 1) ? 'avatar-slide-active' : '';
$avatar_html .= '<img class="avatar-slide ' . esc_attr($active_class) . '" src="' . esc_url($avatar_url) . '" alt="Minecraft Avatar ' . ($i + 1) . '">';
}
}
$avatar_html .= '</div>';
// Nur den Slider ausgeben, wenn mindestens ein Avatar hinterlegt wurde
if ($has_avatars) {
echo $avatar_html;
}
}
add_action('login_form', 'add_minecraft_avatar_slider_to_login');
function add_post_login_links() {
?>
<div class="post-login-links">
<a href="<?php echo esc_url(home_url()); ?>">&larr; Zu <?php bloginfo('name'); ?></a>
</div>
<?php
}
add_action('login_form_bottom', 'add_post_login_links');
// Entfernt unnötige Elemente von der Login-Seite
function customize_login_page() {
add_filter('login_display_language_dropdown', '__return_false');
add_filter('login_display_back_to_blog', '__return_false');
}
add_action('login_head', 'customize_login_page');
// Passt Login-URL und Titel an
function custom_login_url() { return home_url(); }
add_filter('login_headerurl', 'custom_login_url');
function custom_login_title() { return get_bloginfo('name'); }
add_filter('login_headertext', 'custom_login_title');
// =========================================================================
// === SCROLL TO TOP BUTTON ===============================================
// =========================================================================
// Fügt den HTML-Code für den Button zum Footer hinzu
function add_scroll_to_top_button() {
?>
<a href="#" id="scroll-to-top" title="Nach oben scrollen">
<i class="fas fa-chevron-up"></i>
</a>
<?php
}
add_action('wp_footer', 'add_scroll_to_top_button');
// Lädt das JavaScript für den Scroll-to-Top Button
function minecraft_modern_scroll_to_top_script() {
wp_enqueue_script(
'minecraft-scroll-to-top-script',
get_template_directory_uri() . '/js/scroll-to-top.js',
array('jquery'),
'1.0',
true
);
}
add_action('wp_enqueue_scripts', 'minecraft_modern_scroll_to_top_script');
// =============================================================================
// === THEME SETTINGS EXPORT / IMPORT (VOLLSTÄNDIG KORRIGIERT) ===============
// =============================================================================
// 1. Export Handler (Download) - ALLE EINSTELLUNGEN
add_action( 'admin_post_export_theme_settings', 'handle_theme_settings_export' );
function handle_theme_settings_export() {
// Sicherheitscheck
if ( ! current_user_can( 'edit_theme_options' ) ) {
wp_die( __( 'Du hast keine Berechtigung, diese Aktion auszuführen.', 'minecraft-modern-theme' ) );
}
// Theme Slug ermitteln
$theme_slug = get_option( 'stylesheet' );
// 1. Theme Mods (Customizer-Einstellungen) holen
$mods = get_theme_mods();
// 2. Announcement Settings manuell hinzufügen
$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 ) {
$mods[$key] = get_option($key);
}
// 3. Team Daten holen
$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();
$team_data[] = array(
'title' => get_the_title(),
'content' => get_the_content(),
'rank' => get_post_meta( get_the_ID(), '_team_member_rank', true ),
'menu_order' => get_post_field( 'menu_order', get_the_ID() ),
);
}
wp_reset_postdata(); // WICHTIG: Post-Daten zurücksetzen
}
$mods['team_data'] = $team_data;
// Daten als JSON vorbereiten
$data = json_encode( $mods, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
// Header für Download setzen
header( 'Content-Type: application/json; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=' . $theme_slug . '-settings-' . date( 'Y-m-d' ) . '.json' );
header( 'Pragma: no-cache' );
header( 'Expires: 0' );
echo $data;
exit;
}
// 2. Import Handler (AJAX) - ALLE EINSTELLUNGEN
add_action( 'wp_ajax_import_theme_settings', 'handle_theme_settings_import' );
function handle_theme_settings_import() {
// Sicherheitscheck & Nonce
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' ) );
}
// Datei einlesen
$file = $_FILES['import_file']['tmp_name'];
$json_content = file_get_contents( $file );
$data = json_decode( $json_content, true );
// JSON validieren
if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $data ) ) {
wp_send_json_error( __( 'Die hochgeladene Datei ist keine gültige JSON-Datei.', 'minecraft-modern-theme' ) );
}
// Announcement Keys definieren
$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'
);
// KRITISCHER FIX: Team Daten VORHER extrahieren und aus Array entfernen
$team_data = isset($data['team_data']) ? $data['team_data'] : array();
unset($data['team_data']); // Verhindert Fehler beim Durchlaufen der Theme Mods
// 1. Theme Mods & Announcement importieren
foreach ( $data as $mod_key => $mod_value ) {
// Unterscheidung: Announcement = Option, Rest = Theme Mod
if ( in_array( $mod_key, $announcement_keys ) ) {
update_option( $mod_key, $mod_value );
} else {
set_theme_mod( $mod_key, $mod_value );
}
}
// 2. Team Daten importieren
if ( ! empty( $team_data ) ) {
// Vorherige Team-Mitglieder löschen (Clean Import)
$existing_team = new WP_Query(array(
'post_type' => 'team_member',
'posts_per_page' => -1,
'fields' => 'ids'
));
if ( $existing_team->have_posts() ) {
foreach ( $existing_team->posts as $post_id ) {
wp_delete_post( $post_id, true ); // true = permanent löschen
}
}
wp_reset_postdata(); // WICHTIG: Post-Daten zurücksetzen
// Neue Mitglieder anlegen
foreach ( $team_data as $member ) {
$new_id = wp_insert_post(array(
'post_title' => sanitize_text_field( $member['title'] ),
'post_content' => sanitize_textarea_field( $member['content'] ),
'post_type' => 'team_member',
'post_status' => 'publish',
'menu_order' => isset( $member['menu_order'] ) ? intval( $member['menu_order'] ) : 0
));
if ( $new_id && ! is_wp_error( $new_id ) ) {
// Rang als Meta-Data speichern
if ( isset( $member['rank'] ) ) {
update_post_meta( $new_id, '_team_member_rank', sanitize_text_field( $member['rank'] ) );
}
}
}
}
wp_send_json_success( __( 'Einstellungen und Team erfolgreich importiert! Bilder müssen ggf. neu hochgeladen werden.', 'minecraft-modern-theme' ) );
}
/*
* -------------------------------------------------------------------------
* Announcement Bar (Admin + Frontend) vollständiges Modul mit Font-Vorschau & Countdown
* -------------------------------------------------------------------------
*/
/**
* Liste verfügbarer Fonts (Label, CSS-Family, Google-Flag, Google-Name)
*/
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'),
// Script / Schreibschrift
'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'),
);
}
/* ------------------ Admin: Einstellungen & Menü ------------------ */
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');
// --- NEU: Countdown Timer Settings ---
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');
/* ------------------ Admin: Seite (mit Font-Select & Vorschau) ------------------ */
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?';
?>
<div class="wrap mm-announcement-admin">
<h1>Header-Ankündigung</h1>
<p class="description">
Diese Leiste wird auf allen Seiten angezeigt. Die Vorschau unten zeigt sofort, wie die Ankündigung aussieht — Änderungen im Editor oder an den Design-Feldern wirken <strong>direkt in der Vorschau</strong>, erst wenn du auf <em>Änderungen speichern</em> klickst, werden die Einstellungen im Frontend übernommen.
</p>
<form method="post" action="options.php" id="mm-announcement-form">
<?php settings_fields('mm_announcement_group'); ?>
<h2>Allgemein</h2>
<table class="form-table">
<tr>
<th>Aktivieren</th>
<td>
<label>
<input type="checkbox" name="mm_announcement_enabled" value="1" <?php checked(1, get_option('mm_announcement_enabled')); ?>>
Ankündigung anzeigen
</label>
<p class="description">Wenn deaktiviert, wird die Leiste nicht angezeigt.</p>
</td>
</tr>
</table>
<h2>Inhalt</h2>
<table class="form-table">
<tr>
<th style="vertical-align: top;">Text</th>
<td>
<?php
// wp_editor mit ID mm_announcement_text (wichtig für JS)
wp_editor(
get_option('mm_announcement_text'),
'mm_announcement_text',
array(
'textarea_rows' => 6,
'media_buttons' => false,
'tinymce' => true,
'quicktags' => true,
)
);
?>
<!-- ========================= -->
<!-- Icon-Hilfe unter Editor -->
<!-- ========================= -->
<h3>Verfügbare Icons</h3>
<p class="description">Diese Icons kannst du direkt im Ankündigungstext verwenden. Klicke auf ein Icon, um es in den Editor einzufügen.</p>
<div id="mm-announcement-icon-list" style="display:flex; flex-wrap:wrap; gap:10px; margin-bottom:20px;">
<?php
// Liste der Symbole (Unicode / Emoji)
$icons = array('⚡', '‼️', '❗', '✅', '❌', '⭐', '🔥', '💡', '📢', '🎮', '🏆', '🔔', '🎉', '💬', '🛡️');
foreach($icons as $icon) {
echo '<button type="button" class="mm-icon-button" style="
font-size:20px;
padding:6px 10px;
border:1px solid #ccc;
border-radius:4px;
background:#f7f7f7;
cursor:pointer;
" title="Klicke zum Einfügen">'.$icon.'</button>';
}
?>
</div>
<script type="text/javascript">
jQuery(document).ready(function($){
$('#mm-announcement-icon-list .mm-icon-button').on('click', function(){
var icon = $(this).text();
// TinyMCE-Editor einfügen
if (typeof(tinymce) !== 'undefined') {
var editor = tinymce.get('mm_announcement_text');
if(editor) {
editor.execCommand('mceInsertContent', false, icon);
return;
}
}
// Fallback: normale Textarea
var textarea = $('#mm_announcement_text');
var start = textarea[0].selectionStart;
var end = textarea[0].selectionEnd;
var text = textarea.val();
textarea.val(text.substring(0, start) + icon + text.substring(end));
// Cursor nach eingefügtem Icon setzen
textarea[0].selectionStart = textarea[0].selectionEnd = start + icon.length;
textarea.focus();
});
});
</script>
<p class="description">HTML erlaubt (z. B. &lt;a&gt;-Links). Änderungen hier erscheinen sofort in der Vorschau — sie werden aber erst nach "Änderungen speichern" im Frontend übernommen.</p>
<!-- Vorschau direkt unter dem Editor (JETZT IM GLEICHEN TD) -->
<div id="mm-announcement-preview"
style="background: <?php echo $bg; ?>; color: <?php echo $color; ?>; padding: 12px; border-radius: 6px; box-shadow: 0 1px 4px rgba(0,0,0,0.06); margin-top: 14px;">
<div id="mm-announcement-preview-text"
style="font-size: <?php echo $selected_size; ?>px; font-family: <?php
$fonts = mm_announcement_get_font_list();
$key = $selected_font;
echo isset($fonts[$key]) ? esc_attr( $fonts[$key]['css'] ) : 'inherit';
?>; text-align:center;">
<?php
// Für Vorschau den reinen Text anzeigen (HTML gestrippt)
echo esc_html( $text_sample );
?>
</div>
<p style="margin-top:10px; color:#888; font-size:13px; text-align:center;">
Live Vorschau
</p>
</div>
<!-- / Vorschau -->
</td>
</tr>
</table>
<h2>Position</h2>
<table class="form-table">
<tr>
<th>Anzeigeort</th>
<td>
<select name="mm_announcement_position" id="mm-announcement-position">
<option value="top" <?php selected(get_option('mm_announcement_position'), 'top'); ?>>Ganz oben (über dem Header)</option>
<option value="below-header" <?php selected(get_option('mm_announcement_position'), 'below-header'); ?>>Unter dem Header (Standard für Seiten ohne Slider)</option>
</select>
<p class="description">Wähle die Position für die Anzeige. Die Vorschau zeigt die Position optisch — tatsächliche Frontend-Platzierung erfolgt nach Speichern.</p>
</td>
</tr>
</table>
<h2>Design</h2>
<table class="form-table">
<tr>
<th>Hintergrundfarbe</th>
<td><input type="color" name="mm_announcement_bg" id="mm-announcement-bg" value="<?php echo $bg; ?>"></td>
</tr>
<tr>
<th>Textfarbe</th>
<td><input type="color" name="mm_announcement_color" id="mm-announcement-color" value="<?php echo $color; ?>"></td>
</tr>
<tr>
<th>Schriftgröße (px)</th>
<td><input type="number" min="10" max="48" name="mm_announcement_font_size" id="mm-announcement-size" value="<?php echo $selected_size; ?>"></td>
</tr>
<tr>
<th>Schriftfamilie</th>
<td>
<select name="mm_announcement_font_family" id="mm-announcement-font">
<?php foreach ( $fonts as $key => $f ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $selected_font, $key ); ?>>
<?php echo esc_html( $f['label'] ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description">Wähle eine Schriftart. Die Vorschau lädt Google-Fonts automatisch für die Vorschau an (nur im Admin).</p>
</td>
</tr>
</table>
<!-- NEU: Countdown Timer Sektion -->
<h2>Countdown Timer</h2>
<table class="form-table">
<tr>
<th>Countdown aktivieren</th>
<td>
<label>
<input type="checkbox" name="mm_announcement_countdown_enabled" value="1" <?php checked(1, get_option('mm_announcement_countdown_enabled')); ?>>
Timer im Banner anzeigen
</label>
<p class="description">Zeigt einen Countdown neben dem Ankündigungstext an.</p>
</td>
</tr>
<tr>
<th>Label (Text vor dem Timer)</th>
<td>
<input type="text" name="mm_announcement_countdown_label" value="<?php echo esc_attr(get_option('mm_announcement_countdown_label', 'Event in:')); ?>" style="width: 100%;">
<p class="description">z.B. "Server Restart in:"</p>
</td>
</tr>
<tr>
<th>Zieldatum & Uhrzeit</th>
<td>
<input type="datetime-local" name="mm_announcement_countdown_date" value="<?php echo esc_attr(get_option('mm_announcement_countdown_date')); ?>">
<p class="description">Wähle das Datum und die Uhrzeit (lokale Server-Zeit).</p>
</td>
</tr>
<tr>
<th>Nachricht nach Ablauf</th>
<td>
<input type="text" name="mm_announcement_countdown_expired_msg" value="<?php echo esc_attr(get_option('mm_announcement_countdown_expired_msg', 'Event läuft gerade!')); ?>" style="width: 100%;">
<p class="description">Text, der angezeigt wird, wenn der Countdown bei 0 ist.</p>
</td>
</tr>
</table>
<?php submit_button('Änderungen speichern'); ?>
</form>
</div>
<?php
}
/* ------------------ Frontend: Render (global, via wp_body_open) ------------------ */
function mm_render_announcement_bar() {
if ( ! get_option('mm_announcement_enabled') ) {
return;
}
// Countdown Einstellungen holen
$countdown_enabled = get_option('mm_announcement_countdown_enabled', false);
$countdown_label = esc_attr(get_option('mm_announcement_countdown_label', 'Event in:'));
$countdown_date = esc_attr(get_option('mm_announcement_countdown_date', ''));
$countdown_expired = esc_html(get_option('mm_announcement_countdown_expired_msg', 'Event läuft gerade!'));
// sichere Werte aus Optionen
$position = esc_attr( get_option( 'mm_announcement_position', 'below-header' ) );
$bg = esc_attr( get_option( 'mm_announcement_bg', '#1e1e1e' ) );
$color = esc_attr( get_option( 'mm_announcement_color', '#ffffff' ) );
$size = (int) get_option( 'mm_announcement_font_size', 16 );
$font_key = get_option( 'mm_announcement_font_family', 'inherit' );
$fonts = mm_announcement_get_font_list();
$font_css = isset( $fonts[ $font_key ] ) ? $fonts[ $font_key ]['css'] : 'inherit';
$text = get_option( 'mm_announcement_text', '' );
// Escaping
$font_css_esc = wp_strip_all_tags( $font_css );
$text_kses = wp_kses_post( $text );
// Countdown HTML Container
$countdown_html = '';
if ($countdown_enabled && !empty($countdown_date)) {
$countdown_html = '<div class="mm-countdown-wrapper" style="display:inline-flex; align-items:center; margin-left:15px; padding-left:15px; border-left:1px solid rgba(255,255,255,0.3); font-weight:bold;">
<span class="mm-countdown-label">' . $countdown_label . ' </span>
<span class="mm-countdown-timer" data-date="' . $countdown_date . '" data-expired="' . $countdown_expired . '">Laden...</span>
</div>';
}
?>
<div id="mm-announcement"
data-position="<?php echo esc_attr($position); ?>"
style="
background: <?php echo esc_attr($bg); ?>;
color: <?php echo esc_attr($color); ?>;
font-size: <?php echo esc_attr($size); ?>px;
font-family: <?php echo esc_attr( $font_css_esc ); ?>;
">
<div class="mm-announcement-inner">
<div class="mm-announcement-text" style="display:inline-flex; align-items:center; width:100%; justify-content:center;">
<?php echo $text_kses; ?>
<?php echo $countdown_html; ?>
</div>
<button class="mm-announcement-close" aria-label="<?php esc_attr_e('Schließen','minecraft-modern-theme'); ?>">&times;</button>
</div>
</div>
<?php
}
add_action('wp_body_open', 'mm_render_announcement_bar');
/* ------------------ Assets: CSS & JS & Google Fonts ------------------ */
function mm_announcement_enqueue_assets() {
// Announcement CSS (theme/css/announcement.css)
wp_enqueue_style( 'mm-announcement-style', get_template_directory_uri() . '/css/announcement.css', array(), '1.2' );
// Announcement JS (frontend behavior)
wp_enqueue_script( 'mm-announcement-script', get_template_directory_uri() . '/js/announcement.js', array(), '1.3', true );
// Optional: Google Font load (only for selected font)
$fonts = mm_announcement_get_font_list();
$font_key = get_option('mm_announcement_font_family', 'inherit');
if ( isset( $fonts[ $font_key ] ) && ! empty( $fonts[ $font_key ]['google'] ) ) {
$google_name = $fonts[ $font_key ]['google_name'];
if ( $google_name ) {
$url = 'https://fonts.googleapis.com/css2?family=' . rawurlencode( $google_name ) . ':wght@400;700&display=swap';
wp_enqueue_style( 'mm-announcement-google-font', $url, array(), null );
}
}
}
add_action('wp_enqueue_scripts', 'mm_announcement_enqueue_assets');
/* -------------------------------------------------------------------------
* ANNOUNCEMENT ADMIN ASSETS
* ------------------------------------------------------------------------- */
function mm_announcement_admin_assets( $hook ) {
if ( $hook !== 'toplevel_page_mm-announcement' ) {
return;
}
// kleine Admin-CSS inline
wp_add_inline_style( 'wp-admin', '
.mm-announcement-admin .form-table th { width: 180px; vertical-align: top; }
.mm-announcement-admin .description { margin-top: 6px; color: #666; max-width: 720px; }
.mm-announcement-admin h2 { margin-top: 24px; }
#mm-announcement-preview { transition: all 140ms ease; }
#mm-announcement-preview-text { transition: font-family 160ms ease, font-size 120ms ease; white-space: normal; word-break: break-word; }
' );
// Admin JS (lade jQuery)
wp_enqueue_script( 'mm-announcement-admin-script', get_template_directory_uri() . '/js/mm-announcement-admin.js', array('jquery'), '1.1', true );
// Übergib Font-Metadaten an das Admin-Script
// Hinweis: Funktion mm_announcement_get_font_list() muss in deiner Datei definiert sein (aus vorherigem Code)
$fonts = mm_announcement_get_font_list();
wp_localize_script( 'mm-announcement-admin-script', 'MM_Announcement_Fonts', $fonts );
// Übergib aktuelle Werte für initiale Vorschau
$current = array(
'font' => 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') )
);
wp_localize_script( 'mm-announcement-admin-script', 'MM_Announcement_Current', $current );
}
add_action( 'admin_enqueue_scripts', 'mm_announcement_admin_assets' );
/* -------------------------------------------------------------------------
* COMPLETES TEAM MODUL (SICHER & FUNKTIONIEREND)
* ------------------------------------------------------------------------- */
// === 1. Custom Post Type Registrierung ===
function create_team_post_type() {
// Nur laden, wenn im Customizer aktiviert
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,
// FIX: Standard-Menü ausblenden, wir nutzen nur den Manager
'show_in_menu' => false,
)
);
}
}
add_action('init', 'create_team_post_type');
// === 2. Meta-Box für Rang ===
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 );
?>
<p>
<label for="team_member_rank" style="font-weight:600;">Rang:</label>
<input type="text" id="team_member_rank" name="team_member_rank" value="<?php echo esc_attr( $value ); ?>" style="width:100%;" placeholder="z.B. Admin, Mod">
</p>
<?php
}
function save_team_member_rank( $post_id ) {
if ( ! isset( $_POST['team_member_rank_nonce'] ) ) return;
if ( ! wp_verify_nonce( $_POST['team_member_rank_nonce'], 'team_member_rank_save' ) ) return;
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
if ( isset( $_POST['team_member_rank'] ) ) {
update_post_meta( $post_id, '_team_member_rank', sanitize_text_field( $_POST['team_member_rank'] ) );
}
}
add_action( 'save_post', 'save_team_member_rank' );
// === 3. Team Manager Admin Page ===
add_action('admin_menu', 'register_team_manager_page');
function register_team_manager_page() {
add_menu_page(
'Team Manager',
'Team Manager',
'manage_options',
'mm-team-manager',
'mm_team_manager_page_html',
'dashicons-groups',
6
);
// FIX: Media Uploader Skript laden
add_action('admin_enqueue_scripts', function($hook) {
if ($hook === 'toplevel_page_mm-team-manager') {
wp_enqueue_media();
}
});
}
function mm_team_manager_page_html() {
?>
<div class="wrap" style="max-width: 1200px;">
<h1>Team Verwaltung</h1>
<p>Hier kannst du Teammitglieder hinzufügen, sortieren und bearbeiten.</p>
<!-- Formular -->
<div class="card" style="background: #fff; border: 1px solid #ccd0d4; border-left: 4px solid #0073aa; padding: 20px; margin-bottom: 20px; box-shadow: 0 1px 1px rgba(0,0,0,.04);">
<h2>Neues Mitglied hinzufügen</h2>
<form id="mm-add-member-form" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; align-items: start;">
<div>
<label for="new_name"><strong>Name:</strong></label><br>
<input type="text" id="new_name" style="width: 100%;" required>
</div>
<div>
<label for="new_rank"><strong>Rang:</strong></label><br>
<input type="text" id="new_rank" style="width: 100%;" placeholder="z.B. Admin, Mod">
</div>
<div style="grid-column: 1 / -1;">
<label for="new_bio"><strong>Kurzbeschreibung:</strong></label><br>
<textarea id="new_bio" rows="2" style="width: 100%;" placeholder="Kurztext..."></textarea>
</div>
<div style="grid-column: 1 / -1; display: flex; align-items: center; gap: 15px;">
<div style="flex: 1;">
<label><strong>Bild:</strong></label><br>
<input type="hidden" id="new_image_id" value="">
<button type="button" class="button mm-upload-btn" data-target="new_image_id">Bild auswählen</button>
<span id="new_image_name" style="margin-left: 10px; color: #666;">Kein Bild gewählt</span>
</div>
<div id="new_image_preview" style="width: 60px; height: 60px; background: #eee; border: 1px solid #ccc; display: flex; align-items: center; justify-content: center; overflow: hidden;"></div>
</div>
<div style="grid-column: 1 / -1;">
<button type="submit" class="button button-primary button-large" style="width: 100%;">Hinzufügen</button>
</div>
</form>
</div>
<!-- Tabelle -->
<div class="card" style="background: #fff; padding: 20px; box-shadow: 0 1px 1px rgba(0,0,0,.04);">
<table class="wp-list-table widefat fixed striped" id="team-table" style="width: 100%; table-layout: fixed;">
<thead>
<tr>
<th style="width: 80px;">Bild</th>
<th style="width: 25%;">Name</th>
<th style="width: 20%;">Rang</th>
<th>Bio</th>
<th style="width: 80px; text-align: center;">Sort.</th>
<th style="width: 140px; text-align: right;">Aktionen</th>
</tr>
</thead>
<tbody id="team-list-body">
<?php
$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();
$id = get_the_ID();
$name = get_the_title();
$rank = get_post_meta($id, '_team_member_rank', true);
$bio = get_the_content();
$img_id = get_post_thumbnail_id($id);
?>
<tr data-id="<?php echo $id; ?>">
<td style="text-align: center;">
<div class="thumb-preview" style="width: 40px; height: 40px; background: #eee; display: inline-flex; align-items: center; justify-content: center; overflow: hidden; border-radius: 4px; border: 1px solid #ddd;">
<?php echo $img_id ? get_the_post_thumbnail($id, array(40,40)) : '<span style="font-size:20px;">👤</span>'; ?>
</div>
</td>
<td><input type="text" class="inline-edit" data-field="post_title" value="<?php echo esc_attr($name); ?>"></td>
<td><input type="text" class="inline-edit" data-field="_team_member_rank" value="<?php echo esc_attr($rank); ?>"></td>
<td><input type="text" class="inline-edit" data-field="post_content" value="<?php echo esc_attr(wp_strip_all_tags($bio)); ?>"></td>
<td style="text-align: center; vertical-align: middle;">
<button class="button button-small sort-up" title="Nach oben">▲</button>
<button class="button button-small sort-down" title="Nach unten">▼</button>
</td>
<td style="text-align: right; vertical-align: middle;">
<button class="button button-primary button-small save-row" title="Speichern">💾</button>
<button class="button button-small delete-row" title="Löschen">🗑️</button>
</td>
</tr>
<?php
endwhile;
else :
echo '<tr><td colspan="6" style="text-align:center;">Noch keine Mitglieder vorhanden.</td></tr>';
endif;
?>
</tbody>
</table>
</div>
</div>
<!-- CSS & JS -->
<style>
.inline-edit { width: 100%; box-sizing: border-box; padding: 6px; font-size: 13px; }
.card {width: 100%; max-width: 100%; padding: 25px; margin-top: 20px; border: 1px solid #c3c4c7; box-shadow: 0 1px 1px rgba(0, 0, 0, .04); background: #fff; box-sizing: border-box;}
#team-table td { overflow: hidden; }
.thumb-preview img { width: 100%; height: auto; object-fit: cover; }
.mm-upload-btn { margin-top: 5px; }
</style>
<script type="text/javascript">
jQuery(document).ready(function($) {
// --- 1. Media Uploader ---
var mediaUploader;
$(document).on('click', '.mm-upload-btn', function(e) {
e.preventDefault();
var targetInput = $(this).data('target');
var targetPreview = $(this).parent().find('div[id$="_preview"]');
var targetName = $(this).parent().find('span[id$="_name"]');
if (mediaUploader) { mediaUploader.open(); return; }
mediaUploader = wp.media.frames.file_frame = wp.media({
title: 'Bild auswählen', button: { text: 'Bild nutzen' }, multiple: false
});
mediaUploader.on('select', function() {
var attachment = mediaUploader.state().get('selection').first().toJSON();
$('#' + targetInput).val(attachment.id);
targetName.text(attachment.title);
targetPreview.html('<img src="' + attachment.sizes.thumbnail.url + '">');
});
mediaUploader.open();
});
// --- 2. Add Member ---
$('#mm-add-member-form').on('submit', function(e) {
e.preventDefault();
var btn = $(this).find('button[type="submit"]');
btn.prop('disabled', true).text('Lade...');
$.post(ajaxurl, {
action: 'mm_add_team_member',
name: $('#new_name').val(),
rank: $('#new_rank').val(),
bio: $('#new_bio').val(),
img_id: $('#new_image_id').val(),
nonce: '<?php echo wp_create_nonce('mm_team_nonce'); ?>'
}, function(response) {
if(response.success) { location.reload(); }
else { alert('Fehler: ' + response.data); btn.prop('disabled', false).text('Hinzufügen'); }
});
});
// --- 3. Save ---
$('.save-row').on('click', function() {
var row = $(this).closest('tr'); var btn = $(this);
var data = { action: 'mm_update_team_member', id: row.data('id'), nonce: '<?php echo wp_create_nonce('mm_team_nonce'); ?>' };
row.find('.inline-edit').each(function() {
var field = $(this).data('field'); var val = $(this).val();
if(field === 'post_title') data.title = val;
if(field === '_team_member_rank') data.rank = val;
if(field === 'post_content') data.bio = val;
});
btn.text('✓').prop('disabled', true);
$.post(ajaxurl, data, function(res) { setTimeout(function(){ btn.text('💾').prop('disabled', false); }, 1000); });
});
// --- 4. Delete ---
$('.delete-row').on('click', function() {
if(!confirm('Löschen?')) return; var row = $(this).closest('tr');
$.post(ajaxurl, { action: 'mm_delete_team_member', id: row.data('id'), nonce: '<?php echo wp_create_nonce('mm_team_nonce'); ?>' }, function() { row.fadeOut().remove(); });
});
// --- 5. Sort ---
$('.sort-up').on('click', function() { var row = $(this).closest('tr'); var prev = row.prev('tr'); if(prev.length > 0) row.insertBefore(prev); });
$('.sort-down').on('click', function() { var row = $(this).closest('tr'); var next = row.next('tr'); if(next.length > 0) row.insertAfter(next); });
});
</script>
<?php
}
// --- AJAX HANDLERS ---
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)) {
update_post_meta($id, '_team_member_rank', sanitize_text_field($_POST['rank']));
if (!empty($_POST['img_id'])) set_post_thumbnail($id, intval($_POST['img_id']));
wp_send_json_success('Hinzugefügt');
} else { wp_send_json_error('Fehler'); }
}
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']));
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');
}
// === 4. Automatische Team-Seite ===
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' );
// === 5. Template Loader (ROBUSTER FIX) ===
function load_team_page_template( $template ) {
if ( ! get_theme_mod( 'team_enabled', true ) ) return $template;
// 1. Check auf Archiv-Seite
if ( is_post_type_archive('team_member') ) { return get_template_directory() . '/archive-team.php'; }
// 2. Check auf Seite anhand des Slugs
if ( is_page() ) {
// FIX: Korrekte Methode das Page-Objekt zu holen
$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' );
// === 6. Customizer Settings ===
add_action( 'customize_register', 'team_customize_register' );
function team_customize_register( $wp_customize ) {
$wp_customize->add_section( 'team_settings', array( 'title' => 'Team Einstellungen', 'priority' => 65 ) );
$wp_customize->add_setting( 'team_enabled', array( 'default' => true, 'sanitize_callback' => 'wp_validate_boolean' ) );
$wp_customize->add_control( 'team_enabled', array( 'label' => 'Team Showcase aktivieren', 'section' => 'team_settings', 'settings' => 'team_enabled', 'type' => 'checkbox' ) );
}