#gq: # 导入正则表达式模块,用于匹配和处理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): # 拆分匹配结果为3个分组: # group(1): 的部分 href = match.group(2) # 检查当前标签是否已包含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: # 若为外部链接,在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()