mirror of
https://github.com/jkjoy/Typecho-Theme-Puock.git
synced 2026-06-28 02:44:12 +00:00
Compare commits
17 Commits
c313e29d99
..
1.2.3
| Author | SHA1 | Date | |
|---|---|---|---|
| a106080633 | |||
| 57aeb81c62 | |||
| 4dcaefb291 | |||
| 0017693c81 | |||
| d268bd10bb | |||
| b416e58596 | |||
| 5d67235be0 | |||
| 134bb0d66c | |||
| 8afb1934dd | |||
| b73e465a79 | |||
| ae990a722b | |||
| 35806ea2fb | |||
| 3bb53afcea | |||
| 9f389fbb34 | |||
| 4c1c795dc2 | |||
| b3b1407647 | |||
| 6b9cc81754 |
@@ -1 +1,3 @@
|
||||
*.html
|
||||
/.vercel
|
||||
page-talks copy.php
|
||||
|
||||
@@ -32,10 +32,51 @@
|
||||
- 增加系统显示设置
|
||||
- 增加浏览器信息显示设置
|
||||
- 修复pjax模式下的404跳转
|
||||
|
||||
---
|
||||
|
||||
### 2024-07-07 表情短代码解析集成
|
||||
- 2025.07.07 表情短代码解析集成
|
||||
- 1.1.5
|
||||
|
||||
- 将表情短代码(如 :smile:)自动解析为图片表情,集成到主题评论内容输出中。
|
||||
- 将表情短代码(如 :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。
|
||||
|
||||
- 新增支持[success]、[primary]、[danger]、[warning]、[info]、[dark]等alert类短代码,自动渲染为对应的Bootstrap风格提示框。
|
||||
- 新增支持[collapse title='xxx']内容[/collapse]折叠面板短代码,自动渲染为带唯一ID的Bootstrap折叠结构。
|
||||
- 新增支持[download file='xxx.zip' size='12MB']文件地址[/download]下载短代码,自动渲染为带文件名、大小、声明和下载地址的下载信息块。
|
||||
- 新增支持[reply]隐藏内容[/reply]回复可见短代码,未满足条件时前端显示提示,已评论且审核通过后显示隐藏内容。
|
||||
|
||||
- 1.2.0
|
||||
|
||||
- 新增支持首页登录
|
||||
|
||||
- 2025.08.02
|
||||
|
||||
- 1.2.2
|
||||
|
||||
- 增加侧边栏显示的全局开关
|
||||
- 修复代码块中的短代码解析问题
|
||||
- 新增一个随机文章阅读的独立页面
|
||||
|
||||
- 2025.08.03
|
||||
|
||||
- 1.2.3
|
||||
|
||||
- 修复随机页面在 PJAX 模式下的缓存问题
|
||||
- 在随机页面添加缓存控制头,防止 PJAX 缓存
|
||||
- 在 JavaScript 中添加随机页面检测,强制刷新确保获取新文章
|
||||
- 修复后端登录验证问题,确保密码错误时返回正确的错误信息
|
||||
- 重新设计随机页面模板,添加完整的页面结构和加载动画
|
||||
- 修复随机页面重定向问题,使用 JavaScript 跳转避免缓存
|
||||
- 修复随机页面 JavaScript 错误,移除不存在的 InstantClick.preload 方法调用
|
||||
- 优化随机页面跳转逻辑,确保在 PJAX 模式下也能正常自动跳转
|
||||
+15
-5
@@ -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('«', '»', 1, '...', array(
|
||||
<div class="mt20 p-flex-s-right" data-no-instant>
|
||||
<?php $this->pageNav('«', '»', 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'); ?>
|
||||
File diff suppressed because one or more lines are too long
+65
-33
@@ -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('');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -997,6 +1041,13 @@ class Puock {
|
||||
const title = el.attr("title") || el.data("title") || '提示';
|
||||
const url = el.data("url");
|
||||
const onceLoad = el.data("once-load")
|
||||
|
||||
// 检查 url 是否存在,避免 SparkMD5 错误
|
||||
if (!url) {
|
||||
console.warn('Modal toggle: url is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
const id = SparkMD5.hash(url)
|
||||
if (onceLoad && this.data.modalStorage[id]) {
|
||||
this.modalLoadRender(id, this.data.modalStorage[id], title, noTitle, noPadding)
|
||||
@@ -1141,25 +1192,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,
|
||||
|
||||
@@ -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'
|
||||
)); ?>
|
||||
|
||||
+6
-61
@@ -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> 登录
|
||||
</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> 快捷登录
|
||||
</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> 发布评论
|
||||
</button>
|
||||
@@ -171,7 +171,7 @@
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="t-sm c-sub">
|
||||
<span><?php $comments->date('Y-m-d H:i:s'); ?></span>
|
||||
<span><?php echo friendly_date($comments->created); ?></span>
|
||||
<a rel="nofollow" class="hide-info animated bounceIn c-sub-a t-sm ml-1 comment-reply" href="javascript:void(0);" data-coid="<?php echo $comments->coid; ?>"><span class="comment-reply-text"><i class="fa fa-share-from-square"></i>回复</span></a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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
@@ -68,7 +68,7 @@
|
||||
© <?php echo date('Y'); ?> <a href="<?php $this->options->siteUrl(); ?>"><?php $this->options->title(); ?></a>
|
||||
<div class="fs12 mt10 c-sub">
|
||||
<span> 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.3" href="https://github.com/jkjoy/typecho-theme-puock">Puock</a>
|
||||
</span>
|
||||
<span> 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>
|
||||
|
||||
+687
-184
@@ -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());
|
||||
}
|
||||
|
||||
@@ -83,6 +92,16 @@ function themeFields($layout) {
|
||||
$layout->addItem($cover);
|
||||
}
|
||||
|
||||
/** 头像镜像加速全局设置
|
||||
* @param $email
|
||||
* @param $size
|
||||
* @param $default
|
||||
* @return string
|
||||
*/
|
||||
$options = Typecho_Widget::widget('Widget_Options');
|
||||
$gravatarPrefix = empty($options->cnavatar) ? 'https://cravatar.cn/avatar/' : $options->cnavatar;
|
||||
define('__TYPECHO_GRAVATAR_PREFIX__', $gravatarPrefix);
|
||||
|
||||
/*
|
||||
* 文章浏览数统计
|
||||
*/
|
||||
@@ -166,22 +185,17 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['likeup']) && isset($_
|
||||
* 随机封面
|
||||
*/
|
||||
function getPostCover($content, $cid, $fields = null) {
|
||||
// 首先检查是否有自定义封面字段
|
||||
// 优先使用自定义封面字段
|
||||
if ($fields && !empty($fields->cover)) {
|
||||
return $fields->cover;
|
||||
}
|
||||
|
||||
// 尝试从内容中提取第一张图片
|
||||
preg_match_all('/<img.+src=[\'"]([^\'"]+)[\'"].*>/i', $content, $matches);
|
||||
|
||||
if (!empty($matches[1][0])) {
|
||||
// 如果找到图片,返回第一张图片URL
|
||||
return $matches[1][0];
|
||||
} else {
|
||||
// 如果没有图片,使用随机封面(基于文章ID的伪随机)
|
||||
$coverNumber = ($cid % 8) + 1; // 得到1-8的值
|
||||
return Helper::options()->themeUrl . '/assets/img/random/' . $coverNumber . '.jpg';
|
||||
// 从原始内容中提取第一张图片(不管src是什么)
|
||||
if (preg_match('/<img[^>]+src=["\']([^"\']+\.(?:jpg|jpeg|png|webp))["\'][^>]*>/i', $content, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
// 没有图片则用随机封面
|
||||
$coverNumber = ($cid % 8) + 1;
|
||||
return Helper::options()->themeUrl . '/assets/img/random/' . $coverNumber . '.jpg';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,14 +205,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 镜像设置
|
||||
@@ -302,92 +332,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'
|
||||
],
|
||||
@@ -398,17 +429,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',
|
||||
@@ -417,27 +446,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 ($systemVersion[$deviceInfo['system']]) {
|
||||
|
||||
if (isset($deviceInfo['system']) && $deviceInfo['system'] !== "" && isset($systemVersion[$deviceInfo['system']])) {
|
||||
$deviceInfo['systemVersion'] = $systemVersion[$deviceInfo['system']];
|
||||
if ($deviceInfo['systemVersion'] == $userAgent) $deviceInfo['systemVersion'] = '';
|
||||
if ($deviceInfo['systemVersion'] == $userAgent) {
|
||||
$deviceInfo['systemVersion'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 浏览器版本信息
|
||||
$browsers_360SE = [
|
||||
108 => '14.0',
|
||||
@@ -462,7 +522,7 @@ function getBrowsersInfo($userAgent) {
|
||||
30 => '7.5',
|
||||
];
|
||||
$browsers_liebao = [
|
||||
57 => '6.5',
|
||||
57 => '6.5',
|
||||
49 => '6.0',
|
||||
46 => '5.9',
|
||||
42 => '5.3',
|
||||
@@ -474,80 +534,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 ($browsersVersion[$deviceInfo['browser']]) {
|
||||
$deviceInfo["version"] = $browsersVersion[$deviceInfo['browser']];
|
||||
if ($deviceInfo["version"] == $userAgent) $deviceInfo['version'] = '';
|
||||
|
||||
if (isset($deviceInfo['browser'], $browsersVersion[$deviceInfo['browser']])) {
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -960,9 +1021,451 @@ function parse_smiley_shortcode($content) {
|
||||
];
|
||||
$themeUrl = Helper::options()->themeUrl . '/assets/img/smiley/';
|
||||
foreach ($smileys as $code => $img) {
|
||||
$imgTag = '<img class="smiley-img" src="' . $themeUrl . $img . '" alt="' . $code . '" title="表情" style="width:24px;height:24px;vertical-align:middle;" />';
|
||||
$imgTag = '<img class="smiley-img" src="' . $themeUrl . $img . '" alt="' . $code . '" title="表情" style="width:16px;height:16px;vertical-align:middle;" />';
|
||||
$content = str_replace($code, $imgTag, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* 短代码实现
|
||||
*/
|
||||
function get_article_info($atts)
|
||||
{
|
||||
$default_atts = array('id' => '');
|
||||
$atts = array_merge($default_atts, $atts);
|
||||
$db = Typecho_Db::get();
|
||||
if (!empty($atts['id'])) {
|
||||
$post = $db->fetchRow($db->select()->from('table.contents')->where('cid = ?', $atts['id'])->limit(1));
|
||||
} else {
|
||||
return '请提供文章ID';
|
||||
}
|
||||
if (!$post) {
|
||||
return '未找到文章';
|
||||
}
|
||||
$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">';
|
||||
$output .= '<a class="a-link" title="' . $title . '" href="' . $permalink . '" target="_blank">' . $title . '</a>';
|
||||
$output .= '</div>';
|
||||
$output .= '<div class="t-md c-sub text-2line">' . htmlspecialchars($summary) . '</div>';
|
||||
$output .= '</blockquote>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
class ContentFilter
|
||||
{
|
||||
/**
|
||||
* 过滤内容中的短代码
|
||||
*
|
||||
* @param string $content 内容
|
||||
* @param object $widget 小工具对象
|
||||
* @param array $lastResult 上一次结果
|
||||
* @return string 处理后的内容
|
||||
*/
|
||||
public static function filterContent($content, $widget, $lastResult)
|
||||
{
|
||||
// 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) {
|
||||
$repo = htmlspecialchars($matches[1]);
|
||||
return '<div class="github-card text-center" data-repo="' . $repo . '"><div class="spinner-grow text-primary"></div></div>';
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
// alert 类短代码
|
||||
$alertShortcodes = [
|
||||
'success' => 'success',
|
||||
'primary' => 'primary',
|
||||
'danger' => 'danger',
|
||||
'warning' => 'warning',
|
||||
'info' => 'info',
|
||||
'dark' => 'dark',
|
||||
];
|
||||
foreach ($alertShortcodes as $shortcode => $class) {
|
||||
$content = preg_replace(
|
||||
'/\[' . $shortcode . '\](.*?)\[\/' . $shortcode . '\]/is',
|
||||
'<div class="alert alert-' . $class . '">$1</div>',
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
// 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';
|
||||
$title = htmlspecialchars($widget->title);
|
||||
$content = preg_replace_callback(
|
||||
'/<img\s+[^>]*src=["\"]([^"\"]+\.(?:jpg|jpeg|png|webp))["\"][^>]*>/i',
|
||||
function($matches) use ($title, $loadSvg) {
|
||||
$imgUrl = $matches[1];
|
||||
return '<img title="' . $title . '" alt="' . $title . '" decoding="async" data-src="' . $imgUrl . '" data-lazy="true" src="' . $loadSvg . '" />';
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
// collapse 折叠面板短代码
|
||||
static $collapseIndex = 0;
|
||||
$content = preg_replace_callback(
|
||||
'/\[collapse\s+title=(?:\'([^\']*)\'|\"([^\"]*)\")\](.*?)\[\/collapse\]/is',
|
||||
function($matches) use (&$collapseIndex) {
|
||||
$title = $matches[1] !== '' ? $matches[1] : $matches[2];
|
||||
$body = $matches[3];
|
||||
$collapseIndex++;
|
||||
$uniqid = 'collapse-' . mt_rand(100,999) . '-' . $collapseIndex;
|
||||
return '<div class="pk-sc-collapse"><a class="btn btn-primary btn-sm" data-bs-toggle="collapse" href="#' . $uniqid . '" role="button" aria-expanded="false" aria-controls="' . $uniqid . '"><i class="fa fa-angle-up"></i> ' . htmlspecialchars($title) . '</a></div><div class="collapse" id="' . $uniqid . '">' . $body . '</div>';
|
||||
},
|
||||
$content
|
||||
);
|
||||
|
||||
// download 短代码
|
||||
$content = preg_replace_callback(
|
||||
'/\[download\s+file=(?:\'([^\']*)\'|\"([^\"]*)\")\s+size=(?:\'([^\']*)\'|\"([^\"]*)\")\](.*?)\[\/download\]/is',
|
||||
function($matches) {
|
||||
$file = $matches[1] !== '' ? $matches[1] : $matches[2];
|
||||
$size = $matches[3] !== '' ? $matches[3] : $matches[4];
|
||||
$url = $matches[5];
|
||||
return '<div class="p-block p-down-box">'
|
||||
. "<div class='mb15'><i class='fa fa-file-zipper'></i> <span> 文件名称:" . htmlspecialchars($file) . "</span></div>"
|
||||
. "<div class='mb15'><i class='fa fa-download'></i> <span> 文件大小:" . htmlspecialchars($size) . "</span></div>"
|
||||
. "<div class='mb15'><i class='fa-regular fa-bell'></i> <span> 下载声明:本站部分资源来自于网络收集,若侵犯了你的隐私或版权,请及时联系我们删除有关信息。</span></div>"
|
||||
. "<div><i class='fa fa-link'></i><span> 下载地址:<a href='" . htmlspecialchars($url) . "' target='_blank'>点此下载</a> </span></div></p></div>";
|
||||
},
|
||||
$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)
|
||||
->where('status = ?', 'approved')
|
||||
);
|
||||
if ($hasComment) $show = true;
|
||||
} else {
|
||||
$hasComment = $db->fetchRow($db->select()->from('table.comments')
|
||||
->where('cid = ?', $widget->cid)
|
||||
->where('status = ?', 'approved')
|
||||
->where('ip = ?', $widget->request->getIp())
|
||||
);
|
||||
if ($hasComment) $show = true;
|
||||
}
|
||||
}
|
||||
if ($show) {
|
||||
return '<div class="reply-visible">' . $matches[1] . '</div>';
|
||||
} else {
|
||||
return "<div class='alert alert-primary alert-outline'><span class='c-sub fs14'><i class='fa-regular fa-eye'></i> 此处含有隐藏内容,请提交评论并审核通过刷新后即可查看!</span></div>";
|
||||
}
|
||||
},
|
||||
$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|$)/';
|
||||
$text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
|
||||
if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) {
|
||||
foreach ($match as $m) {
|
||||
if (!empty($m[1]))
|
||||
$atts[strtolower($m[1])] = stripcslashes($m[2]);
|
||||
elseif (!empty($m[3]))
|
||||
$atts[strtolower($m[3])] = stripcslashes($m[4]);
|
||||
elseif (!empty($m[5]))
|
||||
$atts[strtolower($m[5])] = stripcslashes($m[6]);
|
||||
elseif (isset($m[7]) && strlen($m[7]))
|
||||
$atts[] = stripcslashes($m[7]);
|
||||
elseif (isset($m[8]))
|
||||
$atts[] = stripcslashes($m[8]);
|
||||
}
|
||||
}
|
||||
return $atts;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册钩子
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->content = array('ContentFilter', 'filterContent');
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array('ContentFilter', 'filterContent');
|
||||
|
||||
// 编辑器按钮类
|
||||
class EditorButton {
|
||||
public static function render()
|
||||
{
|
||||
echo <<<EOF
|
||||
<style>
|
||||
#wmd-button-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
min-height: 40px;
|
||||
height: auto !important;
|
||||
overflow: visible;
|
||||
}
|
||||
#text, .wmd-input {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#wmd-button-row').append('<li class="wmd-button" id="wmd-article-button" title="插入文章引用"><span style="background: none;"><svg t="1687164718203" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4158" width="20" height="20"><path d="M810.666667 213.333333H213.333333c-46.933333 0-85.333333 38.4-85.333333 85.333334v426.666666c0 46.933333 38.4 85.333333 85.333333 85.333334h597.333334c46.933333 0 85.333333-38.4 85.333333-85.333334V298.666667c0-46.933333-38.4-85.333333-85.333333-85.333334z m0 512H213.333333V298.666667h597.333334v426.666666z" p-id="4159"></path><path d="M298.666667 384h426.666666v85.333333H298.666667zM298.666667 554.666667h426.666666v85.333333H298.666667z" p-id="4160"></path></svg></span></li>');
|
||||
// 新增github按钮
|
||||
$('#wmd-button-row').append('<li class="wmd-button" id="wmd-github-button" title="插入GitHub仓库"><span style="background: none;"><svg t="1714380000000" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8888" width="20" height="20"><path d="M512 76C276.6 76 80 272.6 80 508c0 190.2 123.2 351.4 294 408.6 21.4 4 29.2-9.2 29.2-20.4 0-10-0.4-43-0.6-78.2-119.6 26-144.8-57.6-144.8-57.6-19.4-49.2-47.4-62.2-47.4-62.2-38.8-26.6 2.8-26 2.8-26 42.8 3 65.4 44 65.4 44 38.2 65.4 100.2 46.5 124.6 35.6 3.8-27.7 15-46.5 27.2-57.2-95.4-10.8-195.8-47.7-195.8-212.4 0-46.9 16.8-85.3 44.2-115.4-4.4-10.8-19.2-54.2 4.2-113 0 0 36.2-11.6 118.8 44.1 34.4-9.6 71.4-14.4 108.2-14.6 36.8 0.2 73.8 5 108.2 14.6 82.6-55.7 118.8-44.1 118.8-44.1 23.4 58.8 8.6 102.2 4.2 113 27.4 30.1 44.2 68.5 44.2 115.4 0 164.9-100.6 201.5-196.2 212.1 15.4 13.2 29.2 39.2 29.2 79.1 0 57.1-0.5 103.2-0.5 117.3 0 11.3 7.7 24.6 29.4 20.4C820.8 859.4 944 698.2 944 508 944 272.6 747.4 76 512 76z" p-id="8889" fill="#181616"></path></svg></span></li>');
|
||||
|
||||
// alert类按钮
|
||||
var alertTypes = [
|
||||
{id: 'success', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="rgba(100,205,138,1)"><path d="M9.9997 15.1709L19.1921 5.97852L20.6063 7.39273L9.9997 17.9993L3.63574 11.6354L5.04996 10.2212L9.9997 15.1709Z"></path></svg>', label: '成功'},
|
||||
{id: 'primary', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="rgba(240,187,64,1)"><path d="M11.9996 0.5L16.2256 6.68342L23.4123 8.7918L18.8374 14.7217L19.053 22.2082L11.9996 19.6897L4.94617 22.2082L5.16179 14.7217L0.586914 8.7918L7.7736 6.68342L11.9996 0.5ZM9.99959 12H7.99959C7.99959 14.2091 9.79045 16 11.9996 16C14.1418 16 15.8907 14.316 15.9947 12.1996L15.9996 12H13.9996C13.9996 13.1046 13.1042 14 11.9996 14C10.9452 14 10.0814 13.1841 10.0051 12.1493L9.99959 12Z"></path></svg>', label: '重要'},
|
||||
{id: 'danger', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="rgba(251,12,12,1)"><path d="M10.5859 12L2.79297 4.20706L4.20718 2.79285L12.0001 10.5857L19.793 2.79285L21.2072 4.20706L13.4143 12L21.2072 19.7928L19.793 21.2071L12.0001 13.4142L4.20718 21.2071L2.79297 19.7928L10.5859 12Z"></path></svg>', label: '危险'},
|
||||
{id: 'warning', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M4.00001 20V14C4.00001 9.58172 7.58173 6 12 6C16.4183 6 20 9.58172 20 14V20H21V22H3.00001V20H4.00001ZM6.00001 14H8.00001C8.00001 11.7909 9.79087 10 12 10V8C8.6863 8 6.00001 10.6863 6.00001 14ZM11 2H13V5H11V2ZM19.7782 4.80761L21.1924 6.22183L19.0711 8.34315L17.6569 6.92893L19.7782 4.80761ZM2.80762 6.22183L4.22183 4.80761L6.34315 6.92893L4.92894 8.34315L2.80762 6.22183Z"></path></svg>', label: '警告'},
|
||||
{id: 'info', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM11 11V17H13V11H11ZM11 7V9H13V7H11Z"></path></svg>', label: '关于'},
|
||||
{id: 'dark', icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3Z"></path></svg>', label: '黑色'}
|
||||
];
|
||||
alertTypes.forEach(function(type) {
|
||||
$('#wmd-button-row').append('<li class="wmd-button" id="wmd-alert-' + type.id + '" title="插入' + type.label + '提示"><span>' + type.icon + '</span></li>');
|
||||
$(document).on('click', '#wmd-alert-' + type.id, function() {
|
||||
var textarea = $('#text')[0];
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var value = textarea.value;
|
||||
var selected = value.substring(start, end) || type.label;
|
||||
var text = '[' + type.id + ']' + selected + '[/' + type.id + ']';
|
||||
textarea.value = value.substring(0, start) + text + value.substring(end);
|
||||
textarea.setSelectionRange(start + text.length, start + text.length);
|
||||
textarea.focus();
|
||||
$('#text').trigger('change');
|
||||
});
|
||||
});
|
||||
// 折叠面板按钮
|
||||
$('#wmd-button-row').append('<li class="wmd-button" id="wmd-collapse-button" title="插入折叠面板"><span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M19 12C20.0929 12 21.1175 12.2922 22 12.8027V6C22 5.44772 21.5523 5 21 5H12.4142L10.4142 3H3C2.44772 3 2 3.44772 2 4V20C2 20.5523 2.44772 21 3 21H13.8027C13.2922 20.1175 13 19.0929 13 18C13 14.6863 15.6863 12 19 12ZM20.4143 17.9999L22.5356 20.1212L21.1214 21.5354L19.0001 19.4141L16.8788 21.5354L15.4646 20.1212L17.5859 17.9999L15.4646 15.8786L16.8788 14.4644L19.0001 16.5857L21.1214 14.4644L22.5356 15.8786L20.4143 17.9999Z"></path></svg></span></li>');
|
||||
$(document).on('click', '#wmd-collapse-button', function() {
|
||||
var title = prompt('请输入折叠面板标题:', '折叠标题');
|
||||
if (title !== null) {
|
||||
var textarea = $('#text')[0];
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var value = textarea.value;
|
||||
var selected = value.substring(start, end) || '这里是折叠内容';
|
||||
var text = "[collapse title='" + title + "']" + selected + "[/collapse]";
|
||||
textarea.value = value.substring(0, start) + text + value.substring(end);
|
||||
textarea.setSelectionRange(start + text.length, start + text.length);
|
||||
textarea.focus();
|
||||
$('#text').trigger('change');
|
||||
}
|
||||
});
|
||||
// 下载按钮
|
||||
$('#wmd-button-row').append('<li class="wmd-button" id="wmd-download-button" title="插入下载信息"><span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M3 19H21V21H3V19ZM13 9H20L12 17L4 9H11V1H13V9Z"></path></svg></span></li>');
|
||||
$(document).on('click', '#wmd-download-button', function() {
|
||||
var file = prompt('请输入文件名:', 'xxx.zip');
|
||||
if (file === null) return;
|
||||
var size = prompt('请输入文件大小:', '12MB');
|
||||
if (size === null) return;
|
||||
var url = prompt('请输入下载地址:', 'https://example.com/file.zip');
|
||||
if (url === null) return;
|
||||
var textarea = $('#text')[0];
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var value = textarea.value;
|
||||
var text = "[download file='" + file + "' size='" + size + "']" + url + "[/download]";
|
||||
textarea.value = value.substring(0, start) + text + value.substring(end);
|
||||
textarea.setSelectionRange(start + text.length, start + text.length);
|
||||
textarea.focus();
|
||||
$('#text').trigger('change');
|
||||
});
|
||||
// 回复可见按钮
|
||||
$('#wmd-button-row').append('<li class="wmd-button" id="wmd-reply-button" title="插入回复可见"><span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M10.1305 15.8421L9.34268 18.7821L7.41083 18.2645L8.1983 15.3256C7.00919 14.8876 5.91661 14.2501 4.96116 13.4536L2.80783 15.6069L1.39362 14.1927L3.54695 12.0394C2.35581 10.6105 1.52014 8.8749 1.17578 6.96843L2.07634 6.80469C4.86882 8.81573 8.29618 10.0003 12.0002 10.0003C15.7043 10.0003 19.1316 8.81573 21.9241 6.80469L22.8247 6.96843C22.4803 8.8749 21.6446 10.6105 20.4535 12.0394L22.6068 14.1927L21.1926 15.6069L19.0393 13.4536C18.0838 14.2501 16.9912 14.8876 15.8021 15.3256L16.5896 18.2645L14.6578 18.7821L13.87 15.8421C13.2623 15.9461 12.6376 16.0003 12.0002 16.0003C11.3629 16.0003 10.7381 15.9461 10.1305 15.8421Z"></path></svg></span></li>');
|
||||
$(document).on('click', '#wmd-reply-button', function() {
|
||||
var textarea = $('#text')[0];
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var value = textarea.value;
|
||||
var selected = value.substring(start, end) || '这里是隐藏内容';
|
||||
var text = '[reply]' + selected + '[/reply]';
|
||||
textarea.value = value.substring(0, start) + text + value.substring(end);
|
||||
textarea.setSelectionRange(start + text.length, start + text.length);
|
||||
textarea.focus();
|
||||
$('#text').trigger('change');
|
||||
});
|
||||
|
||||
$('#wmd-article-button').click(function() {
|
||||
var articleId = prompt("请输入要引用的文章ID:");
|
||||
if (articleId) {
|
||||
var text = "[article id=\"" + articleId + "\"]";
|
||||
var textarea = $('#text')[0];
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var value = textarea.value;
|
||||
textarea.value = value.substring(0, start) + text + value.substring(end);
|
||||
// 将光标移动到插入的文本之后
|
||||
textarea.setSelectionRange(start + text.length, start + text.length);
|
||||
textarea.focus();
|
||||
// 触发change事件,确保编辑器更新
|
||||
$('#text').trigger('change');
|
||||
}
|
||||
});
|
||||
// github按钮插入
|
||||
$('#wmd-github-button').click(function() {
|
||||
var repo = prompt("请输入GitHub仓库名(如 jkjoy/typecho):");
|
||||
if (repo) {
|
||||
var text = "[github=" + repo + "]";
|
||||
var textarea = $('#text')[0];
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var value = textarea.value;
|
||||
textarea.value = value.substring(0, start) + text + value.substring(end);
|
||||
textarea.setSelectionRange(start + text.length, start + text.length);
|
||||
textarea.focus();
|
||||
$('#text').trigger('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOF;
|
||||
}
|
||||
}
|
||||
// 注册编辑器按钮钩子
|
||||
// 避免重复注册,在最后执行
|
||||
if (!Typecho_Plugin::exists('Widget_Abstract_Contents', 'editor')) {
|
||||
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('EditorButton', 'render');
|
||||
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('EditorButton', 'render');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章摘要
|
||||
* @param $post Widget_Abstract_Contents|array
|
||||
* @param int $length 摘要长度
|
||||
* @return string
|
||||
*/
|
||||
function get_article_summary($post, $length = 100) {
|
||||
// 如果有自定义摘要字段
|
||||
if (is_array($post) && isset($post['fields']) && !empty($post['fields']['summary'])) {
|
||||
return $post['fields']['summary'];
|
||||
}
|
||||
if (is_object($post) && isset($post->fields) && !empty($post->fields->summary)) {
|
||||
return $post->fields->summary;
|
||||
}
|
||||
// 否则自动截取正文
|
||||
$text = is_array($post) ? $post['text'] : (isset($post->text) ? $post->text : '');
|
||||
$text = strip_tags($text); // 去除HTML标签
|
||||
$text = str_replace(["\r", "\n", "\t"], '', $text); // 去除换行和制表
|
||||
if (mb_strlen($text, 'UTF-8') > $length) {
|
||||
return mb_substr($text, 0, $length, 'UTF-8') . '...';
|
||||
}
|
||||
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() // 添加一个是否登录的标志
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 友好时间显示函数
|
||||
* @param $timestamp
|
||||
* @return string
|
||||
*/
|
||||
function friendly_date($timestamp) {
|
||||
$time = is_numeric($timestamp) ? $timestamp : strtotime($timestamp);
|
||||
$diff = time() - $time;
|
||||
if ($diff < 60) {
|
||||
return '刚刚';
|
||||
} elseif ($diff < 3600) {
|
||||
return floor($diff / 60) . '分钟前';
|
||||
} elseif ($diff < 86400) {
|
||||
return floor($diff / 3600) . '小时前';
|
||||
} elseif ($diff < 2592000) {
|
||||
return floor($diff / 86400) . '天前';
|
||||
} elseif ($diff < 31536000) {
|
||||
return floor($diff / 2592000) . '月前';
|
||||
} else {
|
||||
return date('Y-m-d H:i:s', $time);
|
||||
}
|
||||
}
|
||||
+54
-10
@@ -10,7 +10,11 @@
|
||||
'search' => _t('包含关键字 %s 的文章'),
|
||||
'tag' => _t('标签 %s 下的文章'),
|
||||
'author' => _t('%s 发布的文章')
|
||||
], '', ' - '); ?><?php $this->options->title(); ?></title>
|
||||
], '', ' - '); ?>
|
||||
<?php $this->options->title(); ?>
|
||||
<?php if ($this->is('index')) echo ' - '; ?>
|
||||
<?php if ($this->is('index')) $this->options->description() ?>
|
||||
</title>
|
||||
<link rel="canonical" href="<?php $this->options->siteUrl(); ?>">
|
||||
<meta name='robots' content='max-image-preview:large' />
|
||||
<?php $this->options->addhead(); ?>
|
||||
@@ -71,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(); ?>">
|
||||
@@ -79,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>
|
||||
@@ -126,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">
|
||||
@@ -139,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>
|
||||
@@ -155,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>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Pouck theme for Typecho
|
||||
*
|
||||
* 老孙移植
|
||||
*
|
||||
* @package Typecho Pouck Theme
|
||||
* @author 老孙博客
|
||||
* @version 1.1.6
|
||||
* @version 1.2.3
|
||||
* @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'); ?>
|
||||
+1
-1
@@ -16,7 +16,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</div>
|
||||
<div id="page">
|
||||
<div class="row row-cols-1">
|
||||
<div id="posts" class="col-12 animated fadeInLeft ">
|
||||
<div id="posts" class="col-12">
|
||||
<div class="puock-text no-style">
|
||||
<p><?php $this->content(); ?></p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* 随机阅读
|
||||
*
|
||||
* @package custom
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
|
||||
// 防止 PJAX 缓存,确保每次都是随机文章
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
header('X-InstantClick: no-cache');
|
||||
|
||||
// 获取随机文章
|
||||
function getRandomPost() {
|
||||
$db = Typecho_Db::get();
|
||||
|
||||
// 统计符合条件的文章总数
|
||||
$countSql = $db->select('COUNT(*) AS count')
|
||||
->from('table.contents')
|
||||
->where('status = ?', 'publish')
|
||||
->where('type = ?', 'post')
|
||||
->where('created <= ?', time());
|
||||
$countResult = $db->fetchRow($countSql);
|
||||
$total = $countResult['count'];
|
||||
|
||||
if ($total > 0) {
|
||||
// 随机选择一个偏移量
|
||||
$offset = mt_rand(0, $total - 1);
|
||||
|
||||
// 根据偏移量获取一篇文章
|
||||
$sql = $db->select()
|
||||
->from('table.contents')
|
||||
->where('status = ?', 'publish')
|
||||
->where('type = ?', 'post')
|
||||
->where('created <= ?', time())
|
||||
->limit(1)
|
||||
->offset($offset);
|
||||
|
||||
$result = $db->fetchRow($sql);
|
||||
|
||||
if (!empty($result)) {
|
||||
$target = Typecho_Widget::widget('Widget_Abstract_Contents')->filter($result);
|
||||
return $target['permalink'];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取随机文章链接
|
||||
$randomPostUrl = getRandomPost();
|
||||
|
||||
// 直接重定向,实现无感跳转
|
||||
if ($randomPostUrl) {
|
||||
// 使用 302 临时重定向,避免缓存
|
||||
header('Location: ' . $randomPostUrl, true, 302);
|
||||
exit;
|
||||
} else {
|
||||
// 如果没有文章,重定向到首页
|
||||
header('Location: ' . $this->options->siteUrl, true, 302);
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
+8
-2
@@ -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'); ?>
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* 说说页面
|
||||
* @package custom
|
||||
* @author 老孙
|
||||
* @version 1.0
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
$this->need('header.php');
|
||||
?>
|
||||
<div id="breadcrumb" class="animated fadeInUp">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a class="a-link" href="<?php $this->options->siteUrl(); ?>">首页</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page"><?php $this->title() ?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
<div id="page-moments">
|
||||
<div class="row">
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<div id="comments" class="col-lg-8 col-md-12 animated fadeInLeft">
|
||||
<?php else: ?>
|
||||
<div id="comments" class="col-lg-12 col-md-12">
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
// 登录状态检测
|
||||
if ($this->user->hasLogin()) {
|
||||
$GLOBALS['isLogin'] = true;
|
||||
} else {
|
||||
$GLOBALS['isLogin'] = false;
|
||||
}
|
||||
|
||||
// 评论回调函数
|
||||
function threadedComments($comments, $options) {
|
||||
$mail = $comments->mail;
|
||||
$mailHash = md5(strtolower(trim($mail)));
|
||||
$purl = $comments->url;
|
||||
$nickname = $comments->author;
|
||||
$cnavatar = Helper::options()->cnavatar ? Helper::options()->cnavatar : 'https://cravatar.cn/avatar/';
|
||||
$avatarurl = rtrim($cnavatar, '/') . '/' . $mailHash . '?s=80&d=identicon';
|
||||
$loadingImg = Helper::options()->themeUrl . '/assets/img/load.svg';
|
||||
?>
|
||||
<div class="mb20 puock-text moments-item">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-1">
|
||||
<a class="meta ta3" href="<?php echo $purl; ?>" target="_blank">
|
||||
<div class="avatar mb10">
|
||||
<img src='<?php echo $loadingImg; ?>'
|
||||
class='lazy md-avatar mt-1'
|
||||
data-src='<?php echo $avatarurl; ?>'
|
||||
>
|
||||
</div>
|
||||
<div class="t-line-1 info fs12"><?php echo $nickname; ?></div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-md-11">
|
||||
<div class="p-block moment-content-box"> <span class="al"></span>
|
||||
<div class="mt10 moment-content entry-content show-link-icon">
|
||||
<?php if ($comments->parent) {echo getPermalinkFromCoid($comments->parent);} echo parse_smiley_shortcode($comments->content);?>
|
||||
</div>
|
||||
<div class="mt10 moment-footer p-flex-s-right">
|
||||
<span class="t-sm c-sub">
|
||||
<span class="mr-2"><i class="fa-regular fa-clock mr-1"></i><?php echo friendly_date($comments->created); ?></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php $this->comments()->to($comments); ?>
|
||||
<?php if ($this->user->hasLogin() && $this->user->group == 'administrator') : ?>
|
||||
<div class="p-block">
|
||||
<input type="hidden" value="<?php $this->commentUrl() ?>">
|
||||
<div>
|
||||
<span class="t-lg border-bottom border-primary puock-text pb-2">
|
||||
<i class="fa-regular fa-comments mr-1"></i>有什么新鲜事
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt20 clearfix" id="comment-form-box">
|
||||
<form method="post" action="<?php $this->commentUrl() ?>" id="comment-form" class="mt10" role="form">
|
||||
<div class="form-group">
|
||||
<textarea rows="4" name="text" id="comment" class="form-control form-control-sm t-sm" placeholder="发表您的新鲜事儿..." required><?php $this->remember('text'); ?></textarea>
|
||||
</div>
|
||||
<input type="hidden" value="<?php $this->user->screenName(); ?>" name="author" />
|
||||
<input type="hidden" value="<?php $this->user->mail(); ?>" name="mail" />
|
||||
<input type="hidden" value="<?php $this->options->siteUrl(); ?>" name="url" />
|
||||
<input type="hidden" name="_" value="<?php Typecho_Widget::widget('Widget_Security')->to($security);
|
||||
echo $security->getToken($this->request->getRequestUrl()); ?>">
|
||||
<div class="p-flex-sbc mt10">
|
||||
<div class="form-foot">
|
||||
<?php if($this->options->social): ?>
|
||||
<button id="comment-insert-image" class="btn btn-outline-secondary btn-ssm" type="button" title="插入图片">
|
||||
<i class="fa-solid fa-image"></i>
|
||||
</button>
|
||||
<button id="comment-smiley" class="btn btn-outline-secondary btn-ssm pk-modal-toggle" type="button" title="表情" data-once-load="true"
|
||||
data-url="<?php echo get_correct_url('/emoji/'); ?>">
|
||||
<i class="fa-regular fa-face-smile t-md"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<button type="submit" class="btn btn-primary btn-ssm"><i class="fa-regular fa-paper-plane"></i> 立即发表</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($comments->have()): ?>
|
||||
<!-- 评论列表 -->
|
||||
<?php while ($comments->next()): ?>
|
||||
<?php threadedComments($comments, $this->options); ?>
|
||||
<?php endwhile; ?>
|
||||
<!-- 分页导航 -->
|
||||
<div class="mt20 p-flex-s-right" data-no-instant>
|
||||
<?php $comments->pageNav('«', '»', 1, '...', array(
|
||||
'wrapTag' => 'ul',
|
||||
'wrapClass' => 'pagination comment-ajax-load',
|
||||
'itemTag' => 'li',
|
||||
'textTag' => 'span',
|
||||
'currentClass' => 'active',
|
||||
'prevClass' => 'prev',
|
||||
'nextClass' => 'next'
|
||||
)); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var btn = document.getElementById('comment-insert-image');
|
||||
if (!btn) return;
|
||||
btn.addEventListener('click', function() {
|
||||
let modal = document.createElement('div');
|
||||
modal.innerHTML = `
|
||||
<style>
|
||||
.insert-image {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: rgba(211, 134, 202, 0.9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
<div id="img-insert-modal" class="insert-image">
|
||||
<div class="min-width-modal" style="max-width: 290px">
|
||||
<form>
|
||||
<div class="form-label">插入图片</div>
|
||||
<div class="mb15">
|
||||
<label for="img-insert-title" class="form-label">标题(可选)</label>
|
||||
<input id="img-insert-title" type="text" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="mb15">
|
||||
<label for="img-insert-url" class="form-label">图片地址 <span style="color:red">*</span></label>
|
||||
<input id="img-insert-url" type="text" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="mb15 d-flex justify-content-center wh100">
|
||||
<button id="img-insert-cancel" class="btn btn-ssm btn-primary mr5">取消</button>
|
||||
<button id="img-insert-confirm" class="btn btn-ssm btn-primary mr5">插入</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(modal);
|
||||
document.getElementById('img-insert-cancel').onclick = function() {
|
||||
document.body.removeChild(modal);
|
||||
};
|
||||
document.getElementById('img-insert-confirm').onclick = function() {
|
||||
let url = document.getElementById('img-insert-url').value.trim();
|
||||
let title = document.getElementById('img-insert-title').value.trim();
|
||||
if (!url) {
|
||||
alert('图片地址不能为空!');
|
||||
return;
|
||||
}
|
||||
let tag = `<img src=\"${url}\"` + (title ? ` alt=\"${title}\" title=\"${title}\"` : '') + ` />`;
|
||||
let textarea = document.getElementById('comment');
|
||||
if (textarea) {
|
||||
let start = textarea.selectionStart, end = textarea.selectionEnd;
|
||||
let val = textarea.value;
|
||||
textarea.value = val.substring(0, start) + tag + val.substring(end);
|
||||
textarea.focus();
|
||||
textarea.selectionStart = textarea.selectionEnd = start + tag.length;
|
||||
}
|
||||
document.body.removeChild(modal);
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
+9
-14
@@ -16,20 +16,21 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</div>
|
||||
<div id="page-tags">
|
||||
<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="puock-text p-block no-style pb-2">
|
||||
<?php
|
||||
// 获取所有标签
|
||||
$tags = Typecho_Widget::widget('Widget_Metas_Tag_Cloud');
|
||||
|
||||
// 准备字母数组
|
||||
$letters = range('A', 'Z');
|
||||
$letters[] = '0';
|
||||
|
||||
$letters[] = '0';
|
||||
// 获取所有存在的首字母
|
||||
$existingLetters = array();
|
||||
$tagsArray = array();
|
||||
|
||||
$tagsArray = array();
|
||||
while ($tags->next()) {
|
||||
$firstChar = getFirstChar($tags->name);
|
||||
if (!in_array($firstChar, $existingLetters)) {
|
||||
@@ -41,7 +42,6 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
'count' => $tags->count
|
||||
);
|
||||
}
|
||||
|
||||
// 对每个字母下的标签按名称排序
|
||||
foreach ($tagsArray as &$letterTags) {
|
||||
usort($letterTags, function($a, $b) {
|
||||
@@ -49,7 +49,6 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
});
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- 字母导航 -->
|
||||
<ul class='pl-0' id='tags-main-index'>
|
||||
<?php foreach ($letters as $letter): ?>
|
||||
@@ -63,13 +62,11 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<li class='t'><?php echo $i; ?></li>
|
||||
<?php endfor; ?>
|
||||
</ul>
|
||||
|
||||
<!-- 标签列表 -->
|
||||
<ul id='tags-main-box' class='pl-0'>
|
||||
<?php
|
||||
// 按字母顺序排序
|
||||
ksort($tagsArray);
|
||||
|
||||
foreach ($tagsArray as $letter => $tags): ?>
|
||||
<li class='n' id='<?php echo $letter; ?>'>
|
||||
<h4 class='tag-name'><span class='t-lg c-sub'>#</span><?php echo $letter; ?></h4>
|
||||
@@ -81,9 +78,7 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
?>
|
||||
|
||||
<?php if ($this->options->showsidebar): ?>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
<?php endif; ?>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* 说说页面
|
||||
*
|
||||
* @package custom
|
||||
*/
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) exit; ?>
|
||||
<?php $this->need('header.php'); ?>
|
||||
<div id="breadcrumb" class="animated fadeInUp">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a class="a-link" href="<?php $this->options->siteUrl(); ?>">首页</a></li>
|
||||
<li class="breadcrumb-item active " aria-current="page"><?php $this->title() ?></li>
|
||||
</ol>
|
||||
</nav>
|
||||
<div id="page-moments">
|
||||
<div class="row">
|
||||
<div id="posts" class="col-lg-8 col-md-12 animated fadeInLeft ">
|
||||
<?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" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.11.5/js/lightbox.min.js" integrity="sha512-KbRFbjA5bwNan6DvPl1ODUolvTTZ/vckssnFhka5cG80JVa5zSlRPCr055xSgU/q6oMIGhZWLhcbgIC0fyw3RQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<div id="tooot"></div>
|
||||
</div>
|
||||
<script>
|
||||
function fetchAndDisplayToots() {
|
||||
let offset = 0;
|
||||
const limit = 20;
|
||||
function formatHTML(toots) {
|
||||
let htmlString = '';
|
||||
toots.forEach(toot => {
|
||||
const isReblog = toot.reblog && toot.reblog.content;
|
||||
const content = isReblog ? toot.reblog.content : toot.content;
|
||||
const url = isReblog ? toot.reblog.url : toot.url;
|
||||
const account = isReblog ? toot.reblog.account : toot.account;
|
||||
const created_at = isReblog ? toot.reblog.created_at : toot.created_at;
|
||||
const media_attachments = isReblog ? toot.reblog.media_attachments : toot.media_attachments;
|
||||
let mediaHTML = '';
|
||||
if (media_attachments && media_attachments.length > 0) {
|
||||
media_attachments.forEach(attachment => {
|
||||
if (attachment.type === 'image') {
|
||||
mediaHTML += `<a href="${attachment.url}" target="_blank" data-lightbox="image-set"><img src="${attachment.preview_url}" class="thumbnail-image img" ></a>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
const htmlContent = marked.parse(content || '');
|
||||
htmlString += `
|
||||
<div class="mb20 puock-text moments-item">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-1">
|
||||
<a class="meta ta3" href="${account.url}" target="_blank" rel="nofollow">
|
||||
<div class="avatar mb10">
|
||||
<img src='${account.avatar}'
|
||||
class='lazy md-avatar mt-1'
|
||||
data-src='${account.avatar}'
|
||||
alt="${account.display_name}" title="${account.display_name}">
|
||||
</div>
|
||||
<div class="t-line-1 info fs12">${account.display_name}</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-md-11">
|
||||
<div class="p-block moment-content-box"> <span class="al"></span>
|
||||
<div class="mt10 moment-content entry-content show-link-icon">
|
||||
<p>${htmlContent}</p>
|
||||
<div class="resimg">${mediaHTML}</div>
|
||||
</div>
|
||||
<div class="mt10 moment-footer p-flex-s-right"> <span class="t-sm c-sub">
|
||||
<a class="c-sub-a" href="${url}">
|
||||
<span class="mr-2"><i class="fa-regular fa-clock mr-1"></i>${new Date(created_at).toLocaleString()}</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
return htmlString;
|
||||
}
|
||||
function fetchToots() {
|
||||
return fetch('<?php echo $tooot; ?>')
|
||||
.then(response => response.json())
|
||||
.catch(error => {
|
||||
console.error('Error fetching toots:', error);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
const memosContainer = document.getElementById('tooot');
|
||||
if (memosContainer) memosContainer.innerHTML = '';
|
||||
fetchToots().then(data => {
|
||||
if (!Array.isArray(data)) {
|
||||
console.error('toot.json is not an array:', data);
|
||||
return;
|
||||
}
|
||||
const tootsToShow = data.slice(offset, offset + limit);
|
||||
if (memosContainer) memosContainer.innerHTML += formatHTML(tootsToShow);
|
||||
});
|
||||
}
|
||||
|
||||
// 保证首次和 pjax 都能调用
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", fetchAndDisplayToots);
|
||||
} else {
|
||||
fetchAndDisplayToots();
|
||||
}
|
||||
document.addEventListener('pjax:end', fetchAndDisplayToots);
|
||||
</script>
|
||||
<style>
|
||||
div pre code {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
}
|
||||
div p a {
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
}
|
||||
.resimg {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
column-gap: 10px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
/* 单个缩略图的样式 */
|
||||
.thumbnail-image {
|
||||
width: 100%; /* 确保其宽度填满父容器 */
|
||||
height: 200px; /* 固定高度 */
|
||||
display: flex; /* 使用 flexbox 居中 */
|
||||
align-items: center; /* 垂直居中 */
|
||||
justify-content: center; /* 水平居中 */
|
||||
overflow: hidden; /* 确保容器内的多余内容不会显示出来 */
|
||||
border-radius: 4px; /* 圆角 */
|
||||
transition: transform .3s ease; /* 鼠标悬停时的过渡效果 */
|
||||
cursor: zoom-in; /* 鼠标指针变为放大镜 */
|
||||
}
|
||||
img {
|
||||
object-fit: cover; /* 保持图片的纵横比,但会将图片裁剪以填充容器 */
|
||||
object-position: center; /* 保证中央部分 */
|
||||
}
|
||||
/* 缩略图内的图片样式 */
|
||||
.thumbnail-image img {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
/* 当屏幕宽度小于732px时 */
|
||||
@media (max-width: 732px) {
|
||||
.resimg {
|
||||
grid-template-columns: repeat(2, 1fr); /* 修改为两列 */
|
||||
}
|
||||
}
|
||||
/* 当屏幕宽度小于400px时 */
|
||||
@media (max-width: 400px) {
|
||||
.resimg {
|
||||
grid-template-columns: 1fr; /* 修改为一列 */
|
||||
}
|
||||
.thumbnail-image img {
|
||||
width: 100%;
|
||||
height: 480px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?php $this->need('sidebar.php'); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php $this->need('footer.php'); ?>
|
||||
@@ -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'); ?>
|
||||
@@ -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>
|
||||
@@ -52,7 +56,7 @@ $wordCount = mb_strlen($content, 'UTF-8');
|
||||
?>
|
||||
<div class="mt20 entry-content-box">
|
||||
<div class="entry-content show-link-icon content-main puock-text ">
|
||||
<p class="fs12 c-sub no-indent"> <i class="fa-regular fa-clock"></i> 共计<?php echo $wordCount; ?>个字符,预计需要花费 <?php echo ceil($wordCount / 800); ?>分钟才能阅读完成。 </p>
|
||||
<p class="fs12 c-sub no-indent"> <i class="fa-regular fa-clock"></i> 本文共计<?php echo $wordCount; ?>字,预计需要花费 <?php echo ceil($wordCount / 800); ?>分钟才能阅读完成。 </p>
|
||||
<p class="fs12 c-sub">
|
||||
<?php
|
||||
$modified = $this->modified;
|
||||
@@ -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'); ?>
|
||||
+10
-33
@@ -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 $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; ?>' >
|
||||
<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 $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">
|
||||
|
||||
Reference in New Issue
Block a user