'偶遇', 'min_comments' => 0, 'class' => 'vip1', 'color' => '#c0c0c0'), array('name' => '同程', 'min_comments' => 1, 'class' => 'vip2', 'color' => '#a0a0a0'), array('name' => '涉溪', 'min_comments' => 20, 'class' => 'vip3', 'color' => '#9e7a5d'), array('name' => '穿林', 'min_comments' => 60, 'class' => 'vip4', 'color' => '#b87333'), array('name' => '览峰', 'min_comments' => 150, 'class' => 'vip5', 'color' => '#cb6d1e'), array('name' => '渡川', 'min_comments' => 300, 'class' => 'vip6', 'color' => '#cc8400'), array('name' => '聆泉', 'min_comments' => 600, 'class' => 'vip7', 'color' => '#d4af37'), array('name' => '沐霞', 'min_comments' => 1200, 'class' => 'vip8', 'color' => '#ffb800'), array('name' => '共云', 'min_comments' => 2400, 'class' => 'vip9', 'color' => '#ffa500'), array('name' => '印雪', 'min_comments' => 4800, 'class' => 'vip10', 'color' => '#ff8c00'), array('name' => '望星', 'min_comments' => 9600, 'class' => 'vip11', 'color' => '#da70d6'), array('name' => '归真', 'min_comments' => 19200, 'class' => 'vip12', 'color' => '#a42be2'), ); /** * 楼层名称默认配置 */ private static $defaultFloorNames = array('沙发', '板凳', '地板'); /** * 子楼层名称默认配置 */ private static $defaultSubFloorNames = array('B1', 'B2', 'B3'); /** * 激活插件 */ public static function activate() { // 前端样式 Typecho_Plugin::factory('Widget_Archive')->header = array('RecentlyActive_Plugin', 'outputHeader'); // 在评论列表添加用户等级 Typecho_Plugin::factory('Widget_Comments_Archive')->contentEx = array('RecentlyActive_Plugin', 'addUserLevelToComment'); // 在评论列表添加楼层显示 Typecho_Plugin::factory('Widget_Comments_Archive')->contentEx = array('RecentlyActive_Plugin', 'addFloorNumberToComment'); return _t('插件已激活,使用 RecentlyActive_Plugin::show() 显示用户活跃时间'); } /** * 禁用插件 */ public static function deactivate() { return _t('插件已禁用'); } /** * 插件配置面板 */ public static function config(Typecho_Widget_Helper_Form $form) { // 显示模式 $displayMode = new Typecho_Widget_Helper_Form_Element_Radio('display_mode', array( 'relative' => '相对时间(3小时前)', 'absolute' => '绝对时间(2023-01-01 12:00)', 'smart' => '智能模式(1天内用相对时间,更早用绝对时间)' ), 'smart', _t('时间显示模式')); $form->addInput($displayMode); // 时间格式 $dateFormat = new Typecho_Widget_Helper_Form_Element_Text('date_format', NULL, 'Y-m-d H:i', _t('时间格式'), _t('绝对时间格式,如:Y-m-d H:i')); $form->addInput($dateFormat); // 在线状态阈值(分钟) $onlineThreshold = new Typecho_Widget_Helper_Form_Element_Text('online_threshold', NULL, '10', _t('在线状态阈值(分钟)'), _t('多少分钟内显示为"在线"状态')); $form->addInput($onlineThreshold->addRule('isInteger', _t('必须是整数'))); // 是否显示状态点 $showDot = new Typecho_Widget_Helper_Form_Element_Radio('show_dot', array( '1' => '显示状态点 ●', '0' => '不显示状态点' ), '1', _t('状态点显示')); $form->addInput($showDot); // 默认文字 $defaultText = new Typecho_Widget_Helper_Form_Element_Text('default_text', NULL, '从未活跃', _t('默认显示文字'), _t('当用户从未活跃时显示的文字')); $form->addInput($defaultText); // 楼层显示配置区域 echo '

楼层显示配置

'; // 启用楼层显示 $enableFloor = new Typecho_Widget_Helper_Form_Element_Radio('enable_floor', array( '1' => '启用', '0' => '禁用' ), '1', _t('启用楼层显示')); $form->addInput($enableFloor); // 前三楼名称 $floorNames = new Typecho_Widget_Helper_Form_Element_Text('floor_names', NULL, '沙发,板凳,地板', _t('父评论前三楼名称'), _t('父评论前三楼显示的名称,用英文逗号分隔,例如:沙发,板凳,地板')); $form->addInput($floorNames); // 父评论显示格式 $parentFloorFormat = new Typecho_Widget_Helper_Form_Element_Text('parent_floor_format', NULL, '#楼层', _t('父评论楼层显示格式'), _t('父评论楼层显示格式,#楼层 会被替换为实际楼层,例如:#楼层楼 会显示为 1楼')); $form->addInput($parentFloorFormat); // 新增:子评论前三楼名称 $subFloorNames = new Typecho_Widget_Helper_Form_Element_Text('sub_floor_names', NULL, 'B1,B2,B3', _t('子评论前三楼名称'), _t('子评论前三楼显示的名称,用英文逗号分隔,例如:B1,B2,B3')); $form->addInput($subFloorNames); // 新增:子评论显示格式 $subFloorFormat = new Typecho_Widget_Helper_Form_Element_Text('sub_floor_format', NULL, 'B#楼层', _t('子评论楼层显示格式'), _t('子评论楼层显示格式,#楼层 会被替换为实际楼层,例如:B#楼层 会显示为 B1')); $form->addInput($subFloorFormat); // 用户等级配置区域 echo '

用户等级配置

'; // 启用用户等级显示 $enableUserLevel = new Typecho_Widget_Helper_Form_Element_Radio('enable_user_level', array( '1' => '启用', '0' => '禁用' ), '1', _t('启用用户等级显示')); $form->addInput($enableUserLevel); // 管理员标识 $adminLabel = new Typecho_Widget_Helper_Form_Element_Text('admin_label', NULL, '博主', _t('管理员标识'), _t('管理员在评论中显示的文字')); $form->addInput($adminLabel); // 等级配置说明 echo '

等级配置格式:等级名称|所需评论数|CSS类名|颜色值
例如:偶遇|0|vip1|#c0c0c0
每行一个等级,按评论数升序排列

'; // 等级配置 $levelConfig = new Typecho_Widget_Helper_Form_Element_Textarea('level_config', NULL, self::getDefaultLevelConfig(), _t('等级配置'), _t('每行一个等级:等级名称|所需评论数|CSS类名|颜色值')); $form->addInput($levelConfig); // 图标字体CSS $iconFontCss = new Typecho_Widget_Helper_Form_Element_Textarea('iconfont_css', NULL, self::getDefaultIconFontCss(), _t('图标字体CSS'), _t('用户等级图标的CSS样式')); $form->addInput($iconFontCss); } /** * 获取默认等级配置文本 */ private static function getDefaultLevelConfig() { $lines = array(); foreach (self::$defaultLevels as $level) { $lines[] = "{$level['name']}|{$level['min_comments']}|{$level['class']}|{$level['color']}"; } return implode("\n", $lines); } /** * 获取默认图标字体CSS - 更新为提供的CSS */ private static function getDefaultIconFontCss() { $css = '.vipicon { font-family: "FontAwesome", "iconfont"; font-style: normal; font-weight: normal; speak: none; display: inline-block; text-decoration: inherit; text-align: center; font-variant: normal; text-transform: none; line-height: 1em; } .vipicon:before { content: "\e66a"; font-size: 14px; } .com-level { display: inline-block; margin-left: 3px; } .com-level sub { font-size: 11px; vertical-align: baseline; position: relative; top: -0.5em; font-weight: bold; } /* 等级颜色定义 - 同时应用到图标和数字 */ .com-level.vip1 .vipicon, .com-level.vip1 sub { color: #999; } .com-level.vip2 .vipicon, .com-level.vip2 sub { color: #8c8c8c; } .com-level.vip3 .vipicon, .com-level.vip3 sub { color: #666; } .com-level.vip4 .vipicon, .com-level.vip4 sub { color: #52c41a; } .com-level.vip5 .vipicon, .com-level.vip5 sub { color: #1890ff; } .com-level.vip6 .vipicon, .com-level.vip6 sub { color: #722ed1; } .com-level.vip7 .vipicon, .com-level.vip7 sub { color: #faad14; } .com-level.vip8 .vipicon, .com-level.vip8 sub { color: #f5222d; } .com-level.vip9 .vipicon, .com-level.vip9 sub { color: #eb2f96; } .com-level.vip10 .vipicon, .com-level.vip10 sub { color: #fa541c; } .com-level.vip11 .vipicon, .com-level.vip11 sub { color: #13c2c2; } .com-level.vip12 .vipicon, .com-level.vip12 sub { color: #000; } /* 动态CSS - 确保用户自定义的颜色通过内联样式生效 */ .com-level .vipicon, .com-level sub { color: inherit !important; } /* 博主颜色 */ .com-level.blogger .vipicon, .com-level.blogger sub { color: #f5222d !important; }'; return $css; } /** * 个人用户配置(不需要) */ public static function personalConfig(Typecho_Widget_Helper_Form $form) {} /** * 输出前端样式 */ public static function outputHeader() { try { $options = Typecho_Widget::widget('Widget_Options')->plugin('RecentlyActive'); $enableUserLevel = isset($options->enable_user_level) ? $options->enable_user_level : '1'; } catch (Exception $e) { $enableUserLevel = '1'; } // 基础CSS(总是输出,用于活跃时间显示) $css = << .recently-active { display: inline-block; font-size: 15px; color: #666; margin-left: 5px; } .recently-active.online { color: #52c41a; } .recently-active.online:before { font-size: 13px; vertical-align: middle; } .recently-active.online.no-dot:before { content: ""; } .recently-active.offline { color: #999; } .recently-active.offline:before { font-size: 10px; } .recently-active.offline.no-dot:before { content: ""; } .recently-active-tooltip { cursor: help; } CSS; // 只有启用等级显示时才输出等级CSS if ($enableUserLevel == '1') { try { $iconFontCss = isset($options->iconfont_css) ? $options->iconfont_css : self::getDefaultIconFontCss(); $css .= ""; } catch (Exception $e) { $css .= ""; } } echo $css; } /** * 添加楼层号到评论 - 通过钩子自动处理 */ public static function addFloorNumberToComment($content, $widget, $lastResult) { $content = empty($lastResult) ? $content : $lastResult; try { // 获取插件配置 $options = Typecho_Widget::widget('Widget_Options')->plugin('RecentlyActive'); $enableFloor = isset($options->enable_floor) ? $options->enable_floor : '1'; // 如果禁用楼层显示,直接返回原内容 if ($enableFloor == '0') { return $content; } // 判断是父评论还是子评论 $isParentComment = ($widget->parent == 0); if ($isParentComment) { // 父评论:统计全部父评论的楼层 $floorNumber = self::getParentCommentFloorNumber($widget->coid, $widget->cid); $floorHtml = self::generateParentFloorHtml($floorNumber, $options); return $floorHtml . $content; } else { // 子评论:使用新的计数逻辑 $subFloorNumber = self::getSubCommentFloorNumberCorrect($widget->coid, $widget->parent, $widget->cid); $floorHtml = self::generateSubFloorHtml($subFloorNumber, $options); return $floorHtml . $content; } } catch (Exception $e) { // 出错时返回原内容,确保评论不消失 return $content; } } /** * 生成父评论楼层HTML */ private static function generateParentFloorHtml($floorNumber, $options) { // 获取父评论楼层显示格式 $parentFloorFormat = isset($options->parent_floor_format) ? $options->parent_floor_format : '#楼层'; // 获取父评论楼层名称配置 $floorNames = array(); if (isset($options->floor_names) && !empty($options->floor_names)) { $floorNames = explode(',', $options->floor_names); } // 如果配置为空,使用默认名称 if (empty($floorNames)) { $floorNames = self::$defaultFloorNames; } // 根据楼层数获取显示文本 if ($floorNumber <= count($floorNames) && $floorNumber > 0) { $floorText = $floorNames[$floorNumber - 1]; } else { // 使用格式替换 $floorText = str_replace('#楼层', $floorNumber, $parentFloorFormat); } // 生成楼层HTML return '' . htmlspecialchars($floorText) . ''; } /** * 生成子评论楼层HTML */ private static function generateSubFloorHtml($subFloorNumber, $options) { // 获取子评论楼层显示格式 $subFloorFormat = isset($options->sub_floor_format) ? $options->sub_floor_format : 'B#楼层'; // 获取子评论楼层名称配置 $subFloorNames = array(); if (isset($options->sub_floor_names) && !empty($options->sub_floor_names)) { $subFloorNames = explode(',', $options->sub_floor_names); } // 如果配置为空,使用默认名称 if (empty($subFloorNames)) { $subFloorNames = self::$defaultSubFloorNames; } // 根据楼层数获取显示文本 if ($subFloorNumber <= count($subFloorNames) && $subFloorNumber > 0) { $floorText = $subFloorNames[$subFloorNumber - 1]; } else { // 使用格式替换 $floorText = str_replace('#楼层', $subFloorNumber, $subFloorFormat); } // 生成楼层HTML return '' . htmlspecialchars($floorText) . ''; } /** * 获取父评论的楼层号(统计全部父评论) */ private static function getParentCommentFloorNumber($currentCoid, $cid) { try { $db = Typecho_Db::get(); // 查询所有父评论(parent=0)且属于当前文章,按时间升序排列 $query = $db->select('coid') ->from('table.comments') ->where('cid = ?', $cid) ->where('parent = ?', 0) ->where('status = ?', 'approved') ->order('coid', Typecho_Db::SORT_ASC); $comments = $db->fetchAll($query); // 查找当前评论在数组中的位置(从1开始) foreach ($comments as $index => $comment) { if ($comment['coid'] == $currentCoid) { return $index + 1; } } // 如果没找到,返回1 return 1; } catch (Exception $e) { return 1; } } /** * 获取子评论的楼层号 - 正确版本:统计同一父评论下的所有子评论 */ private static function getSubCommentFloorNumberCorrect($currentCoid, $parentCoid, $cid) { try { $db = Typecho_Db::get(); // 首先,我们需要找到当前评论的根父评论 // 对于嵌套回复,根父评论是最顶层的父评论 $rootParentId = self::findRootParent($currentCoid, $parentCoid, $cid); // 如果找到的根父评论不是直接父评论,使用根父评论 if ($rootParentId != $parentCoid) { $parentCoid = $rootParentId; } // 现在,获取该父评论下的所有子评论(按coid排序) $allChildren = self::getAllDirectAndNestedChildren($parentCoid, $cid); // 按coid排序所有子评论 usort($allChildren, function($a, $b) { return $a['coid'] - $b['coid']; }); // 查找当前评论在所有子评论中的位置 foreach ($allChildren as $index => $comment) { if ($comment['coid'] == $currentCoid) { return $index + 1; // 返回楼层号 } } // 如果没找到,可能是查询有问题,尝试简单查询 $simpleQuery = $db->select('coid') ->from('table.comments') ->where('cid = ?', $cid) ->where('parent = ?', $parentCoid) ->where('status = ?', 'approved') ->order('coid', Typecho_Db::SORT_ASC); $directChildren = $db->fetchAll($simpleQuery); foreach ($directChildren as $index => $comment) { if ($comment['coid'] == $currentCoid) { return $index + 1; } } return 1; // 默认返回1 } catch (Exception $e) { return 1; } } /** * 查找评论的根父评论 */ private static function findRootParent($currentCoid, $parentCoid, $cid) { try { $db = Typecho_Db::get(); // 如果当前评论的父评论是0,说明它自己就是父评论 if ($parentCoid == 0) { return $currentCoid; } // 检查父评论的父评论 $currentParentId = $parentCoid; $visited = array($currentCoid); // 防止循环 for ($i = 0; $i < 20; $i++) { // 最多追踪20层 if (in_array($currentParentId, $visited)) { break; // 避免循环 } $visited[] = $currentParentId; $parentComment = $db->fetchRow($db->select('parent') ->from('table.comments') ->where('coid = ?', $currentParentId) ->where('cid = ?', $cid) ->limit(1)); if (!$parentComment || $parentComment['parent'] == 0) { // 找到了根父评论 return $currentParentId; } $currentParentId = $parentComment['parent']; } // 如果循环结束还没找到,返回直接父评论 return $parentCoid; } catch (Exception $e) { return $parentCoid; } } /** * 获取父评论下的所有子评论(包括嵌套的) */ private static function getAllDirectAndNestedChildren($parentCoid, $cid) { $db = Typecho_Db::get(); $allChildren = array(); // 递归获取所有子评论 self::collectChildrenRecursively($parentCoid, $cid, $db, $allChildren); return $allChildren; } /** * 递归收集子评论 */ private static function collectChildrenRecursively($parentCoid, $cid, $db, &$allChildren) { // 获取当前父评论的直接子评论 $children = $db->fetchAll($db->select('coid', 'parent') ->from('table.comments') ->where('cid = ?', $cid) ->where('parent = ?', $parentCoid) ->where('status = ?', 'approved') ->order('coid', Typecho_Db::SORT_ASC)); foreach ($children as $child) { $allChildren[] = $child; // 递归获取子评论的子评论 self::collectChildrenRecursively($child['coid'], $cid, $db, $allChildren); } } /** * 添加用户等级到评论 */ public static function addUserLevelToComment($content, $widget, $lastResult) { $content = empty($lastResult) ? $content : $lastResult; try { // 获取插件配置 $options = Typecho_Widget::widget('Widget_Options')->plugin('RecentlyActive'); $enableUserLevel = isset($options->enable_user_level) ? $options->enable_user_level : '1'; // 如果禁用等级显示,直接返回原内容 if ($enableUserLevel == '0') { return $content; } // 获取评论者邮箱 $email = $widget->mail; // 如果是游客评论,不显示等级 if (!$email) { return $content; } // 通过邮箱获取用户信息 $userInfo = self::getUserInfoByEmail($email); // 获取用户组信息 $isAdmin = false; if ($userInfo && isset($userInfo['group'])) { // Typecho中管理员用户组是 'administrator' $isAdmin = ($userInfo['group'] == 'administrator'); } // 如果是管理员,显示博主标识 if ($isAdmin) { $adminLabel = isset($options->admin_label) ? $options->admin_label : ''; return $content . ''; } // 获取用户评论数 $commentCount = self::getUserCommentCountByEmail($email); // 获取用户等级 $levelInfo = self::getUserLevel($commentCount, $options); // 生成等级HTML $levelHtml = self::generateLevelHtml($levelInfo, $commentCount); return $content . $levelHtml; } catch (Exception $e) { // 出错时返回原内容,确保评论不消失 return $content; } } /** * 根据邮箱获取用户信息 */ private static function getUserInfoByEmail($email) { if (!$email) return null; try { $db = Typecho_Db::get(); $user = $db->fetchRow($db->select('uid', 'name', 'mail', 'group', 'url') ->from('table.users') ->where('mail = ?', $email) ->limit(1)); return $user; } catch (Exception $e) { return null; } } /** * 根据邮箱获取用户评论数 */ private static function getUserCommentCountByEmail($email) { if (!$email) return 0; try { $db = Typecho_Db::get(); $result = $db->fetchAll($db->select(array('COUNT(cid)' => 'commentNum')) ->from('table.comments') ->where('mail = ?', $email)); return $result && isset($result[0]['commentNum']) ? intval($result[0]['commentNum']) : 0; } catch (Exception $e) { return 0; } } /** * 获取用户等级信息 */ private static function getUserLevel($commentCount, $options) { // 解析等级配置 $levels = self::parseLevelConfig($options); $currentLevel = null; $nextLevel = null; // 查找当前等级 for ($i = 0; $i < count($levels); $i++) { if ($commentCount >= $levels[$i]['min_comments']) { $currentLevel = $levels[$i]; $currentLevel['index'] = $i + 1; // 获取下一等级 if (isset($levels[$i + 1])) { $nextLevel = $levels[$i + 1]; } } else { break; } } // 如果没有找到等级,使用第一个等级 if (!$currentLevel && count($levels) > 0) { $currentLevel = $levels[0]; $currentLevel['index'] = 1; if (isset($levels[1])) { $nextLevel = $levels[1]; } } return array( 'current' => $currentLevel, 'next' => $nextLevel, 'comment_count' => $commentCount ); } /** * 解析等级配置 */ private static function parseLevelConfig($options) { $levels = array(); if (isset($options->level_config) && !empty($options->level_config)) { $lines = explode("\n", $options->level_config); foreach ($lines as $line) { $line = trim($line); if (empty($line)) continue; $parts = explode('|', $line); if (count($parts) >= 3) { $level = array( 'name' => trim($parts[0]), 'min_comments' => intval(trim($parts[1])), 'class' => trim($parts[2]) ); // 修复:正确处理颜色值,包括空值情况 if (count($parts) >= 4) { $color = trim($parts[3]); if (!empty($color)) { $level['color'] = $color; } else { // 如果颜色为空,使用默认等级对应的颜色 $level['color'] = self::getDefaultColorByClass($level['class']); } } else { // 如果没有颜色配置,使用默认等级对应的颜色 $level['color'] = self::getDefaultColorByClass($level['class']); } $levels[] = $level; } } } // 如果没有配置,使用默认配置 if (empty($levels)) { $levels = self::$defaultLevels; } // 按评论数排序 usort($levels, function($a, $b) { return $a['min_comments'] - $b['min_comments']; }); return $levels; } /** * 根据等级类名获取默认颜色 */ private static function getDefaultColorByClass($levelClass) { foreach (self::$defaultLevels as $level) { if ($level['class'] == $levelClass) { return $level['color']; } } return '#666'; // 默认颜色 } /** * 生成等级HTML - 修复:使用内联样式确保颜色生效 */ private static function generateLevelHtml($levelInfo, $commentCount) { if (!$levelInfo['current']) { return ''; } $current = $levelInfo['current']; $next = $levelInfo['next']; // 构建title提示文本 $title = htmlspecialchars($current['name']); $title .= " · {$commentCount}评"; if ($next) { $needed = $next['min_comments'] - $commentCount; if ($needed > 0) { $title .= " · 再{$needed}评升至" . htmlspecialchars($next['name']); } } // 生成HTML - 使用行内样式确保颜色生效 $html = sprintf( '', $current['class'], $title, $current['color'] ); $html .= ''; $html .= sprintf('%d', $current['index']); $html .= ''; return $html; } /** * 获取用户活跃时间 */ public static function getActiveTime($userId) { if (!$userId) return 0; try { $db = Typecho_Db::get(); $user = $db->fetchRow($db->select('activated') ->from('table.users') ->where('uid = ?', $userId)); return $user && isset($user['activated']) ? intval($user['activated']) : 0; } catch (Exception $e) { return 0; } } /** * 格式化时间显示 */ public static function formatTime($timestamp, $options = null) { // 如果未提供options,尝试获取插件配置 if (!$options) { try { $options = Typecho_Widget::widget('Widget_Options')->plugin('RecentlyActive'); } catch (Exception $e) { $options = (object)[ 'display_mode' => 'smart', 'date_format' => 'Y-m-d H:i', 'default_text' => '从未活跃' ]; } } if (!$timestamp || $timestamp == 0) { return isset($options->default_text) ? $options->default_text : '从未活跃'; } $currentTime = time(); $diff = $currentTime - $timestamp; $displayMode = isset($options->display_mode) ? $options->display_mode : 'smart'; // 相对时间模式 - 始终返回相对时间 if ($displayMode == 'relative') { return self::getRelativeTime($diff); } // 智能模式 if ($displayMode == 'smart') { if ($diff < 86400) { // 24小时内 return self::getRelativeTime($diff); } else { $format = isset($options->date_format) ? $options->date_format : 'Y-m-d H:i'; return date($format, $timestamp); } } // 绝对时间模式 $format = isset($options->date_format) ? $options->date_format : 'Y-m-d H:i'; return date($format, $timestamp); } /** * 获取相对时间文本 */ private static function getRelativeTime($diff) { if ($diff < 60) { return '刚刚'; } elseif ($diff < 3600) { $minutes = floor($diff / 60); return $minutes . '分钟前'; } elseif ($diff < 86400) { $hours = floor($diff / 3600); return $hours . '小时前'; } elseif ($diff < 2592000) { // 30天 $days = floor($diff / 86400); return $days . '天前'; } elseif ($diff < 31536000) { // 365天 $months = floor($diff / 2592000); return $months . '个月前'; } else { $years = floor($diff / 31536000); return $years . '年前'; } } /** * 判断是否在线 */ private static function isOnline($timestamp, $threshold) { if (!$timestamp) return false; $currentTime = time(); $diff = $currentTime - $timestamp; $thresholdSeconds = $threshold * 60; // 转换为秒 return $diff < $thresholdSeconds; } /** * 前端显示函数 - 核心方法 */ public static function show($userId = null, $customOptions = array()) { if (!$userId) { $user = Typecho_Widget::widget('Widget_User'); $userId = $user->uid; } if (!$userId) { return ''; } $activeTime = self::getActiveTime($userId); // 获取插件配置 $options = null; try { $optionsObj = Typecho_Widget::widget('Widget_Options'); if ($optionsObj) { $options = $optionsObj->plugin('RecentlyActive'); } } catch (Exception $e) { // 忽略异常,使用默认配置 } if (!$options) { $options = (object)[ 'display_mode' => 'smart', 'date_format' => 'Y-m-d H:i', 'default_text' => '从未活跃', 'online_threshold' => '10', 'show_dot' => '1' ]; } // 合并自定义选项 if (!empty($customOptions)) { foreach ($customOptions as $key => $value) { $options->$key = $value; } } $formattedTime = self::formatTime($activeTime, $options); // 构建CSS类 $classes = array('recently-active'); $onlineThreshold = isset($options->online_threshold) ? intval($options->online_threshold) : 10; if (self::isOnline($activeTime, $onlineThreshold)) { $classes[] = 'online'; } else { $classes[] = 'offline'; } // 是否显示状态点 $showDot = isset($options->show_dot) ? $options->show_dot : '1'; if ($showDot == '0') { $classes[] = 'no-dot'; } $classStr = implode(' ', $classes); // 完整时间用于title提示 $fullTime = $activeTime ? date('Y-m-d H:i:s', $activeTime) : '从未活跃'; return sprintf( '%s', $classStr, htmlspecialchars($fullTime), htmlspecialchars($formattedTime) ); } /** * 获取最近活跃用户列表(侧边栏小工具) */ public static function getActiveUsers($limit = 5) { try { $db = Typecho_Db::get(); $users = $db->fetchAll($db->select('uid', 'name', 'mail', 'url', 'activated') ->from('table.users') ->where('activated > 0') ->order('activated', Typecho_Db::SORT_DESC) ->limit($limit)); $result = array(); foreach ($users as $user) { $result[] = array( 'uid' => $user['uid'], 'name' => $user['name'], 'mail' => $user['mail'], 'url' => $user['url'], 'activated' => $user['activated'], 'avatar' => self::getGravatar($user['mail']), 'timeText' => self::formatTime($user['activated']) ); } return $result; } catch (Exception $e) { return array(); } } /** * 获取Gravatar头像 */ private static function getGravatar($email, $size = 40) { $hash = md5(strtolower(trim($email))); return "https://www.gravatar.com/avatar/{$hash}?s={$size}&d=identicon&r=g"; } /** * 获取用户的等级信息(公共方法,可在主题中使用) */ public static function getUserLevelInfo($userId = null) { if (!$userId) { $user = Typecho_Widget::widget('Widget_User'); $userId = $user->uid; } if (!$userId) { return null; } try { $options = Typecho_Widget::widget('Widget_Options')->plugin('RecentlyActive'); $commentCount = self::getUserCommentCount($userId); return self::getUserLevel($commentCount, $options); } catch (Exception $e) { return null; } } /** * 显示用户等级(公共方法,可在主题中使用) */ public static function showUserLevel($userId = null) { if (!$userId) { return ''; } try { // 获取插件配置 $options = Typecho_Widget::widget('Widget_Options')->plugin('RecentlyActive'); $enableUserLevel = isset($options->enable_user_level) ? $options->enable_user_level : '1'; // 如果禁用等级显示,直接返回空 if ($enableUserLevel == '0') { return ''; } // 获取用户信息 $userInfo = self::getUserInfoById($userId); // 判断是否是管理员 $isAdmin = false; if ($userInfo && isset($userInfo['group'])) { $isAdmin = ($userInfo['group'] == 'administrator'); } // 如果是管理员,显示博主标识 if ($isAdmin) { $adminLabel = isset($options->admin_label) ? $options->admin_label : ''; return ''; } // 获取用户评论数 $commentCount = self::getUserCommentCount($userId); // 获取用户等级 $levelInfo = self::getUserLevel($commentCount, $options); // 生成等级HTML return self::generateLevelHtml($levelInfo, $commentCount); } catch (Exception $e) { // 出错时返回空,不影响页面显示 return ''; } } /** * 根据用户ID获取用户信息 */ private static function getUserInfoById($userId) { if (!$userId) return null; try { $db = Typecho_Db::get(); $user = $db->fetchRow($db->select('uid', 'name', 'mail', 'group', 'url') ->from('table.users') ->where('uid = ?', $userId) ->limit(1)); return $user; } catch (Exception $e) { return null; } } /** * 获取用户评论数(通过用户ID) */ private static function getUserCommentCount($userId) { if (!$userId) return 0; try { $db = Typecho_Db::get(); $result = $db->fetchRow($db->select('COUNT(*) as cnt') ->from('table.comments') ->where('authorId = ?', $userId) ->where('status = ?', 'approved')); return $result ? intval($result['cnt']) : 0; } catch (Exception $e) { return 0; } } /** * 前端调用:显示楼层号(静态方法,可在主题中调用)- 修复版本 */ public static function showFloorNumber($coid = null, $cid = null, $customOptions = array()) { if (!$coid || !$cid) { return ''; } try { // 获取插件配置 $options = null; try { $optionsObj = Typecho_Widget::widget('Widget_Options'); if ($optionsObj) { $options = $optionsObj->plugin('RecentlyActive'); } } catch (Exception $e) { // 忽略异常,使用默认配置 } if (!$options) { $options = (object)[ 'enable_floor' => '1', 'floor_names' => '沙发,板凳,地板', 'parent_floor_format' => '#楼层', 'sub_floor_names' => 'B1,B2,B3', 'sub_floor_format' => 'B#楼层' ]; } // 合并自定义选项 if (!empty($customOptions)) { foreach ($customOptions as $key => $value) { $options->$key = $value; } } $enableFloor = isset($options->enable_floor) ? $options->enable_floor : '1'; // 如果禁用楼层显示,直接返回空 if ($enableFloor == '0') { return ''; } // 判断评论类型(需要查询数据库获取评论信息) $commentInfo = self::getCommentInfo($coid); if (!$commentInfo) { return ''; } $isParentComment = ($commentInfo['parent'] == 0); if ($isParentComment) { // 父评论楼层 $floorNumber = self::getParentCommentFloorNumber($coid, $cid); $floorText = self::getParentFloorText($floorNumber, $options); $class = 'parent-floor'; $style = 'margin-right: 5px; font-weight: bold; color: #666;'; } else { // 子评论楼层 - 使用新的正确计数方法 $parentId = $commentInfo['parent']; $subFloorNumber = self::getSubCommentFloorNumberCorrect($coid, $parentId, $cid); $floorText = self::getSubFloorText($subFloorNumber, $options); $class = 'sub-floor'; $style = 'margin-right: 5px; font-weight: bold; color: #888;'; } // 生成楼层HTML return '' . htmlspecialchars($floorText) . ''; } catch (Exception $e) { // 出错时返回空 return ''; } } /** * 获取父评论楼层文本 */ private static function getParentFloorText($floorNumber, $options) { // 获取父评论楼层名称配置 $floorNames = array(); if (isset($options->floor_names) && !empty($options->floor_names)) { $floorNames = explode(',', $options->floor_names); } // 如果配置为空,使用默认名称 if (empty($floorNames)) { $floorNames = self::$defaultFloorNames; } // 根据楼层数获取显示文本 if ($floorNumber <= count($floorNames) && $floorNumber > 0) { return $floorNames[$floorNumber - 1]; } else { // 使用格式替换 $parentFloorFormat = isset($options->parent_floor_format) ? $options->parent_floor_format : '#楼层'; return str_replace('#楼层', $floorNumber, $parentFloorFormat); } } /** * 获取子评论楼层文本 */ private static function getSubFloorText($subFloorNumber, $options) { // 获取子评论楼层名称配置 $subFloorNames = array(); if (isset($options->sub_floor_names) && !empty($options->sub_floor_names)) { $subFloorNames = explode(',', $options->sub_floor_names); } // 如果配置为空,使用默认名称 if (empty($subFloorNames)) { $subFloorNames = self::$defaultSubFloorNames; } // 根据楼层数获取显示文本 if ($subFloorNumber <= count($subFloorNames) && $subFloorNumber > 0) { return $subFloorNames[$subFloorNumber - 1]; } else { // 使用格式替换 $subFloorFormat = isset($options->sub_floor_format) ? $options->sub_floor_format : 'B#楼层'; return str_replace('#楼层', $subFloorNumber, $subFloorFormat); } } /** * 获取评论信息 */ private static function getCommentInfo($coid) { try { $db = Typecho_Db::get(); $comment = $db->fetchRow($db->select('coid', 'parent', 'cid') ->from('table.comments') ->where('coid = ?', $coid) ->limit(1)); return $comment; } catch (Exception $e) { return null; } } }