PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
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 7 months ago gfm.php 7 months ago
gfm.php
450 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 if ( ! defined( 'ABSPATH' ) ) {
14 exit( 0 );
15 }
16
17 class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
18
19 /**
20 * Hooray somewhat arbitrary numbers that are fearful of 1.0.x.
21 */
22 const WPCOM_GHF_MARDOWN_VERSION = '0.9.1';
23
24 /**
25 * Use a [code] shortcode when encountering a fenced code block
26 * @var boolean
27 */
28 public $use_code_shortcode = true;
29
30 /**
31 * Preserve shortcodes, untouched by Markdown.
32 * This requires use within a WordPress installation.
33 * @var boolean
34 */
35 public $preserve_shortcodes = true;
36
37 /**
38 * Preserve the legacy $latex your-latex-code-here$ style
39 * LaTeX markup
40 */
41 public $preserve_latex = true;
42
43 /**
44 * Preserve single-line <code> blocks.
45 * @var boolean
46 */
47 public $preserve_inline_code_blocks = true;
48
49 /**
50 * Strip paragraphs from the output. This is the right default for WordPress,
51 * which generally wants to create its own paragraphs with `wpautop`
52 * @var boolean
53 */
54 public $strip_paras = true;
55
56 // Will run through sprintf - you can supply your own syntax if you want
57 public $shortcode_start = '[code lang=%s]';
58 public $shortcode_end = '[/code]';
59
60 // Stores shortcodes we remove and then replace
61 protected $preserve_text_hash = array();
62
63 /**
64 * Set environment defaults based on presence of key functions/classes.
65 */
66 public function __construct() {
67 $this->use_code_shortcode = class_exists( 'SyntaxHighlighter' );
68 /**
69 * Allow processing shortcode contents.
70 *
71 * @module markdown
72 *
73 * @since 4.4.0
74 *
75 * @param boolean $preserve_shortcodes Defaults to $this->preserve_shortcodes.
76 */
77 $this->preserve_shortcodes = apply_filters( 'jetpack_markdown_preserve_shortcodes', $this->preserve_shortcodes ) && function_exists( 'get_shortcode_regex' );
78 $this->preserve_latex = function_exists( 'latex_markup' );
79 $this->strip_paras = function_exists( 'wpautop' );
80
81 $this->span_gamut['do_strikethrough'] = 55;
82
83 parent::__construct();
84 }
85
86 /**
87 * Overload to specify heading styles only if the hash has space(s) after it. This is actually in keeping with
88 * the documentation and eases the semantic overload of the hash character.
89 * #Will Not Produce a Heading 1
90 * # This Will Produce a Heading 1
91 *
92 * @param string $text Markdown text
93 * @return string HTML-transformed text
94 */
95 public function transform( $text ) {
96 // Preserve anything inside a single-line <code> element
97 if ( $this->preserve_inline_code_blocks ) {
98 $text = $this->single_line_code_preserve( $text );
99 }
100 // Remove all shortcodes so their interiors are left intact
101 if ( $this->preserve_shortcodes ) {
102 $text = $this->shortcode_preserve( $text );
103 }
104 // Remove legacy LaTeX so it's left intact
105 if ( $this->preserve_latex ) {
106 $text = $this->latex_preserve( $text );
107 }
108
109 // Do not process characters inside URLs.
110 $text = $this->urls_preserve( $text );
111
112 // escape line-beginning # chars that do not have a space after them.
113 $text = preg_replace_callback( '|^#{1,6}( )?|um', array( $this, '_doEscapeForHashWithoutSpacing' ), $text );
114
115 /**
116 * Allow third-party plugins to define custom patterns that won't be processed by Markdown.
117 *
118 * @module markdown
119 *
120 * @since 3.9.2
121 *
122 * @param array $custom_patterns Array of custom patterns to be ignored by Markdown.
123 */
124 $custom_patterns = apply_filters( 'jetpack_markdown_preserve_pattern', array() );
125 if ( is_array( $custom_patterns ) && ! empty( $custom_patterns ) ) {
126 foreach ( $custom_patterns as $pattern ) {
127 $text = preg_replace_callback( $pattern, array( $this, '_doRemoveText'), $text );
128 }
129 }
130
131 // run through core Markdown
132 $text = parent::transform( $text );
133
134 // Occasionally Markdown Extra chokes on a para structure, producing odd paragraphs.
135 $text = str_replace( "<p>&lt;</p>\n\n<p>p>", '<p>', $text );
136
137 // put start-of-line # chars back in place
138 $text = $this->restore_leading_hash( $text );
139
140 // Strip paras if set
141 if ( $this->strip_paras ) {
142 $text = $this->unp( $text );
143 }
144
145 // Restore preserved things like shortcodes/LaTeX
146 $text = $this->do_restore( $text );
147
148 return $text;
149 }
150
151 /**
152 * Prevents blocks like <code>__this__</code> from turning into <code><strong>this</strong></code>
153 * @param string $text Text that may need preserving
154 * @return string Text that was preserved if needed
155 */
156 public function single_line_code_preserve( $text ) {
157 return preg_replace_callback( '|<code\b[^>]*>(.*?)</code>|', array( $this, 'do_single_line_code_preserve' ), $text );
158 }
159
160 /**
161 * Regex callback for inline code presevation
162 * @param array $matches Regex matches
163 * @return string Hashed content for later restoration
164 */
165 public function do_single_line_code_preserve( $matches ) {
166 return '<code>' . $this->hash_block( $matches[1] ) . '</code>';
167 }
168
169 /**
170 * Preserve code block contents by HTML encoding them. Useful before getting to KSES stripping.
171 * @param string $text Markdown/HTML content
172 * @return string Markdown/HTML content with escaped code blocks
173 */
174 public function codeblock_preserve( $text ) {
175 return preg_replace_callback( "/^([`~]{3})([^`\n]+)?\n([^`~]+)(\\1)/m", array( $this, 'do_codeblock_preserve' ), $text );
176 }
177
178 /**
179 * Regex callback for code block preservation.
180 * @param array $matches Regex matches
181 * @return string Codeblock with escaped interior
182 */
183 public function do_codeblock_preserve( $matches ) {
184 $block = stripslashes( $matches[3] );
185 $block = esc_html( $block );
186 $block = str_replace( '\\', '\\\\', $block );
187 $open = $matches[1] . $matches[2] . "\n";
188 return $open . $block . $matches[4];
189 }
190
191 /**
192 * Restore previously preserved (i.e. escaped) code block contents.
193 * @param string $text Markdown/HTML content with escaped code blocks
194 * @return string Markdown/HTML content
195 */
196 public function codeblock_restore( $text ) {
197 return preg_replace_callback( "/^([`~]{3})([^`\n]+)?\n([^`~]+)(\\1)/m", array( $this, 'do_codeblock_restore' ), $text );
198 }
199
200 /**
201 * Regex callback for code block restoration (unescaping).
202 * @param array $matches Regex matches
203 * @return string Codeblock with unescaped interior
204 */
205 public function do_codeblock_restore( $matches ) {
206 $block = html_entity_decode( $matches[3], ENT_QUOTES );
207 $open = $matches[1] . $matches[2] . "\n";
208 return $open . $block . $matches[4];
209 }
210
211 /**
212 * Called to preserve legacy LaTeX like $latex some-latex-text $
213 * @param string $text Text in which to preserve LaTeX
214 * @return string Text with LaTeX replaced by a hash that will be restored later
215 */
216 protected function latex_preserve( $text ) {
217 // regex from latex_remove()
218 $regex = '%
219 \$latex(?:=\s*|\s+)
220 ((?:
221 [^$]+ # Not a dollar
222 |
223 (?<=(?<!\\\\)\\\\)\$ # Dollar preceded by exactly one slash
224 )+)
225 (?<!\\\\)\$ # Dollar preceded by zero slashes
226 %ix';
227 $text = preg_replace_callback( $regex, array( $this, '_doRemoveText'), $text );
228 return $text;
229 }
230
231 /**
232 * Called to preserve WP shortcodes from being formatted by Markdown in any way.
233 * @param string $text Text in which to preserve shortcodes
234 * @return string Text with shortcodes replaced by a hash that will be restored later
235 */
236 protected function shortcode_preserve( $text ) {
237 $text = preg_replace_callback( $this->get_shortcode_regex(), array( $this, '_doRemoveText' ), $text );
238 return $text;
239 }
240
241 /**
242 * Avoid characters inside URLs from being formatted by Markdown in any way.
243 *
244 * @param string $text Text in which to preserve URLs.
245 *
246 * @return string Text with URLs replaced by a hash that will be restored later.
247 */
248 protected function urls_preserve( $text ) {
249 $text = preg_replace_callback(
250 '#(?<!<)(?:https?|ftp)://([^\s<>"\'\[\]()]+|\[(?1)*+\]|\((?1)*+\))+(?<![_*.?])#i',
251 array( $this, '_doRemoveText' ),
252 $text
253 );
254 return $text;
255 }
256
257 /**
258 * Restores any text preserved by $this->hash_block()
259 * @param string $text Text that may have hashed preservation placeholders
260 * @return string Text with hashed preseravtion placeholders replaced by original text
261 */
262 protected function do_restore( $text ) {
263 // Reverse hashes to ensure nested blocks are restored.
264 $hashes = array_reverse( $this->preserve_text_hash, true );
265 foreach( $hashes as $hash => $value ) {
266 $placeholder = $this->hash_maker( $hash );
267 $text = str_replace( $placeholder, $value, $text );
268 }
269 // reset the hash
270 $this->preserve_text_hash = array();
271 return $text;
272 }
273
274 /**
275 * Regex callback for text preservation
276 * @param array $m Regex $matches array
277 * @return string A placeholder that will later be replaced by the original text
278 */
279 protected function _doRemoveText( $m ) {
280 return $this->hash_block( $m[0] );
281 }
282
283 /**
284 * Call this to store a text block for later restoration.
285 * @param string $text Text to preserve for later
286 * @return string Placeholder that will be swapped out later for the original text
287 */
288 protected function hash_block( $text ) {
289 $hash = md5( $text );
290 $this->preserve_text_hash[ $hash ] = $text;
291 $placeholder = $this->hash_maker( $hash );
292 return $placeholder;
293 }
294
295 /**
296 * Less glamorous than the Keymaker
297 * @param string $hash An md5 hash
298 * @return string A placeholder hash
299 */
300 protected function hash_maker( $hash ) {
301 return 'MARKDOWN_HASH' . $hash . 'MARKDOWN_HASH';
302 }
303
304 /**
305 * Remove bare <p> elements. <p>s with attributes will be preserved.
306 * @param string $text HTML content
307 * @return string <p>-less content
308 */
309 public function unp( $text ) {
310 return preg_replace( "#<p>(.*?)</p>(\n|$)#ums", '$1$2', $text );
311 }
312
313 /**
314 * A regex of all shortcodes currently registered by the current
315 * WordPress installation
316 * @uses get_shortcode_regex()
317 * @return string A regex for grabbing shortcodes.
318 */
319 protected function get_shortcode_regex() {
320 $pattern = get_shortcode_regex();
321
322 // don't match markdown link anchors that could be mistaken for shortcodes.
323 $pattern .= '(?!\()';
324
325 return "/$pattern/s";
326 }
327
328 /**
329 * Since we escape unspaced #Headings, put things back later.
330 * @param string $text text with a leading escaped hash
331 * @return string text with leading hashes unescaped
332 */
333 protected function restore_leading_hash( $text ) {
334 return preg_replace( "/^(<p>)?(&#35;|\\\\#)/um", "$1#", $text );
335 }
336
337 /**
338 * Overload to support ```-fenced code blocks for pre-Markdown Extra 1.2.8
339 * https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
340 */
341 public function doFencedCodeBlocks( $text ) {
342 // If we're at least at 1.2.8, native fenced code blocks are in.
343 // Below is just copied from it in case we somehow got loaded on
344 // top of someone else's Markdown Extra
345 if ( version_compare( MARKDOWNEXTRA_VERSION, '1.2.8', '>=' ) )
346 return parent::doFencedCodeBlocks( $text );
347
348 #
349 # Adding the fenced code block syntax to regular Markdown:
350 #
351 # ~~~
352 # Code block
353 # ~~~
354 #
355 $less_than_tab = $this->tab_width;
356
357 $text = preg_replace_callback('{
358 (?:\n|\A)
359 # 1: Opening marker
360 (
361 (?:~{3,}|`{3,}) # 3 or more tildes/backticks.
362 )
363 [ ]*
364 (?:
365 \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
366 |
367 '.$this->id_class_attr_catch_re.' # 3: Extra attributes
368 )?
369 [ ]* \n # Whitespace and newline following marker.
370
371 # 4: Content
372 (
373 (?>
374 (?!\1 [ ]* \n) # Not a closing marker.
375 .*\n+
376 )+
377 )
378
379 # Closing marker.
380 \1 [ ]* (?= \n )
381 }xm',
382 array($this, '_doFencedCodeBlocks_callback'), $text);
383
384 return $text;
385 }
386
387 /**
388 * Callback for pre-processing start of line hashes to slyly escape headings that don't
389 * have a leading space
390 * @param array $m preg_match matches
391 * @return string possibly escaped start of line hash
392 */
393 public function _doEscapeForHashWithoutSpacing( $m ) {
394 if ( ! isset( $m[1] ) )
395 $m[0] = '\\' . $m[0];
396 return $m[0];
397 }
398
399 /**
400 * Overload to support Viper's [code] shortcode. Because awesome.
401 */
402 public function _doFencedCodeBlocks_callback( $matches ) {
403 // in case we have some escaped leading hashes right at the start of the block
404 $matches[4] = $this->restore_leading_hash( $matches[4] );
405 // just MarkdownExtra_Parser if we're not going ultra-deluxe
406 if ( ! $this->use_code_shortcode ) {
407 return parent::_doFencedCodeBlocks_callback( $matches );
408 }
409
410 // default to a "text" class if one wasn't passed. Helps with encoding issues later.
411 if ( empty( $matches[2] ) ) {
412 $matches[2] = 'text';
413 }
414
415 $classname =& $matches[2];
416 $codeblock = preg_replace_callback('/^\n+/', array( $this, '_doFencedCodeBlocks_newlines' ), $matches[4] );
417
418 if ( $classname[0] == '.' )
419 $classname = substr( $classname, 1 );
420
421 $codeblock = esc_html( $codeblock );
422 $codeblock = sprintf( $this->shortcode_start, $classname ) . "\n{$codeblock}" . $this->shortcode_end;
423 return "\n\n" . $this->hashBlock( $codeblock ). "\n\n";
424 }
425
426 /**
427 * Add strikethrough support.
428 *
429 * GPL code modified from https://github.com/annaesvensson/yellow-markdown/blob/main/markdown.php
430 */
431 public function do_strikethrough($text) {
432 $parts = preg_split("/(?<![~])(~~)(?![~])/", $text, -1, PREG_SPLIT_DELIM_CAPTURE);
433 if (count($parts)>3) {
434 $text = "";
435 $open = false;
436 foreach ($parts as $part) {
437 if ($part=="~~") {
438 $text .= $open ? "</del>" : "<del>";
439 $open = !$open;
440 } else {
441 $text .= $part;
442 }
443 }
444 if ($open) $text .= "</del>";
445 }
446 return $text;
447 }
448
449 }
450