, , ,

This is the seventh article in a series of unknown length discussing a tool written in Lua to publish posts on a WordPress blog from a PC. The source code is available in a public fossil repository.

  • Part 1 showed how to use XML-RPC and wp.newPost
  • Part 2 added file reading to make a minimal working utility.
  • Part 3 added amenities and made the utility useful.
  • We digressed to talk about OAuth and using it at WordPress.com.
  • Part 4 switched to cURL, REST, and OAuth, but can’t yet post.
  • Part 5 improved the token handling and user display and still can’t post.
  • Part 6 implemented posting and added the ability to set title, tags, and categories from command line options.

In this installment we will refactor some of the script and add a second tool to retrieve posts given their ID.

Some background

Every document stored in a WordPress blog has a unique identifier. This appears empirically to be (I haven’t actually read the internal documentation or source code) a simple integer counter of items stored. The very first post at a blog is ID 2. The About page, the only document that exists when you create a new blog is ID 1.

The REST API makes it easy to retrieve any document when you know its ID, so let’s add that capability to our CLI toolbox. Since this is logically distinct from making a new post, let’s take this opportunity to build a second utility in the WPCLI tools suite making the plural in its name be correct, finally.

Collecting common code

We’ve created a new folder in the working directory tree below wprest, called cli. This folder will contain Lua modules used by the tools. By pushing them down a folder, we avoid the possibility that internal modules might be confused for actual CLI commands. This will make packaging and installing the tools much easier.

Our initial modules are cli.common and cli.wp. The first will contain chunks of code common to the utilities, usually related to making them be utilities that share some behavior and configuration in common. The second will contain Lua code specific to actually using the WP REST API.

With these modules created, the top of wppost.lua looks like this:

local app = require "pl.app"
local lapp = require "pl.lapp"
local pretty = require "pl.pretty"
local utils = require "pl.utils"

-- Add the script's folder to Lua's module paths so that we can easily use
-- modules shared among our scripts. Remember the script's folder in case
-- we want to use it for finding documentation or other resources.
local appdir = app.require_here()
local common = require "cli.common"
local wp = require "cli.wp"

The call to app.require_here() adds the directory holding the calling script itself to package.path and package.cpath which are used by the Lua require() function to find modules. With that set, we can find our new modules with a call like require "cli.common" which loads common.lua from the cli folder found on package.path. It also returns the script’s folder in case we want to use it to locate related files, a feature we don’t need yet, but could.

The cli.wp module will get functions named getme() to return the /me API as a table, and newPost() to call the /posts/new API to actually create a post. Both of these functions have been relocated from wppost.lua into the new wp module.

Similarly, the bulk of the command line argument handling that relates to the configuration file has been moved to cli/common.lua so that all the utilities can easily share a common configuration file and the options to create and control it.

As a result, after the block of help text and option specification that appears in the call lapp() we can collapse all that common logic down to three simple function calls:

common.clearoptional(args, {'title','tags','category','keepraw'} )

A second utility: wpget

When wppost finishes uploading and creating a post, it prints the post ID that was just created. Given that ID, or really and ID at all, we want to retrieve the matching document from the blog.

We simply tweak a few things from wppost.lua to create wpget.lua. First, we change the command line options to specify a post ID and an output file. The output file will default to stdout because that is easy to do.

local args = lapp [[
Options for the blog post itself:
  --ID (number)               The post ID to get.  
  --out (default stdout)      The file to write.

The actual work is done by calling a new function from the cli.wp module which will exercise the GET /posts/ID API. Here we call that then extract the actual post content from the table we get back.

-- Actual main body of the script. Read the command line for file name, user, and password.
-- Make the post as requested and show the result.
local ok, res = wp.getByID(args.ID, args.out)
if ok then
    if args.debug then
  args.out:write(ok and ok.body and ok.body.content or ok.content)
        --local ID = ok and ok.body and ok.body.ID or ok.ID
        --print("wpget success: New post ID=".. ID)
    print("wpget failed:", res)

Finally, the new function wp.getByID() is mostly a copy of wp.newPost():

function M.getByID(ID)
    local c = cURL.easy_init()
    local url = args.baseurl..[[/posts/]]..tostring(ID)..[[?context=edit&http_envelope=true]]
    if args.verbose then print (url) end
    c:setopt_httpheader('Authorization: Bearer ' .. args.token)
    local t,h = {},{}
        writefunction = function(s) 
            t[#t+1] = s 
        headerfunction = function(s) 
            h[#h+1] = s 
    c = nil
    if args.keepraw then
            "POST ",url,
    local j = json.decode(table.concat(t))
    return j

Repository and Checkins

All of the code supporting this tool is in a public fossil repository. See the discussion at the tools page for how to get started using this repository. This post documents work that was checked in as [9fc67f372f].

(Written with StackEdit.)