Skip to content

Commit 5c456d3

Browse files
committed
refactor(web): use parsedown as markdown renderer
1 parent cd327c0 commit 5c456d3

File tree

7 files changed

+2070
-53
lines changed

7 files changed

+2070
-53
lines changed

web/Dockerfile

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ ENV DEBIAN_FRONTEND=noninteractive
1010
RUN dpkg -s gnupg 2>/dev/null || (apt-get update && apt-get install -y gnupg) &&\
1111
echo "deb http://ppa.launchpad.net/stesie/libv8/ubuntu bionic main" | tee /etc/apt/sources.list.d/stesie-libv8.list && apt-key adv --keyserver keyserver.ubuntu.com --recv-keys D858A0DF &&\
1212
apt-get update && apt-get install -y git vim ntp zip unzip curl wget apache2 libapache2-mod-xsendfile libapache2-mod-php php php-dev php-pear php-zip php-mysql php-mbstring php-gd php-intl php-xsl g++ make re2c libv8-7.5-dev libyaml-dev &&\
13-
yes | pecl install yaml &&\
14-
git clone https://github.com/phpv8/v8js.git --depth=1 -b 2.1.2 /tmp/pear/download/v8js-master && cd /tmp/pear/download/v8js-master &&\
15-
phpize && ./configure --with-php-config=/usr/bin/php-config --with-v8js=/opt/libv8-7.5 && make install && cd -
13+
yes | pecl install yaml
1614

1715
ADD . /opt/uoj
1816
WORKDIR /opt/uoj

web/app/models/HTML.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,13 @@ public static function pruifier() {
163163

164164
return new HTMLPurifier($config);
165165
}
166+
167+
public static function parsedown($config = []) {
168+
return new UOJMarkdown($config + [
169+
'math' => [
170+
'enabled' => true,
171+
'matchSingleDollar' => true
172+
]
173+
]);
174+
}
166175
}

web/app/models/UOJBlogEditor.php

Lines changed: 15 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -92,19 +92,12 @@ private function receivePostData() {
9292
$this->post_data['is_hidden'] = isset($_POST["{$this->name}_is_hidden"]) ? 1 : 0;
9393

9494
$purifier = HTML::pruifier();
95+
$parsedown = HTML::parsedown();
9596

9697
$this->post_data['title'] = HTML::escape($this->post_data['title']);
9798

9899
if ($this->type == 'blog') {
99-
$content_md = $_POST[$this->name . '_content_md'];
100-
try {
101-
$v8 = new V8Js('POST');
102-
$v8->content_md = $this->post_data['content_md'];
103-
$v8->executeString(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/js/marked.js'), 'marked.js');
104-
$this->post_data['content'] = $v8->executeString('marked(POST.content_md)');
105-
} catch (V8JsException $e) {
106-
die(json_encode(array('content_md' => '未知错误')));
107-
}
100+
$this->post_data['content'] = $parsedown->text($this->post_data['content_md']);
108101

109102
if (preg_match('/^.*<!--.*readmore.*-->.*$/m', $this->post_data['content'], $matches, PREG_OFFSET_CAPTURE)) {
110103
$content_less = substr($this->post_data['content'], 0, $matches[0][1]);
@@ -118,45 +111,20 @@ private function receivePostData() {
118111
if ($content_array === false || !is_array($content_array)) {
119112
die(json_encode(array('content_md' => '不合法的 YAML 格式')));
120113
}
121-
122-
try {
123-
$v8 = new V8Js('PHP');
124-
$v8->executeString(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/js/marked.js'), 'marked.js');
125-
$v8->executeString(<<<EOD
126-
marked.setOptions({
127-
getLangClass: function(lang) {
128-
lang = lang.toLowerCase();
129-
switch (lang) {
130-
case 'c': return 'c';
131-
case 'c++': return 'cpp';
132-
case 'pascal': return 'pascal';
133-
default: return lang;
134-
}
135-
},
136-
getElementClass: function(tok) {
137-
switch (tok.type) {
138-
case 'list_item_start':
139-
return 'fragment';
140-
case 'loose_item_start':
141-
return 'fragment';
142-
default:
143-
return null;
144-
}
145-
}
146-
})
147-
EOD
148-
);
149-
} catch (V8JsException $e) {
150-
die(json_encode(array('content_md' => '未知错误')));
151-
}
152-
153-
$marked = function($md) use ($v8, $purifier) {
154-
try {
155-
$v8->md = $md;
156-
return $purifier->purify($v8->executeString('marked(PHP.md)'));
157-
} catch (V8JsException $e) {
158-
die(json_encode(array('content_md' => '未知错误')));
114+
115+
$marked = function($md) use ($parsedown, $purifier) {
116+
$dom = new DOMDocument;
117+
$dom->loadHTML(mb_convert_encoding($parsedown->text($md), 'HTML-ENTITIES', 'UTF-8'));
118+
$elements = $dom->getElementsByTagName('li');
119+
120+
foreach ($elements as $element) {
121+
$element->setAttribute(
122+
'class',
123+
$element->getAttribute('class') . ' fragment'
124+
);
159125
}
126+
127+
return $purifier->purify($dom->saveHTML());
160128
};
161129

162130
$config = array();

web/app/models/UOJMarkdown.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
require_once __DIR__ . '../vendor/parsedown/ParsedownMath.php';
4+
5+
class UOJMarkdown extends ParsedownMath {
6+
public function __construct($options = '') {
7+
if (method_exists(get_parent_class(), "__construct")) {
8+
parent::__construct($options);
9+
}
10+
11+
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
12+
$this->InlineTypes['@'][] = 'UserMention';
13+
}
14+
15+
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L340-L345
16+
protected function doGetAttributes($Element) {
17+
if (isset($Element['attributes'])) {
18+
return (array) $Element['attributes'];
19+
}
20+
return array();
21+
}
22+
23+
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L347-L358
24+
protected function doGetContent($Element) {
25+
if (isset($Element['text'])) {
26+
return $Element['text'];
27+
}
28+
if (isset($Element['rawHtml'])) {
29+
return $Element['rawHtml'];
30+
}
31+
if (isset($Element['handler']['argument'])) {
32+
return implode("\n", (array) $Element['handler']['argument']);
33+
}
34+
return null;
35+
}
36+
37+
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L369-L378
38+
protected function doSetAttributes(&$Element, $From, $Args = array()) {
39+
$Attributes = $this->doGetAttributes($Element);
40+
$Content = $this->doGetContent($Element);
41+
if (is_callable($From)) {
42+
$Args = array_merge(array($Content, $Attributes, &$Element), $Args);
43+
$Element['attributes'] = array_replace($Attributes, (array) call_user_func_array($From, $Args));
44+
} else {
45+
$Element['attributes'] = array_replace($Attributes, (array) $From);
46+
}
47+
}
48+
49+
// Add classes to <table>
50+
protected function blockTableComplete($Block) {
51+
$this->doSetAttributes($Block['element'], ['class' => 'table table-bordered']);
52+
53+
return $Block;
54+
}
55+
56+
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
57+
protected function inlineUserMention($Excerpt) {
58+
if (preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
59+
$mentioned_user = queryUser($matches[1]);
60+
61+
if ($mentioned_user) {
62+
return [
63+
'extent' => strlen($matches[0]),
64+
'element' => [
65+
'name' => 'span',
66+
'text' => '@' . $mentioned_user['username'],
67+
'attributes' => [
68+
'class' => "uoj-username",
69+
],
70+
],
71+
];
72+
}
73+
74+
return [
75+
'extent' => strlen($matches[0]),
76+
'markup' => $matches[0],
77+
];
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)