1335 lines
45 KiB
PHP
1335 lines
45 KiB
PHP
<?php
|
||
/**
|
||
* 最近活跃时间、楼层(子父级独立)、用户等级
|
||
*
|
||
* @package RecentlyActive
|
||
* @author 石头厝
|
||
* @version 2.2.0
|
||
* @link https://www.shitoucuo.com/
|
||
*/
|
||
|
||
class RecentlyActive_Plugin implements Typecho_Plugin_Interface
|
||
{
|
||
/**
|
||
* 用户等级配置 - 更新为旅行主题
|
||
*/
|
||
private static $defaultLevels = array(
|
||
array('name' => '偶遇', '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 '<div class="typecho-page-title"><h2>楼层显示配置</h2></div>';
|
||
|
||
// 启用楼层显示
|
||
$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 '<div class="typecho-page-title"><h2>用户等级配置</h2></div>';
|
||
|
||
// 启用用户等级显示
|
||
$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 '<div class="message notice"><p>等级配置格式:等级名称|所需评论数|CSS类名|颜色值<br>例如:偶遇|0|vip1|#c0c0c0<br>每行一个等级,按评论数升序排列</p></div>';
|
||
|
||
// 等级配置
|
||
$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 = <<<CSS
|
||
<style>
|
||
.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;
|
||
}
|
||
</style>
|
||
CSS;
|
||
|
||
// 只有启用等级显示时才输出等级CSS
|
||
if ($enableUserLevel == '1') {
|
||
try {
|
||
$iconFontCss = isset($options->iconfont_css) ? $options->iconfont_css : self::getDefaultIconFontCss();
|
||
$css .= "<style>{$iconFontCss}</style>";
|
||
} catch (Exception $e) {
|
||
$css .= "<style>" . self::getDefaultIconFontCss() . "</style>";
|
||
}
|
||
}
|
||
|
||
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 '<span class="comment-floor parent-floor" style="margin-right: 5px; font-weight: bold; color: #666;">' . htmlspecialchars($floorText) . '</span>';
|
||
}
|
||
|
||
/**
|
||
* 生成子评论楼层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 '<span class="comment-floor sub-floor" style="margin-right: 5px; font-weight: bold; color: #888;">' . htmlspecialchars($floorText) . '</span>';
|
||
}
|
||
|
||
/**
|
||
* 获取父评论的楼层号(统计全部父评论)
|
||
*/
|
||
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 . '<!--<span class="com-level blogger" title="' . htmlspecialchars($adminLabel) . '" style="color: #f5222d !important;"><span class="iconfont vipicon"></span><sub>博主</sub></span>-->';
|
||
}
|
||
|
||
// 获取用户评论数
|
||
$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(
|
||
'<span class="com-level %s" title="%s" style="color: %s !important;">',
|
||
$current['class'],
|
||
$title,
|
||
$current['color']
|
||
);
|
||
|
||
$html .= '<span style="margin-left:-5px;" class="iconfont vipicon"></span>';
|
||
$html .= sprintf('<sub>%d</sub>', $current['index']);
|
||
$html .= '</span>';
|
||
|
||
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(
|
||
'<span class="%s recently-active-tooltip" title="最后活跃: %s">%s</span>',
|
||
$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 '<!--<span class="com-level blogger" title="' . htmlspecialchars($adminLabel) . '" style="color: #f5222d !important;"><span class="iconfont vipicon"></span><sub>博主</sub></span>-->';
|
||
}
|
||
|
||
// 获取用户评论数
|
||
$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 '<span class="comment-floor ' . $class . '" style="' . $style . '">' . htmlspecialchars($floorText) . '</span>';
|
||
|
||
} 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;
|
||
}
|
||
}
|
||
} |