MP-WP User Survey

April 16th, 2020

After realizing that I may be using mp-wp differently from how others use the software I decided that in order to serve this market I would first have to pay off some research debt1. There are two surveys: one for mp-wp users, and one for users of any other blog software.

The survey is meant to be completed in the comments below this article. Feedback on the survey itself is also welcome.

Survey for mp-wp users

  1. Do you use the mp-wp admin interface (web UI)? If so, what actions do you perform via this interface?
  2. Have you modified any of the "settings" in the mp-wp mysql database (also found in the web UI under "Settings" in the left-hand nav)? If so, which ones? When/how often? Via mysql CLI or via the web UI?
  3. Do you run any custom plugins on top of what's included in the trunk of mp-wp?
  4. Have you modified an included theme or created a new theme that you actively use on one of your sites/properties? If so, why / what did you modify?
  5. Do you run mp-wp with any other code modifications (outside of themes and plugins)? If so, why / what did you modify?
  6. Describe your typical workflow when publishing an article to your blog (including any pre/post steps, and any other tools/scripts/etc. outside of mp-wp). Include details about working with images, code samples, etc.
  7. What do you find most tedious or annoying about using mp-wp? (can be related to any aspect of using mp-wp)
  8. What do you like most about using mp-wp?

Survey for users of other blog software

  1. What blog software do you currently use?
  2. How is the blog configured (any knobs to turn, theming, plugins/extensions, etc.)?
  3. Describe your workflow when publishing an article (including any pre/post steps, and any other tools/scripts/etc. outside of the blog software itself). Include details about working with images, code samples, etc.
  4. What do you find most tedious or annoying about using your current blog software?
  5. What do you like most about using your current blog software?
  6. Have you considered switching to mp-wp? What (if anything) would it take for you to switch?

Bonus question

  • What is your primary reason for maintaining a blog?

That's it. Each respondent who completes2 the survey will also receive 0.005 BTC from me via deedbot3.

  1. It also led me to pay off some personal debt, including becoming more literate with command line tools such as sed and awk, so win-win really. []
  2. Completeness here is subjective and I reserve the right to not honor any response that I judge to be intentionally half-assed or made in bad faith. []
  3. Limit 15 respondents. Good for two weeks from the publish date of this article. After taking the survey, send me an invoice via deedbot with a link to your comment as the memo. We needn't have interacted before but I have to have at least seen you talking in one of the channels at some point, and not in a way that got you kicked out. []

Updated Vpatch: Code Embed Plugin for MP-WP

April 4th, 2020

Since there was already a lengthy discussion on the first draft of this patch let's start by looking at a diff between this version and the original (annotated with footnotes this time!):

