back to top

添加友链自动获取RSS(小工具版)

步骤1:创建插件文件

  1. 登录网站的文件管理器或使用FTP
  2. 进入 /wp-content/plugins/ 目录
  3. 创建新文件夹 friendlinks-manager
  4. 在文件夹中创建文件 friendlinks-manager.php,并粘贴下面的完整代码

代码1

<?php
/**
 * Plugin Name: 友链管理与RSS订阅
 * Plugin URI: https://yourwebsite.com/
 * Description: 为WordPress添加友链管理功能并自动抓取友链最新文章
 * Version: 2.1.0
 * Author: Your Name
 * License: GPL v2 or later
 * Text Domain: friendlinks
 */

// 防止直接访问
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * 1. 启用WordPress原生链接管理功能
 */
add_filter( 'pre_option_link_manager_enabled', '__return_true' );

/**
 * 2. 创建links_meta表
 */
register_activation_hook( __FILE__, 'friendlinks_create_tables' );
function friendlinks_create_tables() {
    global $wpdb;
    
    $table_name = $wpdb->prefix . 'links_meta';
    $charset_collate = $wpdb->get_charset_collate();
    
    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        link_id bigint(20) unsigned NOT NULL DEFAULT '0',
        meta_key varchar(255) DEFAULT NULL,
        meta_value longtext,
        PRIMARY KEY (meta_id),
        KEY link_id (link_id),
        KEY meta_key (meta_key)
    ) $charset_collate;";
    
    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
    
    // 创建抓取记录选项
    add_option( 'friendlinks_last_fetch', 0 );
    add_option( 'friendlinks_fetch_count', 0 );
    add_option( 'friendlinks_last_error', '' );
}

/**
 * 3. 为链接添加RSS字段
 */
// 在链接编辑页面添加RSS字段
add_action( 'add_link_fields', 'friendlinks_add_rss_field' );
add_action( 'edit_link_fields', 'friendlinks_add_rss_field' );
function friendlinks_add_rss_field( $link ) {
    $rss_url = isset( $link ) ? friendlinks_get_rss_url( $link->link_id ) : '';
    ?>
    <div class="form-field">
        <label for="link_rss"><?php _e( 'RSS地址', 'friendlinks' ); ?></label>
        <input name="link_rss" id="link_rss" type="url" value="<?php echo esc_url( $rss_url ); ?>" style="width: 95%;" />
        <p class="description"><?php _e( '输入此友链的RSS订阅地址(如:https://example.com/feed/)', 'friendlinks' ); ?></p>
    </div>
    <?php
}

// 保存RSS字段
add_action( 'add_link', 'friendlinks_save_rss_field' );
add_action( 'edit_link', 'friendlinks_save_rss_field' );
function friendlinks_save_rss_field( $link_id ) {
    if ( isset( $_POST['link_rss'] ) ) {
        $rss_url = esc_url_raw( $_POST['link_rss'] );
        friendlinks_update_meta( $link_id, 'rss_url', $rss_url );
    }
}

/**
 * 4. 链接元数据操作函数
 */
function friendlinks_update_meta( $link_id, $meta_key, $meta_value ) {
    global $wpdb;
    
    $link_id = absint( $link_id );
    if ( ! $link_id || ! $meta_key ) {
        return false;
    }
    
    $table_name = $wpdb->prefix . 'links_meta';
    
    // 检查记录是否存在
    $exists = $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM $table_name WHERE link_id = %d AND meta_key = %s",
        $link_id,
        $meta_key
    ) );
    
    $meta_value = maybe_serialize( $meta_value );
    
    if ( $exists ) {
        // 更新
        return $wpdb->update(
            $table_name,
            array( 'meta_value' => $meta_value ),
            array( 'link_id' => $link_id, 'meta_key' => $meta_key ),
            array( '%s' ),
            array( '%d', '%s' )
        );
    } else {
        // 插入
        return $wpdb->insert(
            $table_name,
            array(
                'link_id' => $link_id,
                'meta_key' => $meta_key,
                'meta_value' => $meta_value
            ),
            array( '%d', '%s', '%s' )
        );
    }
}

function friendlinks_get_meta( $link_id, $meta_key = '', $single = true ) {
    global $wpdb;
    
    $link_id = absint( $link_id );
    if ( ! $link_id ) {
        return $single ? '' : array();
    }
    
    $table_name = $wpdb->prefix . 'links_meta';
    
    if ( $meta_key ) {
        $meta_value = $wpdb->get_var( $wpdb->prepare(
            "SELECT meta_value FROM $table_name WHERE link_id = %d AND meta_key = %s",
            $link_id,
            $meta_key
        ) );
        
        if ( $meta_value ) {
            $meta_value = maybe_unserialize( $meta_value );
            return $single ? $meta_value : array( $meta_value );
        }
        
        return $single ? '' : array();
    }
    
    // 获取所有元数据
    $results = $wpdb->get_results( $wpdb->prepare(
        "SELECT meta_key, meta_value FROM $table_name WHERE link_id = %d",
        $link_id
    ) );
    
    $meta = array();
    foreach ( $results as $result ) {
        $meta[$result->meta_key] = maybe_unserialize( $result->meta_value );
    }
    
    return $meta;
}

function friendlinks_get_rss_url( $link_id ) {
    return friendlinks_get_meta( $link_id, 'rss_url' );
}

/**
 * 5. RSS抓取与缓存系统(增强版)
 */
class FriendLinks_RSS_Aggregator {
    
    private static $instance = null;
    private $cache_expiration = 3600; // 1小时缓存
    
    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        add_action( 'wp_feed_options', array( $this, 'set_feed_options' ) );
        add_action( 'friendlinks_fetch_rss', array( $this, 'fetch_all_feeds' ) );
        
        // 计划任务
        if ( ! wp_next_scheduled( 'friendlinks_fetch_rss' ) ) {
            wp_schedule_event( time(), 'hourly', 'friendlinks_fetch_rss' );
        }
        
        // 初始化错误日志
        $this->log( 'FriendLinks RSS聚合系统初始化完成' );
    }
    
    // 设置Feed抓取选项
    public function set_feed_options( $feed ) {
        $feed->set_timeout( 15 ); // 15秒超时
        $feed->enable_cache( true );
        $feed->set_cache_duration( $this->cache_expiration );
        $feed->force_feed( true ); // 强制解析为feed
    }
    
    // 抓取所有友链的RSS
    public function fetch_all_feeds( $posts_per_source = 0 ) {
        global $wpdb;
        
        $this->log( '开始抓取所有友链RSS...' );
        if ( $posts_per_source > 0 ) {
            $this->log( '每源抓取文章数: ' . $posts_per_source );
        }
        
        $links = $wpdb->get_results(
            "SELECT l.link_id, l.link_name, l.link_url, lm.meta_value as rss_url 
             FROM {$wpdb->links} l 
             LEFT JOIN {$wpdb->prefix}links_meta lm ON l.link_id = lm.link_id 
             WHERE lm.meta_key = 'rss_url' AND lm.meta_value != '' AND lm.meta_value IS NOT NULL"
        );
        
        $this->log( '找到 ' . count( $links ) . ' 个需要抓取的友链RSS' );
        
        $all_posts = array();
        $success_count = 0;
        $error_count = 0;
        
        if ( ! empty( $links ) ) {
            foreach ( $links as $link ) {
                $this->log( '正在处理友链: ' . $link->link_name . ' (' . $link->rss_url . ')' );
                
                $feed_posts = $this->fetch_feed( $link->rss_url, $link->link_id, $link->link_name, $posts_per_source );
                
                if ( ! empty( $feed_posts ) ) {
                    $all_posts = array_merge( $all_posts, $feed_posts );
                    $success_count++;
                    $this->log( '✓ 成功获取 ' . count( $feed_posts ) . ' 篇文章' );
                } else {
                    $error_count++;
                    $this->log( '✗ 获取失败或没有文章' );
                }
            }
            
            // 按日期排序
            usort( $all_posts, function( $a, $b ) {
                return strtotime( $b['date'] ) - strtotime( $a['date'] );
            } );
            
            $this->log( '总共获取到 ' . count( $all_posts ) . ' 篇文章,成功: ' . $success_count . ',失败: ' . $error_count );
        }
        
        // 存储到缓存
        set_transient( 'friendlinks_rss_cache', $all_posts, $this->cache_expiration );
        update_option( 'friendlinks_last_fetch', current_time( 'timestamp' ) );
        update_option( 'friendlinks_fetch_count', $success_count );
        
        if ( $error_count > 0 ) {
            update_option( 'friendlinks_last_error', date( 'Y-m-d H:i:s' ) . ' - ' . $error_count . '个RSS源获取失败' );
        }
        
        return $all_posts;
    }
    
    // 抓取单个RSS源(支持每源文章数限制)
    private function fetch_feed( $feed_url, $link_id, $link_name, $posts_per_source = 0 ) {
        $this->log( '[详细] 开始抓取: ' . $link_name );
        $this->log( '[详细] RSS地址: ' . $feed_url );
        
        if ( ! function_exists( 'fetch_feed' ) ) {
            include_once( ABSPATH . WPINC . '/feed.php' );
            $this->log( '[详细] 已加载feed.php' );
        }
        
        // 验证URL格式
        if ( ! filter_var( $feed_url, FILTER_VALIDATE_URL ) ) {
            $this->log( '[详细] ✗ RSS地址格式无效' );
            return array();
        }
        
        $this->log( '[详细] 调用fetch_feed()...' );
        $feed = fetch_feed( $feed_url );
        
        if ( is_wp_error( $feed ) ) {
            $error_msg = $feed->get_error_message();
            $this->log( '[详细] ✗ fetch_feed错误: ' . $error_msg );
            
            // 记录具体错误类型
            if ( strpos( $error_msg, 'cURL error 28' ) !== false ) {
                $this->log( '[详细] 分析: 连接超时' );
            } elseif ( strpos( strtolower( $error_msg ), 'ssl' ) !== false ) {
                $this->log( '[详细] 分析: SSL证书问题' );
            } elseif ( strpos( $error_msg, 'Could not find' ) !== false ) {
                $this->log( '[详细] 分析: 无法找到Feed,URL可能错误' );
            }
            
            return array();
        }
        
        $this->log( '[详细] ✓ Feed对象获取成功' );
        $this->log( '[详细] Feed标题: ' . $feed->get_title() );
        
        // 检查Feed是否有效
        if ( ! $feed || ! method_exists( $feed, 'get_item_quantity' ) ) {
            $this->log( '[详细] ✗ Feed对象无效' );
            return array();
        }
        
        // 使用传入的 posts_per_source,如果没有则使用默认值
        $max_items = ( $posts_per_source > 0 ) ? $posts_per_source : 3;
        $items = $feed->get_items( 0, $max_items );
        
        $this->log( '[详细] 获取到 ' . count( $items ) . ' 篇文章 (每源限制: ' . $max_items . ')' );
        
        $posts = array();
        foreach ( $items as $index => $item ) {
            $post_title = esc_html( $item->get_title() );
            $post_link = esc_url( $item->get_permalink() );
            $post_date = $item->get_date( 'Y-m-d H:i:s' );
            
            $this->log( "[详细] 文章{$index}: {$post_title}" );
            
            $posts[] = array(
                'title'     => $post_title,
                'link'      => $post_link,
                'date'      => $post_date,
                'timestamp' => strtotime( $item->get_date( 'c' ) ),
                'source_id' => $link_id,
                'source'    => $link_name ?: $feed->get_title()
            );
        }
        
        $this->log( '[详细] ✓ 完成处理,返回 ' . count( $posts ) . ' 篇文章' );
        return $posts;
    }
    
    // 获取缓存的文章(支持源数量限制和每源文章数限制)
    public function get_cached_posts( $limit = 10, $source_limit = 0, $posts_per_source = 0 ) {
        $cached = get_transient( 'friendlinks_rss_cache' );
        
        // 如果缓存过期或为空,尝试抓取
        if ( false === $cached || empty( $cached ) ) {
            $this->log( '缓存为空或过期,尝试重新抓取' );
            $cached = $this->fetch_all_feeds( $posts_per_source ); // 传递每源文章数
        }
        
        if ( empty( $cached ) ) {
            return array();
        }
        
        // 如果有源数量限制,进行筛选
        if ( $source_limit > 0 ) {
            $cached = $this->filter_by_source_limit( $cached, $source_limit, $posts_per_source );
        }
        
        // 应用总数量限制
        $result = array_slice( $cached, 0, $limit );
        $this->log( '返回文章: 总数限制 ' . $limit . ',源限制 ' . $source_limit . ',每源限制 ' . $posts_per_source . ',实际返回 ' . count( $result ) . ' 篇' );
        
        return $result;
    }
    
    // 根据源数量限制筛选文章
    private function filter_by_source_limit( $posts, $source_limit, $posts_per_source ) {
        if ( $source_limit <= 0 ) {
            return $posts;
        }
        
        $sources = array();
        $filtered_posts = array();
        $per_source_limit = ( $posts_per_source > 0 ) ? $posts_per_source : 2;
        
        foreach ( $posts as $post ) {
            $source_id = $post['source_id'];
            
            // 初始化该源的计数器
            if ( ! isset( $sources[$source_id] ) ) {
                $sources[$source_id] = 0;
            }
            
            // 如果该源的文章数量还没达到限制,并且我们还没有超过源数量限制
            if ( $sources[$source_id] < $per_source_limit && count( $sources ) <= $source_limit ) {
                $filtered_posts[] = $post;
                $sources[$source_id]++;
            }
            
            // 如果已经达到源数量限制且每个源都达到了文章限制,可以提前结束
            if ( count( $sources ) >= $source_limit ) {
                $all_sources_full = true;
                foreach ( $sources as $count ) {
                    if ( $count < $per_source_limit ) {
                        $all_sources_full = false;
                        break;
                    }
                }
                if ( $all_sources_full ) {
                    break;
                }
            }
        }
        
        $this->log( '按源限制筛选: 限制 ' . $source_limit . ' 个源,每源 ' . $per_source_limit . ' 篇,实际 ' . count( $sources ) . ' 个源,' . count( $filtered_posts ) . ' 篇文章' );
        return $filtered_posts;
    }
    
    // 获取最后抓取时间(友好格式)
    public function get_last_fetch_time() {
        $timestamp = get_option( 'friendlinks_last_fetch', 0 );
        if ( ! $timestamp ) {
            return __( '从未抓取', 'friendlinks' );
        }
        
        $current_time = current_time( 'timestamp' );
        $diff = $current_time - $timestamp;
        
        if ( $diff < 60 ) {
            return __( '刚刚', 'friendlinks' );
        } elseif ( $diff < 3600 ) {
            $minutes = floor( $diff / 60 );
            return sprintf( __( '%d分钟前', 'friendlinks' ), $minutes );
        } elseif ( $diff < 86400 ) {
            $hours = floor( $diff / 3600 );
            return sprintf( __( '%d小时前', 'friendlinks' ), $hours );
        } else {
            $days = floor( $diff / 86400 );
            return sprintf( __( '%d天前', 'friendlinks' ), $days );
        }
    }
    
    // 日志记录函数
    private function log( $message ) {
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            $timestamp = date( 'Y-m-d H:i:s' );
            error_log( "[FriendLinks RSS] {$timestamp} - {$message}" );
        }
    }
}

