Upload functions.php via GUI

This commit is contained in:
2026-01-09 19:45:46 +00:00
parent b0d7f44d9a
commit 839b1a2e85

View File

@@ -61,7 +61,7 @@ function minecraft_modern_scripts() {
'announcement-script', 'announcement-script',
get_template_directory_uri() . '/js/announcement.js', get_template_directory_uri() . '/js/announcement.js',
array(), // Keine Abhängigkeiten array(), // Keine Abhängigkeiten
'1.0', '1.3', // Version angehoben für Countdown Update
true true
); );
@@ -431,7 +431,7 @@ add_action('wp_enqueue_scripts', 'minecraft_modern_scroll_to_top_script');
// === THEME SETTINGS EXPORT / IMPORT (KORRIGIERTE VERSION) ================= // === THEME SETTINGS EXPORT / IMPORT (KORRIGIERTE VERSION) =================
// ============================================================================= // =============================================================================
// 1. Export Handler (Download) // 1. Export Handler (Download) - ERWEITERT UM TEAM
add_action( 'admin_post_export_theme_settings', 'handle_theme_settings_export' ); add_action( 'admin_post_export_theme_settings', 'handle_theme_settings_export' );
function handle_theme_settings_export() { function handle_theme_settings_export() {
@@ -443,9 +443,46 @@ function handle_theme_settings_export() {
// Theme Slug ermitteln // Theme Slug ermitteln
$theme_slug = get_option( 'stylesheet' ); $theme_slug = get_option( 'stylesheet' );
// Alle Einstellungen holen // 1. Theme Mods (Customizer & Announcement Settings) holen
$mods = get_theme_mods(); $mods = get_theme_mods();
// 2. Announcement Settings manuell hinzufügen (siehe vorheriger Schritt)
$mods['mm_announcement_enabled'] = get_option('mm_announcement_enabled');
$mods['mm_announcement_text'] = get_option('mm_announcement_text');
$mods['mm_announcement_bg'] = get_option('mm_announcement_bg');
$mods['mm_announcement_color'] = get_option('mm_announcement_color');
$mods['mm_announcement_font_size'] = get_option('mm_announcement_font_size');
$mods['mm_announcement_font_family'] = get_option('mm_announcement_font_family');
$mods['mm_announcement_position'] = get_option('mm_announcement_position');
$mods['mm_announcement_countdown_enabled'] = get_option('mm_announcement_countdown_enabled');
$mods['mm_announcement_countdown_label'] = get_option('mm_announcement_countdown_label');
$mods['mm_announcement_countdown_date'] = get_option('mm_announcement_countdown_date');
$mods['mm_announcement_countdown_expired_msg'] = get_option('mm_announcement_countdown_expired_msg');
// 3. --- NEU: Team Daten holen ---
$team_data = array();
// Hole alle Teammitglieder sortiert nach Reihenfolge
$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() ),
// Bilder werden NICHT exportiert (siehe Hinweis unten)
);
endwhile;
}
$mods['team_data'] = $team_data;
// --- ENDE TEAM EXPORT ---
// Daten als JSON vorbereiten // Daten als JSON vorbereiten
$data = json_encode( $mods, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); $data = json_encode( $mods, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE );
@@ -459,7 +496,7 @@ function handle_theme_settings_export() {
exit; exit;
} }
// 2. Import Handler (AJAX) // 2. Import Handler (AJAX) - ERWEITERT UM TEAM
add_action( 'wp_ajax_import_theme_settings', 'handle_theme_settings_import' ); add_action( 'wp_ajax_import_theme_settings', 'handle_theme_settings_import' );
function handle_theme_settings_import() { function handle_theme_settings_import() {
@@ -484,11 +521,809 @@ function handle_theme_settings_import() {
wp_send_json_error( __( 'Die hochgeladene Datei ist keine gültige JSON-Datei.', 'minecraft-modern-theme' ) ); wp_send_json_error( __( 'Die hochgeladene Datei ist keine gültige JSON-Datei.', 'minecraft-modern-theme' ) );
} }
// Einstellungen in die Datenbank schreiben // 1. Theme Mods & Announcement importieren (siehe vorheriger Code)
foreach ( $data as $mod_key => $mod_value ) { foreach ( $data as $mod_key => $mod_value ) {
// Differenzierung wie beim Export
if ( strpos( $mod_key, 'mm_announcement_' ) === 0 ) {
update_option( $mod_key, $mod_value );
} else {
set_theme_mod( $mod_key, $mod_value ); set_theme_mod( $mod_key, $mod_value );
} }
}
wp_send_json_success( __( 'Einstellungen erfolgreich importiert! Die Seite wird neu geladen...', 'minecraft-modern-theme' ) ); // 2. --- NEU: Team Daten importieren ---
if ( isset( $data['team_data'] ) && ! empty( $data['team_data'] ) ) {
// Option 1: Vorherige Team-Mitglieder löschen (Clean Import)
// Wir force-delete alle Posts, damit die Liste exakt so ist wie im Backup.
$existing_team = new WP_Query(array(
'post_type' => 'team_member',
'posts_per_page' => -1,
'fields' => 'ids' // Nur IDs holen, schneller
));
if ( $existing_team->have_posts() ) {
while ( $existing_team->have_posts() ) {
wp_delete_post( $existing_team->next_post()->ID, true ); // true = force delete
}
}
// Neue Mitglieder anlegen
foreach ( $data['team_data'] as $member ) {
$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 ( $id && ! is_wp_error( $id ) ) {
// Rang als Meta-Data speichern
if ( isset( $member['rank'] ) ) {
update_post_meta( $id, '_team_member_rank', sanitize_text_field( $member['rank'] ) );
}
// Bilder werden NICFT importiert (siehe Hinweis unten)
}
}
}
// --- ENDE TEAM IMPORT ---
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' ) );
}