|
|
#gq:
|
|
|
# 导入正则表达式模块,用于匹配和处理HTML中的<a>标签
|
|
|
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中的<a>标签
|
|
|
# 匹配规则:<a 开头 + 任意属性(可选) + href=" + 链接地址 + " + 剩余属性及闭合标签
|
|
|
# re.IGNORECASE表示忽略大小写(如<A>和<a>都能匹配)
|
|
|
link_pattern = re.compile(r'(<a\s+(?:[^>]*?\s+)?href=")([^"]*)(".*?/a>)', re.IGNORECASE)
|
|
|
|
|
|
# 定义正则替换的回调函数,每匹配到一个<a>标签就会执行一次
|
|
|
def replacer(match):
|
|
|
# 拆分匹配结果为3个分组:
|
|
|
# group(1):<a 到 href=" 的部分
|
|
|
# group(2):href属性中的链接地址
|
|
|
# group(3):href=" 之后到 </a> 的部分
|
|
|
href = match.group(2)
|
|
|
|
|
|
# 检查当前<a>标签是否已包含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)
|
|
|
|
|
|
# 使用正则表达式替换文章内容中的所有<a>标签,执行replacer回调函数
|
|
|
# 返回处理后的文章内容
|
|
|
return link_pattern.sub(replacer, content)
|
|
|
|
|
|
|
|
|
# 实例化插件类,使插件系统能识别并加载该插件
|
|
|
plugin = ExternalLinksPlugin() |