mirror of
https://git.beihong.wang/wangbeihong/blog-source.git
synced 2026-04-23 18:13:03 +08:00
initial
This commit is contained in:
204
var/Utils/AutoP.php
Executable file
204
var/Utils/AutoP.php
Executable file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
namespace Utils;
|
||||
|
||||
/**
|
||||
* AutoP
|
||||
*
|
||||
* @copyright Copyright (c) 2012 Typecho Team. (http://typecho.org)
|
||||
* @author Joyqi <magike.net@gmail.com>
|
||||
* @license GNU General Public License 2.0
|
||||
*/
|
||||
class AutoP
|
||||
{
|
||||
// 作为段落的标签
|
||||
private const BLOCK = 'p|pre|div|blockquote|form|ul|ol|dd|table|ins|h1|h2|h3|h4|h5|h6';
|
||||
|
||||
/**
|
||||
* 唯一id
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private int $uniqueId = 0;
|
||||
|
||||
/**
|
||||
* 存储的段落
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private array $blocks = [];
|
||||
|
||||
/**
|
||||
* 替换段落的回调函数
|
||||
*
|
||||
* @param array $matches 匹配值
|
||||
* @return string
|
||||
*/
|
||||
public function replaceBlockCallback(array $matches): string
|
||||
{
|
||||
$tagMatch = '|' . $matches[1] . '|';
|
||||
$text = $matches[4];
|
||||
|
||||
switch (true) {
|
||||
/** 用br处理换行 */
|
||||
case false !== strpos(
|
||||
'|li|dd|dt|td|p|a|span|cite|strong|sup|sub|small|del|u|i|b|ins|h1|h2|h3|h4|h5|h6|',
|
||||
$tagMatch
|
||||
):
|
||||
$text = nl2br(trim($text));
|
||||
break;
|
||||
/** 用段落处理换行 */
|
||||
case false !== strpos('|div|blockquote|form|', $tagMatch):
|
||||
$text = $this->cutByBlock($text);
|
||||
if (false !== strpos($text, '</p><p>')) {
|
||||
$text = $this->fixParagraph($text);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/** 没有段落能力的标签 */
|
||||
if (false !== strpos('|a|span|font|code|cite|strong|sup|sub|small|del|u|i|b|', $tagMatch)) {
|
||||
$key = '<b' . $matches[2] . '/>';
|
||||
} else {
|
||||
$key = '<p' . $matches[2] . '/>';
|
||||
}
|
||||
|
||||
$this->blocks[$key] = "<{$matches[1]}{$matches[3]}>{$text}</{$matches[1]}>";
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用段落方法处理换行
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
private function cutByBlock(string $text): string
|
||||
{
|
||||
$space = "( | )";
|
||||
$text = str_replace("\r\n", "\n", trim($text));
|
||||
$text = preg_replace("/{$space}*\n{$space}*/is", "\n", $text);
|
||||
$text = preg_replace("/\s*<p:([0-9]{4})\/>\s*/is", "</p><p:\\1/><p>", $text);
|
||||
$text = preg_replace("/\n{2,}/", "</p><p>", $text);
|
||||
$text = nl2br($text);
|
||||
$text = preg_replace("/(<p>)?\s*<p:([0-9]{4})\/>\s*(<\/p>)?/is", "<p:\\2/>", $text);
|
||||
$text = preg_replace("/<p>{$space}*<\/p>/is", '', $text);
|
||||
$text = preg_replace("/\s*<p>\s*$/is", '', $text);
|
||||
$text = preg_replace("/^\s*<\/p>\s*/is", '', $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修复段落开头和结尾
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
private function fixParagraph(string $text): string
|
||||
{
|
||||
$text = trim($text);
|
||||
if (!preg_match("/^<(" . self::BLOCK . ")(\s|>)/i", $text)) {
|
||||
$text = '<p>' . $text;
|
||||
}
|
||||
|
||||
if (!preg_match("/<\/(" . self::BLOCK . ")>$/i", $text)) {
|
||||
$text = $text . '</p>';
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动分段
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public function parse(string $text): string
|
||||
{
|
||||
/** 重置计数器 */
|
||||
$this->uniqueId = 0;
|
||||
$this->blocks = [];
|
||||
|
||||
/** 将已有的段落后面的换行处理掉 */
|
||||
$text = preg_replace(["/<\/p>\s+<p(\s*)/is", "/\s*<br\s*\/?>\s*/is"], ["</p><p\\1", "<br />"], trim($text));
|
||||
|
||||
/** 将所有非自闭合标签解析为唯一的字符串 */
|
||||
$foundTagCount = 0;
|
||||
$textLength = strlen($text);
|
||||
$uniqueIdList = [];
|
||||
|
||||
if (preg_match_all("/<\/\s*([a-z0-9]+)>/is", $text, $matches, PREG_OFFSET_CAPTURE)) {
|
||||
foreach ($matches[0] as $key => $match) {
|
||||
$tag = $matches[1][$key][0];
|
||||
|
||||
$leftOffset = $match[1] - $textLength;
|
||||
$posSingle = strrpos($text, '<' . $tag . '>', $leftOffset);
|
||||
$posFix = strrpos($text, '<' . $tag . ' ', $leftOffset);
|
||||
$pos = false;
|
||||
|
||||
switch (true) {
|
||||
case (false !== $posSingle && false !== $posFix):
|
||||
$pos = max($posSingle, $posFix);
|
||||
break;
|
||||
case false === $posSingle && false !== $posFix:
|
||||
$pos = $posFix;
|
||||
break;
|
||||
case false !== $posSingle && false === $posFix:
|
||||
$pos = $posSingle;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (false !== $pos) {
|
||||
$uniqueId = $this->makeUniqueId();
|
||||
$uniqueIdList[$uniqueId] = $tag;
|
||||
$tagLength = strlen($tag);
|
||||
|
||||
$text = substr_replace($text, $uniqueId, $pos + 1 + $tagLength, 0);
|
||||
$text = substr_replace(
|
||||
$text,
|
||||
$uniqueId,
|
||||
$match[1] + 7 + $foundTagCount * 10 + $tagLength,
|
||||
0
|
||||
); // 7 = 5 + 2
|
||||
$foundTagCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($uniqueIdList as $uniqueId => $tag) {
|
||||
$text = preg_replace_callback(
|
||||
"/<({$tag})({$uniqueId})([^>]*)>(.*)<\/\\1\\2>/is",
|
||||
[$this, 'replaceBlockCallback'],
|
||||
$text,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
$text = $this->cutByBlock($text);
|
||||
$blocks = array_reverse($this->blocks);
|
||||
|
||||
foreach ($blocks as $blockKey => $blockValue) {
|
||||
$text = str_replace($blockKey, $blockValue, $text);
|
||||
}
|
||||
|
||||
return $this->fixParagraph($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成唯一的id, 为了速度考虑最多支持1万个tag的处理
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function makeUniqueId(): string
|
||||
{
|
||||
return ':' . str_pad($this->uniqueId ++, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
499
var/Utils/Helper.php
Executable file
499
var/Utils/Helper.php
Executable file
@@ -0,0 +1,499 @@
|
||||
<?php
|
||||
|
||||
namespace Utils;
|
||||
|
||||
use Typecho\Common;
|
||||
use Typecho\Db;
|
||||
use Typecho\I18n;
|
||||
use Typecho\Plugin;
|
||||
use Typecho\Widget;
|
||||
use Widget\Base\Options as BaseOptions;
|
||||
use Widget\Options;
|
||||
use Widget\Plugins\Edit;
|
||||
use Widget\Security;
|
||||
use Widget\Service;
|
||||
|
||||
/**
|
||||
* 插件帮手将默认出现在所有的typecho发行版中.
|
||||
* 因此你可以放心使用它的功能, 以方便你的插件安装在用户的系统里.
|
||||
*
|
||||
* @package Helper
|
||||
* @author qining
|
||||
* @version 1.0.0
|
||||
* @link http://typecho.org
|
||||
*/
|
||||
class Helper
|
||||
{
|
||||
/**
|
||||
* 获取Security对象
|
||||
*
|
||||
* @return Security
|
||||
*/
|
||||
public static function security(): Security
|
||||
{
|
||||
return Security::alloc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取单个Widget对象
|
||||
*
|
||||
* @param string $table 表名, 支持 contents, comments, metas, users
|
||||
* @param int $pkId
|
||||
* @return Widget|null
|
||||
*/
|
||||
public static function widgetById(string $table, int $pkId): ?Widget
|
||||
{
|
||||
$table = ucfirst($table);
|
||||
if (!in_array($table, ['Contents', 'Comments', 'Metas', 'Users'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$keys = [
|
||||
'Contents' => 'cid',
|
||||
'Comments' => 'coid',
|
||||
'Metas' => 'mid',
|
||||
'Users' => 'uid'
|
||||
];
|
||||
|
||||
$className = '\Widget\Base\\' . $table;
|
||||
|
||||
$key = $keys[$table];
|
||||
$db = Db::get();
|
||||
$widget = Widget::widget($className . '@' . $pkId);
|
||||
|
||||
$db->fetchRow(
|
||||
$widget->select()->where("{$key} = ?", $pkId)->limit(1),
|
||||
[$widget, 'push']
|
||||
);
|
||||
|
||||
return $widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求异步服务
|
||||
*
|
||||
* @param $method
|
||||
* @param $params
|
||||
*/
|
||||
public static function requestService($method, ... $params)
|
||||
{
|
||||
Service::alloc()->requestService($method, ... $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强行删除某个插件
|
||||
*
|
||||
* @param string $pluginName 插件名称
|
||||
*/
|
||||
public static function removePlugin(string $pluginName)
|
||||
{
|
||||
try {
|
||||
/** 获取插件入口 */
|
||||
[$pluginFileName, $className] = Plugin::portal(
|
||||
$pluginName,
|
||||
__TYPECHO_ROOT_DIR__ . '/' . __TYPECHO_PLUGIN_DIR__
|
||||
);
|
||||
|
||||
/** 获取已启用插件 */
|
||||
$plugins = Plugin::export();
|
||||
$activatedPlugins = $plugins['activated'];
|
||||
|
||||
/** 载入插件 */
|
||||
require_once $pluginFileName;
|
||||
|
||||
/** 判断实例化是否成功 */
|
||||
if (
|
||||
!isset($activatedPlugins[$pluginName]) || !class_exists($className)
|
||||
|| !method_exists($className, 'deactivate')
|
||||
) {
|
||||
throw new Widget\Exception(_t('无法禁用插件'), 500);
|
||||
}
|
||||
|
||||
call_user_func([$className, 'deactivate']);
|
||||
} catch (\Exception $e) {
|
||||
//nothing to do
|
||||
}
|
||||
|
||||
$db = Db::get();
|
||||
|
||||
try {
|
||||
Plugin::deactivate($pluginName);
|
||||
self::setOption('plugins', Plugin::export());
|
||||
} catch (Plugin\Exception $e) {
|
||||
//nothing to do
|
||||
}
|
||||
|
||||
$db->query($db->delete('table.options')->where('name = ?', 'plugin:' . $pluginName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入语言项
|
||||
*
|
||||
* @param string $domain
|
||||
*/
|
||||
public static function lang(string $domain)
|
||||
{
|
||||
$currentLang = I18n::getLang();
|
||||
if ($currentLang) {
|
||||
$currentLang = basename($currentLang);
|
||||
$fileName = dirname(__FILE__) . '/' . $domain . '/lang/' . $currentLang;
|
||||
if (file_exists($fileName)) {
|
||||
I18n::addLang($fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Options对象
|
||||
*
|
||||
* @return Options
|
||||
*/
|
||||
public static function options(): Options
|
||||
{
|
||||
return Options::alloc();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param $value
|
||||
* @return int
|
||||
*/
|
||||
public static function setOption(string $name, $value): int
|
||||
{
|
||||
$options = self::options();
|
||||
$options->{$name} = $value;
|
||||
|
||||
return BaseOptions::alloc()->update(
|
||||
['value' => is_array($value) ? json_encode($value) : $value],
|
||||
Db::get()->sql()->where('name = ?', $name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加路由
|
||||
*
|
||||
* @param string $name 路由名称
|
||||
* @param string $url 路由路径
|
||||
* @param string $widget 组件名称
|
||||
* @param string|null $action 组件动作
|
||||
* @param string|null $after 在某个路由后面
|
||||
* @return integer
|
||||
*/
|
||||
public static function addRoute(
|
||||
string $name,
|
||||
string $url,
|
||||
string $widget,
|
||||
?string $action = null,
|
||||
?string $after = null
|
||||
): int {
|
||||
$routingTable = self::options()->routingTable;
|
||||
if (isset($routingTable[0])) {
|
||||
unset($routingTable[0]);
|
||||
}
|
||||
|
||||
$pos = 0;
|
||||
foreach ($routingTable as $key => $val) {
|
||||
$pos++;
|
||||
|
||||
if ($key == $after) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$pre = array_slice($routingTable, 0, $pos);
|
||||
$next = array_slice($routingTable, $pos);
|
||||
|
||||
$routingTable = array_merge($pre, [
|
||||
$name => [
|
||||
'url' => $url,
|
||||
'widget' => $widget,
|
||||
'action' => $action
|
||||
]
|
||||
], $next);
|
||||
|
||||
return self::setOption('routingTable', $routingTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除路由
|
||||
*
|
||||
* @param string $name 路由名称
|
||||
* @return integer
|
||||
*/
|
||||
public static function removeRoute(string $name): int
|
||||
{
|
||||
$routingTable = self::options()->routingTable;
|
||||
if (isset($routingTable[0])) {
|
||||
unset($routingTable[0]);
|
||||
}
|
||||
|
||||
unset($routingTable[$name]);
|
||||
return self::setOption('routingTable', $routingTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加action扩展
|
||||
*
|
||||
* @param string $actionName 需要扩展的action名称
|
||||
* @param string $widgetName 需要扩展的widget名称
|
||||
* @return integer
|
||||
*/
|
||||
public static function addAction(string $actionName, string $widgetName): int
|
||||
{
|
||||
$actionTable = self::options()->actionTable;
|
||||
$actionTable = empty($actionTable) ? [] : $actionTable;
|
||||
$actionTable[$actionName] = $widgetName;
|
||||
|
||||
return self::setOption('actionTable', $actionTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除action扩展
|
||||
*
|
||||
* @param string $actionName
|
||||
* @return int
|
||||
*/
|
||||
public static function removeAction(string $actionName): int
|
||||
{
|
||||
$actionTable = self::options()->actionTable;
|
||||
$actionTable = empty($actionTable) ? [] : $actionTable;
|
||||
|
||||
if (isset($actionTable[$actionName])) {
|
||||
unset($actionTable[$actionName]);
|
||||
reset($actionTable);
|
||||
}
|
||||
|
||||
return self::setOption('actionTable', $actionTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加一个菜单
|
||||
*
|
||||
* @param string $menuName 菜单名
|
||||
* @return integer
|
||||
*/
|
||||
public static function addMenu(string $menuName): int
|
||||
{
|
||||
$panelTable = self::options()->panelTable;
|
||||
$panelTable['parent'] = empty($panelTable['parent']) ? [] : $panelTable['parent'];
|
||||
$panelTable['parent'][] = $menuName;
|
||||
|
||||
self::setOption('panelTable', $panelTable);
|
||||
|
||||
end($panelTable['parent']);
|
||||
return key($panelTable['parent']) + 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个菜单
|
||||
*
|
||||
* @param string $menuName 菜单名
|
||||
* @return integer
|
||||
*/
|
||||
public static function removeMenu(string $menuName): int
|
||||
{
|
||||
$panelTable = self::options()->panelTable;
|
||||
$panelTable['parent'] = empty($panelTable['parent']) ? [] : $panelTable['parent'];
|
||||
|
||||
if (false !== ($index = array_search($menuName, $panelTable['parent']))) {
|
||||
unset($panelTable['parent'][$index]);
|
||||
}
|
||||
|
||||
self::setOption('panelTable', $panelTable);
|
||||
|
||||
return $index + 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加一个面板
|
||||
*
|
||||
* @param integer $index 菜单索引
|
||||
* @param string $fileName 文件名称
|
||||
* @param string $title 面板标题
|
||||
* @param string $subTitle 面板副标题
|
||||
* @param string $level 进入权限
|
||||
* @param boolean $hidden 是否隐藏
|
||||
* @param string $addLink 新增项目链接, 会显示在页面标题之后
|
||||
* @return integer
|
||||
*/
|
||||
public static function addPanel(
|
||||
int $index,
|
||||
string $fileName,
|
||||
string $title,
|
||||
string $subTitle,
|
||||
string $level,
|
||||
bool $hidden = false,
|
||||
string $addLink = ''
|
||||
): int {
|
||||
$panelTable = self::options()->panelTable;
|
||||
$panelTable['child'] = empty($panelTable['child']) ? [] : $panelTable['child'];
|
||||
$panelTable['child'][$index] = empty($panelTable['child'][$index]) ? [] : $panelTable['child'][$index];
|
||||
$fileName = urlencode(trim($fileName, '/'));
|
||||
$panelTable['child'][$index][]
|
||||
= [$title, $subTitle, 'extending.php?panel=' . $fileName, $level, $hidden, $addLink];
|
||||
|
||||
$panelTable['file'] = empty($panelTable['file']) ? [] : $panelTable['file'];
|
||||
$panelTable['file'][] = $fileName;
|
||||
$panelTable['file'] = array_unique($panelTable['file']);
|
||||
|
||||
self::setOption('panelTable', $panelTable);
|
||||
|
||||
end($panelTable['child'][$index]);
|
||||
return key($panelTable['child'][$index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除一个面板
|
||||
*
|
||||
* @param integer $index 菜单索引
|
||||
* @param string $fileName 文件名称
|
||||
* @return integer
|
||||
*/
|
||||
public static function removePanel(int $index, string $fileName): int
|
||||
{
|
||||
$panelTable = self::options()->panelTable;
|
||||
$panelTable['child'] = empty($panelTable['child']) ? [] : $panelTable['child'];
|
||||
$panelTable['child'][$index] = empty($panelTable['child'][$index]) ? [] : $panelTable['child'][$index];
|
||||
$panelTable['file'] = empty($panelTable['file']) ? [] : $panelTable['file'];
|
||||
$fileName = urlencode(trim($fileName, '/'));
|
||||
|
||||
if (false !== ($key = array_search($fileName, $panelTable['file']))) {
|
||||
unset($panelTable['file'][$key]);
|
||||
}
|
||||
|
||||
$return = 0;
|
||||
foreach ($panelTable['child'][$index] as $key => $val) {
|
||||
if ($val[2] == 'extending.php?panel=' . $fileName) {
|
||||
unset($panelTable['child'][$index][$key]);
|
||||
$return = $key;
|
||||
}
|
||||
}
|
||||
|
||||
self::setOption('panelTable', $panelTable);
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取面板url
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return string
|
||||
*/
|
||||
public static function url(string $fileName): string
|
||||
{
|
||||
return Common::url('extending.php?panel=' . (trim($fileName, '/')), self::options()->adminUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动配置插件变量
|
||||
*
|
||||
* @param mixed $pluginName 插件名称
|
||||
* @param array $settings 变量键值对
|
||||
* @param bool $isPersonal . (default: false) 是否为私人变量
|
||||
*/
|
||||
public static function configPlugin($pluginName, array $settings, bool $isPersonal = false)
|
||||
{
|
||||
if (empty($settings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Edit::configPlugin($pluginName, $settings, $isPersonal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论回复按钮
|
||||
*
|
||||
* @access public
|
||||
* @param string $theId 评论元素id
|
||||
* @param integer $coid 评论id
|
||||
* @param string $word 按钮文字
|
||||
* @param string $formId 表单id
|
||||
* @param integer $style 样式类型
|
||||
* @return void
|
||||
*/
|
||||
public static function replyLink(
|
||||
string $theId,
|
||||
int $coid,
|
||||
string $word = 'Reply',
|
||||
string $formId = 'respond',
|
||||
int $style = 2
|
||||
) {
|
||||
if (self::options()->commentsThreaded) {
|
||||
echo '<a href="#' . $formId . '" rel="nofollow" onclick="return typechoAddCommentReply(\'' .
|
||||
$theId . '\', ' . $coid . ', \'' . $formId . '\', ' . $style . ');">' . $word . '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论取消按钮
|
||||
*
|
||||
* @param string $word 按钮文字
|
||||
* @param string $formId 表单id
|
||||
*/
|
||||
public static function cancelCommentReplyLink(string $word = 'Cancel', string $formId = 'respond')
|
||||
{
|
||||
if (self::options()->commentsThreaded) {
|
||||
echo '<a href="#' . $formId . '" rel="nofollow" onclick="return typechoCancelCommentReply(\'' .
|
||||
$formId . '\');">' . $word . '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 评论回复js脚本
|
||||
*/
|
||||
public static function threadedCommentsScript()
|
||||
{
|
||||
if (self::options()->commentsThreaded) {
|
||||
echo
|
||||
<<<EOF
|
||||
<script type="text/javascript">
|
||||
var typechoAddCommentReply = function (cid, coid, cfid, style) {
|
||||
var _ce = document.getElementById(cid), _cp = _ce.parentNode;
|
||||
var _cf = document.getElementById(cfid);
|
||||
|
||||
var _pi = document.getElementById('comment-parent');
|
||||
if (null == _pi) {
|
||||
_pi = document.createElement('input');
|
||||
_pi.setAttribute('type', 'hidden');
|
||||
_pi.setAttribute('name', 'parent');
|
||||
_pi.setAttribute('id', 'comment-parent');
|
||||
|
||||
var _form = 'form' == _cf.tagName ? _cf : _cf.getElementsByTagName('form')[0];
|
||||
|
||||
_form.appendChild(_pi);
|
||||
}
|
||||
_pi.setAttribute('value', coid);
|
||||
|
||||
if (null == document.getElementById('comment-form-place-holder')) {
|
||||
var _cfh = document.createElement('div');
|
||||
_cfh.setAttribute('id', 'comment-form-place-holder');
|
||||
_cf.parentNode.insertBefore(_cfh, _cf);
|
||||
}
|
||||
|
||||
1 == style ? (null == _ce.nextSibling ? _cp.appendChild(_cf)
|
||||
: _cp.insertBefore(_cf, _ce.nextSibling)) : _ce.appendChild(_cf);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
var typechoCancelCommentReply = function (cfid) {
|
||||
var _cf = document.getElementById(cfid),
|
||||
_cfh = document.getElementById('comment-form-place-holder');
|
||||
|
||||
var _pi = document.getElementById('comment-parent');
|
||||
if (null != _pi) {
|
||||
_pi.parentNode.removeChild(_pi);
|
||||
}
|
||||
|
||||
if (null == _cfh) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_cfh.parentNode.insertBefore(_cf, _cfh);
|
||||
return false;
|
||||
};
|
||||
</script>
|
||||
EOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
1849
var/Utils/HyperDown.php
Executable file
1849
var/Utils/HyperDown.php
Executable file
File diff suppressed because it is too large
Load Diff
37
var/Utils/Markdown.php
Executable file
37
var/Utils/Markdown.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Utils;
|
||||
|
||||
/**
|
||||
* Markdown解析
|
||||
*
|
||||
* @package Markdown
|
||||
* @copyright Copyright (c) 2014 Typecho team (http://www.typecho.org)
|
||||
* @license GNU General Public License 2.0
|
||||
*/
|
||||
class Markdown
|
||||
{
|
||||
/**
|
||||
* convert
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function convert(string $text): string
|
||||
{
|
||||
static $parser;
|
||||
|
||||
if (empty($parser)) {
|
||||
$parser = new HyperDown();
|
||||
|
||||
$parser->hook('afterParseCode', function ($html) {
|
||||
return preg_replace("/<code class=\"([_a-z0-9-]+)\">/i", "<code class=\"lang-\\1\">", $html);
|
||||
});
|
||||
|
||||
$parser->enableHtml(true);
|
||||
}
|
||||
|
||||
return str_replace('<p><!--more--></p>', '<!--more-->', $parser->makeHtml($text));
|
||||
}
|
||||
}
|
||||
|
||||
294
var/Utils/PasswordHash.php
Executable file
294
var/Utils/PasswordHash.php
Executable file
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
namespace Utils;
|
||||
|
||||
/**
|
||||
* Portable PHP password hashing framework.
|
||||
*
|
||||
* @package phpass
|
||||
* @version 0.2 / genuine.
|
||||
* @link http://www.openwall.com/phpass/
|
||||
* @since 2.5
|
||||
*/
|
||||
class PasswordHash
|
||||
{
|
||||
private string $itoa64;
|
||||
|
||||
private int $iteration_count_log2;
|
||||
|
||||
private bool $portable_hashes;
|
||||
|
||||
private string $random_state;
|
||||
|
||||
/**
|
||||
* @param int $iteration_count_log2
|
||||
* @param bool $portable_hashes
|
||||
*/
|
||||
public function __construct(int $iteration_count_log2, bool $portable_hashes)
|
||||
{
|
||||
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
|
||||
$iteration_count_log2 = 8;
|
||||
}
|
||||
|
||||
$this->iteration_count_log2 = $iteration_count_log2;
|
||||
|
||||
$this->portable_hashes = $portable_hashes;
|
||||
|
||||
$this->random_state = microtime() . uniqid(rand(), true); // removed getmypid() for compability reasons
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @return string
|
||||
*/
|
||||
public function hashPassword(string $password): string
|
||||
{
|
||||
$random = '';
|
||||
|
||||
if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
|
||||
$random = $this->getRandomBytes(16);
|
||||
$hash =
|
||||
crypt($password, $this->gensaltBlowfish($random));
|
||||
|
||||
if (strlen($hash) == 60) {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
|
||||
if (strlen($random) < 3) {
|
||||
$random = $this->getRandomBytes(3);
|
||||
}
|
||||
|
||||
$hash =
|
||||
crypt($password, $this->gensaltExtended($random));
|
||||
|
||||
if (strlen($hash) == 20) {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($random) < 6) {
|
||||
$random = $this->getRandomBytes(6);
|
||||
}
|
||||
|
||||
$hash = $this->cryptPrivate($password, $this->gensaltPrivate($random));
|
||||
if (strlen($hash) == 34) {
|
||||
return $hash;
|
||||
}
|
||||
|
||||
# Returning '*' on error is safe here, but would _not_ be safe
|
||||
# in a crypt(3)-like function used _both_ for generating new
|
||||
# hashes and for validating passwords against existing hashes.
|
||||
return '*';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $count
|
||||
* @return string
|
||||
*/
|
||||
private function getRandomBytes(int $count): string
|
||||
{
|
||||
$output = '';
|
||||
if (@is_readable('/dev/urandom') && ($fh = @fopen('/dev/urandom', 'rb'))) {
|
||||
$output = fread($fh, $count);
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
if (strlen($output) < $count) {
|
||||
$output = '';
|
||||
for ($i = 0; $i < $count; $i += 16) {
|
||||
$this->random_state =
|
||||
md5(microtime() . $this->random_state);
|
||||
$output .=
|
||||
pack('H*', md5($this->random_state));
|
||||
}
|
||||
$output = substr($output, 0, $count);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private function gensaltBlowfish(string $input): string
|
||||
{
|
||||
# This one needs to use a different order of characters and a
|
||||
# different encoding scheme from the one in encode64() above.
|
||||
# We care because the last character in our encoded string will
|
||||
# only represent 2 bits. While two known implementations of
|
||||
# bcrypt will happily accept and correct a salt string which
|
||||
# has the 4 unused bits set to non-zero, we do not want to take
|
||||
# chances and we also do not want to waste an additional byte
|
||||
# of entropy.
|
||||
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
|
||||
$output = '$2a$';
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
|
||||
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
|
||||
$output .= '$';
|
||||
|
||||
$i = 0;
|
||||
do {
|
||||
$c1 = ord($input[$i++]);
|
||||
$output .= $itoa64[$c1 >> 2];
|
||||
$c1 = ($c1 & 0x03) << 4;
|
||||
if ($i >= 16) {
|
||||
$output .= $itoa64[$c1];
|
||||
break;
|
||||
}
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 4;
|
||||
$output .= $itoa64[$c1];
|
||||
$c1 = ($c2 & 0x0f) << 2;
|
||||
|
||||
$c2 = ord($input[$i++]);
|
||||
$c1 |= $c2 >> 6;
|
||||
$output .= $itoa64[$c1];
|
||||
$output .= $itoa64[$c2 & 0x3f];
|
||||
} while (1);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private function gensaltExtended(string $input): string
|
||||
{
|
||||
$count_log2 = min($this->iteration_count_log2 + 8, 24);
|
||||
# This should be odd to not reveal weak DES keys, and the
|
||||
# maximum valid value is (2**24 - 1) which is odd anyway.
|
||||
$count = (1 << $count_log2) - 1;
|
||||
|
||||
$output = '_';
|
||||
$output .= $this->itoa64[$count & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 6) & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 12) & 0x3f];
|
||||
$output .= $this->itoa64[($count >> 18) & 0x3f];
|
||||
|
||||
$output .= $this->encode64($input, 3);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @param int $count
|
||||
* @return string
|
||||
*/
|
||||
private function encode64(string $input, int $count): string
|
||||
{
|
||||
$output = '';
|
||||
$i = 0;
|
||||
do {
|
||||
$value = ord($input[$i++]);
|
||||
$output .= $this->itoa64[$value & 0x3f];
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 8;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 6) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
if ($i < $count) {
|
||||
$value |= ord($input[$i]) << 16;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 12) & 0x3f];
|
||||
if ($i++ >= $count) {
|
||||
break;
|
||||
}
|
||||
$output .= $this->itoa64[($value >> 18) & 0x3f];
|
||||
} while ($i < $count);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @param string $setting
|
||||
* @return string
|
||||
*/
|
||||
private function cryptPrivate(string $password, string $setting): string
|
||||
{
|
||||
$output = '*0';
|
||||
if (substr($setting, 0, 2) == $output) {
|
||||
$output = '*1';
|
||||
}
|
||||
|
||||
if (substr($setting, 0, 3) != '$P$') {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$count_log2 = strpos($this->itoa64, $setting[3]);
|
||||
if ($count_log2 < 7 || $count_log2 > 30) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
$count = 1 << $count_log2;
|
||||
|
||||
$salt = substr($setting, 4, 8);
|
||||
if (strlen($salt) != 8) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
# We're kind of forced to use MD5 here since it's the only
|
||||
# cryptographic primitive available in all versions of PHP
|
||||
# currently in use. To implement our own low-level crypto
|
||||
# in PHP would result in much worse performance and
|
||||
# consequently in lower iteration counts and hashes that are
|
||||
# quicker to crack (by non-PHP code).
|
||||
if (PHP_VERSION >= '5') {
|
||||
$hash = md5($salt . $password, true);
|
||||
do {
|
||||
$hash = md5($hash . $password, true);
|
||||
} while (--$count);
|
||||
} else {
|
||||
$hash = pack('H*', md5($salt . $password));
|
||||
do {
|
||||
$hash = pack('H*', md5($hash . $password));
|
||||
} while (--$count);
|
||||
}
|
||||
|
||||
$output = substr($setting, 0, 12);
|
||||
$output .= $this->encode64($hash, 16);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private function gensaltPrivate(string $input): string
|
||||
{
|
||||
$output = '$P$';
|
||||
$output .= $this->itoa64[min($this->iteration_count_log2 +
|
||||
((PHP_VERSION >= '5') ? 5 : 3), 30)];
|
||||
$output .= $this->encode64($input, 6);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @param string $stored_hash
|
||||
* @return bool
|
||||
*/
|
||||
public function checkPassword(string $password, string $stored_hash): bool
|
||||
{
|
||||
$hash = $this->cryptPrivate($password, $stored_hash);
|
||||
if ($hash[0] == '*') {
|
||||
$hash = crypt($password, $stored_hash);
|
||||
}
|
||||
|
||||
return $hash == $stored_hash;
|
||||
}
|
||||
}
|
||||
|
||||
98
var/Utils/Upgrade.php
Executable file
98
var/Utils/Upgrade.php
Executable file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Utils;
|
||||
|
||||
use Typecho\Db;
|
||||
use Widget\Options;
|
||||
|
||||
/**
|
||||
* 升级程序
|
||||
*
|
||||
* @category typecho
|
||||
* @package Upgrade
|
||||
* @copyright Copyright (c) 2008 Typecho team (http://www.typecho.org)
|
||||
* @license GNU General Public License 2.0
|
||||
*/
|
||||
class Upgrade
|
||||
{
|
||||
/**
|
||||
* @param Db $db
|
||||
* @param Options $options
|
||||
*/
|
||||
public static function v1_3_0(Db $db, Options $options)
|
||||
{
|
||||
$routingTable = $options->routingTable;
|
||||
|
||||
$routingTable['comment_page'] = [
|
||||
'url' => '[permalink:string]/comment-page-[commentPage:digital]',
|
||||
'widget' => '\Widget\CommentPage',
|
||||
'action' => 'action'
|
||||
];
|
||||
|
||||
$routingTable['feed'] = [
|
||||
'url' => '/feed[feed:string:0]',
|
||||
'widget' => '\Widget\Feed',
|
||||
'action' => 'render'
|
||||
];
|
||||
|
||||
unset($routingTable[0]);
|
||||
|
||||
$db->query($db->update('table.options')
|
||||
->rows(['value' => json_encode($routingTable)])
|
||||
->where('name = ?', 'routingTable'));
|
||||
|
||||
// fix options->commentsRequireURL
|
||||
$db->query($db->update('table.options')
|
||||
->rows(['name' => 'commentsRequireUrl'])
|
||||
->where('name = ?', 'commentsRequireURL'));
|
||||
|
||||
// fix draft
|
||||
$db->query($db->update('table.contents')
|
||||
->rows(['type' => 'revision'])
|
||||
->where('parent <> 0 AND (type = ? OR type = ?)', 'post_draft', 'page_draft'));
|
||||
|
||||
// fix attachment serialize
|
||||
$lastId = 0;
|
||||
do {
|
||||
$rows = $db->fetchAll(
|
||||
$db->select('cid', 'text')->from('table.contents')
|
||||
->where('cid > ?', $lastId)
|
||||
->where('type = ?', 'attachment')
|
||||
->order('cid', Db::SORT_ASC)
|
||||
->limit(100)
|
||||
);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (strpos($row['text'], 'a:') !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value = @unserialize($row['text']);
|
||||
if ($value !== false) {
|
||||
$db->query($db->update('table.contents')
|
||||
->rows(['text' => json_encode($value)])
|
||||
->where('cid = ?', $row['cid']));
|
||||
}
|
||||
|
||||
$lastId = $row['cid'];
|
||||
}
|
||||
} while (count($rows) === 100);
|
||||
|
||||
$rows = $db->fetchAll($db->select()->from('table.options'));
|
||||
|
||||
foreach ($rows as $row) {
|
||||
if (
|
||||
in_array($row['name'], ['plugins', 'actionTable', 'panelTable'])
|
||||
|| strpos($row['name'], 'plugin:') === 0
|
||||
|| strpos($row['name'], 'theme:') === 0
|
||||
) {
|
||||
$value = @unserialize($row['value']);
|
||||
if ($value !== false) {
|
||||
$db->query($db->update('table.options')
|
||||
->rows(['value' => json_encode($value)])
|
||||
->where('name = ?', $row['name']));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user