Index: xmlrpc_example.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/xmlrpc_example/xmlrpc_example.module,v
retrieving revision 1.6
diff -u -r1.6 xmlrpc_example.module
--- xmlrpc_example/xmlrpc_example.module	2 Oct 2010 13:22:49 -0000	1.6
+++ xmlrpc_example/xmlrpc_example.module	3 Oct 2010 07:45:54 -0000
@@ -75,7 +75,7 @@
  */
 function xmlrpc_example_info() {
   return array(
-    '#markup' => t('This XML-RPC example presents code that shows <ul><li><a href="!server">XML-RPC server code</a></li><li><a href="!client">XML-RPC client code</a></li><li>and <a href="!alter">an example hook_xmlrpc_alter() call</a></li></ul>', array('!server' => url('examples/xmlrpc/server'), 'client' => url('examples/xmlrpc/client'), 'alter' => url('examples/xmlrpc/alter'))),
+    '#markup' => t('This XML-RPC example presents code that shows <ul><li><a href="!server">XML-RPC server code</a></li><li><a href="!client">XML-RPC client code</a></li><li>and <a href="!alter">an example hook_xmlrpc_alter() call</a></li></ul>', array('!server' => url('examples/xmlrpc/server'), '!client' => url('examples/xmlrpc/client'), '!alter' => url('examples/xmlrpc/alter'))),
   );
 }
 
@@ -86,10 +86,11 @@
 //
 // The XMLRPC server will define two different services:
 //
-// - sub: perform the subtraction of two numbers. The minimum and maximum values
-// returned by the server can be configured in the server configuration form.
+// - subtract: perform the subtraction of two numbers. The minimum and maximum
+//   values returned by the server can be configured in the server configuration
+//   form.
 // - add: perform the addition of two numbers. The minimum and maximum values
-// returned by the server can be configured in the server configuration form.
+//   returned by the server can be configured in the server configuration form.
 //
 // If the result value for the operation is over the maximum limit, a custom
 // error number 10001 is returned. This is an arbitrary number and could be any
@@ -111,6 +112,22 @@
  * These functions may be defined in other modules. The example implementation
  * defines specific functions for the example services.
  *
