Index: xmlrpc_example.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/xmlrpc_example/xmlrpc_example.module,v
retrieving revision 1.8
diff -u -r1.8 xmlrpc_example.module
--- xmlrpc_example/xmlrpc_example.module	22 Dec 2010 05:39:08 -0000	1.8
+++ xmlrpc_example/xmlrpc_example.module	15 Jan 2011 01:39:08 -0000
@@ -74,7 +74,7 @@
  * A simple landing-page information function.
  */
 function xmlrpc_example_info() {
-  $server = $GLOBALS['base_url'] . "/xmlrpc.php";
+  $server = url('xmlrpc.php', array('script' => 'xmlrpc.php', 'absolute' => TRUE));
 
   $options = array(
     'system.listMethods' => array(),
@@ -82,10 +82,9 @@
   // Make the xmlrpc request and process the results.
   $supported_methods = xmlrpc($server, $options);
   if ($supported_methods === 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())));
   }
 
-
   return array(
     'basic' => 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')))),
     'method_array' => array('#markup' => theme('item_list', array('title' => t('These methods are supported by !server', array('!server' => $server)), 'items' => $supported_methods))),
@@ -99,10 +98,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
@@ -124,6 +124,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() {
@@ -188,10 +204,10 @@
   $max = variable_get('xmlrpc_example_server_max', 10);
   $min = variable_get('xmlrpc_example_server_min', 0);
   if ($sum > $max) {
-    return xmlrpc_error(10001, t("Result is over the upper limit (@max) defined by the server.", array('@max' => $max)));
+    return xmlrpc_error(10001, t('Result is over the upper limit (@max) defined by the server.', array('@max' => $max)));
   }
   if ($sum < $min) {
-    return xmlrpc_error(10002, t("Result is below the lower limit defined by the server (@min).", array('@min' => $min)));
+    return xmlrpc_error(10002, t('Result is below the lower limit defined by the server (@min).', array('@min' => $min)));
   }
   // Otherwise return the result.
   return $sum;
@@ -218,10 +234,10 @@
 
   // If result is not within maximum and minimum limits, return corresponding error
   if ($diference > $max) {
-    return xmlrpc_error(10001, t("Result is above the upper limit (@max) defined by the server.", array('@max' => $max)));
+    return xmlrpc_error(10001, t('Result is above the upper limit (@max) defined by the server.', array('@max' => $max)));
   }
   if ($diference < $min) {
-    return xmlrpc_error(10002, t("Result is below the lower limit (@min) defined by the server.", array('@min' => $min)));
+    return xmlrpc_error(10002, t('Result is below the lower limit (@min) defined by the server.', array('@min' => $min)));
   }
   // Otherwise return the result.
   return $diference;
@@ -239,20 +255,20 @@
 function xmlrpc_example_server_form() {
   $form = array();
   $form['explanation'] = array(
-    '#markup' => "<div>" . t("This is the XML-RPC server configuration page.<br />Here you may define the maximum and minimum values for the addition or subtraction exposed services.<br />") . "</div>",
+    '#markup' => '<div>' . t('This is the XML-RPC server configuration page.<br />Here you may define the maximum and minimum values for the addition or subtraction exposed services.<br />') . '</div>',
   );
   $form['xmlrpc_example_server_min'] = array(
     '#type' => 'textfield',
-    '#title' => t("Enter the minimum value returned by the subtraction or addition methods"),
-    '#description' => t("If the result of the operation is lower than this value, a custom XML-RPC error will be returned: 10002."),
+    '#title' => t('Enter the minimum value returned by the subtraction or addition methods'),
+    '#description' => t('If the result of the operation is lower than this value, a custom XML-RPC error will be returned: 10002.'),
     '#default_value' => variable_get('xmlrpc_example_server_min', 0),
     '#size' => 5,
     '#required' => TRUE,
   );
   $form['xmlrpc_example_server_max'] = array(
     '#type' => 'textfield',
-    '#title' => t("Enter the maximum value returned by sub or add methods"),
-    '#description' => t("if the result of the operation is bigger than this value, a custom XML-RPC error will be returned: 10001."),
+    '#title' => t('Enter the maximum value returned by sub or add methods'),
+    '#description' => t('if the result of the operation is bigger than this value, a custom XML-RPC error will be returned: 10001.'),
     '#default_value' => variable_get('xmlrpc_example_server_max', 10),
     '#size' => 5,
     '#required' => TRUE,
@@ -289,34 +305,48 @@
 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(
     '#type' => 'textfield',
-    '#title' => t("Enter an integer"),
+    '#title' => t('Enter an integer'),
     '#default_value' => 2,
     '#size' => 5,
     '#required' => TRUE,
   );
   $form['num2'] = array(
     '#type' => 'textfield',
-    '#title' => t("Enter a second integer"),
+    '#title' => t('Enter a second integer'),
     '#default_value' => 2,
     '#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"),
+    '#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"),
+    '#value' => t('Subtract the integers'),
     '#submit' => array('xmlrpc_example_client_subtract_submit'),
   );
+  // This button submits a XML-RPC call to the system.multicall 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',
@@ -327,6 +357,48 @@
 }
 
 /**
+ * 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 this is our
+  // own server.
+  $server = url('xmlrpc.php', array('script' => 'xmlrpc.php', 'absolute' => TRUE));
+  // Then we should define the method to call. xmlrpc() requires that all the
+  // information related to the called method be 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())),
+      'error'
+    );
+  }
+  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,7 +412,7 @@
 function xmlrpc_example_client_add_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";
+  $server = url('xmlrpc.php', array('script' => 'xmlrpc.php', 'absolute' => TRUE));
   // 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
@@ -353,10 +425,17 @@
   // 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())),
+      'error'
+    );
   }
   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)))
+    );
   }
 }
 
@@ -373,7 +452,7 @@
  * @see xmlrpc_example_client_add_submit()
  */
 function xmlrpc_example_client_subtract_submit($form, &$form_state) {
-  $server = $GLOBALS['base_url'] . "/xmlrpc.php";
+  $server = url('xmlrpc.php', array('script' => 'xmlrpc.php', 'absolute' => TRUE));
   $options = array(
     'xmlrpc_example.subtract' => array(
       (int) $form_state['values']['num1'],
@@ -383,10 +462,74 @@
   // 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())),
+      'error'
+    );
   }
   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)))
+    );
+  }
+}
+
+/**
+ * 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 builds the system.multicall request
+ * automatically when there is more than one 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 = url('xmlrpc.php', array('script' => 'xmlrpc.php', 'absolute' => TRUE));
+
+  /*
+   * Drupal's built-in xmlrpc server supports the system.multicall method.
+   *
+   * 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 of single method calls, being the array keys the
+   * service methods to be called, and the array elements the method arguments.
+   *
+   * See the code below this comment as example.
+   */
+
+  // 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)))
+    );
   }
 }
 