// 初始化RSS聚合器
add_action( 'init', function() {
    FriendLinks_RSS_Aggregator::get_instance();
} );

/**
 * 6. 友情链接小工具
 */
class FriendLinks_Widget extends WP_Widget {
    
    public function __construct() {
        parent::__construct(
            'friendlinks_widget',
            __( '友情链接', 'friendlinks' ),
            array(
                'description' => __( '显示友情链接列表', 'friendlinks' ),
                'classname' => 'widget_friendlinks'
            )
        );
    }
    
    public function widget( $args, $instance ) {
        $title = ! empty( $instance['title'] ) ? $instance['title'] : __( '友情链接', 'friendlinks' );
        $category = ! empty( $instance['category'] ) ? $instance['category'] : '';
        $orderby = ! empty( $instance['orderby'] ) ? $instance['orderby'] : 'name';
        $limit = ! empty( $instance['limit'] ) ? absint( $instance['limit'] ) : 0;
        $show_description = ! empty( $instance['show_description'] ) ? true : false;
        
        echo $args['before_widget'];
        
        if ( $title ) {
            echo $args['before_title'] . apply_filters( 'widget_title', $title ) . $args['after_title'];
        }
        
        $query_args = array(
            'orderby' => $orderby,
            'order'   => 'ASC',
            'title_li' => '',
            'echo' => 0
        );
        
        if ( ! empty( $category ) ) {
            if ( is_numeric( $category ) ) {
                $query_args['category'] = $category;
            } else {
                $query_args['category_name'] = $category;
            }
        }
        
        if ( $limit > 0 ) {
            $query_args['limit'] = $limit;
        }
        
        if ( $show_description ) {
            $query_args['show_description'] = true;
        }
        
        $links = wp_list_bookmarks( apply_filters( 'widget_links_args', $query_args ) );
        
        if ( $links ) {
            echo '<ul class="friendlinks-list">' . $links . '</ul>';
        } else {
            echo '<p class="friendlinks-empty">' . __( '暂无友情链接', 'friendlinks' ) . '</p>';
        }
        
        echo $args['after_widget'];
    }
    
    public function form( $instance ) {
        $defaults = array(
            'title' => __( '友情链接', 'friendlinks' ),
            'category' => '',
            'orderby' => 'name',
            'limit' => 0,
            'show_description' => false
        );
        
        $instance = wp_parse_args( (array) $instance, $defaults );
        
        ?>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( '标题:', 'friendlinks' ); ?></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( $instance['title'] ); ?>">
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>"><?php _e( '链接分类:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'category' ) ); ?>" type="text" 
                   value="<?php echo esc_attr( $instance['category'] ); ?>">
            <small><?php _e( '输入分类ID、名称或别名,留空显示所有', 'friendlinks' ); ?></small>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>"><?php _e( '排序方式:', 'friendlinks' ); ?></label>
            <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>" 
                    name="<?php echo esc_attr( $this->get_field_name( 'orderby' ) ); ?>">
                <option value="name" <?php selected( $instance['orderby'], 'name' ); ?>><?php _e( '名称', 'friendlinks' ); ?></option>
                <option value="rating" <?php selected( $instance['orderby'], 'rating' ); ?>><?php _e( '评分', 'friendlinks' ); ?></option>
                <option value="id" <?php selected( $instance['orderby'], 'id' ); ?>><?php _e( 'ID', 'friendlinks' ); ?></option>
                <option value="url" <?php selected( $instance['orderby'], 'url' ); ?>><?php _e( '网址', 'friendlinks' ); ?></option>
                <option value="updated" <?php selected( $instance['orderby'], 'updated' ); ?>><?php _e( '更新时间', 'friendlinks' ); ?></option>
                <option value="rand" <?php selected( $instance['orderby'], 'rand' ); ?>><?php _e( '随机', 'friendlinks' ); ?></option>
            </select>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>"><?php _e( '显示数量:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'limit' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['limit'] ); ?>" min="0" step="1">
            <small><?php _e( '0表示不限制', 'friendlinks' ); ?></small>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_description'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_description' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_description' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_description' ) ); ?>"><?php _e( '显示描述', 'friendlinks' ); ?></label>
        </p>
        <?php
    }
    
    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = ! empty( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '';
        $instance['category'] = ! empty( $new_instance['category'] ) ? sanitize_text_field( $new_instance['category'] ) : '';
        $instance['orderby'] = ! empty( $new_instance['orderby'] ) ? sanitize_text_field( $new_instance['orderby'] ) : 'name';
        $instance['limit'] = ! empty( $new_instance['limit'] ) ? absint( $new_instance['limit'] ) : 0;
        $instance['show_description'] = ! empty( $new_instance['show_description'] ) ? 1 : 0;
        
        return $instance;
    }
}

/**
 * 7. 友链最新文章小工具(增强配置版)
 */
class FriendLinks_RSS_Widget extends WP_Widget {
    
    public function __construct() {
        parent::__construct(
            'friendlinks_rss_widget',
            __( '友链最新文章', 'friendlinks' ),
            array(
                'description' => __( '显示友情链接的最新文章,可配置显示源数量和每源文章数', 'friendlinks' ),
                'classname' => 'widget_friendlinks_rss'
            )
        );
    }
    
    public function widget( $args, $instance ) {
        $title = ! empty( $instance['title'] ) ? $instance['title'] : __( '友链最新文章', 'friendlinks' );
        $limit = ! empty( $instance['limit'] ) ? absint( $instance['limit'] ) : 5;
        $source_limit = ! empty( $instance['source_limit'] ) ? absint( $instance['source_limit'] ) : 0;
        $posts_per_source = ! empty( $instance['posts_per_source'] ) ? absint( $instance['posts_per_source'] ) : 0;
        $show_date = ! empty( $instance['show_date'] ) ? true : false;
        $show_source = ! empty( $instance['show_source'] ) ? true : false;
        $show_fetch_time = ! empty( $instance['show_fetch_time'] ) ? true : false;
        
        $aggregator = FriendLinks_RSS_Aggregator::get_instance();
        $posts = $aggregator->get_cached_posts( $limit, $source_limit, $posts_per_source );
        
        echo $args['before_widget'];
        
        if ( $title ) {
            echo $args['before_title'] . apply_filters( 'widget_title', $title ) . $args['after_title'];
        }
        
        if ( empty( $posts ) ) {
            echo '<div class="friendlinks-no-content">';
            echo '<p>' . __( '暂无友链动态数据', 'friendlinks' ) . '</p>';
            
            if ( $show_fetch_time ) {
                $last_fetch = $aggregator->get_last_fetch_time();
                echo '<p class="friendlinks-fetch-time">' . sprintf( __( '最后抓取尝试:%s', 'friendlinks' ), $last_fetch ) . '</p>';
            }
            echo '</div>';
        } else {
            echo '<ul class="friendlinks-rss-list">';
            foreach ( $posts as $post ) {
                echo '<li class="friendlinks-rss-item">';
                echo '<a href="' . esc_url( $post['link'] ) . '" target="_blank" rel="noopener noreferrer" class="friendlinks-rss-title">';
                echo esc_html( $post['title'] );
                echo '</a>';
                
                if ( $show_date && ! empty( $post['date'] ) ) {
                    $date_display = date_i18n( get_option( 'date_format' ), strtotime( $post['date'] ) );
                    echo '<span class="friendlinks-rss-date">' . $date_display . '</span>';
                }
                
                if ( $show_source && ! empty( $post['source'] ) ) {
                    // 修复:不再显示[测试]字样,改为更友好的格式
                    echo '<span class="friendlinks-rss-source">来自 ' . esc_html( $post['source'] ) . '</span>';
                }
                
                echo '</li>';
            }
            echo '</ul>';
            
            if ( $show_fetch_time ) {
                $last_fetch = $aggregator->get_last_fetch_time();
                echo '<div class="friendlinks-fetch-time">' . sprintf( __( '最后更新:%s', 'friendlinks' ), $last_fetch ) . '</div>';
            }
            
            // 显示配置摘要(调试用,可选)
            if ( current_user_can( 'manage_options' ) && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                echo '<div class="friendlinks-debug-info" style="display:none; font-size:10px; color:#999; margin-top:5px;">';
                echo '配置: 总限' . $limit . '篇';
                if ( $source_limit > 0 ) echo ',源限' . $source_limit . '个';
                if ( $posts_per_source > 0 ) echo ',每源' . $posts_per_source . '篇';
                echo ',实际' . count( $posts ) . '篇';
                echo '</div>';
            }
        }
        
        echo $args['after_widget'];
    }
    