+ * Note: Drupal's built-in XML-RPC server already includes several methods by
+ * default:
+ *
+ * Service dicovery methods:
+ * - system.listMethods: return a list of the methods the server has, by name.
+ * - system.methodSignature: return a description of the argument format a
+ * - system.methodHelp: returns a text description of a particular method.
+ *   particular method expects.
+ *
+ * Other:
+ * - system.multicall: perform several method calls in a single xmlrpc request.
+ * - system.getCapabilities: determine if a given capability is supported.
+ *
+ * The methods defined by hook_xmlrpc() will be added to those provided by
+ * default by Drupal's XML-RPC server.
+ *
  * @see hook_xmlrpc()
  */
 function xmlrpc_example_xmlrpc() {
@@ -276,7 +293,7 @@
 function xmlrpc_example_client_form() {
   $form = array();
   $form['explanation'] = array(
-    '#markup' => "<div>" . t("This example demonstrates how to make XML-RPC calls with Drupal. <br/>It uses the xmlrpc() function to act as a client, calling the XML-RPC server defined in this same example for some defined methods.<br />An XML-RPC error will result if the result is out of bounds defined by the server. These error numbers are defined by the server.<br />") . "</div>",
+    '#markup' => "<div>" . t("This example demonstrates how to make XML-RPC calls with Drupal. <br />The 'Request methods' button makes a request to the server and asks for the available list of methods, as a service discovery request. <br/>The 'Add integers' and 'Subtract integers' use the xmlrpc() function to act as a client, calling the XML-RPC server defined in this same example for some defined methods.<br />An XML-RPC error will result if the result in the addition or subtraction requested is out of bounds defined by the server. These error numbers are defined by the server. <br />The 'Add and Subtract' button performs a multicall operation on the XML-RPC server: several requests in a single XML-RPC call.<br />") . "</div>",
   );
   // We are going to call add and subtract methods, and they work with integer values.
   $form['num1'] = array(
@@ -293,17 +310,31 @@
     '#size' => 5,
     '#required' => TRUE,
   );
-  // Include two buttons, each of them calling a different method.
+  // Include several buttons, each of them calling a different method.
+  // This button submits a XML-RPC call to the system.listMethods method.
+  $form['information'] = array(
+    '#type' => 'submit',
+    '#value' => t("Request methods"),
+    '#submit' => array('xmlrpc_example_client_request_methods_submit'),
+  );
+  // This button submits a XML-RPC call to the xmlrpc_example.add method.
   $form['add'] = array(
     '#type' => 'submit',
     '#value' => t("Add the integers"),
     '#submit' => array('xmlrpc_example_client_add_submit'),
   );
+  // This button submits a XML-RPC call to the xmlrpc_example.subtract method.
   $form['subtract'] = array(
     '#type' => 'submit',
     '#value' => t("Subtract the integers"),
     '#submit' => array('xmlrpc_example_client_subtract_submit'),
   );
+  // This button submits a XML-RPC call to the system.mutilcall method.
+  $form['add_subtract'] = array(
+    '#type' => 'submit',
+    '#value' => t("Add and Subtract"),
+    '#submit' => array('xmlrpc_example_client_multicall_submit'),
+  );
   if (variable_get('xmlrpc_example_alter_enabled', FALSE)) {
     $form['overridden'] = array(
       '#type' => 'markup',
@@ -314,6 +345,47 @@
 }
 
 /**
+ * Submit: query the XML-RPC endpoint for the method system.listMethods
+ * and report the result as a Drupal message. The result is a list of the
+ * available methods in this XML-RPC server.
+ *
+ * Important note: Not all XML-RPC servers implement this method. Drupal's
+ * built-in XML-RPC server implements this method by default.
+ *
+ * @param $form
+ * @param $form_state
+ *
+ * @see xmlrpc()
+ * @see xmlrpc_errno()
+ * @see xmlrpc_error_msg()
+ */
+function xmlrpc_example_client_request_methods_submit($form, &$form_state) {
+  // First define the endpoint of the XML-RPC service, in this case is our
+  // own server.
+  $server = $GLOBALS['base_url'] . "/xmlrpc.php";
+  // Then we should define the method to call. xmlrpc() requires that all the
+  // information related to the called method is passed as an array in the form
+  // of 'method_name' => arguments_array
+  $options = array(
+    'system.listMethods' => array(),
+  );
+  // Make the xmlrpc request and process the results.
+  $result = xmlrpc($server, $options);
+  if ($result === FALSE) {
+    drupal_set_message(
+      t("Error return from xmlrpc(): Error: @errno, Message: @message",
+      array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg()))
+    );
+  }
+  else {
+    drupal_set_message(
+      t("The XML-RPC server returned this response: <pre>@response</pre>",
+      array('@response' => print_r($result, TRUE)))
+    );
+  }
+}
+
+/**
  * Submit: query the XML-RPC endpoint for the method xmlrpc_example.add
  * and report the result as a Drupal message.
  *
@@ -340,10 +412,16 @@
   // Make the xmlrpc request and process the results.
   $result = xmlrpc($server, $options);
   if ($result === FALSE) {
-    drupal_set_message(t("Error return from xmlrpc(): Error: @errno, Message: @message", array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg())));
+    drupal_set_message(
+      t("Error return from xmlrpc(): Error: @errno, Message: @message",
+      array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg()))
+    );
   }
   else {
-    drupal_set_message(t("The XML-RPC server returned this response: @response", array('@response' => print_r($result, TRUE))));
+    drupal_set_message(
+      t("The XML-RPC server returned this response: @response",
+      array('@response' => print_r($result, TRUE)))
+    );
   }
 }
 
@@ -370,10 +448,75 @@
   // Make the xmlrpc request and process the results.
   $result = xmlrpc($server, $options);
   if ($result === FALSE) {
-    drupal_set_message(t("Error return from xmlrpc(): Error: @errno, Message: @message", array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg())));
+    drupal_set_message(
+      t("Error return from xmlrpc(): Error: @errno, Message: @message",
+      array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg()))
+    );
+  }
+  else {
+    drupal_set_message(
+      t("The XML-RPC server returned this response: @response",
+      array('@response' => print_r($result, TRUE)))
+    );
+  }
+}
+
+/**
+ * Submit a multicall request: query the XML-RPC endpoint for the methods
+ * xmlrpc_example.add and xmlrpc_example.subtract and report the result as a
+ * Drupal message. Drupal's XML-RPC client build the system.multicall request
+ * automatically when there is more than on method to call.
+ *
+ * @param $form
+ * @param $form_state
+ *
+ * @see xmlrpc()
+ * @see xmlrpc_errno()
+ * @see xmlrpc_error_msg()
+ * @see xmlrpc_example_client_multicall_submit()
+ */
+function xmlrpc_example_client_multicall_submit($form, &$form_state) {
+  $server = $GLOBALS['base_url'] . "/xmlrpc.php";
+
+  /*
+   *  To make a multicall request, the main invoked method should be the
+   *  function 'system.multicall', and the arguments to make this call must be
+   *  defined as an array method calls, in the form of associative arrays with
+   *  the following parameters:
+   *   - methodName <string> the method to call.
+   *   - params     <array>  the arguments of the method.
+   *
+   *  Drupal's built-in xmlrpc server supports the system.multicall method, and
+   *  the xmlrpc client builds a multicall request when the input is not an
+   *  associative array.
+   */
+
+  // Build an array of several calls, Drupal's xmlrpc built-in support will
+  // construct the correct system.multicall request for the server.
+  $options = array(
+    'xmlrpc_example.add' => array(
+      (int) $form_state['values']['num1'],
+      (int) $form_state['values']['num2'],
+    ),
+    'xmlrpc_example.subtract' => array(
+      (int) $form_state['values']['num1'],
+      (int) $form_state['values']['num2'],
+    ),
+  );
+  // Make the xmlrpc request and process the results.
+  $result = xmlrpc($server, $options);
+
+  if ($result === FALSE) {
+    drupal_set_message(
+      t("Error return from xmlrpc(): Error: @errno, Message: @message",
+      array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg()))
+    );
   }
   else {
-    drupal_set_message(t("The XML-RPC server returned this response: @response", array('@response' => print_r($result, TRUE))));
+    drupal_set_message(
+      t("The XML-RPC server returned this response: <pre>@response</pre>",
+      array('@response' => print_r($result, TRUE)))
+    );
   }
 }
 
Index: xmlrpc_example.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/xmlrpc_example/xmlrpc_example.test,v
retrieving revision 1.3
diff -u -r1.3 xmlrpc_example.test
--- xmlrpc_example/xmlrpc_example.test	2 Oct 2010 13:17:04 -0000	1.3
+++ xmlrpc_example/xmlrpc_example.test	3 Oct 2010 07:49:55 -0000
@@ -38,6 +38,15 @@
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.subtract' => array(4, 3)));
     $this->assertEqual($result, 1, t('Successfully subtracted 4-3 = 1'));
 
+    // Make a multicall request
+    $options = array(
+      'xmlrpc_example.add' => array(5, 2),
+      'xmlrpc_example.subtract' => array(5, 2),
+    );
+    $expected = array(7, 3);
+    $result = xmlrpc($this->xmlrpc_url, $options);
+    $this->assertEqual($result, $expected, t('Successfully called multicall request'));
+
     // Verify default limits
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.subtract' => array(3, 4)));
     $this->assertEqual(xmlrpc_errno(), 10002, t('Results below minimum return custom error: 10002'));
@@ -46,19 +55,30 @@
     $this->assertEqual(xmlrpc_errno(), 10001, t('Results beyond maximum return custom error: 10001'));
   }
 
