698 lines
25 KiB
PHP
698 lines
25 KiB
PHP
|
|
<?php
|
|||
|
|
/**
|
|||
|
|
* 知识备忘插件
|
|||
|
|
*
|
|||
|
|
* @package Memo
|
|||
|
|
* @author 石头厝
|
|||
|
|
* @version 1.1.0
|
|||
|
|
* @link https://www.shitoucuo.com/
|
|||
|
|
*/
|
|||
|
|
class Memo_Plugin implements Typecho_Plugin_Interface
|
|||
|
|
{
|
|||
|
|
/**
|
|||
|
|
* 激活插件
|
|||
|
|
*/
|
|||
|
|
public static function activate()
|
|||
|
|
{
|
|||
|
|
// 创建新的数据表,不与旧插件冲突
|
|||
|
|
$db = Typecho_Db::get();
|
|||
|
|
$prefix = $db->getPrefix();
|
|||
|
|
$tableName = $prefix . 'memo';
|
|||
|
|
|
|||
|
|
// 检查表是否存在
|
|||
|
|
$tables = $db->fetchAll($db->query("SHOW TABLES LIKE '{$tableName}'"));
|
|||
|
|
|
|||
|
|
if (empty($tables)) {
|
|||
|
|
// 创建新表
|
|||
|
|
$sql = "CREATE TABLE `{$tableName}` (
|
|||
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|||
|
|
`content` TEXT NOT NULL COMMENT '内容',
|
|||
|
|
`category` VARCHAR(100) DEFAULT '默认' COMMENT '分类',
|
|||
|
|
`event_date` DATE NULL COMMENT '事件日期(可选)',
|
|||
|
|
`post_cids` VARCHAR(255) DEFAULT '' COMMENT '关联文章CID,多个用逗号分隔',
|
|||
|
|
`original_url` VARCHAR(500) DEFAULT '' COMMENT '原文链接',
|
|||
|
|
`created` DATETIME NOT NULL COMMENT '创建时间',
|
|||
|
|
`modified` DATETIME NOT NULL COMMENT '修改时间',
|
|||
|
|
`status` TINYINT(1) DEFAULT 1 COMMENT '状态:1正常'
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='备忘录记录表';";
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
$db->query($sql);
|
|||
|
|
} catch (Typecho_Db_Exception $e) {
|
|||
|
|
throw new Typecho_Plugin_Exception('创建数据表失败: ' . $e->getMessage());
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 检查表结构,如果缺少original_url字段则添加
|
|||
|
|
$columns = $db->fetchAll($db->query("SHOW COLUMNS FROM `{$tableName}`"));
|
|||
|
|
$hasOriginalUrl = false;
|
|||
|
|
|
|||
|
|
foreach ($columns as $column) {
|
|||
|
|
if ($column['Field'] == 'original_url') {
|
|||
|
|
$hasOriginalUrl = true;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!$hasOriginalUrl) {
|
|||
|
|
// 添加original_url字段
|
|||
|
|
try {
|
|||
|
|
$db->query("ALTER TABLE `{$tableName}` ADD COLUMN `original_url` VARCHAR(500) DEFAULT '' COMMENT '原文链接' AFTER `post_cids`");
|
|||
|
|
} catch (Typecho_Db_Exception $e) {
|
|||
|
|
error_log("添加original_url字段失败: " . $e->getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加管理菜单
|
|||
|
|
Helper::addPanel(3, 'Memo/manage-panel.php', '知识备忘', '备忘管理', 'administrator');
|
|||
|
|
|
|||
|
|
return _t('备忘录插件已激活,请到插件设置中进行配置');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 禁用插件
|
|||
|
|
*/
|
|||
|
|
public static function deactivate()
|
|||
|
|
{
|
|||
|
|
Helper::removePanel(3, 'Memo/manage-panel.php');
|
|||
|
|
return _t('备忘录插件已禁用');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 插件配置面板
|
|||
|
|
*/
|
|||
|
|
public static function config(Typecho_Widget_Helper_Form $form)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 每页显示数量
|
|||
|
|
$perPage = new Typecho_Widget_Helper_Form_Element_Text(
|
|||
|
|
'perPage',
|
|||
|
|
NULL,
|
|||
|
|
'30',
|
|||
|
|
_t('后台每页显示数量'),
|
|||
|
|
_t('后台管理页面每页显示的备忘录数量')
|
|||
|
|
);
|
|||
|
|
$perPage->input->setAttribute('class', 'mini');
|
|||
|
|
$form->addInput($perPage->addRule('isInteger', _t('请输入整数')));
|
|||
|
|
|
|||
|
|
// 前端每页显示数量
|
|||
|
|
$frontPerPage = new Typecho_Widget_Helper_Form_Element_Text(
|
|||
|
|
'frontPerPage',
|
|||
|
|
NULL,
|
|||
|
|
'20',
|
|||
|
|
_t('主题调用每页显示数量'),
|
|||
|
|
_t('在主题中调用时每页显示的备忘录数量')
|
|||
|
|
);
|
|||
|
|
$frontPerPage->input->setAttribute('class', 'mini');
|
|||
|
|
$form->addInput($frontPerPage->addRule('isInteger', _t('请输入整数')));
|
|||
|
|
|
|||
|
|
// 默认分类设置
|
|||
|
|
$defaultCategories = new Typecho_Widget_Helper_Form_Element_Textarea(
|
|||
|
|
'defaultCategories',
|
|||
|
|
NULL,
|
|||
|
|
"技术/经验\n公司/副业\n户外/亲子\n主题/插件\n兴趣/爱好\n工作/效率\n创作/媒体\n个人/成长\n程序/应用",
|
|||
|
|
_t('默认分类'),
|
|||
|
|
_t('每行一个分类,回车分隔。这些分类将在发布和编辑时显示在下拉框中。')
|
|||
|
|
);
|
|||
|
|
$form->addInput($defaultCategories);
|
|||
|
|
|
|||
|
|
// 是否显示事件时间
|
|||
|
|
$showEventDate = new Typecho_Widget_Helper_Form_Element_Radio(
|
|||
|
|
'showEventDate',
|
|||
|
|
array(
|
|||
|
|
'1' => _t('显示'),
|
|||
|
|
'0' => _t('不显示')
|
|||
|
|
),
|
|||
|
|
'1',
|
|||
|
|
_t('显示事件时间'),
|
|||
|
|
_t('是否在备忘录中显示事件时间(如果设置了的话)')
|
|||
|
|
);
|
|||
|
|
$form->addInput($showEventDate);
|
|||
|
|
|
|||
|
|
// 是否显示关联文章
|
|||
|
|
$showRelatedPosts = new Typecho_Widget_Helper_Form_Element_Radio(
|
|||
|
|
'showRelatedPosts',
|
|||
|
|
array(
|
|||
|
|
'1' => _t('显示'),
|
|||
|
|
'0' => _t('不显示')
|
|||
|
|
),
|
|||
|
|
'1',
|
|||
|
|
_t('显示关联文章'),
|
|||
|
|
_t('是否在备忘录下方显示关联的文章链接')
|
|||
|
|
);
|
|||
|
|
$form->addInput($showRelatedPosts);
|
|||
|
|
|
|||
|
|
// 关联文章标题
|
|||
|
|
$relatedPostsTitle = new Typecho_Widget_Helper_Form_Element_Text(
|
|||
|
|
'relatedPostsTitle',
|
|||
|
|
NULL,
|
|||
|
|
'相关文章',
|
|||
|
|
_t('关联文章标题'),
|
|||
|
|
_t('关联文章部分的标题文本')
|
|||
|
|
);
|
|||
|
|
$form->addInput($relatedPostsTitle);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 个人用户配置
|
|||
|
|
*/
|
|||
|
|
public static function personalConfig(Typecho_Widget_Helper_Form $form)
|
|||
|
|
{
|
|||
|
|
// 个人配置(如果需要)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 插件配置面板
|
|||
|
|
*/
|
|||
|
|
public static function configPanel($panel)
|
|||
|
|
{
|
|||
|
|
// 配置面板
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 渲染方法(用于在主题模板中调用)
|
|||
|
|
*/
|
|||
|
|
public static function render()
|
|||
|
|
{
|
|||
|
|
// 获取配置
|
|||
|
|
$options = Typecho_Widget::widget('Widget_Options');
|
|||
|
|
$config = $options->plugin('Memo');
|
|||
|
|
|
|||
|
|
// 引入Action类
|
|||
|
|
require_once dirname(__FILE__) . '/Action.php';
|
|||
|
|
$action = new Memo_Action();
|
|||
|
|
|
|||
|
|
// 参数处理
|
|||
|
|
$currentFile = basename($_SERVER['PHP_SELF']);
|
|||
|
|
$selectedCategory = isset($_GET['cat']) ? trim($_GET['cat']) : '';
|
|||
|
|
$searchKeyword = isset($_GET['search']) ? trim(urldecode($_GET['search'])) : '';
|
|||
|
|
$currentPage = isset($_GET['page']) && $_GET['page'] > 0 ? intval($_GET['page']) : 1;
|
|||
|
|
$perPage = isset($config->frontPerPage) ? intval($config->frontPerPage) : 20;
|
|||
|
|
|
|||
|
|
// 获取分类列表
|
|||
|
|
$categories = $action->getAllCategories();
|
|||
|
|
$categoryCounts = $action->getCategoryCounts();
|
|||
|
|
|
|||
|
|
// 获取备忘录数据
|
|||
|
|
$memos = $action->getMemos($currentPage, $perPage, $searchKeyword, $selectedCategory);
|
|||
|
|
$total = $action->getTotalCount($searchKeyword, $selectedCategory);
|
|||
|
|
$totalPages = ceil($total / $perPage);
|
|||
|
|
|
|||
|
|
// 如果没有数据
|
|||
|
|
if (empty($memos) && empty($searchKeyword) && empty($selectedCategory)) {
|
|||
|
|
echo '<div class="memo-empty">暂无备忘录记录</div>';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 为每条记录获取关联的文章信息
|
|||
|
|
foreach ($memos as &$memo) {
|
|||
|
|
if (!empty($memo['post_cids'])) {
|
|||
|
|
$cids = array_filter(array_map('trim', explode(',', $memo['post_cids'])));
|
|||
|
|
$posts = array();
|
|||
|
|
foreach ($cids as $cid) {
|
|||
|
|
if (is_numeric($cid)) {
|
|||
|
|
$post = $action->getPostByCid($cid);
|
|||
|
|
if ($post) {
|
|||
|
|
$posts[] = $post;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
$memo['posts'] = $posts;
|
|||
|
|
} else {
|
|||
|
|
$memo['posts'] = array();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
?>
|
|||
|
|
<!-- 样式部分 -->
|
|||
|
|
<style>
|
|||
|
|
.memo-container {
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 20px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-title {
|
|||
|
|
text-align: center;
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 20px;
|
|||
|
|
padding-bottom: 10px;
|
|||
|
|
border-bottom: 2px solid #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 搜索框 */
|
|||
|
|
.memo-search {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-search input {
|
|||
|
|
padding: 10px 15px;
|
|||
|
|
border: 1px solid #ddd;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
width: 300px;
|
|||
|
|
max-width: 100%;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-search button {
|
|||
|
|
padding: 10px 20px;
|
|||
|
|
background: #007bff;
|
|||
|
|
color: white;
|
|||
|
|
border: none;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
font-size: 14px;
|
|||
|
|
margin-left: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 分类筛选 */
|
|||
|
|
.memo-categories {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category-list {
|
|||
|
|
display: flex;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
gap: 8px;
|
|||
|
|
justify-content: center;
|
|||
|
|
list-style: none;
|
|||
|
|
padding: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category-item {
|
|||
|
|
display: inline-block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category-link {
|
|||
|
|
display: inline-block;
|
|||
|
|
padding: 6px 12px;
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
color: #495057;
|
|||
|
|
text-decoration: none;
|
|||
|
|
border-radius: 20px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
font-size: 13px;
|
|||
|
|
transition: all 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category-link:hover {
|
|||
|
|
background: #e9ecef;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category-link.active {
|
|||
|
|
background: #28a745;
|
|||
|
|
color: white;
|
|||
|
|
border-color: #28a745;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category-count {
|
|||
|
|
font-size: 11px;
|
|||
|
|
opacity: 0.8;
|
|||
|
|
margin-left: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 备忘录列表 */
|
|||
|
|
.memo-list {
|
|||
|
|
list-style: none;
|
|||
|
|
padding: 0;
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-item {
|
|||
|
|
padding: 15px;
|
|||
|
|
border-bottom: 1px solid #eee;
|
|||
|
|
transition: background 0.2s;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-item:hover {
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-item-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-category {
|
|||
|
|
display: inline-block;
|
|||
|
|
background: #e8f5e8;
|
|||
|
|
color: #2e7d32;
|
|||
|
|
padding: 3px 8px;
|
|||
|
|
border-radius: 3px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-date {
|
|||
|
|
color: #666;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-content {
|
|||
|
|
color: #333;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
font-size: 14px;
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* URL链接样式 - 修复2:只对URL本身加链接 */
|
|||
|
|
.memo-url-link {
|
|||
|
|
color: #0066cc;
|
|||
|
|
text-decoration: none;
|
|||
|
|
border-bottom: 1px dotted #0066cc;
|
|||
|
|
word-break: break-all;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-url-link:hover {
|
|||
|
|
color: #0056b3;
|
|||
|
|
border-bottom: 1px solid #0056b3;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 原文链接 */
|
|||
|
|
.memo-original-link {
|
|||
|
|
margin-top: 10px;
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border-left: 3px solid #28a745;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-original-title {
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #495057;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-original-url {
|
|||
|
|
color: #0066cc;
|
|||
|
|
text-decoration: none;
|
|||
|
|
font-size: 13px;
|
|||
|
|
word-break: break-all;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-original-url:hover {
|
|||
|
|
text-decoration: underline;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 关联文章 */
|
|||
|
|
.memo-related-posts {
|
|||
|
|
margin-top: 10px;
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border-left: 3px solid #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-related-title {
|
|||
|
|
font-size: 12px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #495057;
|
|||
|
|
margin-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-related-list {
|
|||
|
|
list-style: none;
|
|||
|
|
padding: 0;
|
|||
|
|
margin: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-related-item {
|
|||
|
|
margin-bottom: 4px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-related-link {
|
|||
|
|
color: #0066cc;
|
|||
|
|
text-decoration: none;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 分页 */
|
|||
|
|
.memo-pagination {
|
|||
|
|
margin-top: 30px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-pagination a,
|
|||
|
|
.memo-pagination span {
|
|||
|
|
display: inline-block;
|
|||
|
|
padding: 6px 12px;
|
|||
|
|
margin: 0 2px;
|
|||
|
|
background: #f8f9fa;
|
|||
|
|
color: #495057;
|
|||
|
|
text-decoration: none;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
border: 1px solid #dee2e6;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-pagination a:hover {
|
|||
|
|
background: #e9ecef;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-pagination .current {
|
|||
|
|
background: #007bff;
|
|||
|
|
color: white;
|
|||
|
|
border-color: #007bff;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 空状态 */
|
|||
|
|
.memo-empty {
|
|||
|
|
text-align: center;
|
|||
|
|
padding: 40px 20px;
|
|||
|
|
color: #999;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 筛选信息 */
|
|||
|
|
.memo-filter-info {
|
|||
|
|
text-align: center;
|
|||
|
|
margin: 10px 0 20px;
|
|||
|
|
padding: 8px 12px;
|
|||
|
|
background: #e7f3ff;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
color: #0066cc;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-filter-info a {
|
|||
|
|
color: #666;
|
|||
|
|
margin-left: 10px;
|
|||
|
|
text-decoration: none;
|
|||
|
|
font-size: 12px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@media (max-width: 768px) {
|
|||
|
|
.memo-container {
|
|||
|
|
padding: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-search input {
|
|||
|
|
width: 100%;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-search button {
|
|||
|
|
width: 100%;
|
|||
|
|
margin-left: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.memo-item-header {
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: flex-start;
|
|||
|
|
gap: 5px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
<div class="memo-container">
|
|||
|
|
<h2 class="memo-title">备忘录</h2>
|
|||
|
|
|
|||
|
|
<!-- 搜索框 -->
|
|||
|
|
<div class="memo-search">
|
|||
|
|
<form method="get" action="<?php echo $currentFile; ?>">
|
|||
|
|
<input type="text" name="search" placeholder="搜索备忘录内容..." value="<?php echo htmlspecialchars($searchKeyword); ?>">
|
|||
|
|
<?php if ($selectedCategory): ?>
|
|||
|
|
<input type="hidden" name="cat" value="<?php echo htmlspecialchars($selectedCategory); ?>">
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<button type="submit">搜索</button>
|
|||
|
|
<?php if ($searchKeyword || $selectedCategory): ?>
|
|||
|
|
<a href="<?php echo $currentFile; ?>" style="margin-left: 10px; font-size: 13px; color: #666;">清除筛选</a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 分类筛选 -->
|
|||
|
|
<?php if (!empty($categories)): ?>
|
|||
|
|
<div class="memo-categories">
|
|||
|
|
<ul class="memo-category-list">
|
|||
|
|
<li class="memo-category-item">
|
|||
|
|
<a href="<?php echo $currentFile; ?>"
|
|||
|
|
class="memo-category-link <?php echo empty($selectedCategory) ? 'active' : ''; ?>">
|
|||
|
|
全部
|
|||
|
|
</a>
|
|||
|
|
</li>
|
|||
|
|
<?php foreach ($categories as $category): ?>
|
|||
|
|
<li class="memo-category-item">
|
|||
|
|
<a href="<?php echo $currentFile . '?cat=' . urlencode($category); ?>"
|
|||
|
|
class="memo-category-link <?php echo $selectedCategory == $category ? 'active' : ''; ?>">
|
|||
|
|
<?php echo htmlspecialchars($category); ?>
|
|||
|
|
<?php if (isset($categoryCounts[$category])): ?>
|
|||
|
|
<span class="memo-category-count">(<?php echo $categoryCounts[$category]; ?>)</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</a>
|
|||
|
|
</li>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<!-- 筛选信息 -->
|
|||
|
|
<?php if ($searchKeyword || $selectedCategory): ?>
|
|||
|
|
<div class="memo-filter-info">
|
|||
|
|
<?php if ($searchKeyword && $selectedCategory): ?>
|
|||
|
|
搜索"<strong><?php echo htmlspecialchars($searchKeyword); ?></strong>",分类"<strong><?php echo htmlspecialchars($selectedCategory); ?></strong>",共找到 <?php echo $total; ?> 条记录
|
|||
|
|
<?php elseif ($searchKeyword): ?>
|
|||
|
|
搜索"<strong><?php echo htmlspecialchars($searchKeyword); ?></strong>",共找到 <?php echo $total; ?> 条记录
|
|||
|
|
<?php elseif ($selectedCategory): ?>
|
|||
|
|
分类"<strong><?php echo htmlspecialchars($selectedCategory); ?></strong>",共 <?php echo $total; ?> 条记录
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<!-- 备忘录列表 -->
|
|||
|
|
<?php if (!empty($memos)): ?>
|
|||
|
|
<div class="memo-list">
|
|||
|
|
<?php foreach ($memos as $memo): ?>
|
|||
|
|
<div class="memo-item">
|
|||
|
|
<div class="memo-item-header">
|
|||
|
|
<?php if (!empty($memo['category'])): ?>
|
|||
|
|
<span class="memo-category"><?php echo htmlspecialchars($memo['category']); ?></span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if (!empty($memo['event_date']) && isset($config->showEventDate) && $config->showEventDate == 1): ?>
|
|||
|
|
<span class="memo-date"><?php echo date('Y.m.d', strtotime($memo['event_date'])); ?></span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="memo-content">
|
|||
|
|
<?php
|
|||
|
|
$content = $memo['content'];
|
|||
|
|
// 修复2:精确匹配URL,只对URL本身加链接
|
|||
|
|
$content = htmlspecialchars($content);
|
|||
|
|
|
|||
|
|
// 精确的URL正则匹配
|
|||
|
|
$content = preg_replace_callback(
|
|||
|
|
'/(https?:\/\/[a-zA-Z0-9][-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)/',
|
|||
|
|
function($matches) {
|
|||
|
|
$url = $matches[1];
|
|||
|
|
// 清理URL末尾可能错误的标点符号
|
|||
|
|
$punctuation = array('.', ',', ';', ':', '!', '?', ')', ']', '}');
|
|||
|
|
$lastChar = substr($url, -1);
|
|||
|
|
if (in_array($lastChar, $punctuation)) {
|
|||
|
|
$url = substr($url, 0, -1);
|
|||
|
|
$suffix = $lastChar;
|
|||
|
|
} else {
|
|||
|
|
$suffix = '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return '<a href="' . htmlspecialchars($url) . '" class="memo-url-link" target="_blank" rel="noopener noreferrer">' . htmlspecialchars($url) . '</a>' . $suffix;
|
|||
|
|
},
|
|||
|
|
$content
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
if (!empty($searchKeyword)) {
|
|||
|
|
$searchEncoded = htmlspecialchars($searchKeyword);
|
|||
|
|
$content = preg_replace(
|
|||
|
|
'/(' . preg_quote($searchEncoded, '/') . ')/i',
|
|||
|
|
'<mark style="background: yellow;">$1</mark>',
|
|||
|
|
$content
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
echo nl2br($content);
|
|||
|
|
?>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<?php if (!empty($memo['original_url'])): ?>
|
|||
|
|
<div class="memo-original-link">
|
|||
|
|
<div class="memo-original-title">原文链接:</div>
|
|||
|
|
<a href="<?php echo htmlspecialchars($memo['original_url']); ?>"
|
|||
|
|
class="memo-original-url"
|
|||
|
|
target="_blank"
|
|||
|
|
rel="noopener noreferrer">
|
|||
|
|
<?php echo htmlspecialchars($memo['original_url']); ?>
|
|||
|
|
</a>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if (isset($config->showRelatedPosts) && $config->showRelatedPosts == 1 && !empty($memo['posts'])): ?>
|
|||
|
|
<div class="memo-related-posts">
|
|||
|
|
<div class="memo-related-title"><?php echo isset($config->relatedPostsTitle) ? $config->relatedPostsTitle : '相关文章'; ?></div>
|
|||
|
|
<ul class="memo-related-list">
|
|||
|
|
<?php foreach ($memo['posts'] as $post): ?>
|
|||
|
|
<li class="memo-related-item">
|
|||
|
|
<a href="<?php echo $post['url']; ?>" class="memo-related-link" target="_blank">
|
|||
|
|
<?php echo htmlspecialchars($post['title']); ?>
|
|||
|
|
</a>
|
|||
|
|
</li>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 分页 -->
|
|||
|
|
<?php if ($totalPages > 1): ?>
|
|||
|
|
<div class="memo-pagination">
|
|||
|
|
<?php
|
|||
|
|
$start = max(1, $currentPage - 2);
|
|||
|
|
$end = min($totalPages, $currentPage + 2);
|
|||
|
|
|
|||
|
|
if ($currentPage > 1): ?>
|
|||
|
|
<a href="<?php echo $currentFile; ?>?page=<?php echo $currentPage-1; ?><?php echo $selectedCategory ? '&cat=' . urlencode($selectedCategory) : ''; ?><?php echo $searchKeyword ? '&search=' . urlencode($searchKeyword) : ''; ?>">上一页</a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if ($start > 1): ?>
|
|||
|
|
<a href="<?php echo $currentFile; ?>?page=1<?php echo $selectedCategory ? '&cat=' . urlencode($selectedCategory) : ''; ?><?php echo $searchKeyword ? '&search=' . urlencode($searchKeyword) : ''; ?>">1</a>
|
|||
|
|
<?php if ($start > 2): ?>...<?php endif; ?>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php for ($i = $start; $i <= $end; $i++): ?>
|
|||
|
|
<?php if ($i == $currentPage): ?>
|
|||
|
|
<span class="current"><?php echo $i; ?></span>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<a href="<?php echo $currentFile; ?>?page=<?php echo $i; ?><?php echo $selectedCategory ? '&cat=' . urlencode($selectedCategory) : ''; ?><?php echo $searchKeyword ? '&search=' . urlencode($searchKeyword) : ''; ?>"><?php echo $i; ?></a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
<?php endfor; ?>
|
|||
|
|
|
|||
|
|
<?php if ($end < $totalPages): ?>
|
|||
|
|
<?php if ($end < $totalPages - 1): ?>...<?php endif; ?>
|
|||
|
|
<a href="<?php echo $currentFile; ?>?page=<?php echo $totalPages; ?><?php echo $selectedCategory ? '&cat=' . urlencode($selectedCategory) : ''; ?><?php echo $searchKeyword ? '&search=' . urlencode($searchKeyword) : ''; ?>"><?php echo $totalPages; ?></a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php if ($currentPage < $totalPages): ?>
|
|||
|
|
<a href="<?php echo $currentFile; ?>?page=<?php echo $currentPage+1; ?><?php echo $selectedCategory ? '&cat=' . urlencode($selectedCategory) : ''; ?><?php echo $searchKeyword ? '&search=' . urlencode($searchKeyword) : ''; ?>">下一页</a>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
|
|||
|
|
<?php else: ?>
|
|||
|
|
<div class="memo-empty">
|
|||
|
|
<?php if ($searchKeyword || $selectedCategory): ?>
|
|||
|
|
没有找到符合条件的备忘录
|
|||
|
|
<?php else: ?>
|
|||
|
|
暂无备忘录记录
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
<?php
|
|||
|
|
}
|
|||
|
|
}
|