Restricted XML-RPC methods for WordPress mobile app support

For a long time I’ve had the path to xmlrpc.php blocked completely on a handful of sites so that I didn’t have to worry about Things. One thing that this messes with, if you don’t have Jetpack installed, is the WordPress mobile app. Without a WordPress.com connection, the mobile application relies on the XML-RPC API provided by your WordPress.

So I made a couple plugins.

First, the one you shouldn’t use in production, Log XML-RPC Requests, does exactly what it implies: logs incoming XML-RPC requests to a WordPress site as a custom post type.

I activated this on a site and then went screen by screen through the WordPress Android application to determine what XML-RPC methods were absolutely required in order for things to work.

That data was then used in the Restricted XML-RPC Methods plugin. This rejects any XML-RPC request that is not one of those required by the WordPress Android application. And pingbacks, because they’re underused and will always have a special place in my heart. 🙂

Any extra methods required by the WordPress IOS app are not enabled, but only because I don’t use an iPhone as my primary device. I’m happy to add those in if somebody passes me the list. It’s also possible that it just works!

Encoding serialized attributes for Gutenberg blocks in PHP

I stumbled backwards into this and didn’t see it discussed anywhere else yet, so here’s a path for anyone else finding themselves temporarily confused and internet searching.

Say you’ve created a custom block for the WordPress block editor, Gutenberg. In addition to regular content, there are one or more attributes associated with the block:

<!-- wp:jf/custom { "data":"Some data assigned as an attribute." } /-->

Now that you have the block in place, you want to migrate the meta from thousands of posts into this structure and inject it into the post content. This data can contain all sorts of characters, including quotes and HTML markup.

If you enter quotes and HTML markup directly into Gutenberg, the content is saved like so:

<!-- wp:jf/custom { "data":"Hey, a \u003Ca href=\u0022http:s\/\/jeremyfelt.com\/\u0022\u003Elink to my website\u003C\/a\u003E." } /-->

This works because the Block API replaces a handful of special characters via regex after JSON.stringify()has done the initial translation.

WordPress does not have a PHP mirror of the Block API’s serializeAttributes(), but the existing functions for encoding JSON are really flexible. Per the documentation for json_encode(), which itself is used by wp_json_encode(), a handful of constants are available to apply as a bitmask to ensure specific characters are encoded in a specific way.

So we end up with this:

<?php
// String from non-Gutenberg data source.
$string = 'Hey, a <a href="https://jeremyfelt.com/">link to my website</a>.';

$attribute = wp_json_encode( $string, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE );

// And $attribute is now:
// "Hey, a \u003Ca href=\u0022https:\/\/jeremyfelt.com\/\u0022\u003Elink to my website\u003C\/a\u003E."

// If storing with wp_insert_post(), etc...
$attribute = addslashes( $attribute );

$block = '<!-- wp:jf/custom { "data":' . $attribute . ' } /-->';

And the block string can now be stored as part of the post content.

Note: addslashes() is needed if storing the data with wp_insert_post(), etc… because those strip slashes and \u003Ca looks like a slash that needs to be stripped unless it itself is slashed.

Some thoughts on plugins, Jetpack, advertisements, and things

A series of loose thoughts I’ve been trying to work through over the last couple days in response to Jetpack adding feature suggestions to plugin search results in the WordPress dashboard.

I think injecting promotions into the current plugin search interface is not a great idea. I understand why it’s appealing, and I imagine that it will be helpful for many WordPress site owners. However, it opens an ambiguously regulated door to something that should really be part of a designed system.

I don’t think it would be horrible to have some sort of interface in the plugin search, via a WordPress core API, that allows plugins you have installed to promote their other work based on predefined keywords.

Discoverability of safe and performant plugins by WordPress site owners has always been a hard task to solve.

Which I think is why a large plugin of plugins like Jetpack exists. If it doesn’t—in the world we’ve had—the site owner goes somewhere else (Wix, Squarespace, etc…) or installs a series of plugins that do not have the same resources as the team behind Jetpack.

This series of plugins—one plugin, one feature, all safe and performant—is a vision of WordPress that I’ve always preferred. And I’d love if plugin developers I trust could highlight their other plugins when I search for something new.

Overall, this is something that has gotten much better over the years thanks to the plugin review team and tools that make it easier to apply WordPress coding standards.

But! In the past, installing this series of plugins has led to hacked sites. Hacked sites lead to a lack of trust in the WordPress core project and other plugins. A lack of trust leads to a lack of growth.

Which is where my thoughts often end up and are in some ways still muddled.

If the primary goal for WordPress.org is to increase market share, then it’s probably good to have something like Jetpack around until that ideal discoverability catches up.

If the primary goal for WordPress.org is individual ownership, then plugins of plugins should be broken apart into individual features and we should figure out how to better guarantee security across the ecosystem.

In reality, these aren’t exclusive of each other and they aren’t the only possible goals. That’s the part I’m still trying to work through. Why is it important that WordPress powers more than a third of the web? What are the consequences of slowing down growth? Are there ways to refocus our efforts to help better balance the two? Are there better models for plugins?

Still thinking!