Tags

, , ,

Here’s one way to push content to WordPress from the command line. It could be easily extended to any local application that can either call a Lua script or be directly extended in Lua.

WordPress XML-RPC

WordPress itself can be driven through an API exposed over the XML-RPC protocol. This allows programs running in any PC, web server, smart phone, or really any device at all to update content on a WordPress site, as long as there is a network connection available, and the required credentials are either compiled in or known to the user.

As the name hints, XML-RPC is a means to call a function on a remote server, with all the required data carried in an XML document. In this case, the server is the WordPress site, and the required data is everything needed to describe a new blog post.

While the full set of data describing a post is quite rich, for this quick tool we will assume that we can always post a draft with a guessed title and body copied from a file.

For sensible security, we won’t store either the username or the password in the script. For a quick and dirty version, we’ll assume they are both provided on the command line. Naturally, a production version should take much better care with passwords than this.

Warning: The required credentials are the same as would be needed to log in to your WordPress site and edit it yourself. For blogs hosted by wordpress.com this will be a credential that could allow access to every blog accessible to the named user. It would probably make sense to create a separate user for use by each automated tool, give it its own password, and grant it only the rights it needs on the specific blog you need it to update.

Using XML-RPC from Lua

There is a fair bit of nuance involved in getting the XML formatted right, and the request delivered to the right URL. Fortunately, there are some generally available Lua modules that are a great help.

First, I’m happily using the Lua 5.1 released as Lua for Windows. I know that Lua 5.2 is current, and there is even a series of test versions of Lua 5.3. Lua 5.1 is stable, and more than powerful enough, and is the latest version still supported by Lua for Windows. I do want to move to 5.2 and there are builds available for Windows, but I haven’t yet had the need to tackle that upgrade.

LfW includes a large number of documented, built, and tested modules to cover common tasks. That includes LuaExpat for handling XML and LuaSocket for general socket programming which will both be needed for the one module we need to obtain separately. That is Lua XML-RPC, which provides functions that make creating XML-RPC protocol requests and decoding the results easy, as well as methods that hide the entire call and response process. You will want to get the offered ZIP file download from their site, then copy the folder named src from that zip to somewhere in your Lua’s package.path and name it xmlrpc. For this demonstration, I put that folder in the same place as my script will be rather than in the global Lua for Windows installation folder.

Implementation

The file wppost.lua starts with some boiler plate to add local folders containing init.lua to the module search path. This is required for require "xmlrpc" to find the module when stored in the current directory. Production code will want to either store the module in a system location or discover the folder that the main Lua script is executed from and not use the current folder which will generally not be a useful place.

package.path = [[.?init.lua;]]..package.path

require "xmlrpc"
require "xmlrpc.http"

Some of the fields describing the blog post need to have HTML entities properly encoded before they are delivered to WP. This is specifically the case for the post title. We will not be doing this to the post body on the assumption that the document to be posted is already properly formatted HTML or Markdown.

---
-- Replace certain significan characters from a string with their 
-- corresponding HTML entity. Only the mandatory characters are 
-- replaced.
function htmlentities(s)
    local e = s:gsub("[%&%<%>]",{
          ["&"]="&",
          ["<"]="<",
          [">"]=">",
        })
    return e
end

Actually making the XML-RPC request boils down to filling out a table with the correct keys that describe the post. There are a bunch more fields that could be provided in the content table. Some, like the post_date field are relatively easy to construct and use. (That one is just an ISO-8601 date and time string, for instance.) Some, such as the specifications for tags and categories, require using other RPC methods to translate the human-readable strings into internal identifiers. Doing that is left as an exercise for the reader.

--- 
-- Call the wp.newPost XML-RPC method on the named URL, with 
-- title, body, username, and password as specified.
--
function wp_newPost(title, body, rpcurl, username, password)

    title = htmlentities(title)

    local content = {
        post_type = 'post',       -- post, page, link...
        post_status = 'draft',    -- draft, publish, pending, future, private...
        --post_date = '2014-04-01 03:14:16'   -- post date, required if status is 'future'
        post_title = title,     -- Title
        post_content = body,    -- full content, including <!--more--> if desired
    }

    local ok, res = xmlrpc.http.call(
        rpcurl,         -- something like "https://blog.example.com/xmlrpc.php"
        'wp.newPost', 
        0,              -- BlogID, ignored by WP
        username, 
        password,       -- clear-text, so do be sure to use https, not http
        content         -- Post details
    )
    return ok, res
end

At this point, we have the skeleton of a utility in place. The next steps involve handling the command line, reading a document into a string, and sensibly handling the username and password. I’ll leave those to Part 2 which I’ll publish “real soon now”.

Written with StackEdit.