metaWeblog.newPost posting to WordPress from Word

Quick and dirty : posting to WordPress from Word with VBA. I put the text source on the server.

It works, to a point. You can take a Word document, run the PostToWordpress macro (with your own blogs settings) and it puts the content in WordPress.

I haven’t worked with Word for a few years, you’d have to ask someone with up-to-date Word vba skills how to grab the document title, and sanitize and format the document text into proper html-output. For the tests I grabbed a snippet off the net, it does not do a perfect job but the basic idea is kosher.

metaWeblog.newPost

Most sites (including blogger) have skipped from blogger.newPost to newer formats. I prefer using the metaWeblog.newPost rpc method, it is a bit more versatile, and supported by WordPress as well.

(ref. msdn library article on metaWeblog.newPost)

The methodcall is structured like this :

  • methodname : metaWeblog.newPost
  • blogid
  • blog user name
  • blog password
  • the post struct with it’s members
    • categories array
    • description (the doc text)
    • title
    • date created
  • publish (0=save as draft, 1=publish immediately)

So let’s build that step by step as vba routine :

  1.  
  2. Sub PostToWordpress()
  3.  
  4. 'the basic blog settings
  5.     txtURL = "http://www.blog.com/xmlrpc.php"
  6.     txtBlogId = "1"
  7.     txtUserName = "MyUserName"
  8.     txtPassWord = "MyPassword"
  9.  
  10. 'the document settings
  11.     txtTitle = "MyTitle"
  12.     'note : you can use the documents creation date here
  13.     txtDateCreated = Format(Now(), "yyyyMMdd") & "T" & Format(Now(), "hh:mm:ss")
  14.    
  15.     'Categories is an array in the post,
  16.     'you can use category names,
  17.     'and you can always use "Uncategorized"
  18.     'as all wordpress blogs have it
  19.  
  20.     Dim MyCategories(2) As String
  21.     MyCategories(1) = "Uncategorized"
  22.     MyCategories(2) = "example_category_one"
  23.  
  24.     'note: this function grabs the document text,
  25.     'replaces linebreaks with html linebreaks etc.
  26.  
  27.     'it needs a lot more work
  28.     txtDocument = getCurrentDocAsSimpleHtml()

Once I have the blog settings and the content + attributes for the new WordPress post, I set up the XMLHttpRequest object :

  1.      
  2.     Dim objSvrHTTP As ServerXMLHTTP
  3.     Dim strT As String
  4.     Set objSvrHTTP = New ServerXMLHTTP
  5.  
  6.     objSvrHTTP.Open "POST", txtURL, False, CStr(txtUserName), _
  7.     CStr(txtPassWord)
  8.  
  9.     objSvrHTTP.setRequestHeader "Accept", "application/xml"
  10.     objSvrHTTP.setRequestHeader "Content-Type", "application/xml"

Then I start building the XML to send to WordPress,

  1.  
  2. 'methodcall    
  3.     strT = strT & "<methodcall>"
  4.  
  5. 'methodname
  6.     strT = strT & "<methodname>metaWeblog.newPost</methodname>"
  7.  
  8. 'parameters : blog settings
  9.     strT = strT & "<params>"
  10.     strT = strT & "<param><value><string>" & txtBlogId & "</string></value></param>"
  11.     strT = strT & "<param><value><string>" & txtUserName & "</string></value></param>"
  12.     strT = strT & "<param><value><string>" & txtPassWord & "</string></value></param>"
  13.  
  14. 'parameters : the post structure
  15.     strT = strT & "<param>"
  16.         strT = strT & "<struct>"
  17.  
  18. 'parameters : post : category array
  19.             strT = strT & "<member><name>categories</name>"
  20.                 strT = strT & "<value>"
  21.                     strT = strT & "<array>"
  22.                         strT = strT & "<data>"
  23.  
  24.    For i = 1 To UBound(MyCategories)
  25.                             strT = strT & "<value>" & MyCategories(i) & "</value>"
  26.    Next i
  27.                         strT = strT & "</data>"
  28.                     strT = strT & "</array>"
  29.                 strT = strT & "</value>"
  30.             strT = strT & "</member>"
  31.  
  32. 'parameters : post : the content
  33.         strT = strT & "<member><name>description</name>"
  34.             strT = strT & "<value>< [!CDATA[" & txtDocument & "]]></value>"
  35.         strT = strT & "</member>"
  36.  
  37. 'parameters : post : title
  38.         strT = strT & "<member><name>title</name><value>" & txtTitle & "</value></member>"
  39.  
  40. 'parameters : post : date created
  41.         strT = strT & "<member><name>dateCreated</name><value><datetime .iso8601>" &  txtDateCreated & "</datetime></value></member>"
  42.         strT = strT & "</struct>"
  43.     strT = strT & "</param>"
  44.  
  45. 'parameters : post : store as draft (0) or publish immediately (1)
  46.     strT = strT & "<param><value><boolean>0</boolean></value></param>"
  47.  
  48. 'end parameters
  49.     strT = strT & "</params>"
  50.  
  51. 'end methodcall
  52.     strT = strT & "</methodcall>"
  53.  
  54. 'send it to wordpress
  55.     objSvrHTTP.send strT
  56.  
  57. 'send the response to the debug window
  58.     Debug.Print objSvrHTTP.responseText
  59.  
  60. End Sub

