'偶遇', '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 '';
}
/**
* 生成子评论楼层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 '';
}
/**
* 获取父评论的楼层号(统计全部父评论)
*/
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 '';
} 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;
}
}
}