10]
);
if ( ! is_wp_error($response) && 200 === wp_remote_retrieve_response_code($response) ) {
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if ( $data && isset($data['tag_name']) ) {
$tag = ltrim($data['tag_name'], 'v');
$release_info = [
'version' => $tag,
'download_url' => $data['zipball_url'] ?? '',
'notes' => $data['body'] ?? '',
'published_at' => $data['published_at'] ?? '',
];
set_transient( $transient_key, $release_info, 6 * HOUR_IN_SECONDS );
} else {
set_transient( $transient_key, [], HOUR_IN_SECONDS );
}
} else {
set_transient( $transient_key, [], HOUR_IN_SECONDS );
}
}
return $release_info;
}
// Admin-Notice für Plugin-Update
function fsp_show_update_notice() {
if ( ! current_user_can('manage_options') ) {
return;
}
$current_version = fsp_get_plugin_version();
$latest_release = fsp_get_latest_release_info();
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'),
'fsp_clear_cache_action'
);
?>
\' +
\'\' +
\'\' +
\'\' +
\'\' +
\'\';
$("#fsp-events-container").append(newRow);
});
// Ereignis entfernen
$(document).on("click", ".fsp-remove-event", function() {
$(this).closest(".fsp-event-row").remove();
});
});
');
}
public function get_active_events() {
if (isset($_GET['fsp_test_date'])) {
$today = sanitize_text_field($_GET['fsp_test_date']);
} else {
$today = date('Y-m-d');
}
$active_events = [];
foreach ($this->get_fixed_events() as $event_key => $event_data) {
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;
}
}
}
foreach ($this->get_custom_events($today) as $event_data) {
$active_events[] = $event_data;
}
return $active_events;
}
private function get_fixed_events() {
$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));
return [
'advent' => ['name' => 'Adventszeit', 'start' => "$year-12-01", 'end' => "$year-12-23", 'class' => 'festive-advent', 'effects' => ['snow']],
'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']],
'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']],
];
}
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)) {
foreach ($custom_events as $event) {
if (empty($event['date']) || !is_array($event)) continue;
$event_date = sanitize_text_field($event['date']);
$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'] ?? ''));
$active_custom_events[] = [
'name' => $name ?: 'Besonderer Tag!',
'class' => 'festive-birthday',
'effects' => ['balloons', 'confetti']
];
}
}
}
return $active_custom_events;
}
public function load_assets() {
$active_events = $this->get_active_events();
if (empty($active_events)) return;
$body_classes = [];
$effects_to_load = [];
foreach ($active_events as $event) {
if (!empty($event['class'])) $body_classes[] = $event['class'];
if (!empty($event['effects'])) $effects_to_load = array_merge($effects_to_load, $event['effects']);
}
add_filter('body_class', function($classes) use ($body_classes) {
return array_merge($classes, array_unique($body_classes));
});
wp_enqueue_style('fsp-style', plugin_dir_url(__FILE__) . 'assets/css/festive.css', [], '3.4');
wp_enqueue_script('jquery');
wp_localize_script('jquery', 'fsp_vars', [
'plugin_url' => plugin_dir_url(__FILE__)
]);
if (in_array('confetti', $effects_to_load) || in_array('newyear_fireworks', $effects_to_load)) {
wp_enqueue_script('fsp-confetti-lib', plugin_dir_url(__FILE__) . 'assets/js/canvas-confetti.min.js', [], '3.4', true);
}
foreach (array_unique($effects_to_load) as $effect) {
switch ($effect) {
case 'snow':
wp_enqueue_script('fsp-snow', plugin_dir_url(__FILE__) . 'assets/js/snowstorm.js', [], '3.4', true);
break;
case 'confetti':
wp_add_inline_script('fsp-confetti-lib', 'jQuery(document).ready(function($) { setTimeout(() => confetti({particleCount: 200, spread: 120, origin: { y: 0.6 } }), 1000); });');
break;
case 'eggs':
wp_enqueue_script('fsp-eggs', plugin_dir_url(__FILE__) . 'assets/js/easter-eggs.js', ['jquery'], '3.4', true);
break;
case 'spiders':
wp_enqueue_script('fsp-spiders', plugin_dir_url(__FILE__) . 'assets/js/halloween-spiders.js', ['jquery'], '3.4', true);
break;
case 'balloons':
wp_enqueue_script('fsp-balloons', plugin_dir_url(__FILE__) . 'assets/js/balloons.js', ['jquery'], '3.4', true);
break;
case 'star':
wp_enqueue_script('fsp-star', plugin_dir_url(__FILE__) . 'assets/js/christmas-star.js', ['jquery'], '3.4', true);
break;
case 'santa':
wp_enqueue_script('fsp-santa', plugin_dir_url(__FILE__) . 'assets/js/santa-sleigh.js', ['jquery'], '3.4', true);
break;
case 'newyear_fireworks':
$this->add_newyear_script();
break;
}
}
}
private function add_newyear_script() {
$script = '
jQuery(document).ready(function($) {
if (!$("body").hasClass("festive-newyear")) {
return;
}
if (typeof confetti === "undefined") {
console.error("FSP: Konfetti-Bibliothek nicht gefunden.");
return;
}
function runBigFireworks() {
var count = 250;
var defaults = { origin: { y: 0.7 }, zIndex: 999999, colors: ["#bb0000", "#ffffff", "#ff0000", "#ffbb00", "#ffee00", "#00ff00", "#0099ff", "#0000ff"] };
function fire(particleRatio, opts) {
confetti(Object.assign({}, defaults, opts, { particleCount: Math.floor(count * particleRatio) }));
}
fire(0.25, { spread: 26, startVelocity: 55 });
fire(0.2, { spread: 60 });
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 });
var rainDefaults = { origin: { y: 0 }, angle: 90, drift: 0, gravity: 1.2, scalar: 1.2, zIndex: 999999 };
confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.1, colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"] }));
confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.5, colors: ["#bb0000", "#ffffff", "#ffbb00", "#ffee00"] }));
confetti(Object.assign({}, rainDefaults, { particleCount: 40, x: 0.9, colors: ["#ff0000", "#00ff00", "#0000ff", "#ffff00"] }));
}
function createRocket(isLastRocket) {
const startX = Math.random() * window.innerWidth;
const $rocket = $("").css({
position: "fixed",
width: "6px",
height: "25px",
background: "linear-gradient(to top, #ff4500, #ffa500)",
bottom: "-30px",
left: startX + "px",
zIndex: 999999,
borderRadius: "50% 50% 0 0",
boxShadow: "0 0 15px 5px rgba(255, 165, 0, 0.8)"
});
$("body").append($rocket);
$rocket.animate(
{ bottom: window.innerHeight * 0.8 },
{
duration: 1800,
easing: "linear",
complete: function() {
const x = startX / window.innerWidth;
const y = 0.2;
confetti({
particleCount: 75,
spread: 100,
origin: { x: x, y: y },
colors: ["#ff0000", "#ffa500", "#ffff00", "#ffffff"],
gravity: 1.1,
scalar: 1.0,
startVelocity: 40
});
if (isLastRocket) {
setTimeout(runBigFireworks, 500);
}
$(this).remove();
}
}
);
}
function runNewYearShow() {
const rocketCount = 5 + Math.floor(Math.random() * 4);
for (let i = 0; i < rocketCount; i++) {
const isLast = (i === rocketCount - 1);
setTimeout(() => createRocket(isLast), i * 400);
}
}
runNewYearShow();
setInterval(runNewYearShow, 12000);
});
';
wp_add_inline_script('fsp-confetti-lib', $script);
}
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 register_settings() {
register_setting('fsp_settings_group', 'fsp_events');
foreach (array_keys($this->get_fixed_events()) as $key) {
register_setting('fsp_settings_group', "fsp_fixed_{$key}");
}
register_setting('fsp_settings_group', 'fsp_enable_music');
}
public function render_admin_page() {
$events = get_option('fsp_events', []);
?>
🎉 Festive Seasons Pro Einstellungen
get_active_events();
if (empty($active_events)) return '';
$names = array_map(fn($e) => $e['name'], $active_events);
return 'Heute feiern wir: ' . esc_html(implode(' + ', $names)) . '
';
}
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 '
🎵 Weihnachtsmusik abspielen?
';
}
}
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 '' . esc_html(implode(' + ', $names)) . '
';
echo $args['after_widget'];
}
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : 'Heute ist...';
?>