Files
EditHistory/Plugin.php
2026-02-23 17:26:10 +08:00

943 lines
31 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文章编辑历史
*
* @package EditHistory
* @version 5.4.0
* @author 石头厝
* @link https://www.shitoucuo.com
*/
class EditHistory_Plugin implements Typecho_Plugin_Interface
{
private static $tableName = 'edit_history';
public static function activate()
{
self::createTable();
Typecho_Plugin::factory('Widget_Contents_Post_Edit')->write = array(__CLASS__, 'saveEditHistory');
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx = array(__CLASS__, 'parseContent');
Typecho_Plugin::factory('Widget_Archive')->header = array(__CLASS__, 'outputHeader');
// 🔥 新增:文章删除时同时删除编辑历史
Typecho_Plugin::factory('Widget_Contents_Post_Edit')->delete = array(__CLASS__, 'deletePostHistory');
// 添加后台管理菜单 - 使用正确的Helper方法
Helper::addPanel(1, 'EditHistory/manage-panel.php', '历史管理', '查看编辑历史', 'administrator');
return '编录插件已激活';
}
public static function deactivate()
{
// 使用Helper::removePanel()方法正确移除菜单
try {
Helper::removePanel(1, 'EditHistory/manage-panel.php');
} catch (Exception $e) {
// 如果Helper方法失败尝试直接删除数据库记录
try {
$db = Typecho_Db::get();
$db->query($db->delete('table.options')
->where('name = ?', 'panel:EditHistory/manage-panel.php'));
} catch (Exception $e2) {
// 静默失败
}
}
return '编辑记录插件已禁用';
}
public static function config(Typecho_Widget_Helper_Form $form)
{
$position = new Typecho_Widget_Helper_Form_Element_Radio(
'position',
array('auto' => '自动在文章末尾显示', 'manual' => '手动调用', 'both' => '两者都显示'),
'auto',
'显示位置'
);
$form->addInput($position);
$maxRecords = new Typecho_Widget_Helper_Form_Element_Text('maxRecords', NULL, '10', '最大显示记录数');
$maxRecords->input->setAttribute('type', 'number');
$form->addInput($maxRecords);
$showPublishDays = new Typecho_Widget_Helper_Form_Element_Radio(
'showPublishDays',
array('1' => '显示', '0' => '不显示'),
'1',
'显示发布天数'
);
$form->addInput($showPublishDays);
$timeFormat = new Typecho_Widget_Helper_Form_Element_Select(
'timeFormat',
array('detail' => '详细格式', 'simple' => '简洁格式', 'relative' => '相对时间'),
'detail',
'时间显示格式'
);
$form->addInput($timeFormat);
$showSummary = new Typecho_Widget_Helper_Form_Element_Radio(
'showSummary',
array('1' => '显示', '0' => '不显示'),
'1',
'显示编辑摘要'
);
$form->addInput($showSummary);
$summaryLength = new Typecho_Widget_Helper_Form_Element_Text('summaryLength', NULL, '100', '摘要长度');
$form->addInput($summaryLength);
$requireEditSummary = new Typecho_Widget_Helper_Form_Element_Radio(
'requireEditSummary',
array('1' => '启用(必填)', '0' => '禁用(可选)'),
'0',
'编辑摘要必填'
);
$form->addInput($requireEditSummary);
$editSummaryPlaceholder = new Typecho_Widget_Helper_Form_Element_Text(
'editSummaryPlaceholder',
NULL,
'请简要描述本次编辑的内容(例如:修正错别字、更新数据、补充说明等)',
'编辑摘要提示文字'
);
$form->addInput($editSummaryPlaceholder);
$defaultCollapsed = new Typecho_Widget_Helper_Form_Element_Radio(
'defaultCollapsed',
array('1' => '默认收起', '0' => '默认展开'),
'1',
'默认显示状态'
);
$form->addInput($defaultCollapsed);
}
public static function personalConfig(Typecho_Widget_Helper_Form $form) {}
private static function createTable()
{
try {
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
$sql = "CREATE TABLE IF NOT EXISTS `{$prefix}edit_history` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cid` int(10) unsigned NOT NULL,
`editor` int(10) unsigned DEFAULT NULL,
`edit_time` int(10) unsigned NOT NULL,
`edit_content` text,
`edit_type` varchar(20) DEFAULT 'update',
PRIMARY KEY (`id`),
KEY `idx_cid` (`cid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
$db->query($sql);
} catch (Exception $e) {
// 静默失败
}
}
/**
* 🔥 新增:删除文章时同时删除编辑历史
*/
public static function deletePostHistory($cid, $widget)
{
try {
$db = Typecho_Db::get();
// 删除该文章的所有编辑历史
$db->query($db->delete('table.' . self::$tableName)
->where('cid = ?', $cid));
} catch (Exception $e) {
// 静默失败
}
return $cid;
}
/**
* 保存编辑记录
*/
public static function saveEditHistory($contents, $widget)
{
try {
$db = Typecho_Db::get();
// 只在有文章ID时保存
if ($widget->cid) {
// 🔥 关键修复:检查是否是真正的发布操作
// 获取当前文章状态
$currentStatus = $widget->status;
// 🔥 重要:草稿状态下的任何操作都不应该记录编辑历史
if ($currentStatus === 'draft' || $currentStatus === 'waiting' || $currentStatus === 'private') {
// 草稿、待审核、私密文章状态下的任何保存都不记录
return $contents;
}
// 🔥 重要:只有已发布的文章才可能记录编辑历史
if ($currentStatus !== 'publish') {
return $contents;
}
// 🔥 现在只处理已发布文章的编辑
// 检查是否是真正的用户编辑操作
$isRealEdit = false;
// 方法1检查是否有编辑摘要用户主动填写了编辑说明
if (isset($_POST['fields']['editSummary']) && !empty(trim($_POST['fields']['editSummary']))) {
$isRealEdit = true;
}
// 方法2检查是否有明确的发布操作
if (isset($_POST['do']) && $_POST['do'] === 'publish') {
// 已经是发布状态,检查是否有内容变化
if (!$isRealEdit) {
// 检查文章内容是否有变化(简单判断)
$oldContent = $widget->text;
$newContent = isset($_POST['text']) ? $_POST['text'] : '';
if ($oldContent != $newContent) {
$isRealEdit = true;
}
}
}
// 方法3检查自动保存标记 - 排除自动保存
$isAutoSave = false;
if (isset($_POST['_']) || isset($_POST['autoSave']) || isset($_POST['autosave'])) {
$isAutoSave = true;
}
// 方法4检查时间间隔 - 短时间内重复提交可能是自动保存
static $lastSaveTime = 0;
$currentTime = time();
if ($currentTime - $lastSaveTime < 10) { // 10秒内重复保存
$isAutoSave = true;
}
// 方法5检查是否是保存草稿或预览
if (isset($_POST['do']) && ($_POST['do'] === 'save' || $_POST['do'] === 'preview')) {
return $contents; // 直接返回,不记录
}
// 🔥 最终判断:只有真正的编辑操作才记录
if (!$isRealEdit || $isAutoSave) {
return $contents;
}
// 更新最后保存时间
$lastSaveTime = $currentTime;
$user = Typecho_Widget::widget('Widget_User');
$time = time();
// 获取编辑摘要
$editSummary = '';
// 从POST数据中获取编辑摘要
if (isset($_POST['fields']['editSummary'])) {
$editSummary = trim($_POST['fields']['editSummary']);
}
// 准备编辑内容摘要
$summaryContent = '';
if (!empty($editSummary)) {
$summaryContent = $editSummary;
} else {
$summaryContent = '未填写编辑说明';
}
// 保存到数据库
$data = array(
'cid' => $widget->cid,
'editor' => $user->uid ? $user->uid : 1,
'edit_time' => $time,
'edit_content' => $summaryContent,
'edit_type' => 'update'
);
// 执行数据库插入
try {
$result = $db->query($db->insert('table.' . self::$tableName)->rows($data));
} catch (Exception $e) {
// 静默失败
}
}
} catch (Exception $e) {
// 静默失败
}
return $contents;
}
/**
* 提供给主题的字段添加方法
*/
public static function addFieldToLayout($layout)
{
$options = Typecho_Widget::widget('Widget_Options');
$pluginOptions = $options->plugin('EditHistory');
$placeholder = isset($pluginOptions->editSummaryPlaceholder) ?
$pluginOptions->editSummaryPlaceholder :
'请简要描述本次编辑的内容(例如:修正错别字、更新数据、补充说明等)';
$required = isset($pluginOptions->requireEditSummary) && $pluginOptions->requireEditSummary == '1';
$editSummary = new Typecho_Widget_Helper_Form_Element_Textarea(
'editSummary',
NULL,
NULL,
'编辑说明',
'此备注会显示在文章的编辑记录中,修改一次填写一次,自动记录修改'
);
$editSummary->input->setAttribute('rows', '4');
$editSummary->input->setAttribute('placeholder', $placeholder);
$editSummary->input->setAttribute('style', 'width: 100%; padding: 10px; border-radius: 4px; resize: vertical;');
// 使用 fields[] 数组作为name这是Typecho自定义字段的标准方式
$editSummary->input->setAttribute('name', 'fields[editSummary]');
$layout->addItem($editSummary);
}
private static function getContentSummary($content, $length = 100)
{
if (empty($content)) return '';
$content = strip_tags($content);
$content = trim($content);
return Typecho_Common::subStr($content, 0, $length, '...');
}
/**
* 输出CSS样式和JavaScript
*/
public static function outputHeader()
{
echo '<style>
.edit-history-container {
margin: 0 0;
padding: 0;
width:100%;
}
.edit-history-box {
border-radius: 10px;
background: #ffffff;
overflow: hidden;
}
.dark .edit-history-box {
background: rgb(30 41 59 / var(--tw-bg-opacity));
}
.edit-history-header {
padding: 18px 24px;
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
color: white;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
user-select: none;
}
.dark .edit-history-header {
background: rgb(10 12 25 / var(--tw-bg-opacity));
}
.edit-history-title {
font-size: 18px;
font-weight: 600;
margin: 0;
display: flex;
align-items: center;
gap: 10px;
}
.edit-history-count {
background: rgba(255,255,255,0.2);
padding: 2px 8px;
border-radius: 12px;
font-size: 13px;
}
.edit-history-toggle {
background: rgba(255,255,255,0.2);
border: none;
color: white;
padding: 6px 12px;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
}
.edit-history-toggle:hover {
background: rgba(255,255,255,0.3);
transform: translateY(-1px);
}
.edit-history-toggle .icon {
font-size: 14px;
transition: transform 0.3s ease;
}
.edit-history-toggle.collapsed .icon {
transform: rotate(180deg);
}
.edit-history-content-wrapper {
overflow: hidden;
transition: max-height 0.5s ease;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
background: #ffffff;
}
.dark .edit-history-content-wrapper {
background: rgb(30 41 59 / var(--tw-bg-opacity));
}
.publish-info {
padding: 16px 24px;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
color: #495057;
font-size: 14px;
line-height: 1.6;
margin:0 auto;
}
.dark .publish-info {
background: rgb(51 65 85 / var(--tw-bg-opacity));
color: #cbd5e1;
}
.publish-days {
color: #dc2626;
background: rgba(106, 17, 203, 0.1);
padding: 5px 15px;
border-radius:20px;
}
.dark .publish-days {
background: rgb(10 12 25 / var(--tw-bg-opacity)) !important;
color: rgb(156 163 175 / var(--tw-text-opacity)) !important;
}
.edit-history-list {
list-style: none;
padding: 0;
margin: 0;
}
.edit-history-item {
padding: 18px 24px;
border-bottom: 1px solid #f1f3f5;
transition: all 0.3s ease;
position: relative;
}
.dark .edit-history-item {
border-bottom: 1px solid #334155;
}
.edit-history-item:last-child {
border-bottom: none;
}
.edit-history-item:hover {
background-color: #f8f9fa;
}
.dark .edit-history-item:hover {
background-color: rgb(51 65 85 / var(--tw-bg-opacity));
}
.edit-history-item.create-item {
background-color: #f0f7ff;
}
.dark .edit-history-item.create-item {
background-color: rgb(30 58 138 / 0.2);
}
.edit-history-time {
font-size: 13px;
color: #666;
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 6px;
}
.dark .edit-history-time {
color: #cbd5e1;
}
.edit-history-author {
font-size: 15px;
color: #333;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.dark .edit-history-author {
color: #e2e8f0;
}
.edit-history-action {
display: inline-block;
padding: 3px 10px;
border-radius: 15px;
font-size: 12px;
font-weight: 500;
}
.edit-history-action.create {
background: #1890ff;
color: white;
}
.edit-history-action.update {
background: #52c41a;
color: white;
}
.dark .edit-history-action.update{
background: rgb(10 12 25 / var(--tw-bg-opacity)) !important;
color: rgb(156 163 175 /1) !important;
}
.edit-history-summary {
display: inline-block;
margin-left: 8px;
font-size: 14px;
color: #666;
vertical-align: middle;
}
.dark .edit-history-summary {
color: #cbd5e1;
}
.edit-history-summary-text {
color: #1890ff;
font-weight: 500;
}
.dark .edit-history-summary-text {
color: #60a5fa;
}
.edit-history-summary-default {
color: #999;
}
.dark .edit-history-summary-default {
color: #94a3b8;
}
.edit-history-empty {
padding: 60px 24px;
color: #adb5bd;
font-size: 15px;
}
.dark .edit-history-empty {
color: #64748b;
}
.edit-history-footer {
padding: 14px 24px;
background: #f8f9fa;
font-size: 12px;
color: #868e96;
text-align: center;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
}
.dark .edit-history-footer {
background: rgb(51 65 85 / var(--tw-bg-opacity));
color: #94a3b8;
}
@media (max-width: 768px) {
.edit-history-box {
border-radius: 8px;
margin: 30px 0;
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.05);
}
.edit-history-header,
.edit-history-item,
.publish-info {
padding: 14px 16px;
}
.edit-history-footer {
flex-direction: column;
gap: 8px;
padding: 12px 16px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
.edit-history-title {
font-size: 16px;
}
.edit-history-summary {
margin-left: 0;
margin-top: 4px;
display: block;
width: 100%;
}
.edit-history-content-wrapper {
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
}
}
</style>';
}
public static function parseContent($content, $widget, $lastResult)
{
$content = empty($lastResult) ? $content : $lastResult;
if ($widget->is('single')) {
$options = Typecho_Widget::widget('Widget_Options');
$pluginOptions = $options->plugin('EditHistory');
$position = isset($pluginOptions->position) ? $pluginOptions->position : 'auto';
// 检查是否有编辑记录
$hasEditHistory = self::hasEditHistory($widget->cid);
// 只要有编辑记录就显示
if ($hasEditHistory) {
if ($position === 'auto' || $position === 'both') {
$editHistory = self::renderEditHistory($widget->cid);
if ($editHistory) {
$content .= $editHistory;
}
}
}
}
return $content;
}
/**
* 判断文章是否有编辑记录
*/
private static function hasEditHistory($cid)
{
try {
$db = Typecho_Db::get();
// 检查是否有更新记录
$updateCount = $db->fetchRow($db->select('COUNT(*) as count')
->from('table.' . self::$tableName)
->where('cid = ?', $cid));
return $updateCount && $updateCount['count'] > 0;
} catch (Exception $e) {
return false;
}
}
/**
* 渲染编辑记录HTML
*/
public static function renderEditHistory($cid = null)
{
if (!$cid) {
$widget = Typecho_Widget::widget('Widget_Archive');
if (!$widget->is('single')) return '';
$cid = $widget->cid;
}
try {
$db = Typecho_Db::get();
$options = Typecho_Widget::widget('Widget_Options');
$pluginOptions = $options->plugin('EditHistory');
// 获取文章信息
$post = $db->fetchRow($db->select('created', 'authorId')
->from('table.contents')
->where('cid = ?', $cid)
->where('type = ?', 'post')
->limit(1));
if (!$post) return '';
// 获取所有编辑记录
$maxRecords = isset($pluginOptions->maxRecords) ? intval($pluginOptions->maxRecords) : 10;
$limit = $maxRecords > 0 ? $maxRecords : 1000;
// 查询所有编辑记录,按时间倒序排列(最新的在最前面)
$history = $db->fetchAll($db->select(
'id',
'edit_time',
'edit_content',
'edit_type',
'editor'
)
->from('table.' . self::$tableName)
->where('cid = ?', $cid)
->order('edit_time', Typecho_Db::SORT_DESC)
->limit($limit));
if (empty($history)) return '';
// 计算正确的编辑次数
$totalEdits = count($history);
// 不使用引用传递,创建新数组
$formattedHistory = array();
foreach ($history as $index => $record) {
$formattedRecord = $record; // 复制数组,不使用引用
$formattedRecord['edit_number'] = $totalEdits - $index;
$formattedHistory[] = $formattedRecord;
}
// 获取用户信息
$userIds = array();
foreach ($formattedHistory as $record) {
if ($record['editor']) {
$userIds[$record['editor']] = $record['editor'];
}
}
$users = array();
if (!empty($userIds)) {
$userResults = $db->fetchAll($db->select('uid', 'screenName', 'name')
->from('table.users')
->where('uid IN (' . implode(',', $userIds) . ')'));
foreach ($userResults as $user) {
$users[$user['uid']] = $user;
}
}
// 计算发布天数
$publishDays = '';
if (isset($pluginOptions->showPublishDays) && $pluginOptions->showPublishDays == '1') {
$days = self::calculateDaysFromPublish($post['created']);
$publishDays = '<div class="publish-info">
<span class="publish-days">本文发布于' . $days . '前,内容可能有时效性,注意参考阅读</span>
</div>';
}
// 获取默认收起/展开设置
$defaultCollapsed = isset($pluginOptions->defaultCollapsed) && $pluginOptions->defaultCollapsed == '1';
$collapseClass = $defaultCollapsed ? 'collapsed' : '';
$toggleText = $defaultCollapsed ? '<!--展开-->' : '<!--收起-->';
$icon = $defaultCollapsed ? '↓' : '↑';
// 生成HTML
$html = '<div class="edit-history-container">
<div class="edit-history-box">
<div class="edit-history-header" onclick="toggleEditHistory(this)">
<h3 class="edit-history-title">
<!--📝-->编辑记录
<span class="edit-history-count">' . count($formattedHistory) . '次</span>
</h3>
<button class="editHistory-toggle ' . $collapseClass . '">
<span class="icon1">' . $icon . '</span>
<span class="text">' . $toggleText . '</span>
</button>
</div>
<div class="edit-history-content-wrapper" style="' . ($defaultCollapsed ? 'max-height: 0;' : 'max-height: 2000px;') . '">
' . $publishDays . '
<ul class="edit-history-list">';
foreach ($formattedHistory as $record) {
$timeFormat = isset($pluginOptions->timeFormat) ? $pluginOptions->timeFormat : 'detail';
$editTime = self::formatTime($record['edit_time'], $timeFormat);
// 显示第几次编辑
$editNumber = $record['edit_number'];
$actionText = "{$editNumber}次编辑";
// 获取作者名称
$authorName = '系统';
if ($record['editor'] && isset($users[$record['editor']])) {
$user = $users[$record['editor']];
if (!empty($user['screenName'])) {
$authorName = $user['screenName'];
} elseif (!empty($user['name'])) {
$authorName = $user['name'];
}
}
$html .= '<li class="edit-history-item update-item">
<div class="edit-history-time">🕒 ' . $editTime . '</div>
<div class="edit-history-author">
<span class="edit-history-action update">' . $actionText . '</span>
编辑人:' . htmlspecialchars($authorName);
// 显示编辑摘要
if (isset($pluginOptions->showSummary) && $pluginOptions->showSummary == '1' &&
!empty($record['edit_content'])) {
$content = $record['edit_content'];
// 判断是否是用户填写的摘要
if ($content != '未填写编辑说明') {
$html .= '<span class="edit-history-summary"> 修改摘要:<span class="edit-history-summary-text">' . htmlspecialchars($content) . '</span></span>';
} else {
$html .= '<span class="edit-history-summary"> 修改摘要:<span class="edit-history-summary-default">' . htmlspecialchars($content) . '</span></span>';
}
}
$html .= '</div></li>';
}
$html .= '</ul>
<div class="edit-history-footer">
<div>本文共有' . count($formattedHistory) . '次编辑修改记录</div>
<div>最后编辑:' . self::formatTime($formattedHistory[0]['edit_time'], $timeFormat) . '</div>
</div>
</div>
</div>
</div>
<script>
function toggleEditHistory(header) {
var wrapper = header.nextElementSibling;
var toggleBtn = header.querySelector(".editHistory-toggle");
var icon = toggleBtn.querySelector(".icon");
var text = toggleBtn.querySelector(".text");
if (wrapper.style.maxHeight && wrapper.style.maxHeight !== "0px") {
// 收起
wrapper.style.maxHeight = "0";
toggleBtn.classList.add("collapsed");
icon.textContent = "↓";
text.textContent = "";
} else {
// 展开
wrapper.style.maxHeight = wrapper.scrollHeight + "px";
toggleBtn.classList.remove("collapsed");
icon.textContent = "↑";
text.textContent = "";
}
}
// 初始设置
document.addEventListener("DOMContentLoaded", function() {
var wrappers = document.querySelectorAll(".edit-history-content-wrapper");
for (var i = 0; i < wrappers.length; i++) {
var wrapper = wrappers[i];
if (!wrapper.style.maxHeight) {
var header = wrapper.previousElementSibling;
var toggleBtn = header.querySelector(".editHistory-toggle");
if (toggleBtn.classList.contains("collapsed")) {
wrapper.style.maxHeight = "0";
} else {
wrapper.style.maxHeight = wrapper.scrollHeight + "px";
}
}
}
});
</script>';
return $html;
} catch (Exception $e) {
return '';
}
}
private static function calculateDaysFromPublish($publishTime)
{
$currentTime = time();
$diff = $currentTime - $publishTime;
$days = floor($diff / 86400);
if ($days == 0) {
$hours = floor($diff / 3600);
if ($hours == 0) {
$minutes = floor($diff / 60);
return $minutes > 0 ? $minutes . '分钟' : '刚刚';
}
return $hours . '小时';
} elseif ($days == 1) {
return '1天';
} else {
return $days . '天'; // 🔥 修改超过1天全部显示为天数
}
}
private static function formatTime($timestamp, $format)
{
switch ($format) {
case 'simple':
return date('Y年m月d日', $timestamp);
case 'relative':
return self::getRelativeTime($timestamp);
default:
return date('Y年m月d日 H:i', $timestamp);
}
}
private static function getRelativeTime($timestamp)
{
$current = time();
$diff = $current - $timestamp;
if ($diff < 60) return '刚刚';
elseif ($diff < 3600) return floor($diff / 60) . '分钟前';
elseif ($diff < 86400) return floor($diff / 3600) . '小时前';
elseif ($diff < 2592000) return floor($diff / 86400) . '天前';
else return floor($diff / 86400) . '天前'; // 🔥 修改超过30天也显示为天数
}
public static function output()
{
return self::renderEditHistory();
}
/**
* 获取文章永久链接
* 修改方法使用Typecho的标准方法获取文章链接
*/
public static function getPostPermalink($cid)
{
try {
$db = Typecho_Db::get();
$post = $db->fetchRow($db->select('slug', 'type', 'created')
->from('table.contents')
->where('cid = ?', $cid)
->where('type = ?', 'post')
->limit(1));
if (!$post) return '#';
// 使用Typecho的标准方法获取文章对象
$widget = Typecho_Widget::widget('Widget_Archive', array('type' => 'single'), array('cid' => $cid));
// 直接返回文章的永久链接
return $widget->permalink;
} catch (Exception $e) {
// 如果上述方法失败尝试简单构造URL
try {
$options = Typecho_Widget::widget('Widget_Options');
$siteUrl = rtrim($options->siteUrl, '/');
// 查询文章slug
$db = Typecho_Db::get();
$post = $db->fetchRow($db->select('slug')
->from('table.contents')
->where('cid = ?', $cid)
->limit(1));
if ($post && !empty($post['slug'])) {
return $siteUrl . '/' . $post['slug'] . '.html';
}
} catch (Exception $e2) {
return '#';
}
return '#';
}
}
}