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