Posting to WordPress from Drafts 5
The simpler a process the better; the fewer components involved the less there is to go wrong. If it’s self contained there are no relied upon third party services to disappear.
I love using Ulysses because it posts directly to WordPress and, as much as I love Drafts, having to rely on Workflow to get text to the blog is always a little frustrating. Don’t get me wrong, it works perfectly and the power of Workflow is amazing. Still, there is always the possibility of it going away since Apple’s acquisition.
Drafts 5 (currently in beta) will bring so much power of its own with advanced scripting support and I’m excited for where it will let us go. If that includes direct posting to the blog then even better.
So I started looking at what was possible and whether I could get an action working that allowed me to post straight from Drafts without relying on anything else.
The good news is I did it! I created something that did exactly what I wanted. The bad news, however, is that it’s not exactly a viable solution due to security when not used over https.
Here’s how
I’m not familiar with OAuth, tokens and all that so authentication was the first issue. The WordPress REST API seemed like a good place to start as it supports basic authentication as well as OAuth but it needs to be enabled.
Fortunately, there is a plugin which can do this for us: Basic Authentication handler.
Once installed and activated the plugin allows basic authentication by sending a base64 encoded version of your WordPress username and password. As this can be easily intercepted and decoded it should never be used over a non-secure connection. I would also recommend using a non-admin account.
Using the Postman app to send some tests to the API is a good way to test; it lets you see the raw queries and results so is an easy way to see the base64 encoded string needed to authenticate.
The API request header will therefore be as below:
"headers": {
'Authorization': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ='
}
(That is, of course, just an example base64 string.)
The root point of the API is http(s)://site.address/wp-json
but we want to work with posts so the endpoint for our request becomes: http(s)://site.address/wp-json/wp/v2/posts
.
Then we need to define the actual request itself and the post data we will be passing. Creating the request is as follows:
var http = HTTP.create();
var response = http.request({
"url": endpoint,
"method": "POST",
"encoding": "form",
so we just require the post data itself. We could just send the post title and content meaning the post would be created in accordance with the site defaults but we can send more arguments as part of the request to have greater control such as post status, format, comment status, etc. The full list of arguments and their possible values can be found in the API reference documentation.
Here’s an example:
"data": {
"title": draft.title,
"content": draft.content,
"status": "draft",
"format": "status",
"comment_status": "open"
},
Once it’s all put together posting to WordPress is easy and doesn’t require any other apps or third party services.
If you are running the Drafts 5 beta you can import the action template to post a status (with no title) by tapping this link on the device where Drafts 5 is installed.
If you were to be posting a full post with a title you would need to add something like the following to separate the title from the full content of the draft and then change the content data to the new variable:
var post_body = draft.content.replace("# "+draft.title, "");
Spent a little while updating my Drafts actions for posting to WordPress so they now get the new post ID from the WordPress API's JSON response and then take me straight to the post for checking.
I've made some further enhancement to my Drafts actions for posting to WordPress. To prevent accidental posting I have inserted a prompt with just Post and Cancel buttons. There is also a check to see if the draft is empty and throw an error if it is. The right author ID is now passed as part of the post data to ensure it is assigned to my posting account rather than the admin account. Each post submitted by this method should be as because of the credentials used to connect but better safe than sorry. And, one more thing (just because I can!) As the authentication requires a base64 encoded version of the WordPress username and password I even created an action to generate that string for me.
With a view to replacing Workflow with Drafts (at least as far as is currently possible) I decided to look at posting #indieweb like and replies directly from the app itself. I was wondering how to pull in the post title using JavaScript but then realised this would just be duplicating effort as my Likes and Replies plugin already does the heavy lifting. All I would need to do was pass the URL as part of the HTTP request so it could be used as the relevant custom field. The catch, however, is that custom fields are not exposed via the API by default. Luckily, we can extend the API response and expose extra fields. Hooking into
rest_api_init
and using theregister_rest_field
method you can add a field and specify the callback functions for retrieving or updating that field via the API:add_action( 'rest_api_init', 'slug_register_like' ); function slug_register_like() { register_rest_field( 'post', 'Liked', array( 'get_callback' => 'slug_get_like', 'update_callback' => 'slug_update_like', 'schema' => null, ) ); }
The above sets up the "Liked" custom field for items of type "post" then defines the functions used to retrieve or update. I've not done anything special, just taken it directly from the API Handbook. This means that I can update the post data as seen before with the custom field, where url is the web page to be liked or replied to:"data": { "title": draft.title, "content": draft.content, "status": "draft", "format": "status", "comment_status": "open", "Liked": url },
I've then created new actions in Drafts which take the URL from the clipboard, or prompt for one if the clipboard doesn't hold one, and submit a post as before. The only difference is that I have to update the post after submission for the Likes and Replies plugin to trigger. Performing likes or replies using Workflow meant that I was typing into a small, plain text box. The new aproach means that I can type what I want to say in advance, with the benefits or Markdown support, and then trigger the process.