#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()