Ever since the Great Federating of Giantpaper, I’ve been thinking about how I would get Activitypub to correctly display the different types of blog posts I have set up. Ex: this post, which looked like this on Mastodon:
Or this one, which turned into this:
There were some things I learned about WP posts ending up on the Fediverse through this plugin:
- Custom fields (like those generated by Advanced Custom Fields) don’t get included, so links from my linklog posts won’t appear (which is why this post was posted as a micropost instead of a linkpost)
- Most HTML gets stripped out by some Fedi services like Mastodon, which is why videos weren’t appearing on microposts
The Activitypub plugin has a setting for tweaking the template that gets used for federation:
…but there’s no template tag for custom fields (and no way to not have the plugin render video URLs as video embeds).
Enter Filters
I saw that there was a PR for making the post template filterable, that was upcoming for v2.0.0. And against my judgement, I jumped at it when it released, thinking there would be some sort of documentation for the new filters, but NOPE. I didn’t even know what the filter was called!! 😬 I did see the addition of a new filter name in v2.0.0.0, which is what I thought it was going to be, but actually trying to use it to filter stuff got me the dreaded “Your author URL does not return valid JSON for application/activity+json. Please check if your hosting supports alternate Accept headers.” error in WordPress’s Site Health page. And also I (somehow) found that outputting the contents of $template
from the first parameter that it was actually meant to alter the post template (from the screenshot above with the [ap_....]
tags), but I’m trying to figure out how to modify the HTML output of [ap_content]
. So that wasn’t it.
I saw that the plugin repo has a discussion board! And that people actually use said discussion board! I searched “filters” and found “Modifying ActivityPub posts via WordPress filter? #228“. Trying to add a filter for activitypub_post
got me an error. So that wasn’t it either.
(FYI, the filter name wasn’t what I was looking for, but I did find a Very Important Piece of info in the linked thread that helped me debug my code later. More on that later.)
AND THEN, the search bar at the top, I searched for apply_filters (because if there’s a new way to filter content, it for sure would be added to the code via apply_filters()
):
And there it was: Line 623 in includes/transformer/class-post.php:
After some trial and error, I fiiiiiiiinally got it to work. 😩
Behind the Scenes!
Here is my (very unofficial) documentation for this:
/** * * @param string $content The outputted HTML of [ap_content] * @param WP_Post $post The post object -- see https://developer.wordpress.org/reference/classes/wp_post/ and https://developer.wordpress.org/reference/functions/get_post/ for more info * */ add_filter('activitypub_the_content', function($content, $post){ // add code here return $content }, 100, 2);
First the videos…
This is just some normal non-WP specific code. So if you’re already familiar with PHP, you might already know this. But to make sure your videos show up in your fediposts:
// Remove iframes from Youtube videos "#<figure[^>]+><div[^>]+><iframe.+ src=\"https://www\.youtube\.com/embed/(.+)\?[^\"]+\".+></iframe></div></figure>#", "<p><a href=\"https://youtube.com/watch?v=\\1\">https://youtube.com/watch?v=\\1</a></p>", $content ); // Remove iframes from Vimeo videos "#<figure[^>]+><div[^>]+><iframe.+ src=\"https://player\.vimeo.com/video/(.+)\?[^\"]+\".+></iframe></div></figure>#", "<p><a href=\"https://vimeo.com/\\1\">https://vimeo.com/\\1</a></p>", $content );
You’re basically removing the iframes and surrounding <figure>
and <div>
tags from around the videos and reformatting the embed URLs (inside the src=""
attribute) back to their web accessible URLs (so https://www.youtube.com/embed/t476sB13EOg
→ https://www.youtube.com/watch?v=t476sB13EOg
). And then linking to themselves, which will make Mastodon (and maybe other fediservers) think “oh hey, this is a link, let’s put up a preview”, and embeds the video.
So from this:
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper"><iframe loading="lazy" title="The Prince of Peace" width="500" height="375" src="https://www.youtube.com/embed/AQZqaz10TOM?feature=oembed" frameborder="0" allow="web-share" allowfullscreen></iframe></div></figure>
To this:
<p><a href="https://youtube.com/watch?v=AQZqaz10TOM">https://youtube.com/watch?v=AQZqaz10TOM</a></p>
And the links!
This uses some more WP-specific PHP to detect what post you’re displaying, but here’s what I have:
// Linklog -- prepend external link to post $href = get_field('external_url', $post->ID); if (has_category('linklog', $post)) { $content = '<p><a href="' .$href. '">' .$href. '</a></p>' . $content; }
All together now:
add_filter('activitypub_the_content', function($content, $post){ // Remove iframes from Youtube videos "#<figure[^>]+><div[^>]+><iframe.+ src=\"https://www\.youtube\.com/embed/(.+)\?[^\"]+\".+></iframe></div></figure>#", "<p><a href=\"https://youtube.com/watch?v=\\1\">https://youtube.com/watch?v=\\1</a></p>", $content ); // Remove iframes from Vimeo videos "#<figure[^>]+><div[^>]+><iframe.+ src=\"https://player\.vimeo.com/video/(.+)\?[^\"]+\".+></iframe></div></figure>#", "<p><a href=\"https://vimeo.com/\\1\">https://vimeo.com/\\1</a></p>", $content ); // Linklog -- prepend external link to post $href = get_field('external_url', $post->ID); if (has_category('linklog', $post)) { $content = '<p><a href="' .$href. '">' .$href. '</a></p>' .$content; } return $content; }, 100, 2);
Edit
Updated code to handle syntax highlighting better (it looks REEEALLY bad on Mastodon).
Edit #2
Mastodon does not apply monospace fonts to <pre>
. TIL
Edit #3
Fixed syntax error in the last code snippet. Getting PHP and Javascript mixed up.