    public function form( $instance ) {
        $defaults = array(
            'title' => __( '友链最新文章', 'friendlinks' ),
            'limit' => 5,
            'source_limit' => 0,
            'posts_per_source' => 0,
            'show_date' => true,
            'show_source' => false,
            'show_fetch_time' => true
        );
        
        $instance = wp_parse_args( (array) $instance, $defaults );
        
        ?>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( '标题:', 'friendlinks' ); ?></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( $instance['title'] ); ?>">
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>"><?php _e( '总显示数量:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'limit' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['limit'] ); ?>" min="1" max="20" step="1">
            <small><?php _e( '最多显示多少篇文章', 'friendlinks' ); ?></small>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'source_limit' ) ); ?>"><?php _e( '显示友链数量:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'source_limit' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'source_limit' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['source_limit'] ); ?>" min="0" max="10" step="1">
            <small><?php _e( '0表示不限制,最多显示多少个友链的数据', 'friendlinks' ); ?></small>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'posts_per_source' ) ); ?>"><?php _e( '每个友链显示文章数:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'posts_per_source' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'posts_per_source' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['posts_per_source'] ); ?>" min="0" max="10" step="1">
            <small><?php _e( '0表示不限制,每个友链最多显示多少篇文章', 'friendlinks' ); ?></small>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_date'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_date' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_date' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_date' ) ); ?>"><?php _e( '显示发布日期', 'friendlinks' ); ?></label>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_source'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_source' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_source' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_source' ) ); ?>"><?php _e( '显示来源网站', 'friendlinks' ); ?></label>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_fetch_time'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_fetch_time' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_fetch_time' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_fetch_time' ) ); ?>"><?php _e( '显示最后抓取时间', 'friendlinks' ); ?></label>
        </p>
        <?php
    }
    
    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = ! empty( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '';
        $instance['limit'] = ! empty( $new_instance['limit'] ) ? absint( $new_instance['limit'] ) : 5;
        $instance['source_limit'] = ! empty( $new_instance['source_limit'] ) ? absint( $new_instance['source_limit'] ) : 0;
        $instance['posts_per_source'] = ! empty( $new_instance['posts_per_source'] ) ? absint( $new_instance['posts_per_source'] ) : 0;
        $instance['show_date'] = ! empty( $new_instance['show_date'] ) ? 1 : 0;
        $instance['show_source'] = ! empty( $new_instance['show_source'] ) ? 1 : 0;
        $instance['show_fetch_time'] = ! empty( $new_instance['show_fetch_time'] ) ? 1 : 0;
        
        return $instance;
    }
}

/**
 * 8. 注册小工具
 */
function friendlinks_register_widgets() {
    register_widget( 'FriendLinks_Widget' );
    register_widget( 'FriendLinks_RSS_Widget' );
}
add_action( 'widgets_init', 'friendlinks_register_widgets' );

/**
 * 9. 添加管理页面
 */
add_action( 'admin_menu', 'friendlinks_add_admin_page' );
function friendlinks_add_admin_page() {
    add_links_page(
        __( '友链RSS设置', 'friendlinks' ),
        __( 'RSS设置', 'friendlinks' ),
        'manage_options',
        'friendlinks-settings',
        'friendlinks_admin_page_content'
    );
}

function friendlinks_admin_page_content() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    
    // 处理手动抓取请求
    if ( isset( $_POST['friendlinks_manual_fetch'] ) && check_admin_referer( 'friendlinks_manual_fetch' ) ) {
        $aggregator = FriendLinks_RSS_Aggregator::get_instance();
        $result = $aggregator->fetch_all_feeds();
        $message = __( 'RSS抓取已完成!', 'friendlinks' );
    }
    
    $aggregator = FriendLinks_RSS_Aggregator::get_instance();
    $last_fetch = $aggregator->get_last_fetch_time();
    $fetch_count = get_option( 'friendlinks_fetch_count', 0 );
    $last_error = get_option( 'friendlinks_last_error', '' );
    
    // 获取当前配置摘要
    global $wpdb;
    $total_links = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->links}" );
    $rss_links = $wpdb->get_var( 
        "SELECT COUNT(DISTINCT l.link_id) 
         FROM {$wpdb->links} l 
         LEFT JOIN {$wpdb->prefix}links_meta lm ON l.link_id = lm.link_id 
         WHERE lm.meta_key = 'rss_url' AND lm.meta_value != ''"
    );
    
    ?>
    <div class="wrap">
        <h1><?php _e( '友链RSS设置', 'friendlinks' ); ?></h1>
        
        <?php if ( isset( $message ) ) : ?>
            <div class="notice notice-success is-dismissible">
                <p><?php echo $message; ?></p>
            </div>
        <?php endif; ?>
        
        <div class="card">
            <h2 class="title"><?php _e( '系统概览', 'friendlinks' ); ?></h2>
            <table class="form-table">
                <tr>
                    <th scope="row"><?php _e( '友链总数', 'friendlinks' ); ?></th>
                    <td><?php echo $total_links; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '已配置RSS的友链', 'friendlinks' ); ?></th>
                    <td><?php echo $rss_links; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '最后抓取时间', 'friendlinks' ); ?></th>
                    <td><?php echo $last_fetch; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '成功抓取的RSS源', 'friendlinks' ); ?></th>
                    <td><?php echo $fetch_count; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '下次计划抓取', 'friendlinks' ); ?></th>
                    <td>
                        <?php
                        $next_scheduled = wp_next_scheduled( 'friendlinks_fetch_rss' );
                        if ( $next_scheduled ) {
                            echo date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $next_scheduled );
                        } else {
                            _e( '未计划', 'friendlinks' );
                        }
                        ?>
                    </td>
                </tr>
                <?php if ( ! empty( $last_error ) ) : ?>
                <tr>
                    <th scope="row"><?php _e( '最后错误', 'friendlinks' ); ?></th>
                    <td style="color: #dc3232;"><?php echo esc_html( $last_error ); ?></td>
                </tr>
                <?php endif; ?>
            </table>
        </div>
        
        <div class="card">
            <h2 class="title"><?php _e( '手动操作', 'friendlinks' ); ?></h2>
            <form method="post">
                <?php wp_nonce_field( 'friendlinks_manual_fetch' ); ?>
                <p><?php _e( '点击按钮立即抓取所有友链的RSS更新:', 'friendlinks' ); ?></p>
                <p>
                    <input type="submit" name="friendlinks_manual_fetch" class="button button-primary" 
                           value="<?php _e( '立即抓取RSS', 'friendlinks' ); ?>">
                </p>
            </form>
        </div>
        
        <div class="card">
            <h2 class="title"><?php _e( '新功能介绍', 'friendlinks' ); ?></h2>
            <p><strong>版本 2.1.0 新增功能:</strong></p>
            <ul>
                <li>✓ 修复RSS来源显示格式(不再显示<code>[测试]</code>字样)</li>
                <li>✓ 新增"显示友链数量"配置(控制显示几个友链的数据)</li>
                <li>✓ 新增"每个友链显示文章数"配置(控制每个源显示多少条文章)</li>
                <li>✓ 增强的日志系统,记录详细的配置执行信息</li>
            </ul>
            <p><?php _e( '要使用新功能,请前往"外观 → 小工具",编辑"友链最新文章"小工具进行配置。', 'friendlinks' ); ?></p>
        </div>
        
        <div class="card">
            <h2 class="title"><?php _e( '调试信息', 'friendlinks' ); ?></h2>
            <p><?php _e( '调试日志位置:', 'friendlinks' ); ?> <code>/wp-content/debug.log</code></p>
            <p><?php _e( '日志中包含以 [FriendLinks RSS] 开头的详细抓取和配置信息。', 'friendlinks' ); ?></p>
            <p>
                <a href="<?php echo admin_url( 'site-health.php?tab=debug' ); ?>" class="button">
                    <?php _e( '查看站点健康状态', 'friendlinks' ); ?>
                </a>
                <button type="button" class="button button-secondary" onclick="clearFriendlinksCache()">
                    <?php _e( '清除RSS缓存', 'friendlinks' ); ?>
                </button>
            </p>
        </div>
        
        <script>
        function clearFriendlinksCache() {
            if (confirm('确定要清除友链RSS缓存吗?下次访问时将重新抓取数据。')) {
                jQuery.post(ajaxurl, {
                    action: 'friendlinks_clear_cache',
                    _ajax_nonce: '<?php echo wp_create_nonce( "friendlinks_clear_cache" ); ?>'
                }, function(response) {
                    if (response.success) {
                        alert('缓存已清除!');
                        location.reload();
                    } else {
                        alert('清除失败:' + response.data);
                    }
                });
            }
        }
        </script>
    </div>
    <?php
}

/**
 * 10. 添加AJAX清理缓存功能
 */
add_action( 'wp_ajax_friendlinks_clear_cache', 'friendlinks_ajax_clear_cache' );
function friendlinks_ajax_clear_cache() {
    if ( ! current_user_can( 'manage_options' ) || ! check_ajax_referer( 'friendlinks_clear_cache', false, false ) ) {
        wp_die( '权限不足' );
    }
    
    delete_transient( 'friendlinks_rss_cache' );
    update_option( 'friendlinks_last_fetch', 0 );
    
    wp_send_json_success( '缓存已清除' );
}

/**
 * 11. 添加CSS样式
 */
function friendlinks_enqueue_styles() {
    wp_register_style(
        'friendlinks-styles',
        plugin_dir_url( __FILE__ ) . 'css/friendlinks-styles.css',
        array(),
        '2.1.0'
    );
    
    // 只要小工具可能显示就加载样式
    if ( is_active_widget( false, false, 'friendlinks_widget', true ) || 
         is_active_widget( false, false, 'friendlinks_rss_widget', true ) ||
         is_admin() ) {
        wp_enqueue_style( 'friendlinks-styles' );
    }
    
    // 在管理页面加载jQuery
    if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] === 'friendlinks-settings' ) {
        wp_enqueue_script( 'jquery' );
    }
}
add_action( 'wp_enqueue_scripts', 'friendlinks_enqueue_styles' );
add_action( 'admin_enqueue_scripts', 'friendlinks_enqueue_styles' );