conversion to html

The main practical problem is converting the Word document content to html. I grabbed two quick functions to select the current document text, convert the most common html entities and replace linebreaks with their html counterparts, but it is far from perfect.

  1. Private Function getCurrentDocAsSimpleHtml() As String
  2. '… see source text
  3. End Function
  4.  
  5. ' simple HTML entity encoder
  6. Private Function encode(ByVal s As String) As String
  7.     If s = "&" Then
  8.         encode = "&amp;"
  9.     ElseIf s = "< " Then
  10.         encode = "&lt;"
  11.     ElseIf s = ">" Then
  12.         encode = "&gt;"
  13.     ElseIf s = Chr(13) Then
  14.         encode = "<br />"
  15.     Else
  16.         encode = s
  17.     End If
  18. End Function

The WordPress Incutio xml-parser will not pass content it cannot make sense of, which leaves you with an empty post.

using CDATA

A rather common hack I used above is using < ![CDATA[ ]]> to wrap the post content, which indicates to the wordpress xml-parser that I pass a string of raw character data as content. The parser ignores it, and WordPress stuffs everything in the database.

Works great, but it can backfire and slip stuff in the database that wordpress cannot handle and display correctly.

I’d test without using CDATA if you want to post from Word on a regular basis, or use a more professional html-converter on the word documents first.

Posted in wordpress, xml-rpc.

10 Comments

  1. Thanks for the quick response dude… Just what I was looking for.

    Just thought these would help you a bit…

    Post from Excel to WordPress
    Post from Word to WordPress
    VBA to WordPress

    Excellent.

  2. Thanks to you for the tutorial.

    I have one question for you if you can answer please.

    I have used the code above and have also added some more code to also include custom fields but it does not working. Could you tell me what you think the problem might be?

    I have added the following code after ‘parameters : post : date created

    ‘parameter : post structure : member : custom_fields
    strT = strT & “custom_fields”

    strT = strT & “”

    strT = strT & “MyField1My Field 1 Value”
    strT = strT & “MyField2My Field 2 Value”

    strT = strT & “”
    strT = strT & “”

    Thanks

    Right-oh

  3. Lets us try again…

    Change the “[” with “”

    ‘parameter : post structure : member : custom_fields
    strT = strT & “[member][name]custom_fields[/name][value][array]”

    strT = strT & “[data][array]”

    strT = strT & “[array][key]MyField1[/key][value]My Field 1 Value[/value][/array]”
    strT = strT & “[array][key]MyField2[/key][value]My Field 2 Value[/value][/array]”

    strT = strT & “[/array][/data][/array]”
    strT = strT & “[/value][/member]”

    • @Right foot, you were on the right way.

      Custom fields can have a unique id as well, so a basic key-value pair does not cover it ( http://codex.wordpress.org/XML-RPC_wp shows the mw_newPost data structure ).

      It requires an array with structs as [data] in stead of an array with arrays :

      ‘parameter : post structure : member : custom_fields
      strT = strT & “[member][name]custom_fields[/name][value][array][data]”

      ‘list with id-key-value structs, id is optional
      strT = strT & “[struct]”
      strT = strT & “[member][name]key[/name][value]MyField1[/value][/member]”
      strT = strT & “[member][name]value[/name][value]MyField1 Value[/value][/member]”
      strT = strT & “[/struct]”

      strT = strT & “[struct]”
      strT = strT & “[member][name]key[/name][value]MyField2[/value][/member]”
      strT = strT & “[member][name]value[/name][value]MyField2 Value[/value][/member]”
      strT = strT & “[/struct]”

      strT = strT & “[/data][/array][/value][/member]“

  4. Hey,
    great post. But it seems that you’ve made a misprint in this string:
    strT = strT & “”

    It should looks like this:
    strT = strT & “”

    Cheers
    Alex

Leave a Reply

Your email address will not be published. Required fields are marked *