diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..33bca5d --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,165 @@ +/* 后台样式 */ +.flm-admin-container { + display: flex; + gap: 2rem; + margin-top: 20px; +} + +.flm-add-form { + flex: 1; + max-width: 400px; + background: #fff; + padding: 20px; + border-radius: 4px; + box-shadow: 0 1px 1px rgba(0,0,0,0.04); +} + +.flm-links-list { + flex: 2; + background: #fff; + padding: 20px; + border-radius: 4px; + box-shadow: 0 1px 1px rgba(0,0,0,0.04); +} + +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: 600; +} + +.form-group input[type="text"], +.form-group input[type="url"] { + width: 100%; + padding: 8px; + border: 1px solid #ddd; + border-radius: 4px; +} + +#flm-sortable-links { + list-style: none; + padding: 0; + margin: 20px 0; +} + +.flm-link-item { + display: flex; + align-items: center; + gap: 15px; + padding: 15px; + margin-bottom: 15px; + background: #f9f9f9; + border-radius: 4px; + border-left: 4px solid #6667ab; +} + +.flm-link-preview { + display: flex; + align-items: center; + gap: 10px; + min-width: 200px; +} + +.flm-link-icon { + width: 32px; + height: 32px; + border-radius: 50%; + object-fit: cover; +} + +.flm-link-fields { + flex: 1; +} + +.flm-link-actions { + min-width: 100px; +} + +.flm-import-export { + margin-top: 30px; + padding-top: 20px; + border-top: 1px solid #eee; +} + +.flm-import-export h3 { + margin-bottom: 15px; +} + +.flm-export, .flm-import { + margin-bottom: 15px; +} + +/* 前端展示样式 */ +.flm-links-container { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20px; + margin: 20px 0; +} + +.flm-link-card { + border: 1px solid #6667ab; + border-radius: 14px; + padding: 20px; + text-align: center; + transition: all 0.3s ease; +} + +.flm-link-card:hover { + box-shadow: 0 4px 8px rgba(0,0,0,0.1); + transform: translateY(-2px); +} + +.flm-link-icon-container { + margin-bottom: 10px; +} + +.flm-link-icon-container img { + width: 64px; + height: 64px; + border-radius: 50%; + object-fit: cover; + border: 1px solid #eee; +} + +.flm-link-name { + font-weight: bold; + color: inherit; /* 使用主题默认颜色 */ + margin-top: 10px; +} + +.flm-link-card a { + text-decoration: none; + color: inherit; + display: block; +} + +/* 响应式设计 */ +@media (max-width: 1024px) { + .flm-links-container { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 600px) { + .flm-admin-container { + flex-direction: column; + } + + .flm-add-form { + max-width: 100%; + } + + .flm-links-container { + grid-template-columns: 1fr; + } + + .flm-link-item { + flex-direction: column; + align-items: flex-start; + } +} \ No newline at end of file diff --git a/assets/images/default-icon.png b/assets/images/default-icon.png new file mode 100644 index 0000000..3afa636 Binary files /dev/null and b/assets/images/default-icon.png differ diff --git a/assets/js/admin.js b/assets/js/admin.js new file mode 100644 index 0000000..2983c44 --- /dev/null +++ b/assets/js/admin.js @@ -0,0 +1,45 @@ +jQuery(document).ready(function($) { + // 使链接可排序 + $('#flm-sortable-links').sortable({ + handle: '.flm-link-preview', + placeholder: 'flm-link-item-placeholder', + axis: 'y', + update: function() { + // 更新排序后可以在这里添加AJAX保存逻辑 + } + }); + + // 删除链接 + $('.flm-delete-link').on('click', function(e) { + e.preventDefault(); + + if (!confirm('确定要删除这个链接吗?')) { + return; + } + + var $button = $(this); + var linkId = $button.data('link-id'); + + $.ajax({ + url: flm_vars.ajax_url, + type: 'POST', + data: { + action: 'flm_delete_link', + link_id: linkId, + nonce: flm_vars.nonce + }, + beforeSend: function() { + $button.prop('disabled', true).text('删除中...'); + }, + success: function() { + $button.closest('.flm-link-item').fadeOut(300, function() { + $(this).remove(); + }); + }, + error: function() { + alert('删除失败,请重试'); + $button.prop('disabled', false).text('删除'); + } + }); + }); +}); \ No newline at end of file diff --git a/friend-links-manager.php b/friend-links-manager.php new file mode 100644 index 0000000..f38f295 --- /dev/null +++ b/friend-links-manager.php @@ -0,0 +1,99 @@ +prefix . 'friend_links'; + + $charset_collate = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE $table_name ( + id mediumint(9) NOT NULL AUTO_INCREMENT, + name varchar(100) NOT NULL, + url varchar(255) NOT NULL, + icon varchar(255) DEFAULT '', + sort_order int(11) DEFAULT 0, + PRIMARY KEY (id) + ) $charset_collate;"; + + require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); + dbDelta($sql); + + // 初始化插件版本 + update_option('flm_version', FLM_VERSION); +} + +// 动态卸载逻辑(替代 register_uninstall_hook) +register_deactivation_hook(__FILE__, 'flm_cleanup'); +function flm_cleanup() { + global $wpdb; + + // 删除数据库表 + $table_name = $wpdb->prefix . 'friend_links'; + if ($wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name) { + $wpdb->query("DROP TABLE IF EXISTS $table_name"); + } + + // 清理插件选项 + delete_option('flm_version'); + + // 强制刷新缓存(针对某些缓存插件) + wp_cache_flush(); +} + +// 加载插件功能文件 +require_once FLM_PLUGIN_DIR . 'includes/admin-page.php'; +require_once FLM_PLUGIN_DIR . 'includes/shortcode.php'; + +// 加载样式和脚本 +add_action('wp_enqueue_scripts', 'flm_enqueue_scripts'); +function flm_enqueue_scripts() { + wp_enqueue_style( + 'flm-style', + FLM_PLUGIN_URL . 'assets/css/style.css', + array(), + FLM_VERSION + ); +} + +// 后台脚本和样式 +add_action('admin_enqueue_scripts', 'flm_admin_enqueue_scripts'); +function flm_admin_enqueue_scripts($hook) { + if ('toplevel_page_friend-links-manager' === $hook) { + wp_enqueue_style( + 'flm-admin-style', + FLM_PLUGIN_URL . 'assets/css/style.css', + array(), + FLM_VERSION + ); + + wp_enqueue_script( + 'flm-admin-js', + FLM_PLUGIN_URL . 'assets/js/admin.js', + array('jquery', 'jquery-ui-sortable'), + FLM_VERSION, + true + ); + + wp_localize_script('flm-admin-js', 'flm_vars', array( + 'ajax_url' => admin_url('admin-ajax.php'), + 'nonce' => wp_create_nonce('flm_nonce') + )); + } +} \ No newline at end of file diff --git a/includes/admin-page.php b/includes/admin-page.php new file mode 100644 index 0000000..c530005 --- /dev/null +++ b/includes/admin-page.php @@ -0,0 +1,378 @@ +"), '', $url); + + // 使用正则提取纯URL + if (preg_match('/(https?:\/\/[^\s\"\'<>]+)/i', $url, $matches)) { + $url = $matches[1]; + } + + // 最终过滤和验证 + $url = filter_var($url, FILTER_SANITIZE_URL); + if (!preg_match('/^https?:\/\//i', $url)) { + $url = 'http://' . ltrim($url, '/'); + } + + return rtrim($url, '/'); +} + +// 极端严格的文本清理(用于导出) +function flm_sanitize_export_text($text) { + if (empty($text)) return ''; + + // 彻底移除所有HTML/JavaScript代码 + $text = html_entity_decode($text); + $text = strip_tags($text); + $text = str_replace(array("\r", "\n", "\t", "\\", "'", '"', "<", ">"), '', $text); + + return sanitize_text_field($text); +} + +// 管理页面内容 +function flm_admin_page() { + global $wpdb; + $table_name = $wpdb->prefix . 'friend_links'; + + // 显示警告信息 + echo '
警告:禁用该插件将删除所有链接数据,请在禁用前导出包含链接的CSV文件!
链接添加成功!
该URL的链接已存在!
链接更新成功!
链接删除成功!
' . $message . '