Transmitting nodes between Drupal sites

The need

As discussed on g.d.o, over a year ago, in Templates or XSLT: How to structure data transfers and client-side content generation? incorporating a format for data exchange is unresolved in Drupal (no easy array/object to Json and back, no standard array/object to XML and back for easy Ajax-Drupal invokation).

So I need to do this now, and my obvious choice is SOA XML, at this stage of the 21st century. However, the lack of being sure of PHP 5 support means having to support PHP 4 XML libraries... prefer to wait till there is a basic standard.

Won't dither around with that.

OK, so if I isolate the serialization/unserialization part, it can be replaced with javascript templates/JSON/XML/XSLT or whatever standard way the Drupal community arrives at.

So what to do to implement it now?

I'm gonna use XML-RPC for the transmission, and I'm gonna use good ole PHP serialize()/unserialize() (array to and from ASCII string) to transmit and receive nodes (we will leave authentication/security issues to one side for the sake of simplicity).

Checking out serialize()/unserialize()

So the PHP Cookbook gives some examples, which could be rendered like this:

$pantry = array('sugar' => '2 lbs.','butter' => '3 sticks'); 
$pantry_s = serialize($pantry);
echo $pantry_s;
$p = unserialize($pantry_s);
print_r($p);

And this gives:

a:2:{s:5:"sugar";s:6:"2 lbs.";s:6:"butter";s:8:"3 sticks";}

Array
(
  [sugar] => 2 lbs.
  [butter] => 3 sticks
)

Let's try that with a node:

Title: This is a page node

Body: In viverra aliquet turpis. Sed at urna pretium diam congue mollis. Duis tempor. Nullam ornare, elit eu imperdiet luctus, eros felis varius sapien, eget venenatis mauris tellus a eros. Mauris a enim. Aenean non ipsum. Nulla facilisi. Nam rutrum, justo a rutrum adipiscing, eros dolor placerat erat, ac suscipit enim pede non nulla. Vestibulum id mi. Praesent placerat scelerisque elit. Sed venenatis. Suspendisse lacinia. Vivamus fermentum. Nullam blandit, quam consequat iaculis faucibus, sem ipsum aliquet diam, nec pharetra lectus risus at sapien. Mauris nec purus quis magna pharetra sollicitudin. Fusce tellus orci, mollis non, adipiscing a, pulvinar et, lacus. Nulla diam.

Name: victorkane

Taxonomy: bungee jumping, Company, Inc,. funny

etc., etc., etc.,

Using the following code:

$page = node_load(33044);
$p_s = serialize($page);
print $p_s;

gives us:

