From 632ac9139ef0446eb201cb722adb8e4eed98586b Mon Sep 17 00:00:00 2001 From: guqi Date: Sun, 19 Oct 2025 20:34:04 +0800 Subject: [PATCH] =?UTF-8?q?merge=20develop=20&=20=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/article_copyright/plugin.py | 2 +- plugins/external_links/plugin.py | 48 +++++++++++++++++++++++------ plugins/reading_time/plugin.py | 48 ++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/plugins/article_copyright/plugin.py b/plugins/article_copyright/plugin.py index 9af7c66..2f6dbae 100644 --- a/plugins/article_copyright/plugin.py +++ b/plugins/article_copyright/plugin.py @@ -11,7 +11,7 @@ from djangoblog.plugin_manage import hooks # 从常量定义文件导入文章内容钩子的名称 # 使用常量可以避免硬编码字符串,增加代码的可读性和可维护性 # ARTICLE_CONTENT_HOOK_NAME 的值很可能就是 'article_content' 或类似的字符串 -from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME +from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME # 2. 定义插件主类 diff --git a/plugins/external_links/plugin.py b/plugins/external_links/plugin.py index 5b2ef14..1b0a056 100644 --- a/plugins/external_links/plugin.py +++ b/plugins/external_links/plugin.py @@ -1,48 +1,76 @@ +# 导入正则表达式模块,用于匹配和处理HTML中的标签 import re +# 导入URL解析模块,用于解析链接的域名等信息 from urllib.parse import urlparse +# 导入Django博客系统的插件基类,当前插件需继承此类实现标准化功能 from djangoblog.plugin_manage.base_plugin import BasePlugin +# 导入插件钩子管理模块,用于注册和触发插件功能 from djangoblog.plugin_manage import hooks +# 导入文章内容钩子常量,指定插件要作用的具体钩子位置 from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME +# 定义外部链接处理插件类,继承自插件基类BasePlugin class ExternalLinksPlugin(BasePlugin): + # 插件名称,用于在插件管理界面展示 PLUGIN_NAME = '外部链接处理器' + # 插件功能描述,说明插件的核心作用 PLUGIN_DESCRIPTION = '自动为文章中的外部链接添加 target="_blank" 和 rel="noopener noreferrer" 属性。' + # 插件版本号,用于版本管理和更新识别 PLUGIN_VERSION = '0.1.0' + # 插件作者信息 PLUGIN_AUTHOR = 'liangliangyy' + # 注册插件钩子的方法,插件加载时会自动调用 def register_hooks(self): + # 将当前插件的process_external_links方法,注册到文章内容处理钩子上 + # 意味着文章内容渲染前,会自动执行该方法处理链接 hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.process_external_links) + # 核心方法:处理文章内容中的外部链接 + # 参数content为文章原始HTML内容,*args和**kwargs用于接收额外参数(预留扩展性) def process_external_links(self, content, *args, **kwargs): + # 导入Django工具函数,用于获取当前网站的域名(如example.com) from djangoblog.utils import get_current_site + # 获取当前网站的域名,用于判断链接是否为"外部链接" site_domain = get_current_site().domain - # 正则表达式查找所有 标签 + # 定义正则表达式,用于匹配HTML中的标签 + # 匹配规则:都能匹配) link_pattern = re.compile(r'(]*?\s+)?href=")([^"]*)(".*?/a>)', re.IGNORECASE) + # 定义正则替换的回调函数,每匹配到一个标签就会执行一次 def replacer(match): - # match.group(1) 是 ... + # 拆分匹配结果为3个分组: + # group(1): 的部分 href = match.group(2) - # 如果链接已经有 target 属性,则不处理 + # 检查当前标签是否已包含target属性(不区分大小写,如Target或TARGET) + # 若已存在target属性,则不做处理,直接返回原标签 if 'target=' in match.group(0).lower(): return match.group(0) - # 解析链接 + # 解析当前链接地址,获取其域名、路径等结构化信息 parsed_url = urlparse(href) - # 如果链接是外部的 (有域名且域名不等于当前网站域名) + # 判断链接是否为外部链接: + # 1. parsed_url.netloc不为空(排除无域名的链接,如相对路径 /about) + # 2. 链接的域名(netloc)不等于当前网站域名(site_domain) if parsed_url.netloc and parsed_url.netloc != site_domain: - # 添加 target 和 rel 属性 + # 若为外部链接,在href闭合后添加 target="_blank"(新窗口打开) + # 和 rel="noopener noreferrer"(安全属性,防止窗口劫持) return f'{match.group(1)}{href}" target="_blank" rel="noopener noreferrer"{match.group(3)}' - # 否则返回原样 + # 若为内部链接(或无域名链接),不做修改,直接返回原标签 return match.group(0) + # 使用正则表达式替换文章内容中的所有标签,执行replacer回调函数 + # 返回处理后的文章内容 return link_pattern.sub(replacer, content) -plugin = ExternalLinksPlugin() +# 实例化插件类,使插件系统能识别并加载该插件 +plugin = ExternalLinksPlugin() \ No newline at end of file diff --git a/plugins/reading_time/plugin.py b/plugins/reading_time/plugin.py index 35f9db1..6cf9a7f 100644 --- a/plugins/reading_time/plugin.py +++ b/plugins/reading_time/plugin.py @@ -1,43 +1,69 @@ +# 导入数学运算模块,用于向上取整计算 import math +# 导入正则表达式模块,用于处理HTML内容和文本分词 import re +# 导入Django博客插件基类,当前插件需继承此类 from djangoblog.plugin_manage.base_plugin import BasePlugin +# 导入插件钩子管理模块,用于注册插件功能 from djangoblog.plugin_manage import hooks +# 导入文章内容钩子常量,指定插件作用的位置 from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME class ReadingTimePlugin(BasePlugin): + # 插件名称,用于插件管理界面展示 PLUGIN_NAME = '阅读时间预测' + # 插件功能描述,说明核心作用 PLUGIN_DESCRIPTION = '估算文章阅读时间并显示在文章开头。' + # 插件版本号 PLUGIN_VERSION = '0.1.0' + # 插件作者信息 PLUGIN_AUTHOR = 'liangliangyy' def register_hooks(self): + """注册插件钩子,将处理方法绑定到文章内容钩子""" + # 当文章内容被渲染时,触发add_reading_time方法 hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.add_reading_time) def add_reading_time(self, content, *args, **kwargs): """ - 计算阅读时间并添加到内容开头。 + 计算文章阅读时间并添加到内容开头 + + 参数: + content: 文章原始HTML内容 + *args, **kwargs: 预留参数,用于接收额外信息 + 返回: + 添加了阅读时间信息的HTML内容 """ - # 移除HTML标签和空白字符,以获得纯文本 + # 1. 清理内容:使用正则移除所有HTML标签(<...>格式),保留纯文本 clean_content = re.sub(r'<[^>]*>', '', content) + # 移除文本前后的空白字符(空格、换行等) clean_content = clean_content.strip() - - # 中文和英文单词混合计数的一个简单方法 - # 匹配中文字符或连续的非中文字符(视为单词) + + # 2. 计数单词/字符: + # 正则匹配规则:匹配单个中文字符([\u4e00-\u9fa5])或连续的非中文字符(视为英文单词,\w+) + # 这样处理中英文混合的内容,兼顾两种语言的计数逻辑 words = re.findall(r'[\u4e00-\u9fa5]|\w+', clean_content) + # 统计匹配到的元素数量(中文按单个字算,英文按单词算) word_count = len(words) - - # 按平均每分钟200字的速度计算 + + # 3. 计算阅读时间: + # 设定平均阅读速度为每分钟200字(中英文通用的经验值) reading_speed = 200 + # 总字数除以阅读速度,向上取整得到分钟数 reading_minutes = math.ceil(word_count / reading_speed) - # 如果阅读时间少于1分钟,则显示为1分钟 + # 处理边界情况:如果计算结果小于1分钟,强制显示为1分钟 if reading_minutes < 1: reading_minutes = 1 - + + # 4. 生成阅读时间的HTML片段: + # 使用浅灰色文字,斜体样式,显示在段落中 reading_time_html = f'

预计阅读时间:{reading_minutes} 分钟

' - + + # 将阅读时间信息添加到文章内容开头并返回 return reading_time_html + content -plugin = ReadingTimePlugin() \ No newline at end of file +# 实例化插件,使插件系统能够识别并加载 +plugin = ReadingTimePlugin() \ No newline at end of file -- 2.34.1