9 Commits

Author SHA1 Message Date
浪子 b416e58596 1.2.2
增加全局侧边栏的显示开关
2025-08-02 10:50:39 +08:00
浪子 5d67235be0 1.2.2
先解析短代码再解析markdown
2025-08-01 10:58:21 +08:00
浪子 134bb0d66c 1.2.1
增加了多条公告的功能 并修改了相关描述
2025-07-27 13:39:53 +08:00
浪子 8afb1934dd 1.2.1 2025-07-19 11:11:55 +08:00
浪子 b73e465a79 1.2.0 2025-07-19 10:09:34 +08:00
浪子 ae990a722b 1.2.1 2025-07-12 08:24:51 +08:00
浪子 35806ea2fb 1.2.0 2025-07-11 19:16:34 +08:00
浪子 3bb53afcea 1.2.0 2025-07-10 19:48:37 +08:00
浪子 9f389fbb34 1.2.0 2025-07-10 18:58:47 +08:00
14 changed files with 539 additions and 348 deletions
+14 -3
View File
@@ -32,8 +32,6 @@
- 增加系统显示设置
- 增加浏览器信息显示设置
- 修复pjax模式下的404跳转
---
- 2025.07.07 表情短代码解析集成
- 1.1.5
@@ -41,12 +39,14 @@
- 将表情短代码(如 :smile:)自动解析为图片表情,集成到主题评论内容输出中。
- 2025.07.08
- 1.1.6
- 修复头像区域背景的bug
- 优化php8.3兼容性
- 1.1.8
- 优化github链接的正则表达式,只解析主仓库链接(如 https://github.com/用户名/仓库名),不再解析子路径(如 /tree/、/blob/ 等)为卡片。
- 实现文章内容图片懒加载,自动将常见图片格式(jpg、jpeg、png、webp)的<img>标签替换为带懒加载属性的格式。
- 修正getPostCover函数,确保只从原始内容中提取第一张真实图片地址,不受懒加载替换影响,避免首页首图变成load.svg。
@@ -54,4 +54,15 @@
- 新增支持[success]、[primary]、[danger]、[warning]、[info]、[dark]等alert类短代码,自动渲染为对应的Bootstrap风格提示框。
- 新增支持[collapse title='xxx']内容[/collapse]折叠面板短代码,自动渲染为带唯一ID的Bootstrap折叠结构。
- 新增支持[download file='xxx.zip' size='12MB']文件地址[/download]下载短代码,自动渲染为带文件名、大小、声明和下载地址的下载信息块。
- 新增支持[reply]隐藏内容[/reply]回复可见短代码,未满足条件时前端显示提示,已评论且审核通过后显示隐藏内容。
- 新增支持[reply]隐藏内容[/reply]回复可见短代码,未满足条件时前端显示提示,已评论且审核通过后显示隐藏内容。
- 1.2.0
- 新增支持首页登录
- 2025.08.02
- 1.2.2
- 增加侧边栏显示的全局开关
- 修复代码块中的短代码解析问题
+15 -5
View File
@@ -3,10 +3,16 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit;
$this->need('header.php');
?>
<div class="row row-cols-1">
<?php if ($this->options->showsidebar): ?>
<div class="col-lg-8 col-md-12 animated fadeInLeft ">
<div class="animated fadeInLeft ">
<?php else: ?>
<div class="col-lg-12 col-md-12">
<div class="row box-plr15">
<?php endif; ?>
<div> <!--文章列表-->
<div id="posts">
<?php if ($this->options->listmodel): ?>
<div class=" mr-0 ml-0">
<?php while ($this->next()): ?>
<?php
@@ -61,21 +67,25 @@ $coverImage = getPostCover($this->content, $this->cid);
</article>
<?php endwhile; ?>
</div>
<div class="mt20 p-flex-s-right" data-no-instant>
<?php $this->pageNav('&laquo;', '&raquo;', 1, '...', array(
<div class="mt20 p-flex-s-right" data-no-instant>
<?php $this->pageNav('&laquo;', '&raquo;', 1, '...', array(
'wrapTag' => 'ul',
'wrapClass' => 'pagination comment-ajax-load',
'itemTag' => 'li',
'textTag' => 'span',
'currentClass' => 'active',
'currentClass' => 'cur',
'prevClass' => 'prev',
'nextClass' => 'next'
)); ?>
</div>
</div>
<?php else: ?>
<?php $this->need('card.php'); ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php if ($this->options->showsidebar): ?>
<?php $this->need('sidebar.php'); ?>
<?php endif; ?>
<?php $this->need('footer.php'); ?>
+58 -33
View File
@@ -143,6 +143,10 @@ class Puock {
});
// form ajax submit
$(document).on("submit", ".ajax-form", (e) => {
// 如果是登录弹窗表单,允许原生提交
if ($(e.target).attr('id') === 'front-login-form') {
return true;
}
e.preventDefault();
const form = $(this.ct(e));
const formEls = form.find(":input")
@@ -208,6 +212,32 @@ class Puock {
}
return false;
})
// 登录弹窗表单AJAX提交(集成Puock插件接口)
$(document).off('submit', '#front-login-form');
$(document).on('submit', '#front-login-form', function(e) {
e.preventDefault();
var $form = $(this);
var data = $form.serialize();
$.ajax({
url: '/index.php/ajaxlogin/',
type: 'POST',
data: data,
dataType: 'json',
success: function(res) {
if (res.success) {
window.Puock.toast(res.msg || '登录成功', TYPE_SUCCESS);
setTimeout(function() {
window.location.reload();
}, 800);
} else {
window.Puock.toast(res.msg || '登录失败', TYPE_DANGER);
}
},
error: function() {
window.Puock.toast('请求失败', TYPE_DANGER);
}
});
});
}
pageLinkBlankOpenInit() {
@@ -415,7 +445,7 @@ class Puock {
this.pageLinkBlankOpenInit()
this.initGithubCard();
this.keyUpHandle();
this.loadHitokoto();
// this.loadHitokoto();
// this.asyncCacheViews();
this.swiperInit();
// this.validateInit();
@@ -771,12 +801,14 @@ class Puock {
}
parseFormData(formEl, args = {}) {
// 先获取表单所有字段
const dataArr = formEl.serializeArray();
const data = {...args};
const data = {};
for (let i = 0; i < dataArr.length; i++) {
data[dataArr[i].name] = dataArr[i].value;
}
return jQuery.param(data);
// 合并额外参数
return jQuery.param(Object.assign(data, args));
}
eventCommentPreSubmit() {
@@ -881,19 +913,26 @@ class Puock {
eventOpenCommentBox() {
$(document).off("click", ".comment-reply");
$(document).on("click", ".comment-reply", (e) => {
$(document).on("click", ".comment-reply", function(e) {
e.preventDefault();
this.data.comment.replyId = $(this.ct(e)).attr("data-coid");
if ($.trim(this.data.comment.replyId) === '') {
this.toast('结构有误', TYPE_DANGER);
const replyBtn = $(e.currentTarget);
const replyId = replyBtn.attr("data-coid");
if ($.trim(replyId) === '') {
window.Puock.toast('结构有误', TYPE_DANGER);
return;
}
const cf = $("#comment-form"),
commentLi = $(this.ct(e)).closest('.post-comment');
const cf = $("#comment-form");
const commentLi = replyBtn.closest('.post-comment');
// 只在表单在原位时插入占位符
if (!$("#comment-form-place-holder").length && cf.parent().attr("id") === "comment-form-box") {
cf.before('<div id="comment-form-place-holder"></div>');
}
// 每次都append到目标评论下方
commentLi.append(cf);
$("#comment-cancel").removeClass("d-none");
$("#comment").val("");
$("#comment_parent").val(this.data.comment.replyId);
$("#comment_parent").val(replyId);
window.Puock.data.comment.replyId = replyId;
// 滚动至表单
if (cf.length && cf[0].scrollIntoView) {
cf[0].scrollIntoView({behavior: "smooth", block: "center"});
@@ -904,12 +943,17 @@ class Puock {
eventCloseCommentBox() {
$(document).off("click", "#comment-cancel");
$(document).on("click", "#comment-cancel", () => {
const cf = $("#comment-form"),
cb = $(".post-comment .box-sw").parent();
cf.removeClass("box-sw");
$("#comment-form-box").append(cf);
const cf = $("#comment-form");
const holder = $("#comment-form-place-holder");
if (holder.length) {
holder.before(cf);
holder.remove();
} else {
$("#comment-form-box").append(cf);
}
$("#comment-cancel").addClass("d-none");
this.data.comment.replyId = null;
$("#comment_parent").val('');
});
}
@@ -1141,25 +1185,6 @@ class Puock {
});
}
loadHitokoto() {
setTimeout(() => {
$(".widget-puock-hitokoto").each((_, v) => {
const el = $(v);
const api = el.attr("data-api") || "https://v1.hitokoto.cn/"
$.get(api, (res) => {
el.find(".t").text(res.hitokoto ?? res.content ?? "无内容");
el.find('.f').text(res.from);
el.find('.fb').removeClass("d-none");
}, 'json').fail((err) => {
console.error(err)
el.find(".t").text("加载失败:" + err.responseText || err);
el.remove(".fb");
})
})
}, 300)
}
toast(msg, type = TYPE_PRIMARY, options = {}) {
options = Object.assign({
duration: 2600,
+1 -1
View File
@@ -73,7 +73,7 @@ if ($pageprev == '1' && $this->have()):
'wrapClass' => 'pagination comment-ajax-load',
'itemTag' => 'li',
'textTag' => 'span',
'currentClass' => 'active',
'currentClass' => 'cur',
'prevClass' => 'prev',
'nextClass' => 'next'
)); ?>
+5 -60
View File
@@ -43,9 +43,9 @@
<div>
<?php if(!$this->user->hasLogin()): ?>
<div class="d-inline-block">
<a class="btn btn-primary btn-ssm" href="<?php $this->options->loginUrl(); ?>" title="登录" target="_blank">
<i class="fa fa-right-to-bracket"></i>&nbsp;登录
</a>
<button class="btn btn-primary btn-ssm pk-modal-toggle" type="button" data-once-load="true" data-id="front-login" title="快捷登录" data-url="<?php echo get_correct_url('/login/'); ?>">
<i class="fa fa-right-to-bracket"></i>&nbsp;快捷登录
</button>
</div>
<?php endif; ?>
</div>
@@ -59,7 +59,7 @@
<i class="fa-regular fa-face-smile t-md"></i>
</button>
<?php endif; ?>
<input type="hidden" name="parent" id="comment-parent" value="">
<input type="hidden" name="parent" id="comment_parent" value="">
<button type="submit" id="comment-submit" class="btn btn-primary btn-ssm">
<i class="fa-regular fa-paper-plane"></i>&nbsp;发布评论
</button>
@@ -217,59 +217,4 @@
</div>
<?php endif; ?>
</li>
<?php } ?>
<script>
// 评论局部回复,表单移动到评论下方
document.addEventListener('DOMContentLoaded', function() {
// 监听评论区的回复按钮
document.body.addEventListener('click', function(e) {
var target = e.target;
if (
target.classList.contains('comment-reply') ||
(target.parentNode && target.parentNode.classList && target.parentNode.classList.contains('comment-reply'))
) {
e.preventDefault();
// 兼容span嵌套与a标签直接点击
var replyBtn = target.classList.contains('comment-reply') ? target : target.parentNode;
var commentId = replyBtn.getAttribute('data-coid');
var commentLi = replyBtn.closest('.post-comment');
var respondBox = document.getElementById('comment-form-box');
var commentForm = document.getElementById('comment-form');
var cancelBtn = document.getElementById('comment-cancel');
var parentInput = document.getElementById('comment-parent');
// 记录原位置
if (!document.getElementById('comment-form-place-holder')) {
var holder = document.createElement('div');
holder.id = 'comment-form-place-holder';
respondBox.parentNode.insertBefore(holder, respondBox);
}
// 移动表单
commentLi.appendChild(respondBox);
// 设置parent
if (parentInput) parentInput.value = commentId;
// 展示取消按钮
if(cancelBtn) cancelBtn.classList.remove('d-none');
// 聚焦文本域
var textarea = commentForm.querySelector('textarea');
if (textarea) textarea.focus();
// 滚动至表单
respondBox.scrollIntoView({behavior: "smooth", block: "center"});
return false;
}
// 取消回复
if (target.id === 'comment-cancel') {
e.preventDefault();
var respondBox = document.getElementById('comment-form-box');
var holder = document.getElementById('comment-form-place-holder');
var parentInput = document.getElementById('comment-parent');
if (holder) {
holder.parentNode.insertBefore(respondBox, holder);
holder.parentNode.removeChild(holder);
}
if (parentInput) parentInput.value = '';
target.classList.add('d-none');
return false;
}
});
});
</script>
<?php } ?>
+1 -1
View File
@@ -68,7 +68,7 @@
&copy; <?php echo date('Y'); ?> <a href="<?php $this->options->siteUrl(); ?>"><?php $this->options->title(); ?></a>
<div class="fs12 mt10 c-sub">
<span> &nbsp;Theme by
<a target="_blank" class="c-sub" title="Puock v2.8.14" href="https://github.com/jkjoy/typecho-theme-puock">Puock</a>
<a target="_blank" class="c-sub" title="Puock v1.2.2" href="https://github.com/jkjoy/typecho-theme-puock">Puock</a>
</span>
<span> &nbsp;Powered by
<a target="_blank" class="c-sub" title="Typecho" href="https://typecho.org">Typecho</a> <p><a target="_blank" class="c-sub" title="老孙博客" href="https://imsun.org">老孙博客</a>制作</p>
+349 -198
View File
@@ -33,13 +33,13 @@ function themeConfig($form)
$form->addInput($cmsmodel);
$friendlink = new Typecho_Widget_Helper_Form_Element_Radio('friendlink',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('友情链接'), _t('选择"是"在首页显示友情链接。开启前请安装"Links"插件。链接分类需设置为home,默认关闭'));
'0', _t('友情链接'), _t('选择"是"在首页显示友情链接。开启前请安装"Links"插件。链接分类需设置为<b>home</b>,默认关闭'));
$form->addInput($friendlink);
$social = new Typecho_Widget_Helper_Form_Element_Radio('social',
array('0'=> _t('否'), '1'=> _t('是')),
'0', _t('社交分享显示'), _t('选择"是"在文章页面显示社交分享。需要搭配插件使用,默认关闭'));
$form->addInput($social);
$gonggao = new Typecho_Widget_Helper_Form_Element_Textarea('gonggao', NULL, NULL, _t('站点公告'), _t('支持HTML'));
$gonggao = new Typecho_Widget_Helper_Form_Element_Textarea('gonggao', NULL, NULL, _t('站点公告'), _t('<b style=color:red>使用格式: </b><b>标题|链接|图标</b><br>多条公告回车分隔.链接和图标可以为空.图标使用Font Awesome,如: fa-regular fa-bell'));
$form->addInput($gonggao);
$adlisttop = new Typecho_Widget_Helper_Form_Element_Textarea('adlisttop', NULL, NULL, _t('文章列表上方广告位'), _t('支持HTML'));
$form->addInput($adlisttop);
@@ -51,14 +51,24 @@ function themeConfig($form)
$form->addInput($articlemid);
$articlefoot = new Typecho_Widget_Helper_Form_Element_Textarea('articlefoot', NULL, NULL, _t('文章页底部广告位'), _t('支持HTML'));
$form->addInput($articlefoot);
$addhead = new Typecho_Widget_Helper_Form_Element_Textarea('addhead', NULL, NULL, _t('网站验证代码'), _t('若开启无刷新加载,请在标签上加上data-instant属性'));
$addhead = new Typecho_Widget_Helper_Form_Element_Textarea('addhead', NULL, '<link rel="stylesheet" href="https://cdnjs.imsun.org/lxgw-wenkai-screen-webfont/style.css"></link>
<style>* {font-family: -apple-system, BlinkMacSystemFont,"LXGW WenKai Screen", sans-serif;}</style>', _t('网站验证代码'), _t('若开启无刷新加载,请在标签上加上data-instant属性'));
$form->addInput($addhead);
$tongji = new Typecho_Widget_Helper_Form_Element_Textarea('tongji', NULL, NULL, _t('网站统计代码'), _t('支持HTML'));
$tongji = new Typecho_Widget_Helper_Form_Element_Textarea('tongji', NULL, '<script async defer src="https://0tz.top/tracker.js" data-website-id="Puock"></script>', _t('网站统计代码'), _t('支持HTML'));
$form->addInput($tongji);
$footerinfo = new Typecho_Widget_Helper_Form_Element_Textarea('footerinfo', NULL, NULL, _t('底部关于我们'), _t('支持HTML'));
$footerinfo = new Typecho_Widget_Helper_Form_Element_Textarea('footerinfo', NULL, '<a href="/feed" target="_blank"><i class="fa-solid fa-rss fa-2x"></i></a>
<a href="https://jiong.us/@sun" target="_blank"><i class="fa-brands fa-mastodon fa-2x"></i></a>
<a href="https://discord.gg/RUUcPEQKNt" target="_blank"><i class="fa-brands fa-discord fa-2x"></i></a>
<a href="https://t.me/imsunpw" target="_blank"><i class="fa-brands fa-telegram fa-2x"></i></a>
<a href="mailto:imsunpw@gmail.com" target="_blank"><i class="fa-solid fa-envelope fa-2x"></i></a>
<a href="/sitemap.xml" target="_blank"><i class="fa-solid fa-sitemap fa-2x"></i></a>', _t('底部关于我们'), _t('支持HTML'));
$form->addInput($footerinfo);
$footercopyright = new Typecho_Widget_Helper_Form_Element_Textarea('footercopyright', NULL, NULL, _t('底部版权信息'), _t('支持HTML'));
$footercopyright = new Typecho_Widget_Helper_Form_Element_Textarea('footercopyright', NULL, '<b>版权所有 转载请注明出处</b>', _t('底部版权信息'), _t('支持HTML'));
$form->addInput($footercopyright);
$showsidebar = new Typecho_Widget_Helper_Form_Element_Radio('showsidebar',
array('0'=> _t('关闭'), '1'=> _t('显示')),
'0', _t('全局侧边栏显示'), _t('<p style=color:red>注意!</p>如果选择关闭侧边栏则下方的侧边栏显示设置将无效'));
$form->addInput($showsidebar);
$sidebarBlock = new \Typecho\Widget\Helper\Form\Element\Checkbox(
'sidebarBlock',
[
@@ -72,7 +82,6 @@ function themeConfig($form)
['ShowSearch', 'ShowAdmin', 'ShowRecentPosts', 'ShowHotPosts', 'ShowRecentComments', 'ShowTags'],
_t('侧边栏显示')
);
$form->addInput($sidebarBlock->multiMode());
}
@@ -186,14 +195,30 @@ function getAllCommenters() {
$db = Typecho_Db::get();
$commenters = array();
// 查询所有评论者信息并按邮箱分组统计
$query = $db->select('author, mail, COUNT(*) as comment_count, url')
$isSqlite = strpos(strtolower(get_class($db->getAdapter())), 'sqlite') !== false;
if ($isSqlite) {
// SQLite 不支持 ANY_VALUE
$query = $db->select('author', 'mail', 'COUNT(*) as comment_count', 'url')
->from('table.comments')
->where('status = ?', 'approved')
->where('authorId != ?', 1)
->group('mail')
->order('comment_count', Typecho_Db::SORT_DESC);
} else {
// MySQL 用 ANY_VALUE 兼容 ONLY_FULL_GROUP_BY
$query = $db->select(
'ANY_VALUE(author) as author',
'mail',
'COUNT(*) as comment_count',
'ANY_VALUE(url) as url'
)
->from('table.comments')
->where('status = ?', 'approved') // 只统计已通过审核的评论
->where('authorId != ?', 1) // 排除ID为1的管理员
->where('authorId != ?', 1)
->where('status = ?', 'approved')
->group('mail')
->order('comment_count', Typecho_Db::SORT_DESC);
}
$rows = $db->fetchAll($query);
// 获取 Gravatar 镜像设置
@@ -297,92 +322,93 @@ function getBrowsersInfo($userAgent) {
"version" => "",
"device" => "PC"
];
$match = [
// 浏览器 - 国外浏览器
"Safari" => strstr($userAgent, 'Safari') != false ,
"Chrome" => strstr($userAgent, 'Chrome') != false || strstr($userAgent, 'CriOS') != false ,
"IE" => strstr($userAgent, 'MSIE') != false || strstr($userAgent, 'Trident') != false ,
"Edge" => strstr($userAgent, 'Edge') != false || strstr($userAgent, 'Edg/') != false || strstr($userAgent, 'EdgA') != false || strstr($userAgent, 'EdgiOS') != false,
"Firefox" => strstr($userAgent, 'Firefox') != false || strstr($userAgent, 'FxiOS') != false ,
"Firefox Focus" => strstr($userAgent, 'Focus') != false,
"Chromium" => strstr($userAgent,'Chromium') != false,
"Opera" => strstr($userAgent,'Opera') != false || strstr($userAgent,'OPR') != false,
"Vivaldi" => strstr($userAgent,'Vivaldi') != false,
"Yandex" => strstr($userAgent,'YaBrowser') != false,
"Arora" => strstr($userAgent,'Arora') != false,
"Lunascape" => strstr($userAgent,'Lunascape') != false,
"QupZilla" => strstr($userAgent,'QupZilla') != false,
"Coc Coc" => strstr($userAgent,'coc_coc_browser') != false,
"Kindle" => strstr($userAgent,'Kindle') != false || strstr($userAgent,'Silk/') != false,
"Iceweasel" => strstr($userAgent,'Iceweasel') != false,
"Konqueror" => strstr($userAgent,'Konqueror') != false,
"Iceape" => strstr($userAgent,'Iceape') != false,
"SeaMonkey" => strstr($userAgent,'SeaMonkey') != false,
"Epiphany" => strstr($userAgent,'Epiphany') != false,
"Safari" => strstr($userAgent, 'Safari') !== false,
"Chrome" => strstr($userAgent, 'Chrome') !== false || strstr($userAgent, 'CriOS') !== false,
"IE" => strstr($userAgent, 'MSIE') !== false || strstr($userAgent, 'Trident') !== false,
"Edge" => strstr($userAgent, 'Edge') !== false || strstr($userAgent, 'Edg/') !== false || strstr($userAgent, 'EdgA') !== false || strstr($userAgent, 'EdgiOS') !== false,
"Firefox" => strstr($userAgent, 'Firefox') !== false || strstr($userAgent, 'FxiOS') !== false,
"Firefox Focus" => strstr($userAgent, 'Focus') !== false,
"Chromium" => strstr($userAgent, 'Chromium') !== false,
"Opera" => strstr($userAgent, 'Opera') !== false || strstr($userAgent, 'OPR') !== false,
"Vivaldi" => strstr($userAgent, 'Vivaldi') !== false,
"Yandex" => strstr($userAgent, 'YaBrowser') !== false,
"Arora" => strstr($userAgent, 'Arora') !== false,
"Lunascape" => strstr($userAgent, 'Lunascape') !== false,
"QupZilla" => strstr($userAgent, 'QupZilla') !== false,
"Coc Coc" => strstr($userAgent, 'coc_coc_browser') !== false,
"Kindle" => strstr($userAgent, 'Kindle') !== false || strstr($userAgent, 'Silk/') !== false,
"Iceweasel" => strstr($userAgent, 'Iceweasel') !== false,
"Konqueror" => strstr($userAgent, 'Konqueror') !== false,
"Iceape" => strstr($userAgent, 'Iceape') !== false,
"SeaMonkey" => strstr($userAgent, 'SeaMonkey') !== false,
"Epiphany" => strstr($userAgent, 'Epiphany') !== false,
// 浏览器 - 国内浏览器
"360" => strstr($userAgent,'QihooBrowser') != false || strstr($userAgent,'QHBrowser') != false,
"360EE" => strstr($userAgent,'360EE') != false,
"360SE" => strstr($userAgent,'360SE') != false,
"UC" => strstr($userAgent,'UCBrowser') != false || strstr($userAgent,' UBrowser') != false || strstr($userAgent,'UCWEB') != false,
"QQBrowser" => strstr($userAgent,'QQBrowser') != false,
"QQ" => strstr($userAgent,'QQ/') != false,
"Baidu" => strstr($userAgent,'Baidu') != false || strstr($userAgent,'BIDUBrowser') != false || strstr($userAgent,'baidubrowser') != false || strstr($userAgent,'baiduboxapp') != false || strstr($userAgent,'BaiduHD') != false,
"Maxthon" => strstr($userAgent,'Maxthon') != false,
"Sogou" => strstr($userAgent,'MetaSr') != false || strstr($userAgent,'Sogou') != false,
"Liebao" => strstr($userAgent,'LBBROWSER') != false || strstr($userAgent,'LieBaoFast') != false,
"2345Explorer" => strstr($userAgent,'2345Explorer') != false || strstr($userAgent,'Mb2345Browser') != false || strstr($userAgent,'2345chrome') != false,
"115Browser" => strstr($userAgent,'115Browser') != false,
"TheWorld" => strstr($userAgent,'TheWorld') != false,
"Quark" => strstr($userAgent,'Quark') != false,
"Qiyu" => strstr($userAgent,'Qiyu') != false,
"360" => strstr($userAgent, 'QihooBrowser') !== false || strstr($userAgent, 'QHBrowser') !== false,
"360EE" => strstr($userAgent, '360EE') !== false,
"360SE" => strstr($userAgent, '360SE') !== false,
"UC" => strstr($userAgent, 'UCBrowser') !== false || strstr($userAgent, ' UBrowser') !== false || strstr($userAgent, 'UCWEB') !== false,
"QQBrowser" => strstr($userAgent, 'QQBrowser') !== false,
"QQ" => strstr($userAgent, 'QQ/') !== false,
"Baidu" => strstr($userAgent, 'Baidu') !== false || strstr($userAgent, 'BIDUBrowser') !== false || strstr($userAgent, 'baidubrowser') !== false || strstr($userAgent, 'baiduboxapp') !== false || strstr($userAgent, 'BaiduHD') !== false,
"Maxthon" => strstr($userAgent, 'Maxthon') !== false,
"Sogou" => strstr($userAgent, 'MetaSr') !== false || strstr($userAgent, 'Sogou') !== false,
"Liebao" => strstr($userAgent, 'LBBROWSER') !== false || strstr($userAgent, 'LieBaoFast') !== false,
"2345Explorer" => strstr($userAgent, '2345Explorer') !== false || strstr($userAgent, 'Mb2345Browser') !== false || strstr($userAgent, '2345chrome') !== false,
"115Browser" => strstr($userAgent, '115Browser') !== false,
"TheWorld" => strstr($userAgent, 'TheWorld') !== false,
"Quark" => strstr($userAgent, 'Quark') !== false,
"Qiyu" => strstr($userAgent, 'Qiyu') !== false,
// 浏览器 - 手机厂商
"XiaoMi" => strstr($userAgent,'MiuiBrowser') != false,
"Huawei" => strstr($userAgent,'HuaweiBrowser') != false || strstr($userAgent,'HUAWEI/') != false || strstr($userAgent,'HONOR') != false || strstr($userAgent,'HBPC/') != false,
"Vivo" => strstr($userAgent,'VivoBrowser') != false,
"OPPO" => strstr($userAgent,'HeyTapBrowser') != false,
"XiaoMi" => strstr($userAgent, 'MiuiBrowser') !== false,
"Huawei" => strstr($userAgent, 'HuaweiBrowser') !== false || strstr($userAgent, 'HUAWEI/') !== false || strstr($userAgent, 'HONOR') !== false || strstr($userAgent, 'HBPC/') !== false,
"Vivo" => strstr($userAgent, 'VivoBrowser') !== false,
"OPPO" => strstr($userAgent, 'HeyTapBrowser') !== false,
// 浏览器 - 客户端
"Wechat" => strstr($userAgent,'MicroMessenger') != false,
"WechatWork" => strstr($userAgent,'wxwork/') != false,
"Taobao" => strstr($userAgent,'AliApp(TB') != false,
"Alipay" => strstr($userAgent,'AliApp(AP') != false,
"Weibo" => strstr($userAgent,'Weibo') != false,
"Douban" => strstr($userAgent,'com.douban.frodo') != false,
"Suning" => strstr($userAgent,'SNEBUY-APP') != false,
"iQiYi" => strstr($userAgent,'IqiyiApp') != false,
"DingTalk" => strstr($userAgent,'DingTalk') != false,
"Douyin" => strstr($userAgent,'aweme') != false,
"Wechat" => strstr($userAgent, 'MicroMessenger') !== false,
"WechatWork" => strstr($userAgent, 'wxwork/') !== false,
"Taobao" => strstr($userAgent, 'AliApp(TB') !== false,
"Alipay" => strstr($userAgent, 'AliApp(AP') !== false,
"Weibo" => strstr($userAgent, 'Weibo') !== false,
"Douban" => strstr($userAgent, 'com.douban.frodo') !== false,
"Suning" => strstr($userAgent, 'SNEBUY-APP') !== false,
"iQiYi" => strstr($userAgent, 'IqiyiApp') !== false,
"DingTalk" => strstr($userAgent, 'DingTalk') !== false,
"Douyin" => strstr($userAgent, 'aweme') !== false,
// 系统或平台
"Windows" => strstr($userAgent,'Windows') != false,
"Linux" => strstr($userAgent,'Linux') != false || strstr($userAgent,'X11') != false,
"Mac OS" => strstr($userAgent,'Macintosh') != false,
"Android" => strstr($userAgent,'Android') != false || strstr($userAgent,'Adr') != false,
"HarmonyOS" => strstr($userAgent,'HarmonyOS') != false,
"Ubuntu" => strstr($userAgent,'Ubuntu') != false,
"FreeBSD" => strstr($userAgent,'FreeBSD') != false,
"Debian" => strstr($userAgent,'Debian') != false,
"Windows Phone" => strstr($userAgent,'IEMobile') != false || strstr($userAgent,'Windows Phone') != false,
"BlackBerry" => strstr($userAgent,'BlackBerry') != false || strstr($userAgent,'RIM') != false,
"MeeGo" => strstr($userAgent,'MeeGo') != false,
"Symbian" => strstr($userAgent,'Symbian') != false,
"iOS" => strstr($userAgent,'like Mac OS X') != false,
"Chrome OS" => strstr($userAgent,'CrOS') != false,
"WebOS" => strstr($userAgent,'hpwOS') != false,
"Windows" => strstr($userAgent, 'Windows') !== false,
"Linux" => strstr($userAgent, 'Linux') !== false || strstr($userAgent, 'X11') !== false,
"Mac OS" => strstr($userAgent, 'Macintosh') !== false,
"Android" => strstr($userAgent, 'Android') !== false || strstr($userAgent, 'Adr') !== false,
"HarmonyOS" => strstr($userAgent, 'HarmonyOS') !== false,
"Ubuntu" => strstr($userAgent, 'Ubuntu') !== false,
"FreeBSD" => strstr($userAgent, 'FreeBSD') !== false,
"Debian" => strstr($userAgent, 'Debian') !== false,
"Windows Phone" => strstr($userAgent, 'IEMobile') !== false || strstr($userAgent, 'Windows Phone') !== false,
"BlackBerry" => strstr($userAgent, 'BlackBerry') !== false || strstr($userAgent, 'RIM') !== false,
"MeeGo" => strstr($userAgent, 'MeeGo') !== false,
"Symbian" => strstr($userAgent, 'Symbian') !== false,
"iOS" => strstr($userAgent, 'like Mac OS X') !== false,
"Chrome OS" => strstr($userAgent, 'CrOS') !== false,
"WebOS" => strstr($userAgent, 'hpwOS') !== false,
// 设备
"Mobile" => strstr($userAgent,'Mobi') != false || strstr($userAgent,'iPh') != false || strstr($userAgent,'480') != false,
"Tablet" => strstr($userAgent,'Tablet') != false || strstr($userAgent,'Pad') != false || strstr($userAgent,'Nexus 7') != false,
"Mobile" => strstr($userAgent, 'Mobi') !== false || strstr($userAgent, 'iPh') !== false || strstr($userAgent, '480') !== false,
"Tablet" => strstr($userAgent, 'Tablet') !== false || strstr($userAgent, 'Pad') !== false || strstr($userAgent, 'Nexus 7') !== false,
];
// 部分修正 | 因typecho评论数据只存储了ua的信息,所以不能完全进行修正尤其是360相关浏览器
// 部分修正
if ($match['Baidu'] && $match['Opera']) $match['Baidu'] = false;
if ($match['iOS']) $match['Safari'] = true;
// 基本信息
$baseInfo = [
"browser" => [
'Safari', 'Chrome', 'Edge', 'IE', 'Firefox', 'Firefox Focus', 'Chromium',
'Opera', 'Vivaldi', 'Yandex', 'Arora', 'Lunascape','QupZilla', 'Coc Coc',
'Kindle', 'Iceweasel', 'Konqueror', 'Iceape','SeaMonkey', 'Epiphany', 'XiaoMi',
'Vivo', 'OPPO', '360', '360SE','360EE', 'UC', 'QQBrowser', 'QQ', 'Huawei', 'Baidu',
'Opera', 'Vivaldi', 'Yandex', 'Arora', 'Lunascape', 'QupZilla', 'Coc Coc',
'Kindle', 'Iceweasel', 'Konqueror', 'Iceape', 'SeaMonkey', 'Epiphany', 'XiaoMi',
'Vivo', 'OPPO', '360', '360SE', '360EE', 'UC', 'QQBrowser', 'QQ', 'Huawei', 'Baidu',
'Maxthon', 'Sogou', 'Liebao', '2345Explorer', '115Browser', 'TheWorld', 'Quark', 'Qiyu',
'Wechat', 'WechatWork', 'Taobao', 'Alipay', 'Weibo', 'Douban', 'Suning', 'iQiYi', 'DingTalk', 'Douyin'
],
@@ -393,17 +419,15 @@ function getBrowsersInfo($userAgent) {
],
"device" => ['Mobile', 'Tablet'],
];
foreach ($baseInfo as $k => $v) {
foreach ($v as $xv) {
if ($match[$xv]) $deviceInfo[$k] = $xv;
}
}
// 操作系统版本信息
$windowsVersion = [
'10' => "10",
'6.4' => '10',
'6.3' => '8.1',
'6.2' => '8',
'6.1' => '7',
@@ -412,29 +436,58 @@ function getBrowsersInfo($userAgent) {
'5.1' => 'XP',
'5.0' => '2000',
];
$wv = pregMatch("/^Mozilla\/\d.0 \(Windows NT ([\d.]+)[;)].*$/", $userAgent);
// Extract Windows NT version and build number
if (preg_match("/\(Windows NT ([\d.]+)(?:;[^)]*Build\/(\d+))?(?:;[^)]*)?\)/i", $userAgent, $matches)) {
$ntVersion = $matches[1];
$buildNumber = isset($matches[2]) ? (int)$matches[2] : 0;
if ($ntVersion === '10.0') {
// Check build number for Windows 11
if ($buildNumber >= 22000) {
$deviceInfo['systemVersion'] = '11';
} else {
// Fallback heuristic: Check Edge version (Windows 11 requires Edge >= 91)
if ($deviceInfo['browser'] === 'Edge' && preg_match("/Edg\/([\d.]+)/", $userAgent, $edgeMatch)) {
$edgeVersion = (float)$edgeMatch[1];
if ($edgeVersion >= 91) {
$deviceInfo['systemVersion'] = '11'; // Assume Windows 11 for modern Edge
} else {
$deviceInfo['systemVersion'] = '10';
}
} else {
$deviceInfo['systemVersion'] = '10';
// Log ambiguous case for debugging
error_log("Ambiguous Windows version (NT 10.0, no build number): $userAgent");
}
}
} else {
$deviceInfo['systemVersion'] = $windowsVersion[$ntVersion] ?? $ntVersion;
}
}
$HarmonyOSVersion = [
10 => "2",
12 => "3"
];
$systemVersion = [
"Windows" => $windowsVersion[$wv] ?? $wv,
"Android" => pregMatch("/^.*Android ([\d.]+);.*$/", $userAgent),
"HarmonyOS" => $HarmonyOSVersion[pregMatch("/^Mozilla.*Android ([\d.]+)[;)].*$/", $userAgent)] ?? '',
"iOS" => preg_replace("/_/", '.', pregMatch("/^.*OS ([\d_]+) like.*$/", $userAgent)),
"Debian" => pregMatch("/^.*Debian\/([\d.]+).*$/", $userAgent),
"Windows Phone" => pregMatch("/^.*Windows Phone( OS)? ([\d.]+);.*$/", $userAgent),
"Mac OS" => preg_replace("/_/", '.',pregMatch("/^.*Mac OS X ([\d_]+).*$/", $userAgent)),
"WebOS" => pregMatch("/^.*hpwOS\/([\d.]+);.*$/", $userAgent)
"Windows" => $deviceInfo['systemVersion'], // Already set above
"Android" => preg_match("/Android ([\d.]+);/", $userAgent, $matches) ? $matches[1] : '',
"HarmonyOS" => preg_match("/Android ([\d.]+)[;)]/", $userAgent, $matches) ? ($HarmonyOSVersion[(int)$matches[1]] ?? '') : '',
"iOS" => preg_match("/OS ([\d_]+) like/", $userAgent, $matches) ? str_replace('_', '.', $matches[1]) : '',
"Debian" => preg_match("/Debian\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Windows Phone" => preg_match("/Windows Phone( OS)? ([\d.]+);/", $userAgent, $matches) ? $matches[2] : '',
"Mac OS" => preg_match("/Mac OS X ([\d_]+)/", $userAgent, $matches) ? str_replace('_', '.', $matches[1]) : '',
"WebOS" => preg_match("/hpwOS\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''
];
if (isset($deviceInfo['system']) && $deviceInfo['system'] !== "" && isset($systemVersion[$deviceInfo['system']])) {
$deviceInfo['systemVersion'] = $systemVersion[$deviceInfo['system']];
if ($deviceInfo['systemVersion'] == $userAgent) {
$deviceInfo['systemVersion'] = '';
}
$deviceInfo['systemVersion'] = $systemVersion[$deviceInfo['system']];
if ($deviceInfo['systemVersion'] == $userAgent) {
$deviceInfo['systemVersion'] = '';
}
}
// 浏览器版本信息
$browsers_360SE = [
108 => '14.0',
@@ -459,7 +512,7 @@ function getBrowsersInfo($userAgent) {
30 => '7.5',
];
$browsers_liebao = [
57 => '6.5',
57 => '6.5',
49 => '6.0',
46 => '5.9',
42 => '5.3',
@@ -471,82 +524,81 @@ function getBrowsersInfo($userAgent) {
$browsers_2345 = [
69 => '10.0',
55 => '9.9',
69 => '10.0',
55 => '9.9',
69 => '10.0',
55 => '9.9'
];
$chromeVersion = pregMatch('/^.*Chrome\/([\d]+).*$/', $userAgent);
$chromeVersion = preg_match('/Chrome\/([\d]+)/', $userAgent, $matches) ? (int)$matches[1] : 0;
$browsersVersion = [
"Safari" => pregMatch("/^.*Version\/([\d.]+).*$/", $userAgent),
"Chrome" => pregMatch("/^.*Chrome\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*CriOS\/([\d.]+).*$/", $userAgent),
"IE" => pregMatch("/^.*MSIE ([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*rv:([\d.]+).*$/", $userAgent),
"Edge" => pregMatch("/^.*Edge\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*Edg\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*EdgA\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*EdgiOS\/([\d.]+).*$/", $userAgent),
"Firefox" => pregMatch("/^.*Firefox\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*FxiOS\/([\d.]+).*$/", $userAgent),
"Firefox Focus" => pregMatch("/^.*Focus\/([\d.]+).*$/", $userAgent),
"Chromium" => pregMatch("/^.*Chromium\/([\d.]+).*$/", $userAgent),
"Opera" => pregMatch("/^.*Opera\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*OPR\/([\d.]+).*$/", $userAgent),
"Vivaldi" => pregMatch("/^.*Vivaldi\/([\d.]+).*$/", $userAgent),
"Yandex" => pregMatch("/^.*YaBrowser\/([\d.]+).*$/", $userAgent),
"Brave" => pregMatch("/^.*Chrome\/([\d.]+).*$/", $userAgent),
"Arora" => pregMatch("/^.*Arora\/([\d.]+).*$/", $userAgent),
"Lunascape" => pregMatch("/^.*Lunascape[\/\s]([\d.]+).*$/", $userAgent),
"QupZilla" => pregMatch("/^.*QupZilla[\/\s]([\d.]+).*$/", $userAgent),
"Coc Coc" => pregMatch("/^.*coc_coc_browser\/([\d.]+).*$/", $userAgent),
"Kindle" => pregMatch("/^.*Version\/([\d.]+).*$/", $userAgent),
"Iceweasel" => pregMatch("/^.*Iceweasel\/([\d.]+).*$/", $userAgent),
"Konqueror" => pregMatch("/^.*Konqueror\/([\d.]+).*$/", $userAgent),
"Iceape" => pregMatch("/^.*Iceape\/([\d.]+).*$/", $userAgent),
"SeaMonkey" => pregMatch("/^.*SeaMonkey\/([\d.]+).*$/", $userAgent),
"Epiphany" => pregMatch("/^.*Epiphany\/([\d.]+).*$/", $userAgent),
"360" => pregMatch("/^.*QihooBrowser(HD)?\/([\d.]+).*$/", $userAgent),
"Maxthon" => pregMatch("/^.*Maxthon\/([\d.]+).*$/", $userAgent),
"QQBrowser" => pregMatch("/^.*QQBrowser\/([\d.]+).*$/", $userAgent),
"QQ" => pregMatch("/^.*QQ\/([\d.]+).*$/", $userAgent),
"Baidu" => pregMatch("/^.*BIDUBrowser[\s\/]([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*baiduboxapp\/([\d.]+).*$/", $userAgent),
"UC" => pregMatch("/^.*UC?Browser\/([\d.]+).*$/", $userAgent),
"Sogou" => pregMatch("/^.*SE ([\d.X]+).*$/", $userAgent) ?? pregMatch("/^.*SogouMobileBrowser\/([\d.]+).*$/", $userAgent),
"115Browser" => pregMatch("/^.*115Browser\/([\d.]+).*$/", $userAgent),
"TheWorld" => pregMatch("/^.*TheWorld ([\d.]+).*$/", $userAgent),
"XiaoMi" => pregMatch("/^.*MiuiBrowser\/([\d.]+).*$/", $userAgent),
"Vivo" => pregMatch("/^.*VivoBrowser\/([\d.]+).*$/", $userAgent),
"OPPO" => pregMatch("/^.*HeyTapBrowser\/([\d.]+).*$/", $userAgent),
"Quark" => pregMatch("/^.*Quark\/([\d.]+).*$/", $userAgent),
"Qiyu" => pregMatch("/^.*Qiyu\/([\d.]+).*$/", $userAgent),
"Wechat" => pregMatch("/^.*MicroMessenger\/([\d.]+).*$/", $userAgent),
"WechatWork" => pregMatch("/^.*wxwork\/([\d.]+).*$/", $userAgent),
"Taobao" => pregMatch("/^.*AliApp\(TB\/([\d.]+).*$/", $userAgent),
"Alipay" => pregMatch("/^.*AliApp\(AP\/([\d.]+).*$/", $userAgent),
"Weibo" => pregMatch("/^.*weibo__([\d.]+).*$/", $userAgent),
"Douban" => pregMatch("/^.*com.douban.frodo\/([\d.]+).*$/", $userAgent),
"Suning" => pregMatch("/^.*SNEBUY-APP([\d.]+).*$/", $userAgent),
"iQiYi" => pregMatch("/^.*IqiyiVersion\/([\d.]+).*$/", $userAgent),
"DingTalk" => pregMatch("/^.*DingTalk\/([\d.]+).*$/", $userAgent),
"Douyin" => pregMatch("/^.*app_version\/([\d.]+).*$/", $userAgent),
"Huawei" => pregMatch("/^.*Version\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*HuaweiBrowser\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*HBPC\/([\d.]+).*$/", $userAgent),
"360SE" => $browsers_360SE[$chromeVersion] ?? '',
"360EE" => $browsers_360EE[$chromeVersion] ?? '',
"Liebao" => pregMatch("/^.*LieBaoFast\/([\d.]+).*$/", $userAgent) ?? $browsers_liebao[$chromeVersion],
"2345Explorer" => $browsers_2345[$chromeVersion] ?? pregMatch("/^.*2345Explorer\/([\d.]+).*$/", $userAgent) ?? pregMatch("/^.*Mb2345Browser\/([\d.]+).*$/", $userAgent),
"Safari" => preg_match("/Version\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Chrome" => preg_match("/Chrome\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/CriOS\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''),
"IE" => preg_match("/MSIE ([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/rv:([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''),
"Edge" => preg_match("/Edge\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/Edg\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/EdgA\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/EdgiOS\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''))),
"Firefox" => preg_match("/Firefox\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/FxiOS\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''),
"Firefox Focus" => preg_match("/Focus\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Chromium" => preg_match("/Chromium\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Opera" => preg_match("/Opera\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/OPR\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''),
"Vivaldi" => preg_match("/Vivaldi\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Yandex" => preg_match("/YaBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Brave" => preg_match("/Chrome\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Arora" => preg_match("/Arora\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Lunascape" => preg_match("/Lunascape[\/\s]([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"QupZilla" => preg_match("/QupZilla[\/\s]([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Coc Coc" => preg_match("/coc_coc_browser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Kindle" => preg_match("/Version\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Iceweasel" => preg_match("/Iceweasel\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Konqueror" => preg_match("/Konqueror\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Iceape" => preg_match("/Iceape\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"SeaMonkey" => preg_match("/SeaMonkey\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Epiphany" => preg_match("/Epiphany\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"360" => preg_match("/QihooBrowser(HD)?\/([\d.]+)/", $userAgent, $matches) ? $matches[2] : '',
"Maxthon" => preg_match("/Maxthon\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"QQBrowser" => preg_match("/QQBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"QQ" => preg_match("/QQ\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Baidu" => preg_match("/BIDUBrowser[\s\/]([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/baiduboxapp\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''),
"UC" => preg_match("/UC?Browser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Sogou" => preg_match("/SE ([\d.X]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/SogouMobileBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : ''),
"115Browser" => preg_match("/115Browser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"TheWorld" => preg_match("/TheWorld ([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"XiaoMi" => preg_match("/MiuiBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Vivo" => preg_match("/VivoBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"OPPO" => preg_match("/HeyTapBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Quark" => preg_match("/Quark\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Qiyu" => preg_match("/Qiyu\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Wechat" => preg_match("/MicroMessenger\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"WechatWork" => preg_match("/wxwork\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Taobao" => preg_match("/AliApp\(TB\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Alipay" => preg_match("/AliApp\(AP\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Weibo" => preg_match("/weibo__([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Douban" => preg_match("/com.douban.frodo\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Suning" => preg_match("/SNEBUY-APP([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"iQiYi" => preg_match("/IqiyiVersion\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"DingTalk" => preg_match("/DingTalk\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Douyin" => preg_match("/app_version\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '',
"Huawei" => preg_match("/Version\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/HuaweiBrowser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/HBPC\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : '')),
"360SE" => isset($browsers_360SE[$chromeVersion]) ? $browsers_360SE[$chromeVersion] : '',
"360EE" => isset($browsers_360EE[$chromeVersion]) ? $browsers_360EE[$chromeVersion] : '',
"Liebao" => preg_match("/LieBaoFast\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (isset($browsers_liebao[$chromeVersion]) ? $browsers_liebao[$chromeVersion] : ''),
"2345Explorer" => preg_match("/2345Explorer\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (preg_match("/Mb2345Browser\/([\d.]+)/", $userAgent, $matches) ? $matches[1] : (isset($browsers_2345[$chromeVersion]) ? $browsers_2345[$chromeVersion] : '')),
];
if (isset($deviceInfo['browser'], $browsersVersion[$deviceInfo['browser']])) {
$deviceInfo['version'] = $browsersVersion[$deviceInfo['browser']];
if ($deviceInfo['version'] == $userAgent) {
$deviceInfo['version'] = '';
}
$deviceInfo['version'] = $browsersVersion[$deviceInfo['browser']];
if ($deviceInfo['version'] == $userAgent) {
$deviceInfo['version'] = '';
}
}
// 修正浏览器版本信息
$chrome = pregMatch('/\S+Browser/', $userAgent);
if ($deviceInfo['browser'] == 'Chrome' && $chrome) {
$deviceInfo['browser'] = $chrome;
$deviceInfo['version'] = pregMatch('/^.*Browser\/([\d.]+).*$/', $userAgent);
if (preg_match('/\S+Browser/', $userAgent, $matches)) {
$chrome = $matches[0];
if ($deviceInfo['browser'] == 'Chrome' && $chrome !== 'Chrome') {
$deviceInfo['browser'] = $chrome;
if (preg_match('/Browser\/([\d.]+)/', $userAgent, $matches)) {
$deviceInfo['version'] = $matches[1];
}
}
}
return $deviceInfo;
}
@@ -964,12 +1016,12 @@ function parse_smiley_shortcode($content) {
}
return $content;
}
?>
<?php
/**
* 短代码实现
*/
function get_article_info($atts) {
function get_article_info($atts)
{
$default_atts = array('id' => '');
$atts = array_merge($default_atts, $atts);
$db = Typecho_Db::get();
@@ -981,11 +1033,10 @@ function get_article_info($atts) {
if (!$post) {
return '未找到文章';
}
// 获取文章对象,避免permalink警告
$widget = Typecho_Widget::widget('Widget_Archive@article_quote_' . $post['cid'], 'cid=' . $post['cid'], null, false);
$permalink = $widget->permalink;
$title = htmlspecialchars($widget->title);
$summary = get_article_summary($widget);
$post = Typecho_Widget::widget('Widget_Abstract_Contents')->push($post);
$permalink = $post['permalink'];
$title = htmlspecialchars($post['title']);
$summary = get_article_summary($post);
$output = '<blockquote class="article-quote">';
$output .= '<div class="t-lg t-line-1">';
@@ -996,17 +1047,37 @@ function get_article_info($atts) {
return $output;
}
// 创建一个新的类来处理内容过滤
class ContentFilter
{
/**
* 过滤内容中的短代码
*
* @param string $content 内容
* @param object $widget 小工具对象
* @param array $lastResult 上一次结果
* @return string 处理后的内容
*/
public static function filterContent($content, $widget, $lastResult)
{
// 先做github短代码和链接替换
// Step 1: 保护代码块
$codeBlocks = [];
$content = preg_replace_callback(
'/```[\s\S]*?```/m', // 匹配 Markdown 代码块(包括多行)
function ($matches) use (&$codeBlocks) {
$placeholder = '<!--CODEBLOCK_' . count($codeBlocks) . '-->';
$codeBlocks[$placeholder] = $matches[0]; // 存储原始代码块内容
return $placeholder;
},
$content
);
// Step 2: 执行现有的短代码处理
// GitHub 短代码替换
$content = preg_replace_callback('/\[github=([\w\-\.]+\/[\w\-\.]+)\]/i', function($matches) {
$repo = htmlspecialchars($matches[1]);
return '<div class="github-card text-center" data-repo="' . $repo . '"><div class="spinner-grow text-primary"></div></div>';
}, $content);
// 只匹配主仓库链接,后面只能是空格、标点、换行或结尾
$content = preg_replace_callback(
'#https://github\.com/([\w\-\.]+/[\w\-\.]+)(?=[\s\.,;:!\?\)\]\}\"\'\n]|$)#i',
function($matches) {
@@ -1016,10 +1087,7 @@ class ContentFilter
$content
);
// 再进行 Markdown 解析
$content = empty($lastResult) ? $widget->markdown($content) : $lastResult;
// alert类短代码批量替换
// alert 类短代码
$alertShortcodes = [
'success' => 'success',
'primary' => 'primary',
@@ -1036,12 +1104,12 @@ class ContentFilter
);
}
// 其他短代码处理
// article 短代码
$content = preg_replace_callback('/\[article\s+([^\]]+)\]/', function($matches) {
$atts = self::parse_atts($matches[1]);
return get_article_info($atts);
}, $content);
// 懒加载图片替换
$themeUrl = Helper::options()->themeUrl;
$loadSvg = $themeUrl . '/assets/img/load.svg';
@@ -1054,7 +1122,8 @@ class ContentFilter
},
$content
);
// collapse折叠面板短代码
// collapse 折叠面板短代码
static $collapseIndex = 0;
$content = preg_replace_callback(
'/\[collapse\s+title=(?:\'([^\']*)\'|\"([^\"]*)\")\](.*?)\[\/collapse\]/is',
@@ -1067,7 +1136,8 @@ class ContentFilter
},
$content
);
// download下载短代码
// download 短代码
$content = preg_replace_callback(
'/\[download\s+file=(?:\'([^\']*)\'|\"([^\"]*)\")\s+size=(?:\'([^\']*)\'|\"([^\"]*)\")\](.*?)\[\/download\]/is',
function($matches) {
@@ -1082,17 +1152,16 @@ class ContentFilter
},
$content
);
// 回复可见短代码
// reply 短代码
$content = preg_replace_callback(
'/\[reply\](.*?)\[\/reply\]/is',
function($matches) use ($widget) {
$show = false;
// 仅在文章页生效
if ($widget instanceof Widget_Archive && $widget->is('single')) {
$user = Typecho_Widget::widget('Widget_User');
$db = Typecho_Db::get();
if ($user->hasLogin) {
// 登录用户,判断是否有通过审核的评论
$hasComment = $db->fetchRow($db->select()->from('table.comments')
->where('cid = ?', $widget->cid)
->where('mail = ?', $user->mail)
@@ -1100,7 +1169,6 @@ class ContentFilter
);
if ($hasComment) $show = true;
} else {
// 未登录,判断IP
$hasComment = $db->fetchRow($db->select()->from('table.comments')
->where('cid = ?', $widget->cid)
->where('status = ?', 'approved')
@@ -1117,9 +1185,19 @@ class ContentFilter
},
$content
);
// Step 3: 恢复代码块
foreach ($codeBlocks as $placeholder => $code) {
$content = str_replace($placeholder, $code, $content);
}
// Step 4: 进行 Markdown 解析
$content = empty($lastResult) ? $widget->markdown($content) : $lastResult;
return $content;
}
// 解析短代码属性
// 解析短代码属性(保持不变)
private static function parse_atts($text) {
$atts = array();
$pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
@@ -1141,7 +1219,8 @@ class ContentFilter
return $atts;
}
}
// 注册钩子,自动处理所有内容输出
// 注册钩子
Typecho_Plugin::factory('Widget_Abstract_Contents')->content = array('ContentFilter', 'filterContent');
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('ContentFilter', 'filterContent');
@@ -1309,3 +1388,75 @@ function get_article_summary($post, $length = 100) {
}
return $text;
}
/**
* 获取站点统计信息(包含当前登录用户信息)
* @return array
*/
function get_site_statistics() {
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
// 获取当前登录用户对象
$currentUser = Typecho_Widget::widget('Widget_User');
// 如果用户未登录,获取默认uid=1的用户
if (!$currentUser->hasLogin()) {
$defaultUser = $db->fetchRow($db->select()->from('table.users')->where('uid = ?', 1));
$email = $defaultUser['mail'];
$nickname = $defaultUser['screenName'];
} else {
// 用户已登录,使用当前用户信息
$email = $currentUser->mail;
$nickname = $currentUser->screenName;
}
// 获取用户设置的 Gravatar 镜像
$cnavatar = Helper::options()->cnavatar ? Helper::options()->cnavatar : 'https://cravatar.cn/avatar/';
$hash = md5($email);
$avatar = rtrim($cnavatar, '/') . '/' . $hash . '?s=80&d=identicon';
// 其余统计信息保持不变
$userCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.users'))->num;
$postCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.contents')->where('type = ?', 'post')->where('status = ?', 'publish'))->num;
$commentCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.comments'))->num;
$totalViews = $db->fetchObject(
$db->select(array('SUM(views)' => 'viewsum'))->from('table.contents')->where('type = ?', 'post')
)->viewsum;
if ($totalViews === null) $totalViews = 0;
return [
'email' => $email,
'nickname' => $nickname,
'avatar' => $avatar,
'userCount' => $userCount,
'postCount' => $postCount,
'commentCount' => $commentCount,
'totalViews' => $totalViews,
'isLogin' => $currentUser->hasLogin() // 添加一个是否登录的标志
];
}
// Typecho AJAX 登录接口,支持前端 AJAX 提交并返回 JSON
//if (!empty($_GET['ajaxLogin']) && $_GET['ajaxLogin'] == 1) {
// header('Content-Type: application/json; charset=utf-8');
// if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
// echo json_encode(['success' => false, 'msg' => '请求方式错误']);
// exit;
// }
// $name = isset($_POST['name']) ? trim($_POST['name']) : '';
// $password = isset($_POST['password']) ? $_POST['password'] : '';
// $referer = isset($_POST['referer']) ? $_POST['referer'] : '/';
// if (!$name || !$password) {
// echo json_encode(['success' => false, 'msg' => '用户名或密码不能为空']);
// exit;
// }
// $user = Typecho_Widget::widget('Widget_User');
// try {
// $user->login($name, $password, isset($_POST['remember']) ? 1 : 0);
// echo json_encode(['success' => true, 'msg' => '登录成功', 'redirect' => $referer]);
// } catch (Typecho_Exception $e) {
// echo json_encode(['success' => false, 'msg' => $e->getMessage()]);
// }
// exit;
//}
+49 -9
View File
@@ -75,7 +75,7 @@
</li>
<?php \Widget\Contents\Page\Rows::alloc()->to($pages); ?>
<?php while ($pages->next()): ?>
<li <?php if ($this->is('page', $pages->slug)): ?> class='current-menu-item current_page_item menu-current<?php endif; ?> menu-item menu-item-type-post_type menu-item-object-page '>
<li class="menu-item menu-item-type-post_type menu-item-object-page<?php if ($this->is('page', $pages->slug)) echo ' current-menu-item current_page_item menu-current'; ?>">
<a class='ww'
href="<?php $pages->permalink(); ?>"
title="<?php $pages->title(); ?>">
@@ -83,6 +83,15 @@
</a>
</li>
<?php endwhile; ?>
<?php if($this->user->hasLogin()): ?>
<li>
<a data-bs-toggle="tooltip" title="用户中心" href="/admin" target="_blank">
<img alt="用户中心" src="<?php $stats = get_site_statistics();echo $stats['avatar']; ?>" class="min-avatar">
</a>
</li>
<?php else: ?>
<li><a data-no-instant data-bs-toggle="tooltip" title="登入" data-title="登入" href="javascript:void(0)" class="pk-modal-toggle" data-once-load="true" data-url="<?php echo get_correct_url('/login/'); ?>"><i class="fa fa-right-to-bracket"></i></a></li>
<?php endif; ?>
<li><a class="colorMode" data-bs-toggle="tooltip" title="模式切换" href="javascript:void(0)"><i class="fa fa-circle-half-stroke"></i></a></li>
<li><a class="search-modal-btn" data-bs-toggle="tooltip" title="搜索" href="javascript:void(0)"><i class="fa fa-search"></i></a></li>
</ul>
@@ -130,8 +139,8 @@
<?php endwhile; ?>
<li class='menu-item menu-item-type-post_type menu-item-object-page'>
<span><a href="#">分类</a>
<a href="#menu-sub-689" data-bs-toggle="collapse"><i class="fa fa-chevron-down t-sm ml-1 menu-sub-icon"></i></a></span>
<ul id="menu-sub-689" class="sub-menu collapse">
<a href="#menu" data-bs-toggle="collapse"><i class="fa fa-chevron-down t-sm ml-1 menu-sub-icon"></i></a></span>
<ul id="menu" class="sub-menu collapse">
<?php $categories = Typecho_Widget::widget('Widget_Metas_Category_List'); ?>
<?php while($categories->next()): ?>
<li class="menu-item menu-item-type-post_type menu-item-object-page menu-item-child">
@@ -143,6 +152,15 @@
</li>
<?php endwhile; ?>
</ul>
<?php if($this->user->hasLogin()): ?>
<li>
<a data-bs-toggle="tooltip" title="用户中心" href="/admin" target="_blank">
<img alt="用户中心" src="<?php $stats = get_site_statistics();echo $stats['avatar']; ?>" class="min-avatar">
</a>
</li>
<?php else: ?>
<li><a data-no-instant data-bs-toggle="tooltip" title="登入" data-title="登入" href="javascript:void(0)" class="pk-modal-toggle" data-once-load="true" data-url="<?php echo get_correct_url('/login/'); ?>"><i class="fa fa-right-to-bracket"></i></a></li>
<?php endif; ?>
</ul>
</nav>
</div>
@@ -159,12 +177,34 @@
<div data-swiper="init" data-swiper-class="global-top-notice-swiper" data-swiper-args='{"direction":"vertical","autoplay":{"delay":3000,"disableOnInteraction":false},"loop":true}'>
<div class="swiper global-top-notice-swiper">
<div class="swiper-wrapper">
<div class="swiper-slide t-line-1">
<a class="ta3" data-no-instant href="javascript:void(0)">
<span class="notice-icon"><i class="fa-regular fa-bell"></i></span>
<span><?php $this->options->gonggao(); ?></span>
</a>
</div>
<?php
// 获取公告内容
$gonggao = $this->options->gonggao;
if ($gonggao) {
// 按行分割
$lines = explode("\n", $gonggao);
foreach ($lines as $line) {
$parts = explode('|', $line);
// 只处理格式正确的行
if (count($parts) >= 3) {
$title = trim($parts[0]);
$url = trim($parts[1]);
$icon = trim($parts[2]);
// 链接为空时使用 javascript:void(0)
$href = $url !== '' ? htmlspecialchars($url) : 'javascript:void(0)';
// 图标为空时使用默认
$icon_class = $icon !== '' ? $icon : 'fa-regular fa-bell';
// 输出 HTML
echo '<div class="swiper-slide t-line-1">';
echo '<a class="ta3" data-no-instant href="' . $href . '">';
echo '<span class="notice-icon"><i class="' . $icon_class . '"></i></span>';
echo '<span>' . htmlspecialchars($title) . '</span>';
echo '</a>';
echo '</div>';
}
}
}
?>
</div>
</div>
</div>
+11 -3
View File
@@ -1,10 +1,11 @@
<?php
/**
* Pouck theme for Typecho
*
* 老孙移植
*
* @package Typecho Pouck Theme
* @author 老孙博客
* @version 1.1.9
* @version 1.2.2
* @link http://www.imsun.org
*/
@@ -13,8 +14,13 @@ $this->need('header.php');
$this->need('sticky.php');
?>
<div class="row row-cols-1">
<?php if ($this->options->showsidebar): ?>
<div class="col-lg-8 col-md-12 animated fadeInLeft ">
<div class="animated fadeInLeft ">
<?php else: ?>
<div class="col-lg-12 col-md-12">
<div class="row box-plr15">
<?php endif; ?>
<div> <!--文章列表-->
<div id="posts">
<?php if ($this->options->listmodel): ?>
@@ -82,7 +88,7 @@ if ($pageprev == '1' && $this->have()):
'wrapClass' => 'pagination comment-ajax-load',
'itemTag' => 'li',
'textTag' => 'span',
'currentClass' => 'active',
'currentClass' => 'cur',
'prevClass' => 'prev',
'nextClass' => 'next'
)); ?>
@@ -96,5 +102,7 @@ if ($pageprev == '1' && $this->have()):
</div>
</div>
</div>
<?php if ($this->options->showsidebar): ?>
<?php $this->need('sidebar.php'); ?>
<?php endif; ?>
<?php $this->need('footer.php'); ?>
+8 -2
View File
@@ -17,7 +17,11 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
<?php $commenters = getAllCommenters(); ?>
<div id="page-reads">
<div id="page" class="row row-cols-1">
<?php if ($this->options->showsidebar): ?>
<div id="posts" class="col-lg-8 col-md-12 animated fadeInLeft ">
<?php else: ?>
<div id="posts" class="col-lg-12 col-md-12">
<?php endif; ?>
<div class="p-block puock-text">
<h2 class="t-lg"><?php $this->title() ?></h2>
<div class="mt20 row pd-links">
@@ -44,5 +48,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
</div>
</div>
</div>
<?php $this->need('sidebar.php'); ?>
<?php $this->need('footer.php'); ?>
<?php if ($this->options->showsidebar): ?>
<?php $this->need('sidebar.php'); ?>
<?php endif; ?>
<?php $this->need('footer.php'); ?>
+7 -1
View File
@@ -15,7 +15,11 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
</nav>
<div id="page-moments">
<div class="row">
<?php if ($this->options->showsidebar): ?>
<div id="posts" class="col-lg-8 col-md-12 animated fadeInLeft ">
<?php else: ?>
<div id="posts" class="col-lg-12 col-md-12">
<?php endif; ?>
<?php $tooot = $this->fields->tooot ? $this->fields->tooot : 'https://www.imsun.org/toot.json'; ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/15.0.12/marked.min.js" integrity="sha512-rCQgmUulW6f6QegOvTntKKb5IAoxTpGVCdWqYjkXEpzAns6XUFs8NKVqWe+KQpctp/EoRSFSuykVputqknLYMg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.5/css/lightbox.min.css" integrity="sha512-xtV3HfYNbQXS/1R1jP53KbFcU9WXiSA1RFKzl5hRlJgdOJm4OxHCWYpskm6lN0xp0XtKGpAfVShpbvlFH3MDAA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
@@ -161,8 +165,10 @@ img {
height: 480px;
}
}
</style>
</style>
<?php if ($this->options->showsidebar): ?>
<?php $this->need('sidebar.php'); ?>
<?php endif; ?>
</div>
</div>
<?php $this->need('footer.php'); ?>
+6
View File
@@ -10,7 +10,11 @@
</div>
<div id="page-empty">
<div id="page" class="row row-cols-1">
<?php if ($this->options->showsidebar): ?>
<div id="post-main" class="col-lg-8 col-md-12 animated fadeInLeft ">
<?php else: ?>
<div id="post-main" class="col-lg-12 col-md-12">
<?php endif; ?>
<div class="p-block"><div>
<h1 id="post-title" class="mb-0 puock-text t-xxl"><?php $this->title() ?></h1>
</div>
@@ -60,5 +64,7 @@ if($days > 180){
<?php $this->need('comments.php'); ?>
<?php endif; ?>
</div>
<?php if ($this->options->showsidebar): ?>
<?php $this->need('sidebar.php'); ?>
<?php endif; ?>
<?php $this->need('footer.php'); ?>
+6
View File
@@ -15,7 +15,11 @@
<div class="puock-text p-block t-md ad-page-top"><?php $this->options->articletop(); ?></div>
<?php endif; ?>
<div class="row row-cols-1 post-row">
<?php if ($this->options->showsidebar): ?>
<div id="post-main" class="col-lg-8 col-md-12 animated fadeInLeft ">
<?php else: ?>
<div id="post-main" class="col-lg-12 col-md-12">
<?php endif; ?>
<div class="p-block"><div>
<h1 id="post-title" class="mb-0 puock-text t-xxl"><?php $this->title() ?></h1>
</div>
@@ -216,5 +220,7 @@ if($days > 180){
<div class="puock-text p-block t-md ad-comment-top"><?php $this->options->articlefoot(); ?></div>
<?php endif; ?>
</div>
<?php if ($this->options->showsidebar): ?>
<?php $this->need('sidebar.php'); ?>
<?php endif; ?>
<?php $this->need('footer.php'); ?>
+9 -32
View File
@@ -18,57 +18,34 @@
</div>
<?php endif; ?>
<!-- 个人信息 -->
<?php
// 获取数据库连接
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
// 1. 获取uid=1的用户信息
$user = $db->fetchRow($db->select()->from('table.users')->where('uid = ?', 1));
$email = $user['mail'];
$nickname = $user['screenName'];
// 获取用户设置的 Gravatar 镜像
$cnavatar = Helper::options()->cnavatar ? Helper::options()->cnavatar : 'https://cravatar.cn/avatar/';
$hash = md5($email);
$avatar = rtrim($cnavatar, '/') . '/' . $hash . '?s=80&d=identicon';
// 2. 获取用户总数
$userCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.users'))->num;
// 3. 获取文章总数(只统计 type='post' 且 status='publish'
$postCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.contents')->where('type = ?', 'post')->where('status = ?', 'publish'))->num;
// 4. 获取评论总数
$commentCount = $db->fetchObject($db->select(array('COUNT(*)' => 'num'))->from('table.comments'))->num;
// 5. 获取文章浏览总量(累加所有文章的 views 字段)
$totalViews = $db->fetchObject(
$db->select(array('SUM(views)' => 'viewsum'))->from('table.contents')->where('type = ?', 'post')
)->viewsum;
if ($totalViews === null) $totalViews = 0;
?>
<!-- 个人信息 -->
<?php $stats = get_site_statistics(); ?>
<?php if (!empty($this->options->sidebarBlock) && in_array('ShowAdmin', $this->options->sidebarBlock)): ?>
<div class="widget-puock-author widget">
<div class="header" style="background-image: url('<?php echo !empty($this->options->bgUrl) ? $this->options->bgUrl : $this->options->themeUrl('assets/img/cover.png'); ?>')">
<img src='<?php $this->options->themeUrl('assets/img/load.svg'); ?>' class='lazy avatar' data-src='<?php echo $avatar; ?>' >
<img src='<?php $this->options->themeUrl('assets/img/load.svg'); ?>' class='lazy avatar' data-src='<?php echo $stats['avatar']; ?>' >
</div>
<div class="content t-md puock-text">
<div class="text-center p-2">
<div class="t-lg"><?php echo $nickname; ?></div>
<div class="t-lg"><?php echo $stats['nickname']; ?></div>
<div class="mt10 t-sm"><?php $this->options->description(); ?></div>
</div>
<div class="row mt10">
<div class="col-3 text-center">
<div class="c-sub t-sm">用户数</div>
<div><?php echo $userCount; ?></div>
<div><?php echo $stats['userCount']; ?></div>
</div>
<div class="col-3 text-center">
<div class="c-sub t-sm">文章数</div>
<div><?php echo $postCount; ?></div>
<div><?php echo $stats['postCount']; ?></div>
</div>
<div class="col-3 text-center">
<div class="c-sub t-sm">评论数</div>
<div><?php echo $commentCount; ?></div>
<div><?php echo $stats['commentCount']; ?></div>
</div>
<div class="col-3 text-center">
<div class="c-sub t-sm">阅读量</div>
<div><?php echo $totalViews; ?></div>
<div><?php echo $stats['totalViews']; ?></div>
</div>
</div>
</div>
@@ -132,7 +109,7 @@ if ($totalViews === null) $totalViews = 0;
<div class="pk-widget p-block">
<div>
<span class="t-lg border-bottom border-primary puock-text pb-2">
<i class="fa fa-chart-simple mr-1"></i>热门文章
<i class="fa-solid fa-fire"></i> 热门文章
</span>
</div>
<div class="mt20">