5 Commits
Author SHA1 Message Date
M_Viper ac87d6194a festive-seasons-pro.php aktualisiert 2026-04-02 19:22:54 +00:00
M_Viper 74d2a573eb README.md aktualisiert 2026-03-01 11:22:22 +00:00
M_Viper 600791f339 festive-seasons-pro.php aktualisiert 2026-02-11 19:02:20 +00:00
M_Viper 056cf4d287 festive-seasons-pro.php aktualisiert 2026-01-06 23:30:11 +00:00
M_Viper af031542e9 festive-seasons-pro.php aktualisiert 2025-12-10 09:35:33 +00:00
2 changed files with 563 additions and 419 deletions
+4
View File
@@ -112,3 +112,7 @@ In den meisten Fällen liegt es an einem Caching-Problem.
➡️ Bitte auch dort den Cache löschen. ➡️ Bitte auch dort den Cache löschen.
--- ---
**Copyright © 2026 - M_Viper - Alle Rechte vorbehalten**
Die unbefugte Vervielfältigung, Verbreitung oder Weitergabe dieses Plugins ist strafbar und wird rechtlich verfolgt.
+559 -419
View File
@@ -1,419 +1,559 @@
<?php <?php
/* /*
Plugin Name: Festive Seasons Pro (Clean & Robust) Plugin Name: Festive Seasons Pro
Description: Zeigt saisonale Effekte (Schnee, Eier, Spinnen, Ballons, Feuerwerk) für feste Feiertage und individuelle Geburtstage an. Plugin URI: https://git.viper.ipv64.net/M_Viper/Festive-Seasons-Pro
Version: 3.4 Description: Zeigt saisonale Effekte (Schnee, Eier, Spinnen, Ballons, Feuerwerk) für feste Feiertage und individuelle Geburtstage an.
Author: M_Viper Version: 1.2
*/ Author: M_Viper
Author URI: https://m-viper.de
if (!defined('ABSPATH')) { Requires at least: 6.8
exit; Tested up to: 6.8
} PHP Version: 7.4
License: GPL2
class Festive_Seasons_Pro { License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: Festive-Seasons-Pro
public function __construct() { Tags: seasonal, festive, christmas, easter, halloween, new year, birthdays, celebrations, snow, confetti, fireworks
add_action('init', [$this, 'init_plugin']); Support: [Discord Support](https://discord.com/invite/FdRs4BRd8D)
} Support: [Telegram Support](https://t.me/M_Viper04)
*/
public function init_plugin() {
add_action('wp_enqueue_scripts', [$this, 'load_assets']); if (!defined('ABSPATH')) {
add_action('wp_footer', [$this, 'render_music_consent']); exit;
add_action('admin_menu', [$this, 'add_admin_menu_page']); }
add_action('admin_init', [$this, 'register_settings']);
add_action('widgets_init', [$this, 'register_widget']); // === FESTIVE SEASONS PRO - UPDATE SYSTEM ===
add_shortcode('festive_event', [$this, 'render_shortcode']);
} // Aktuelle Plugin-Version aus Plugin-Header lesen
function fsp_get_plugin_version() {
public function get_active_events() { if ( ! function_exists( 'get_plugin_data' ) ) {
if (isset($_GET['fsp_test_date'])) { require_once ABSPATH . 'wp-admin/includes/plugin.php';
$today = sanitize_text_field($_GET['fsp_test_date']); }
} else {
$today = date('Y-m-d'); $plugin_data = get_plugin_data( __FILE__ );
} return $plugin_data['Version'] ?? '0.0.0';
}
$active_events = [];
// Cache löschen (manueller Refresh)
foreach ($this->get_fixed_events() as $event_key => $event_data) { function fsp_clear_update_cache() {
if (isset($event_data['start'], $event_data['end']) && $today >= $event_data['start'] && $today <= $event_data['end']) { if ( isset($_GET['fsp_clear_cache']) && current_user_can('manage_options') ) {
if (get_option("fsp_fixed_{$event_key}", 'yes') === 'yes') { check_admin_referer('fsp_clear_cache_action');
$active_events[] = $event_data; delete_transient('fsp_latest_release');
} wp_redirect( admin_url('plugins.php') );
} exit;
} }
}
foreach ($this->get_custom_events($today) as $event_data) { add_action('admin_init', 'fsp_clear_update_cache');
$active_events[] = $event_data;
} // Neueste Release-Infos von Gitea holen
function fsp_get_latest_release_info( $force_refresh = false ) {
return $active_events; $transient_key = 'fsp_latest_release';
}
if ( $force_refresh ) {
private function get_fixed_events() { delete_transient( $transient_key );
$test_year = isset($_GET['fsp_test_date']) ? substr(sanitize_text_field($_GET['fsp_test_date']), 0, 4) : date('Y'); }
$year = is_numeric($test_year) ? $test_year : date('Y');
$easter_date = date('Y-m-d', easter_date($year)); $release_info = get_transient( $transient_key );
return [ if ( false === $release_info ) {
'advent' => ['name' => 'Adventszeit', 'start' => "$year-12-01", 'end' => "$year-12-23", 'class' => 'festive-advent', 'effects' => ['snow']], $response = wp_remote_get(
'christmas' => ['name' => 'Weihnachten', 'start' => "$year-12-24", 'end' => "$year-12-26", 'class' => 'festive-christmas', 'effects' => ['snow', 'music', 'star', 'santa']], // NEU: Santa-Animation 'https://git.viper.ipv64.net/api/v1/repos/M_Viper/Festive-Seasons-Pro/releases/latest',
'newyear' => ['name' => 'Silvester', 'start' => "$year-12-31", 'end' => ($year + 1) . "-01-01", 'class' => 'festive-newyear', 'effects' => ['newyear_fireworks']], ['timeout' => 10]
'halloween' => ['name' => 'Halloween', 'start' => "$year-10-31", 'end' => "$year-10-31", 'class' => 'festive-halloween', 'effects' => ['spiders']], );
'easter' => ['name' => 'Ostern', 'start' => date('Y-m-d', strtotime($easter_date . ' -1 day')), 'end' => date('Y-m-d', strtotime($easter_date)), 'class' => 'festive-easter', 'effects' => ['eggs']],
]; if ( ! is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response) ) {
} $body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
private function get_custom_events($today) {
$custom_events = get_option('fsp_events', []); if ( $data && isset($data['tag_name']) ) {
$active_custom_events = []; $tag = ltrim($data['tag_name'], 'v');
$today_day_month = date('m-d', strtotime($today));
$release_info = [
if (is_array($custom_events)) { 'version' => $tag,
foreach ($custom_events as $event) { 'download_url' => $data['zipball_url'] ?? '',
if (empty($event['date']) || !is_array($event)) continue; 'notes' => $data['body'] ?? '',
$event_date = sanitize_text_field($event['date']); 'published_at' => $data['published_at'] ?? '',
$date_obj = DateTime::createFromFormat('d-m', $event_date) ?: DateTime::createFromFormat('Y-m-d', $event_date); ];
if ($date_obj && $date_obj->format('m-d') === $today_day_month) {
$name = trim(($event['name'] ?? '') . ' ' . ($event['text'] ?? '')); set_transient( $transient_key, $release_info, 6 * HOUR_IN_SECONDS );
$active_custom_events[] = [ } else {
'name' => $name ?: 'Besonderer Tag!', set_transient( $transient_key, [], HOUR_IN_SECONDS );
'class' => 'festive-birthday', }
'effects' => ['balloons', 'confetti'] } else {
]; set_transient( $transient_key, [], HOUR_IN_SECONDS );
} }
} }
}
return $active_custom_events; return $release_info;
} }
public function load_assets() { // Admin-Notice für Plugin-Update
$active_events = $this->get_active_events(); function fsp_show_update_notice() {
if (empty($active_events)) return; if ( ! current_user_can('manage_options') ) {
return;
$body_classes = []; }
$effects_to_load = [];
$current_version = fsp_get_plugin_version();
foreach ($active_events as $event) { $latest_release = fsp_get_latest_release_info();
if (!empty($event['class'])) $body_classes[] = $event['class'];
if (!empty($event['effects'])) $effects_to_load = array_merge($effects_to_load, $event['effects']); if ( ! empty($latest_release['version']) && version_compare($current_version, $latest_release['version'], '<') ) {
} $refresh_url = wp_nonce_url(
admin_url('plugins.php?fsp_clear_cache=1'),
add_filter('body_class', function($classes) use ($body_classes) { 'fsp_clear_cache_action'
return array_merge($classes, array_unique($body_classes)); );
}); ?>
<div class="notice notice-warning is-dismissible">
wp_enqueue_style('fsp-style', plugin_dir_url(__FILE__) . 'assets/css/festive.css', [], '3.4'); <h3>Festive Seasons Pro – Update verfügbar</h3>
wp_enqueue_script('jquery'); <p>
Aktuelle Version: <strong><?php echo esc_html($current_version); ?></strong><br>
wp_localize_script('jquery', 'fsp_vars', [ Neueste Version: <strong><?php echo esc_html($latest_release['version']); ?></strong>
'plugin_url' => plugin_dir_url(__FILE__) </p>
]); <p>
<a href="<?php echo esc_url($latest_release['download_url']); ?>" class="button button-primary" target="_blank">
if (in_array('confetti', $effects_to_load) || in_array('newyear_fireworks', $effects_to_load)) { Update herunterladen
wp_enqueue_script('fsp-confetti-lib', plugin_dir_url(__FILE__) . 'assets/js/canvas-confetti.min.js', [], '3.4', true); </a>
} <a href="https://git.viper.ipv64.net/M_Viper/Festive-Seasons-Pro/releases" class="button" target="_blank">
Release Notes
foreach (array_unique($effects_to_load) as $effect) { </a>
switch ($effect) { <a href="<?php echo esc_url($refresh_url); ?>" class="button">
case 'snow': Jetzt neu prüfen
wp_enqueue_script('fsp-snow', plugin_dir_url(__FILE__) . 'assets/js/snowstorm.js', [], '3.4', true); </a>
break; </p>
case 'confetti': </div>
wp_add_inline_script('fsp-confetti-lib', 'jQuery(document).ready(function($) { setTimeout(() => confetti({particleCount: 200, spread: 120, origin: { y: 0.6 } }), 1000); });'); <?php
break; }
case 'eggs': }
wp_enqueue_script('fsp-eggs', plugin_dir_url(__FILE__) . 'assets/js/easter-eggs.js', ['jquery'], '3.4', true); add_action('admin_notices', 'fsp_show_update_notice');
break;
case 'spiders':
wp_enqueue_script('fsp-spiders', plugin_dir_url(__FILE__) . 'assets/js/halloween-spiders.js', ['jquery'], '3.4', true); class Festive_Seasons_Pro {
break;
case 'balloons': public function __construct() {
wp_enqueue_script('fsp-balloons', plugin_dir_url(__FILE__) . 'assets/js/balloons.js', ['jquery'], '3.4', true); add_action('init', [$this, 'init_plugin']);
break; }
case 'star':
wp_enqueue_script('fsp-star', plugin_dir_url(__FILE__) . 'assets/js/christmas-star.js', ['jquery'], '3.4', true); public function init_plugin() {
break; add_action('wp_enqueue_scripts', [$this, 'load_assets']);
case 'santa': // NEU: Santa-Animation add_action('wp_footer', [$this, 'render_music_consent']);
wp_enqueue_script('fsp-santa', plugin_dir_url(__FILE__) . 'assets/js/santa-sleigh.js', ['jquery'], '3.4', true); add_action('admin_menu', [$this, 'add_admin_menu_page']);
break; add_action('admin_init', [$this, 'register_settings']);
case 'newyear_fireworks': add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
$this->add_newyear_script(); add_action('widgets_init', [$this, 'register_widget']);
break; add_shortcode('festive_event', [$this, 'render_shortcode']);
} }
}
} // Admin-Scripts laden
public function enqueue_admin_scripts($hook) {
private function add_newyear_script() { // Nur auf der Plugin-Einstellungsseite laden
$script = ' if ($hook !== 'settings_page_festive-seasons-pro') {
jQuery(document).ready(function($) { return;
if (!$("body").hasClass("festive-newyear")) { }
return;
} // jQuery ist standardmäßig in WordPress verfügbar
if (typeof confetti === "undefined") { wp_enqueue_script('jquery');
console.error("FSP: Konfetti-Bibliothek nicht gefunden.");
return; // Inline-Script für Event-Verwaltung
} wp_add_inline_script('jquery', '
jQuery(document).ready(function($) {
function runBigFireworks() { // Neues Ereignis hinzufügen
var count = 250; $("#fsp-add-event").on("click", function() {
var defaults = { origin: { y: 0.7 }, zIndex: 999999, colors: ["#bb0000", "#ffffff", "#ff0000", "#ffbb00", "#ffee00", "#00ff00", "#0099ff", "#0000ff"] }; var index = $("#fsp-events-container .fsp-event-row").length;
function fire(particleRatio, opts) { var newRow = \'<div class="fsp-event-row" style="margin-bottom: 10px; display: flex; gap: 10px; align-items: center;">\' +
confetti(Object.assign({}, defaults, opts, { particleCount: Math.floor(count * particleRatio) })); \'<input type="text" name="fsp_events[\' + index + \'][name]" placeholder="Name (z.B. Max)" style="width: 200px;" />\' +
} \'<input type="text" name="fsp_events[\' + index + \'][date]" placeholder="Datum (TT-MM)" style="width: 100px;" />\' +
fire(0.25, { spread: 26, startVelocity: 55 }); \'<input type="text" name="fsp_events[\' + index + \'][text]" placeholder="Text (z.B. wird 30!)" style="flex-grow: 1;" />\' +
fire(0.2, { spread: 60 }); \'<button type="button" class="button button-secondary fsp-remove-event">Entfernen</button>\' +
fire(0.35, { spread: 100, decay: 0.91, scalar: 0.8 }); \'</div>\';
fire(0.1, { spread: 120, startVelocity: 25, decay: 0.92, scalar: 1.2 }); $("#fsp-events-container").append(newRow);
fire(0.1, { spread: 120, startVelocity: 45 }); });
var rainDefaults = { origin: { y: 0 }, angle: 90, drift: 0, gravity: 1.2, scalar: 1.2, zIndex: 999999 }; // Ereignis entfernen
confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.1, colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"] })); $(document).on("click", ".fsp-remove-event", function() {
confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.5, colors: ["#bb0000", "#ffffff", "#ffbb00", "#ffee00"] })); $(this).closest(".fsp-event-row").remove();
confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.9, colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"] })); });
} });
');
function createRocket(isLastRocket) { }
const startX = Math.random() * window.innerWidth;
public function get_active_events() {
const $rocket = $("<div class=\"fsp-rocket\"></div>").css({ if (isset($_GET['fsp_test_date'])) {
position: "fixed", $today = sanitize_text_field($_GET['fsp_test_date']);
width: "6px", } else {
height: "25px", $today = date('Y-m-d');
background: "linear-gradient(to top, #ff4500, #ffa500)", }
bottom: "-30px",
left: startX + "px", $active_events = [];
zIndex: 999999,
borderRadius: "50% 50% 0 0", foreach ($this->get_fixed_events() as $event_key => $event_data) {
boxShadow: "0 0 15px 5px rgba(255, 165, 0, 0.8)" if (isset($event_data['start'], $event_data['end']) && $today >= $event_data['start'] && $today <= $event_data['end']) {
}); if (get_option("fsp_fixed_{$event_key}", 'yes') === 'yes') {
$active_events[] = $event_data;
$("body").append($rocket); }
}
$rocket.animate( }
{ bottom: window.innerHeight * 0.8 },
{ foreach ($this->get_custom_events($today) as $event_data) {
duration: 1800, $active_events[] = $event_data;
easing: "linear", }
complete: function() {
const x = startX / window.innerWidth; return $active_events;
const y = 0.2; }
confetti({ private function get_fixed_events() {
particleCount: 75, $test_year = isset($_GET['fsp_test_date']) ? substr(sanitize_text_field($_GET['fsp_test_date']), 0, 4) : date('Y');
spread: 100, $year = is_numeric($test_year) ? $test_year : date('Y');
origin: { x: x, y: y }, $easter_date = date('Y-m-d', easter_date($year));
colors: ["#ff0000", "#ffa500", "#ffff00", "#ffffff"],
gravity: 1.1, return [
scalar: 1.0, 'advent' => ['name' => 'Adventszeit', 'start' => "$year-12-01", 'end' => "$year-12-23", 'class' => 'festive-advent', 'effects' => ['snow']],
startVelocity: 40 'christmas' => ['name' => 'Weihnachten', 'start' => "$year-12-24", 'end' => "$year-12-26", 'class' => 'festive-christmas', 'effects' => ['snow', 'music', 'star', 'santa']],
}); 'newyear' => ['name' => 'Silvester', 'start' => "$year-12-31", 'end' => ($year + 1) . "-01-01", 'class' => 'festive-newyear', 'effects' => ['newyear_fireworks']],
'halloween' => ['name' => 'Halloween', 'start' => "$year-10-31", 'end' => "$year-10-31", 'class' => 'festive-halloween', 'effects' => ['spiders']],
if (isLastRocket) { 'easter' => ['name' => 'Ostern', 'start' => date('Y-m-d', strtotime($easter_date . ' -1 day')), 'end' => date('Y-m-d', strtotime($easter_date)), 'class' => 'festive-easter', 'effects' => ['eggs']],
setTimeout(runBigFireworks, 500); ];
} }
$(this).remove(); private function get_custom_events($today) {
} $custom_events = get_option('fsp_events', []);
} $active_custom_events = [];
); $today_day_month = date('m-d', strtotime($today));
}
if (is_array($custom_events)) {
function runNewYearShow() { foreach ($custom_events as $event) {
const rocketCount = 5 + Math.floor(Math.random() * 4); if (empty($event['date']) || !is_array($event)) continue;
$event_date = sanitize_text_field($event['date']);
for (let i = 0; i < rocketCount; i++) { $date_obj = DateTime::createFromFormat('d-m', $event_date) ?: DateTime::createFromFormat('Y-m-d', $event_date);
const isLast = (i === rocketCount - 1); if ($date_obj && $date_obj->format('m-d') === $today_day_month) {
setTimeout(() => createRocket(isLast), i * 400); $name = trim(($event['name'] ?? '') . ' ' . ($event['text'] ?? ''));
} $active_custom_events[] = [
} 'name' => $name ?: 'Besonderer Tag!',
'class' => 'festive-birthday',
runNewYearShow(); 'effects' => ['balloons', 'confetti']
setInterval(runNewYearShow, 12000); ];
}); }
'; }
wp_add_inline_script('fsp-confetti-lib', $script); }
} return $active_custom_events;
}
public function add_admin_menu_page() {
add_options_page('Festive Seasons Pro', 'Festive Seasons', 'manage_options', 'festive-seasons-pro', [$this, 'render_admin_page']); public function load_assets() {
} $active_events = $this->get_active_events();
if (empty($active_events)) return;
public function register_settings() {
register_setting('fsp_settings_group', 'fsp_events'); $body_classes = [];
foreach (array_keys($this->get_fixed_events()) as $key) { $effects_to_load = [];
register_setting('fsp_settings_group', "fsp_fixed_{$key}");
} foreach ($active_events as $event) {
register_setting('fsp_settings_group', 'fsp_enable_music'); if (!empty($event['class'])) $body_classes[] = $event['class'];
} if (!empty($event['effects'])) $effects_to_load = array_merge($effects_to_load, $event['effects']);
}
public function render_admin_page() {
$events = get_option('fsp_events', []); add_filter('body_class', function($classes) use ($body_classes) {
?> return array_merge($classes, array_unique($body_classes));
<div class="wrap"> });
<h1>Festive Seasons Pro Einstellungen</h1>
<form method="post" action="options.php"> wp_enqueue_style('fsp-style', plugin_dir_url(__FILE__) . 'assets/css/festive.css', [], '3.4');
<?php settings_fields('fsp_settings_group'); ?> wp_enqueue_script('jquery');
<?php do_settings_sections('fsp_settings_group'); ?>
wp_localize_script('jquery', 'fsp_vars', [
<h2>Allgemeine Einstellungen</h2> 'plugin_url' => plugin_dir_url(__FILE__)
<table class="form-table" role="presentation"> ]);
<tr>
<th scope="row">Musik-Einwilligung</th> if (in_array('confetti', $effects_to_load) || in_array('newyear_fireworks', $effects_to_load)) {
<td> wp_enqueue_script('fsp-confetti-lib', plugin_dir_url(__FILE__) . 'assets/js/canvas-confetti.min.js', [], '3.4', true);
<fieldset> }
<label><input type="checkbox" name="fsp_enable_music" value="yes" <?php checked(get_option('fsp_enable_music', 'yes'), 'yes'); ?> /> Einwilligungsbox für Weihnachtsmusik anzeigen (funktioniert nur, wenn Weihnachten aktiv ist).</label>
</fieldset> foreach (array_unique($effects_to_load) as $effect) {
</td> switch ($effect) {
</tr> case 'snow':
</table> wp_enqueue_script('fsp-snow', plugin_dir_url(__FILE__) . 'assets/js/snowstorm.js', [], '3.4', true);
break;
<h2>Feste Feiertage</h2> case 'confetti':
<table class="form-table" role="presentation"> wp_add_inline_script('fsp-confetti-lib', 'jQuery(document).ready(function($) { setTimeout(() => confetti({particleCount: 200, spread: 120, origin: { y: 0.6 } }), 1000); });');
<?php foreach ($this->get_fixed_events() as $key => $event): ?> break;
<tr> case 'eggs':
<th scope="row"><?php echo esc_html($event['name']); ?></th> wp_enqueue_script('fsp-eggs', plugin_dir_url(__FILE__) . 'assets/js/easter-eggs.js', ['jquery'], '3.4', true);
<td> break;
<label><input type="checkbox" name="fsp_fixed_<?php echo $key; ?>" value="yes" <?php checked(get_option("fsp_fixed_{$key}", 'yes'), 'yes'); ?> /> Aktivieren</label> case 'spiders':
</td> wp_enqueue_script('fsp-spiders', plugin_dir_url(__FILE__) . 'assets/js/halloween-spiders.js', ['jquery'], '3.4', true);
</tr> break;
<?php endforeach; ?> case 'balloons':
</table> wp_enqueue_script('fsp-balloons', plugin_dir_url(__FILE__) . 'assets/js/balloons.js', ['jquery'], '3.4', true);
break;
<h2>Geburtstage & Jubiläe</h2> case 'star':
<p>Hier können Sie beliebige Ereignisse hinzufügen. Das Datum wird im Format <strong>TT-MM</strong> (z.B. 24-12) oder <strong>JJJJ-MM-TT</strong> (z.B. 2024-05-10) angegeben.</p> wp_enqueue_script('fsp-star', plugin_dir_url(__FILE__) . 'assets/js/christmas-star.js', ['jquery'], '3.4', true);
<div id="fsp-events-container"> break;
<?php if (is_array($events)) : ?> case 'santa':
<?php foreach ($events as $index => $event) : ?> wp_enqueue_script('fsp-santa', plugin_dir_url(__FILE__) . 'assets/js/santa-sleigh.js', ['jquery'], '3.4', true);
<div class="fsp-event-row" style="margin-bottom: 10px; display: flex; gap: 10px; align-items: center;"> break;
<input type="text" name="fsp_events[<?php echo $index; ?>][name]" placeholder="Name (z.B. Max)" value="<?php echo esc_attr($event['name'] ?? ''); ?>" style="width: 200px;" /> case 'newyear_fireworks':
<input type="text" name="fsp_events[<?php echo $index; ?>][date]" placeholder="Datum (TT-MM)" value="<?php echo esc_attr($event['date'] ?? ''); ?>" style="width: 100px;" /> $this->add_newyear_script();
<input type="text" name="fsp_events[<?php echo $index; ?>][text]" placeholder="Text (z.B. wird 30!)" value="<?php echo esc_attr($event['text'] ?? ''); ?>" style="flex-grow: 1;" /> break;
<button type="button" class="button button-secondary fsp-remove-event">Entfernen</button> }
</div> }
<?php endforeach; ?> }
<?php endif; ?>
</div> private function add_newyear_script() {
<button type="button" id="fsp-add-event" class="button button-primary">+ Neues Ereignis hinzufügen</button> $script = '
jQuery(document).ready(function($) {
<?php submit_button(); ?> if (!$("body").hasClass("festive-newyear")) {
</form> return;
</div> }
if (typeof confetti === "undefined") {
<script> console.error("FSP: Konfetti-Bibliothek nicht gefunden.");
jQuery(document).ready(function($) { return;
$("#fsp-add-event").on("click", function() { }
var index = $("#fsp-events-container .fsp-event-row").length;
var newRow = \'<div class="fsp-event-row" style="margin-bottom: 10px; display: flex; gap: 10px; align-items: center;"><input type="text" name="fsp_events[\' + index + \'][name]" placeholder="Name (z.B. Max)" style="width: 200px;" /><input type="text" name="fsp_events[\' + index + \'][date]" placeholder="Datum (TT-MM)" style="width: 100px;" /><input type="text" name="fsp_events[\' + index + \'][text]" placeholder="Text (z.B. wird 30!)" style="flex-grow: 1;" /><button type="button" class="button button-secondary fsp-remove-event">Entfernen</button></div>\'; function runBigFireworks() {
$("#fsp-events-container").append(newRow); var count = 250;
}); var defaults = { origin: { y: 0.7 }, zIndex: 999999, colors: ["#bb0000", "#ffffff", "#ff0000", "#ffbb00", "#ffee00", "#00ff00", "#0099ff", "#0000ff"] };
$(document).on("click", ".fsp-remove-event", function() { function fire(particleRatio, opts) {
$(this).closest(".fsp-event-row").remove(); confetti(Object.assign({}, defaults, opts, { particleCount: Math.floor(count * particleRatio) }));
}); }
}); fire(0.25, { spread: 26, startVelocity: 55 });
</script> fire(0.2, { spread: 60 });
<?php fire(0.35, { spread: 100, decay: 0.91, scalar: 0.8 });
} fire(0.1, { spread: 120, startVelocity: 25, decay: 0.92, scalar: 1.2 });
fire(0.1, { spread: 120, startVelocity: 45 });
public function render_shortcode() {
$active_events = $this->get_active_events(); var rainDefaults = { origin: { y: 0 }, angle: 90, drift: 0, gravity: 1.2, scalar: 1.2, zIndex: 999999 };
if (empty($active_events)) return ''; confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.1, colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"] }));
$names = array_map(fn($e) => $e['name'], $active_events); confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.5, colors: ["#bb0000", "#ffffff", "#ffbb00", "#ffee00"] }));
return '<div class="fsp-today">Heute feiern wir: <strong>' . esc_html(implode(' + ', $names)) . '</strong></div>'; confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.9, colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"] }));
} }
public function register_widget() { function createRocket(isLastRocket) {
register_widget('FSP_Widget'); const startX = Math.random() * window.innerWidth;
}
const $rocket = $("<div class=\"fsp-rocket\"></div>").css({
public function render_music_consent() { position: "fixed",
if (get_option('fsp_enable_music', 'yes') !== 'yes') { width: "6px",
return; height: "25px",
} background: "linear-gradient(to top, #ff4500, #ffa500)",
bottom: "-30px",
$active_events = $this->get_active_events(); left: startX + "px",
$has_music = false; zIndex: 999999,
foreach ($active_events as $event) { borderRadius: "50% 50% 0 0",
if (in_array('music', $event['effects'] ?? [])) { $has_music = true; break; } boxShadow: "0 0 15px 5px rgba(255, 165, 0, 0.8)"
} });
if (!$has_music) return;
$("body").append($rocket);
$audio_url = plugin_dir_url(__FILE__) . 'assets/audio/xmas-jingle.mp3';
echo '<div id="fsp-music-consent" style="position:fixed;bottom:20px;right:20px;background:#fff;color:#333;padding:20px;border-radius:8px;z-index:99999;box-shadow:0 4px 15px rgba(0,0,0,0.2);display:none;max-width:300px;"> $rocket.animate(
<p style="margin:0 0 15px 0;">Weihnachtsmusik abspielen?</p> { bottom: window.innerHeight * 0.8 },
<button id="fsp-music-yes" style="background:#c0392b;color:#fff;border:none;padding:8px 15px;border-radius:5px;cursor:pointer;margin-right:10px;">Ja</button> {
<button id="fsp-music-no" style="background:#bdc3c7;color:#333;border:none;padding:8px 15px;border-radius:5px;cursor:pointer;">Nein</button> duration: 1800,
</div> easing: "linear",
complete: function() {
<audio id="fsp-xmas-audio" src="' . $audio_url . '" loop style="display:none;"></audio> const x = startX / window.innerWidth;
const y = 0.2;
<script>
jQuery(function($) { confetti({
const consentBox = $("#fsp-music-consent"); particleCount: 75,
const audioPlayer = $("#fsp-xmas-audio")[0]; spread: 100,
origin: { x: x, y: y },
setTimeout(() => consentBox.fadeIn(500), 2000); colors: ["#ff0000", "#ffa500", "#ffff00", "#ffffff"],
gravity: 1.1,
$("#fsp-music-yes").on("click", function() { scalar: 1.0,
const playPromise = audioPlayer.play(); startVelocity: 40
});
if (playPromise !== undefined) {
playPromise.then(_ => { if (isLastRocket) {
console.log("FSP: Musik wird abgespielt."); setTimeout(runBigFireworks, 500);
}).catch(error => { }
console.error("FSP: Autoplay blockiert. Zeige Steuerelemente an.");
audioPlayer.style.position = "fixed"; $(this).remove();
audioPlayer.style.bottom = "20px"; }
audioPlayer.style.left = "20px"; }
audioPlayer.style.zIndex = "99999"; );
audioPlayer.style.width = "250px"; }
audioPlayer.style.display = "block";
audioPlayer.controls = true; function runNewYearShow() {
}); const rocketCount = 5 + Math.floor(Math.random() * 4);
}
consentBox.fadeOut(300); for (let i = 0; i < rocketCount; i++) {
}); const isLast = (i === rocketCount - 1);
setTimeout(() => createRocket(isLast), i * 400);
$("#fsp-music-no").on("click", function() { }
consentBox.fadeOut(300); }
});
}); runNewYearShow();
</script>'; setInterval(runNewYearShow, 12000);
} });
} ';
wp_add_inline_script('fsp-confetti-lib', $script);
class FSP_Widget extends WP_Widget { }
public function __construct() {
parent::__construct('fsp_widget', 'Festive Seasons Anlass', ['description' => 'Zeigt den heutigen festlichen Anlass an.']); public function add_admin_menu_page() {
} add_options_page('Festive Seasons Pro', 'Festive Seasons', 'manage_options', 'festive-seasons-pro', [$this, 'render_admin_page']);
}
public function widget($args, $instance) {
global $festive_seasons_pro_instance; public function register_settings() {
if (!$festive_seasons_pro_instance) return; register_setting('fsp_settings_group', 'fsp_events');
foreach (array_keys($this->get_fixed_events()) as $key) {
$active_events = $festive_seasons_pro_instance->get_active_events(); register_setting('fsp_settings_group', "fsp_fixed_{$key}");
if (empty($active_events)) return; }
register_setting('fsp_settings_group', 'fsp_enable_music');
$names = array_map(fn($e) => $e['name'], $active_events); }
echo $args['before_widget'];
if (!empty($instance['title'])) { public function render_admin_page() {
echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; $events = get_option('fsp_events', []);
} ?>
echo '<div style="text-align:center; padding:15px; background:rgba(255,255,255,0.2); border-radius:10px;">' . esc_html(implode(' + ', $names)) . '</div>'; <div class="wrap">
echo $args['after_widget']; <h1>🎉 Festive Seasons Pro Einstellungen</h1>
} <form method="post" action="options.php">
<?php settings_fields('fsp_settings_group'); ?>
public function form($instance) { <?php do_settings_sections('fsp_settings_group'); ?>
$title = !empty($instance['title']) ? $instance['title'] : 'Heute ist...';
?> <h2>⚙️ Allgemeine Einstellungen</h2>
<p><label for="<?php echo esc_attr($this->get_field_id('title')); ?>">Titel:</label><input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>"></p> <table class="form-table" role="presentation">
<?php <tr>
} <th scope="row">Musik-Einwilligung</th>
<td>
public function update($new_instance, $old_instance) { <fieldset>
$instance = []; <label><input type="checkbox" name="fsp_enable_music" value="yes" <?php checked(get_option('fsp_enable_music', 'yes'), 'yes'); ?> /> Einwilligungsbox für Weihnachtsmusik anzeigen (funktioniert nur, wenn Weihnachten aktiv ist).</label>
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : ''; </fieldset>
return $instance; </td>
} </tr>
} </table>
$festive_seasons_pro_instance = new Festive_Seasons_Pro(); <h2>🎄 Feste Feiertage</h2>
<table class="form-table" role="presentation">
<?php foreach ($this->get_fixed_events() as $key => $event): ?>
<tr>
<th scope="row"><?php echo esc_html($event['name']); ?></th>
<td>
<label><input type="checkbox" name="fsp_fixed_<?php echo $key; ?>" value="yes" <?php checked(get_option("fsp_fixed_{$key}", 'yes'), 'yes'); ?> /> Aktivieren</label>
</td>
</tr>
<?php endforeach; ?>
</table>
<h2>🎂 Geburtstage & Jubiläen</h2>
<p>Hier können Sie beliebige Ereignisse hinzufügen. Das Datum wird im Format <strong>TT-MM</strong> (z.B. 24-12) oder <strong>JJJJ-MM-TT</strong> (z.B. 2024-05-10) angegeben.</p>
<div id="fsp-events-container" style="margin-bottom: 15px;">
<?php if (is_array($events) && !empty($events)) : ?>
<?php foreach ($events as $index => $event) : ?>
<div class="fsp-event-row" style="margin-bottom: 10px; display: flex; gap: 10px; align-items: center;">
<input type="text" name="fsp_events[<?php echo $index; ?>][name]" placeholder="Name (z.B. Max)" value="<?php echo esc_attr($event['name'] ?? ''); ?>" style="width: 200px;" />
<input type="text" name="fsp_events[<?php echo $index; ?>][date]" placeholder="Datum (TT-MM)" value="<?php echo esc_attr($event['date'] ?? ''); ?>" style="width: 100px;" />
<input type="text" name="fsp_events[<?php echo $index; ?>][text]" placeholder="Text (z.B. wird 30!)" value="<?php echo esc_attr($event['text'] ?? ''); ?>" style="flex-grow: 1;" />
<button type="button" class="button button-secondary fsp-remove-event">Entfernen</button>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<button type="button" id="fsp-add-event" class="button button-primary" style="margin-bottom: 20px;">
<span class="dashicons dashicons-plus-alt" style="margin-top: 3px;"></span> Neues Ereignis hinzufügen
</button>
<?php submit_button('Einstellungen speichern'); ?>
</form>
</div>
<?php
}
public function render_shortcode() {
$active_events = $this->get_active_events();
if (empty($active_events)) return '';
$names = array_map(fn($e) => $e['name'], $active_events);
return '<div class="fsp-today">Heute feiern wir: <strong>' . esc_html(implode(' + ', $names)) . '</strong></div>';
}
public function register_widget() {
register_widget('FSP_Widget');
}
public function render_music_consent() {
if (get_option('fsp_enable_music', 'yes') !== 'yes') {
return;
}
$active_events = $this->get_active_events();
$has_music = false;
foreach ($active_events as $event) {
if (in_array('music', $event['effects'] ?? [])) { $has_music = true; break; }
}
if (!$has_music) return;
$audio_url = plugin_dir_url(__FILE__) . 'assets/audio/xmas-jingle.mp3';
echo '<div id="fsp-music-consent" style="position:fixed;bottom:20px;right:20px;background:#fff;color:#333;padding:20px;border-radius:8px;z-index:99999;box-shadow:0 4px 15px rgba(0,0,0,0.2);display:none;max-width:300px;">
<p style="margin:0 0 15px 0;">🎵 Weihnachtsmusik abspielen?</p>
<button id="fsp-music-yes" style="background:#c0392b;color:#fff;border:none;padding:8px 15px;border-radius:5px;cursor:pointer;margin-right:10px;">Ja</button>
<button id="fsp-music-no" style="background:#bdc3c7;color:#333;border:none;padding:8px 15px;border-radius:5px;cursor:pointer;">Nein</button>
</div>
<audio id="fsp-xmas-audio" src="' . $audio_url . '" loop style="display:none;"></audio>
<script>
jQuery(function($) {
const consentBox = $("#fsp-music-consent");
const audioPlayer = $("#fsp-xmas-audio")[0];
setTimeout(() => consentBox.fadeIn(500), 2000);
$("#fsp-music-yes").on("click", function() {
const playPromise = audioPlayer.play();
if (playPromise !== undefined) {
playPromise.then(_ => {
console.log("FSP: Musik wird abgespielt.");
}).catch(error => {
console.error("FSP: Autoplay blockiert. Zeige Steuerelemente an.");
audioPlayer.style.position = "fixed";
audioPlayer.style.bottom = "20px";
audioPlayer.style.left = "20px";
audioPlayer.style.zIndex = "99999";
audioPlayer.style.width = "250px";
audioPlayer.style.display = "block";
audioPlayer.controls = true;
});
}
consentBox.fadeOut(300);
});
$("#fsp-music-no").on("click", function() {
consentBox.fadeOut(300);
});
});
</script>';
}
}
class FSP_Widget extends WP_Widget {
public function __construct() {
parent::__construct('fsp_widget', 'Festive Seasons Anlass', ['description' => 'Zeigt den heutigen festlichen Anlass an.']);
}
public function widget($args, $instance) {
global $festive_seasons_pro_instance;
if (!$festive_seasons_pro_instance) return;
$active_events = $festive_seasons_pro_instance->get_active_events();
if (empty($active_events)) return;
$names = array_map(fn($e) => $e['name'], $active_events);
echo $args['before_widget'];
if (!empty($instance['title'])) {
echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title'];
}
echo '<div style="text-align:center; padding:15px; background:rgba(255,255,255,0.2); border-radius:10px;">' . esc_html(implode(' + ', $names)) . '</div>';
echo $args['after_widget'];
}
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : 'Heute ist...';
?>
<p><label for="<?php echo esc_attr($this->get_field_id('title')); ?>">Titel:</label><input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>"></p>
<?php
}
public function update($new_instance, $old_instance) {
$instance = [];
$instance['title'] = (!empty($new_instance['title'])) ? sanitize_text_field($new_instance['title']) : '';
return $instance;
}
}
$festive_seasons_pro_instance = new Festive_Seasons_Pro();