/**
 * 12. 创建CSS文件
 */
function friendlinks_create_css_file() {
    $css_dir = plugin_dir_path( __FILE__ ) . 'css';
    if ( ! file_exists( $css_dir ) ) {
        wp_mkdir_p( $css_dir );
    }
    
    $css_file = $css_dir . '/friendlinks-styles.css';
    $css_content = '/* 友链管理插件样式 - 版本 2.1.0 */

/* 友情链接小工具样式 */
.widget_friendlinks ul.friendlinks-list,
.widget_friendlinks_rss ul.friendlinks-rss-list {
    list-style: none;
    padding-left: 0;
    margin: 0 0 15px 0;
}

.friendlinks-list li,
.friendlinks-rss-item {
    margin-bottom: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid #f0f0f0;
    line-height: 1.5;
}

.friendlinks-list li:last-child,
.friendlinks-rss-item:last-child {
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: 0;
}

.friendlinks-list li a {
    text-decoration: none;
    color: #2c3e50;
    font-weight: 500;
    display: block;
    padding: 2px 0;
    transition: color 0.2s ease;
}

.friendlinks-list li a:hover {
    color: #3498db;
}

.friendlinks-list li .link-description {
    font-size: 12px;
    color: #7f8c8d;
    display: block;
    margin-top: 2px;
}

/* 友链最新文章样式 */
.friendlinks-rss-title {
    text-decoration: none;
    color: #2980b9;
    font-weight: 500;
    display: block;
    margin-bottom: 4px;
    transition: color 0.2s ease;
}

.friendlinks-rss-title:hover {
    color: #e74c3c;
    text-decoration: underline;
}

.friendlinks-rss-date {
    font-size: 12px;
    color: #7f8c8d;
    display: block;
    margin-top: 2px;
}

.friendlinks-rss-source {
    font-size: 11px;
    color: #95a5a6;
    background: #f8f9fa;
    padding: 2px 6px;
    border-radius: 3px;
    margin-left: 8px;
    display: inline-block;
    font-style: italic;
}

/* 无内容提示 */
.friendlinks-no-content {
    text-align: center;
    padding: 20px 10px;
    color: #95a5a6;
    font-style: italic;
    border: 1px dashed #ecf0f1;
    border-radius: 4px;
    background: #fdfdfd;
}

.friendlinks-empty {
    color: #95a5a6;
    font-style: italic;
    margin: 0;
    padding: 10px;
    text-align: center;
}

/* 抓取时间 */
.friendlinks-fetch-time {
    font-size: 11px;
    color: #bdc3c7;
    text-align: right;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed #ecf0f1;
}

/* 小工具表单中的说明文字 */
.widget_friendlinks_rss small {
    font-size: 11px;
    color: #666;
    display: block;
    margin-top: 2px;
}

/* 管理页面样式 */
.wrap .card {
    max-width: 800px;
    margin-bottom: 20px;
    padding: 20px;
    background: #fff;
    border: 1px solid #ccd0d4;
    box-shadow: 0 1px 1px rgba(0,0,0,.04);
}

/* 响应式调整 */
@media screen and (max-width: 768px) {
    .friendlinks-rss-source {
        display: block;
        margin-left: 0;
        margin-top: 4px;
    }
    
    .friendlinks-rss-date {
        font-size: 11px;
    }
}

/* 调试信息(仅管理员可见) */
.friendlinks-debug-info {
    display: none;
}';
    
    if ( ! file_exists( $css_file ) || filesize( $css_file ) < 100 ) {
        file_put_contents( $css_file, $css_content );
    }
}

// 激活时创建CSS文件
register_activation_hook( __FILE__, 'friendlinks_create_css_file' );

/**
 * 13. 清理计划任务(停用时)
 */
register_deactivation_hook( __FILE__, 'friendlinks_deactivate' );
function friendlinks_deactivate() {
    $timestamp = wp_next_scheduled( 'friendlinks_fetch_rss' );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, 'friendlinks_fetch_rss' );
    }
    
    // 删除缓存和选项
    delete_transient( 'friendlinks_rss_cache' );
    delete_option( 'friendlinks_last_fetch' );
    delete_option( 'friendlinks_fetch_count' );
    delete_option( 'friendlinks_last_error' );
}

/**
 * 14. 加载文本域(国际化)
 */
function friendlinks_load_textdomain() {
    load_plugin_textdomain( 'friendlinks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
}
add_action( 'plugins_loaded', 'friendlinks_load_textdomain' );

代码2,增加头像管理

<?php
/**
 * Plugin Name: 友链管理与RSS订阅
 * Plugin URI: https://yourwebsite.com/
 * Description: 为WordPress添加友链管理功能并自动抓取友链最新文章,支持友链头像显示
 * Version: 3.0.0
 * Author: Your Name
 * License: GPL v2 or later
 * Text Domain: friendlinks
 */

// 防止直接访问
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * 1. 启用WordPress原生链接管理功能
 */
add_filter( 'pre_option_link_manager_enabled', '__return_true' );

/**
 * 2. 创建links_meta表
 */
register_activation_hook( __FILE__, 'friendlinks_create_tables' );
function friendlinks_create_tables() {
    global $wpdb;
    
    $table_name = $wpdb->prefix . 'links_meta';
    $charset_collate = $wpdb->get_charset_collate();
    
    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
        meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
        link_id bigint(20) unsigned NOT NULL DEFAULT '0',
        meta_key varchar(255) DEFAULT NULL,
        meta_value longtext,
        PRIMARY KEY (meta_id),
        KEY link_id (link_id),
        KEY meta_key (meta_key)
    ) $charset_collate;";
    
    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );
    
    // 创建抓取记录选项
    add_option( 'friendlinks_last_fetch', 0 );
    add_option( 'friendlinks_fetch_count', 0 );
    add_option( 'friendlinks_last_error', '' );
}

/**
 * 3. 为链接添加RSS和头像字段
 */
// 在链接编辑页面添加字段
add_action( 'add_link_fields', 'friendlinks_add_custom_fields' );
add_action( 'edit_link_fields', 'friendlinks_add_custom_fields' );
function friendlinks_add_custom_fields( $link ) {
    $rss_url = isset( $link ) ? friendlinks_get_meta( $link->link_id, 'rss_url' ) : '';
    $avatar_url = isset( $link ) ? friendlinks_get_meta( $link->link_id, 'avatar_url' ) : '';
    ?>
    <div class="form-field">
        <label for="link_rss"><?php _e( 'RSS地址', 'friendlinks' ); ?></label>
        <input name="link_rss" id="link_rss" type="url" value="<?php echo esc_url( $rss_url ); ?>" style="width: 95%;" />
        <p class="description"><?php _e( '输入此友链的RSS订阅地址(如:https://example.com/feed/)', 'friendlinks' ); ?></p>
    </div>
    <div class="form-field">
        <label for="link_avatar"><?php _e( '头像地址', 'friendlinks' ); ?></label>
        <input name="link_avatar" id="link_avatar" type="url" value="<?php echo esc_url( $avatar_url ); ?>" style="width: 95%;" />
        <p class="description"><?php _e( '输入此友链的头像图片地址(建议尺寸:50x50像素)', 'friendlinks' ); ?></p>
        <?php if ( ! empty( $avatar_url ) ) : ?>
        <div style="margin-top: 5px;">
            <img src="<?php echo esc_url( $avatar_url ); ?>" alt="头像预览" style="max-width: 50px; max-height: 50px; border: 1px solid #ddd; padding: 2px; border-radius: 3px;" />
        </div>
        <?php endif; ?>
    </div>
    <?php
}

// 保存字段
add_action( 'add_link', 'friendlinks_save_custom_fields' );
add_action( 'edit_link', 'friendlinks_save_custom_fields' );
function friendlinks_save_custom_fields( $link_id ) {
    if ( isset( $_POST['link_rss'] ) ) {
        $rss_url = esc_url_raw( $_POST['link_rss'] );
        friendlinks_update_meta( $link_id, 'rss_url', $rss_url );
    }
    
    if ( isset( $_POST['link_avatar'] ) ) {
        $avatar_url = esc_url_raw( $_POST['link_avatar'] );
        friendlinks_update_meta( $link_id, 'avatar_url', $avatar_url );
    }
}

/**
 * 4. 链接元数据操作函数
 */
function friendlinks_update_meta( $link_id, $meta_key, $meta_value ) {
    global $wpdb;
    
    $link_id = absint( $link_id );
    if ( ! $link_id || ! $meta_key ) {
        return false;
    }
    
    $table_name = $wpdb->prefix . 'links_meta';
    
    // 检查记录是否存在
    $exists = $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM $table_name WHERE link_id = %d AND meta_key = %s",
        $link_id,
        $meta_key
    ) );
    
    $meta_value = maybe_serialize( $meta_value );
    
    if ( $exists ) {
        // 更新
        return $wpdb->update(
            $table_name,
            array( 'meta_value' => $meta_value ),
            array( 'link_id' => $link_id, 'meta_key' => $meta_key ),
            array( '%s' ),
            array( '%d', '%s' )
        );
    } else {
        // 插入
        return $wpdb->insert(
            $table_name,
            array(
                'link_id' => $link_id,
                'meta_key' => $meta_key,
                'meta_value' => $meta_value
            ),
            array( '%d', '%s', '%s' )
        );
    }
}

function friendlinks_get_meta( $link_id, $meta_key = '', $single = true ) {
    global $wpdb;
    
    $link_id = absint( $link_id );
    if ( ! $link_id ) {
        return $single ? '' : array();
    }
    
    $table_name = $wpdb->prefix . 'links_meta';
    
    if ( $meta_key ) {
        $meta_value = $wpdb->get_var( $wpdb->prepare(
            "SELECT meta_value FROM $table_name WHERE link_id = %d AND meta_key = %s",
            $link_id,
            $meta_key
        ) );
        
        if ( $meta_value ) {
            $meta_value = maybe_unserialize( $meta_value );
            return $single ? $meta_value : array( $meta_value );
        }
        
        return $single ? '' : array();
    }
    
    // 获取所有元数据
    $results = $wpdb->get_results( $wpdb->prepare(
        "SELECT meta_key, meta_value FROM $table_name WHERE link_id = %d",
        $link_id
    ) );
    
    $meta = array();
    foreach ( $results as $result ) {
        $meta[$result->meta_key] = maybe_unserialize( $result->meta_value );
    }
    
    return $meta;
}

function friendlinks_get_rss_url( $link_id ) {
    return friendlinks_get_meta( $link_id, 'rss_url' );
}

function friendlinks_get_avatar_url( $link_id ) {
    return friendlinks_get_meta( $link_id, 'avatar_url' );
}

/**
 * 5. RSS抓取与缓存系统(增强版)
 */
class FriendLinks_RSS_Aggregator {
    
    private static $instance = null;
    private $cache_expiration = 3600; // 1小时缓存
    
    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        add_action( 'wp_feed_options', array( $this, 'set_feed_options' ) );
        add_action( 'friendlinks_fetch_rss', array( $this, 'fetch_all_feeds' ) );
        
        // 计划任务
        if ( ! wp_next_scheduled( 'friendlinks_fetch_rss' ) ) {
            wp_schedule_event( time(), 'hourly', 'friendlinks_fetch_rss' );
        }
        
