You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tentest/doc/DjangoBlog/djangoblog/plugin_manage/base_plugin.py

275 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#wr 导入必要模块日志用于记录插件运行信息Path用于路径处理
#wr Django模板相关模块用于模板渲染及处理模板不存在的异常
import logging
from pathlib import Path
from django.template import TemplateDoesNotExist # wrDjango模板不存在时抛出的异常
from django.template.loader import render_to_string # wrDjango的模板渲染函数
#wr创建当前模块的日志记录器用于记录插件相关日志
logger = logging.getLogger(__name__)
class BasePlugin:
"""
wr插件基类所有自定义插件需继承此类并实现必要的抽象方法。
提供插件元数据管理、位置渲染、模板处理、静态资源管理等核心功能,
实现了插件系统的基础框架。
"""
# ===================== 插件元数据(子类必须定义) =====================
PLUGIN_NAME = None # wr插件名称例如"文章推荐插件"
PLUGIN_DESCRIPTION = None # wr插件描述说明插件功能
PLUGIN_VERSION = None # wr插件版本例如"1.0.0"
PLUGIN_AUTHOR = None #wr 插件作者(可选,可留空)
# ===================== 插件配置(子类可根据需求重写) =====================
SUPPORTED_POSITIONS = [] # wr插件支持的显示位置列表例如['sidebar', 'article_bottom']
DEFAULT_PRIORITY = 100 #wr 默认优先级:数字越小,插件在同位置越靠前显示
POSITION_PRIORITIES = {} #wr 位置特定优先级:为不同位置单独设置优先级(覆盖默认值)
def __init__(self):
"""
wr初始化插件实例。
检查元数据完整性,获取插件路径和标识符,执行初始化逻辑并注册钩子。
"""
#wr 校验插件元数据:名称、描述、版本为必填项,未定义则抛出异常
if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]):
raise ValueError("插件元数据PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION必须定义。")
#wr 获取插件所在目录路径和唯一标识符slug
self.plugin_dir = self._get_plugin_directory() # 插件目录路径Path对象
self.plugin_slug = self._get_plugin_slug() # 插件唯一标识(默认使用目录名)
# wr执行插件初始化逻辑子类可重写
self.init_plugin()
# wr注册插件钩子子类可重写以注册自定义钩子
self.register_hooks()
def _get_plugin_directory(self):
"""
wr获取插件所在的目录路径。
通过反射获取当前插件类所在的文件路径,进而得到目录路径。
"""
import inspect
# 获取当前类(子类)的定义文件路径
plugin_file = inspect.getfile(self.__class__)
# 返回文件所在的目录路径
return Path(plugin_file).parent
def _get_plugin_slug(self):
"""
wr获取插件的唯一标识符slug
默认使用插件目录的名称作为slug确保唯一性。
"""
return self.plugin_dir.name
def init_plugin(self):
"""
wr插件初始化逻辑钩子方法
子类可重写此方法实现自定义初始化操作(如加载配置、连接数据库等)。
默认仅记录初始化日志。
"""
logger.info(f'{self.PLUGIN_NAME} 已初始化。')
def register_hooks(self):
"""
wr注册插件钩子钩子方法
子类可重写此方法注册自定义钩子如监听系统事件、注册URL路由等
默认不执行任何操作。
"""
pass
# ===================== 位置渲染系统(核心功能) =====================
def render_position_widget(self, position, context, **kwargs):
"""
wr根据指定位置渲染插件组件是位置渲染的入口方法。
Args:
position: 位置标识(如'sidebar'表示侧边栏)
context: Django模板上下文包含页面渲染所需数据
**kwargs: 额外参数如文章ID、用户信息等按需传递
Returns:
dict: 包含渲染结果的字典({'html': HTML内容, 'priority': 优先级, 'plugin_name': 插件名}
若位置不支持或无需显示则返回None
"""
#wr 检查当前位置是否在插件支持的位置列表中不支持则直接返回None
if position not in self.SUPPORTED_POSITIONS:
return None
#wr 检查插件是否应在当前位置显示调用should_display判断
if not self.should_display(position, context, **kwargs):
return None
#wr 动态拼接当前位置对应的渲染方法名如position为'sidebar',则方法名为'render_sidebar_widget'
method_name = f'render_{position}_widget'
#wr 检查当前类是否实现了对应位置的渲染方法
if hasattr(self, method_name):
#wr 调用对应方法获取HTML内容
html = getattr(self, method_name)(context, **kwargs)
#wr 若渲染成功返回非空HTML则构造结果字典
if html:
#wr 优先级:优先使用位置特定优先级,否则使用默认优先级
priority = self.POSITION_PRIORITIES.get(position, self.DEFAULT_PRIORITY)
return {
'html': html,
'priority': priority,
'plugin_name': self.PLUGIN_NAME
}
#wr 若未实现对应渲染方法或渲染失败返回None
return None
def should_display(self, position, context, **kwargs):
"""
wr判断插件是否应在指定位置显示钩子方法
子类可重写此方法实现条件显示逻辑(如仅在特定页面/用户组显示)。
默认返回True始终显示
Args:
position: 位置标识
context: 模板上下文
**kwargs: 额外参数
Returns:
bool: True表示显示False表示不显示
"""
return True
# ===================== 位置渲染方法(子类需按需重写) =====================
def render_sidebar_widget(self, context, **kwargs):
"""wr渲染侧边栏组件钩子方法。子类重写此方法实现侧边栏显示内容。"""
return None
def render_article_bottom_widget(self, context, **kwargs):
"""wr渲染文章底部组件钩子方法。子类重写此方法实现文章底部显示内容。"""
return None
def render_article_top_widget(self, context, **kwargs):
"""wr渲染文章顶部组件钩子方法。子类重写此方法实现文章顶部显示内容。"""
return None
def render_header_widget(self, context, **kwargs):
"""wr渲染页头组件钩子方法。子类重写此方法实现页头显示内容。"""
return None
def render_footer_widget(self, context, **kwargs):
"""wr渲染页脚组件钩子方法。子类重写此方法实现页脚显示内容。"""
return None
def render_comment_before_widget(self, context, **kwargs):
"""wr渲染评论前组件钩子方法。子类重写此方法实现评论区前显示内容。"""
return None
def render_comment_after_widget(self, context, **kwargs):
"""wr渲染评论后组件钩子方法。子类重写此方法实现评论区后显示内容。"""
return None
# ===================== 模板系统(插件模板渲染) =====================
def render_template(self, template_name, context=None):
"""
wr 渲染插件自带的模板文件。
模板路径固定为"plugins/[插件slug]/[模板文件名]"
Args:
template_name: 模板文件名(如"sidebar.html"
context: 模板上下文(字典类型,默认为空)
Returns:
str: 渲染后的HTML字符串若模板不存在返回空字符串并记录警告日志
"""
if context is None:
context = {} # 确保上下文不为None
#wr 构造模板路径:插件模板需放在"plugins/插件slug/"目录下
template_path = f"plugins/{self.plugin_slug}/{template_name}"
try:
#wr 使用Django的render_to_string渲染模板
return render_to_string(template_path, context)
except TemplateDoesNotExist:
#wr 模板不存在时记录警告日志
logger.warning(f"插件模板不存在:{template_path}")
return ""
# ===================== 静态资源系统CSS/JS等资源管理 =====================
def get_static_url(self, static_file):
"""
wr获取插件静态文件的URL。
静态文件需放在插件目录的"static/[插件slug]/"遵循Django静态文件规范
Args:
static_file: 静态文件相对路径(如"css/style.css"
Returns:
str: 静态文件的完整URL"/static/demo_plugin/css/style.css"
"""
from django.templatetags.static import static # wr导入Django静态文件工具
#wr 构造静态文件路径并生成URL
return static(f"{self.plugin_slug}/static/{self.plugin_slug}/{static_file}")
def get_css_files(self):
"""
wr获取插件所需的CSS文件列表钩子方法
子类重写此方法返回CSS文件路径列表系统会自动在页面加载这些CSS。
Returns:
list: CSS文件路径列表如["css/style.css"]
"""
return []
def get_js_files(self):
"""
wr获取插件所需的JavaScript文件列表钩子方法
子类重写此方法返回JS文件路径列表系统会自动在页面加载这些JS。
Returns:
list: JS文件路径列表如["js/script.js"]
"""
return []
def get_head_html(self, context=None):
"""
wr获取需要插入到HTML头部<head>标签内)的内容(钩子方法)。
子类重写此方法返回自定义头部内容如额外的CSS链接、meta标签等
Args:
context: 模板上下文
Returns:
str: 需插入到<head>的HTML字符串
"""
return ""
def get_body_html(self, context=None):
"""
wr获取需要插入到HTML底部</body>标签前)的内容(钩子方法)。
子类重写此方法返回自定义底部内容如额外的JS脚本
Args:
context: 模板上下文
Returns:
str: 需插入到<body>底部的HTML字符串
"""
return ""
def get_plugin_info(self):
"""
wr获取插件的详细信息元数据+配置)。
用于插件管理界面展示插件信息。
Returns:
dict: 包含插件信息的字典
"""
return {
'name': self.PLUGIN_NAME, #wr 插件名称
'description': self.PLUGIN_DESCRIPTION, #wr 插件描述
'version': self.PLUGIN_VERSION, #wr 插件版本
'author': self.PLUGIN_AUTHOR, #wr 插件作者
'slug': self.plugin_slug, #wr 插件唯一标识
'directory': str(self.plugin_dir), #wr 插件目录路径
'supported_positions': self.SUPPORTED_POSITIONS, # wr支持的显示位置
'priorities': self.POSITION_PRIORITIES #wr 各位置的优先级
}