From b3b140764784295f21e167484b613c8fb53908ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=AA=E5=AD=90?= Date: Wed, 9 Jul 2025 16:56:11 +0800 Subject: [PATCH] 1.1.8 --- README.MD | 12 +- functions.php | 364 ++++++++++++++++++++++++++++++++++++++++++++++++-- header.php | 6 +- index.php | 2 +- sidebar.php | 2 +- 5 files changed, 369 insertions(+), 17 deletions(-) diff --git a/README.MD b/README.MD index fb96d82..47231a5 100644 --- a/README.MD +++ b/README.MD @@ -44,4 +44,14 @@ - 1.1.6 - 修复头像区域背景的bug - - 优化php8.3兼容性 \ No newline at end of file + - 优化php8.3兼容性 + + - 1.1.8 + - 优化github链接的正则表达式,只解析主仓库链接(如 https://github.com/用户名/仓库名),不再解析子路径(如 /tree/、/blob/ 等)为卡片。 + - 实现文章内容图片懒加载,自动将常见图片格式(jpg、jpeg、png、webp)的标签替换为带懒加载属性的格式。 + - 修正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]回复可见短代码,未满足条件时前端显示提示,已评论且审核通过后显示隐藏内容。 \ No newline at end of file diff --git a/functions.php b/functions.php index 7d09675..ae222f7 100644 --- a/functions.php +++ b/functions.php @@ -166,22 +166,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('//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('/]+src=["\']([^"\']+\.(?:jpg|jpeg|png|webp))["\'][^>]*>/i', $content, $matches)) { + return $matches[1]; } + // 没有图片则用随机封面 + $coverNumber = ($cid % 8) + 1; + return Helper::options()->themeUrl . '/assets/img/random/' . $coverNumber . '.jpg'; } /** @@ -969,4 +964,347 @@ function parse_smiley_shortcode($content) { } return $content; } -?> \ No newline at end of file +?> + ''); + $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); + $summary = get_article_summary($post); + $permalink = $post['permalink']; + $title = htmlspecialchars($post['title']); + + $output = '
'; + $output .= '
'; + $output .= '' . $title . ''; + $output .= '
'; + $output .= '
' . htmlspecialchars($summary) . '
'; + $output .= '
'; + + return $output; +} +// 创建一个新的类来处理内容过滤 +class ContentFilter +{ + public static function filterContent($content, $widget, $lastResult) + { + // 先做github短代码和链接替换 + $content = preg_replace_callback('/\[github=([\w\-\.]+\/[\w\-\.]+)\]/i', function($matches) { + $repo = htmlspecialchars($matches[1]); + return '
'; + }, $content); + // 只匹配主仓库链接,后面只能是空格、标点、换行或结尾 + $content = preg_replace_callback( + '#https://github\.com/([\w\-\.]+/[\w\-\.]+)(?=[\s\.,;:!\?\)\]\}\"\'\n]|$)#i', + function($matches) { + $repo = htmlspecialchars($matches[1]); + return '
'; + }, + $content + ); + + // 再进行 Markdown 解析 + $content = empty($lastResult) ? $widget->markdown($content) : $lastResult; + + // 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', + '
$1
', + $content + ); + } + + // 其他短代码处理 + $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( + '/]*src=["\"]([^"\"]+\.(?:jpg|jpeg|png|webp))["\"][^>]*>/i', + function($matches) use ($title, $loadSvg) { + $imgUrl = $matches[1]; + return '' . $title . ''; + }, + $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 '
' . $body . '
'; + }, + $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 '
' + . "
  文件名称:" . htmlspecialchars($file) . "
" + . "
  文件大小:" . htmlspecialchars($size) . "
" + . "
  下载声明:本站部分资源来自于网络收集,若侵犯了你的隐私或版权,请及时联系我们删除有关信息。
" + . "
下载地址:点此下载

"; + }, + $content + ); + // 回复可见短代码 + $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 { + // 未登录,判断IP + $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 '
' . $matches[1] . '
'; + } else { + return "
  此处含有隐藏内容,请提交评论并审核通过刷新后即可查看!
"; + } + }, + $content + ); + 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 << +#wmd-button-row { + display: flex; + flex-wrap: wrap; + min-height: 40px; + height: auto !important; + overflow: visible; +} +#text, .wmd-input { + margin-top: 0 !important; +} + + +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; +} diff --git a/header.php b/header.php index 86f4066..67cdbbf 100644 --- a/header.php +++ b/header.php @@ -10,7 +10,11 @@ 'search' => _t('包含关键字 %s 的文章'), 'tag' => _t('标签 %s 下的文章'), 'author' => _t('%s 发布的文章') - ], '', ' - '); ?>options->title(); ?> + ], '', ' - '); ?> + options->title(); ?> + is('index')) echo ' - '; ?> + is('index')) $this->options->description() ?> + options->addhead(); ?> diff --git a/index.php b/index.php index 056d446..f84a152 100644 --- a/index.php +++ b/index.php @@ -4,7 +4,7 @@ * * @package Typecho Pouck Theme * @author 老孙博客 - * @version 1.1.7 + * @version 1.1.8 * @link http://www.imsun.org */ diff --git a/sidebar.php b/sidebar.php index 02a071d..728d448 100644 --- a/sidebar.php +++ b/sidebar.php @@ -45,7 +45,7 @@ if ($totalViews === null) $totalViews = 0; ?> options->sidebarBlock) && in_array('ShowAdmin', $this->options->sidebarBlock)): ?>
-
+
' class='lazy avatar' data-src='' >