2016-07-18 16:39:37 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
class UOJBlogEditor {
|
|
|
|
|
public $type = 'blog';
|
|
|
|
|
public $name;
|
|
|
|
|
public $blog_url;
|
|
|
|
|
public $save;
|
|
|
|
|
public $cur_data = array();
|
|
|
|
|
public $post_data = array();
|
2022-03-20 10:23:40 +00:00
|
|
|
|
public $show_editor = true;
|
|
|
|
|
public $show_tags = true;
|
2016-07-18 16:39:37 +00:00
|
|
|
|
|
|
|
|
|
public $label_text = array(
|
|
|
|
|
'title' => '标题',
|
|
|
|
|
'tags' => '标签(多个标签用逗号隔开)',
|
|
|
|
|
'content' => '内容',
|
|
|
|
|
'view blog' => '查看博客',
|
|
|
|
|
'blog visibility' => '博客可见性',
|
|
|
|
|
'private' => '未公开',
|
|
|
|
|
'public' => '公开'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
public $validator = array();
|
|
|
|
|
|
|
|
|
|
function __construct() {
|
|
|
|
|
global $REQUIRE_LIB;
|
|
|
|
|
$REQUIRE_LIB['blog-editor'] = '';
|
|
|
|
|
|
|
|
|
|
$this->validator = array(
|
|
|
|
|
'title' => function(&$title) {
|
|
|
|
|
if ($title == '') {
|
|
|
|
|
return '标题不能为空';
|
|
|
|
|
}
|
|
|
|
|
if (strlen($title) > 100) {
|
|
|
|
|
return '标题不能超过 100 个字节';
|
|
|
|
|
}
|
|
|
|
|
if (HTML::escape($title) === '') {
|
|
|
|
|
return '无效编码';
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
},
|
|
|
|
|
'content_md' => function(&$content_md) {
|
|
|
|
|
if (strlen($content_md) > 1000000) {
|
|
|
|
|
return '内容过长';
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
},
|
|
|
|
|
'tags' => function(&$tags) {
|
|
|
|
|
$tags = str_replace(',', ',', $tags);
|
|
|
|
|
$tags_raw = explode(',', $tags);
|
|
|
|
|
if (count($tags_raw) > 10) {
|
|
|
|
|
return '标签个数不能超过10';
|
|
|
|
|
}
|
|
|
|
|
$tags = array();
|
|
|
|
|
foreach ($tags_raw as $tag) {
|
|
|
|
|
$tag = trim($tag);
|
|
|
|
|
if (strlen($tag) == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (strlen($tag) > 30) {
|
|
|
|
|
return '标签 “' . HTML::escape($tag) .'” 太长';
|
|
|
|
|
}
|
|
|
|
|
if (in_array($tag, $tags, true)) {
|
|
|
|
|
return '标签 “' . HTML::escape($tag) .'” 重复出现';
|
|
|
|
|
}
|
|
|
|
|
$tags[] = $tag;
|
|
|
|
|
}
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function validate($name) {
|
|
|
|
|
if (!isset($_POST["{$this->name}_{$name}"])) {
|
|
|
|
|
return '不能为空';
|
|
|
|
|
}
|
|
|
|
|
$this->post_data[$name] = $_POST["{$this->name}_{$name}"];
|
|
|
|
|
$val = $this->validator[$name];
|
|
|
|
|
return $val($this->post_data[$name]);
|
|
|
|
|
}
|
|
|
|
|
private function receivePostData() {
|
|
|
|
|
$errors = array();
|
2022-03-20 10:23:40 +00:00
|
|
|
|
|
|
|
|
|
$keys = array('title');
|
|
|
|
|
if ($this->show_tags) {
|
|
|
|
|
$keys[] = 'tags';
|
|
|
|
|
}
|
|
|
|
|
if ($this->show_editor) {
|
|
|
|
|
$keys[] = 'content_md';
|
|
|
|
|
}
|
|
|
|
|
foreach ($keys as $name) {
|
2016-07-18 16:39:37 +00:00
|
|
|
|
$cur_err = $this->validate($name);
|
|
|
|
|
if ($cur_err) {
|
|
|
|
|
$errors[$name] = $cur_err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($errors) {
|
|
|
|
|
die(json_encode($errors));
|
|
|
|
|
}
|
|
|
|
|
crsf_defend();
|
|
|
|
|
|
|
|
|
|
$this->post_data['is_hidden'] = isset($_POST["{$this->name}_is_hidden"]) ? 1 : 0;
|
|
|
|
|
|
2022-10-09 13:33:13 +00:00
|
|
|
|
$purifier = HTML::purifier();
|
2022-10-09 23:35:48 +00:00
|
|
|
|
$parsedown = new ParsedownMath([
|
|
|
|
|
'math' => [
|
|
|
|
|
'enabled' => true,
|
|
|
|
|
'matchSingleDollar' => true
|
|
|
|
|
]
|
|
|
|
|
]);
|
2016-07-18 16:39:37 +00:00
|
|
|
|
|
|
|
|
|
$this->post_data['title'] = HTML::escape($this->post_data['title']);
|
|
|
|
|
|
2022-03-20 10:23:40 +00:00
|
|
|
|
if ($this->show_editor) {
|
|
|
|
|
if ($this->type == 'blog') {
|
|
|
|
|
$content_md = $_POST[$this->name . '_content_md'];
|
2022-10-09 23:35:48 +00:00
|
|
|
|
$this->post_data['content'] = $parsedown->text($this->post_data['content_md']);
|
2016-07-18 16:39:37 +00:00
|
|
|
|
|
2022-03-20 10:23:40 +00:00
|
|
|
|
if (preg_match('/^.*<!--.*readmore.*-->.*$/m', $this->post_data['content'], $matches, PREG_OFFSET_CAPTURE)) {
|
|
|
|
|
$content_less = substr($this->post_data['content'], 0, $matches[0][1]);
|
|
|
|
|
$content_more = substr($this->post_data['content'], $matches[0][1] + strlen($matches[0][0]));
|
|
|
|
|
$this->post_data['content'] = $purifier->purify($content_less).'<!-- readmore -->'.$purifier->purify($content_more);
|
|
|
|
|
} else {
|
|
|
|
|
$this->post_data['content'] = $purifier->purify($this->post_data['content']);
|
|
|
|
|
}
|
|
|
|
|
} elseif ($this->type == 'slide') {
|
|
|
|
|
$content_array = yaml_parse($this->post_data['content_md']);
|
|
|
|
|
if ($content_array === false || !is_array($content_array)) {
|
|
|
|
|
die(json_encode(array('content_md' => '不合法的 YAML 格式')));
|
|
|
|
|
}
|
2016-07-18 16:39:37 +00:00
|
|
|
|
|
2022-10-09 23:35:48 +00:00
|
|
|
|
$marked = function($md) use ($parsedown, $purifier) {
|
|
|
|
|
$dom = new DOMDocument;
|
|
|
|
|
$dom->loadHTML(mb_convert_encoding($parsedown->text($md), 'HTML-ENTITIES', 'UTF-8'));
|
|
|
|
|
$elements = $dom->getElementsByTagName('li');
|
|
|
|
|
|
|
|
|
|
foreach ($elements as $element) {
|
|
|
|
|
$element->setAttribute('class',
|
|
|
|
|
$element->getAttribute('class') . ' fragment');
|
2022-03-20 10:23:40 +00:00
|
|
|
|
}
|
2022-10-09 23:35:48 +00:00
|
|
|
|
|
|
|
|
|
return $purifier->purify($dom->saveHTML());
|
2022-03-20 10:23:40 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$config = array();
|
|
|
|
|
$this->post_data['content'] = '';
|
|
|
|
|
foreach ($content_array as $slide_name => $slide_content) {
|
|
|
|
|
if (is_array($slide_content) && is_array($slide_content['config'])) {
|
|
|
|
|
foreach (array('theme') as $config_key) {
|
|
|
|
|
if (is_string($slide_content['config'][$config_key]) && strlen($slide_content['config'][$config_key]) <= 30) {
|
|
|
|
|
$config[$config_key] = $slide_content['config'][$config_key];
|
|
|
|
|
}
|
2016-07-18 16:39:37 +00:00
|
|
|
|
}
|
2022-03-20 10:23:40 +00:00
|
|
|
|
continue;
|
2016-07-18 16:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-20 10:23:40 +00:00
|
|
|
|
$this->post_data['content'] .= '<section>';
|
2016-07-18 16:39:37 +00:00
|
|
|
|
|
2022-03-20 10:23:40 +00:00
|
|
|
|
if (is_string($slide_content)) {
|
|
|
|
|
$this->post_data['content'] .= $marked($slide_content);
|
|
|
|
|
} elseif (is_array($slide_content)) {
|
|
|
|
|
if (is_array($slide_content['children'])) {
|
|
|
|
|
foreach ($slide_content['children'] as $cslide_name => $cslide_content) {
|
|
|
|
|
$this->post_data['content'] .= '<section>';
|
|
|
|
|
$this->post_data['content'] .= $marked($cslide_content);
|
|
|
|
|
$this->post_data['content'] .= '</section>';
|
|
|
|
|
}
|
2016-07-18 16:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-20 10:23:40 +00:00
|
|
|
|
$this->post_data['content'] .= "</section>\n";
|
2016-07-18 16:39:37 +00:00
|
|
|
|
}
|
2022-03-20 10:23:40 +00:00
|
|
|
|
$this->post_data['content'] = json_encode($config) . "\n" . $this->post_data['content'];
|
2016-07-18 16:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function handleSave() {
|
2022-10-02 09:02:17 +00:00
|
|
|
|
global $REQUIRE_LIB;
|
|
|
|
|
|
2016-07-18 16:39:37 +00:00
|
|
|
|
$save = $this->save;
|
|
|
|
|
$this->receivePostData();
|
|
|
|
|
$ret = $save($this->post_data);
|
|
|
|
|
if (!$ret) {
|
|
|
|
|
$ret = array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isset($_POST['need_preview'])) {
|
|
|
|
|
ob_start();
|
|
|
|
|
if ($this->type == 'blog') {
|
2022-10-02 09:02:17 +00:00
|
|
|
|
$req_lib = array('mathjax' => '');
|
|
|
|
|
|
|
|
|
|
if (isset($REQUIRE_LIB['bootstrap5'])) {
|
|
|
|
|
$req_lib['bootstrap5'] = '';
|
|
|
|
|
$req_lib['hljs'] = '';
|
|
|
|
|
} else {
|
|
|
|
|
$req_lib['shjs'] = '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echoUOJPageHeader('博客预览', array('ShowPageHeader' => false, 'REQUIRE_LIB' => $req_lib));
|
|
|
|
|
if (!isset($REQUIRE_LIB['bootstrap5'])) {
|
|
|
|
|
echo '<link rel="stylesheet" type="text/css" href="' . HTML::url('/css/markdown.css') . '">';
|
|
|
|
|
}
|
2022-09-18 12:47:21 +00:00
|
|
|
|
echo '<article class="markdown-body">';
|
2016-07-18 16:39:37 +00:00
|
|
|
|
echo $this->post_data['content'];
|
|
|
|
|
echo '</article>';
|
2022-10-03 06:28:33 +00:00
|
|
|
|
|
|
|
|
|
if (isset($REQUIRE_LIB['bootstrap5'])) {
|
|
|
|
|
echo <<<EOD
|
|
|
|
|
<script>
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
$('.markdown-body table').each(function() {
|
2022-10-08 00:50:23 +00:00
|
|
|
|
$(this).addClass('table table-bordered');
|
2022-10-03 06:28:33 +00:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
EOD;
|
|
|
|
|
}
|
2016-07-18 16:39:37 +00:00
|
|
|
|
echoUOJPageFooter(array('ShowPageFooter' => false));
|
2020-06-25 12:41:16 +00:00
|
|
|
|
} elseif ($this->type == 'slide') {
|
2016-07-18 16:39:37 +00:00
|
|
|
|
uojIncludeView('slide', array_merge(
|
|
|
|
|
UOJContext::pageConfig(),
|
|
|
|
|
array(
|
|
|
|
|
'PageTitle' => '幻灯片预览',
|
|
|
|
|
'content' => $this->post_data['content']
|
|
|
|
|
)
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
$ret['html'] = ob_get_contents();
|
|
|
|
|
ob_end_clean();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
die(json_encode($ret));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function runAtServer() {
|
|
|
|
|
if (isset($_POST["save-{$this->name}"])) {
|
|
|
|
|
$this->handleSave();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public function printHTML() {
|
2022-10-02 06:25:22 +00:00
|
|
|
|
global $REQUIRE_LIB;
|
|
|
|
|
|
|
|
|
|
uojIncludeView('blog-editor', array('editor' => $this, 'REQUIRE_LIB' => $REQUIRE_LIB));
|
2016-07-18 16:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|