PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 2.0.2
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v2.0.2
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 2.0.2, at lib/readme-parser.php

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