#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头部(标签内)的内容(钩子方法)。 子类重写此方法返回自定义头部内容(如额外的CSS链接、meta标签等)。 Args: context: 模板上下文 Returns: str: 需插入到的HTML字符串 """ return "" def get_body_html(self, context=None): """ wr获取需要插入到HTML底部(标签前)的内容(钩子方法)。 子类重写此方法返回自定义底部内容(如额外的JS脚本)。 Args: context: 模板上下文 Returns: str: 需插入到底部的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 各位置的优先级 }