@@ -488,12 +631,12 @@
 function xmlrpc_example_alter_form() {
   $form = array();
   $form['explanation'] = array(
-    '#markup' => "<div>" . t("This is a configuration form to enable the alteration of XML-RPC methods using hook_xmlrpc_alter.<br />hook_xmlrpc_alter() can be used to alter the current defined methods by other modules. In this case as demonstration, we will overide current add and subtraction methods with others not being limited. Remember that this hook is optional and is not required to create XMLRPC services.<br />") . "</div>",
+    '#markup' => '<div>' . t('This is a configuration form to enable the alteration of XML-RPC methods using hook_xmlrpc_alter.<br />hook_xmlrpc_alter() can be used to alter the current defined methods by other modules. In this case as demonstration, we will overide current add and subtraction methods with others not being limited. Remember that this hook is optional and is not required to create XMLRPC services.<br />') . '</div>',
   );
   $form['xmlrpc_example_alter_enabled'] = array(
     '#type' => 'checkbox',
-    '#title' => t("Overide current xmlrpc_example.add and xmlrpc_example.subtraction methods"),
-    '#description' => t("If this checkbox is enabled, the default methods will be replaced with custom methods that ignore the XML-RPC server maximum and minimum restrictions."),
+    '#title' => t('Overide current xmlrpc_example.add and xmlrpc_example.subtraction methods'),
+    '#description' => t('If this checkbox is enabled, the default methods will be replaced with custom methods that ignore the XML-RPC server maximum and minimum restrictions.'),
     '#default_value' => variable_get('xmlrpc_example_alter_enabled', 0),
   );
   $form['info'] = array(
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	15 Jan 2011 01:39:17 -0000
@@ -24,7 +24,7 @@
 
     // Init common variables.
     global $base_url;
-    $this->xmlrpc_url = url($base_url . '/xmlrpc.php', array('external' => TRUE));
+    $this->xmlrpc_url = url('xmlrpc.php', array('script' => 'xmlrpc.php', 'absolute' => TRUE));
   }
 
   /**
@@ -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)));
-
+    $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)));
+    $result = $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 Multicall 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.'));
   }
 
   /**
@@ -68,11 +88,11 @@
     // Set different minimum and maxmimum valuesI.
     $options = array('xmlrpc_example_server_min' => 3, 'xmlrpc_example_server_max' => 7);
     $this->drupalPost('examples/xmlrpc/server', $options, t('Save configuration'));
-    $this->assertText(t("The configuration options have been saved"), t("Results limited to >= 3 and <= 7"));
+    $this->assertText(t('The configuration options have been saved'), t('Results limited to >= 3 and <= 7'));
 
     $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)));
+    $this->assertText(t('The XML-RPC server returned this response: @num', array('@num' => 5)));
 
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.add' => array(3, 4)));
     $this->assertEqual($result, 7, t('Successfully added 3+4 = 7'));
@@ -92,18 +112,18 @@
     // Enable XML-RPC service altering functionality.
     $options = array('xmlrpc_example_alter_enabled' => 1);
     $this->drupalPost('examples/xmlrpc/alter', $options, t('Save configuration'));
-    $this->assertText(t("The configuration options have been saved"), t("Results are not limited due to methods alteration"));
+    $this->assertText(t('The configuration options have been saved'), t('Results are not limited due to methods alteration'));
 
     // After altering the functionality, the add and subtract methods have no
     // limits and should not return any error.
     $edit = array('num1' => 80, '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' => 77)));
+    $this->assertText(t('The XML-RPC server returned this response: @num', array('@num' => 77)));
 
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.add' => array(30, 4)));
     $this->assertEqual($result, 34, t('Successfully added 30+4 = 34'));
 
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.subtract' => array(4, 30)));
     $this->assertEqual($result, -26, t('Successfully substracted 4-30 = -26'));
-   }
+  }
 }