        // 初始化错误日志
        $this->log( 'FriendLinks RSS聚合系统初始化完成' );
    }
    
    // 设置Feed抓取选项
    public function set_feed_options( $feed ) {
        $feed->set_timeout( 15 ); // 15秒超时
        $feed->enable_cache( true );
        $feed->set_cache_duration( $this->cache_expiration );
        $feed->force_feed( true ); // 强制解析为feed
    }
    
    // 抓取所有友链的RSS
    public function fetch_all_feeds( $posts_per_source = 0 ) {
        global $wpdb;
        
        $this->log( '开始抓取所有友链RSS...' );
        if ( $posts_per_source > 0 ) {
            $this->log( '每源抓取文章数: ' . $posts_per_source );
        }
        
        $links = $wpdb->get_results(
            "SELECT l.link_id, l.link_name, l.link_url, l.link_image, 
                    lm_rss.meta_value as rss_url,
                    lm_avatar.meta_value as avatar_url
             FROM {$wpdb->links} l 
             LEFT JOIN {$wpdb->prefix}links_meta lm_rss ON l.link_id = lm_rss.link_id AND lm_rss.meta_key = 'rss_url'
             LEFT JOIN {$wpdb->prefix}links_meta lm_avatar ON l.link_id = lm_avatar.link_id AND lm_avatar.meta_key = 'avatar_url'
             WHERE lm_rss.meta_value != '' AND lm_rss.meta_value IS NOT NULL"
        );
        
        $this->log( '找到 ' . count( $links ) . ' 个需要抓取的友链RSS' );
        
        $all_posts = array();
        $success_count = 0;
        $error_count = 0;
        
        if ( ! empty( $links ) ) {
            foreach ( $links as $link ) {
                $this->log( '正在处理友链: ' . $link->link_name . ' (' . $link->rss_url . ')' );
                
                $feed_posts = $this->fetch_feed( $link->rss_url, $link->link_id, $link->link_name, $posts_per_source );
                
                if ( ! empty( $feed_posts ) ) {
                    // 为每篇文章添加头像信息
                    foreach ( $feed_posts as &$post ) {
                        $post['avatar'] = $this->get_avatar_for_link( $link );
                    }
                    
                    $all_posts = array_merge( $all_posts, $feed_posts );
                    $success_count++;
                    $this->log( '✓ 成功获取 ' . count( $feed_posts ) . ' 篇文章' );
                } else {
                    $error_count++;
                    $this->log( '✗ 获取失败或没有文章' );
                }
            }
            
            // 按日期排序
            usort( $all_posts, function( $a, $b ) {
                return strtotime( $b['date'] ) - strtotime( $a['date'] );
            } );
            
            $this->log( '总共获取到 ' . count( $all_posts ) . ' 篇文章,成功: ' . $success_count . ',失败: ' . $error_count );
        }
        
        // 存储到缓存
        set_transient( 'friendlinks_rss_cache', $all_posts, $this->cache_expiration );
        update_option( 'friendlinks_last_fetch', current_time( 'timestamp' ) );
        update_option( 'friendlinks_fetch_count', $success_count );
        
        if ( $error_count > 0 ) {
            update_option( 'friendlinks_last_error', date( 'Y-m-d H:i:s' ) . ' - ' . $error_count . '个RSS源获取失败' );
        }
        
        return $all_posts;
    }
    
    // 获取友链头像(优先级:自定义头像 > 链接图片 > 默认头像)
    private function get_avatar_for_link( $link ) {
        // 1. 自定义头像字段
        if ( ! empty( $link->avatar_url ) ) {
            return esc_url( $link->avatar_url );
        }
        
        // 2. WordPress链接自带的图片字段
        if ( ! empty( $link->link_image ) ) {
            return esc_url( $link->link_image );
        }
        
        // 3. 尝试从网站获取favicon
        $favicon = $this->get_favicon_url( $link->link_url );
        if ( $favicon ) {
            return $favicon;
        }
        
        // 4. 返回默认头像
        return plugins_url( 'images/default-avatar.png', __FILE__ );
    }
    
    // 获取网站favicon
    private function get_favicon_url( $site_url ) {
        $domain = parse_url( $site_url, PHP_URL_HOST );
        if ( ! $domain ) {
            return false;
        }
        
        // 常见favicon地址
        $favicon_urls = array(
            'https://' . $domain . '/favicon.ico',
            'https://' . $domain . '/favicon.png',
            'https://www.' . $domain . '/favicon.ico',
            'https://www.' . $domain . '/favicon.png',
        );
        
        // 简单的URL验证,实际使用时可能需要更复杂的检查
        foreach ( $favicon_urls as $url ) {
            if ( filter_var( $url, FILTER_VALIDATE_URL ) ) {
                return $url;
            }
        }
        
        return false;
    }
    
    // 抓取单个RSS源(支持每源文章数限制)
    private function fetch_feed( $feed_url, $link_id, $link_name, $posts_per_source = 0 ) {
        $this->log( '[详细] 开始抓取: ' . $link_name );
        $this->log( '[详细] RSS地址: ' . $feed_url );
        
        if ( ! function_exists( 'fetch_feed' ) ) {
            include_once( ABSPATH . WPINC . '/feed.php' );
            $this->log( '[详细] 已加载feed.php' );
        }
        
        // 验证URL格式
        if ( ! filter_var( $feed_url, FILTER_VALIDATE_URL ) ) {
            $this->log( '[详细] ✗ RSS地址格式无效' );
            return array();
        }
        
        $this->log( '[详细] 调用fetch_feed()...' );
        $feed = fetch_feed( $feed_url );
        
        if ( is_wp_error( $feed ) ) {
            $error_msg = $feed->get_error_message();
            $this->log( '[详细] ✗ fetch_feed错误: ' . $error_msg );
            
            // 记录具体错误类型
            if ( strpos( $error_msg, 'cURL error 28' ) !== false ) {
                $this->log( '[详细] 分析: 连接超时' );
            } elseif ( strpos( strtolower( $error_msg ), 'ssl' ) !== false ) {
                $this->log( '[详细] 分析: SSL证书问题' );
            } elseif ( strpos( $error_msg, 'Could not find' ) !== false ) {
                $this->log( '[详细] 分析: 无法找到Feed,URL可能错误' );
            }
            
            return array();
        }
        
        $this->log( '[详细] ✓ Feed对象获取成功' );
        $this->log( '[详细] Feed标题: ' . $feed->get_title() );
        
        // 检查Feed是否有效
        if ( ! $feed || ! method_exists( $feed, 'get_item_quantity' ) ) {
            $this->log( '[详细] ✗ Feed对象无效' );
            return array();
        }
        
        // 使用传入的 posts_per_source,如果没有则使用默认值
        $max_items = ( $posts_per_source > 0 ) ? $posts_per_source : 3;
        $items = $feed->get_items( 0, $max_items );
        
        $this->log( '[详细] 获取到 ' . count( $items ) . ' 篇文章 (每源限制: ' . $max_items . ')' );
        
        $posts = array();
        foreach ( $items as $index => $item ) {
            $post_title = esc_html( $item->get_title() );
            $post_link = esc_url( $item->get_permalink() );
            $post_date = $item->get_date( 'Y-m-d H:i:s' );
            
            $this->log( "[详细] 文章{$index}: {$post_title}" );
            
            $posts[] = array(
                'title'     => $post_title,
                'link'      => $post_link,
                'date'      => $post_date,
                'timestamp' => strtotime( $item->get_date( 'c' ) ),
                'source_id' => $link_id,
                'source'    => $link_name ?: $feed->get_title(),
                'avatar'    => '' // 将在外部添加
            );
        }
        
        $this->log( '[详细] ✓ 完成处理,返回 ' . count( $posts ) . ' 篇文章' );
        return $posts;
    }
    
    // 获取缓存的文章(支持源数量限制和每源文章数限制)
    public function get_cached_posts( $limit = 10, $source_limit = 0, $posts_per_source = 0 ) {
        $cached = get_transient( 'friendlinks_rss_cache' );
        
        // 如果缓存过期或为空,尝试抓取
        if ( false === $cached || empty( $cached ) ) {
            $this->log( '缓存为空或过期,尝试重新抓取' );
            $cached = $this->fetch_all_feeds( $posts_per_source ); // 传递每源文章数
        }
        
        if ( empty( $cached ) ) {
            return array();
        }
        
        // 如果有源数量限制,进行筛选
        if ( $source_limit > 0 ) {
            $cached = $this->filter_by_source_limit( $cached, $source_limit, $posts_per_source );
        }
        
        // 应用总数量限制
        $result = array_slice( $cached, 0, $limit );
        $this->log( '返回文章: 总数限制 ' . $limit . ',源限制 ' . $source_limit . ',每源限制 ' . $posts_per_source . ',实际返回 ' . count( $result ) . ' 篇' );
        
        return $result;
    }
    
    // 根据源数量限制筛选文章
    private function filter_by_source_limit( $posts, $source_limit, $posts_per_source ) {
        if ( $source_limit <= 0 ) {
            return $posts;
        }
        
        $sources = array();
        $filtered_posts = array();
        $per_source_limit = ( $posts_per_source > 0 ) ? $posts_per_source : 2;
        
        foreach ( $posts as $post ) {
            $source_id = $post['source_id'];
            
            // 初始化该源的计数器
            if ( ! isset( $sources[$source_id] ) ) {
                $sources[$source_id] = 0;
            }
            
            // 如果该源的文章数量还没达到限制,并且我们还没有超过源数量限制
            if ( $sources[$source_id] < $per_source_limit && count( $sources ) <= $source_limit ) {
                $filtered_posts[] = $post;
                $sources[$source_id]++;
            }
            
            // 如果已经达到源数量限制且每个源都达到了文章限制,可以提前结束
            if ( count( $sources ) >= $source_limit ) {
                $all_sources_full = true;
                foreach ( $sources as $count ) {
                    if ( $count < $per_source_limit ) {
                        $all_sources_full = false;
                        break;
                    }
                }
                if ( $all_sources_full ) {
                    break;
                }
            }
        }
        
        $this->log( '按源限制筛选: 限制 ' . $source_limit . ' 个源,每源 ' . $per_source_limit . ' 篇,实际 ' . count( $sources ) . ' 个源,' . count( $filtered_posts ) . ' 篇文章' );
        return $filtered_posts;
    }
    
    // 获取最后抓取时间(友好格式)
    public function get_last_fetch_time() {
        $timestamp = get_option( 'friendlinks_last_fetch', 0 );
        if ( ! $timestamp ) {
            return __( '从未抓取', 'friendlinks' );
        }
        
        $current_time = current_time( 'timestamp' );
        $diff = $current_time - $timestamp;
        
        if ( $diff < 60 ) {
            return __( '刚刚', 'friendlinks' );
        } elseif ( $diff < 3600 ) {
            $minutes = floor( $diff / 60 );
            return sprintf( __( '%d分钟前', 'friendlinks' ), $minutes );
        } elseif ( $diff < 86400 ) {
            $hours = floor( $diff / 3600 );
            return sprintf( __( '%d小时前', 'friendlinks' ), $hours );
        } else {
            $days = floor( $diff / 86400 );
            return sprintf( __( '%d天前', 'friendlinks' ), $days );
        }
    }
    
    // 日志记录函数
    private function log( $message ) {
        if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
            $timestamp = date( 'Y-m-d H:i:s' );
            error_log( "[FriendLinks RSS] {$timestamp} - {$message}" );
        }
    }
}