O:8:"stdClass":25:{s:3:"nid";s:5:"33044";s:3:"vid";s:5:"34185";s:4:"type";s:4:"page";s:6:"status";s:1:"1";s:7:"created";s:10:"1187172116";s:7:"changed";s:10:"1187172116";s:7:"comment";s:1:"2";s:7:"promote";s:1:"0";s:6:"sticky";s:1:"0";s:18:"revision_timestamp";s:10:"1187172116";s:5:"title";s:19:"This is a page node";s:4:"body";s:677:"In viverra aliquet turpis. Sed at urna pretium diam congue mollis. Duis tempor. Nullam ornare, elit eu imperdiet luctus, eros felis varius sapien, eget venenatis mauris tellus a eros. Mauris a enim. Aenean non ipsum. Nulla facilisi. Nam rutrum, justo a rutrum adipiscing, eros dolor placerat erat, ac suscipit enim pede non nulla. Vestibulum id mi. Praesent placerat scelerisque elit. Sed venenatis. Suspendisse lacinia. Vivamus fermentum. Nullam blandit, quam consequat iaculis faucibus, sem ipsum aliquet diam, nec pharetra lectus risus at sapien. Mauris nec purus quis magna pharetra sollicitudin. Fusce tellus orci, mollis non, adipiscing a, pulvinar et, lacus. Nulla diam.";s:6:"teaser";s:549:"In viverra aliquet turpis. Sed at urna pretium diam congue mollis. Duis tempor. Nullam ornare, elit eu imperdiet luctus, eros felis varius sapien, eget venenatis mauris tellus a eros. Mauris a enim. Aenean non ipsum. Nulla facilisi. Nam rutrum, justo a rutrum adipiscing, eros dolor placerat erat, ac suscipit enim pede non nulla. Vestibulum id mi. Praesent placerat scelerisque elit. Sed venenatis. Suspendisse lacinia. Vivamus fermentum. Nullam blandit, quam consequat iaculis faucibus, sem ipsum aliquet diam, nec pharetra lectus risus at sapien.";s:3:"log";s:0:"";s:6:"format";s:1:"3";s:3:"uid";s:1:"2";s:4:"name";s:10:"victorkane";s:7:"picture";s:28:"files/pictures/picture-2.jpg";s:4:"data";s:112:"a:4:{s:18:"admin_compact_mode";b:1;s:7:"contact";i:1;s:14:"picture_delete";s:0:"";s:14:"picture_upload";s:0:"";}";s:1:"0";b:0;s:22:"last_comment_timestamp";s:10:"1187172116";s:17:"last_comment_name";N;s:13:"comment_count";s:1:"0";s:8:"taxonomy";a:3:{i:625;O:8:"stdClass":5:{s:3:"tid";s:3:"625";s:3:"vid";s:1:"9";s:4:"name";s:14:"bungee jumping";s:11:"description";s:0:"";s:6:"weight";s:1:"0";}i:626;O:8:"stdClass":5:{s:3:"tid";s:3:"626";s:3:"vid";s:1:"9";s:4:"name";s:13:"Company, Inc.";s:11:"description";s:0:"";s:6:"weight";s:1:"0";}i:624;O:8:"stdClass":5:{s:3:"tid";s:3:"624";s:3:"vid";s:1:"9";s:4:"name";s:5:"funny";s:11:"description";s:0:"";s:6:"weight";s:1:"0";}}s:5:"files";a:0:{}}

cool, sounds like something we could even encrypt and send to the mother lode. Unstandard as all heck, but it will work.

So, erm, how do we send stuff with XML-RPC from one Drupal site to another?

Well, good ole' Matt Westgate and John K. VanDyk's Pro Drupal Development should give us the answer to that, shouldn't it?

And yes, Chapter 19 is entitled "XML-RPC"!

Test 1

So here is a "guess the lucky number" server and the code calling the server from another site, and the result:

Server module:

<?php
// $Id$
/**
* Implementation of hook_xmlrpc().
*
* Maps external names of XML-RPC methods to callback functions.
*/
function xmlrpc_ssa_xmlrpc() {
  return array(
    array(
      'xmlrpc_ssa.guessLuckyNumber', // External method name.
      'xmlrpc_ssa_xmls_guess_lucky_number', // Drupal function to run.
      array('string', 'int'), // Return value's type, then any parameter types
      t('Returns a lucky number.') // Description.
    )
  );
}
/**
* Test if given number matches a random lucky number.
*/
function xmlrpc_ssa_xmls_guess_lucky_number($guess) {
  if ($guess < 1 || $guess > 10) {
    return xmlrpc_error(1, t('Your guess must be between 1 and 10.'));
  }
  $lucky_number = mt_rand(1, 10);
  if ($guess == $lucky_number) {
    return t('Your number matched!');
  }
  else {
    return t('Sorry, the number was @num.', array('@num' => $lucky_number));
  }
}

Client invocation from another Drupal site (use moshe weitzman's- devel module's Execute php block to try it out real quick):

$lucky_number = xmlrpc('http://empleos.mentor/xmlrpc.php', 'xmlrpc_ssa.guessLuckyNumber', 5);
  if (xmlrpc_error()) {
  $error_num = xmlrpc_errno();
  $error = xmlrpc_error();
  print_r ($error);
}
echo $lucky_number;&lt;/code&gt;

