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	2 Oct 2010 21:01:14 -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'))),
   );
 }
 
@@ -111,6 +111,23 @@
  * 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. This list include all the common methods for service discovery
+ * proposed by Edd Dumbill:
+ * @link http://tldp.org/HOWTO/XML-RPC-HOWTO/xmlrpc-howto-interfaces.html
+ * @link http://xmlrpc-c.sourceforge.net/introspection.html
+ *
+ * The list of these XML-RPC methods provided by default is:
+ * - system.multicall: perform several method calls in a single xmlrpc request.
+ * - system.methodSignature: return a description of the argument format a
+ *   particular method expects.
+ * - system.getCapabilities: determe if a given capability is supported.
+ * - system.listMethods: return a list of the methods the server has, by name.
+ * - system.methodHelp: returns a text description of a particular method.
+ *
+ * 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/>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 />The 'Add and Subtract' button performs a multicall operation on the XML-RCP server: several requests in a single XML-RCP call.<br />") . "</div>",
   );
   // We are going to call add and subtract methods, and they work with integer values.
   $form['num1'] = array(
@@ -293,7 +310,13 @@
     '#size' => 5,
     '#required' => TRUE,
   );
+  // Include several buttons, each of them calling a different method.
   // Include two buttons, each of them calling a different method.
+  $form['information'] = array(
+    '#type' => 'submit',
+    '#value' => t("Request methods"),
+    '#submit' => array('xmlrpc_example_client_request_methods_submit'),
+  );
   $form['add'] = array(
     '#type' => 'submit',
     '#value' => t("Add the integers"),
@@ -304,6 +327,11 @@
     '#value' => t("Subtract the integers"),
     '#submit' => array('xmlrpc_example_client_subtract_submit'),
   );
+  $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 +342,45 @@
 }
 
 /**
+ * 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. As described
+ * here @link http://tldp.org/HOWTO/XML-RPC-HOWTO/xmlrpc-howto-interfaces.html
+ * there are a few methods proposed to make services discoverable by client
+ * applications.
+ *
+ * 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.
  *
@@ -377,6 +444,59 @@
   }
 }
 
+/**
+ * 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: <pre>@response</pre>", array('@response' => print_r($result, TRUE))));
+  }
+}
+
 // The client part of the module ends here.
 
 // The alteration part of the module starts here. hook_xmlrpc_alter() is
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	2 Oct 2010 21:40:27 -0000
@@ -38,27 +38,47 @@
     $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'));
 
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.add' => array(7, 4)));
     $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."));
   }
 
   /**

