Typecho 文档:如何通过插件达成功能需求?
适用程序:Typecho
程序版本:1.2.1
文档作者:Lopwon
作者博客:Lopwon.com
发布页面:Lopwon.com/attachment/3942/
许可方式:CC BY-NC-SA
注意:此文档源于作者在博客改造中的一些经验总结,转载还请署名。
敬告:此文档操作涉及程序核心文件的修改,作者不对你在使用中产生的任何问题造成的不良后果,承担责任。
文档说明
插件的好处是:无需对程序核心代码和主题模板做修改,也可以通过即插即用的方式扩展功能。本文档以(历史上有今天)为案例,实操如何通过插件达成这一功能需求。
历史上有今天,指的是不同年份,但同月同日发布的文章。由此,将博客中具有这一特性的文章关联起来,那么,需要先获取当前(月+日)日期,再与博客中所有文章的发布(月+日)日期做匹配,如有,则输出文章(标题+链接+年份),若无,则输出指定提示,如:历史上无今天。
功能解析
1. 获取当前(月+日)日期,指定为东八区;
$targetTimezone = new DateTimeZone('Asia/Shanghai'); // 设置目标时区为东八区,避免服务器不处于东八区,但用户为东八区时产生的偏差
$dateTime = new DateTime('now', $targetTimezone); // 创建一个 DateTime 对象,并设置为东八区的当前时间
$currMonth = $dateTime->format('m'); // 获取当前月
$currDay = $dateTime->format('d'); // 获取当前日
2. 获取博客中已发布的所有文章的(月+日)日期,并在遍历中匹配当前日期;
$db = Typecho_Db::get(); // 获取数据库连接实例
$result = $db->fetchAll(
$db->select()
->from('table.contents') // 从表 *_contents 获取数据
->where('status = ?', 'publish') // 仅限公开的文章(含未发布的)
->where('type = ?', 'post') // 仅限文章(不包括独立页和附件)
->order('created', Typecho_Db::SORT_DESC) // 最新发布的文章排在前
);
$found = false; // 标记是否有匹配的文章,默认为 false
foreach($result as $value) {
$filterValue = Typecho_Widget::widget('Widget_Abstract_Contents')->filter($value); // 主要为了获取 permalink year month day 等数据
$year = $filterValue['year']; // 文章发布年
$month = $filterValue['month']; // 文章发布月
$day = $filterValue['day']; // 文章发布日
$title = $filterValue['title']; // 文章标题
$permalink = $filterValue['permalink']; // 文章链接
if ($month === $currMonth && $day === $currDay) { // 如果当前(月+日)与文章发布(月+日)都相同,则为(历史上有今天)文章
echo '<a class="lopwon_today-item" href="' . $permalink . '">'. $title . ' (' . $year . ')</a>'; // 输出匹配的文章信息
$found = true; // 找到匹配的文章则标记为 true
}
}
if (!$found) { // 如果没有找到匹配的文章
echo '<i>历史上无今天!</i>'; // 输出提示
}
3. 使用插件执行及输出结果;
<?php
// 定义命名空间,Typecho 1.2.0 引入
namespace TypechoPlugin\LopwonToday; // 其中 LopwonToday 与插件文件夹名称一致
// 引入 Typecho 插件接口和所需的类库
use Typecho\Plugin\PluginInterface; // 引入插件接口,插件需要实现这个接口,对应文件 var\Typecho\Plugin\PluginInterface.php
use Typecho\Widget\Helper\Form; // 引入 Form 类,帮助构建插件的配置表单,对应目录 var\Typecho\Widget\Helper\Form 如定义 config 配置面板时,则对应 Element 内文件
use Utils\Helper; // 引入 Helper 工具类,提供 Typecho 内置方法,如 options addAction addPanel addRoute 等,对应文件 var\Utils\Helper.php
use Typecho\Db; // 引入数据库类,用于与 Typecho 数据库进行交互,如 select where join 等,对应目录 var\Typecho\Db 及文件 var\Typecho\Db.php
use Typecho\Widget; // 引入 Widget 类,允许在插件中创建和管理 Widgets 工具,对应目录 var\Typecho\Widget
// 防止直接访问该文件,确保只有通过 Typecho 环境加载
if (!defined('__TYPECHO_ROOT_DIR__')) exit; // 如果常量 __TYPECHO_ROOT_DIR__ 未定义,则退出
// 定义插件信息,对应文件 var\Typecho\Plugin.php 的 parseInfo()
/**
* 历史上有今天
*
* @package Lopwon Today
* @author Lopwon
* @version 1.0.0
* @link http://www.lopwon.com
*/
// 定义一个 Plugin 类,实现 Typecho 插件接口 PluginInterface
class Plugin implements PluginInterface
{
/**
* 激活插件方法,如果激活失败,直接抛出异常
*/
public static function activate()
{
/**
* 插件的激活接口,在当前案例中,使用了自定义的接口 \Typecho\Plugin::factory('Lopwon_Today')->Lopwon = [__CLASS__, 'render'];
* 那么,在主题中则调用 Typecho_Plugin::factory('Lopwon_Today')->Lopwon(); 以输出插件处理结果
*
* 其中 Lopwon_Today 和 Lopwon 可自定义,但需要保持定义与调用的名称一致
* 例如,在插件中自定义接口为 \Typecho\Plugin::factory('forSnapic')->Snapic = [__CLASS__, 'render'];
* 那么,在主题中则调用 Typecho_Plugin::factory('forSnapic')->Snapic(); 以输出插件处理结果
*
* 其中 render 为插件实现方法(函数)的名称,可自定义,但需要与以下 public static function render() 自定义方法(函数)的 render 名称保持一致
* 例如,在插件中自定义接口为 \Typecho\Plugin::factory('Lopwon_Today')->Lopwon = [__CLASS__, 'output'];
* 那么,以下自定义方法(函数)则为 public static function output()
*/
\Typecho\Plugin::factory('Lopwon_Today')->Lopwon = [__CLASS__, 'render']; // 自定义接口,其中 render 为插件实现方法(函数)的名称
\Typecho\Plugin::factory('Widget_Archive')->header = [__CLASS__, 'head']; // Typecho 内置接口,用于页头输出内容,如 style 样式等,其中 head 与以下函数名称 head 一致
\Typecho\Plugin::factory('Widget_Archive')->footer = [__CLASS__, 'foot']; // Typecho 内置接口,用于页脚输出内容,如 JavaScript 脚本等,其中 foot 与以下函数名称 foot 一致
}
/**
* 禁用插件方法,如果禁用失败,直接抛出异常
*/
public static function deactivate()
{
/**
* 插件的禁用接口,在当前案例中,注销插件时没有什么资源需要释放,所以无需编写,但需要保留此函数定义
*
* 通常在:增加 addPanel 面板;增加 addAction 动作;增加 addRoute 路由后,禁用插件时做移除面板、动作、路由的操作
* 也常用于删除缓存文件或数据等操作
*/
}
/**
* 获取插件配置面板
*
* @param Form $form 配置面板
*/
public static function config(Form $form)
{
// 插件的配置面板,在当前案例中,没有什么参数需要配置,所以无需编写,但需要保留此函数定义
}
/**
* 个人用户的配置面板
*
* @param Form $form
*/
public static function personalConfig(Form $form)
{
// 个人用户的配置面板,在当前案例中,没有此需求,所以无需编写,但需要保留此函数定义
}
/**
* 在页面 head 里输出 css 文件,对应位置在主题中的 <?php $this->header(); ?>
*/
public static function head()
{
$pluginUrl = Helper::options()->pluginUrl . '/LopwonToday/'; // 获取插件路径
echo '<link rel="stylesheet" href="' . $pluginUrl . 'lopwon.today.css" />'; // 输出引入 lopwon.today.css 样式文件
}
/**
* 在页面 body 里输出 JavaScript 文件,对应位置在主题中的 <?php $this->footer(); ?>
*/
public static function foot()
{
$pluginUrl = Helper::options()->pluginUrl . '/LopwonToday/'; // 获取插件路径
echo '<link rel="stylesheet" href="' . $pluginUrl . 'lopwon.today.js" />'; // 输出引入 lopwon.today.js 脚本文件
}
/**
* 插件实现方法,对应位置在主题中调用插件的 <?php Typecho_Plugin::factory('Lopwon_Today')->Lopwon(); ?>
*/
public static function render()
{
/**
* 获取当前(月+日)日期,指定为东八区;
*/
$targetTimezone = new \DateTimeZone('Asia/Shanghai'); // 设置目标时区为东八区,避免服务器不处于东八区,但用户为东八区时产生的偏差
$dateTime = new \DateTime('now', $targetTimezone); // 创建一个 DateTime 对象,并设置为东八区的当前时间
$currMonth = $dateTime->format('m'); // 获取当前月
$currDay = $dateTime->format('d'); // 获取当前日
/**
* 获取博客中已发布的所有文章的(月+日)日期,并在遍历中匹配当前日期;
*/
$db = Db::get(); // 获取数据库连接实例
$result = $db->fetchAll(
$db->select()
->from('table.contents') // 从表 *_contents 获取数据
->where('status = ?', 'publish') // 仅限公开的文章(含未发布的)
->where('type = ?', 'post') // 仅限文章(不包括独立页和附件)
->order('created', Db::SORT_DESC) // 最新发布的文章排在前
);
$found = false; // 标记是否有匹配的文章,默认为 false
foreach($result as $value) {
$filterValue = Widget::widget('Widget_Abstract_Contents')->filter($value); // 主要为了获取 permalink year month day 等数据
$year = $filterValue['year']; // 文章发布年
$month = $filterValue['month']; // 文章发布月
$day = $filterValue['day']; // 文章发布日
$title = $filterValue['title']; // 文章标题
$permalink = $filterValue['permalink']; // 文章链接
if ($month === $currMonth && $day === $currDay) { // 如果当前(月+日)与文章发布(月+日)都相同,则为(历史上有今天)文章
echo '<a class="lopwon_today-item" href="' . $permalink . '">'. $title . ' (' . $year . ')</a>'; // 输出匹配的文章信息
$found = true; // 找到匹配的文章则标记为 true
}
}
if (!$found) { // 如果没有找到匹配的文章
echo '<i>历史上无今天!</i>'; // 输出提示
}
}
}
4. 在主题中调用输出插件处理结果;
<?php Typecho_Plugin::factory('Lopwon_Today')->Lopwon(); // Lopwon Today ?>
以上演示了插件的基础制作方法,点击以下(我要下载)获取成品插件,当然,使用历史上有今天,需要博客有一定的存量文章,才有显示效果,要不就成了历史上无今天。
如何快速查看插件的效果呢?在启用插件并在主题中调用插件后,找到过往已发布的某篇文章A和B,把其中一篇文章(如:A)的发布日期临时修改为今天的日期(假设为2022年11月09日),那么,文章B则将发布日期临时修改为与文章A相同的月和日,但不同年份(如:2019年11月09日),最后,在前台访问调用插件的页面,即可看到插件效果。
当然 Typecho 内置了丰富的插件接口,可以实现更具场景化的扩展功能,另一款插件 Lopwon Hook 快速构建生成了这些接口,可供开发者使用。
参考来源
This is a message