Custom XML-RPC Methods in WordPress

With more and more talk around WordPress 3.0 it seems that the system is gaining even more popularity than ever before due to it’s extensibility. Today I’d like to discuss a topic that might not be of much use to bloggers and basic websites, but is generally required in larger projects – XML-RPC. It’s not limited to WordPress 3.0, but as custom post types are on their way in 3.0, we’ll probably see tonnes of new ways WordPress being used.

In general terms (and as written in the Codex), XML-RPC is simply a way of pushing in and pulling out data of a WordPress blog. Used primarily in offline blogging clients such as Windows Live Writer, a few Firefox addons and mobile clients, all of which support a set of XML-RPC blogging APIs. Okay that’s fine, but not used too much, especially in complex systems, as the standard APIs don’t support:

  • Slug corrections
  • Custom taxonomy
  • Custom post types
  • Custom fields

Which of course is easier to do via the standard WordPress admin panel, especially if meta-boxes are well-designed and implemented. So today’s use of XML RPC in WordPress is limited to blogging.

Now, suppose you have some complex system, let’s say a real estate agency website, where properties are listed, locations as taxonomies and property details as custom fields (number of beds, baths, geo location, etc). There’s no way to input this stuff via the standard XML RPC clients, nor the standard XML RPC API which comes with WordPress. Not too useful I know, since when posting properties agents should be careful to the info the input, thus it’s much wiser to use a computer than a phone or something, but..

Suppose your real estate agency has a CRM they’ve been working in for years. Wouldn’t it be nice if the CRM could insert/change property info directly into the website, without having the agents even look at the WordPress admin panel? That, my friends, could be done via custom XML RPC queries.

Basically, anything could be converted into an XML-RPC query, even the most rediculous stuff, such as changing the title of the homepage. This means that if you have enough time and patience, you could build your own (could be stand-alone) admin panel for your website, fully based on XML-RPC calls. Remember those APS.NET content management systems that are managed from standalone .NET applications? ;)

One more example of XML-RPC usage, which I actually implemented a few days ago – data replication between two websites in different languages. Let’s go back to the real estate example, say this agency has two websites – one in English (.com) and one in Russian (.ru). The English agent got all the data for a new property listing so he publishes it on the .com website. As soon as the post is published, an XML-RPC query is fired at the .ru website, where all the data, filtered through Google Translator or something, is saved as a draft. This way, the Russian agents login to the system, see that there are new drafts, open them up, correct the translations and publish. Voila! No more e-mails back and forth with 4GB of photos ;) Oh, didn’t I mention? Files could also be sent via XML-RPC if done correctly!

Alright, stop the theory, let’s talk some code! The filter used to extend the currently supported XML-RPC methods is called xmlrpc_methods and is located somewhere in xmlrpc.php in your WordPress root directory. There’s a list of all supported methods there, so feel free to disable some or add your own:

add_filter('xmlrpc_methods', 'my_xmlrpc_methods');
function my_xmlrpc_methods($methods)
{
	$methods['myMethod'] = 'my_function';
	return $methods;
}

This will add a new XML-RPC method called myMethod, which would fire my_function. I think the callback function is supposed to be global here and could not be wrapped up into a class and passed on as an array. Not sure for what reason, but it seems that the WordPress environment is not fully initialized during XML-RPC calls, thus only the init and other top-level actions are being fired, perhaps there’s a reason behind that.

Anyways, the next step is to do something in my_function:

function my_function($args)
{
	// Parse the arguments, assuming they're in the correct order
	$username	= $args[0];
	$password	= $args[1];
	$data = $args[2];

	global $wp_xmlrpc_server;

	// Let's run a check to see if credentials are okay
	if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
		return $wp_xmlrpc_server->error;
	}

	// Let's gather the title and custom fields
	// At a later stage we'll send these via XML-RPC
	$title = $data["title"];
	$custom_fields = $data["custom_fields"];

	// Format the new post
	$new_post = array(
		'post_status' => 'draft',
		'post_title' => $title,
		'post_type' => 'property',
	);

	// Run the insert and add all meta values
	$new_post_id = wp_insert_post($new_post);
	foreach($custom_fields as $meta_key => $values)
		foreach ($values as $meta_value)
			add_post_meta($new_post_id, $meta_key, $meta_value);

	// Just output something ;)
	return "Done!";
}

There, it’s pretty much self-explanatory. We assume that the XML-RPC query contains the username, password and an array of data where the title and custom_fields are defined. This is pretty much all XML-RPC customizing that has to be done, and the final part is actually running the call which should be a well-formated XML-RPC query. We’ll use the xmlrpc_encode_request php function for that, and the WP_Http class to actually send the request:

$post_id = 1; // Replace with post_id you need to send
$wp_query = new WP_Query("p=" . $post_id);
if ($wp_query->have_posts())
{
	$wp_query->the_post();
	$custom_fields = get_post_custom($post_id);

	// This is the data being sent as the third argument in XML-RPC
	// Could be an associative array
	$data = array(
		"title" => $this->get_the_title(),
		"custom_fields" => $custom_fields
	);

	// Format the XML RPC query params, format the request
	// We're using myMethod which we defined above
	// Assuming the username is admin and the password is 1 ;)
	$params = array("admin", "1", $data);
	$params = xmlrpc_encode_request('myMethod', $params);

	// Initiate a new HTTP carrier and fire the request, sending the parameters
	// using the POST method. We could then capture the result by reading $result
	$request = new WP_Http;
	$result = $request->request('http://domain.org/xmlrpc.php',
		array('method' => 'POST', 'body' => $params));
}

All done! The code above clearly shows how to format a simple XML-RPC request using php and you should’t have any trouble doing this in other programming languages (perhaps for standalone software). One more thing to do is enable XML-RPC support in the Writing section under Settings in the blog that’s supposed to receive the requests (though the HTTP result usually states if there are any problems).

This is only an example, perhaps not too useful, but extending it to send over images, videos, etc automatically would speed up the whole process. Writing a special mobile client for agents would also be an awesome idea. And not only real estate! All this is applicable to any kind of products or services, anything that is slightly bigger than just a post with a title and some text ;)

So, what do you use XML-RPC for? What XML-RPC clients do you prefer? And are you willing to write a standalone application based on XML-RPC as the admin panel of a WordPress blog if your clients don’t like the usual backend?

Cheers!

25 thoughts on “Custom XML-RPC Methods in WordPress

  1. do you have an example of where these files should be placed and how the page should be called? banging my head against the wall here.

    thanks for your help

  2. Fantastic post, and exactly what I was looking for for a project I'm getting started on. I've got a question though.

    I've got a case where some content needs to be syndicated across multiple sites when it's published, and another where content needs to be syndicated after a few days delay. For the first case, i wanted to attach this code to the Publish button in WP Admin. For the other I was going to try to query the posts against a custom field on a daily basis and then fire this functionality with the results.

    Could you lend any insight into attaching this code to the Publish button?

    Thanks!
    -Rob

  3. Hey thanks so much for this post!

    I have been playing around with this and a Flex XML-RPC Library. I was able to get the wp.getPage method to work from Flex and load a post…

    However, when I tried your code in a plugin file I could not get it to work. I am kind of confused about the

    'wp.getUsersBlogs' => 'this:wp_getUsersBlogs',

    in the methods array in xmlrpc.php… What is the 'wp.' and 'this:' for? I feel this may be part of the problem…

    Thanks much for the great tutorial and any help is greatly appreciated!!

  4. one thing i don't really understand is that many programs using this service aren't able to get the uploaded images attached to the post.
    are you aware of a way to do this?
    do you know programs (for mac) using it.
    Or is this a glitch in WordPress…

    Tx.
    (Finding a way would improve my workload on posting blogposts a whole lot..)

  5. Save time on Wordpress with Custom XML-RPC | Savetime On

  6. GoTop’s Blog » 利用XML-RPC发布或修改包含custom_firlds的文章到Wordpress

  7. Thanks Konstantin. Good post, as always.

    Just my two cents:
    Instead of hooking in "xmlrpc_methods", you could also extend the the "wp_xmlrpc_server" server class and replace the XMLRPC-Server with your own. For bigger projects, this is much more flexible.

    1) Own XML-RPC server class
    class Webeo_XMLRPC extends wp_xmlrpc_server {
    public function __construct() {
    parent::__construct();

    $methods = array(
    'webeo.getPost' => 'this:webeo_getPost',
    'webeo.getPosts' => 'this:webeo_getPosts',
    );

    $this->methods = array_merge($this->methods, $methods);
    }

    public static function wbo_getName() {
    return __CLASS__;
    }

    public function sayHello($args) {
    return 'Hello Konstantin!';
    }

    public function webeo_getPost($args) {
    // do your stuff
    }
    }

    2) Register new server class

    add_action('plugins_loaded', array('Webeo_Base', 'init'));
    class Webeo_Base {
    public function __construct() {
    add_filter('wp_xmlrpc_server_class', array('Webeo_XMLRPC', 'wbo_getName'));
    }
    }

    cheers
    roman

  8. Where i have to paste the last part of code? I've tried to use in new php but i cant get it.
    Thanks in advance!

    • Natgeo, you don't just copy and paste the code and expect it to work, read through the article to figure it out, it's quite simple, really ;)

Comments are closed.