// 初始化RSS聚合器
add_action( 'init', function() {
    FriendLinks_RSS_Aggregator::get_instance();
} );

/**
 * 6. 友情链接小工具
 */
class FriendLinks_Widget extends WP_Widget {
    
    public function __construct() {
        parent::__construct(
            'friendlinks_widget',
            __( '友情链接', 'friendlinks' ),
            array(
                'description' => __( '显示友情链接列表', 'friendlinks' ),
                'classname' => 'widget_friendlinks'
            )
        );
    }
    
    public function widget( $args, $instance ) {
        $title = ! empty( $instance['title'] ) ? $instance['title'] : __( '友情链接', 'friendlinks' );
        $category = ! empty( $instance['category'] ) ? $instance['category'] : '';
        $orderby = ! empty( $instance['orderby'] ) ? $instance['orderby'] : 'name';
        $limit = ! empty( $instance['limit'] ) ? absint( $instance['limit'] ) : 0;
        $show_description = ! empty( $instance['show_description'] ) ? true : false;
        $show_avatars = ! empty( $instance['show_avatars'] ) ? true : false;
        $avatar_size = ! empty( $instance['avatar_size'] ) ? absint( $instance['avatar_size'] ) : 32;
        
        echo $args['before_widget'];
        
        if ( $title ) {
            echo $args['before_title'] . apply_filters( 'widget_title', $title ) . $args['after_title'];
        }
        
        $query_args = array(
            'orderby' => $orderby,
            'order'   => 'ASC',
            'title_li' => '',
            'echo' => 0
        );
        
        if ( ! empty( $category ) ) {
            if ( is_numeric( $category ) ) {
                $query_args['category'] = $category;
            } else {
                $query_args['category_name'] = $category;
            }
        }
        
        if ( $limit > 0 ) {
            $query_args['limit'] = $limit;
        }
        
        if ( $show_description ) {
            $query_args['show_description'] = true;
        }
        
        $links = wp_list_bookmarks( apply_filters( 'widget_links_args', $query_args ) );
        
        if ( $links ) {
            // 如果需要显示头像,我们需要自定义输出
            if ( $show_avatars ) {
                echo '<ul class="friendlinks-list with-avatars">';
                $links_array = wp_list_bookmarks( array_merge( $query_args, array( 'echo' => false ) ) );
                // 这里需要解析链接并添加头像,但由于wp_list_bookmarks返回的是HTML字符串,我们重写输出逻辑
                global $wpdb;
                $bookmarks = get_bookmarks( $query_args );
                
                foreach ( $bookmarks as $bookmark ) {
                    $avatar_url = friendlinks_get_avatar_url( $bookmark->link_id );
                    if ( empty( $avatar_url ) && ! empty( $bookmark->link_image ) ) {
                        $avatar_url = $bookmark->link_image;
                    }
                    
                    echo '<li class="friendlinks-item-with-avatar">';
                    if ( $avatar_url ) {
                        echo '<img src="' . esc_url( $avatar_url ) . '" alt="' . esc_attr( $bookmark->link_name ) . '" class="friendlink-avatar" style="width:' . $avatar_size . 'px;height:' . $avatar_size . 'px;" /> ';
                    }
                    echo '<a href="' . esc_url( $bookmark->link_url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $bookmark->link_name ) . '</a>';
                    if ( $show_description && ! empty( $bookmark->link_description ) ) {
                        echo '<span class="link-description">' . esc_html( $bookmark->link_description ) . '</span>';
                    }
                    echo '</li>';
                }
                echo '</ul>';
            } else {
                echo '<ul class="friendlinks-list">' . $links . '</ul>';
            }
        } else {
            echo '<p class="friendlinks-empty">' . __( '暂无友情链接', 'friendlinks' ) . '</p>';
        }
        
        echo $args['after_widget'];
    }
    
    public function form( $instance ) {
        $defaults = array(
            'title' => __( '友情链接', 'friendlinks' ),
            'category' => '',
            'orderby' => 'name',
            'limit' => 0,
            'show_description' => false,
            'show_avatars' => false,
            'avatar_size' => 32
        );
        
        $instance = wp_parse_args( (array) $instance, $defaults );
        
        ?>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( '标题:', 'friendlinks' ); ?></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( $instance['title'] ); ?>">
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>"><?php _e( '链接分类:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'category' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'category' ) ); ?>" type="text" 
                   value="<?php echo esc_attr( $instance['category'] ); ?>">
            <small><?php _e( '输入分类ID、名称或别名,留空显示所有', 'friendlinks' ); ?></small>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>"><?php _e( '排序方式:', 'friendlinks' ); ?></label>
            <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'orderby' ) ); ?>" 
                    name="<?php echo esc_attr( $this->get_field_name( 'orderby' ) ); ?>">
                <option value="name" <?php selected( $instance['orderby'], 'name' ); ?>><?php _e( '名称', 'friendlinks' ); ?></option>
                <option value="rating" <?php selected( $instance['orderby'], 'rating' ); ?>><?php _e( '评分', 'friendlinks' ); ?></option>
                <option value="id" <?php selected( $instance['orderby'], 'id' ); ?>><?php _e( 'ID', 'friendlinks' ); ?></option>
                <option value="url" <?php selected( $instance['orderby'], 'url' ); ?>><?php _e( '网址', 'friendlinks' ); ?></option>
                <option value="updated" <?php selected( $instance['orderby'], 'updated' ); ?>><?php _e( '更新时间', 'friendlinks' ); ?></option>
                <option value="rand" <?php selected( $instance['orderby'], 'rand' ); ?>><?php _e( '随机', 'friendlinks' ); ?></option>
            </select>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>"><?php _e( '显示数量:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'limit' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['limit'] ); ?>" min="0" step="1">
            <small><?php _e( '0表示不限制', 'friendlinks' ); ?></small>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_description'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_description' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_description' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_description' ) ); ?>"><?php _e( '显示描述', 'friendlinks' ); ?></label>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_avatars'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_avatars' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_avatars' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_avatars' ) ); ?>"><?php _e( '显示头像', 'friendlinks' ); ?></label>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'avatar_size' ) ); ?>"><?php _e( '头像尺寸:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'avatar_size' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'avatar_size' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['avatar_size'] ); ?>" min="16" max="100" step="1">
            <small><?php _e( '头像的宽度和高度(像素)', 'friendlinks' ); ?></small>
        </p>
        <?php
    }
    
    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = ! empty( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '';
        $instance['category'] = ! empty( $new_instance['category'] ) ? sanitize_text_field( $new_instance['category'] ) : '';
        $instance['orderby'] = ! empty( $new_instance['orderby'] ) ? sanitize_text_field( $new_instance['orderby'] ) : 'name';
        $instance['limit'] = ! empty( $new_instance['limit'] ) ? absint( $new_instance['limit'] ) : 0;
        $instance['show_description'] = ! empty( $new_instance['show_description'] ) ? 1 : 0;
        $instance['show_avatars'] = ! empty( $new_instance['show_avatars'] ) ? 1 : 0;
        $instance['avatar_size'] = ! empty( $new_instance['avatar_size'] ) ? absint( $new_instance['avatar_size'] ) : 32;
        
        return $instance;
    }
}

/**
 * 7. 友链最新文章小工具(增强配置版,支持头像显示)
 */
class FriendLinks_RSS_Widget extends WP_Widget {
    
    public function __construct() {
        parent::__construct(
            'friendlinks_rss_widget',
            __( '友链最新文章', 'friendlinks' ),
            array(
                'description' => __( '显示友情链接的最新文章,可配置显示源数量和每源文章数,支持头像显示', 'friendlinks' ),
                'classname' => 'widget_friendlinks_rss'
            )
        );
    }
    
    public function widget( $args, $instance ) {
        $title = ! empty( $instance['title'] ) ? $instance['title'] : __( '友链最新文章', 'friendlinks' );
        $limit = ! empty( $instance['limit'] ) ? absint( $instance['limit'] ) : 5;
        $source_limit = ! empty( $instance['source_limit'] ) ? absint( $instance['source_limit'] ) : 0;
        $posts_per_source = ! empty( $instance['posts_per_source'] ) ? absint( $instance['posts_per_source'] ) : 0;
        $show_date = ! empty( $instance['show_date'] ) ? true : false;
        $show_source = ! empty( $instance['show_source'] ) ? true : false;
        $show_fetch_time = ! empty( $instance['show_fetch_time'] ) ? true : false;
        $show_avatar = ! empty( $instance['show_avatar'] ) ? true : false;
        $avatar_position = ! empty( $instance['avatar_position'] ) ? $instance['avatar_position'] : 'before';
        $avatar_size = ! empty( $instance['avatar_size'] ) ? absint( $instance['avatar_size'] ) : 32;
        
        $aggregator = FriendLinks_RSS_Aggregator::get_instance();
        $posts = $aggregator->get_cached_posts( $limit, $source_limit, $posts_per_source );
        
        echo $args['before_widget'];
        
        if ( $title ) {
            echo $args['before_title'] . apply_filters( 'widget_title', $title ) . $args['after_title'];
        }
        
        if ( empty( $posts ) ) {
            echo '<div class="friendlinks-no-content">';
            echo '<p>' . __( '暂无友链动态数据', 'friendlinks' ) . '</p>';
            
            if ( $show_fetch_time ) {
                $last_fetch = $aggregator->get_last_fetch_time();
                echo '<p class="friendlinks-fetch-time">' . sprintf( __( '最后抓取尝试:%s', 'friendlinks' ), $last_fetch ) . '</p>';
            }
            echo '</div>';
        } else {
            echo '<ul class="friendlinks-rss-list' . ( $show_avatar ? ' with-avatars' : '' ) . '">';
            foreach ( $posts as $post ) {
                echo '<li class="friendlinks-rss-item">';
                
                // 显示头像(如果启用)
                if ( $show_avatar && ! empty( $post['avatar'] ) && $avatar_position === 'before' ) {
                    echo '<img src="' . esc_url( $post['avatar'] ) . '" alt="' . esc_attr( $post['source'] ) . '" class="friendlinks-rss-avatar avatar-' . $avatar_position . '" style="width:' . $avatar_size . 'px;height:' . $avatar_size . 'px;" /> ';
                }
                
                // 文章标题链接
                echo '<a href="' . esc_url( $post['link'] ) . '" target="_blank" rel="noopener noreferrer" class="friendlinks-rss-title">';
                echo esc_html( $post['title'] );
                echo '</a>';
                
                // 显示头像(如果启用且在标题后)
                if ( $show_avatar && ! empty( $post['avatar'] ) && $avatar_position === 'after' ) {
                    echo ' <img src="' . esc_url( $post['avatar'] ) . '" alt="' . esc_attr( $post['source'] ) . '" class="friendlinks-rss-avatar avatar-' . $avatar_position . '" style="width:' . $avatar_size . 'px;height:' . $avatar_size . 'px;" />';
                }
                
                // 显示日期
                if ( $show_date && ! empty( $post['date'] ) ) {
                    $date_display = date_i18n( get_option( 'date_format' ), strtotime( $post['date'] ) );
                    echo '<span class="friendlinks-rss-date">' . $date_display . '</span>';
                }
                
                // 显示来源
                if ( $show_source && ! empty( $post['source'] ) ) {
                    echo '<span class="friendlinks-rss-source">来自 ' . esc_html( $post['source'] ) . '</span>';
                }
                
                echo '</li>';
            }
            echo '</ul>';
            
            if ( $show_fetch_time ) {
                $last_fetch = $aggregator->get_last_fetch_time();
                echo '<div class="friendlinks-fetch-time">' . sprintf( __( '最后更新:%s', 'friendlinks' ), $last_fetch ) . '</div>';
            }
            
            // 显示配置摘要(调试用,可选)
            if ( current_user_can( 'manage_options' ) && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
                echo '<div class="friendlinks-debug-info" style="display:none; font-size:10px; color:#999; margin-top:5px;">';
                echo '配置: 总限' . $limit . '篇';
                if ( $source_limit > 0 ) echo ',源限' . $source_limit . '个';
                if ( $posts_per_source > 0 ) echo ',每源' . $posts_per_source . '篇';
                echo ',实际' . count( $posts ) . '篇';
                echo '</div>';
            }
        }
        
        echo $args['after_widget'];
    }
    
