Implementing XML-RPC for Kapost

If you have a custom CMS or one that doesn't already support XML-RPC, you can implement your own XML-RPC support in your CMS.

Kapost needs only a subset of the full 'MetaWeblog API' to be exposed via a standard compliant XMLRPC server endpoint (yoursite.com/endpoint).

All methods are expected to throw XML-RPC compliant (appropriate) error messages in case of any errors.

WARNING: Kapost cannot debug any potential issues that can arise in your custom code, which can prevent Kapost from being able to successfully push content to your CMS instance.

blogger.getUsersBlogs (required)

blogger.getUsersBlogs(app_id=string, username=string, password=string)

Returns an array of hashes in the format:

 [{'blogid' => '1', 'blogName' => 'My Blog'}]

This call must return at least one blog.

For authentication purposes, this blog_id together with the username and password is used by all subsequent XMLRPC calls.

metaWeblog.newPost (required)

metaWeblog.newPost(blog_id=string, username=string, password=string, content=hash, publish=boolean)

The content hash contains at least the following:

{
    'title'     => 'hello',
    'description'   => 'world',
    'mt_keywords'   => 'hello, world',
    'mt_excerpt'    => 'hello world excerpt',
    'categories'    => ['apples', 'oranges'],
    'custom_fields' => 
    [
        {'key' => 'custom_field_one', 'value' => 'value one'},
        {'key' => 'custom_field_two', 'value' => 'value two'}
    ]
}

If the publish boolean is 'true', the content should be 'stored' as published, otherwise as draft.

Returns a string which is the 'internal content id' of the piece of content that has been inserted into the database.

This 'internal content id' will be used later when calling metaWeblog.editPost or metaWeblog.getPost as illustrated below.

metaWeblog.editPost (required)

metaWeblog.editPost(content_id=string, username=string, password=string, content=hash, publish=boolean)

The content hash contains at least the following:

{
    'title'     => 'hello',
    'description'   => 'world',
    'mt_keywords'   => 'hello, world',
    'mt_excerpt'    => 'hello world excerpt',
    'categories'    => ['apples', 'oranges'],
    'custom_fields' => 
    [
        {'id' => 'custom_field_id', 'key' => 'custom_field_one', 'value' => 'value one'},
        {'id' => 'custom_field_id', 'key' => 'custom_field_two', 'value' => 'value two'}
    ]
}

If the publish boolean is 'true', the content should be 'stored' as published, otherwise as draft.

Returns True if the content has been successfully updated or throws the appropriate error message.

metaWeblog.getPost (required)

metaWeblog.getPost(content_id=string, username=string, password=string) 

Returns a hash with a least the following:

{
    'title'     => 'hello',
    'description'   => 'world',
    'mt_keywords'   => 'hello, world',
    'mt_excerpt'    => 'hello world excerpt',
    'categories'    => ['apples', 'oranges'],
  'permaLink' => 'http://yourdomain.com/hello-world-post', 
    'custom_fields' =>
    [
        {'id' => 'custom_field_id', 'key' => 'custom_field_one', 'value' => 'value one'},
        {'id' => 'custom_field_id', 'key' => 'custom_field_two', 'value' => 'value two'}
    ]
}

permaLink (case sensitive) = permalink to the given piece of content

Notes

If the piece of content specified by the given content_id doesn't exist, then an XML-RPC error response should be returned with the error code set to 404 and the message set to Invalid post ID. (case sensitive)

Kapost uses this 'error information' to decide whether it should issue a metaWeblog.newPost or metaWeblog.editPost during republish.

For example, if a piece of content was published, but then subsequently deleted from within the CMS (we strongly discourage this!), then on a 'republish' it will be recreated as a brand new piece of content, given that Kapost receives this XML-RPC error response presented above.

metaWeblog.newMediaObject (required)

metaWeblog.newMediaObject(blog_id=string, username=string, password=string, content=hash)

The content hash contains at least the following:

{'name' => 'panda.jpg', 'type' => 'image/jpeg', 'bits' => 'XYZYZ.........=='}

name = filename of the media file (with proper extension, no path)

type = standard MIME type (image/jpeg)

bits = base64 encoded binary blob of the media file

Returns a hash with a least the following:

{'url' => 'http://yoursite.com/uploads/panda.jpg'}

url = url to the 'stored' media file

metaWeblog.getCategories (required)

metaWeblog.getCategories(blog_id=string, username=string, password=string)

Returns an array of hashes with at least the following:

[{'categoryId' => 'cat_1', 'categoryName' => 'Category One', 'parentId' => '0'}, 
 {'categoryId' => 'cat_2', 'categoryName' => 'Category Two', 'parentId' => 'cat_1'}]

If there's no hierarchy or the given category is a root category, the 'parentId' can be set to '0'.

kapost.getPreview (optional)

Implementing this method is 100% optional and you should consider it only if you want the preview functionality.

kapost.getPreview(blog_id=string, username=string, password=string, content=hash, content_id=string)

For examples of the content hash please refer to the metaWeblog.newPost and metaWeblog.editPost methods. Also the content_id parameter will be missing on the first preview, but will be present on any subsequent previews.

Returns a hash with at least the following:

{'url' => 'http://yoursite.com/preview?p=1010&nonce=3600', 'id' => '1'}

Where url is a 'signed' (i.e with a nonce) permalink that is expected to expire after a given amount of time. It is up to you to decide how long, but the recommended minimum is at least 5 minutes.

As for the id that is the content_id we referred to while discussing the signature of this method and will be present on subsequent preview request for the given content.

Content that has been previewed, won't receive a metaWeblog.newPost call on publish, Kapost will call metaWeblog.editPost instead with the stored content_id mentioned above.

This means that you should persist the previewed content in a 'draft' state while updating it on each preview call, instead of just storing it temporarily for the lifetime of a single request.

Also, your kapost.getPreview implementation shouldn't touch the 'content' if the content transitioned from the 'draft' state into 'published' (or the state appropriate for your CMS). This scenario occurs when a 'published' post is being previewed from within Kapost.

Please note that if you implemented kapost.getPreview after you connected to your CMS, you'll have to refresh the connection by hitting 'save' in order for Kapost to update its exposed method cache and display the 'Preview' option (under the More menu) for your posts.

Files (optional)

If you want to publish individual files (i.e Word or PDF documents) into your own "Media Library" or "DAM", you can take a look at "Implementing file support via XML-RPC for Kapost" guide by clicking here.

Libraries & Documentation

There are XMLRPC client / server libraries for pretty much every 'language' out there (some of them have it built-in their standard libraries, like Python or Ruby).

PHP

.NET (C#, VB.NET)

Python

Sample client-server implementation in Python is located here: https://gist.github.com/2845926.

More

To learn more about XMLRPC and the MetaWeblog API check out the following resources:

Development/Testing on Localhost

To connect Kapost to your localhost for testing, please refer to the following document.