commit
This commit is contained in:
165
assets/css/style.css
Normal file
165
assets/css/style.css
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
BIN
assets/images/default-icon.png
Normal file
BIN
assets/images/default-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
45
assets/js/admin.js
Normal file
45
assets/js/admin.js
Normal file
@@ -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('删除');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
99
friend-links-manager.php
Normal file
99
friend-links-manager.php
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
Plugin Name: 友情链接管理器
|
||||||
|
Description: 管理并展示友情链接,支持导入导出
|
||||||
|
Version: 1.1.1
|
||||||
|
Author: 林海草原
|
||||||
|
Author URI: https://lhcy.org
|
||||||
|
Text Domain: friend-links-manager
|
||||||
|
*/
|
||||||
|
|
||||||
|
defined('ABSPATH') or die('无权访问');
|
||||||
|
|
||||||
|
// 定义插件常量
|
||||||
|
define('FLM_VERSION', '1.0');
|
||||||
|
define('FLM_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||||
|
define('FLM_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||||
|
|
||||||
|
// 创建数据库表
|
||||||
|
register_activation_hook(__FILE__, 'flm_create_table');
|
||||||
|
function flm_create_table() {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->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')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
378
includes/admin-page.php
Normal file
378
includes/admin-page.php
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// 添加管理菜单
|
||||||
|
add_action('admin_menu', 'flm_add_admin_menu');
|
||||||
|
function flm_add_admin_menu() {
|
||||||
|
add_menu_page(
|
||||||
|
'友情链接管理',
|
||||||
|
'友情链接',
|
||||||
|
'manage_options',
|
||||||
|
'friend-links-manager',
|
||||||
|
'flm_admin_page',
|
||||||
|
'dashicons-admin-links',
|
||||||
|
30
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动获取favicon
|
||||||
|
function flm_get_favicon($url) {
|
||||||
|
$domain = parse_url($url, PHP_URL_HOST);
|
||||||
|
if ($domain) {
|
||||||
|
$domain = preg_replace('/^www\./', '', $domain);
|
||||||
|
return 'https://favicon.im/' . urlencode($domain);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 极端严格的URL清理(用于导出)
|
||||||
|
function flm_sanitize_export_url($url) {
|
||||||
|
if (empty($url)) return '';
|
||||||
|
|
||||||
|
// 先解码HTML实体
|
||||||
|
$url = html_entity_decode($url);
|
||||||
|
|
||||||
|
// 移除所有HTML标签和特殊字符
|
||||||
|
$url = strip_tags($url);
|
||||||
|
$url = str_replace(array("\r", "\n", "\t", "\\", "'", '"', " ", "<", ">"), '', $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 '<div class="notice notice-warning"><p><strong>警告:</strong>禁用该插件将删除所有链接数据,请在禁用前导出包含链接的CSV文件!</p></div>';
|
||||||
|
|
||||||
|
// 处理表单提交
|
||||||
|
if (isset($_POST['flm_action'])) {
|
||||||
|
check_admin_referer('flm_nonce');
|
||||||
|
|
||||||
|
switch ($_POST['flm_action']) {
|
||||||
|
case 'add_link':
|
||||||
|
if (!empty($_POST['name']) && !empty($_POST['url'])) {
|
||||||
|
$name = sanitize_text_field($_POST['name']);
|
||||||
|
$url = esc_url_raw($_POST['url']);
|
||||||
|
$icon = !empty($_POST['icon']) ? esc_url_raw($_POST['icon']) : flm_get_favicon($url);
|
||||||
|
|
||||||
|
$existing = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT id FROM $table_name WHERE url = %s",
|
||||||
|
$url
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!$existing) {
|
||||||
|
$wpdb->insert($table_name, array(
|
||||||
|
'name' => $name,
|
||||||
|
'url' => $url,
|
||||||
|
'icon' => $icon,
|
||||||
|
'sort_order' => 0
|
||||||
|
));
|
||||||
|
echo '<div class="notice notice-success"><p>链接添加成功!</p></div>';
|
||||||
|
} else {
|
||||||
|
echo '<div class="notice notice-error"><p>该URL的链接已存在!</p></div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'update_links':
|
||||||
|
if (!empty($_POST['link_ids'])) {
|
||||||
|
foreach ($_POST['link_ids'] as $index => $id) {
|
||||||
|
$wpdb->update($table_name, array(
|
||||||
|
'name' => sanitize_text_field($_POST['link_names'][$index]),
|
||||||
|
'url' => esc_url_raw($_POST['link_urls'][$index]),
|
||||||
|
'icon' => esc_url_raw($_POST['link_icons'][$index]),
|
||||||
|
'sort_order' => $index
|
||||||
|
), array('id' => intval($id)));
|
||||||
|
}
|
||||||
|
echo '<div class="notice notice-success"><p>链接更新成功!</p></div>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'delete_link':
|
||||||
|
if (!empty($_POST['link_id'])) {
|
||||||
|
$wpdb->delete($table_name, array('id' => intval($_POST['link_id'])));
|
||||||
|
echo '<div class="notice notice-success"><p>链接删除成功!</p></div>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'export_links':
|
||||||
|
$links = $wpdb->get_results("SELECT name, url, icon FROM $table_name ORDER BY sort_order ASC");
|
||||||
|
|
||||||
|
// 清除所有输出缓冲
|
||||||
|
while (ob_get_level()) {
|
||||||
|
ob_end_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
header('Content-Type: text/csv; charset=utf-8');
|
||||||
|
header('Content-Disposition: attachment; filename=friend-links-export-' . date('Y-m-d') . '.csv');
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
header('Expires: 0');
|
||||||
|
|
||||||
|
$output = fopen('php://output', 'w');
|
||||||
|
|
||||||
|
// 添加BOM头解决中文乱码
|
||||||
|
fwrite($output, chr(0xEF).chr(0xBB).chr(0xBF));
|
||||||
|
|
||||||
|
// 只写入三列标题
|
||||||
|
fputcsv($output, array(
|
||||||
|
'网站名称',
|
||||||
|
'网站URL',
|
||||||
|
'图标URL'
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach ($links as $link) {
|
||||||
|
// 对每列数据应用极端清理
|
||||||
|
fputcsv($output, array(
|
||||||
|
flm_sanitize_export_text($link->name),
|
||||||
|
flm_sanitize_export_url($link->url),
|
||||||
|
flm_sanitize_export_url($link->icon)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($output);
|
||||||
|
exit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'import_links':
|
||||||
|
if (!empty($_FILES['import_file']['tmp_name'])) {
|
||||||
|
$file = $_FILES['import_file']['tmp_name'];
|
||||||
|
$handle = fopen($file, 'r');
|
||||||
|
$import_count = 0;
|
||||||
|
$update_count = 0;
|
||||||
|
$error_count = 0;
|
||||||
|
|
||||||
|
// 跳过标题行
|
||||||
|
fgetcsv($handle);
|
||||||
|
|
||||||
|
while (($data = fgetcsv($handle)) !== false) {
|
||||||
|
if (count($data) < 2 || empty($data[0]) || empty($data[1])) {
|
||||||
|
$error_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = sanitize_text_field($data[0]);
|
||||||
|
$url = esc_url_raw($data[1]);
|
||||||
|
$icon = isset($data[2]) ? esc_url_raw($data[2]) : flm_get_favicon($data[1]);
|
||||||
|
|
||||||
|
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
|
$error_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($name) && !empty($url)) {
|
||||||
|
$existing = $wpdb->get_row($wpdb->prepare(
|
||||||
|
"SELECT id FROM $table_name WHERE url = %s",
|
||||||
|
$url
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($existing) {
|
||||||
|
$wpdb->update($table_name, array(
|
||||||
|
'name' => $name,
|
||||||
|
'icon' => $icon
|
||||||
|
), array('id' => $existing->id));
|
||||||
|
$update_count++;
|
||||||
|
} else {
|
||||||
|
$wpdb->insert($table_name, array(
|
||||||
|
'name' => $name,
|
||||||
|
'url' => $url,
|
||||||
|
'icon' => $icon,
|
||||||
|
'sort_order' => 0
|
||||||
|
));
|
||||||
|
$import_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($handle);
|
||||||
|
|
||||||
|
$message = sprintf(
|
||||||
|
'导入完成!新增 %d 条链接,更新 %d 条已有链接',
|
||||||
|
$import_count,
|
||||||
|
$update_count
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($error_count > 0) {
|
||||||
|
$message .= sprintf(',跳过 %d 条格式不正确的记录', $error_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="notice notice-success"><p>' . $message . '</p></div>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有链接
|
||||||
|
$links = $wpdb->get_results("SELECT * FROM $table_name ORDER BY sort_order ASC");
|
||||||
|
?>
|
||||||
|
<div class="wrap">
|
||||||
|
<h1>友情链接管理</h1>
|
||||||
|
|
||||||
|
<div class="flm-admin-container">
|
||||||
|
<!-- 添加新链接表单 -->
|
||||||
|
<div class="flm-add-form">
|
||||||
|
<h2>添加新链接</h2>
|
||||||
|
<form method="post">
|
||||||
|
<?php wp_nonce_field('flm_nonce'); ?>
|
||||||
|
<input type="hidden" name="flm_action" value="add_link">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">网站名称 (必填)</label>
|
||||||
|
<input type="text" id="name" name="name" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="url">网站URL (必填)</label>
|
||||||
|
<input type="url" id="url" name="url" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="icon">网站图标URL (可选)</label>
|
||||||
|
<input type="url" id="icon" name="icon">
|
||||||
|
<p class="description">留空将自动获取favicon</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="button button-primary">添加链接</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 链接列表 -->
|
||||||
|
<div class="flm-links-list">
|
||||||
|
<h2>链接列表</h2>
|
||||||
|
|
||||||
|
<form method="post" id="flm-links-form">
|
||||||
|
<?php wp_nonce_field('flm_nonce'); ?>
|
||||||
|
<input type="hidden" name="flm_action" value="update_links">
|
||||||
|
|
||||||
|
<ul id="flm-sortable-links">
|
||||||
|
<?php foreach ($links as $link): ?>
|
||||||
|
<li class="flm-link-item">
|
||||||
|
<input type="hidden" name="link_ids[]" value="<?php echo $link->id; ?>">
|
||||||
|
|
||||||
|
<div class="flm-link-preview">
|
||||||
|
<?php if ($link->icon): ?>
|
||||||
|
<img src="<?php echo esc_url($link->icon); ?>" alt="<?php echo esc_attr($link->name); ?>" class="flm-link-icon">
|
||||||
|
<?php endif; ?>
|
||||||
|
<span class="flm-link-name"><?php echo esc_html($link->name); ?></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flm-link-fields">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>网站名称</label>
|
||||||
|
<input type="text" name="link_names[]" value="<?php echo esc_attr($link->name); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>网站URL</label>
|
||||||
|
<input type="url" name="link_urls[]" value="<?php echo esc_attr($link->url); ?>" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>网站图标URL</label>
|
||||||
|
<input type="url" name="link_icons[]" value="<?php echo esc_attr($link->icon); ?>">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flm-link-actions">
|
||||||
|
<button type="button" class="button flm-delete-link" data-link-id="<?php echo $link->id; ?>">删除</button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<?php if (!empty($links)): ?>
|
||||||
|
<button type="submit" class="button button-primary">保存更改</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- 导入导出 -->
|
||||||
|
<div class="flm-import-export">
|
||||||
|
<h3>导入/导出</h3>
|
||||||
|
|
||||||
|
<div class="flm-export">
|
||||||
|
<form method="post">
|
||||||
|
<?php wp_nonce_field('flm_nonce'); ?>
|
||||||
|
<input type="hidden" name="flm_action" value="export_links">
|
||||||
|
<button type="submit" class="button">导出为CSV</button>
|
||||||
|
<p class="description">导出的CSV文件将只包含三列数据:网站名称、网站URL、图标URL</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flm-import">
|
||||||
|
<form method="post" enctype="multipart/form-data">
|
||||||
|
<?php wp_nonce_field('flm_nonce'); ?>
|
||||||
|
<input type="hidden" name="flm_action" value="import_links">
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="import_file">选择CSV文件</label>
|
||||||
|
<input type="file" id="import_file" name="import_file" accept=".csv" required>
|
||||||
|
<p class="description">请选择包含三列数据的CSV文件(网站名称、网站URL、图标URL)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="button button-primary">导入链接</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
// 使链接可排序
|
||||||
|
$('#flm-sortable-links').sortable();
|
||||||
|
|
||||||
|
// 删除链接
|
||||||
|
$('.flm-delete-link').on('click', function() {
|
||||||
|
if (confirm('确定要删除这个链接吗?')) {
|
||||||
|
var linkId = $(this).data('link-id');
|
||||||
|
var $form = $('#flm-links-form');
|
||||||
|
|
||||||
|
$form.append('<input type="hidden" name="flm_action" value="delete_link">');
|
||||||
|
$form.append('<input type="hidden" name="link_id" value="' + linkId + '">');
|
||||||
|
$form.submit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理AJAX删除请求
|
||||||
|
add_action('wp_ajax_flm_delete_link', 'flm_ajax_delete_link');
|
||||||
|
function flm_ajax_delete_link() {
|
||||||
|
check_ajax_referer('flm_nonce', 'nonce');
|
||||||
|
|
||||||
|
if (!empty($_POST['link_id'])) {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'friend_links';
|
||||||
|
$wpdb->delete($table_name, array('id' => intval($_POST['link_id'])));
|
||||||
|
wp_send_json_success();
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json_error();
|
||||||
|
}
|
37
includes/shortcode.php
Normal file
37
includes/shortcode.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
// 注册短代码
|
||||||
|
add_shortcode('friend_links', 'flm_display_friend_links');
|
||||||
|
function flm_display_friend_links($atts) {
|
||||||
|
global $wpdb;
|
||||||
|
$table_name = $wpdb->prefix . 'friend_links';
|
||||||
|
|
||||||
|
$atts = shortcode_atts(array(
|
||||||
|
'random' => 'true'
|
||||||
|
), $atts);
|
||||||
|
|
||||||
|
$order_by = ($atts['random'] === 'true') ? 'RAND()' : 'sort_order ASC';
|
||||||
|
$links = $wpdb->get_results("SELECT * FROM $table_name ORDER BY $order_by");
|
||||||
|
|
||||||
|
if (empty($links)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<div class="flm-links-container">
|
||||||
|
<?php foreach ($links as $link): ?>
|
||||||
|
<div class="flm-link-card">
|
||||||
|
<a href="<?php echo esc_url($link->url); ?>" target="_blank" rel="noopener noreferrer">
|
||||||
|
<div class="flm-link-icon-container">
|
||||||
|
<img src="<?php echo $link->icon ?: FLM_PLUGIN_URL . 'assets/images/default-icon.png'; ?>"
|
||||||
|
alt="<?php echo esc_attr($link->name); ?>"
|
||||||
|
class="flm-link-icon">
|
||||||
|
</div>
|
||||||
|
<div class="flm-link-name"><?php echo esc_html($link->name); ?></div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
Reference in New Issue
Block a user