diff options
author | ArthurHoaro <arthur@hoa.ro> | 2019-05-25 15:52:27 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2020-01-18 09:55:59 +0100 |
commit | cf92b4dd1521241eefc58eaf6dcd202cd83969d8 (patch) | |
tree | 9d6f6f4dc9eabe111c3ba5609eea8b22cd5a23aa /plugins | |
parent | 336a28fa4a09b968ce4705900bf57693e672f0bf (diff) | |
download | Shaarli-cf92b4dd1521241eefc58eaf6dcd202cd83969d8.tar.gz Shaarli-cf92b4dd1521241eefc58eaf6dcd202cd83969d8.tar.zst Shaarli-cf92b4dd1521241eefc58eaf6dcd202cd83969d8.zip |
Apply the new system (Bookmark + Service) to the whole code base
See https://github.com/shaarli/Shaarli/issues/1307
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/markdown/README.md | 102 | ||||
-rw-r--r-- | plugins/markdown/help.html | 5 | ||||
-rw-r--r-- | plugins/markdown/markdown.css | 173 | ||||
-rw-r--r-- | plugins/markdown/markdown.meta | 4 | ||||
-rw-r--r-- | plugins/markdown/markdown.php | 365 |
5 files changed, 0 insertions, 649 deletions
diff --git a/plugins/markdown/README.md b/plugins/markdown/README.md deleted file mode 100644 index bc9427e2..00000000 --- a/plugins/markdown/README.md +++ /dev/null | |||
@@ -1,102 +0,0 @@ | |||
1 | ## Markdown Shaarli plugin | ||
2 | |||
3 | Convert all your shaares description to HTML formatted Markdown. | ||
4 | |||
5 | [Read more about Markdown syntax](http://daringfireball.net/projects/markdown/syntax). | ||
6 | |||
7 | Markdown processing is done with [Parsedown library](https://github.com/erusev/parsedown). | ||
8 | |||
9 | ### Installation | ||
10 | |||
11 | As a default plugin, it should already be in `tpl/plugins/` directory. | ||
12 | If not, download and unpack it there. | ||
13 | |||
14 | The directory structure should look like: | ||
15 | |||
16 | ``` | ||
17 | --- plugins | ||
18 | |--- markdown | ||
19 | |--- help.html | ||
20 | |--- markdown.css | ||
21 | |--- markdown.meta | ||
22 | |--- markdown.php | ||
23 | |--- README.md | ||
24 | ``` | ||
25 | |||
26 | To enable the plugin, just check it in the plugin administration page. | ||
27 | |||
28 | You can also add `markdown` to your list of enabled plugins in `data/config.json.php` | ||
29 | (`general.enabled_plugins` list). | ||
30 | |||
31 | This should look like: | ||
32 | |||
33 | ``` | ||
34 | "general": { | ||
35 | "enabled_plugins": [ | ||
36 | "markdown", | ||
37 | [...] | ||
38 | ], | ||
39 | } | ||
40 | ``` | ||
41 | |||
42 | Parsedown parsing library is imported using Composer. If you installed Shaarli using `git`, | ||
43 | or the `master` branch, run | ||
44 | |||
45 | composer update --no-dev --prefer-dist | ||
46 | |||
47 | ### No Markdown tag | ||
48 | |||
49 | If the tag `nomarkdown` is set for a shaare, it won't be converted to Markdown syntax. | ||
50 | |||
51 | > Note: this is a special tag, so it won't be displayed in link list. | ||
52 | |||
53 | ### HTML escape | ||
54 | |||
55 | By default, HTML tags are escaped. You can enable HTML tags rendering | ||
56 | by setting `security.markdwon_escape` to `false` in `data/config.json.php`: | ||
57 | |||
58 | ```json | ||
59 | { | ||
60 | "security": { | ||
61 | "markdown_escape": false | ||
62 | } | ||
63 | } | ||
64 | ``` | ||
65 | |||
66 | With this setting, Markdown support HTML tags. For example: | ||
67 | |||
68 | > <strong>strong</strong><strike>strike</strike> | ||
69 | |||
70 | Will render as: | ||
71 | |||
72 | > <strong>strong</strong><strike>strike</strike> | ||
73 | |||
74 | |||
75 | **Warning:** | ||
76 | |||
77 | * This setting might present **security risks** (XSS) on shared instances, even though tags | ||
78 | such as script, iframe, etc should be disabled. | ||
79 | * If you want to shaare HTML code, it is necessary to use inline code or code blocks. | ||
80 | * If your shaared descriptions contained HTML tags before enabling the markdown plugin, | ||
81 | enabling it might break your page. | ||
82 | |||
83 | ### Known issue | ||
84 | |||
85 | #### Redirector | ||
86 | |||
87 | If you're using a redirector, you *need* to add a space after a link, | ||
88 | otherwise the rest of the line will be `urlencode`. | ||
89 | |||
90 | ``` | ||
91 | [link](http://domain.tld)-->test | ||
92 | ``` | ||
93 | |||
94 | Will consider `http://domain.tld)-->test` as URL. | ||
95 | |||
96 | Instead, add an additional space. | ||
97 | |||
98 | ``` | ||
99 | [link](http://domain.tld) -->test | ||
100 | ``` | ||
101 | |||
102 | > Won't fix because a `)` is a valid part of an URL. | ||
diff --git a/plugins/markdown/help.html b/plugins/markdown/help.html deleted file mode 100644 index ded3d347..00000000 --- a/plugins/markdown/help.html +++ /dev/null | |||
@@ -1,5 +0,0 @@ | |||
1 | <div class="md_help"> | ||
2 | %s | ||
3 | <a href="http://daringfireball.net/projects/markdown/syntax" title="%s"> | ||
4 | %s</a>. | ||
5 | </div> | ||
diff --git a/plugins/markdown/markdown.css b/plugins/markdown/markdown.css deleted file mode 100644 index ce19cd2a..00000000 --- a/plugins/markdown/markdown.css +++ /dev/null | |||
@@ -1,173 +0,0 @@ | |||
1 | /** | ||
2 | * Credit to Simon Laroche <https://github.com/simonlc/Markdown-CSS> | ||
3 | * whom created the CSS which this file is based on. | ||
4 | * License: Unlicense <http://unlicense.org/> | ||
5 | */ | ||
6 | |||
7 | .markdown p{ | ||
8 | margin:0.75em 0; | ||
9 | } | ||
10 | |||
11 | .markdown img{ | ||
12 | max-width:100%; | ||
13 | } | ||
14 | |||
15 | .markdown h1, .markdown h2, .markdown h3, .markdown h4, .markdown h5, .markdown h6{ | ||
16 | font-weight:normal; | ||
17 | font-style:normal; | ||
18 | line-height:1em; | ||
19 | margin:0.75em 0; | ||
20 | } | ||
21 | .markdown h4, .markdown h5, .markdown h6{ font-weight: bold; } | ||
22 | .markdown h1{ font-size:2.5em; } | ||
23 | .markdown h2{ font-size:2em; } | ||
24 | .markdown h3{ font-size:1.5em; } | ||
25 | .markdown h4{ font-size:1.2em; } | ||
26 | .markdown h5{ font-size:1em; } | ||
27 | .markdown h6{ font-size:0.9em; } | ||
28 | |||
29 | .markdown blockquote{ | ||
30 | color:#666666; | ||
31 | padding-left: 3em; | ||
32 | border-left: 0.5em #EEE solid; | ||
33 | margin:0.75em 0; | ||
34 | } | ||
35 | .markdown hr { display: block; height: 2px; border: 0; border-top: 1px solid #aaa;border-bottom: 1px solid #eee; margin: 1em 0; padding: 0; } | ||
36 | .markdown pre, .markdown code, .markdown kbd, .markdown samp { | ||
37 | font-family: monospace, 'courier new'; | ||
38 | font-size: 0.98em; | ||
39 | } | ||
40 | .markdown pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } | ||
41 | |||
42 | .markdown b, .markdown strong { font-weight: bold; } | ||
43 | |||
44 | .markdown dfn, .markdown em { font-style: italic; } | ||
45 | |||
46 | .markdown ins { background: #ff9; color: #000; text-decoration: none; } | ||
47 | |||
48 | .markdown mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } | ||
49 | |||
50 | .markdown sub, .markdown sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } | ||
51 | .markdown sup { top: -0.5em; } | ||
52 | .markdown sub { bottom: -0.25em; } | ||
53 | |||
54 | .markdown ul, .markdown ol { margin: 1em 0; padding: 0 0 0 2em; } | ||
55 | .markdown li p:last-child { margin:0 } | ||
56 | .markdown dd { margin: 0 0 0 2em; } | ||
57 | |||
58 | .markdown img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } | ||
59 | |||
60 | .markdown table { border-collapse: collapse; border-spacing: 0; } | ||
61 | .markdown td { vertical-align: top; } | ||
62 | |||
63 | @media only screen and (min-width: 480px) { | ||
64 | .markdown {font-size:0.9em;} | ||
65 | } | ||
66 | |||
67 | @media only screen and (min-width: 768px) { | ||
68 | .markdown {font-size:1em;} | ||
69 | } | ||
70 | |||
71 | #linklist .markdown li { | ||
72 | padding: 0; | ||
73 | border: none; | ||
74 | background: none; | ||
75 | } | ||
76 | |||
77 | #linklist .markdown ul li { | ||
78 | list-style: circle; | ||
79 | } | ||
80 | |||
81 | #linklist .markdown ol li { | ||
82 | list-style: decimal; | ||
83 | } | ||
84 | |||
85 | .markdown table { | ||
86 | padding: 0; | ||
87 | } | ||
88 | .markdown table tr { | ||
89 | border-top: 1px solid #cccccc; | ||
90 | background-color: white; | ||
91 | margin: 0; | ||
92 | padding: 0; | ||
93 | } | ||
94 | .markdown table tr:nth-child(2n) { | ||
95 | background-color: #f8f8f8; | ||
96 | } | ||
97 | .markdown table tr th { | ||
98 | font-weight: bold; | ||
99 | border: 1px solid #cccccc; | ||
100 | text-align: left; | ||
101 | margin: 0; | ||
102 | padding: 6px 13px; | ||
103 | } | ||
104 | .markdown table tr td { | ||
105 | border: 1px solid #cccccc; | ||
106 | text-align: left; | ||
107 | margin: 0; | ||
108 | padding: 6px 13px; | ||
109 | } | ||
110 | .markdown table tr th :first-child, .markdown table tr td :first-child { | ||
111 | margin-top: 0; | ||
112 | } | ||
113 | .markdown table tr th :last-child, table tr td :last-child { | ||
114 | margin-bottom: 0; | ||
115 | } | ||
116 | |||
117 | .markdown pre { | ||
118 | background-color: #eee; | ||
119 | padding: 4px 9px; | ||
120 | -webkit-border-radius: 5px; | ||
121 | -moz-border-radius: 5px; | ||
122 | border-radius: 5px; | ||
123 | overflow: auto; | ||
124 | box-shadow: 0 -1px 0 #e5e5e5,0 0 1px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24); | ||
125 | } | ||
126 | |||
127 | .markdown pre code { | ||
128 | color: black; | ||
129 | font-family: 'Consolas', 'Monaco', 'Andale Mono', monospace; | ||
130 | direction: ltr; | ||
131 | text-align: left; | ||
132 | white-space: pre; | ||
133 | word-spacing: normal; | ||
134 | word-break: normal; | ||
135 | line-height: 1.7; | ||
136 | font-size: 11.5px; | ||
137 | -moz-tab-size: 4; | ||
138 | -o-tab-size: 4; | ||
139 | tab-size: 4; | ||
140 | -webkit-hyphens: none; | ||
141 | -moz-hyphens: none; | ||
142 | -ms-hyphens: none; | ||
143 | hyphens: none; | ||
144 | } | ||
145 | |||
146 | .markdown :not(pre) code { | ||
147 | background-color: #eee; | ||
148 | padding: 1px 3px; | ||
149 | border-radius: 1px; | ||
150 | box-shadow: 0 -1px 0 #e5e5e5,0 0 1px rgba(0,0,0,0.12),0 1px 1px rgba(0,0,0,0.24); | ||
151 | } | ||
152 | |||
153 | #pageheader .md_help { | ||
154 | color: white; | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | Remove header links style | ||
159 | */ | ||
160 | #pageheader .md_help a { | ||
161 | color: lightgray; | ||
162 | font-weight: bold; | ||
163 | text-decoration: underline; | ||
164 | |||
165 | background: none; | ||
166 | box-shadow: none; | ||
167 | padding: 0; | ||
168 | margin: 0; | ||
169 | } | ||
170 | |||
171 | #pageheader .md_help a:hover { | ||
172 | color: white; | ||
173 | } | ||
diff --git a/plugins/markdown/markdown.meta b/plugins/markdown/markdown.meta deleted file mode 100644 index 322856ea..00000000 --- a/plugins/markdown/markdown.meta +++ /dev/null | |||
@@ -1,4 +0,0 @@ | |||
1 | description="Render shaare description with Markdown syntax.<br><strong>Warning</strong>: | ||
2 | If your shaared descriptions contained HTML tags before enabling the markdown plugin, | ||
3 | enabling it might break your page. | ||
4 | See the <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/markdown#html-rendering\">README</a>." | ||
diff --git a/plugins/markdown/markdown.php b/plugins/markdown/markdown.php deleted file mode 100644 index f6f66cc5..00000000 --- a/plugins/markdown/markdown.php +++ /dev/null | |||
@@ -1,365 +0,0 @@ | |||
1 | <?php | ||
2 | |||
3 | /** | ||
4 | * Plugin Markdown. | ||
5 | * | ||
6 | * Shaare's descriptions are parsed with Markdown. | ||
7 | */ | ||
8 | |||
9 | use Shaarli\Config\ConfigManager; | ||
10 | use Shaarli\Plugin\PluginManager; | ||
11 | use Shaarli\Router; | ||
12 | |||
13 | /* | ||
14 | * If this tag is used on a shaare, the description won't be processed by Parsedown. | ||
15 | */ | ||
16 | define('NO_MD_TAG', 'nomarkdown'); | ||
17 | |||
18 | /** | ||
19 | * Parse linklist descriptions. | ||
20 | * | ||
21 | * @param array $data linklist data. | ||
22 | * @param ConfigManager $conf instance. | ||
23 | * | ||
24 | * @return mixed linklist data parsed in markdown (and converted to HTML). | ||
25 | */ | ||
26 | function hook_markdown_render_linklist($data, $conf) | ||
27 | { | ||
28 | foreach ($data['links'] as &$value) { | ||
29 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | ||
30 | $value = stripNoMarkdownTag($value); | ||
31 | continue; | ||
32 | } | ||
33 | $value['description_src'] = $value['description']; | ||
34 | $value['description'] = process_markdown( | ||
35 | $value['description'], | ||
36 | $conf->get('security.markdown_escape', true), | ||
37 | $conf->get('security.allowed_protocols') | ||
38 | ); | ||
39 | } | ||
40 | return $data; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Parse feed linklist descriptions. | ||
45 | * | ||
46 | * @param array $data linklist data. | ||
47 | * @param ConfigManager $conf instance. | ||
48 | * | ||
49 | * @return mixed linklist data parsed in markdown (and converted to HTML). | ||
50 | */ | ||
51 | function hook_markdown_render_feed($data, $conf) | ||
52 | { | ||
53 | foreach ($data['links'] as &$value) { | ||
54 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | ||
55 | $value = stripNoMarkdownTag($value); | ||
56 | continue; | ||
57 | } | ||
58 | $value['description'] = reverse_feed_permalink($value['description']); | ||
59 | $value['description'] = process_markdown( | ||
60 | $value['description'], | ||
61 | $conf->get('security.markdown_escape', true), | ||
62 | $conf->get('security.allowed_protocols') | ||
63 | ); | ||
64 | } | ||
65 | |||
66 | return $data; | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Parse daily descriptions. | ||
71 | * | ||
72 | * @param array $data daily data. | ||
73 | * @param ConfigManager $conf instance. | ||
74 | * | ||
75 | * @return mixed daily data parsed in markdown (and converted to HTML). | ||
76 | */ | ||
77 | function hook_markdown_render_daily($data, $conf) | ||
78 | { | ||
79 | //var_dump($data);die; | ||
80 | // Manipulate columns data | ||
81 | foreach ($data['linksToDisplay'] as &$value) { | ||
82 | if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { | ||
83 | $value = stripNoMarkdownTag($value); | ||
84 | continue; | ||
85 | } | ||
86 | $value['formatedDescription'] = process_markdown( | ||
87 | $value['formatedDescription'], | ||
88 | $conf->get('security.markdown_escape', true), | ||
89 | $conf->get('security.allowed_protocols') | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | return $data; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Check if noMarkdown is set in tags. | ||
98 | * | ||
99 | * @param string $tags tag list | ||
100 | * | ||
101 | * @return bool true if markdown should be disabled on this link. | ||
102 | */ | ||
103 | function noMarkdownTag($tags) | ||
104 | { | ||
105 | return preg_match('/(^|\s)'. NO_MD_TAG .'(\s|$)/', $tags); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Remove the no-markdown meta tag so it won't be displayed. | ||
110 | * | ||
111 | * @param array $link Link data. | ||
112 | * | ||
113 | * @return array Updated link without no markdown tag. | ||
114 | */ | ||
115 | function stripNoMarkdownTag($link) | ||
116 | { | ||
117 | if (! empty($link['taglist'])) { | ||
118 | $offset = array_search(NO_MD_TAG, $link['taglist']); | ||
119 | if ($offset !== false) { | ||
120 | unset($link['taglist'][$offset]); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | if (!empty($link['tags'])) { | ||
125 | str_replace(NO_MD_TAG, '', $link['tags']); | ||
126 | } | ||
127 | |||
128 | return $link; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * When link list is displayed, include markdown CSS. | ||
133 | * | ||
134 | * @param array $data includes data. | ||
135 | * | ||
136 | * @return mixed - includes data with markdown CSS file added. | ||
137 | */ | ||
138 | function hook_markdown_render_includes($data) | ||
139 | { | ||
140 | if ($data['_PAGE_'] == Router::$PAGE_LINKLIST | ||
141 | || $data['_PAGE_'] == Router::$PAGE_DAILY | ||
142 | || $data['_PAGE_'] == Router::$PAGE_EDITLINK | ||
143 | ) { | ||
144 | $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/markdown/markdown.css'; | ||
145 | } | ||
146 | |||
147 | return $data; | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Hook render_editlink. | ||
152 | * Adds an help link to markdown syntax. | ||
153 | * | ||
154 | * @param array $data data passed to plugin | ||
155 | * | ||
156 | * @return array altered $data. | ||
157 | */ | ||
158 | function hook_markdown_render_editlink($data) | ||
159 | { | ||
160 | // Load help HTML into a string | ||
161 | $txt = file_get_contents(PluginManager::$PLUGINS_PATH .'/markdown/help.html'); | ||
162 | $translations = [ | ||
163 | t('Description will be rendered with'), | ||
164 | t('Markdown syntax documentation'), | ||
165 | t('Markdown syntax'), | ||
166 | ]; | ||
167 | $data['edit_link_plugin'][] = vsprintf($txt, $translations); | ||
168 | // Add no markdown 'meta-tag' in tag list if it was never used, for autocompletion. | ||
169 | if (! in_array(NO_MD_TAG, $data['tags'])) { | ||
170 | $data['tags'][NO_MD_TAG] = 0; | ||
171 | } | ||
172 | |||
173 | return $data; | ||
174 | } | ||
175 | |||
176 | |||
177 | /** | ||
178 | * Remove HTML links auto generated by Shaarli core system. | ||
179 | * Keeps HREF attributes. | ||
180 | * | ||
181 | * @param string $description input description text. | ||
182 | * | ||
183 | * @return string $description without HTML links. | ||
184 | */ | ||
185 | function reverse_text2clickable($description) | ||
186 | { | ||
187 | $descriptionLines = explode(PHP_EOL, $description); | ||
188 | $descriptionOut = ''; | ||
189 | $codeBlockOn = false; | ||
190 | $lineCount = 0; | ||
191 | |||
192 | foreach ($descriptionLines as $descriptionLine) { | ||
193 | // Detect line of code: starting with 4 spaces, | ||
194 | // except lists which can start with +/*/- or `2.` after spaces. | ||
195 | $codeLineOn = preg_match('/^ +(?=[^\+\*\-])(?=(?!\d\.).)/', $descriptionLine) > 0; | ||
196 | // Detect and toggle block of code | ||
197 | if (!$codeBlockOn) { | ||
198 | $codeBlockOn = preg_match('/^```/', $descriptionLine) > 0; | ||
199 | } elseif (preg_match('/^```/', $descriptionLine) > 0) { | ||
200 | $codeBlockOn = false; | ||
201 | } | ||
202 | |||
203 | $hashtagTitle = ' title="Hashtag [^"]+"'; | ||
204 | // Reverse `inline code` hashtags. | ||
205 | $descriptionLine = preg_replace( | ||
206 | '!(`[^`\n]*)<a href="[^ ]*"'. $hashtagTitle .'>([^<]+)</a>([^`\n]*`)!m', | ||
207 | '$1$2$3', | ||
208 | $descriptionLine | ||
209 | ); | ||
210 | |||
211 | // Reverse all links in code blocks, only non hashtag elsewhere. | ||
212 | $hashtagFilter = (!$codeBlockOn && !$codeLineOn) ? '(?!'. $hashtagTitle .')': '(?:'. $hashtagTitle .')?'; | ||
213 | $descriptionLine = preg_replace( | ||
214 | '#<a href="[^ ]*"'. $hashtagFilter .'>([^<]+)</a>#m', | ||
215 | '$1', | ||
216 | $descriptionLine | ||
217 | ); | ||
218 | |||
219 | // Make hashtag links markdown ready, otherwise the links will be ignored with escape set to true | ||
220 | if (!$codeBlockOn && !$codeLineOn) { | ||
221 | $descriptionLine = preg_replace( | ||
222 | '#<a href="([^ ]*)"'. $hashtagTitle .'>([^<]+)</a>#m', | ||
223 | '[$2]($1)', | ||
224 | $descriptionLine | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | $descriptionOut .= $descriptionLine; | ||
229 | if ($lineCount++ < count($descriptionLines) - 1) { | ||
230 | $descriptionOut .= PHP_EOL; | ||
231 | } | ||
232 | } | ||
233 | return $descriptionOut; | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * Remove <br> tag to let markdown handle it. | ||
238 | * | ||
239 | * @param string $description input description text. | ||
240 | * | ||
241 | * @return string $description without <br> tags. | ||
242 | */ | ||
243 | function reverse_nl2br($description) | ||
244 | { | ||
245 | return preg_replace('!<br */?>!im', '', $description); | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Remove HTML spaces ' ' auto generated by Shaarli core system. | ||
250 | * | ||
251 | * @param string $description input description text. | ||
252 | * | ||
253 | * @return string $description without HTML links. | ||
254 | */ | ||
255 | function reverse_space2nbsp($description) | ||
256 | { | ||
257 | return preg_replace('/(^| ) /m', '$1 ', $description); | ||
258 | } | ||
259 | |||
260 | function reverse_feed_permalink($description) | ||
261 | { | ||
262 | return preg_replace('@— <a href="([^"]+)" title="[^"]+">([^<]+)</a>$@im', '— [$2]($1)', $description); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * Replace not whitelisted protocols with http:// in given description. | ||
267 | * | ||
268 | * @param string $description input description text. | ||
269 | * @param array $allowedProtocols list of allowed protocols. | ||
270 | * | ||
271 | * @return string $description without malicious link. | ||
272 | */ | ||
273 | function filter_protocols($description, $allowedProtocols) | ||
274 | { | ||
275 | return preg_replace_callback( | ||
276 | '#]\((.*?)\)#is', | ||
277 | function ($match) use ($allowedProtocols) { | ||
278 | return ']('. whitelist_protocols($match[1], $allowedProtocols) .')'; | ||
279 | }, | ||
280 | $description | ||
281 | ); | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * Remove dangerous HTML tags (tags, iframe, etc.). | ||
286 | * Doesn't affect <code> content (already escaped by Parsedown). | ||
287 | * | ||
288 | * @param string $description input description text. | ||
289 | * | ||
290 | * @return string given string escaped. | ||
291 | */ | ||
292 | function sanitize_html($description) | ||
293 | { | ||
294 | $escapeTags = array( | ||
295 | 'script', | ||
296 | 'style', | ||
297 | 'link', | ||
298 | 'iframe', | ||
299 | 'frameset', | ||
300 | 'frame', | ||
301 | ); | ||
302 | foreach ($escapeTags as $tag) { | ||
303 | $description = preg_replace_callback( | ||
304 | '#<\s*'. $tag .'[^>]*>(.*</\s*'. $tag .'[^>]*>)?#is', | ||
305 | function ($match) { | ||
306 | return escape($match[0]); | ||
307 | }, | ||
308 | $description | ||
309 | ); | ||
310 | } | ||
311 | $description = preg_replace( | ||
312 | '#(<[^>]+\s)on[a-z]*="?[^ "]*"?#is', | ||
313 | '$1', | ||
314 | $description | ||
315 | ); | ||
316 | return $description; | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * Render shaare contents through Markdown parser. | ||
321 | * 1. Remove HTML generated by Shaarli core. | ||
322 | * 2. Reverse the escape function. | ||
323 | * 3. Generate markdown descriptions. | ||
324 | * 4. Sanitize sensible HTML tags for security. | ||
325 | * 5. Wrap description in 'markdown' CSS class. | ||
326 | * | ||
327 | * @param string $description input description text. | ||
328 | * @param bool $escape escape HTML entities | ||
329 | * | ||
330 | * @return string HTML processed $description. | ||
331 | */ | ||
332 | function process_markdown($description, $escape = true, $allowedProtocols = []) | ||
333 | { | ||
334 | $parsedown = new Parsedown(); | ||
335 | |||
336 | $processedDescription = $description; | ||
337 | $processedDescription = reverse_nl2br($processedDescription); | ||
338 | $processedDescription = reverse_space2nbsp($processedDescription); | ||
339 | $processedDescription = reverse_text2clickable($processedDescription); | ||
340 | $processedDescription = filter_protocols($processedDescription, $allowedProtocols); | ||
341 | $processedDescription = unescape($processedDescription); | ||
342 | $processedDescription = $parsedown | ||
343 | ->setMarkupEscaped($escape) | ||
344 | ->setBreaksEnabled(true) | ||
345 | ->text($processedDescription); | ||
346 | $processedDescription = sanitize_html($processedDescription); | ||
347 | |||
348 | if (!empty($processedDescription)) { | ||
349 | $processedDescription = '<div class="markdown">'. $processedDescription . '</div>'; | ||
350 | } | ||
351 | |||
352 | return $processedDescription; | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * This function is never called, but contains translation calls for GNU gettext extraction. | ||
357 | */ | ||
358 | function markdown_dummy_translation() | ||
359 | { | ||
360 | // meta | ||
361 | t('Render shaare description with Markdown syntax.<br><strong>Warning</strong>: | ||
362 | If your shaared descriptions contained HTML tags before enabling the markdown plugin, | ||
363 | enabling it might break your page. | ||
364 | See the <a href="https://github.com/shaarli/Shaarli/tree/master/plugins/markdown#html-rendering">README</a>.'); | ||
365 | } | ||