PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 1.0.8
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v1.0.8
3.2.2 3.2.3 3.2.1 3.2.0 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.9 trunk 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.3 1.1.4 1.1.5 All 174 releases
master-addons / lib / readme-parser.php

readme-parser.php in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits 1.0.8, at lib/readme-parser.php

333 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // This is the path to markdown.php
3 if ( !defined('WORDPRESS_README_MARKDOWN') ) {
4 if ( defined('AUTOMATTIC_README_MARKDOWN') )
5 define( 'WORDPRESS_README_MARKDOWN', AUTOMATTIC_README_MARKDOWN );
6 else
7 define('WORDPRESS_README_MARKDOWN', MELA_PLUGIN_PATH . '/lib/markdown.php');
8 }
9
10 Class WordPress_Readme_Parser {
11
12 function __construct() {
13 // This space intentially blank
14 }
15
16 function parse_readme( $file ) {
17 $file_contents = @implode('', @file($file));
18 return $this->parse_readme_contents( $file_contents );
19 }
20
21 function parse_readme_contents( $file_contents ) {
22 $file_contents = str_replace(array("\r\n", "\r"), "\n", $file_contents);
23 $file_contents = trim($file_contents);
24 if ( 0 === strpos( $file_contents, "\xEF\xBB\xBF" ) )
25 $file_contents = substr( $file_contents, 3 );
26
27 // Markdown transformations
28 $file_contents = preg_replace( "|^###([^#]+)#*?\s*?\n|im", '=$1='."\n", $file_contents );
29 $file_contents = preg_replace( "|^##([^#]+)#*?\s*?\n|im", '==$1=='."\n", $file_contents );
30 $file_contents = preg_replace( "|^#([^#]+)#*?\s*?\n|im", '===$1==='."\n", $file_contents );
31
32 // === Plugin Name ===
33 // Must be the very first thing.
34 if ( !preg_match('|^===(.*)===|', $file_contents, $_name) )
35 return array(); // require a name
36 $name = trim($_name[1], '=');
37 $name = $this->sanitize_text( $name );
38
39 $file_contents = $this->chop_string( $file_contents, $_name[0] );
40
41
42 // Requires at least: 1.5
43 if ( preg_match('|Requires at least:(.*)|i', $file_contents, $_requires_at_least) )
44 $requires_at_least = $this->sanitize_text($_requires_at_least[1]);
45 else
46 $requires_at_least = NULL;
47
48
49 // Tested up to: 2.1
50 if ( preg_match('|Tested up to:(.*)|i', $file_contents, $_tested_up_to) )
51 $tested_up_to = $this->sanitize_text( $_tested_up_to[1] );
52 else
53 $tested_up_to = NULL;
54
55
56 // Stable tag: 10.4-ride-the-fire-eagle-danger-day
57 if ( preg_match('|Stable tag:(.*)|i', $file_contents, $_stable_tag) )
58 $stable_tag = $this->sanitize_text( $_stable_tag[1] );
59 else
60 $stable_tag = NULL; // we assume trunk, but don't set it here to tell the difference between specified trunk and default trunk
61
62
63 // Tags: some tag, another tag, we like tags
64 if ( preg_match('|Tags:(.*)|i', $file_contents, $_tags) ) {
65 $tags = preg_split('|,[\s]*?|', trim($_tags[1]));
66 foreach ( array_keys($tags) as $t )
67 $tags[$t] = $this->sanitize_text( $tags[$t] );
68 } else {
69 $tags = array();
70 }
71
72
73 // Contributors: markjaquith, mdawaffe, zefrank
74 $contributors = array();
75 if ( preg_match('|Contributors:(.*)|i', $file_contents, $_contributors) ) {
76 $temp_contributors = preg_split('|,[\s]*|', trim($_contributors[1]));
77 foreach ( array_keys($temp_contributors) as $c ) {
78 $tmp_sanitized = $this->user_sanitize( $temp_contributors[$c] );
79 if ( strlen(trim($tmp_sanitized)) > 0 )
80 $contributors[$c] = $tmp_sanitized;
81 unset($tmp_sanitized);
82 }
83 }
84
85
86 // Donate Link: URL
87 if ( preg_match('|Donate link:(.*)|i', $file_contents, $_donate_link) )
88 $donate_link = esc_url( $_donate_link[1] );
89 else
90 $donate_link = NULL;
91
92
93 // togs, conts, etc are optional and order shouldn't matter. So we chop them only after we've grabbed their values.
94 foreach ( array('tags', 'contributors', 'requires_at_least', 'tested_up_to', 'stable_tag', 'donate_link') as $chop ) {
95 if ( $$chop ) {
96 $_chop = '_' . $chop;
97 $file_contents = $this->chop_string( $file_contents, ${$_chop}[0] );
98 }
99 }
100
101 $file_contents = trim($file_contents);
102
103
104 // short-description fu
105 if ( !preg_match('/(^(.*?))^[\s]*=+?[\s]*.+?[\s]*=+?/ms', $file_contents, $_short_description) )
106 $_short_description = array( 1 => &$file_contents, 2 => &$file_contents );
107 $short_desc_filtered = $this->sanitize_text( $_short_description[2] );
108 $short_desc_length = strlen($short_desc_filtered);
109 $short_description = substr($short_desc_filtered, 0, 150);
110 if ( $short_desc_length > strlen($short_description) )
111 $truncated = true;
112 else
113 $truncated = false;
114 if ( $_short_description[1] )
115 $file_contents = $this->chop_string( $file_contents, $_short_description[1] ); // yes, the [1] is intentional
116
117 // == Section ==
118 // Break into sections
119 // $_sections[0] will be the title of the first section, $_sections[1] will be the content of the first section
120 // the array alternates from there: title2, content2, title3, content3... and so forth
121 $_sections = preg_split('/^[\s]*==[\s]*(.+?)[\s]*==/m', $file_contents, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
122
123 $sections = array();
124 for ( $i=1; $i <= count($_sections); $i +=2 ) {
125 if($i==1){continue;}
126 $_sections[$i] = preg_replace('/^[\s]*=[\s]+(.+?)[\s]+=/m', '<h4>$1</h4>', $_sections[$i]);
127 $_sections[$i] = $this->filter_text( $_sections[$i], true );
128 $title = $this->sanitize_text( $_sections[$i-1] );
129 $sections[str_replace(' ', '_', strtolower($title))] = array('title' => $title, 'content' => $_sections[$i]);
130 }
131
132
133 // Special sections
134 // This is where we nab our special sections, so we can enforce their order and treat them differently, if needed
135 // upgrade_notice is not a section, but parse it like it is for now
136 $final_sections = array();
137 foreach ( array('description', 'installation', 'frequently_asked_questions', 'screenshots', 'changelog', 'change_log', 'upgrade_notice') as $special_section ) {
138 if ( isset($sections[$special_section]) ) {
139 $final_sections[$special_section] = $sections[$special_section]['content'];
140 unset($sections[$special_section]);
141 }
142 }
143 if ( isset($final_sections['change_log']) && empty($final_sections['changelog']) )
144 $final_sections['changelog'] = $final_sections['change_log'];
145
146
147 $final_screenshots = array();
148 if ( isset($final_sections['screenshots']) ) {
149 preg_match_all('|<li>(.*?)</li>|s', $final_sections['screenshots'], $screenshots, PREG_SET_ORDER);
150 if ( $screenshots ) {
151 foreach ( (array) $screenshots as $ss )
152 $final_screenshots[] = $ss[1];
153 }
154 }
155
156 // Parse the upgrade_notice section specially:
157 // 1.0 => blah, 1.1 => fnord
158 // if ( isset($final_sections['upgrade_notice']) ) {
159 // $upgrade_notice = array();
160 // $split = preg_split( '#<h4>(.*?)</h4>#', $final_sections['upgrade_notice'], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
161 // for ( $i = 0; $i < count( $split ); $i += 2 )
162 // $upgrade_notice[$this->sanitize_text( $split[$i] )] = substr( $this->sanitize_text( $split[$i + 1] ), 0, 300 );
163 // unset( $final_sections['upgrade_notice'] );
164 // }
165
166 // No description?
167 // No problem... we'll just fall back to the old style of description
168 // We'll even let you use markup this time!
169 $excerpt = false;
170 if ( !isset($final_sections['description']) ) {
171 $final_sections = array_merge(array('description' => $this->filter_text( $_short_description[2], true )), $final_sections);
172 $excerpt = true;
173 }
174
175
176 // dump the non-special sections into $remaining_content
177 // their order will be determined by their original order in the readme.txt
178 $remaining_content = '';
179 foreach ( $sections as $s_name => $s_data ) {
180 $remaining_content .= "\n<h3>{$s_data['title']}</h3>\n{$s_data['content']}";
181 }
182 $remaining_content = trim($remaining_content);
183
184
185 // All done!
186 // $r['tags'] and $r['contributors'] are simple arrays
187 // $r['sections'] is an array with named elements
188 $r = array(
189 'name' => $name,
190 'tags' => $tags,
191 'requires_at_least' => $requires_at_least,
192 'tested_up_to' => $tested_up_to,
193 'stable_tag' => $stable_tag,
194 'contributors' => $contributors,
195 'donate_link' => $donate_link,
196 'short_description' => $short_description,
197 'screenshots' => $final_screenshots,
198 'is_excerpt' => $excerpt,
199 'is_truncated' => $truncated,
200 'sections' => $final_sections,
201 'remaining_content' => $remaining_content,
202 // 'upgrade_notice' => $upgrade_notice
203 );
204
205 return $r;
206 }
207
208 function chop_string( $string, $chop ) { // chop a "prefix" from a string: Agressive! uses strstr not 0 === strpos
209 if ( $_string = strstr($string, $chop) ) {
210 $_string = substr($_string, strlen($chop));
211 return trim($_string);
212 } else {
213 return trim($string);
214 }
215 }
216
217 function user_sanitize( $text, $strict = false ) { // whitelisted chars
218 if ( function_exists('user_sanitize') ) // bbPress native
219 return user_sanitize( $text, $strict );
220
221 if ( $strict ) {
222 $text = preg_replace('/[^a-z0-9-]/i', '', $text);
223 $text = preg_replace('|-+|', '-', $text);
224 } else {
225 $text = preg_replace('/[^a-z0-9_-]/i', '', $text);
226 }
227 return $text;
228 }
229
230 function sanitize_text( $text ) { // not fancy
231 $text = strip_tags($text);
232 $text = esc_html($text);
233 $text = trim($text);
234 return $text;
235 }
236
237 function filter_text( $text, $markdown = false ) { // fancy, Markdown
238 $text = trim($text);
239
240 $text = call_user_func( array( __CLASS__, 'code_trick' ), $text, $markdown ); // A better parser than Markdown's for: backticks -> CODE
241
242 if ( $markdown ) { // Parse markdown.
243 if ( !function_exists('Markdown') )
244 require( WORDPRESS_README_MARKDOWN );
245 $text = Markdown($text);
246 }
247
248 $allowed = array(
249 'a' => array(
250 'href' => array(),
251 'title' => array(),
252 'rel' => array()),
253 'blockquote' => array('cite' => array()),
254 'br' => array(),
255 'p' => array(),
256 'code' => array(),
257 'pre' => array(),
258 'em' => array(),
259 'strong' => array(),
260 'ul' => array(),
261 'ol' => array(),
262 'li' => array(),
263 'h3' => array(),
264 'h4' => array()
265 );
266
267 $text = balanceTags($text);
268
269 $text = wp_kses( $text, $allowed );
270 $text = trim($text);
271 return $text;
272 }
273
274 function code_trick( $text, $markdown ) { // Don't use bbPress native function - it's incompatible with Markdown
275 // If doing markdown, first take any user formatted code blocks and turn them into backticks so that
276 // markdown will preserve things like underscores in code blocks
277 if ( $markdown )
278 $text = preg_replace_callback("!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", array( __CLASS__,'decodeit'), $text);
279
280 $text = str_replace(array("\r\n", "\r"), "\n", $text);
281 if ( !$markdown ) {
282 // This gets the "inline" code blocks, but can't be used with Markdown.
283 $text = preg_replace_callback("|(`)(.*?)`|", array( __CLASS__, 'encodeit'), $text);
284 // This gets the "block level" code blocks and converts them to PRE CODE
285 $text = preg_replace_callback("!(^|\n)`(.*?)`!s", array( __CLASS__, 'encodeit'), $text);
286 } else {
287 // Markdown can do inline code, we convert bbPress style block level code to Markdown style
288 $text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array( __CLASS__, 'indent'), $text);
289 }
290 return $text;
291 }
292
293 function indent( $matches ) {
294 $text = $matches[3];
295 $text = preg_replace('|^|m', $matches[2] . ' ', $text);
296 return $matches[1] . $text;
297 }
298
299 function encodeit( $matches ) {
300 if ( function_exists('encodeit') ) // bbPress native
301 return encodeit( $matches );
302
303 $text = trim($matches[2]);
304 $text = htmlspecialchars($text, ENT_QUOTES);
305 $text = str_replace(array("\r\n", "\r"), "\n", $text);
306 $text = preg_replace("|\n\n\n+|", "\n\n", $text);
307 $text = str_replace('&amp;lt;', '&lt;', $text);
308 $text = str_replace('&amp;gt;', '&gt;', $text);
309 $text = "<code>$text</code>";
310 if ( "`" != $matches[1] )
311 $text = "<pre>$text</pre>";
312 return $text;
313 }
314
315 function decodeit( $matches ) {
316 if ( function_exists('decodeit') ) // bbPress native
317 return decodeit( $matches );
318
319 $text = $matches[2];
320 $trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
321 $text = strtr($text, $trans_table);
322 $text = str_replace('<br />', '', $text);
323 $text = str_replace('&#38;', '&', $text);
324 $text = str_replace('&#39;', "'", $text);
325 if ( '<pre><code>' == $matches[1] )
326 $text = "\n$text\n";
327 return "`$text`";
328 }
329
330 } // end class
331
332 // $li = new WordPress_Readme_Parser();
333 // Class Automattic_Readme extends WordPress_Readme_Parser {}