After some discussion around what should be included in a first version of this feature I have published for review a draft vpatch of the mp-wp patch viewer plugin. There are several things to call out in the code so let's go through this line-by-line:
#S1-L9: I think this file should be renamed to something like mp-wp-content-processing.php but left it as is for now to provide a meaningful diff for review (rather than the wholesale file deleted / file added you'd get otherwise).
#S1-L22: This is not so much to claim authorship but rather to claim ownership1 and to make it clear that the world starts here.
#S1-L47: These constants were moved inside the class. I couldn't see any reason for them to remain global. The WP_FOOTNOTES_VERSION
was removed because its only purpose was to help manage the options state between db and file.
#S1-L63: For obvious reasons, I can't render the codeblock delimiters inside a codeblock delimited by the codeblock delimiters2—which, in this patch, I have set to [[
and /]]
. I grepped through the trb genesis and found no matches, and in all 162k! lines of the mp-wp genesis3 there were only three instances of [[
and six of /]]
. It appears to be safe for bash as well, at least based on what I could find in the wild. I personally like these delimiters because they are succinct and follow closely the established pattern of (( ))
for footnotes.
#S1-L102: This had to come before the wpautop
filter which is responsible for inserting <p>
and <br />
tags all over the place. Thankfully wpautop
ignores content within <pre>
tags so that its ill effects can be avoided by processing the codeblocks before it has a chance to run4. The priority for footnotes processing was left unchanged.
Known limitations: Some stress testing that I did locally revealed an upper limit for a single snippet of approximately 5k lines of code. Adding multiple ~5k snippets to a single article topped out around roughly ~20k lines (4 snippets). Testing with ~525 line snippets failed somewhere between 55-60 snippets on the page (so getting really close to 30k lines there before bumping into the 128MB default memory limit for a PHP script). I have not yet tried these tests with an increased memory limit since the results seemed more than sufficient for how it would be used.
That covers the bulk of it, the rest should be self-explanatory. In terms of overall design, I did think of combining the footnotes and codeblock processing passes into a single filter but we would have to get around the issue of wpautop
(codeblocks coming before and footnotes after). However, I couldn't see a performance justification for this since the extraction -> formatting -> reinsertion loops would still need to happen in separate passes.
The patch and signature for anyone who would like to take it for a spin in the own environment:
mp-wp_add-embedded-vpatch-formatting.vpatch
mp-wp_add-embedded-vpatch-formatting.vpatch.billymg.sig
1 | diff -uNr a/mp-wp/manifest b/mp-wp/manifest |
2 | |
3 | |
4 | |
5 | 569483 mp-wp_remove-tinymce-and-other-crud billymg Remove tinymce, most of the importers, the self-update feature, and the google gears and press-this plugins |
6 | 602064 mp-wp_apply-htmlspecialchars-to-post-edit-content billymg Run post content through htmlspecialchars() before loading into the post edit UI |
7 | 605926 mp-wp_comments_filtering diana_coman Recent comments widget should show only people's comments (no track/pingbacks); theme default changed to show trackbacks/pingbacks as last/at the bottom in an article's comments list. |
8 | +614805 mp-wp_add-embedded-vpatch-formatting billymg Add the ability to embed vpatches within article content. Embedded vpatch blocks will be formatted with diff syntax highlighting and anchored line numbers |
9 | diff -uNr a/mp-wp/wp-content/plugins/footnotes.php b/mp-wp/wp-content/plugins/footnotes.php |
10 | |
11 | |
12 | |
13 | <?php |
14 | /* |
15 | -Plugin Name: WP-Footnotes |
16 | -Plugin URI: http://www.elvery.net/drzax/more-things/wordpress-footnotes-plugin/ |
17 | -Version: 4.2 |
18 | -Description: Allows a user to easily add footnotes to a post. |
19 | -Author: Simon Elvery |
20 | -Author URI: http://www.elvery.net/drzax/ |
21 | +Plugin Name: MP-WP-Content-Processing |
22 | +Plugin URI: http://billymg.com/category/mp-wp/ |
23 | +Description: Allows for the custom processing of article content. Currently supports footnotes and embedded vpatch snippets. |
24 | +Author: billymg |
25 | +Author URI: http://billymg.com |
26 | */ |
27 | |
28 | -/* |
29 | - * This file is part of WP-Footnotes a plugin for Word Press |
30 | - * Copyright (C) 2007 Simon Elvery |
31 | - * |
32 | - * This program is free software; you can redistribute it and/or |
33 | - * modify it under the terms of the GNU General Public License |
34 | - * as published by the Free Software Foundation; either version 2 |
35 | - * of the License, or (at your option) any later version. |
36 | - * |
37 | - * This program is distributed in the hope that it will be useful, |
38 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
39 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
40 | - * GNU General Public License for more details. |
41 | - * |
42 | - * You should have received a copy of the GNU General Public License |
43 | - * along with this program; if not, write to the Free Software |
44 | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
45 | - */ |
46 | - |
47 | -// Some important constants |
48 | -define('WP_FOOTNOTES_OPEN', " (("); //You can change this if you really have to, but I wouldn't recommend it. |
49 | -define('WP_FOOTNOTES_CLOSE', "))"); //Same with this one. |
50 | -define('WP_FOOTNOTES_VERSION', '4.2'); |
51 | - |
52 | // Instantiate the class |
53 | -$swas_wp_footnotes = new swas_wp_footnotes(); |
54 | +$mp_wp_content_processing = new mp_wp_content_processing(); |
55 | |
56 | // Encapsulate in a class |
57 | -class swas_wp_footnotes { |
58 | - var $current_options; |
59 | - var $default_options; |
60 | +class mp_wp_content_processing { |
61 | + const MP_WP_FOOTNOTES_OPEN = " (("; |
62 | + const MP_WP_FOOTNOTES_CLOSE = "))"; |
63 | + const MP_WP_CODEBLOCK_OPEN = "[["; |
64 | + const MP_WP_CODEBLOCK_CLOSE = "/]]"; |
65 | + |
66 | + var $options; |
67 | |
68 | /** |
69 | * Constructor. |
70 | */ |
71 | - function swas_wp_footnotes() { |
72 | + function mp_wp_content_processing() { |
73 | // Define the implemented option styles |
74 | $this->styles = array( |
75 | 'decimal' => '1,2...10', |
76 | |
77 | ); |
78 | |
79 | // Define default options |
80 | - $this->default_options = array('superscript'=>true, |
81 | + $this->options = array('superscript'=>true, |
82 | 'pre_backlink'=>' [', |
83 | 'backlink'=>'↩', |
84 | 'post_backlink'=>']', |
85 | 'pre_identifier'=>'', |
86 | - 'list_style_type'=>'decimal', |
87 | + 'list_style_type'=>'lower-roman', |
88 | 'list_style_symbol'=>'†', |
89 | 'post_identifier'=>'', |
90 | 'pre_footnotes'=>'', |
91 | 'post_footnotes'=>'', |
92 | - 'style_rules'=>'ol.footnotes{font-size:0.8em; color:#666666;}', |
93 | 'no_display_home'=>false, |
94 | 'no_display_archive'=>false, |
95 | 'no_display_date'=>false, |
96 | |
97 | 'no_display_search'=>false, |
98 | 'no_display_feed'=>false, |
99 | 'combine_identical_notes'=>false, |
100 | - 'priority'=>11, |
101 | - 'version'=>WP_FOOTNOTES_VERSION); |
102 | + 'codeblocks_priority'=>9, // highest value that comes before wpautop filter |
103 | + 'footnotes_priority'=>11); |
104 | |
105 | - // Get the current settings or setup some defaults if needed |
106 | - if (!$this->current_options = get_option('swas_footnote_options')){ |
107 | - $this->current_options = $this->default_options; |
108 | - update_option('swas_footnote_options', $this->current_options); |
109 | - } else { |
110 | - // Set any unset options |
111 | - if ($this->current_options['version'] != WP_FOOTNOTES_VERSION) { |
112 | - foreach ($this->default_options as $key => $value) { |
113 | - if (!isset($this->current_options[$key])) { |
114 | - $this->current_options[$key] = $value; |
115 | - } |
116 | - } |
117 | - $this->current_options['version'] = WP_FOOTNOTES_VERSION; |
118 | - update_option('swas_footnote_options', $this->current_options); |
119 | - } |
120 | + // Hook me up |
121 | + add_action('the_content', array($this, 'process_codeblocks'), $this->options['codeblocks_priority']); |
122 | + add_action('the_content', array($this, 'process_footnotes'), $this->options['footnotes_priority']); |
123 | + add_action('wp_head', array($this, 'insert_styles')); |
124 | + } |
125 | + |
126 | + /** |
127 | + * Searches the text and apply markup to codeblocks. |
128 | + * Adds line number links and diff syntax highlighting. |
129 | + * @param $data string The content of the post. |
130 | + * @return string The new content with formatted codeblocks. |
131 | + */ |
132 | + function process_codeblocks($data) { |
133 | + global $post; |
134 | + |
135 | + // Regex extraction of all codeblocks (or return if there are none) |
136 | + if ( |
137 | + !preg_match_all( |
138 | + "/(".preg_quote(self::MP_WP_CODEBLOCK_OPEN).")(.*)(".preg_quote(self::MP_WP_CODEBLOCK_CLOSE, '/').")/Us", |
139 | + $data, |
140 | + $codeblocks, |
141 | + PREG_SET_ORDER |
142 | + ) |
143 | + ) { |
144 | + return $data; |
145 | } |
146 | |
147 | -/* |
148 | - if (!empty($_POST['save_options'])){ |
149 | - $footnotes_options['superscript'] = (array_key_exists('superscript', $_POST)) ? true : false; |
150 | - $footnotes_options['pre_backlink'] = $_POST['pre_backlink']; |
151 | - $footnotes_options['backlink'] = $_POST['backlink']; |
152 | - $footnotes_options['post_backlink'] = $_POST['post_backlink']; |
153 | - $footnotes_options['pre_identifier'] = $_POST['pre_identifier']; |
154 | - $footnotes_options['list_style_type'] = $_POST['list_style_type']; |
155 | - $footnotes_options['post_identifier'] = $_POST['post_identifier']; |
156 | - $footnotes_options['list_style_symbol'] = $_POST['list_style_symbol']; |
157 | - $footnotes_options['pre_footnotes'] = stripslashes($_POST['pre_footnotes']); |
158 | - $footnotes_options['post_footnotes'] = stripslashes($_POST['post_footnotes']); |
159 | - $footnotes_options['style_rules'] = stripslashes($_POST['style_rules']); |
160 | - $footnotes_options['no_display_home'] = (array_key_exists('no_display_home', $_POST)) ? true : false; |
161 | - $footnotes_options['no_display_archive'] = (array_key_exists('no_display_archive', $_POST)) ? true : false; |
162 | - $footnotes_options['no_display_date'] = (array_key_exists('no_display_date', $_POST)) ? true : false; |
163 | - $footnotes_options['no_display_category'] = (array_key_exists('no_display_category', $_POST)) ? true : false; |
164 | - $footnotes_options['no_display_search'] = (array_key_exists('no_display_search', $_POST)) ? true : false; |
165 | - $footnotes_options['no_display_feed'] = (array_key_exists('no_display_feed', $_POST)) ? true : false; |
166 | - $footnotes_options['combine_identical_notes'] = (array_key_exists('combine_identical_notes', $_POST)) ? true : false; |
167 | - $footnotes_options['priority'] = $_POST['priority']; |
168 | - update_option('swas_footnote_options', $footnotes_options); |
169 | - }elseif(!empty($_POST['reset_options'])){ |
170 | - update_option('swas_footnote_options', ''); |
171 | - update_option('swas_footnote_options', $this->default_options); |
172 | + for ($i = 0; $i < count($codeblocks); $i++) { |
173 | + $codeblocks[$i]['snippet'] = $this->format_snippet($codeblocks[$i][2], $i+1); |
174 | } |
175 | -*/ |
176 | |
177 | - // Hook me up |
178 | - add_action('the_content', array($this, 'process'), $this->current_options['priority']); |
179 | - add_action('admin_menu', array($this, 'add_options_page')); // Insert the Admin panel. |
180 | - add_action('wp_head', array($this, 'insert_styles')); |
181 | + foreach ($codeblocks as $key => $value) { |
182 | + $data = substr_replace($data, $value['snippet'], strpos($data,$value[0]),strlen($value[0])); |
183 | + } |
184 | + |
185 | + return $data; |
186 | + } |
187 | + |
188 | + function format_snippet($snippet, $snippet_number) { |
189 | + $formatted_snippet = htmlspecialchars($snippet); |
190 | + $code_lines = explode("\r\n", $formatted_snippet); |
191 | + |
192 | + foreach ($code_lines as $idx => $line) { |
193 | + $line_number = sprintf('S%d-L%d', $snippet_number, $idx+1); |
194 | + $line_link = sprintf('<a href="#%s" name="%s">%d</a>', $line_number, $line_number, $idx+1); |
195 | + $line_open = sprintf('<tr><td class="line-number-column">%s</td><td class="content-column">', $line_link); |
196 | + $line_close = '</td></tr>'; |
197 | + |
198 | + if (substr($line, 0, 5) == 'diff ') { |
199 | + $code_lines[$idx] = sprintf('%s<span class="line-filename">%s</span>%s', $line_open, $line, $line_close); |
200 | + } elseif (substr($line, 0, 4) == '--- ' || substr($line, 0, 4) == '+++ ' || substr($line, 0, 3) == '@@ ') { |
201 | + $code_lines[$idx] = sprintf('%s<span class="line-meta">%s</span>%s', $line_open, $line, $line_close); |
202 | + } elseif (substr($line, 0, 1) == '-') { |
203 | + $code_lines[$idx] = sprintf('%s<span class="line-removed">%s</span>%s', $line_open, $line, $line_close); |
204 | + } elseif (substr($line, 0, 1) == '+') { |
205 | + $code_lines[$idx] = sprintf('%s<span class="line-added">%s</span>%s', $line_open, $line, $line_close); |
206 | + } else { |
207 | + $code_lines[$idx] = sprintf('%s<span class="line-default">%s</span>%s', $line_open, $line, $line_close); |
208 | + } |
209 | + } |
210 | + |
211 | + $formatted_snippet = implode("\n", $code_lines); |
212 | + |
213 | + $formatted_snippet = sprintf( |
214 | + '%s%s%s', |
215 | + '<pre class="mp-wp-codeblock"><table cellpadding="0" cellspacing="0"><tbody>', |
216 | + $formatted_snippet, |
217 | + '</tbody></table></pre>' |
218 | + ); |
219 | + |
220 | + return $formatted_snippet; |
221 | } |
222 | |
223 | /** |
224 | |
225 | * @param $data string The content of the post. |
226 | * @return string The new content with footnotes generated. |
227 | */ |
228 | - function process($data) { |
229 | + function process_footnotes($data) { |
230 | global $post; |
231 | |
232 | // Check for and setup the starting number |
233 | $start_number = (preg_match("|<!\-\-startnum=(\d+)\-\->|",$data,$start_number_array)==1) ? $start_number_array[1] : 1; |
234 | |
235 | - // Regex extraction of all footnotes (or return if there are none) |
236 | - if (!preg_match_all("/(".preg_quote(WP_FOOTNOTES_OPEN)."|<footnote>)(.*)(".preg_quote(WP_FOOTNOTES_CLOSE)."|<\/footnote>)/Us", $data, $identifiers, PREG_SET_ORDER)) { |
237 | + // Remove codeblocks from content to be parsed for footnotes |
238 | + $data_sans_codeblocks = preg_replace("/(<pre class=\"mp-wp-codeblock\">)(.*)(<\/pre>)/Us", '', $data); |
239 | + |
240 | + // Regex extraction of all footnotes from non-codeblock content (or return if there are none) |
241 | + if (!preg_match_all("/(".preg_quote(self::MP_WP_FOOTNOTES_OPEN)."|<footnote>)(.*)(".preg_quote(self::MP_WP_FOOTNOTES_CLOSE)."|<\/footnote>)/Us", $data_sans_codeblocks, $identifiers, PREG_SET_ORDER)) { |
242 | return $data; |
243 | } |
244 | |
245 | // Check whether we are displaying them or not |
246 | $display = true; |
247 | - if ($this->current_options['no_display_home'] && is_home()) $display = false; |
248 | - if ($this->current_options['no_display_archive'] && is_archive()) $display = false; |
249 | - if ($this->current_options['no_display_date'] && is_date()) $display = false; |
250 | - if ($this->current_options['no_display_category'] && is_category()) $display = false; |
251 | - if ($this->current_options['no_display_search'] && is_search()) $display = false; |
252 | - if ($this->current_options['no_display_feed'] && is_feed()) $display = false; |
253 | + if ($this->options['no_display_home'] && is_home()) $display = false; |
254 | + if ($this->options['no_display_archive'] && is_archive()) $display = false; |
255 | + if ($this->options['no_display_date'] && is_date()) $display = false; |
256 | + if ($this->options['no_display_category'] && is_category()) $display = false; |
257 | + if ($this->options['no_display_search'] && is_search()) $display = false; |
258 | + if ($this->options['no_display_feed'] && is_feed()) $display = false; |
259 | |
260 | $footnotes = array(); |
261 | |
262 | |
263 | if ( array_key_exists(get_post_meta($post->ID, 'footnote_style', true), $this->styles) ) { |
264 | $style = get_post_meta($post->ID, 'footnote_style', true); |
265 | } else { |
266 | - $style = $this->current_options['list_style_type']; |
267 | + $style = $this->options['list_style_type']; |
268 | } |
269 | |
270 | // Create 'em |
271 | |
272 | |
273 | |
274 | // if we're combining identical notes check if we've already got one like this & record keys |
275 | - if ($this->current_options['combine_identical_notes']){ |
276 | + if ($this->options['combine_identical_notes']){ |
277 | for ($j=0; $j<count($footnotes); $j++){ |
278 | if ($footnotes[$j]['text'] == $identifiers[$i]['text']){ |
279 | $identifiers[$i]['use_footnote'] = $j; |
280 | |
281 | $id_id = "identifier_".$key."_".$post->ID; |
282 | $id_num = ($style == 'decimal') ? $value['use_footnote']+$start_number : $this->convert_num($value['use_footnote']+$start_number, $style, count($footnotes)); |
283 | $id_href = ( ($use_full_link) ? get_permalink($post->ID) : '' ) . "#footnote_".$value['use_footnote']."_".$post->ID; |
284 | - |
285 | -// $id_title = str_replace('"', """, htmlentities(strip_tags($value['text']), ENT_QUOTES, 'UTF-8')); |
286 | - |
287 | $id_title = str_replace('"', '`', strip_tags($value['text'])); |
288 | - $id_replace = $this->current_options['pre_identifier'].'<a href="'.$id_href.'" id="'.$id_id.'" class="footnote-link footnote-identifier-link" title="'.$id_title.'">'.$id_num.'</a>'.$this->current_options['post_identifier']; |
289 | - if ($this->current_options['superscript']) $id_replace = '<sup>'.$id_replace.'</sup>'; |
290 | + $id_replace = $this->options['pre_identifier'].'<a href="'.$id_href.'" id="'.$id_id.'" class="footnote-link footnote-identifier-link" title="'.$id_title.'">'.$id_num.'</a>'.$this->options['post_identifier']; |
291 | + if ($this->options['superscript']) $id_replace = '<sup>'.$id_replace.'</sup>'; |
292 | if ($display) $data = substr_replace($data, $id_replace, strpos($data,$value[0]),strlen($value[0])); |
293 | else $data = substr_replace($data, '', strpos($data,$value[0]),strlen($value[0])); |
294 | } |
295 | |
296 | // Display footnotes |
297 | if ($display) { |
298 | $start = ($start_number != 1) ? 'start="'.$start_number.'" ' : ''; |
299 | - $data = $data.$this->current_options['pre_footnotes']; |
300 | + $data = $data.$this->options['pre_footnotes']; |
301 | |
302 | $data = $data . '<ol '.$start.'class="footnotes">'; |
303 | foreach ($footnotes as $key => $value) { |
304 | $data = $data.'<li id="footnote_'.$key.'_'.$post->ID.'" class="footnote"'; |
305 | if ($style == 'symbol') { |
306 | $data = $data . ' style="list-style-type:none;"'; |
307 | - } elseif($style != $this->current_options['list_style_type']) { |
308 | + } elseif($style != $this->options['list_style_type']) { |
309 | $data = $data . ' style="list-style-type:' . $style . ';"'; |
310 | } |
311 | $data = $data . '>'; |
312 | |
313 | $data = $data.$value['text']; |
314 | if (!is_feed()){ |
315 | foreach($value['identifiers'] as $identifier){ |
316 | - $data = $data.$this->current_options['pre_backlink'].'<a href="'.( ($use_full_link) ? get_permalink($post->ID) : '' ).'#identifier_'.$identifier.'_'.$post->ID.'" class="footnote-link footnote-back-link">'.$this->current_options['backlink'].'</a>'.$this->current_options['post_backlink']; |
317 | + $data = $data.$this->options['pre_backlink'].'<a href="'.( ($use_full_link) ? get_permalink($post->ID) : '' ).'#identifier_'.$identifier.'_'.$post->ID.'" class="footnote-link footnote-back-link">'.$this->options['backlink'].'</a>'.$this->options['post_backlink']; |
318 | } |
319 | } |
320 | $data = $data . '</li>'; |
321 | } |
322 | - $data = $data . '</ol>' . $this->current_options['post_footnotes']; |
323 | + $data = $data . '</ol>' . $this->options['post_footnotes']; |
324 | } |
325 | return $data; |
326 | } |
327 | |
328 | - /** |
329 | - * Really insert the options page. |
330 | - */ |
331 | - function footnotes_options_page() { |
332 | - $this->current_options = get_option('swas_footnote_options'); |
333 | - foreach ($this->current_options as $key=>$setting) { |
334 | - $new_setting[$key] = htmlentities($setting); |
335 | - } |
336 | - $this->current_options = $new_setting; |
337 | - unset($new_setting); |
338 | - include (dirname(__FILE__) . '/options.php'); |
339 | - } |
340 | - |
341 | - /** |
342 | - * Insert the options page into the admin area. |
343 | - */ |
344 | - function add_options_page() { |
345 | - // Add a new menu under Options: |
346 | - add_options_page('Footnotes', 'Footnotes', 8, __FILE__, array($this, 'footnotes_options_page')); |
347 | - } |
348 | - |
349 | - function upgrade_post($data){ |
350 | - $data = str_replace('<footnote>',WP_FOOTNOTES_OPEN,$data); |
351 | - $data = str_replace('</footnote>',WP_FOOTNOTES_CLOSE,$data); |
352 | - return $data; |
353 | - } |
354 | - |
355 | - function insert_styles(){ |
356 | + function insert_styles() { |
357 | ?> |
358 | <style type="text/css"> |
359 | - <?php if ($this->current_options['list_style_type'] != 'symbol'): ?> |
360 | - ol.footnotes li {list-style-type:<?php echo $this->current_options['list_style_type']; ?>;} |
361 | + ol.footnotes { font-size: 0.8em; color: #666666; } |
362 | + pre.mp-wp-codeblock { background: none; color: #333; border: 1px solid #ddd; padding: 0; } |
363 | + td.line-number-column { background: #f5f6f7; text-align: right; } |
364 | + td.line-number-column a { color: #555; padding: 0 5px; } |
365 | + td.content-column { padding-left: 10px; } |
366 | + span.line-filename { font-weight: bold; } |
367 | + span.line-meta { color: #999; } |
368 | + span.line-added { color: green; } |
369 | + span.line-removed { color:red; } |
370 | + |
371 | + <?php if ($this->options['list_style_type'] != 'symbol'): ?> |
372 | + ol.footnotes li { list-style-type: <?php echo $this->options['list_style_type']; ?>; } |
373 | <?php endif; ?> |
374 | - <?php echo $this->current_options['style_rules'];?> |
375 | + |
376 | </style> |
377 | <?php |
378 | } |
379 | |
380 | |
381 | - function convert_num ($num, $style, $total){ |
382 | + function convert_num ($num, $style, $total) { |
383 | switch ($style) { |
384 | case 'decimal-leading-zero' : |
385 | $width = max(2, strlen($total)); |
386 | |
387 | case 'symbol' : |
388 | $sym = ''; |
389 | for ($i = 0; $i<$num; $i++) { |
390 | - $sym .= $this->current_options['list_style_symbol']; |
391 | + $sym .= $this->options['list_style_symbol']; |
392 | } |
393 | return $sym; |
394 | } |
395 | |
396 | * @param string $case Upper or lower case. |
397 | * @return string The roman numeral |
398 | */ |
399 | - function roman($num, $case= 'upper'){ |
400 | + function roman($num, $case= 'upper') { |
401 | $num = (int) $num; |
402 | $conversion = array('M'=>1000, 'CM'=>900, 'D'=>500, 'CD'=>400, 'C'=>100, 'XC'=>90, 'L'=>50, 'XL'=>40, 'X'=>10, 'IX'=>9, 'V'=>5, 'IV'=>4, 'I'=>1); |
403 | $roman = ''; |
404 | |
405 | return ($case == 'lower') ? strtolower($roman) : $roman; |
406 | } |
407 | |
408 | - function alpha($num, $case='upper'){ |
409 | + function alpha($num, $case='upper') { |
410 | $j = 1; |
411 | for ($i = 'A'; $i <= 'ZZ'; $i++){ |
412 | if ($j == $num){ |
413 |
- So that if something goes wrong I'm on the line to fix it, lest my reputation suffer the negratings. [↩]
- I tried escaping with [ and ] (as I did for this article) but because the code snippet itself is sent through
htmlspecialchars
that wouldn't work either, so I decided this to be an unwinnable battle and moved on. If anyone would like to point out what I'm missing I'd be happy to update this article. [↩] - 23k of which have since been snipped. [↩]
wpautop
can also be disabled at the theme level via the addition ofremove_filter( 'the_content', 'wpautop' );
in the theme's function.php file. [↩]