    public function form( $instance ) {
        $defaults = array(
            'title' => __( '友链最新文章', 'friendlinks' ),
            'limit' => 5,
            'source_limit' => 0,
            'posts_per_source' => 0,
            'show_date' => true,
            'show_source' => false,
            'show_fetch_time' => true,
            'show_avatar' => false,
            'avatar_position' => 'before',
            'avatar_size' => 32
        );
        
        $instance = wp_parse_args( (array) $instance, $defaults );
        
        ?>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( '标题:', 'friendlinks' ); ?></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( $instance['title'] ); ?>">
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>"><?php _e( '总显示数量:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'limit' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'limit' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['limit'] ); ?>" min="1" max="20" step="1">
            <small><?php _e( '最多显示多少篇文章', 'friendlinks' ); ?></small>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'source_limit' ) ); ?>"><?php _e( '显示友链数量:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'source_limit' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'source_limit' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['source_limit'] ); ?>" min="0" max="10" step="1">
            <small><?php _e( '0表示不限制,最多显示多少个友链的数据', 'friendlinks' ); ?></small>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'posts_per_source' ) ); ?>"><?php _e( '每个友链显示文章数:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'posts_per_source' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'posts_per_source' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['posts_per_source'] ); ?>" min="0" max="10" step="1">
            <small><?php _e( '0表示不限制,每个友链最多显示多少篇文章', 'friendlinks' ); ?></small>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_date'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_date' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_date' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_date' ) ); ?>"><?php _e( '显示发布日期', 'friendlinks' ); ?></label>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_source'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_source' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_source' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_source' ) ); ?>"><?php _e( '显示来源网站', 'friendlinks' ); ?></label>
        </p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_fetch_time'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_fetch_time' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_fetch_time' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_fetch_time' ) ); ?>"><?php _e( '显示最后抓取时间', 'friendlinks' ); ?></label>
        </p>
        <hr style="border: none; border-top: 1px solid #ddd; margin: 15px 0;">
        <p><strong><?php _e( '头像设置', 'friendlinks' ); ?></strong></p>
        <p>
            <input class="checkbox" type="checkbox" <?php checked( $instance['show_avatar'] ); ?> 
                   id="<?php echo esc_attr( $this->get_field_id( 'show_avatar' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'show_avatar' ) ); ?>" />
            <label for="<?php echo esc_attr( $this->get_field_id( 'show_avatar' ) ); ?>"><?php _e( '显示友链头像', 'friendlinks' ); ?></label>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'avatar_position' ) ); ?>"><?php _e( '头像位置:', 'friendlinks' ); ?></label>
            <select class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'avatar_position' ) ); ?>" 
                    name="<?php echo esc_attr( $this->get_field_name( 'avatar_position' ) ); ?>">
                <option value="before" <?php selected( $instance['avatar_position'], 'before' ); ?>><?php _e( '标题前', 'friendlinks' ); ?></option>
                <option value="after" <?php selected( $instance['avatar_position'], 'after' ); ?>><?php _e( '标题后', 'friendlinks' ); ?></option>
            </select>
        </p>
        <p>
            <label for="<?php echo esc_attr( $this->get_field_id( 'avatar_size' ) ); ?>"><?php _e( '头像尺寸:', 'friendlinks' ); ?></label>
            <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'avatar_size' ) ); ?>" 
                   name="<?php echo esc_attr( $this->get_field_name( 'avatar_size' ) ); ?>" type="number" 
                   value="<?php echo esc_attr( $instance['avatar_size'] ); ?>" min="16" max="100" step="1">
            <small><?php _e( '头像的宽度和高度(像素)', 'friendlinks' ); ?></small>
        </p>
        <?php
    }
    
    public function update( $new_instance, $old_instance ) {
        $instance = $old_instance;
        $instance['title'] = ! empty( $new_instance['title'] ) ? sanitize_text_field( $new_instance['title'] ) : '';
        $instance['limit'] = ! empty( $new_instance['limit'] ) ? absint( $new_instance['limit'] ) : 5;
        $instance['source_limit'] = ! empty( $new_instance['source_limit'] ) ? absint( $new_instance['source_limit'] ) : 0;
        $instance['posts_per_source'] = ! empty( $new_instance['posts_per_source'] ) ? absint( $new_instance['posts_per_source'] ) : 0;
        $instance['show_date'] = ! empty( $new_instance['show_date'] ) ? 1 : 0;
        $instance['show_source'] = ! empty( $new_instance['show_source'] ) ? 1 : 0;
        $instance['show_fetch_time'] = ! empty( $new_instance['show_fetch_time'] ) ? 1 : 0;
        $instance['show_avatar'] = ! empty( $new_instance['show_avatar'] ) ? 1 : 0;
        $instance['avatar_position'] = ! empty( $new_instance['avatar_position'] ) ? sanitize_text_field( $new_instance['avatar_position'] ) : 'before';
        $instance['avatar_size'] = ! empty( $new_instance['avatar_size'] ) ? absint( $new_instance['avatar_size'] ) : 32;
        
        return $instance;
    }
}

/**
 * 8. 注册小工具
 */
function friendlinks_register_widgets() {
    register_widget( 'FriendLinks_Widget' );
    register_widget( 'FriendLinks_RSS_Widget' );
}
add_action( 'widgets_init', 'friendlinks_register_widgets' );

/**
 * 9. 添加管理页面
 */
add_action( 'admin_menu', 'friendlinks_add_admin_page' );
function friendlinks_add_admin_page() {
    add_links_page(
        __( '友链RSS设置', 'friendlinks' ),
        __( 'RSS设置', 'friendlinks' ),
        'manage_options',
        'friendlinks-settings',
        'friendlinks_admin_page_content'
    );
}

function friendlinks_admin_page_content() {
    if ( ! current_user_can( 'manage_options' ) ) {
        return;
    }
    
    // 处理手动抓取请求
    if ( isset( $_POST['friendlinks_manual_fetch'] ) && check_admin_referer( 'friendlinks_manual_fetch' ) ) {
        $aggregator = FriendLinks_RSS_Aggregator::get_instance();
        $result = $aggregator->fetch_all_feeds();
        $message = __( 'RSS抓取已完成!', 'friendlinks' );
    }
    
    $aggregator = FriendLinks_RSS_Aggregator::get_instance();
    $last_fetch = $aggregator->get_last_fetch_time();
    $fetch_count = get_option( 'friendlinks_fetch_count', 0 );
    $last_error = get_option( 'friendlinks_last_error', '' );
    
    // 获取当前配置摘要
    global $wpdb;
    $total_links = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->links}" );
    $rss_links = $wpdb->get_var( 
        "SELECT COUNT(DISTINCT l.link_id) 
         FROM {$wpdb->links} l 
         LEFT JOIN {$wpdb->prefix}links_meta lm ON l.link_id = lm.link_id AND lm.meta_key = 'rss_url'
         WHERE lm.meta_value != ''"
    );
    $avatar_links = $wpdb->get_var( 
        "SELECT COUNT(DISTINCT l.link_id) 
         FROM {$wpdb->links} l 
         LEFT JOIN {$wpdb->prefix}links_meta lm ON l.link_id = lm.link_id AND lm.meta_key = 'avatar_url'
         WHERE lm.meta_value != ''"
    );
    
    ?>
    <div class="wrap">
        <h1><?php _e( '友链RSS设置', 'friendlinks' ); ?></h1>
        
        <?php if ( isset( $message ) ) : ?>
            <div class="notice notice-success is-dismissible">
                <p><?php echo $message; ?></p>
            </div>
        <?php endif; ?>
        
        <div class="card">
            <h2 class="title"><?php _e( '系统概览', 'friendlinks' ); ?></h2>
            <table class="form-table">
                <tr>
                    <th scope="row"><?php _e( '友链总数', 'friendlinks' ); ?></th>
                    <td><?php echo $total_links; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '已配置RSS的友链', 'friendlinks' ); ?></th>
                    <td><?php echo $rss_links; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '已配置头像的友链', 'friendlinks' ); ?></th>
                    <td><?php echo $avatar_links; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '最后抓取时间', 'friendlinks' ); ?></th>
                    <td><?php echo $last_fetch; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '成功抓取的RSS源', 'friendlinks' ); ?></th>
                    <td><?php echo $fetch_count; ?></td>
                </tr>
                <tr>
                    <th scope="row"><?php _e( '下次计划抓取', 'friendlinks' ); ?></th>
                    <td>
                        <?php
                        $next_scheduled = wp_next_scheduled( 'friendlinks_fetch_rss' );
                        if ( $next_scheduled ) {
                            echo date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $next_scheduled );
                        } else {
                            _e( '未计划', 'friendlinks' );
                        }
                        ?>
                    </td>
                </tr>
                <?php if ( ! empty( $last_error ) ) : ?>
                <tr>
                    <th scope="row"><?php _e( '最后错误', 'friendlinks' ); ?></th>
                    <td style="color: #dc3232;"><?php echo esc_html( $last_error ); ?></td>
                </tr>
                <?php endif; ?>
            </table>
        </div>
        
        <div class="card">
            <h2 class="title"><?php _e( '手动操作', 'friendlinks' ); ?></h2>
            <form method="post">
                <?php wp_nonce_field( 'friendlinks_manual_fetch' ); ?>
                <p><?php _e( '点击按钮立即抓取所有友链的RSS更新:', 'friendlinks' ); ?></p>
                <p>
                    <input type="submit" name="friendlinks_manual_fetch" class="button button-primary" 
                           value="<?php _e( '立即抓取RSS', 'friendlinks' ); ?>">
                </p>
            </form>
        </div>
        
        <div class="card">
            <h2 class="title"><?php _e( '新功能介绍', 'friendlinks' ); ?></h2>
            <p><strong>版本 3.0.0 新增功能:</strong></p>
            <ul>
                <li>✓ 新增友链头像管理功能</li>
                <li>✓ RSS文章列表支持显示友链头像</li>
                <li>✓ 可设置头像显示位置(标题前/标题后)</li>
                <li>✓ 可自定义头像尺寸</li>
                <li>✓ 友情链接小工具也支持头像显示</li>
                <li>✓ 头像获取优先级:自定义头像 > 链接图片 > 网站favicon > 默认头像</li>
            </ul>
            <p><?php _e( '要使用新功能,请在编辑友链时添加头像地址,并在小工具设置中启用头像显示。', 'friendlinks' ); ?></p>
        </div>
        
        <div class="card">
            <h2 class="title"><?php _e( '调试信息', 'friendlinks' ); ?></h2>
            <p><?php _e( '调试日志位置:', 'friendlinks' ); ?> <code>/wp-content/debug.log</code></p>
            <p><?php _e( '日志中包含以 [FriendLinks RSS] 开头的详细抓取和配置信息。', 'friendlinks' ); ?></p>
            <p>
                <a href="<?php echo admin_url( 'site-health.php?tab=debug' ); ?>" class="button">
                    <?php _e( '查看站点健康状态', 'friendlinks' ); ?>
                </a>
                <button type="button" class="button button-secondary" onclick="clearFriendlinksCache()">
                    <?php _e( '清除RSS缓存', 'friendlinks' ); ?>
                </button>
            </p>
        </div>
        
        <script>
        function clearFriendlinksCache() {
            if (confirm('确定要清除友链RSS缓存吗?下次访问时将重新抓取数据。')) {
                jQuery.post(ajaxurl, {
                    action: 'friendlinks_clear_cache',
                    _ajax_nonce: '<?php echo wp_create_nonce( "friendlinks_clear_cache" ); ?>'
                }, function(response) {
                    if (response.success) {
                        alert('缓存已清除!');
                        location.reload();
                    } else {
                        alert('清除失败:' + response.data);
                    }
                });
            }
        }
        </script>
    </div>
    <?php
}

