Jeremy Felt's web

Webmentions work log 20200115

Published Wednesday night 🌗

Tonight is Pullman’s first Homebrew Website Club and I’m going to use the allocated hacking time to figure out what might be misfiring in the Webmention plugin’s always approve feature.

Side note: It feels weird typing “Webmention” rather than “webmention”. I think I’m going to use the lowercase version from now on unless I feel like being official about something.

The first hurdle I’ve run into is making two local sites communicate with each other in WordPress. I haven’t had to do this in a while, so my config is a little out of whack.

The most obvious issue is SSL verification. WordPress by default verifies SSL certificates when using things like wp_remote_get(). I have jeremyfelt.test and letsmakeplugins.test setup locally through Valet and both have self-signed certificates that WordPress doesn’t handle.

add_filter( 'https_ssl_verify', '__return_false' );

I added a filter in an mu-plugin to ignore SSL verification on the local sites.

The next hurdle is that WordPress uses gethostbyname() in wp_http_validate_url() to find the IP address of the domain and make sure it isn’t 127.0.0.1, which in this case… it is!

add_filter( 'http_request_host_is_external', '__return_true' );

I added another filter in an mu-plugin to ignore this validation so that I can do all kinds of non-secure things in my local environment. Exciting!

To make sure things were operating as expected, I followed Aaron’s guide to sending manual webmentions. Sometimes using curl is a lot nicer than working through PHP as a way to see what’s really happening. I started an Xdebug session in my editor and watched for incoming webmention requests.

When the webmention endpoint receives a new webmention, it applies a webmention_comment_data filter to process all of the comment data through a handful of things. One of these things is the endpoint’s auto_approve() method, which compares the comment data against the list of auto-allowed domains. Once all this is complete, it creates the new comment with wp_new_comment()—I’m not familiar enough with the reasoning or differences, but it’s possible that wp_insert_comment() could work here as an alternative. I’ll save that for later.

Anyhow. wp_new_comment() sanitizes things so that they look right before comment insertion. It also does the following:

$commentdata['comment_approved'] = wp_allow_comment( $commentdata, $avoid_die );

So whether the comment is already marked as approved (it was) or not, it is run through an extra handful of checks via wp_allow_comment(), including: check_comment().

In check_comment(), WordPress looks at the current comment_moderation option value and if it is set to 1 to enable comment moderation, then false is immediately returned whether or not the comment data was already marked as approved.

There’s another opportunity to filter this value, and I think it’s probably safe to do so.

$approved = apply_filters( 'pre_comment_approved', $approved, $commentdata );

By hooking into the pre_comment_approved filter, we can check to see if the comment type is a webmention and if it was originally set with an approved status. If so, then we can tell wp_allow_comment() to return true and everything works as intended.

add_filter( 'pre_comment_approved', 'jf_check_comment', 10, 2 );

function jf_check_comment( $approved, $commentdata ) {
    if ( 'webmention' !== $commentdata['comment_type'] ) {
        return $approved;
    }

    if ( 1 === $commentdata['comment_approved'] ) {
        return 1;
    }

    return $approved;
}

Aside: I noticed that the documentation for the $approved parameter does not mention that trash is one of the possible values. I opened up a new trac ticket and assigned as a good first bug.

I tested this out as an mu-plugin and tried with my local domains both on and off the allowed domain list and things seem to be working as I expect. I turned this into a pull request to see if everyone else expects the same. 🙂

I’m feeling good about this. It’s at least closer to a solution than I was the other day. I also have a working local development environment for playing with webmentions, which will make future progress even smoother.

I’ll toss the filter into my custom plugin so that I can use it right away. This also clears out one of my to-dos from the last work log. I’ll probably work on a replies post type next because that seems like the next most fun. 🎉

Leave a Reply

The only requirement for your mention to be recognized is a link to this post in your post's content. You can update or delete your post and then re-submit the URL in the form to update or remove your response from this page.

Learn more about Webmentions.