PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.8.1
Jetpack – WP Security, Backup, Speed, & Growth v14.8.1
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / _inc / lib / markdown / gfm.php
jetpack / _inc / lib / markdown Last commit date
README.md 8 years ago extra.php 1 year ago gfm.php 2 years ago
gfm.php
446 lines
1 <?php
2 /**
3 * GitHub-Flavoured Markdown. Inspired by Evan's plugin, but modified.
4 *
5 * @author Evan Solomon
6 * @author Matt Wiebe <wiebe@automattic.com>
7 * @author Brandon Kraft <kraft@automattic.com> -- Strikedown support converted from GPL code at https://github.com/annaesvensson/yellow-markdown/blob/main/markdown.php
8 * @link https://github.com/evansolomon/wp-github-flavored-markdown-comments
9 *
10 * Add a few extras from GitHub's Markdown implementation. Must be used in a WordPress environment.
11 */
12
13 class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
14
15 /**
16 * Hooray somewhat arbitrary numbers that are fearful of 1.0.x.
17 */
18 const WPCOM_GHF_MARDOWN_VERSION = '0.9.1';
19
20 /**
21 * Use a [code] shortcode when encountering a fenced code block
22 * @var boolean
23 */
24 public $use_code_shortcode = true;
25
26 /**
27 * Preserve shortcodes, untouched by Markdown.
28 * This requires use within a WordPress installation.
29 * @var boolean
30 */
31 public $preserve_shortcodes = true;
32
33 /**
34 * Preserve the legacy $latex your-latex-code-here$ style
35 * LaTeX markup
36 */
37 public $preserve_latex = true;
38
39 /**
40 * Preserve single-line <code> blocks.
41 * @var boolean
42 */
43 public $preserve_inline_code_blocks = true;
44
45 /**
46 * Strip paragraphs from the output. This is the right default for WordPress,
47 * which generally wants to create its own paragraphs with `wpautop`
48 * @var boolean
49 */
50 public $strip_paras = true;
51
52 // Will run through sprintf - you can supply your own syntax if you want
53 public $shortcode_start = '[code lang=%s]';
54 public $shortcode_end = '[/code]';
55
56 // Stores shortcodes we remove and then replace
57 protected $preserve_text_hash = array();
58
59 /**
60 * Set environment defaults based on presence of key functions/classes.
61 */
62 public function __construct() {
63 $this->use_code_shortcode = class_exists( 'SyntaxHighlighter' );
64 /**
65 * Allow processing shortcode contents.
66 *
67 * @module markdown
68 *
69 * @since 4.4.0
70 *
71 * @param boolean $preserve_shortcodes Defaults to $this->preserve_shortcodes.
72 */
73 $this->preserve_shortcodes = apply_filters( 'jetpack_markdown_preserve_shortcodes', $this->preserve_shortcodes ) && function_exists( 'get_shortcode_regex' );
74 $this->preserve_latex = function_exists( 'latex_markup' );
75 $this->strip_paras = function_exists( 'wpautop' );
76
77 $this->span_gamut['do_strikethrough'] = 55;
78
79 parent::__construct();
80 }
81
82 /**
83 * Overload to specify heading styles only if the hash has space(s) after it. This is actually in keeping with
84 * the documentation and eases the semantic overload of the hash character.
85 * #Will Not Produce a Heading 1
86 * # This Will Produce a Heading 1
87 *
88 * @param string $text Markdown text
89 * @return string HTML-transformed text
90 */
91 public function transform( $text ) {
92 // Preserve anything inside a single-line <code> element
93 if ( $this->preserve_inline_code_blocks ) {
94 $text = $this->single_line_code_preserve( $text );
95 }
96 // Remove all shortcodes so their interiors are left intact
97 if ( $this->preserve_shortcodes ) {
98 $text = $this->shortcode_preserve( $text );
99 }
100 // Remove legacy LaTeX so it's left intact
101 if ( $this->preserve_latex ) {
102 $text = $this->latex_preserve( $text );
103 }
104
105 // Do not process characters inside URLs.
106 $text = $this->urls_preserve( $text );
107
108 // escape line-beginning # chars that do not have a space after them.
109 $text = preg_replace_callback( '|^#{1,6}( )?|um', array( $this, '_doEscapeForHashWithoutSpacing' ), $text );
110
111 /**
112 * Allow third-party plugins to define custom patterns that won't be processed by Markdown.
113 *
114 * @module markdown
115 *
116 * @since 3.9.2
117 *
118 * @param array $custom_patterns Array of custom patterns to be ignored by Markdown.
119 */
120 $custom_patterns = apply_filters( 'jetpack_markdown_preserve_pattern', array() );
121 if ( is_array( $custom_patterns ) && ! empty( $custom_patterns ) ) {
122 foreach ( $custom_patterns as $pattern ) {
123 $text = preg_replace_callback( $pattern, array( $this, '_doRemoveText'), $text );
124 }
125 }
126
127 // run through core Markdown
128 $text = parent::transform( $text );
129
130 // Occasionally Markdown Extra chokes on a para structure, producing odd paragraphs.
131 $text = str_replace( "<p>&lt;</p>\n\n<p>p>", '<p>', $text );
132
133 // put start-of-line # chars back in place
134 $text = $this->restore_leading_hash( $text );
135
136 // Strip paras if set
137 if ( $this->strip_paras ) {
138 $text = $this->unp( $text );
139 }
140
141 // Restore preserved things like shortcodes/LaTeX
142 $text = $this->do_restore( $text );
143
144 return $text;
145 }
146
147 /**
148 * Prevents blocks like <code>__this__</code> from turning into <code><strong>this</strong></code>
149 * @param string $text Text that may need preserving
150 * @return string Text that was preserved if needed
151 */
152 public function single_line_code_preserve( $text ) {
153 return preg_replace_callback( '|<code\b[^>]*>(.*?)</code>|', array( $this, 'do_single_line_code_preserve' ), $text );
154 }
155
156 /**
157 * Regex callback for inline code presevation
158 * @param array $matches Regex matches
159 * @return string Hashed content for later restoration
160 */
161 public function do_single_line_code_preserve( $matches ) {
162 return '<code>' . $this->hash_block( $matches[1] ) . '</code>';
163 }
164
165 /**
166 * Preserve code block contents by HTML encoding them. Useful before getting to KSES stripping.
167 * @param string $text Markdown/HTML content
168 * @return string Markdown/HTML content with escaped code blocks
169 */
170 public function codeblock_preserve( $text ) {
171 return preg_replace_callback( "/^([`~]{3})([^`\n]+)?\n([^`~]+)(\\1)/m", array( $this, 'do_codeblock_preserve' ), $text );
172 }
173
174 /**
175 * Regex callback for code block preservation.
176 * @param array $matches Regex matches
177 * @return string Codeblock with escaped interior
178 */
179 public function do_codeblock_preserve( $matches ) {
180 $block = stripslashes( $matches[3] );
181 $block = esc_html( $block );
182 $block = str_replace( '\\', '\\\\', $block );
183 $open = $matches[1] . $matches[2] . "\n";
184 return $open . $block . $matches[4];
185 }
186
187 /**
188 * Restore previously preserved (i.e. escaped) code block contents.
189 * @param string $text Markdown/HTML content with escaped code blocks
190 * @return string Markdown/HTML content
191 */
192 public function codeblock_restore( $text ) {
193 return preg_replace_callback( "/^([`~]{3})([^`\n]+)?\n([^`~]+)(\\1)/m", array( $this, 'do_codeblock_restore' ), $text );
194 }
195
196 /**
197 * Regex callback for code block restoration (unescaping).
198 * @param array $matches Regex matches
199 * @return string Codeblock with unescaped interior
200 */
201 public function do_codeblock_restore( $matches ) {
202 $block = html_entity_decode( $matches[3], ENT_QUOTES );
203 $open = $matches[1] . $matches[2] . "\n";
204 return $open . $block . $matches[4];
205 }
206
207 /**
208 * Called to preserve legacy LaTeX like $latex some-latex-text $
209 * @param string $text Text in which to preserve LaTeX
210 * @return string Text with LaTeX replaced by a hash that will be restored later
211 */
212 protected function latex_preserve( $text ) {
213 // regex from latex_remove()
214 $regex = '%
215 \$latex(?:=\s*|\s+)
216 ((?:
217 [^$]+ # Not a dollar
218 |
219 (?<=(?<!\\\\)\\\\)\$ # Dollar preceded by exactly one slash
220 )+)
221 (?<!\\\\)\$ # Dollar preceded by zero slashes
222 %ix';
223 $text = preg_replace_callback( $regex, array( $this, '_doRemoveText'), $text );
224 return $text;
225 }
226
227 /**
228 * Called to preserve WP shortcodes from being formatted by Markdown in any way.
229 * @param string $text Text in which to preserve shortcodes
230 * @return string Text with shortcodes replaced by a hash that will be restored later
231 */
232 protected function shortcode_preserve( $text ) {
233 $text = preg_replace_callback( $this->get_shortcode_regex(), array( $this, '_doRemoveText' ), $text );
234 return $text;
235 }
236
237 /**
238 * Avoid characters inside URLs from being formatted by Markdown in any way.
239 *
240 * @param string $text Text in which to preserve URLs.
241 *
242 * @return string Text with URLs replaced by a hash that will be restored later.
243 */
244 protected function urls_preserve( $text ) {
245 $text = preg_replace_callback(
246 '#(?<!<)(?:https?|ftp)://([^\s<>"\'\[\]()]+|\[(?1)*+\]|\((?1)*+\))+(?<![_*.?])#i',
247 array( $this, '_doRemoveText' ),
248 $text
249 );
250 return $text;
251 }
252
253 /**
254 * Restores any text preserved by $this->hash_block()
255 * @param string $text Text that may have hashed preservation placeholders
256 * @return string Text with hashed preseravtion placeholders replaced by original text
257 */
258 protected function do_restore( $text ) {
259 // Reverse hashes to ensure nested blocks are restored.
260 $hashes = array_reverse( $this->preserve_text_hash, true );
261 foreach( $hashes as $hash => $value ) {
262 $placeholder = $this->hash_maker( $hash );
263 $text = str_replace( $placeholder, $value, $text );
264 }
265 // reset the hash
266 $this->preserve_text_hash = array();
267 return $text;
268 }
269
270 /**
271 * Regex callback for text preservation
272 * @param array $m Regex $matches array
273 * @return string A placeholder that will later be replaced by the original text
274 */
275 protected function _doRemoveText( $m ) {
276 return $this->hash_block( $m[0] );
277 }
278
279 /**
280 * Call this to store a text block for later restoration.
281 * @param string $text Text to preserve for later
282 * @return string Placeholder that will be swapped out later for the original text
283 */
284 protected function hash_block( $text ) {
285 $hash = md5( $text );
286 $this->preserve_text_hash[ $hash ] = $text;
287 $placeholder = $this->hash_maker( $hash );
288 return $placeholder;
289 }
290
291 /**
292 * Less glamorous than the Keymaker
293 * @param string $hash An md5 hash
294 * @return string A placeholder hash
295 */
296 protected function hash_maker( $hash ) {
297 return 'MARKDOWN_HASH' . $hash . 'MARKDOWN_HASH';
298 }
299
300 /**
301 * Remove bare <p> elements. <p>s with attributes will be preserved.
302 * @param string $text HTML content
303 * @return string <p>-less content
304 */
305 public function unp( $text ) {
306 return preg_replace( "#<p>(.*?)</p>(\n|$)#ums", '$1$2', $text );
307 }
308
309 /**
310 * A regex of all shortcodes currently registered by the current
311 * WordPress installation
312 * @uses get_shortcode_regex()
313 * @return string A regex for grabbing shortcodes.
314 */
315 protected function get_shortcode_regex() {
316 $pattern = get_shortcode_regex();
317
318 // don't match markdown link anchors that could be mistaken for shortcodes.
319 $pattern .= '(?!\()';
320
321 return "/$pattern/s";
322 }
323
324 /**
325 * Since we escape unspaced #Headings, put things back later.
326 * @param string $text text with a leading escaped hash
327 * @return string text with leading hashes unescaped
328 */
329 protected function restore_leading_hash( $text ) {
330 return preg_replace( "/^(<p>)?(&#35;|\\\\#)/um", "$1#", $text );
331 }
332
333 /**
334 * Overload to support ```-fenced code blocks for pre-Markdown Extra 1.2.8
335 * https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
336 */
337 public function doFencedCodeBlocks( $text ) {
338 // If we're at least at 1.2.8, native fenced code blocks are in.
339 // Below is just copied from it in case we somehow got loaded on
340 // top of someone else's Markdown Extra
341 if ( version_compare( MARKDOWNEXTRA_VERSION, '1.2.8', '>=' ) )
342 return parent::doFencedCodeBlocks( $text );
343
344 #
345 # Adding the fenced code block syntax to regular Markdown:
346 #
347 # ~~~
348 # Code block
349 # ~~~
350 #
351 $less_than_tab = $this->tab_width;
352
353 $text = preg_replace_callback('{
354 (?:\n|\A)
355 # 1: Opening marker
356 (
357 (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
358 )
359 [ ]*
360 (?:
361 \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
362 |
363 '.$this->id_class_attr_catch_re.' # 3: Extra attributes
364 )?
365 [ ]* \n # Whitespace and newline following marker.
366
367 # 4: Content
368 (
369 (?>
370 (?!\1 [ ]* \n) # Not a closing marker.
371 .*\n+
372 )+
373 )
374
375 # Closing marker.
376 \1 [ ]* (?= \n )
377 }xm',
378 array($this, '_doFencedCodeBlocks_callback'), $text);
379
380 return $text;
381 }
382
383 /**
384 * Callback for pre-processing start of line hashes to slyly escape headings that don't
385 * have a leading space
386 * @param array $m preg_match matches
387 * @return string possibly escaped start of line hash
388 */
389 public function _doEscapeForHashWithoutSpacing( $m ) {
390 if ( ! isset( $m[1] ) )
391 $m[0] = '\\' . $m[0];
392 return $m[0];
393 }
394
395 /**
396 * Overload to support Viper's [code] shortcode. Because awesome.
397 */
398 public function _doFencedCodeBlocks_callback( $matches ) {
399 // in case we have some escaped leading hashes right at the start of the block
400 $matches[4] = $this->restore_leading_hash( $matches[4] );
401 // just MarkdownExtra_Parser if we're not going ultra-deluxe
402 if ( ! $this->use_code_shortcode ) {
403 return parent::_doFencedCodeBlocks_callback( $matches );
404 }
405
406 // default to a "text" class if one wasn't passed. Helps with encoding issues later.
407 if ( empty( $matches[2] ) ) {
408 $matches[2] = 'text';
409 }
410
411 $classname =& $matches[2];
412 $codeblock = preg_replace_callback('/^\n+/', array( $this, '_doFencedCodeBlocks_newlines' ), $matches[4] );
413
414 if ( $classname[0] == '.' )
415 $classname = substr( $classname, 1 );
416
417 $codeblock = esc_html( $codeblock );
418 $codeblock = sprintf( $this->shortcode_start, $classname ) . "\n{$codeblock}" . $this->shortcode_end;
419 return "\n\n" . $this->hashBlock( $codeblock ). "\n\n";
420 }
421
422 /**
423 * Add strikethrough support.
424 *
425 * GPL code modified from https://github.com/annaesvensson/yellow-markdown/blob/main/markdown.php
426 */
427 public function do_strikethrough($text) {
428 $parts = preg_split("/(?<![~])(~~)(?![~])/", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
429 if (count($parts)>3) {
430 $text = "";
431 $open = false;
432 foreach ($parts as $part) {
433 if ($part=="~~") {
434 $text .= $open ? "</del>" : "<del>";
435 $open = !$open;
436 } else {
437 $text .= $part;
438 }
439 }
440 if ($open) $text .= "</del>";
441 }
442 return $text;
443 }
444
445 }
446