The lazier I want to get the more code I need to write, but in the end, it always worth it. Pfefferle had pre-baked a solution already1 but my theme is special2 so I had to come up with my own solution.
Me vs sanity
For a long while I manually copy pasted URLs into an additional post
meta field in my WordPress, which I called webmention_url
.
When I wanted to reply I:
- copied the URL
- went over to my admin area
- opened a new post
- copied the URL to a post meta field
- selected webmention type from the radiobutton
- posted the reply
This was tedious, smelled redundant, and introduced a lot more work.
To show the URLs I had to add it in my theme - it wasn't part of
the_content
, so it wasn't showing in the RSS or in the mail
I'm sending out to some.
I had to hook that post meta field into the webmention plugin and do other magic on it, which made all the things even more complicated.
Bookmarklets
To solve the copy-paste part I turned to Press This3 - which I used a long, long time ago, and when I wanted to use it now, it didn't work. Because Firefox4.
To solve it:
about:config
security.csp.enable => false
This sacrificed some security, which I'm not proud of, but I'm tired of all the fresh things that break things we're actively using.
After this I realized, I need to pass the type of the webmention - so
for reply, I added &type=reply
at the end of the
bookmarklet url, which now looks like this:
javascript: (function(a, b, c, d) {
function e(a, c) {
if ("undefined" != typeof c) {
var d = b.createElement("input");
d.name = a, d.value = c, d.type = "hidden", p.appendChild(d)
}
}
var f, g, h, i, j, k, l, m, n, o = a.encodeURIComponent,
p = b.createElement("form"),
q = b.getElementsByTagName("head")[0],
r = "_press_this_app",
s = !0;
if (d) {
if (!c.match(/^https?:/)) return void(top.location.href = d);
if (d += "&u=" + o(c), c.match(/^https:/) && d.match(/^http:/) && (s = !1), a.getSelection ? h = a.getSelection() + "" : b.getSelection ? h = b.getSelection() + "" : b.selection && (h = b.selection.createRange().text || ""), d += "&buster=" + (new Date).getTime(), s || (b.title && (d += "&t=" + o(b.title.substr(0, 256))), h && (d += "&s=" + o(h.substr(0, 512)))), f = a.outerWidth || b.documentElement.clientWidth || 600, g = a.outerHeight || b.documentElement.clientHeight || 700, f = 800 > f || f > 5e3 ? 600 : .7 * f, g = 800 > g || g > 3e3 ? 700 : .9 * g, !s) return void a.open(d, r, "location,resizable,scrollbars,width=" + f + ",height=" + g);
i = q.getElementsByTagName("meta") || [];
for (var t = 0; t < i.length && !(t > 200); t++) {
var u = i[t],
v = u.getAttribute("name"),
w = u.getAttribute("property"),
x = u.getAttribute("content");
x && (v ? e("_meta[" + v + "]", x) : w && e("_meta[" + w + "]", x))
}
j = q.getElementsByTagName("link") || [];
for (var y = 0; y < j.length && !(y >= 50); y++) {
var z = j[y],
A = z.getAttribute("rel");
("canonical" === A || "icon" === A || "shortlink" === A) && e("_links[" + A + "]", z.getAttribute("href"))
}
b.body.getElementsByClassName && (k = b.body.getElementsByClassName("hfeed")[0]), k = b.getElementById("content") || k || b.body, l = k.getElementsByTagName("img") || [];
for (var B = 0; B < l.length && !(B >= 100); B++) n = l[B], n.src.indexOf("avatar") > -1 || n.className.indexOf("avatar") > -1 || n.width && n.width < 256 || n.height && n.height < 128 || e("_images[]", n.src);
m = b.body.getElementsByTagName("iframe") || [];
for (var C = 0; C < m.length && !(C >= 50); C++) e("_embeds[]", m[C].src);
b.title && e("t", b.title), h && e("s", h), p.setAttribute("method", "POST"), p.setAttribute("action", d), p.setAttribute("target", r), p.setAttribute("style", "display: none;"), a.open("about:blank", r, "location,resizable,scrollbars,width=" + f + ",height=" + g), b.body.appendChild(p), p.submit()
}
})(window, document, top.location.href, "https:\/\/example.com\/wp-admin\/press-this.php?v=8&type=reply");
The harder part was to catch this in PHP. The initial approach of filling the post meta required the post to be saved first, which made me trying to introduce 3 additional filters to Press This5 - 2 of them turned out to be redundant.
I used this setup for a little while, but it wasn't as good as I wanted it to. Then I came across how voxpelli6 is doing replies on Android:
https://www.youtube.com/watch?v=CBPmSpD2jN4
And it hit me: this is how I should be doing it! Inline, nicely integrated with my Markdown format and since I already extend the webmention/pingback URL list with the matches from the content, I wouldn't need to deal with that at all. No more special post meta which can't be accessed with the Android WordPress app either.
The technical gore
I have to give credit to http://regex101.com/ again for helping creating that monster regex.
<?php
add_filter( 'press_this_data', 'cleanup_press_this_data', 9, 1 );
add_filter( 'press_this_suggested_html', 'cleanup_press_this_suggested', 2, 2 );
add_filter('enable_press_this_media_discovery', '__return_false' );
function cleanup_press_this_data ( $data ) {
if ( isset( $data['s'] ) && ! empty( $data['s'] ))
// do magic here; I try to make Markdown from HTML, for example
$data['s'] = some_parser_function( $data['s'] );
return $data;
}
function cleanup_press_this_suggested ( $default_html, $data ) {
$ref = array();
$relation = '';
parse_str ( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_QUERY ), $ref );
if ( is_array( $ref ) && isset ( $ref['u'] ) && ! empty( $ref['u'] ) ) {
$url = $ref['u'];
$t = '';
if ( isset( $ref['type'] ) )
$t = $ref['type'];
switch ( $t ) {
case 'fav':
case 'like':
case 'u-like-of':
$type = 'like: ';
break;
case 'repost':
$type = 'from: ';
break;
case 'reply':
$type = 're: ';
break;
default:
$type = '';
break;
}
$relation = "---\n{$type}{$url}\n---\n\n";
}
$default_html = array (
'quote' => '> %1$s',
'link' => '',
'embed' => $relation,
);
return $default_html;
}
Presenting this is a little trickier; this is what I use:
<?php
add_filter( 'the_content', 'convert_reaction', 1 );
function convert_reaction ( $content ) {
$pattern = "/---[\n\r]+(?:(.*?):\s+)?+\b((?:http|https)\:\/\/?[a-zA-Z0-9\.\/\?\:@\-_=#]+\.[a-zA-Z0-9\.\/\?\:@\-_=#&]*)(?:[\n\r]+((?!---).*))?[\n\r]+---/mi";
$matches = array();
preg_match_all( $pattern, $content, $matches);
if ( empty( $matches ) || ! isset( $matches[0] ) || empty( $matches[0] ) )
return $content;
$replace = false;
$r = false;
$type = false;
$rsvp = '';
$rsvps = array (
'no' => __("Sorry, can't make it."),
'yes' => __("I'll be there."),
'maybe' => __("I'll do my best, but don't count on me for sure."),
);
$replace = $matches[0][0];
$type = trim($matches[1][0]);
$url = trim($matches[2][0]);
$data = trim($matches[3][0]);
if ( $type == 're' && !empty( $data ) )
$rsvp = '<data class="p-rsvp" value="' . $rsvp .'">'. $rsvps[ $rsvp ] .'</data>';
switch ($type) {
case 'like':
case 'fav':
$cl = 'u-like-of';
$prefix = '';
break;
case 'from':
case 'repost':
$cl = 'u-repost-of';
$prefix = '*reposted from:* ';
break;
case 're':
$cl = 'u-in-reply-to';
$prefix = '**RE:** ';
break;
default:
$cl = 'u-url';
$prefix = '**URL:** ';
break;
}
$title = str_replace ( parse_url( $url, PHP_URL_SCHEME) .'://', '', $url);
$r = "\n{$prefix}[{$title}]({$url}){.{$cl}}\n{$rsvp}";
return str_replace ( $replace, $r, $content );
}
Adding Android
Thanks to the long and detailed description of Chris Aldrich7 I added this functionality to
Android as well, with URL Forward8 as:
https://example.com/wp-admin/press-this.php?v=8&type=reply&u=@url
Footnotes
Automating thins is good for you.
(Oh, by the way: this entry was written by Peter Molnar, and originally posted on petermolnar dot net.)
Responses from the internet
http://stream.boffosocko.com/2016/peter-molnar-mentioned-this-post-in-his-article-on-extending