1 --- old/footnotes.php
2 +++ current/footnotes.php
3 @@ -14,8 +14,8 @@
4 class mp_wp_content_processing {
5 const MP_WP_FOOTNOTES_OPEN = " ((";
6 const MP_WP_FOOTNOTES_CLOSE = "))";
7 - const MP_WP_CODEBLOCK_OPEN = "[[";
8 - const MP_WP_CODEBLOCK_CLOSE = "/]]";
9 + const MP_WP_CODEBLOCK_OPEN = "\[[0-9]\[";1
10 + const MP_WP_CODEBLOCK_CLOSE = "]]";
11
12 var $options;
13
14 @@ -40,7 +40,7 @@
15 'backlink'=>'↩',
16 'post_backlink'=>']',
17 'pre_identifier'=>'',
18 - 'list_style_type'=>'lower-roman',
19 + 'list_style_type'=>'decimal',2
20 'list_style_symbol'=>'†',
21 'post_identifier'=>'',
22 'pre_footnotes'=>'',
23 @@ -73,7 +73,7 @@
24 // Regex extraction of all codeblocks (or return if there are none)
25 if (
26 !preg_match_all(
27 - "/(".preg_quote(self::MP_WP_CODEBLOCK_OPEN).")(.*)(".preg_quote(self::MP_WP_CODEBLOCK_CLOSE, '/').")/Us",
28 + "/(".self::MP_WP_CODEBLOCK_OPEN.")(.*)(".preg_quote(self::MP_WP_CODEBLOCK_CLOSE, '/').")/Us",3
29 $data,
30 $codeblocks,
31 PREG_SET_ORDER
32 @@ -83,7 +83,7 @@
33 }
34
35 for ($i = 0; $i < count($codeblocks); $i++) {
36 - $codeblocks[$i]['snippet'] = $this->format_snippet($codeblocks[$i][2], $i+1);
37 + $codeblocks[$i]['snippet'] = $this->format_snippet($codeblocks[$i][2], substr($codeblocks[$i][1], 1, 1), $i+1);4
38 }
39
40 foreach ($codeblocks as $key => $value) {
41 @@ -93,41 +93,59 @@
42 return $data;
43 }
44
45 - function format_snippet($snippet, $snippet_number) {
46 - $formatted_snippet = htmlspecialchars($snippet);5
47 - $code_lines = explode("\r\n", $formatted_snippet);
48 -
49 + function format_snippet($snippet, $syntax_index, $snippet_number) {
50 + $highlighting_functions = array(
51 + 'highlight_as_plain_text',
52 + 'highlight_as_diff'
53 + );
54 +
55 + if (is_null($highlighting_functions[$syntax_index])) {
56 + $syntax_index = 0;
57 + }
58 +
59 + $code_lines = explode("\r\n", $snippet);
60 +
61 foreach ($code_lines as $idx => $line) {
62 $line_number = sprintf('S%d-L%d', $snippet_number, $idx+1);
63 $line_link = sprintf('<a href="#%s" name="%s">%d</a>', $line_number, $line_number, $idx+1);
64 $line_open = sprintf('<tr><td class="line-number-column">%s</td><td class="content-column">', $line_link);
65 $line_close = '</td></tr>';
66 -
67 - if (substr($line, 0, 5) == 'diff ') {
68 - $code_lines[$idx] = sprintf('%s<span class="line-filename">%s</span>%s', $line_open, $line, $line_close);
69 - } elseif (substr($line, 0, 4) == '--- ' || substr($line, 0, 4) == '+++ ' || substr($line, 0, 3) == '@@ ') {
70 - $code_lines[$idx] = sprintf('%s<span class="line-meta">%s</span>%s', $line_open, $line, $line_close);
71 - } elseif (substr($line, 0, 1) == '-') {
72 - $code_lines[$idx] = sprintf('%s<span class="line-removed">%s</span>%s', $line_open, $line, $line_close);
73 - } elseif (substr($line, 0, 1) == '+') {
74 - $code_lines[$idx] = sprintf('%s<span class="line-added">%s</span>%s', $line_open, $line, $line_close);
75 - } else {
76 - $code_lines[$idx] = sprintf('%s<span class="line-default">%s</span>%s', $line_open, $line, $line_close);
77 - }
78 +
79 + $code_lines[$idx] = $this->$highlighting_functions[$syntax_index]($line_open, $line, $line_close);
80 }
81
82 $formatted_snippet = implode("\n", $code_lines);
83
84 $formatted_snippet = sprintf(
85 '%s%s%s',
86 - '<pre class="mp-wp-codeblock"><table cellpadding="0" cellspacing="0"><tbody>',
87 + '<div class="mp-wp-codeblock"><table cellpadding="0" cellspacing="0"><tbody>',6
88 $formatted_snippet,
89 - '</tbody></table></pre>'
90 + '</tbody></table></div>'
91 );
92
93 return $formatted_snippet;
94 }
95
96 + function highlight_as_plain_text($line_open, $line, $line_close) {
97 + return sprintf('%s<span class="line-default">%s</span>%s', $line_open, $line, $line_close);
98 + }
99 +
100 + function highlight_as_diff($line_open, $line, $line_close) {
101 + if (substr($line, 0, 5) == 'diff ') {
102 + $highlighted_line = sprintf('%s<span class="line-filename">%s</span>%s', $line_open, $line, $line_close);
103 + } elseif (substr($line, 0, 4) == '--- ' || substr($line, 0, 4) == '+++ ' || substr($line, 0, 3) == '@@ ') {
104 + $highlighted_line = sprintf('%s<span class="line-meta">%s</span>%s', $line_open, $line, $line_close);
105 + } elseif (substr($line, 0, 1) == '-') {
106 + $highlighted_line = sprintf('%s<span class="line-removed">%s</span>%s', $line_open, $line, $line_close);
107 + } elseif (substr($line, 0, 1) == '+') {
108 + $highlighted_line = sprintf('%s<span class="line-added">%s</span>%s', $line_open, $line, $line_close);
109 + } else {
110 + $highlighted_line = sprintf('%s<span class="line-default">%s</span>%s', $line_open, $line, $line_close);
111 + }
112 +
113 + return $highlighted_line;
114 + }
115 +
116 /**
117 * Searches the text and extracts footnotes.
118 * Adds the identifier links and creats footnotes list.
119 @@ -140,11 +158,8 @@
120 // Check for and setup the starting number
121 $start_number = (preg_match("|<!\-\-startnum=(\d+)\-\->|",$data,$start_number_array)==1) ? $start_number_array[1] : 1;
122
123 - // Remove codeblocks from content to be parsed for footnotes
124 - $data_sans_codeblocks = preg_replace("/(<pre class=\"mp-wp-codeblock\">)(.*)(<\/pre>)/Us", '', $data);
125 -
126 - // Regex extraction of all footnotes from non-codeblock content (or return if there are none)
127 - 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)) {
128 + // Regex extraction of all footnotes (or return if there are none)
129 + if (!preg_match_all("/(".preg_quote(self::MP_WP_FOOTNOTES_OPEN)."|<footnote>)(.*)(".preg_quote(self::MP_WP_FOOTNOTES_CLOSE)."|<\/footnote>)/Us", $data, $identifiers, PREG_SET_ORDER)) {
130 return $data;
131 }
132
133 @@ -250,12 +265,31 @@
134 ?>
135 <style type="text/css">
136 ol.footnotes { font-size: 0.8em; color: #666666; }
137 - pre.mp-wp-codeblock { background: none; color: #333; border: 1px solid #ddd; padding: 0; }
138 - td.line-number-column { background: #f5f6f7; text-align: right; }
139 + a.footnote-link,
140 + td.line-number-column {7
141 + -moz-user-select: none;
142 + -webkit-user-select: none;
143 + user-select: none;
144 + }
145 + div.mp-wp-codeblock {
146 + background: none;
147 + font-family: monospace;
148 + color: #333;
149 + border: 1px solid #ddd;
150 + padding: 0;
151 + overflow: auto;
152 + }
153 + td.line-number-column { background: #f5f6f7; text-align: right; vertical-align: top; }
154 td.line-number-column a { color: #555; padding: 0 5px; }
155 - td.content-column { padding-left: 10px; }
156 + td.content-column {
157 + padding-left: 10px;
158 + white-space: pre-wrap;
159 + tab-size: 4;
160 + -moz-tab-size: 4;
161 + max-width: 670px; /* adjust as necessary to fit your blog's viewport */
162 + }
163 span.line-filename { font-weight: bold; }
164 - span.line-meta { color: #999; }
165 + span.line-meta { color: #999; word-wrap: break-word; }8
166 span.line-added { color: green; }
167 span.line-removed { color:red; }
168
169

I think I hit on all the grievances raised during the original draft review. Overall I'm happy with the outcome and happy that I got to learn something about how to use sed and regular expressions for quick file manipulation. Next up for me will be adding proper server-side selection to the main mp-wp tree, if no one else has gotten to it first.

The updated patch in its entirety, in the form of file links and an embed in this post, why not:

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 --- a/mp-wp/manifest 4ad5c0b7eda9c670f311a23da92114ab10bf30b9990b46882047aea3ab569395f643cb4aa6144ee0ba74b9821df1e69b26d299c1f0e9c11631a78c46f95913bd
3 +++ b/mp-wp/manifest e728e4d8266826d750b5ddfbb65612507575faeed2a38a52da45f8a4f9650b7a0793207e8542c1a4c38f5476f9ab10882179eeb99a2cdcba33e3ccbdcf103dcd
4 @@ -5,3 +5,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 +624366 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 --- a/mp-wp/wp-content/plugins/footnotes.php 8e2449d4ac26ea05f080cec9d025ef8a8585221ee30da439b37ff1578d084e1c63cbe3f89e3d6868c19d0fa9f73a9af99b444251e7a854f6c87e316628d94859
11 +++ b/mp-wp/wp-content/plugins/footnotes.php 7498014fc2fd9fd154959bb316c49e2ba419e330ac958a6a19671610e35fb1f787e0190dc618ff7bb2cb13996efd09e395ff79032f175f628a71f3c44f9b5910
12 @@ -1,49 +1,28 @@
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 = "\[[0-9]\[";
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 @@ -56,7 +35,7 @@
77 );
78
79 // Define default options
80 - $this->default_options = array('superscript'=>true,
81 + $this->options = array('superscript'=>true,
82 'pre_backlink'=>' [',
83 'backlink'=>'&#8617;',
84 'post_backlink'=>']',
85 @@ -66,7 +45,6 @@
86 'post_identifier'=>'',
87 'pre_footnotes'=>'',
88 'post_footnotes'=>'',
89 - 'style_rules'=>'ol.footnotes{font-size:0.8em; color:#666666;}',
90 'no_display_home'=>false,
91 'no_display_archive'=>false,
92 'no_display_date'=>false,
93 @@ -74,58 +52,98 @@
94 'no_display_search'=>false,
95 'no_display_feed'=>false,
96 'combine_identical_notes'=>false,
97 - 'priority'=>11,
98 - 'version'=>WP_FOOTNOTES_VERSION);
99 + 'codeblocks_priority'=>9, // highest value that comes before wpautop filter
100 + 'footnotes_priority'=>11);
101
102 - // Get the current settings or setup some defaults if needed
103 - if (!$this->current_options = get_option('swas_footnote_options')){
104 - $this->current_options = $this->default_options;
105 - update_option('swas_footnote_options', $this->current_options);
106 - } else {
107 - // Set any unset options
108 - if ($this->current_options['version'] != WP_FOOTNOTES_VERSION) {
109 - foreach ($this->default_options as $key => $value) {
110 - if (!isset($this->current_options[$key])) {
111 - $this->current_options[$key] = $value;
112 - }
113 - }
114 - $this->current_options['version'] = WP_FOOTNOTES_VERSION;
115 - update_option('swas_footnote_options', $this->current_options);
116 - }
117 + // Hook me up
118 + add_action('the_content', array($this, 'process_codeblocks'), $this->options['codeblocks_priority']);
119 + add_action('the_content', array($this, 'process_footnotes'), $this->options['footnotes_priority']);
120 + add_action('wp_head', array($this, 'insert_styles'));
121 + }
122 +
123 + /**
124 + * Searches the text and apply markup to codeblocks.
125 + * Adds line number links and diff syntax highlighting.
126 + * @param $data string The content of the post.
127 + * @return string The new content with formatted codeblocks.
128 + */
129 + function process_codeblocks($data) {
130 + global $post;
131 +
132 + // Regex extraction of all codeblocks (or return if there are none)
133 + if (
134 + !preg_match_all(
135 + "/(".self::MP_WP_CODEBLOCK_OPEN.")(.*)(".preg_quote(self::MP_WP_CODEBLOCK_CLOSE, '/').")/Us",
136 + $data,
137 + $codeblocks,
138 + PREG_SET_ORDER
139 + )
140 + ) {
141 + return $data;
142 }
143
144 -/*
145 - if (!empty($_POST['save_options'])){
146 - $footnotes_options['superscript'] = (array_key_exists('superscript', $_POST)) ? true : false;
147 - $footnotes_options['pre_backlink'] = $_POST['pre_backlink'];
148 - $footnotes_options['backlink'] = $_POST['backlink'];
149 - $footnotes_options['post_backlink'] = $_POST['post_backlink'];
150 - $footnotes_options['pre_identifier'] = $_POST['pre_identifier'];
151 - $footnotes_options['list_style_type'] = $_POST['list_style_type'];
152 - $footnotes_options['post_identifier'] = $_POST['post_identifier'];
153 - $footnotes_options['list_style_symbol'] = $_POST['list_style_symbol'];
154 - $footnotes_options['pre_footnotes'] = stripslashes($_POST['pre_footnotes']);
155 - $footnotes_options['post_footnotes'] = stripslashes($_POST['post_footnotes']);
156 - $footnotes_options['style_rules'] = stripslashes($_POST['style_rules']);
157 - $footnotes_options['no_display_home'] = (array_key_exists('no_display_home', $_POST)) ? true : false;
158 - $footnotes_options['no_display_archive'] = (array_key_exists('no_display_archive', $_POST)) ? true : false;
159 - $footnotes_options['no_display_date'] = (array_key_exists('no_display_date', $_POST)) ? true : false;
160 - $footnotes_options['no_display_category'] = (array_key_exists('no_display_category', $_POST)) ? true : false;
161 - $footnotes_options['no_display_search'] = (array_key_exists('no_display_search', $_POST)) ? true : false;
162 - $footnotes_options['no_display_feed'] = (array_key_exists('no_display_feed', $_POST)) ? true : false;
163 - $footnotes_options['combine_identical_notes'] = (array_key_exists('combine_identical_notes', $_POST)) ? true : false;
164 - $footnotes_options['priority'] = $_POST['priority'];
165 - update_option('swas_footnote_options', $footnotes_options);
166 - }elseif(!empty($_POST['reset_options'])){
167 - update_option('swas_footnote_options', '');
168 - update_option('swas_footnote_options', $this->default_options);
169 + for ($i = 0; $i < count($codeblocks); $i++) {
170 + $codeblocks[$i]['snippet'] = $this->format_snippet($codeblocks[$i][2], substr($codeblocks[$i][1], 1, 1), $i+1);
171 }
172 -*/
173
174 - // Hook me up
175 - add_action('the_content', array($this, 'process'), $this->current_options['priority']);
176 - add_action('admin_menu', array($this, 'add_options_page')); // Insert the Admin panel.
177 - add_action('wp_head', array($this, 'insert_styles'));
178 + foreach ($codeblocks as $key => $value) {
179 + $data = substr_replace($data, $value['snippet'], strpos($data,$value[0]),strlen($value[0]));
180 + }
181 +
182 + return $data;
183 + }
184 +
185 + function format_snippet($snippet, $syntax_index, $snippet_number) {
186 + $highlighting_functions = array(
187 + 'highlight_as_plain_text',
188 + 'highlight_as_diff'
189 + );
190 +
191 + if (is_null($highlighting_functions[$syntax_index])) {
192 + $syntax_index = 0;
193 + }
194 +
195 + $code_lines = explode("\r\n", $snippet);
196 +
197 + foreach ($code_lines as $idx => $line) {
198 + $line_number = sprintf('S%d-L%d', $snippet_number, $idx+1);
199 + $line_link = sprintf('<a href="#%s" name="%s">%d</a>', $line_number, $line_number, $idx+1);
200 + $line_open = sprintf('<tr><td class="line-number-column">%s</td><td class="content-column">', $line_link);
201 + $line_close = '</td></tr>';
202 +
203 + $code_lines[$idx] = $this->$highlighting_functions[$syntax_index]($line_open, $line, $line_close);
204 + }
205 +
206 + $formatted_snippet = implode("\n", $code_lines);
207 +
208 + $formatted_snippet = sprintf(
209 + '%s%s%s',
210 + '<div class="mp-wp-codeblock"><table cellpadding="0" cellspacing="0"><tbody>',
211 + $formatted_snippet,
212 + '</tbody></table></div>'
213 + );
214 +
215 + return $formatted_snippet;
216 + }
217 +
218 + function highlight_as_plain_text($line_open, $line, $line_close) {
219 + return sprintf('%s<span class="line-default">%s</span>%s', $line_open, $line, $line_close);
220 + }
221 +
222 + function highlight_as_diff($line_open, $line, $line_close) {
223 + if (substr($line, 0, 5) == 'diff ') {
224 + $highlighted_line = sprintf('%s<span class="line-filename">%s</span>%s', $line_open, $line, $line_close);
225 + } elseif (substr($line, 0, 4) == '--- ' || substr($line, 0, 4) == '+++ ' || substr($line, 0, 3) == '@@ ') {
226 + $highlighted_line = sprintf('%s<span class="line-meta">%s</span>%s', $line_open, $line, $line_close);
227 + } elseif (substr($line, 0, 1) == '-') {
228 + $highlighted_line = sprintf('%s<span class="line-removed">%s</span>%s', $line_open, $line, $line_close);
229 + } elseif (substr($line, 0, 1) == '+') {
230 + $highlighted_line = sprintf('%s<span class="line-added">%s</span>%s', $line_open, $line, $line_close);
231 + } else {
232 + $highlighted_line = sprintf('%s<span class="line-default">%s</span>%s', $line_open, $line, $line_close);
233 + }
234 +
235 + return $highlighted_line;
236 }
237
238 /**
239 @@ -134,25 +152,25 @@
240 * @param $data string The content of the post.
241 * @return string The new content with footnotes generated.
242 */
243 - function process($data) {
244 + function process_footnotes($data) {
245 global $post;
246
247 // Check for and setup the starting number
248 $start_number = (preg_match("|<!\-\-startnum=(\d+)\-\->|",$data,$start_number_array)==1) ? $start_number_array[1] : 1;
249
250 // Regex extraction of all footnotes (or return if there are none)
251 - if (!preg_match_all("/(".preg_quote(WP_FOOTNOTES_OPEN)."|<footnote>)(.*)(".preg_quote(WP_FOOTNOTES_CLOSE)."|<\/footnote>)/Us", $data, $identifiers, PREG_SET_ORDER)) {
252 + if (!preg_match_all("/(".preg_quote(self::MP_WP_FOOTNOTES_OPEN)."|<footnote>)(.*)(".preg_quote(self::MP_WP_FOOTNOTES_CLOSE)."|<\/footnote>)/Us", $data, $identifiers, PREG_SET_ORDER)) {
253 return $data;
254 }
255
256 // Check whether we are displaying them or not
257 $display = true;
258 - if ($this->current_options['no_display_home'] && is_home()) $display = false;
259 - if ($this->current_options['no_display_archive'] && is_archive()) $display = false;
260 - if ($this->current_options['no_display_date'] && is_date()) $display = false;
261 - if ($this->current_options['no_display_category'] && is_category()) $display = false;
262 - if ($this->current_options['no_display_search'] && is_search()) $display = false;
263 - if ($this->current_options['no_display_feed'] && is_feed()) $display = false;
264 + if ($this->options['no_display_home'] && is_home()) $display = false;
265 + if ($this->options['no_display_archive'] && is_archive()) $display = false;
266 + if ($this->options['no_display_date'] && is_date()) $display = false;
267 + if ($this->options['no_display_category'] && is_category()) $display = false;
268 + if ($this->options['no_display_search'] && is_search()) $display = false;
269 + if ($this->options['no_display_feed'] && is_feed()) $display = false;
270
271 $footnotes = array();
272
273 @@ -160,7 +178,7 @@
274 if ( array_key_exists(get_post_meta($post->ID, 'footnote_style', true), $this->styles) ) {
275 $style = get_post_meta($post->ID, 'footnote_style', true);
276 } else {
277 - $style = $this->current_options['list_style_type'];
278 + $style = $this->options['list_style_type'];
279 }
280
281 // Create 'em
282 @@ -175,7 +193,7 @@
283
284
285 // if we're combining identical notes check if we've already got one like this & record keys
286 - if ($this->current_options['combine_identical_notes']){
287 + if ($this->options['combine_identical_notes']){
288 for ($j=0; $j<count($footnotes); $j++){
289 if ($footnotes[$j]['text'] == $identifiers[$i]['text']){
290 $identifiers[$i]['use_footnote'] = $j;
291 @@ -206,12 +224,9 @@
292 $id_id = "identifier_".$key."_".$post->ID;
293 $id_num = ($style == 'decimal') ? $value['use_footnote']+$start_number : $this->convert_num($value['use_footnote']+$start_number, $style, count($footnotes));
294 $id_href = ( ($use_full_link) ? get_permalink($post->ID) : '' ) . "#footnote_".$value['use_footnote']."_".$post->ID;
295 -
296 -// $id_title = str_replace('"', "&quot;", htmlentities(strip_tags($value['text']), ENT_QUOTES, 'UTF-8'));
297 -
298 $id_title = str_replace('"', '`', strip_tags($value['text']));
299 - $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'];
300 - if ($this->current_options['superscript']) $id_replace = '<sup>'.$id_replace.'</sup>';
301 + $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'];
302 + if ($this->options['superscript']) $id_replace = '<sup>'.$id_replace.'</sup>';
303 if ($display) $data = substr_replace($data, $id_replace, strpos($data,$value[0]),strlen($value[0]));
304 else $data = substr_replace($data, '', strpos($data,$value[0]),strlen($value[0]));
305 }
306 @@ -219,14 +234,14 @@
307 // Display footnotes
308 if ($display) {
309 $start = ($start_number != 1) ? 'start="'.$start_number.'" ' : '';
310 - $data = $data.$this->current_options['pre_footnotes'];
311 + $data = $data.$this->options['pre_footnotes'];
312
313 $data = $data . '<ol '.$start.'class="footnotes">';
314 foreach ($footnotes as $key => $value) {
315 $data = $data.'<li id="footnote_'.$key.'_'.$post->ID.'" class="footnote"';
316 if ($style == 'symbol') {
317 $data = $data . ' style="list-style-type:none;"';
318 - } elseif($style != $this->current_options['list_style_type']) {
319 + } elseif($style != $this->options['list_style_type']) {
320 $data = $data . ' style="list-style-type:' . $style . ';"';
321 }
322 $data = $data . '>';
323 @@ -236,56 +251,58 @@
324 $data = $data.$value['text'];
325 if (!is_feed()){
326 foreach($value['identifiers'] as $identifier){
327 - $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'];
328 + $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'];
329 }
330 }
331 $data = $data . '</li>';
332 }
333 - $data = $data . '</ol>' . $this->current_options['post_footnotes'];
334 + $data = $data . '</ol>' . $this->options['post_footnotes'];
335 }
336 return $data;
337 }
338
339 - /**
340 - * Really insert the options page.
341 - */
342 - function footnotes_options_page() {
343 - $this->current_options = get_option('swas_footnote_options');
344 - foreach ($this->current_options as $key=>$setting) {
345 - $new_setting[$key] = htmlentities($setting);
346 - }
347 - $this->current_options = $new_setting;
348 - unset($new_setting);
349 - include (dirname(__FILE__) . '/options.php');
350 - }
351 -
352 - /**
353 - * Insert the options page into the admin area.
354 - */
355 - function add_options_page() {
356 - // Add a new menu under Options:
357 - add_options_page('Footnotes', 'Footnotes', 8, __FILE__, array($this, 'footnotes_options_page'));
358 - }
359 -
360 - function upgrade_post($data){
361 - $data = str_replace('<footnote>',WP_FOOTNOTES_OPEN,$data);
362 - $data = str_replace('</footnote>',WP_FOOTNOTES_CLOSE,$data);
363 - return $data;
364 - }
365 -
366 - function insert_styles(){
367 + function insert_styles() {
368 ?>
369 <style type="text/css">
370 - <?php if ($this->current_options['list_style_type'] != 'symbol'): ?>
371 - ol.footnotes li {list-style-type:<?php echo $this->current_options['list_style_type']; ?>;}
372 + ol.footnotes { font-size: 0.8em; color: #666666; }
373 + a.footnote-link,
374 + td.line-number-column {
375 + -moz-user-select: none;
376 + -webkit-user-select: none;
377 + user-select: none;
378 + }
379 + div.mp-wp-codeblock {
380 + background: none;
381 + font-family: monospace;
382 + color: #333;
383 + border: 1px solid #ddd;
384 + padding: 0;
385 + overflow: auto;
386 + }
387 + td.line-number-column { background: #f5f6f7; text-align: right; vertical-align: top; }
388 + td.line-number-column a { color: #555; padding: 0 5px; }
389 + td.content-column {
390 + padding-left: 10px;
391 + white-space: pre-wrap;
392 + tab-size: 4;
393 + -moz-tab-size: 4;
394 + max-width: 670px; /* adjust as necessary to fit your blog's viewport */
395 + }
396 + span.line-filename { font-weight: bold; }
397 + span.line-meta { color: #999; word-wrap: break-word; }
398 + span.line-added { color: green; }
399 + span.line-removed { color:red; }
400 +
401 + <?php if ($this->options['list_style_type'] != 'symbol'): ?>
402 + ol.footnotes li { list-style-type: <?php echo $this->options['list_style_type']; ?>; }
403 <?php endif; ?>
404 - <?php echo $this->current_options['style_rules'];?>
405 +
406 </style>
407 <?php
408 }
409
410
411 - function convert_num ($num, $style, $total){
412 + function convert_num ($num, $style, $total) {
413 switch ($style) {
414 case 'decimal-leading-zero' :
415 $width = max(2, strlen($total));
416 @@ -301,7 +318,7 @@
417 case 'symbol' :
418 $sym = '';
419 for ($i = 0; $i<$num; $i++) {
420 - $sym .= $this->current_options['list_style_symbol'];
421 + $sym .= $this->options['list_style_symbol'];
422 }
423 return $sym;
424 }
425 @@ -318,7 +335,7 @@
426 * @param string $case Upper or lower case.
427 * @return string The roman numeral
428 */
429 - function roman($num, $case= 'upper'){
430 + function roman($num, $case= 'upper') {
431 $num = (int) $num;
432 $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);
433 $roman = '';
434 @@ -331,7 +348,7 @@
435 return ($case == 'lower') ? strtolower($roman) : $roman;
436 }
437
438 - function alpha($num, $case='upper'){
439 + function alpha($num, $case='upper') {
440 $j = 1;
441 for ($i = 'A'; $i <= 'ZZ'; $i++){
442 if ($j == $num){
443
  1. This is now a regex pattern to include an encoded syntax index. The current patch supports plain text/no highlighting (0) and linux diff -u (1). []
  2. I reverted this change to the default footnote marker style back to its original. I personally like the roman numerals though so that's what billymg.com is using now. Feel free to set it how you like using this variable. []
  3. The open tag no longer needs to be escaped now that it's a regex pattern. []
  4. Pass the new syntax index to format_snippet() so it knows how to highlight the codeblock. []
  5. Upon jfw's suggestion, I decided to not run the codeblock content through htmlspecialchars(). The idea being that the operator can escape their code with a simple line of sed before pasting into mp-wp. This way of thinking also opened up the possibility of including footnotes within codeblocks, since the '((' can be pre-escaped in the sed -> mp-wp workflow.

    This was all I needed for this article: cat [FILE] | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&apos;/g; s/((/\&lpar;\&lpar;/g; s/))/\&rpar;\&rpar;/g; s/\[([0-9])\[/\&lsqb;$1\&lsqb;/g; s/]]/\&rsqb;\&rsqb;/g' > [FILE_ESCAPED] []

  6. I changed this to a plain div because the pre tag wrapped around the other markup (which is probably a misuse of the pre tag in any case) would cause extra newlines to be added when copy/pasting from the snippet into a text file. []
  7. This little bit of CSS avoids selecting footnote markers and line numbers when copy/pasting text from a codeblock. Go ahead, try it out. []
  8. In other jfw-was-right, this change and the white-space: pre-wrap; above were added to eliminate the unseemly horizontal scrolling present in the initial draft. []

MP-WP Roadmap Proposal

March 7th, 2020

Now that I finally have a moment to breathe1, and will soon have an open schedule to dedicate towards republican work2, I figured this would be the right time to describe what I hope to accomplish in the coming months so that others can provide feedback, lest my outline not reflect what would actually be useful3.

The roadmap, in rough priority order:

  1. Finalize patch viewer plugin based on feedback
    Estimate: a few hours
  2. Add server-side selection code to base themes (if investigation to include this higher up the stack doesn't yield anything)
    Estimate: ~1 day
  3. Finish writing tests for the test suite
    Estimate: ~1 week
  4. Bulk image upload
    Estimate: ~1 week
  5. Code chopping (not in priority order)
    Estimate: variable/unknown
    1. Most (all?) of the dashboard "widgets"4
    2. Superfluous icons (all of them?)
    3. The theme editor found at /wp-admin/theme-editor.php5
    4. The plugin editor found at /wp-admin/plugin-editor.php6
    5. Import/export features7
    6. Useless "settings" and associated features
    7. All javascript8
  6. A republican theme, to replace one or both of the existing included themes
    Estimate: a few weeks, depending on revisions

Finalizing the patch viewer comes first since it's very close to being releasable as is9. Next is simply bringing the already-implemented server-side selection code into the main mp-wp vtree. This will likely need to be handled by adding the code to the bundled themes if indeed there's no easy way to include the feature further upstream.

Next we get to the medium-sized items, the first of which is completing the test suite to provide some assurance that subsequent larger changes don't break any existing functionality. As it stands the test suite covers posting and editing articles, as well as uploading and inserting images. Compared to the spec, this put coverage at about a third complete. The bulk image uploader should also be a quick win so I added that above code chopping.

That leaves the larger (and likely ongoing) trimming project, as well as a proper republican theme. The first slimming patch was one of the most satisfying pieces of work I've completed in recent memory and I'd very much like to experience that high again. I expect designing a republican theme to also be a pleasant and satisfying project, almost to the point that I see it more as a reward to be earned for completing the items before it. Plus everyone is already running their own (and to varying degrees, customized) themes, so I saw this as the least pressing item on the list.

There are other items that have been mentioned that I have not included here10—and the list of existing features due for the chopping block is not exhaustive—but altogether this feels like a good place to start.

  1. In 2019 it was the saltmines that were crushing me, these past two months it's been the escape process. []
  2. As was the plan. []
  3. In addition to publishing this for feedback, I'm also publishing it in the hope that it will inspire some designers and/or frontend-focused engineers who have been lurking to see this as a project they can get excited about and contribute to. This is a chance to work on the blogging platform of the republic and therefore the world. And all this without the trannyCoC bs or having to wade through god knows what kind of crap has been tacked onto turdpress since mp-wp forked from 2.7. So if you're interested I do hope you'll introduce yourself. []
  4. I find the 'Right Now' and 'Recent Comments' boxes to be somewhat useful for at-a-glance comment moderation purposes. []
  5. Use yer own damn editor. []
  6. Ditto here. []
  7. The existing mysqldump/mysqlimport commands should work fine for this. []
  8. Perhaps in stages, until completely gone. []
  9. I believe the only outstanding decision to be made is regarding the open/close tags. It's been about a month since the draft patch was published so I think at this point anyone who wanted to weigh in already has. I find myself agreeing with spyked's last comment suggesting that perhaps it be left as a constant in the file so that the operator can easily configure it to what best suits their blog and workflow. This leaves only the decision of what to choose as the default value for this const in the published patch, and for that I sort of like mp's suggested [1[ ... ]], where 1 is an index value mapping to a supported syntax (so for the inaugural patch, 1 == 'unix diff' since it will initially be the only supported syntax). []
  10. Such as redoing themes entirely. []