Result:

Sorry, the number was 8. 

Test 2 (Iteration 1)

So let's transmit a node already.

New server:

<?php
/**
* Implementation of hook_xmlrpc().
*
* Maps external names of XML-RPC methods to callback functions.
*/
function xmlrpc_ssa_xmlrpc() {
  return array(
    array(
      'xmlrpc_ssa.newNode', // External method name.
      'xmlrpc_ssa_xmls_new_node', // Drupal function to run.
      array('string', 'string'), // Return value's type, then any parameter types
      t('Accepts a new node.') // Description.
    )
  );
}
/**
* Unserialize node, create it and return confirmation or error.
*/
function xmlrpc_ssa_xmls_new_node($node_s) {
  if ($node_s == '') {
    return xmlrpc_error(1, t('Error 99901. Node empty'));
  }
  $node = unserialize($node_s);
  return t('Received @node_title', array('@node_title' => $node -> title));
}

Result (success!):

Received This is a page node

Test 3 (Iteration 2)

So now the server is are going to actually write the node! (I won't be mean and leave that as an "exercise to the reader" Cool ).

New server:

<?php
// $Id$
/**
* Implementation of hook_xmlrpc().
*
* Maps external names of XML-RPC methods to callback functions.
*/
function xmlrpc_ssa_xmlrpc() {
  return array(
    array(
      'xmlrpc_ssa.newNode', // External method name.
      'xmlrpc_ssa_xmls_new_node', // Drupal function to run.
      array('string', 'string'), // Return value's type, then any parameter types
      t('Accepts a new node.') // Description.
    )
  );
}
/**
* Unserialize node, create it and return confirmation or error.
*/
function xmlrpc_ssa_xmls_new_node($node_s) {
  if ($node_s == '') {
    return xmlrpc_error(1, t('Error 99901. Node empty'));
  }  
  $node = unserialize($node_s);
  switch ($node -> type) {
    case 'page':
      unset($node -> nid);
      unset($node -> vid);
      unset($node -> created);
      unset($node -> changed);
      unset($node -> revision_timestamp);
      unset($node -> taxonomy);
      $node = node_submit($node);
      node_save($node);
        break;
    default:
        break;
  }
  return t('Received @node_title', array('@node_title' => $node -> title . ' nid: ' . $node -> nid));
}

Same invocation. It just works! Simplified things with the unsetting of certain fields. Obviously, matching taxonomy nodes .... maybe next post?

Thanks!

Thanks Victor, interesting writeup. The more examples of XML-RPC + Drupal, the better. It's shipped with Drupal core, but it's used very rarely, unfortunately.

See you at DrupalCon hopefully!

Yeah, will be there!

Looking forward to seeing you there.

Yeah, it's powerful stuff.

Services Module

I doing this type of stuff with services module which has an xmlrpc, json, and amfphp server option and 5 basic services (node, user, taxonomy, system, views).

Sounds like what you are doing is covered by services module or could be.

http://drupal.org/project/services

Ah, that is very interesting

I was thinking of looking into the services module.

Sounds like a great option, will definitely look into it. 

Or use Publish - Subscribe modules

Which, yes, need updating for Drupal 5 and some love, but originally written by Jon Van Dyk.

Also, FeedAPI will be able to map RSS feeds / attributes to arbitrary CCK fields in the near feature.

very interesting

Yeah, that will be great!

Also, will be interesting to see some progress on the JSON / AJAX side.

Thanks for your comment.

Victor

Victor can I encourage you to look at this site to site info!

Victor thanks for pointing this out as do-able, and I would like you to keep me posted on this since it can solve much of our challenges at present.

I so enjoy your site but admittedly, some of the IT is way over my head.

I am very interested in you processes approach to project management and how to build that into a project portal in Drupal.

By the way, What tool do you use to draw your nice flow diagrams?
Keep up the good work

Johan