forked from AlanDecode/VOID-Plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPlugin.php
More file actions
executable file
·373 lines (339 loc) · 13.1 KB
/
Copy pathPlugin.php
File metadata and controls
executable file
·373 lines (339 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;
/**
* VOID 主题配套插件
*
* @package VOID
* @author 熊猫小A
* @version 1.4.0
* @link https://blog.imalan.cn
*/
require_once __DIR__ . '/libs/bootstrap.php';
// 为兼容 Typecho 1.3 移除的旧式 Interface 别名
if (!interface_exists('Typecho_Plugin_Interface') && interface_exists('Typecho\Plugin\PluginInterface')) {
class_alias('Typecho\Plugin\PluginInterface', 'Typecho_Plugin_Interface');
}
class VOID_Plugin implements Typecho_Plugin_Interface
{
public static $VERSION = '1.4.0';
private static function assetVersion($relativePath)
{
$relativePath = ltrim((string)$relativePath, '/');
return @filemtime(__DIR__ . '/' . $relativePath) ?: self::$VERSION;
}
private static function cleanupLegacyActivityPanel()
{
try {
Helper::removePanel(3, 'VOID/pages/showActivity.php');
} catch (Exception $e) {
}
}
public static function editorStatsPanel()
{
ob_start();
Helper::options()->index('/action/void?wordcount_preview=1');
$previewUrl = trim(ob_get_clean());
$cssPath = 'assets/admin/editor/stats.css';
$jsPath = 'assets/admin/editor/stats.js';
$cssVersion = self::assetVersion($cssPath);
$jsVersion = self::assetVersion($jsPath);
$config = array(
'previewUrl' => $previewUrl
);
?>
<script>window.VOIDEditorStatsConfig = <?php echo json_encode($config, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;</script>
<link rel="stylesheet" href="<?php Helper::options()->pluginUrl('VOID/' . $cssPath); ?>?v=<?php echo rawurlencode((string)$cssVersion); ?>">
<script src="<?php Helper::options()->pluginUrl('VOID/' . $jsPath); ?>?v=<?php echo rawurlencode((string)$jsVersion); ?>"></script>
<?php
}
private static function hasColumn($table, $field) {
$db = Typecho_Db::get();
// 兼容性:改为精确匹配,避免 LIKE 模糊匹配导致误判
$rows = $db->fetchAll("SHOW COLUMNS FROM `".$table."`");
foreach ($rows as $row) {
if (isset($row['Field']) && $row['Field'] === $field) {
return true;
}
}
return false;
}
private static function hasTable($table) {
$db = Typecho_Db::get();
// 兼容性:改为精确匹配,避免 LIKE 模糊匹配导致误判
$rows = $db->fetchAll("SHOW TABLES");
foreach ($rows as $row) {
if ($table === reset($row)) {
return true;
}
}
return false;
}
private static function hasIndex($table, $indexName) {
$db = Typecho_Db::get();
$rows = $db->fetchAll("SHOW INDEX FROM `".$table."`");
foreach ($rows as $row) {
if (isset($row['Key_name']) && $row['Key_name'] === $indexName) {
return true;
}
}
return false;
}
private static function queryAndCatch($sql) {
$db = Typecho_Db::get();
try {
$db->query($sql);
} catch (Typecho_Db_Query_Exception $th) {
throw new Typecho_Plugin_Exception($th->getMessage());
}
}
/**
* 激活插件方法,如果激活失败,直接抛出异常
*
* @access public
* @return void
* @throws Typecho_Plugin_Exception
*/
public static function activate()
{
// 检查数据库类型
$db = Typecho_Db::get();
$prefix = $db->getPrefix();
$adapterName = strtolower($db->getAdapterName() ?? '');
if (strpos($adapterName, 'mysql') === false) {
throw new Typecho_Plugin_Exception('启用失败,本插件暂时只支持 MySQL 数据库,您的数据库是:'.$adapterName);
}
// 检查是否存在对应扩展
if (!extension_loaded('openssl')) {
throw new Typecho_Plugin_Exception('启用失败,PHP 需启用 OpenSSL 扩展。');
}
if (!class_exists('DOMDocument') || !class_exists('DOMXPath')) {
throw new Typecho_Plugin_Exception('启用失败,PHP 需启用 DOM 扩展。');
}
try {
VOID_IpDb::bootstrapOrFail();
} catch (Exception $e) {
throw new Typecho_Plugin_Exception('启用失败,IP 数据库初始化失败:' . $e->getMessage());
}
/** 图片附件尺寸解析,注册 hook */
Typecho_Plugin::factory('Widget_Upload')->upload = array('VOID_Plugin', 'upload');
/** 字数统计 */
// contents 表中若无 wordCount 字段则添加
if (!self::hasColumn($prefix.'contents', 'wordCount')) {
self::queryAndCatch('ALTER TABLE `'. $prefix .'contents` ADD COLUMN `wordCount` INT(10) DEFAULT 0;');
}
// 更新一次字数统计
VOID_WordCount::updateAllWordCount();
// 注册 hook
Typecho_Plugin::factory('Widget_Contents_Post_Edit')->finishPublish = array('VOID_Plugin', 'updateContent');
Typecho_Plugin::factory('Widget_Contents_Page_Edit')->finishPublish = array('VOID_Plugin', 'updateContent');
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('VOID_Plugin', 'editorStatsPanel');
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('VOID_Plugin', 'editorStatsPanel');
// 加入查询
Typecho_Plugin::factory('Widget_Archive')->___wordCount = array('VOID_Plugin', 'wordCount');
/** 文章点赞 */
// 创建字段
if (!self::hasColumn($prefix.'contents', 'likes')) {
self::queryAndCatch('ALTER TABLE `'. $prefix .'contents` ADD COLUMN `likes` INT(10) DEFAULT 0;');
}
// 加入查询
Typecho_Plugin::factory('Widget_Archive')->___likes = array('VOID_Plugin', 'likes');
/** 评论赞踩 */
// 创建字段
if (!self::hasColumn($prefix.'comments', 'likes')) {
self::queryAndCatch('ALTER TABLE `'. $prefix .'comments` ADD COLUMN `likes` INT(10) DEFAULT 0;');
}
if (!self::hasColumn($prefix.'comments', 'dislikes')) {
self::queryAndCatch('ALTER TABLE `'. $prefix .'comments` ADD COLUMN `dislikes` INT(10) DEFAULT 0;');
}
/** 浏览量统计 */
// 创建字段
if (!self::hasColumn($prefix.'contents', 'viewsNum')) {
self::queryAndCatch('ALTER TABLE `'. $prefix .'contents` ADD COLUMN `viewsNum` INT(10) DEFAULT 0;');
}
//增加浏览数
Typecho_Plugin::factory('Widget_Archive')->beforeRender = array('VOID_Plugin', 'updateViewCount');
// 加入查询
Typecho_Plugin::factory('Widget_Archive')->___viewsNum = array('VOID_Plugin', 'viewsNum');
/** 点赞与投票数据库 */
// 创建表,保存点赞与投票相关信息
$table_name = $prefix . 'votes';
if (!self::hasTable($table_name)) {
$sql = 'create table IF NOT EXISTS `'.$table_name.'` (
`vid` int unsigned auto_increment,
`id` int unsigned not null,
`table` char(32) not null,
`type` char(32) not null,
`agent` text,
`ip` varchar(128),
`created` int unsigned default 0,
primary key (`vid`)
) default charset=utf8;
CREATE INDEX index_ip ON '.$table_name.'(`ip`);
CREATE INDEX index_id ON '.$table_name.'(`id`);
CREATE INDEX index_table ON '.$table_name.'(`table`);
CREATE INDEX index_created ON '.$table_name.'(`created`)';
$sqls = explode(';', $sql);
foreach ($sqls as $sql) {
self::queryAndCatch($sql);
}
} else {
if (!self::hasColumn($prefix.'votes', 'created')) {
self::queryAndCatch('ALTER TABLE `'. $prefix .'votes` ADD COLUMN `created` INT(10) DEFAULT 0;');
}
if (!self::hasIndex($prefix.'votes', 'index_created')) {
self::queryAndCatch('CREATE INDEX index_created ON `'. $prefix .'votes`(`created`)');
}
}
// 添加一个面板,展示互动信息,例如评论赞踩、文章点赞
self::cleanupLegacyActivityPanel();
Helper::addPanel(3, 'VOID/pages/activity.php', '互动', '查看访客互动', 'administrator');
// 历史版本曾注册独立 IP 数据库面板,当前已迁移到插件设置页中管理。
Helper::removePanel(3, 'VOID/pages/ipdb.php');
// 添加投票路由,文章与评论
Helper::addAction('void', 'VOID_Action');
// 评论列表显示来源
Typecho_Plugin::factory('Widget_Comments_Admin')->callIp = array('VOID_Plugin', 'commentLocation');
}
/**
* 禁用插件方法,如果禁用失败,直接抛出异常
*
* @static
* @access public
* @return void
* @throws Typecho_Plugin_Exception
*/
public static function deactivate()
{
Helper::removeAction('void');
Helper::removeAction('void_vote');
Helper::removePanel(3, 'VOID/pages/activity.php');
Helper::removePanel(3, 'VOID/pages/showActivity.php');
Helper::removePanel(3, 'VOID/pages/ipdb.php');
}
/**
* 获取插件配置面板
*
* @access public
* @param Typecho_Widget_Helper_Form $form 配置面板
* @return void
*/
public static function config(Typecho_Widget_Helper_Form $form)
{
// 可设置每次获取图片基础信息数量上限
$parseImgLimit = new Typecho_Widget_Helper_Form_Element_Text('parseImgLimit', NULL, '10', _t('单次图片处理数量上限'),
_t('这里是每次获取图片基础信息的数量上限。不建议设置过大的数值,太大可能导致处理超时。'));
$form->addInput($parseImgLimit);
}
/**
* 个人用户的配置面板
*
* @access public
* @param Typecho_Widget_Helper_Form $form
* @return void
*/
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
/**
* 返回文章字数
*/
public static function viewsNum($archive)
{
$db = Typecho_Db::get();
$row = $db->fetchRow($db->select('viewsNum')
->from('table.contents')
->where('cid = ?', $archive->cid));
return $row['viewsNum'];
}
/**
* 返回文章点赞数
*/
public static function likes($archive)
{
$db = Typecho_Db::get();
$row = $db->fetchRow($db->select('likes')
->from('table.contents')
->where('cid = ?', $archive->cid));
return $row['likes'];
}
/**
* 返回文章字数
*/
public static function wordCount($archive)
{
$db = Typecho_Db::get();
$row = $db->fetchRow($db->select('wordCount')
->from('table.contents')
->where('cid = ?', $archive->cid));
return $row['wordCount'];
}
/**
* 更新文章字数统计
*
* @access public
* @param mixed $archive
* @return void
*/
public static function updateContent($contents, $widget)
{
VOID_WordCount::wordCountByCid($widget->cid);
$ret = VOID_ParseImgInfo::parse($widget->cid);
}
/**
* 更新文章浏览量
*
* @param Widget_Archive $archive
* @return void
*/
public static function updateViewCount($archive) {
if($archive->is('single')){
$cid = $archive->cid;
$views = Typecho_Cookie::get('__void_post_views');
if(empty($views)){
$views = array();
} else {
$views = explode(',', $views ?? '');
}
if(!in_array($cid,$views)){
$db = Typecho_Db::get();
$row = $db->fetchRow($db->select('viewsNum')
->from('table.contents')
->where('cid = ?', $cid));
$db->query($db->update('table.contents')
->rows(array('viewsNum' => (int)$row['viewsNum']+1))
->where('cid = ?', $cid));
array_push($views, $cid);
$views = implode(',', $views);
Typecho_Cookie::set('__void_post_views', $views); //记录查看cookie
}
}
}
/**
* 在附件链接尾部添加后缀
*
* @access public
* @param Widget_Upload $uploadObj 上传对象
* @return void
*/
public static function upload($uploadObj)
{
// 若是图片,则增加后缀
if ($uploadObj->attachment->isImage) {
$meta = getimagesize(__TYPECHO_ROOT_DIR__.$uploadObj->attachment->path);
if ($meta != false) {
$uploadObj->attachment->url =
$uploadObj->attachment->url.'#vwid='.$meta[0].'&vhei='.$meta[1];
}
}
}
/**
* 插件实现方法
*
* @access public
* @param Typecho_Widget $comments 评论
* @return void
*/
public static function commentLocation($comments)
{
$location = VOID_IpDb::locate($comments->ip);
echo $comments->ip . '<br>' . $location;
}
}