456 lines
14 KiB
PHP
456 lines
14 KiB
PHP
<?php
|
|
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
|
/**
|
|
* 为博客添加Gravatar头像墙功能, 支持镜像加速与缓存
|
|
*
|
|
* @package Avatars
|
|
* @author 羽中
|
|
* @version 1.2.5
|
|
* @dependence 14.5.26-*
|
|
* @link http://www.yzmb.me/archives/net/avatars-for-typecho
|
|
*/
|
|
class Avatars_Plugin implements Typecho_Plugin_Interface
|
|
{
|
|
/**
|
|
* 默认美化提示样式
|
|
*
|
|
* @access private
|
|
* @var string
|
|
*/
|
|
private static $decss = '#tooltip {position:absolute;z-index:20;max-width:200px;background-color:#101010;text-align:left;padding:0 8px;border-radius:6px;-moz-border-radius:6px;-webkit-border-radius:6px;}'.PHP_EOL.
|
|
'#tooltip p {color:#FFF;font:12px "Microsoft YaHei",Tahoma,Arial,Sans-Serif;}'.PHP_EOL.
|
|
'#tooltip p em {display:block;margin-top:1px;color:#70B9ED;font-style:normal;}';
|
|
|
|
/**
|
|
* 激活插件方法,如果激活失败,直接抛出异常
|
|
*
|
|
* @access public
|
|
* @return void
|
|
* @throws Typecho_Plugin_Exception
|
|
*/
|
|
public static function activate()
|
|
{
|
|
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('Avatars_Plugin','walls');
|
|
Typecho_Plugin::factory('Widget_Abstract_Comments')->gravatar = array('Avatars_Plugin','avatars');
|
|
|
|
Typecho_Plugin::factory('Widget_Archive')->header = array('Avatars_Plugin','avcss');
|
|
Typecho_Plugin::factory('Widget_Archive')->footer = array('Avatars_Plugin','avjs');
|
|
|
|
//模版调用钩子
|
|
Typecho_Plugin::factory('Widget_Archive')->callAvatars = array('Avatars_Plugin', 'output');
|
|
}
|
|
|
|
/**
|
|
* 禁用插件方法,如果禁用失败,直接抛出异常
|
|
*
|
|
* @static
|
|
* @access public
|
|
* @return void
|
|
* @throws Typecho_Plugin_Exception
|
|
*/
|
|
public static function deactivate(){}
|
|
|
|
/**
|
|
* 获取插件配置面板
|
|
*
|
|
* @access public
|
|
* @param Typecho_Widget_Helper_Form $form 配置面板
|
|
* @return void
|
|
*/
|
|
public static function config(Typecho_Widget_Helper_Form $form)
|
|
{
|
|
echo
|
|
'<div style="color:#999;font-size:.92857em;word-break:break-all;"><p>'
|
|
._t('在主题模版文件中的适当位置插入代码%s或编辑页<br/>面写入%s即可显示读者墙(其中黑色字参数为标签与class名称, 可自定义)','<strong style="color:#467B96;"><?php $this->avatars("<span style="color:#444;">li</span>","<span style="color:#444;">mostactive</span>"); ?></strong>','<strong style="color:#467B96;">[AVATARS|<span style="color:#444;">li</span>|<span style="color:#444;">mostactive</span>]</strong>').
|
|
'</p></div>
|
|
<script src="'.Helper::options()->adminUrl.'js/jquery.js"></script>
|
|
<script>
|
|
$(function(){
|
|
var ts = $("#tipstyle-0-7"),
|
|
tt = $("#tooltip-1"),
|
|
it = $("#typecho-option-item-tipstyle-6");
|
|
if (!ts.val()) {
|
|
ts.val(\''.str_replace(PHP_EOL,'\r',self::$decss).'\');
|
|
}
|
|
if (tt.is(":checked")) it.attr("style","display:block");
|
|
tt.click(function(){
|
|
it.toggle(100);
|
|
});
|
|
});
|
|
</script>';
|
|
|
|
$wsize = new Typecho_Widget_Helper_Form_Element_Text('wsize',
|
|
NULL,'32',_t('读者墙头像大小'),_t('读者墙上的头像尺寸(单位px不用填写)'));
|
|
$wsize->input->setAttribute('class','w-10');
|
|
$wsize->addRule('required',_t('头像尺寸不能为空'));
|
|
$form->addInput($wsize->addRule('isInteger',_t('请填写整数数字')));
|
|
|
|
$wdefault = new Typecho_Widget_Helper_Form_Element_Text('wdefault',
|
|
NULL,'',_t('读者墙缺省头像'),_t('支持gravatar随机头像后缀(identicon/monsterid/wavatar/retro等), 自定义图片url注意尺寸应一致'));
|
|
$wdefault->input->setAttribute('class','w-60');
|
|
$form->addInput($wdefault);
|
|
|
|
$listnumber = new Typecho_Widget_Helper_Form_Element_Text('listnumber',
|
|
NULL,'10',_t('读者墙头像数目'),_t('读者墙上最多展示的评论者头像个数'));
|
|
$listnumber->input->setAttribute('class','w-10');
|
|
$listnumber->addRule('required',_t('头像个数不能为空'));
|
|
$form->addInput($listnumber->addRule('isInteger',_t('请填写整数数字')));
|
|
|
|
$since = new Typecho_Widget_Helper_Form_Element_Text('since',
|
|
NULL,'30',_t('读者墙收录时间'),_t('读者墙将展示该天数以内的评论排行'));
|
|
$since->input->setAttribute('class','w-10');
|
|
$since->addRule('required',_t('收录时间不能为空'));
|
|
$form->addInput($since->addRule('isInteger',_t('请填写整数数字')));
|
|
|
|
$altword = new Typecho_Widget_Helper_Form_Element_Text('altword',
|
|
NULL,'条评论',_t('读者墙提示文字'),_t('个性化读者墙头像的评论数提示文字'));
|
|
$altword->input->setAttribute('style','width:150px;');
|
|
$form->addInput($altword);
|
|
|
|
$tooltip = new Typecho_Widget_Helper_Form_Element_Checkbox('tooltip',
|
|
array(1=>_t('美化效果')),NULL,'');
|
|
$tooltip->label->setAttribute('style','position:absolute;color:#999;font-weight:normal;bottom:37px;left:190px;');
|
|
$tooltip->input->setAttribute('style','position:absolute;bottom:5px;right:74px;');
|
|
$tooltip->setAttribute('style','position:relative;');
|
|
$form->addInput($tooltip);
|
|
|
|
$tipstyle = new Typecho_Widget_Helper_Form_Element_Textarea('tipstyle',NULL,self::$decss,_t('自定义美化样式'),_t('可定制<strong style="color:#467B96;">#tooltip</strong>(浮层背景), <strong style="color:#467B96;">#tooltip p</strong>(提示文字), <strong style="color:#467B96;">#tooltip p em</strong>(读者链接)样式, 留空即恢复默认'));
|
|
$tipstyle->input->setAttribute('style','word-break:break-all;max-width:600px;height:100px;');
|
|
$tipstyle->setAttribute('style','display:none;');
|
|
$form->addInput($tipstyle);
|
|
|
|
$proxy = new Typecho_Widget_Helper_Form_Element_Radio('proxy',
|
|
array(''=>_t('否'),'https://cdn.v2ex.com/gravatar/'=>_t('v2ex镜像'),'geekzu'=>_t('极客族镜像')),'',_t('用代理加速头像'),_t('国内直连gravatar服务器不流畅时可选'));
|
|
$form->addInput($proxy);
|
|
|
|
$cache = new Typecho_Widget_Helper_Form_Element_Checkbox('cache',
|
|
array(1=>_t('是')),NULL,_t('在本地缓存头像'),_t('将头像下载到插件的cache目录中调用'));
|
|
$form->addInput($cache);
|
|
|
|
$comment = new Typecho_Widget_Helper_Form_Element_Checkbox('comment',
|
|
array(1=>_t('是')),NULL,_t('作用评论区头像'),_t('评论区头像也用上两项设置(加速/缓存)'));
|
|
$form->addInput($comment);
|
|
|
|
$security = Helper::security();
|
|
if (Typecho_Request::getInstance()->is('action=deletefiles')) {
|
|
$security->protect();
|
|
self::deletefiles();
|
|
}
|
|
//清空动作按钮
|
|
$deletefiles = new Typecho_Widget_Helper_Form_Element_Submit();
|
|
$deletefiles->value(_t('清空缓存'));
|
|
$deletefiles->setAttribute('style','position:relative');
|
|
$deletefiles->input->setAttribute('style','position:absolute;bottom:127.5px;left:110px;padding-bottom:1px;');
|
|
$deletefiles->input->setAttribute('class','btn btn-xs btn-warn');
|
|
$deletefiles->input->setAttribute('formaction',$security->getAdminUrl('options-plugin.php?config=Avatars&action=deletefiles'));
|
|
$form->addItem($deletefiles);
|
|
}
|
|
|
|
/**
|
|
* 个人用户的配置面板
|
|
*
|
|
* @access public
|
|
* @param Typecho_Widget_Helper_Form $form
|
|
* @return void
|
|
*/
|
|
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
|
|
|
|
/**
|
|
* 输出头部样式
|
|
*
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public static function avcss()
|
|
{
|
|
$settings = Helper::options()->plugin('Avatars');
|
|
$tipstyle = $settings->tipstyle;
|
|
$css = '';
|
|
|
|
if ($settings->tooltip) {
|
|
$css = '
|
|
<style type="text/css">
|
|
'.($tipstyle ? $tipstyle : self::$decss).'
|
|
</style>
|
|
';
|
|
}
|
|
echo $css;
|
|
}
|
|
|
|
/**
|
|
* 输出底部脚本
|
|
*
|
|
* @access public
|
|
* @return void
|
|
*/
|
|
public static function avjs()
|
|
{
|
|
$settings = Helper::options()->plugin('Avatars');
|
|
$js = '';
|
|
|
|
if ($settings->tooltip) {
|
|
$js = '
|
|
<script type="text/javascript">
|
|
window.jQuery || document.write(\'<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"><\/script>\')</script>
|
|
<script type="text/javascript">
|
|
$(function(){
|
|
var st = function(){
|
|
$(\'.avatars a\').mouseover(function(e){
|
|
this.myTitle = this.title;
|
|
this.myHref = this.href;
|
|
this.myHref = this.myHref.length > 61 ? this.myHref.toString().substring(0,50) + \'...\': this.myHref;
|
|
this.title = \'\';
|
|
var t = \'<div id="tooltip"><p>\' + this.myTitle + \'<em>\' + decodeURI(this.myHref) + \'</em>\' + \'</p></div>\';
|
|
$(\'body\').append(t);
|
|
$(\'#tooltip\').css({
|
|
opacity: \'0.8\',
|
|
top: e.pageY + 10 + \'px\',
|
|
left: e.pageX + 5 + \'px\'
|
|
}).show(\'fast\')
|
|
}).mouseout(function(){
|
|
this.title = this.myTitle;
|
|
$(\'#tooltip\').remove()
|
|
}).mousemove(function(e){
|
|
$(\'#tooltip\').css({
|
|
top: e.pageY + 10 + \'px\',
|
|
left: e.pageX + 5 + \'px\'
|
|
})
|
|
})
|
|
};
|
|
st();
|
|
});
|
|
</script>
|
|
';
|
|
}
|
|
echo $js;
|
|
}
|
|
|
|
/**
|
|
* 读者墙标签解析
|
|
*
|
|
* @access public
|
|
* @param string $content
|
|
* @return string
|
|
*/
|
|
public static function walls($content,$widget,$lastResult)
|
|
{
|
|
$content = empty($lastResult) ? $content : $lastResult;
|
|
|
|
if ($widget->is('page') && false!==stripos($content,'[AVATARS')) {
|
|
$content = preg_replace_callback('/\[AVATARS(\w*[^>]*)\]/i',array('Avatars_Plugin','callback'),$content);
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* 标签参数回调
|
|
*
|
|
* @access public
|
|
* @param array $match
|
|
* @return string
|
|
*/
|
|
public static function callback($match)
|
|
{
|
|
$listtag = 'li';
|
|
$class = 'mostactive';
|
|
|
|
if ($match['1']) {
|
|
if (preg_match('/\|([\w-]*)\|([\w-]*)/i',$match['1'],$out)) {
|
|
$listtag = trim($out['1']);
|
|
$class = trim($out['2']);
|
|
}
|
|
}
|
|
|
|
return self::output(Typecho_Widget::widget('Widget_Archive'),array($listtag,$class,true));
|
|
}
|
|
|
|
/**
|
|
* 输出读者墙实例
|
|
*
|
|
* @access public
|
|
* @param array $params 实例参数
|
|
* @return string
|
|
*/
|
|
public static function output($widget,array $params)
|
|
{
|
|
$options = Helper::options();
|
|
$settings = $options->plugin('Avatars');
|
|
$tipstyle = $settings->tipstyle;
|
|
|
|
//处理实例参数
|
|
$listtag = 'li';
|
|
$listtag = isset($params['0']) && is_string($params['0']) ? $params['0'] : $listtag;
|
|
$classname = 'mostactive';
|
|
$classname = isset($params['1']) && is_string($params['1']) ? $params['1'] : $classname;
|
|
$iscall = false;
|
|
$iscall = !empty($params['2']) && is_bool($params['2']) ? $params['2'] : $iscall;
|
|
|
|
//获取评论计数
|
|
$db = Typecho_Db::get();
|
|
$select = $db->select(array('COUNT(author)'=>'cnt'),'author','url','mail')->from('table.comments')
|
|
->where('status = ?','approved')
|
|
->where('authorId = ?','0') //排除博主
|
|
->where('type = ?','comment')
|
|
->where('created > ?',$options->gmtTime + $options->timezone - $settings->since*24*3600) //收录时间
|
|
->limit($settings->listnumber)
|
|
->group('author')
|
|
->order('cnt',Typecho_Db::SORT_DESC); //降序排列
|
|
$counts = $db->fetchAll($select);
|
|
|
|
$wurl = '';
|
|
$mostactive = '<div class="avatars">
|
|
';
|
|
foreach ($counts as $count) {
|
|
//获取优化地址
|
|
$wurl = self::avurl($count['mail'],$settings->wsize,$options->commentsAvatarRating //同步评级
|
|
,$settings->wdefault,Typecho_Widget::widget('Widget_Archive')->request->isSecure() //安全请求
|
|
);
|
|
|
|
$mostactive .= '
|
|
<'.$listtag;
|
|
$mostactive .= ' class="'.$classname.'"';
|
|
$mostactive .= '><a href="';
|
|
//静默空白链接
|
|
$mostactive .= $count['url'] ? $count['url'] : '#';
|
|
$mostactive .= '"';
|
|
//同步nofollow设置
|
|
$mostactive .= $options->commentsUrlNofollow ? ' rel="external nofollow"' : '';
|
|
$mostactive .= ' title="'.$count['author'].' - '.$count['cnt'].$settings->altword.'">';
|
|
$mostactive .= $wurl ? '<img src="'.$wurl.'" alt="'.$count['author'].'" class="avatar"/>' : '<span style="font-weight:bold;color:#467B96;">'._t('缓存出错!').'</span>';
|
|
$mostactive .= '</a></'.$listtag.'>';
|
|
}
|
|
$mostactive .= '</div>
|
|
';
|
|
|
|
//模版输出判断
|
|
if ($iscall) {
|
|
return $mostactive;
|
|
} else {
|
|
echo $mostactive;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 输出头像地址
|
|
*
|
|
* @access private
|
|
* @param string $mail 邮箱地址
|
|
* @param integer $size 头像尺寸
|
|
* @param string $rate 头像评级
|
|
* @param string $default 默认头像
|
|
* @param boolean $secure https请求
|
|
* @return string
|
|
*/
|
|
private static function avurl($mail,$size,$rating,$default,$secure=false)
|
|
{
|
|
$options = Helper::options();
|
|
$settings = $options->plugin('Avatars');
|
|
$hash = $mail ? md5(strtolower(trim($mail))) : '';
|
|
|
|
$proxy = $settings->proxy;
|
|
$server = $proxy ?
|
|
($proxy=='geekzu' ? ($secure ? 'https://sdn.geekzu.org/avatar/' : 'http://fdn.geekzu.org/avatar/') : $proxy)
|
|
: ($secure ? 'https://secure.gravatar.com/avatar/' : 'http://'.rand(0,2).'.gravatar.com/avatar/');
|
|
|
|
$url = $server.$hash;
|
|
$url .= '?s='.$size;
|
|
$url .= '&r='.$rating;
|
|
$url .= '&d='.$default;
|
|
|
|
if ($settings->cache) {
|
|
$path = __TYPECHO_ROOT_DIR__.__TYPECHO_PLUGIN_DIR__.'/Avatars/cache/';
|
|
//生成缓存目录
|
|
if (!is_dir($path)) {
|
|
if (!self::makedir($path)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//默认缓存15日
|
|
$cachetime = 14*24*3600;
|
|
$cachedir = $path.$hash.$size;
|
|
|
|
//生成缓存头像
|
|
if (!is_file($cachedir) || (time()-filemtime($cachedir))>$cachetime) {
|
|
if (!@copy($url,$cachedir)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$url = $options->pluginUrl.'/Avatars/cache/'.$hash.$size;
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* 缓存头像清空
|
|
*
|
|
* @access private
|
|
* @return void
|
|
*/
|
|
private static function deletefiles()
|
|
{
|
|
$path = glob(__TYPECHO_ROOT_DIR__.'/usr/plugins/Avatars/cache/*');
|
|
if ($path) {
|
|
foreach ($path as $filename) {
|
|
@unlink($filename);
|
|
}
|
|
}
|
|
|
|
Typecho_Widget::widget('Widget_Notice')->set(_t('本地头像缓存已清空!'),'notice');
|
|
Typecho_Response::getInstance()->goBack();
|
|
}
|
|
|
|
/**
|
|
* 本地目录创建
|
|
*
|
|
* @access private
|
|
* @param string $path 路径
|
|
* @return boolean
|
|
*/
|
|
private static function makedir($path)
|
|
{
|
|
$path = preg_replace("/\\\+/",'/',$path);
|
|
$current = rtrim($path,'/');
|
|
$last = $current;
|
|
|
|
while (!is_dir($current) && false!==strpos($path,'/')) {
|
|
$last = $current;
|
|
$current = dirname($current);
|
|
}
|
|
if ($last==$current) {
|
|
return true;
|
|
}
|
|
if (!@mkdir($last)) {
|
|
return false;
|
|
}
|
|
|
|
$stat = @stat($last);
|
|
$perms = $stat['mode'] & 0007777;
|
|
@chmod($last,$perms);
|
|
|
|
return self::makedir($path);
|
|
}
|
|
|
|
/**
|
|
* 兼容评论区头像
|
|
*
|
|
* @access public
|
|
* @param integer $size 头像尺寸
|
|
* @param string $default 默认头像
|
|
* @return void
|
|
*/
|
|
public static function avatars($size,$rating,$default,$comments)
|
|
{
|
|
$mail = $comments->mail;
|
|
$issecure = $comments->request->isSecure();
|
|
|
|
$url = Helper::options()->plugin('Avatars')->comment ? self::avurl($mail,$size,$rating,$default,$issecure)
|
|
: Typecho_Common::gravatarUrl($mail,$size,$rating,$default,$issecure);
|
|
|
|
echo $url ? '<img class="avatar" src="'.$url.'" alt="'.$comments->author.'"/>' : '<span style="font-weight:bold;color:#467B96;">'._t('缓存出错!').'</span>';
|
|
}
|
|
|
|
} |