Merge pull request #772 from liangliangyy/dev
插件能力
commit
cf2c4854fc
@ -0,0 +1,41 @@
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BasePlugin:
|
||||
# 插件元数据
|
||||
PLUGIN_NAME = None
|
||||
PLUGIN_DESCRIPTION = None
|
||||
PLUGIN_VERSION = None
|
||||
|
||||
def __init__(self):
|
||||
if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]):
|
||||
raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.")
|
||||
self.init_plugin()
|
||||
self.register_hooks()
|
||||
|
||||
def init_plugin(self):
|
||||
"""
|
||||
插件初始化逻辑
|
||||
子类可以重写此方法来实现特定的初始化操作
|
||||
"""
|
||||
logger.info(f'{self.PLUGIN_NAME} initialized.')
|
||||
|
||||
def register_hooks(self):
|
||||
"""
|
||||
注册插件钩子
|
||||
子类可以重写此方法来注册特定的钩子
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_plugin_info(self):
|
||||
"""
|
||||
获取插件信息
|
||||
:return: 包含插件元数据的字典
|
||||
"""
|
||||
return {
|
||||
'name': self.PLUGIN_NAME,
|
||||
'description': self.PLUGIN_DESCRIPTION,
|
||||
'version': self.PLUGIN_VERSION
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
ARTICLE_DETAIL_LOAD = 'article_detail_load'
|
||||
ARTICLE_CREATE = 'article_create'
|
||||
ARTICLE_UPDATE = 'article_update'
|
||||
ARTICLE_DELETE = 'article_delete'
|
||||
|
||||
ARTICLE_CONTENT_HOOK_NAME = "the_content"
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_hooks = {}
|
||||
|
||||
|
||||
def register(hook_name: str, callback: callable):
|
||||
"""
|
||||
注册一个钩子回调。
|
||||
"""
|
||||
if hook_name not in _hooks:
|
||||
_hooks[hook_name] = []
|
||||
_hooks[hook_name].append(callback)
|
||||
logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'")
|
||||
|
||||
|
||||
def run_action(hook_name: str, *args, **kwargs):
|
||||
"""
|
||||
执行一个 Action Hook。
|
||||
它会按顺序执行所有注册到该钩子上的回调函数。
|
||||
"""
|
||||
if hook_name in _hooks:
|
||||
logger.debug(f"Running action hook '{hook_name}'")
|
||||
for callback in _hooks[hook_name]:
|
||||
try:
|
||||
callback(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"Error running action hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True)
|
||||
|
||||
|
||||
def apply_filters(hook_name: str, value, *args, **kwargs):
|
||||
"""
|
||||
执行一个 Filter Hook。
|
||||
它会把 value 依次传递给所有注册的回调函数进行处理。
|
||||
"""
|
||||
if hook_name in _hooks:
|
||||
logger.debug(f"Applying filter hook '{hook_name}'")
|
||||
for callback in _hooks[hook_name]:
|
||||
try:
|
||||
value = callback(value, *args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True)
|
||||
return value
|
||||
@ -0,0 +1 @@
|
||||
# This file makes this a Python package
|
||||
@ -0,0 +1 @@
|
||||
# This file makes this a Python package
|
||||
@ -0,0 +1 @@
|
||||
# This file makes this a Python package
|
||||
@ -0,0 +1,48 @@
|
||||
import re
|
||||
from urllib.parse import urlparse
|
||||
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 ExternalLinksPlugin(BasePlugin):
|
||||
PLUGIN_NAME = '外部链接处理器'
|
||||
PLUGIN_DESCRIPTION = '自动为文章中的外部链接添加 target="_blank" 和 rel="noopener noreferrer" 属性。'
|
||||
PLUGIN_VERSION = '0.1.0'
|
||||
PLUGIN_AUTHOR = 'liangliangyy'
|
||||
|
||||
def register_hooks(self):
|
||||
hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.process_external_links)
|
||||
|
||||
def process_external_links(self, content, *args, **kwargs):
|
||||
from djangoblog.utils import get_current_site
|
||||
site_domain = get_current_site().domain
|
||||
|
||||
# 正则表达式查找所有 <a> 标签
|
||||
link_pattern = re.compile(r'(<a\s+(?:[^>]*?\s+)?href=")([^"]*)(".*?/a>)', re.IGNORECASE)
|
||||
|
||||
def replacer(match):
|
||||
# match.group(1) 是 <a ... href="
|
||||
# match.group(2) 是链接 URL
|
||||
# match.group(3) 是 ">...</a>
|
||||
href = match.group(2)
|
||||
|
||||
# 如果链接已经有 target 属性,则不处理
|
||||
if 'target=' in match.group(0).lower():
|
||||
return match.group(0)
|
||||
|
||||
# 解析链接
|
||||
parsed_url = urlparse(href)
|
||||
|
||||
# 如果链接是外部的 (有域名且域名不等于当前网站域名)
|
||||
if parsed_url.netloc and parsed_url.netloc != site_domain:
|
||||
# 添加 target 和 rel 属性
|
||||
return f'{match.group(1)}{href}" target="_blank" rel="noopener noreferrer"{match.group(3)}'
|
||||
|
||||
# 否则返回原样
|
||||
return match.group(0)
|
||||
|
||||
return link_pattern.sub(replacer, content)
|
||||
|
||||
|
||||
plugin = ExternalLinksPlugin()
|
||||
@ -0,0 +1 @@
|
||||
# This file makes this a Python package
|
||||
Loading…
Reference in new issue