getPrefix(); try { $db->query("ALTER TABLE `{$prefix}users` ADD `user_feed` VARCHAR(255) DEFAULT NULL"); } catch (Exception $e) { // 字段可能已存在 } // 添加管理菜单 Helper::addPanel(3, 'UserCard/manage-users.php', 'RSS卡片', 'RSS卡片', 'administrator'); // 前端资源 Typecho_Plugin::factory('Widget_Archive')->header = array('UserCard_Plugin', 'addHeader'); return _t('用户卡片插件已激活'); } /** * 禁用插件 */ public static function deactivate() { // 移除管理菜单 Helper::removePanel(3, 'UserCard/manage-users.php'); return _t('用户卡片插件已禁用'); } /** * 插件配置 */ public static function config(Typecho_Widget_Helper_Form $form) { // 管理员评论是否显示卡片 $show_admin_card = new Typecho_Widget_Helper_Form_Element_Radio( 'show_admin_card', array( '1' => _t('显示'), '0' => _t('不显示') ), '1', _t('管理员评论卡片'), _t('是否在管理员发表的评论上显示用户卡片(普通用户不受此设置影响)') ); $form->addInput($show_admin_card); // RSS缓存时间 $cache_time = new Typecho_Widget_Helper_Form_Element_Text( 'cache_time', NULL, '3600', _t('RSS缓存时间(秒)'), _t('RSS数据缓存时间,建议设置为3600秒(1小时)') ); $form->addInput($cache_time->addRule('required', _t('必须填写缓存时间'))->addRule('isInteger', _t('请输入整数'))); // 最多显示条数 $max_items = new Typecho_Widget_Helper_Form_Element_Text( 'max_items', NULL, '5', _t('最多显示文章数'), _t('用户卡片中最多显示的RSS文章数量') ); $form->addInput($max_items->addRule('required', _t('必须填写显示条数'))->addRule('isInteger', _t('请输入整数'))); // RSS超时时间 $timeout = new Typecho_Widget_Helper_Form_Element_Text( 'timeout', NULL, '10', _t('RSS请求超时时间(秒)'), _t('获取RSS数据时的超时时间') ); $form->addInput($timeout->addRule('required', _t('必须填写超时时间'))->addRule('isInteger', _t('请输入整数'))); // 添加卡片显示延迟 $hover_delay = new Typecho_Widget_Helper_Form_Element_Text( 'hover_delay', NULL, '200', _t('卡片显示延迟(毫秒)'), _t('鼠标悬停后显示卡片的延迟时间,防止误触发') ); $form->addInput($hover_delay->addRule('required', _t('必须填写延迟时间'))->addRule('isInteger', _t('请输入整数'))); } /** * 个人配置 - 保持为空 */ public static function personalConfig(Typecho_Widget_Helper_Form $form) { // 不在个人页面显示RSS地址字段 } /** * 添加头部资源 */ public static function addHeader() { // 只在文章页面加载 if (!Typecho_Widget::widget('Widget_Archive')->is('single')) { return; } // 获取配置 $options = Typecho_Widget::widget('Widget_Options')->plugin('UserCard'); $hover_delay = isset($options->hover_delay) ? intval($options->hover_delay) : 200; echo ''; echo ''; } /** * 生成用户卡片(供主题调用) */ public static function render($comments) { if (!$comments || $comments->authorId == 0) { // 游客 if ($comments->url) { return '' . $comments->author . ''; } else { return $comments->author; } } try { $db = Typecho_Db::get(); $user = $db->fetchRow($db->select() ->from('table.users') ->where('uid = ?', $comments->authorId)); if (!$user) { return $comments->author; } // 获取插件配置 $options = Typecho_Widget::widget('Widget_Options')->plugin('UserCard'); $show_admin_card = isset($options->show_admin_card) ? intval($options->show_admin_card) : 1; // 检查用户是否为管理员 $is_admin = ($user['group'] == 'administrator'); // 如果是管理员且配置为不显示卡片,则返回普通链接 if ($is_admin && !$show_admin_card) { if (!empty($user['url'])) { return '' . $comments->author . ''; } else { return $comments->author; } } // 评论数 $commentCount = $db->fetchObject($db->select('COUNT(*) as cnt') ->from('table.comments') ->where('authorId = ?', $user['uid']) ->where('status = ?', 'approved'))->cnt; // RSS文章 $rssItems = array(); if (!empty($user['user_feed'])) { $rssItems = self::getRssItems($user['user_feed']); } // 基本信息 $displayName = !empty($user['screenName']) ? $user['screenName'] : $user['name']; $userUrl = !empty($user['url']) ? $user['url'] : '#'; // 构建HTML $html = ''; $html .= '' . $displayName . ''; // 卡片 $html .= '
'; $html .= '
'; $html .= '

' . $displayName . '

'; $html .= '
'; $html .= '
'; $html .= '
'; // 注册时间 $html .= '

' . _t('注册时间:') . '' . date('Y-m-d', $user['created']) . '

'; // 评论数 $html .= '

' . _t('已评论数:') . '' . $commentCount . _t('条') . '

'; // 最后登录 if ($user['logged']) { $html .= '

' . _t('最后登录:') . '' . date('m-d H:i', $user['logged']) . '

'; } $html .= '
'; // RSS文章 if (!empty($rssItems) && is_array($rssItems) && count($rssItems) > 0) { // 获取配置中的显示条数 $max_display_items = isset($options->max_items) ? intval($options->max_items) : 5; // 限制显示的条数 $displayItems = array_slice($rssItems, 0, $max_display_items); $html .= '
'; $html .= '

' . _t('') . '

'; $html .= '
    '; foreach ($displayItems as $item) { $title = htmlspecialchars($item['title']); if (mb_strlen($title, 'UTF-8') > 40) { $title = mb_substr($title, 0, 40, 'UTF-8') . '...'; } $html .= '
  • '; $html .= '' . $title . ''; $html .= '' . date('Y-m-d', $item['date']) . ''; $html .= '
  • '; } $html .= '
'; $html .= '
'; } $html .= '
'; $html .= '
'; $html .= '
'; return $html; } catch (Exception $e) { // 出错时返回简单链接 if ($comments->url) { return '' . $comments->author . ''; } else { return $comments->author; } } } /** * 获取RSS文章 */ private static function getRssItems($url) { if (empty($url)) { return array(); } // 检查缓存 $cacheKey = 'usercard_rss_' . md5($url); $cache = self::getCache($cacheKey); if ($cache !== false) { return $cache; } // 获取配置 $options = Typecho_Widget::widget('Widget_Options')->plugin('UserCard'); $timeout = isset($options->timeout) ? intval($options->timeout) : 10; // 固定从RSS获取最多10条数据保存到缓存 $fetch_max_items = 10; // 获取RSS $context = stream_context_create(array( 'http' => array('timeout' => $timeout), 'ssl' => array('verify_peer' => false) )); $content = @file_get_contents($url, false, $context); if (empty($content)) { return array(); } $xml = @simplexml_load_string($content); if (!$xml) { return array(); } $items = array(); $counter = 0; // RSS格式 if (isset($xml->channel->item)) { foreach ($xml->channel->item as $item) { if ($counter >= $fetch_max_items) break; $title = trim((string)$item->title); $link = trim((string)$item->link); if ($title && $link) { $pubDate = isset($item->pubDate) ? strtotime((string)$item->pubDate) : time(); $items[] = array( 'title' => $title, 'link' => $link, 'date' => $pubDate ); $counter++; } } } // 设置缓存 $cacheTime = isset($options->cache_time) ? intval($options->cache_time) : 3600; self::setCache($cacheKey, $items, $cacheTime); return $items; } /** * 获取缓存 */ private static function getCache($key) { $cacheFile = dirname(__FILE__) . '/cache/' . $key . '.json'; if (file_exists($cacheFile)) { $data = json_decode(file_get_contents($cacheFile), true); if ($data && isset($data['expire']) && $data['expire'] > time()) { return $data['data']; } } return false; } /** * 设置缓存 */ private static function setCache($key, $data, $expire = 3600) { $cacheDir = dirname(__FILE__) . '/cache'; if (!is_dir($cacheDir)) { @mkdir($cacheDir, 0777, true); } $cacheFile = $cacheDir . '/' . $key . '.json'; $cacheData = array( 'data' => $data, 'expire' => time() + $expire ); @file_put_contents($cacheFile, json_encode($cacheData, JSON_UNESCAPED_UNICODE)); } }