yoone-snow/yoone-snow.php

306 lines
14 KiB
PHP

<?php
/*
Plugin Name: Yoone Snow
Description: 首页 canvas 雪花效果
Version: 1.2.0
Author: Yoone
*/
if (!defined('ABSPATH')) { exit; }
function yoone_snow_is_enabled() {
return function_exists('is_front_page') ? is_front_page() : false;
}
function yoone_snow_enqueue_assets() {
if (!yoone_snow_is_enabled()) { return; }
$style_handle = 'yoone-snow-style';
$style_src = plugins_url('css/snow.css', __FILE__);
wp_register_style($style_handle, $style_src, array(), '1.1.0', 'all');
wp_enqueue_style($style_handle);
// 注册形状渲染脚本 保证主脚本之前加载
$shape_index_handle = 'yoone-snow-shapes-index';
wp_register_script($shape_index_handle, plugins_url('js/shapes/index.js', __FILE__), array(), '1.1.0', true);
$shape_utils_handle = 'yoone-snow-shapes-utils';
wp_register_script($shape_utils_handle, plugins_url('js/shapes/utils.js', __FILE__), array($shape_index_handle), '1.1.0', true);
$shape_dot_handle = 'yoone-snow-shapes-dot';
wp_register_script($shape_dot_handle, plugins_url('js/shapes/dot.js', __FILE__), array($shape_index_handle), '1.1.0', true);
$shape_flake_handle = 'yoone-snow-shapes-flake';
wp_register_script($shape_flake_handle, plugins_url('js/shapes/flake.js', __FILE__), array($shape_index_handle), '1.1.0', true);
$shape_yuanbao_handle = 'yoone-snow-shapes-yuanbao';
wp_register_script($shape_yuanbao_handle, plugins_url('js/shapes/yuanbao.js', __FILE__), array($shape_index_handle), '1.1.0', true);
$shape_coin_handle = 'yoone-snow-shapes-coin';
wp_register_script($shape_coin_handle, plugins_url('js/shapes/coin.js', __FILE__), array($shape_index_handle), '1.1.0', true);
$shape_santa_handle = 'yoone-snow-shapes-santa-hat';
wp_register_script($shape_santa_handle, plugins_url('js/shapes/santa_hat.js', __FILE__), array($shape_utils_handle), '1.1.0', true);
$shape_cane_handle = 'yoone-snow-shapes-candy-cane';
wp_register_script($shape_cane_handle, plugins_url('js/shapes/candy_cane.js', __FILE__), array($shape_utils_handle), '1.1.0', true);
$shape_sock_handle = 'yoone-snow-shapes-christmas-sock';
wp_register_script($shape_sock_handle, plugins_url('js/shapes/christmas_sock.js', __FILE__), array($shape_utils_handle), '1.1.0', true);
$shape_tree_handle = 'yoone-snow-shapes-christmas-tree';
wp_register_script($shape_tree_handle, plugins_url('js/shapes/christmas_tree.js', __FILE__), array($shape_utils_handle), '1.1.0', true);
$shape_reindeer_handle = 'yoone-snow-shapes-reindeer';
wp_register_script($shape_reindeer_handle, plugins_url('js/shapes/reindeer.js', __FILE__), array($shape_utils_handle), '1.1.0', true);
$shape_berry_handle = 'yoone-snow-shapes-christmas-berry';
wp_register_script($shape_berry_handle, plugins_url('js/shapes/christmas_berry.js', __FILE__), array($shape_utils_handle), '1.1.0', true);
// 注册并加载主脚本 设置依赖确保顺序正确
$script_handle = 'yoone-snow-script';
$script_src = plugins_url('js/snow-canvas.js', __FILE__);
wp_register_script(
$script_handle,
$script_src,
array(
$shape_index_handle,
$shape_dot_handle,
$shape_flake_handle,
$shape_yuanbao_handle,
$shape_coin_handle,
$shape_santa_handle,
$shape_cane_handle,
$shape_sock_handle,
$shape_tree_handle,
$shape_reindeer_handle,
$shape_berry_handle
),
'1.1.0',
true
);
wp_enqueue_script($script_handle);
// 将后端设置传递到前端脚本 变量名称为 YooneSnowSettings
// 简化设置 仅保留复选框选择的形状集合
$mixed_items_option = get_option('yoone_snow_mixed_items', array('dot','flake'));
if (is_string($mixed_items_option)) {
$mixed_items_option = array_filter(array_map('trim', explode(',', $mixed_items_option)));
}
$allowed_shapes = array('dot','flake','yuanbao','coin','santa_hat','candy_cane','christmas_sock','christmas_tree','reindeer','christmas_berry');
$mixed_items_sanitized = array_values(array_unique(array_intersect($mixed_items_option, $allowed_shapes)));
if (empty($mixed_items_sanitized)) { $mixed_items_sanitized = array('dot','flake'); }
// 读取媒体形状集合 并映射为可用的 URL 列表
$media_ids = get_option('yoone_snow_media_items', array());
if (!is_array($media_ids)) { $media_ids = array(); }
$media_urls = array();
foreach ($media_ids as $mid) {
$url = wp_get_attachment_url(intval($mid));
if ($url) { $media_urls[] = $url; }
}
wp_localize_script($script_handle, 'YooneSnowSettings', array(
'selectedShapes' => $mixed_items_sanitized,
'mediaItems' => $media_urls,
// 读取并传递首页显示时长设置 单位为秒 0 表示无限
'displayDurationSeconds' => intval(get_option('yoone_snow_home_duration', 0)),
// 传递资源基础映射 用于前端按需加载 SVG 图像
'assetsMap' => array(
'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__),
'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__),
'christmas_sock' => plugins_url('assets/圣诞袜子.svg', __FILE__),
'christmas_tree' => plugins_url('assets/圣诞树.svg', __FILE__),
'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__),
'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__),
),
));
}
// 在后台设置页面加载媒体库脚本和交互脚本 用于选择 SVG 或图片
function yoone_snow_admin_enqueue($hook) {
// 条件判断 仅在插件设置页面加载脚本 保持性能
if ($hook !== 'settings_page_yoone_snow') { return; }
// 加载媒体库脚本 以便使用 wp.media 选择器
wp_enqueue_media();
// 注册并加载后台交互脚本
$admin_script_handle = 'yoone-snow-admin-media';
wp_register_script($admin_script_handle, plugins_url('js/admin-media.js', __FILE__), array(), '1.1.0', true);
wp_enqueue_script($admin_script_handle);
}
function yoone_snow_render_overlay() {
if (!yoone_snow_is_enabled()) { return; }
static $yoone_snow_rendered = false;
if ($yoone_snow_rendered) { return; }
$yoone_snow_rendered = true;
echo '<canvas id="effectiveAppsSnow" aria-hidden="true"></canvas>';
}
add_action('wp_enqueue_scripts', 'yoone_snow_enqueue_assets');
add_action('wp_body_open', 'yoone_snow_render_overlay');
add_action('wp_footer', 'yoone_snow_render_overlay', 100);
// 注册设置页面和设置项 用于选择雪花形状
function yoone_snow_register_settings() {
// 移除形状下拉选项 仅保留复选集合设置
// 注册 mixed 形状集合设置项 默认包含 dot 和 flake
register_setting('yoone_snow_options', 'yoone_snow_mixed_items', array(
'type' => 'array',
'sanitize_callback' => function($value) {
// 将输入统一为数组 并过滤到允许的集合
$allowed = array('dot','flake','yuanbao','coin','santa_hat','candy_cane','christmas_sock','christmas_tree','reindeer','christmas_berry');
if (is_string($value)) {
$value = array_filter(array_map('trim', explode(',', $value)));
}
if (!is_array($value)) { $value = array('dot','flake'); }
$filtered = array_values(array_unique(array_intersect($value, $allowed)));
return !empty($filtered) ? $filtered : array('dot','flake');
},
'default' => array('dot','flake'),
));
// 添加设置分区 标题为 Snow Settings
add_settings_section(
'yoone_snow_section',
'Snow Settings',
function() {
// 输出分区描述 使用英文标点保证兼容
echo '<p>Configure snow appearance</p>';
},
'yoone_snow'
);
// 移除下拉字段 保留复选框作为唯一选择入口
// 添加形状复选集合 用于选择参与渲染的形状
add_settings_field(
'yoone_snow_mixed_items',
'Shapes',
function() {
$current_list = get_option('yoone_snow_mixed_items', array('dot','flake'));
if (is_string($current_list)) {
$current_list = array_filter(array_map('trim', explode(',', $current_list)));
}
$options = array(
'dot' => 'Dot',
'flake' => 'Snowflake',
'yuanbao' => 'Yuanbao',
'coin' => 'Coin',
'santa_hat' => 'Santa Hat',
'candy_cane' => 'Candy Cane',
'christmas_sock' => 'Christmas Sock',
'christmas_tree' => 'Christmas Tree',
'reindeer' => 'Reindeer',
'christmas_berry' => 'Christmas Berry',
);
foreach ($options as $key => $label) {
$checked = in_array($key, $current_list, true) ? 'checked' : '';
echo '<label style="margin-right:12px;"><input type="checkbox" name="yoone_snow_mixed_items[]" value="' . esc_attr($key) . '" ' . $checked . ' /> ' . esc_html($label) . '</label>';
}
echo '<p class="description">Choose shapes to render</p>';
},
'yoone_snow',
'yoone_snow_section'
);
// 注册媒体形状集合 设置项保存为附件 ID 数组
register_setting('yoone_snow_options', 'yoone_snow_media_items', array(
'type' => 'array',
'sanitize_callback' => function($value) {
// 将输入统一为整数 ID 数组 并过滤无效值
if (is_string($value)) {
$value = array_filter(array_map('trim', explode(',', $value)));
}
if (!is_array($value)) { $value = array(); }
$ids = array();
foreach ($value as $item) {
$id = intval($item);
if ($id > 0) { $ids[] = $id; }
}
return array_values(array_unique($ids));
},
'default' => array(),
));
// 添加媒体形状选择字段 支持从媒体库选择图片或 SVG
add_settings_field(
'yoone_snow_media_items',
'Media Shapes',
function() {
// 读取当前已选择的附件 ID 列表 并渲染缩略图列表与添加按钮
$current_media = get_option('yoone_snow_media_items', array());
if (!is_array($current_media)) { $current_media = array(); }
echo '<div id="yooneSnowMediaList" style="display:flex;flex-wrap:wrap;gap:12px;">';
foreach ($current_media as $attachment_id) {
$url = wp_get_attachment_thumb_url($attachment_id);
if (!$url) { $url = wp_get_attachment_url($attachment_id); }
$safe_id = intval($attachment_id);
$safe_url = esc_url($url);
echo '<div class="yoone-snow-media-item" data-attachment-id="' . $safe_id . '" style="border:1px solid #ddd;padding:8px;display:flex;flex-direction:column;align-items:center;">';
echo '<img src="' . $safe_url . '" alt="media" style="width:72px;height:72px;object-fit:contain;" />';
echo '<input type="hidden" name="yoone_snow_media_items[]" value="' . $safe_id . '" />';
echo '<button type="button" class="button yoone-snow-remove-media" style="margin-top:6px;">Remove</button>';
echo '</div>';
}
echo '</div>';
echo '<p><button type="button" class="button button-primary" id="yooneSnowAddMedia">Add Images</button></p>';
echo '<p class="description">Choose images or SVG from media library to render</p>';
},
'yoone_snow',
'yoone_snow_section'
);
// 注册首页显示时长设置 项为整数秒 0 表示无限
register_setting('yoone_snow_options', 'yoone_snow_home_duration', array(
'type' => 'integer',
'sanitize_callback' => function($value) {
// 将输入转换为非负整数 单位为秒 0 表示无限
$num = intval($value);
if ($num < 0) { $num = 0; }
return $num;
},
'default' => 0,
));
// 添加首页显示时长字段 输入为数字最小值为 0
add_settings_field(
'yoone_snow_home_duration',
'Home Display Duration Seconds',
function() {
// 读取当前设置值 并渲染数字输入框
$current = intval(get_option('yoone_snow_home_duration', 0));
echo '<input type="number" min="0" name="yoone_snow_home_duration" value="' . esc_attr($current) . '" style="width:120px;" />';
echo '<p class="description">Duration in seconds for snow on home 0 means infinite</p>';
},
'yoone_snow',
'yoone_snow_section'
);
}
// 添加设置页面到后台菜单 条目在设置菜单下
function yoone_snow_add_settings_page() {
add_options_page(
'Yoone Snow',
'Yoone Snow',
'manage_options',
'yoone_snow',
function() {
// 渲染设置页面 表单提交到 options.php
echo '<div class="wrap">';
echo '<h1>Yoone Snow</h1>';
echo '<form method="post" action="options.php">';
settings_fields('yoone_snow_options');
do_settings_sections('yoone_snow');
submit_button();
echo '</form>';
echo '</div>';
}
);
}
// 在 admin 初始化时注册设置 在 admin 菜单挂载页面
add_action('admin_init', 'yoone_snow_register_settings');
add_action('admin_menu', 'yoone_snow_add_settings_page');
add_action('admin_enqueue_scripts', 'yoone_snow_admin_enqueue');
// 在插件列表行添加 Settings 链接 指向设置页面
function yoone_snow_plugin_action_links($links) {
// 构造设置页面链接 使用 admin_url 保证后台路径正确
$settingsUrl = admin_url('options-general.php?page=yoone_snow');
$settingsLink = '<a href="' . esc_url($settingsUrl) . '">Settings</a>';
// 将设置链接插入到最前面 便于用户点击
array_unshift($links, $settingsLink);
return $links;
}
// 绑定到当前插件的 action links 钩子 使用 plugin_basename 计算插件标识
add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'yoone_snow_plugin_action_links');