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

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