You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
504 lines
16 KiB
504 lines
16 KiB
<?php |
|
if (!defined('__TYPECHO_ROOT_DIR__')) exit; |
|
/** |
|
* 评论归档 |
|
* |
|
* @category typecho |
|
* @package Widget |
|
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) |
|
* @license GNU General Public License 2.0 |
|
* @version $Id$ |
|
*/ |
|
|
|
/** |
|
* 评论归档组件 |
|
* |
|
* @category typecho |
|
* @package Widget |
|
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org) |
|
* @license GNU General Public License 2.0 |
|
*/ |
|
class Widget_Comments_Archive extends Widget_Abstract_Comments |
|
{ |
|
/** |
|
* 当前页 |
|
* |
|
* @access private |
|
* @var integer |
|
*/ |
|
private $_currentPage; |
|
|
|
/** |
|
* 所有文章个数 |
|
* |
|
* @access private |
|
* @var integer |
|
*/ |
|
private $_total = false; |
|
|
|
/** |
|
* 子父级评论关系 |
|
* |
|
* @access private |
|
* @var array |
|
*/ |
|
private $_threadedComments = array(); |
|
|
|
/** |
|
* _singleCommentOptions |
|
* |
|
* @var mixed |
|
* @access private |
|
*/ |
|
private $_singleCommentOptions = NULL; |
|
|
|
/** |
|
* 构造函数,初始化组件 |
|
* |
|
* @access public |
|
* @param mixed $request request对象 |
|
* @param mixed $response response对象 |
|
* @param mixed $params 参数列表 |
|
* @return void |
|
*/ |
|
public function __construct($request, $response, $params = NULL) |
|
{ |
|
parent::__construct($request, $response, $params); |
|
$this->parameter->setDefault('parentId=0&commentPage=0&commentsNum=0&allowComment=1'); |
|
} |
|
|
|
/** |
|
* 评论回调函数 |
|
* |
|
* @access private |
|
* @return void |
|
*/ |
|
private function threadedCommentsCallback() |
|
{ |
|
$singleCommentOptions = $this->_singleCommentOptions; |
|
if (function_exists('threadedComments')) { |
|
return threadedComments($this, $singleCommentOptions); |
|
} |
|
|
|
$commentClass = ''; |
|
if ($this->authorId) { |
|
if ($this->authorId == $this->ownerId) { |
|
$commentClass .= ' comment-by-author'; |
|
} else { |
|
$commentClass .= ' comment-by-user'; |
|
} |
|
} |
|
?> |
|
<li itemscope itemtype="http://schema.org/UserComments" id="<?php $this->theId(); ?>" class="comment-body<?php |
|
if ($this->levels > 0) { |
|
echo ' comment-child'; |
|
$this->levelsAlt(' comment-level-odd', ' comment-level-even'); |
|
} else { |
|
echo ' comment-parent'; |
|
} |
|
$this->alt(' comment-odd', ' comment-even'); |
|
echo $commentClass; |
|
?>"> |
|
<div class="comment-author" itemprop="creator" itemscope itemtype="http://schema.org/Person"> |
|
<span itemprop="image"><?php $this->gravatar($singleCommentOptions->avatarSize, $singleCommentOptions->defaultAvatar); ?></span> |
|
<cite class="fn" itemprop="name"><?php $singleCommentOptions->beforeAuthor(); |
|
$this->author(); |
|
$singleCommentOptions->afterAuthor(); ?></cite> |
|
</div> |
|
<div class="comment-meta"> |
|
<a href="<?php $this->permalink(); ?>"><time itemprop="commentTime" datetime="<?php $this->date('c'); ?>"><?php $singleCommentOptions->beforeDate(); |
|
$this->date($singleCommentOptions->dateFormat); |
|
$singleCommentOptions->afterDate(); ?></time></a> |
|
<?php if ('waiting' == $this->status) { ?> |
|
<em class="comment-awaiting-moderation"><?php $singleCommentOptions->commentStatus(); ?></em> |
|
<?php } ?> |
|
</div> |
|
<div class="comment-content" itemprop="commentText"> |
|
<?php $this->content(); ?> |
|
</div> |
|
<div class="comment-reply"> |
|
<?php $this->reply($singleCommentOptions->replyWord); ?> |
|
</div> |
|
<?php if ($this->children) { ?> |
|
<div class="comment-children" itemprop="discusses"> |
|
<?php $this->threadedComments(); ?> |
|
</div> |
|
<?php } ?> |
|
</li> |
|
<?php |
|
} |
|
|
|
/** |
|
* 获取当前评论链接 |
|
* |
|
* @access protected |
|
* @return string |
|
*/ |
|
protected function ___permalink() |
|
{ |
|
|
|
if ($this->options->commentsPageBreak) { |
|
$pageRow = array('permalink' => $this->parentContent['pathinfo'], 'commentPage' => $this->_currentPage); |
|
return Typecho_Router::url('comment_page', |
|
$pageRow, $this->options->index) . '#' . $this->theId; |
|
} |
|
|
|
return $this->parentContent['permalink'] . '#' . $this->theId; |
|
} |
|
|
|
/** |
|
* 子评论 |
|
* |
|
* @access protected |
|
* @return array |
|
*/ |
|
protected function ___children() |
|
{ |
|
return $this->options->commentsThreaded && !$this->isTopLevel && isset($this->_threadedComments[$this->coid]) |
|
? $this->_threadedComments[$this->coid] : array(); |
|
} |
|
|
|
/** |
|
* 是否到达顶层 |
|
* |
|
* @access protected |
|
* @return boolean |
|
*/ |
|
protected function ___isTopLevel() |
|
{ |
|
return $this->levels > $this->options->commentsMaxNestingLevels - 2; |
|
} |
|
|
|
/** |
|
* 重载内容获取 |
|
* |
|
* @access protected |
|
* @return void |
|
*/ |
|
protected function ___parentContent() |
|
{ |
|
return $this->parameter->parentContent; |
|
} |
|
|
|
/** |
|
* 输出文章评论数 |
|
* |
|
* @access public |
|
* @param string $string 评论数格式化数据 |
|
* @return void |
|
*/ |
|
public function num() |
|
{ |
|
$args = func_get_args(); |
|
if (!$args) { |
|
$args[] = '%d'; |
|
} |
|
|
|
$num = intval($this->_total); |
|
|
|
echo sprintf(isset($args[$num]) ? $args[$num] : array_pop($args), $num); |
|
} |
|
|
|
/** |
|
* 执行函数 |
|
* |
|
* @access public |
|
* @return void |
|
*/ |
|
public function execute() |
|
{ |
|
if (!$this->parameter->parentId) { |
|
return; |
|
} |
|
|
|
$commentsAuthor = Typecho_Cookie::get('__typecho_remember_author'); |
|
$commentsMail = Typecho_Cookie::get('__typecho_remember_mail'); |
|
$select = $this->select()->where('table.comments.cid = ?', $this->parameter->parentId) |
|
->where('table.comments.status = ? OR (table.comments.author = ? AND table.comments.mail = ? AND table.comments.status = ?)', 'approved', $commentsAuthor, $commentsMail, 'waiting'); |
|
$threadedSelect = NULL; |
|
|
|
if ($this->options->commentsShowCommentOnly) { |
|
$select->where('table.comments.type = ?', 'comment'); |
|
} |
|
|
|
$select->order('table.comments.coid', 'ASC'); |
|
$this->db->fetchAll($select, array($this, 'push')); |
|
|
|
/** 需要输出的评论列表 */ |
|
$outputComments = array(); |
|
|
|
/** 如果开启评论回复 */ |
|
if ($this->options->commentsThreaded) { |
|
|
|
foreach ($this->stack as $coid => &$comment) { |
|
|
|
/** 取出父节点 */ |
|
$parent = $comment['parent']; |
|
|
|
/** 如果存在父节点 */ |
|
if (0 != $parent && isset($this->stack[$parent])) { |
|
|
|
/** 如果当前节点深度大于最大深度, 则将其挂接在父节点上 */ |
|
if ($comment['levels'] >= $this->options->commentsMaxNestingLevels) { |
|
$comment['levels'] = $this->stack[$parent]['levels']; |
|
$parent = $this->stack[$parent]['parent']; // 上上层节点 |
|
$comment['parent'] = $parent; |
|
} |
|
|
|
/** 计算子节点顺序 */ |
|
$comment['order'] = isset($this->_threadedComments[$parent]) |
|
? count($this->_threadedComments[$parent]) + 1 : 1; |
|
|
|
/** 如果是子节点 */ |
|
$this->_threadedComments[$parent][$coid] = $comment; |
|
} else { |
|
$outputComments[$coid] = $comment; |
|
} |
|
|
|
} |
|
|
|
$this->stack = $outputComments; |
|
} |
|
|
|
/** 评论排序 */ |
|
if ('DESC' == $this->options->commentsOrder) { |
|
$this->stack = array_reverse($this->stack, true); |
|
$this->_threadedComments = array_map('array_reverse', $this->_threadedComments); |
|
} |
|
|
|
/** 评论总数 */ |
|
$this->_total = count($this->stack); |
|
|
|
/** 对评论进行分页 */ |
|
if ($this->options->commentsPageBreak) { |
|
if ('last' == $this->options->commentsPageDisplay && !$this->parameter->commentPage) { |
|
$this->_currentPage = ceil($this->_total / $this->options->commentsPageSize); |
|
} else { |
|
$this->_currentPage = $this->parameter->commentPage ? $this->parameter->commentPage : 1; |
|
} |
|
|
|
/** 截取评论 */ |
|
$this->stack = array_slice($this->stack, |
|
($this->_currentPage - 1) * $this->options->commentsPageSize, $this->options->commentsPageSize); |
|
|
|
/** 评论置位 */ |
|
$this->row = current($this->stack); |
|
$this->length = count($this->stack); |
|
} |
|
|
|
reset($this->stack); |
|
} |
|
|
|
/** |
|
* 将每行的值压入堆栈 |
|
* |
|
* @access public |
|
* @param array $value 每行的值 |
|
* @return array |
|
*/ |
|
public function push(array $value) |
|
{ |
|
$value = $this->filter($value); |
|
|
|
/** 计算深度 */ |
|
if (0 != $value['parent'] && isset($this->stack[$value['parent']]['levels'])) { |
|
$value['levels'] = $this->stack[$value['parent']]['levels'] + 1; |
|
} else { |
|
$value['levels'] = 0; |
|
} |
|
|
|
/** 重载push函数,使用coid作为数组键值,便于索引 */ |
|
$this->stack[$value['coid']] = $value; |
|
$this->length ++; |
|
|
|
return $value; |
|
} |
|
|
|
/** |
|
* 输出分页 |
|
* |
|
* @access public |
|
* @param string $prev 上一页文字 |
|
* @param string $next 下一页文字 |
|
* @param int $splitPage 分割范围 |
|
* @param string $splitWord 分割字符 |
|
* @param string $template 展现配置信息 |
|
* @return void |
|
*/ |
|
public function pageNav($prev = '«', $next = '»', $splitPage = 3, $splitWord = '...', $template = '') |
|
{ |
|
if ($this->options->commentsPageBreak && $this->_total > $this->options->commentsPageSize) { |
|
$default = array( |
|
'wrapTag' => 'ol', |
|
'wrapClass' => 'page-navigator' |
|
); |
|
|
|
if (is_string($template)) { |
|
parse_str($template, $config); |
|
} else { |
|
$config = $template; |
|
} |
|
|
|
$template = array_merge($default, $config); |
|
|
|
$pageRow = $this->parameter->parentContent; |
|
$pageRow['permalink'] = $pageRow['pathinfo']; |
|
|
|
$query = Typecho_Router::url('comment_page', $pageRow, $this->options->index); |
|
|
|
/** 使用盒状分页 */ |
|
$nav = new Typecho_Widget_Helper_PageNavigator_Box($this->_total, |
|
$this->_currentPage, $this->options->commentsPageSize, $query); |
|
$nav->setPageHolder('commentPage'); |
|
$nav->setAnchor('comments'); |
|
|
|
echo '<' . $template['wrapTag'] . (empty($template['wrapClass']) |
|
? '' : ' class="' . $template['wrapClass'] . '"') . '>'; |
|
$nav->render($prev, $next, $splitPage, $splitWord, $template); |
|
echo '</' . $template['wrapTag'] . '>'; |
|
} |
|
} |
|
|
|
/** |
|
* 递归输出评论 |
|
* |
|
* @access protected |
|
* @return void |
|
*/ |
|
public function threadedComments() |
|
{ |
|
$children = $this->children; |
|
if ($children) { |
|
//缓存变量便于还原 |
|
$tmp = $this->row; |
|
$this->sequence ++; |
|
|
|
//在子评论之前输出 |
|
echo $this->_singleCommentOptions->before; |
|
|
|
foreach ($children as $child) { |
|
$this->row = $child; |
|
$this->threadedCommentsCallback(); |
|
$this->row = $tmp; |
|
} |
|
|
|
//在子评论之后输出 |
|
echo $this->_singleCommentOptions->after; |
|
|
|
$this->sequence --; |
|
} |
|
} |
|
|
|
/** |
|
* 列出评论 |
|
* |
|
* @access private |
|
* @param mixed $singleCommentOptions 单个评论自定义选项 |
|
* @return void |
|
*/ |
|
public function listComments($singleCommentOptions = NULL) |
|
{ |
|
//初始化一些变量 |
|
$this->_singleCommentOptions = Typecho_Config::factory($singleCommentOptions); |
|
$this->_singleCommentOptions->setDefault(array( |
|
'before' => '<ol class="comment-list">', |
|
'after' => '</ol>', |
|
'beforeAuthor' => '', |
|
'afterAuthor' => '', |
|
'beforeDate' => '', |
|
'afterDate' => '', |
|
'dateFormat' => $this->options->commentDateFormat, |
|
'replyWord' => _t('回复'), |
|
'commentStatus' => _t('您的评论正等待审核!'), |
|
'avatarSize' => 32, |
|
'defaultAvatar' => NULL |
|
)); |
|
$this->pluginHandle()->trigger($plugged)->listComments($this->_singleCommentOptions, $this); |
|
|
|
if (!$plugged) { |
|
if ($this->have()) { |
|
echo $this->_singleCommentOptions->before; |
|
|
|
while ($this->next()) { |
|
$this->threadedCommentsCallback(); |
|
} |
|
|
|
echo $this->_singleCommentOptions->after; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* 重载alt函数,以适应多级评论 |
|
* |
|
* @access public |
|
* @return void |
|
*/ |
|
public function alt() |
|
{ |
|
$args = func_get_args(); |
|
$num = func_num_args(); |
|
|
|
$sequence = $this->levels <= 0 ? $this->sequence : $this->order; |
|
|
|
$split = $sequence % $num; |
|
echo $args[(0 == $split ? $num : $split) -1]; |
|
} |
|
|
|
/** |
|
* 根据深度余数输出 |
|
* |
|
* @access public |
|
* @param string $param 需要输出的值 |
|
* @return void |
|
*/ |
|
public function levelsAlt() |
|
{ |
|
$args = func_get_args(); |
|
$num = func_num_args(); |
|
$split = $this->levels % $num; |
|
echo $args[(0 == $split ? $num : $split) -1]; |
|
} |
|
|
|
/** |
|
* 评论回复链接 |
|
* |
|
* @access public |
|
* @param string $word 回复链接文字 |
|
* @return void |
|
*/ |
|
public function reply($word = '') |
|
{ |
|
if ($this->options->commentsThreaded && !$this->isTopLevel && $this->parameter->allowComment) { |
|
$word = empty($word) ? _t('回复') : $word; |
|
$this->pluginHandle()->trigger($plugged)->reply($word, $this); |
|
|
|
if (!$plugged) { |
|
echo '<a href="' . substr($this->permalink, 0, - strlen($this->theId) - 1) . '?replyTo=' . $this->coid . |
|
'#' . $this->parameter->respondId . '" rel="nofollow" onclick="return TypechoComment.reply(\'' . |
|
$this->theId . '\', ' . $this->coid . ');">' . $word . '</a>'; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* 取消评论回复链接 |
|
* |
|
* @access public |
|
* @param string $word 取消回复链接文字 |
|
* @return void |
|
*/ |
|
public function cancelReply($word = '') |
|
{ |
|
if ($this->options->commentsThreaded) { |
|
$word = empty($word) ? _t('取消回复') : $word; |
|
$this->pluginHandle()->trigger($plugged)->cancelReply($word, $this); |
|
|
|
if (!$plugged) { |
|
$replyId = $this->request->filter('int')->replyTo; |
|
echo '<a id="cancel-comment-reply-link" href="' . $this->parameter->parentContent['permalink'] . '#' . $this->parameter->respondId . |
|
'" rel="nofollow"' . ($replyId ? '' : ' style="display:none"') . ' onclick="return TypechoComment.cancelReply();">' . $word . '</a>'; |
|
} |
|
} |
|
} |
|
}
|
|
|