/**
 * 10. 添加AJAX清理缓存功能
 */
add_action( 'wp_ajax_friendlinks_clear_cache', 'friendlinks_ajax_clear_cache' );
function friendlinks_ajax_clear_cache() {
    if ( ! current_user_can( 'manage_options' ) || ! check_ajax_referer( 'friendlinks_clear_cache', false, false ) ) {
        wp_die( '权限不足' );
    }
    
    delete_transient( 'friendlinks_rss_cache' );
    update_option( 'friendlinks_last_fetch', 0 );
    
    wp_send_json_success( '缓存已清除' );
}

/**
 * 11. 创建默认头像和CSS文件
 */
function friendlinks_create_assets() {
    // 创建CSS目录
    $css_dir = plugin_dir_path( __FILE__ ) . 'css';
    if ( ! file_exists( $css_dir ) ) {
        wp_mkdir_p( $css_dir );
    }
    
    // 创建图片目录
    $images_dir = plugin_dir_path( __FILE__ ) . 'images';
    if ( ! file_exists( $images_dir ) ) {
        wp_mkdir_p( $images_dir );
    }
    
    // 创建默认头像(如果不存在)
    $default_avatar = $images_dir . '/default-avatar.png';
    if ( ! file_exists( $default_avatar ) ) {
        // 这里可以创建一个简单的默认头像
        // 由于创建图像需要GD库,我们提供一个简单的方法:使用一个在线默认头像
        // 实际使用时,你可以准备一个本地的默认头像图片
    }
    
    // 创建CSS文件
    $css_file = $css_dir . '/friendlinks-styles.css';
    $css_content = '/* 友链管理插件样式 - 版本 3.0.0 */

/* 友情链接小工具样式 */
.widget_friendlinks ul.friendlinks-list,
.widget_friendlinks_rss ul.friendlinks-rss-list {
    list-style: none;
    padding-left: 0;
    margin: 0 0 15px 0;
}

.friendlinks-list li,
.friendlinks-rss-item {
    margin-bottom: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid #f0f0f0;
    line-height: 1.5;
    display: flex;
    align-items: center;
}

.friendlinks-list li:last-child,
.friendlinks-rss-item:last-child {
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: 0;
}

.friendlinks-list li a {
    text-decoration: none;
    color: #2c3e50;
    font-weight: 500;
    display: block;
    padding: 2px 0;
    transition: color 0.2s ease;
    flex-grow: 1;
}

.friendlinks-list li a:hover {
    color: #3498db;
}

.friendlinks-list li .link-description {
    font-size: 12px;
    color: #7f8c8d;
    display: block;
    margin-top: 2px;
}

/* 友链头像样式 */
.friendlinks-rss-avatar,
.friendlink-avatar {
    border-radius: 3px;
    border: 1px solid #eee;
    object-fit: cover;
    vertical-align: middle;
}

.friendlinks-rss-avatar.avatar-before {
    margin-right: 8px;
    float: left;
}

.friendlinks-rss-avatar.avatar-after {
    margin-left: 8px;
    float: right;
}

/* 带头像的列表项 */
.friendlinks-rss-list.with-avatars .friendlinks-rss-item {
    min-height: 40px;
    align-items: flex-start;
}

/* 友链最新文章样式 */
.friendlinks-rss-title {
    text-decoration: none;
    color: #2980b9;
    font-weight: 500;
    display: block;
    margin-bottom: 4px;
    transition: color 0.2s ease;
    flex-grow: 1;
}

.friendlinks-rss-title:hover {
    color: #e74c3c;
    text-decoration: underline;
}

.friendlinks-rss-date {
    font-size: 12px;
    color: #7f8c8d;
    display: block;
    margin-top: 2px;
    clear: both;
}

.friendlinks-rss-source {
    font-size: 11px;
    color: #95a5a6;
    background: #f8f9fa;
    padding: 2px 6px;
    border-radius: 3px;
    margin-left: 8px;
    display: inline-block;
    font-style: italic;
}

/* 无内容提示 */
.friendlinks-no-content {
    text-align: center;
    padding: 20px 10px;
    color: #95a5a6;
    font-style: italic;
    border: 1px dashed #ecf0f1;
    border-radius: 4px;
    background: #fdfdfd;
}

.friendlinks-empty {
    color: #95a5a6;
    font-style: italic;
    margin: 0;
    padding: 10px;
    text-align: center;
}

/* 抓取时间 */
.friendlinks-fetch-time {
    font-size: 11px;
    color: #bdc3c7;
    text-align: right;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed #ecf0f1;
    clear: both;
}

/* 小工具表单中的说明文字 */
.widget_friendlinks_rss small {
    font-size: 11px;
    color: #666;
    display: block;
    margin-top: 2px;
}

/* 管理页面样式 */
.wrap .card {
    max-width: 800px;
    margin-bottom: 20px;
    padding: 20px;
    background: #fff;
    border: 1px solid #ccd0d4;
    box-shadow: 0 1px 1px rgba(0,0,0,.04);
}

/* 响应式调整 */
@media screen and (max-width: 768px) {
    .friendlinks-rss-source {
        display: block;
        margin-left: 0;
        margin-top: 4px;
    }
    
    .friendlinks-rss-date {
        font-size: 11px;
    }
    
    .friendlinks-rss-avatar {
        max-width: 24px;
        max-height: 24px;
    }
}

/* 调试信息(仅管理员可见) */
.friendlinks-debug-info {
    display: none;
}

/* 友情链接小工具中的头像 */
.friendlinks-list.with-avatars li {
    padding-left: 0;
}

.friendlinks-item-with-avatar {
    display: flex;
    align-items: center;
    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid #f0f0f0;
}

.friendlinks-item-with-avatar:last-child {
    border-bottom: none;
}

.friendlinks-item-with-avatar .friendlink-avatar {
    margin-right: 10px;
    flex-shrink: 0;
}

.friendlinks-item-with-avatar a {
    flex-grow: 1;
}

.friendlinks-item-with-avatar .link-description {
    display: block;
    font-size: 12px;
    color: #666;
    margin-top: 2px;
}';
    
    if ( ! file_exists( $css_file ) || filesize( $css_file ) < 100 ) {
        file_put_contents( $css_file, $css_content );
    }
}

// 激活时创建资源文件
register_activation_hook( __FILE__, 'friendlinks_create_assets' );

/**
 * 12. 添加CSS样式
 */
function friendlinks_enqueue_styles() {
    wp_register_style(
        'friendlinks-styles',
        plugin_dir_url( __FILE__ ) . 'css/friendlinks-styles.css',
        array(),
        '3.0.0'
    );
    
    // 只要小工具可能显示就加载样式
    if ( is_active_widget( false, false, 'friendlinks_widget', true ) || 
         is_active_widget( false, false, 'friendlinks_rss_widget', true ) ||
         is_admin() ) {
        wp_enqueue_style( 'friendlinks-styles' );
    }
    
    // 在管理页面加载jQuery
    if ( is_admin() && isset( $_GET['page'] ) && $_GET['page'] === 'friendlinks-settings' ) {
        wp_enqueue_script( 'jquery' );
    }
}
add_action( 'wp_enqueue_scripts', 'friendlinks_enqueue_styles' );
add_action( 'admin_enqueue_scripts', 'friendlinks_enqueue_styles' );

/**
 * 13. 清理计划任务(停用时)
 */
register_deactivation_hook( __FILE__, 'friendlinks_deactivate' );
function friendlinks_deactivate() {
    $timestamp = wp_next_scheduled( 'friendlinks_fetch_rss' );
    if ( $timestamp ) {
        wp_unschedule_event( $timestamp, 'friendlinks_fetch_rss' );
    }
    
    // 删除缓存和选项
    delete_transient( 'friendlinks_rss_cache' );
    delete_option( 'friendlinks_last_fetch' );
    delete_option( 'friendlinks_fetch_count' );
    delete_option( 'friendlinks_last_error' );
}

/**
 * 14. 加载文本域(国际化)
 */
function friendlinks_load_textdomain() {
    load_plugin_textdomain( 'friendlinks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
}
add_action( 'plugins_loaded', 'friendlinks_load_textdomain' );

步骤2:创建CSS文件夹和文件

  1. 在 friendlinks-manager 文件夹中创建 css 子文件夹
  2. 在 css 文件夹中创建 friendlinks-styles.css 文件
  3. 代码激活时会自动创建CSS内容,你也可以手动创建
  4. 代码2需要在插件目录下创建 css 和 images 文件夹

步骤3:激活插件

  1. 登录WordPress后台
  2. 进入 插件 → 已安装的插件
  3. 找到 “友链管理与RSS订阅” 插件
  4. 点击 “启用”

步骤4:数据库表创建

插件激活时会自动创建所需的数据库表:

  • wp_links_meta (存储友链的RSS地址)
  • 在 wp_options 表中添加抓取记录

步骤5:添加友情链接

  1. 进入 链接 → 添加
  2. 填写友链信息:
    • 名称:友链网站名称
    • Web地址:友链网站URL
    • 描述:(可选)
    • RSS地址:输入友链网站的RSS地址(例如:https://example.com/feed/ 或 https://example.com/rss/
  3. 选择分类(可选)
  4. 点击 “添加链接”

如何获取友链RSS地址:

  • 大多数WordPress网站:https://域名.com/feed/
  • 其他博客平台:通常在网站底部有RSS图标
  • 可以尝试:域名.com/rss 或 域名.com/atom.xml

步骤6:配置小工具

  1. 进入 外观 → 小工具
  2. 在可用小工具中找到:
    • 友情链接:显示友链列表
    • 友链最新文章:显示友链的最新文章
  3. 配置”友情链接”小工具

Leave a Reply

Please enter your comment!
Please enter your name here