添加代码注释

jyf_branch
姜雨菲 5 months ago
parent 3c45f06fce
commit d200df6caf

@ -1,84 +1,99 @@
import logging
from pathlib import Path
import logging # 用于日志记录
from pathlib import Path # 用于文件路径处理
from django.template import TemplateDoesNotExist
from django.template.loader import render_to_string
from django.template import TemplateDoesNotExist # Django模板不存在异常
from django.template.loader import render_to_string # 用于渲染模板为字符串
# 创建日志记录器,用于记录插件相关日志
logger = logging.getLogger(__name__)
class BasePlugin:
# 插件元数据
PLUGIN_NAME = None
PLUGIN_DESCRIPTION = None
PLUGIN_VERSION = None
PLUGIN_AUTHOR = None
# 插件配置
SUPPORTED_POSITIONS = [] # 支持的显示位置
DEFAULT_PRIORITY = 100 # 默认优先级(数字越小优先级越高)
POSITION_PRIORITIES = {} # 各位置的优先级 {'sidebar': 50, 'article_bottom': 80}
"""
插件基类所有自定义插件需继承此类并实现特定方法
提供插件元数据管理位置渲染模板渲染静态资源处理等基础功能
"""
# 插件元数据(子类必须重写这些属性)
PLUGIN_NAME = None # 插件名称(如"天气插件"
PLUGIN_DESCRIPTION = None # 插件描述(功能说明)
PLUGIN_VERSION = None # 插件版本(如"1.0.0"
PLUGIN_AUTHOR = None # 插件作者
# 插件配置(子类可根据需求重写)
SUPPORTED_POSITIONS = [] # 支持的显示位置(如['sidebar', 'footer']表示支持侧边栏和页脚)
DEFAULT_PRIORITY = 100 # 默认优先级(数字越小优先级越高,用于多个插件在同一位置排序)
POSITION_PRIORITIES = {} # 各位置的优先级(覆盖默认值,如{'sidebar': 50}表示侧边栏优先级为50
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.")
raise ValueError("插件元数据PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION必须定义。")
# 设置插件路径
self.plugin_dir = self._get_plugin_directory()
self.plugin_slug = self._get_plugin_slug()
# 设置插件路径和唯一标识
self.plugin_dir = self._get_plugin_directory() # 插件所在目录路径
self.plugin_slug = self._get_plugin_slug() # 插件唯一标识(默认使用目录名)
# 初始化插件并注册钩子
self.init_plugin()
self.register_hooks()
def _get_plugin_directory(self):
"""获取插件目录路径"""
"""获取插件所在的目录路径(内部方法)"""
import inspect
# 通过inspect模块获取当前类的定义文件路径再获取其所在目录
plugin_file = inspect.getfile(self.__class__)
return Path(plugin_file).parent
def _get_plugin_slug(self):
"""获取插件标识符(目录名)"""
"""获取插件的唯一标识符(默认使用插件目录名,内部方法"""
return self.plugin_dir.name
def init_plugin(self):
"""
插件初始化逻辑
子类可以重写此方法来实现特定的初始化操作
插件初始化逻辑钩子方法
子类可重写此方法实现自定义初始化操作如加载配置连接数据库等
"""
logger.info(f'{self.PLUGIN_NAME} initialized.')
logger.info(f'{self.PLUGIN_NAME} 初始化完成。')
def register_hooks(self):
"""
注册插件钩子
子类可以重写此方法来注册特定的钩子
注册插件钩子钩子方法
子类可重写此方法注册自定义钩子如响应Django信号注册URL路由等
"""
pass
# === 位置渲染系统 ===
def render_position_widget(self, position, context, **kwargs):
"""
根据位置渲染插件组件
根据指定位置渲染插件组件核心方法
Args:
position: 位置标识
context: 模板上下文
**kwargs: 额外参数
position: 位置标识'sidebar'表示侧边栏
context: 模板上下文包含当前请求用户等信息
**kwargs: 额外参数如文章ID页面类型等
Returns:
dict: {'html': 'HTML内容', 'priority': 优先级} None
dict: 包含渲染结果的字典格式为
{'html': 'HTML内容', 'priority': 优先级, 'plugin_name': 插件名}
若不支持该位置或不满足显示条件返回None
"""
# 检查当前位置是否在插件支持的位置列表中
if position not in self.SUPPORTED_POSITIONS:
return None
# 检查条件显示
# 检查是否满足显示条件调用should_display方法
if not self.should_display(position, context, **kwargs):
return None
# 调用具体的位置渲染方法
# 动态调用对应位置的渲染方法如position为'sidebar'则调用render_sidebar_widget
method_name = f'render_{position}_widget'
if hasattr(self, method_name):
# 调用具体位置的渲染方法获取HTML内容
html = getattr(self, method_name)(context, **kwargs)
if html:
if html: # 若渲染成功有HTML内容
# 确定当前位置的优先级优先使用POSITION_PRIORITIES否则用默认值
priority = self.POSITION_PRIORITIES.get(position, self.DEFAULT_PRIORITY)
return {
'html': html,
@ -90,97 +105,135 @@ class BasePlugin:
def should_display(self, position, context, **kwargs):
"""
判断插件是否应该在指定位置显示
子类可重写此方法实现条件显示逻辑
判断插件是否应该在指定位置显示钩子方法
子类可重写此方法实现条件显示逻辑如只在特定页面/用户组显示
Args:
position: 位置标识
context: 模板上下文
**kwargs: 额外参数
Returns:
bool: 是否显示
bool: True表示显示False表示不显示
"""
return True
return True # 默认始终显示
# === 各位置渲染方法 - 子类重写 ===
# === 各位置渲染方法 - 子类需根据支持的位置重写 ===
def render_sidebar_widget(self, context, **kwargs):
"""渲染侧边栏组件"""
"""渲染侧边栏组件(钩子方法),子类重写此方法实现侧边栏内容"""
return None
def render_article_bottom_widget(self, context, **kwargs):
"""渲染文章底部组件"""
"""渲染文章底部组件(钩子方法),子类重写此方法实现文章底部内容"""
return None
def render_article_top_widget(self, context, **kwargs):
"""渲染文章顶部组件"""
"""渲染文章顶部组件(钩子方法),子类重写此方法实现文章顶部内容"""
return None
def render_header_widget(self, context, **kwargs):
"""渲染页头组件"""
"""渲染页头组件(钩子方法),子类重写此方法实现页头内容"""
return None
def render_footer_widget(self, context, **kwargs):
"""渲染页脚组件"""
"""渲染页脚组件(钩子方法),子类重写此方法实现页脚内容"""
return None
def render_comment_before_widget(self, context, **kwargs):
"""渲染评论前组件"""
"""渲染评论前组件(钩子方法),子类重写此方法实现评论区前内容"""
return None
def render_comment_after_widget(self, context, **kwargs):
"""渲染评论后组件"""
"""渲染评论后组件(钩子方法),子类重写此方法实现评论区后内容"""
return None
# === 模板系统 ===
def render_template(self, template_name, context=None):
"""
渲染插件模板
渲染插件自带的模板文件
Args:
template_name: 模板文件名
context: 模板上下文
template_name: 模板文件名"sidebar.html"
context: 模板上下文传递给模板的变量
Returns:
HTML字符串
str: 渲染后的HTML字符串模板不存在则返回空字符串
"""
if context is None:
context = {}
context = {} # 默认为空上下文
# 构建模板路径plugins/插件标识/模板名遵循Django模板查找规则
template_path = f"plugins/{self.plugin_slug}/{template_name}"
try:
# 调用Django的render_to_string渲染模板
return render_to_string(template_path, context)
except TemplateDoesNotExist:
logger.warning(f"Plugin template not found: {template_path}")
# 模板不存在时记录警告日志
logger.warning(f"插件模板不存在:{template_path}")
return ""
# === 静态资源系统 ===
def get_static_url(self, static_file):
"""获取插件静态文件URL"""
from django.templatetags.static import static
"""
获取插件静态文件的URL如CSSJS图片等
Args:
static_file: 静态文件相对路径"css/style.css"
Returns:
str: 静态文件的完整URL"/static/myplugin/static/myplugin/css/style.css"
"""
from django.templatetags.static import static # 导入Django的static标签
# 构建静态文件路径:插件标识/static/插件标识/文件路径遵循Django静态文件规则
return static(f"{self.plugin_slug}/static/{self.plugin_slug}/{static_file}")
def get_css_files(self):
"""获取插件CSS文件列表"""
"""
获取插件需要加载的CSS文件列表钩子方法
子类重写此方法返回CSS文件路径列表框架会自动在页面加载这些CSS
Returns:
list: CSS文件路径列表["css/style.css"]
"""
return []
def get_js_files(self):
"""获取插件JavaScript文件列表"""
"""
获取插件需要加载的JavaScript文件列表钩子方法
子类重写此方法返回JS文件路径列表框架会自动在页面加载这些JS
Returns:
list: JS文件路径列表["js/script.js"]
"""
return []
def get_head_html(self, context=None):
"""获取需要插入到<head>中的HTML内容"""
"""
获取需要插入到HTML头部<head>标签内的内容钩子方法
子类重写此方法返回自定义HTML如额外的CSS链接meta标签等
Returns:
str: 要插入<head>的HTML字符串
"""
return ""
def get_body_html(self, context=None):
"""获取需要插入到<body>底部的HTML内容"""
"""
获取需要插入到HTML body底部的内容钩子方法
子类重写此方法返回自定义HTML如额外的JS脚本
Returns:
str: 要插入<body>底部的HTML字符串
"""
return ""
def get_plugin_info(self):
"""
获取插件信息
:return: 包含插件元数据的字典
获取插件的详细信息用于插件管理展示等
Returns:
dict: 包含插件元数据和配置的字典
"""
return {
'name': self.PLUGIN_NAME,
@ -191,4 +244,4 @@ class BasePlugin:
'directory': str(self.plugin_dir),
'supported_positions': self.SUPPORTED_POSITIONS,
'priorities': self.POSITION_PRIORITIES
}
}
Loading…
Cancel
Save