-
   /**
    * Perform several calls using XML-RPC web client.
    */
   function testXmlrpcExampleClient() {
     // Now test the UI.
+    // Add the integers.
     $edit = array('num1' => 3, 'num2' => 5);
     $this->drupalPost('examples/xmlrpc/client', $edit, t('Add the integers'));
     $this->assertText(t("The XML-RPC server returned this response: @num", array('@num' => 8)));
-
+    // Subtract the integers.
     $edit = array('num1' => 8, 'num2' => 3);
     $this->drupalPost('examples/xmlrpc/client', $edit, t('Subtract the integers'));
     $this->assertText(t("The XML-RPC server returned this response: @num", array('@num' => 5)));
+    // Request available methods.
+    $this->drupalPost('examples/xmlrpc/client', $edit, t('Request methods'));
+    $this->assertText('xmlrpc_example.add', t("The XML-RPC Add method was found."));
+    $this->assertText('xmlrpc_example.subtract', t("The XML-RPC Subtract method was found."));
+    // Before testing multicall, verify that method exists
+    $this->assertText('system.multicall', t("The XML-RPC Subtract method was found."));
+    // Verify multicall request
+    $edit = array('num1' => 5, 'num2' => 2);
+    $this->drupalPost('examples/xmlrpc/client', $edit, t('Add and Subtract'));
+    $this->assertText('[0] =&gt; 7', t("The XML-RPC server returned the addition result."));
+    $this->assertText('[1] =&gt; 3', t("The XML-RPC server returned the subtraction result."));
   }
 
   /**
@@ -105,5 +125,5 @@
 
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.subtract' => array(4, 30)));
     $this->assertEqual($result, -26, t('Successfully substracted 4-30 = -26'));
-   }
+  }
 }

