Index: xmlrpc_example.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/xmlrpc_example/xmlrpc_example.info,v
retrieving revision 1.6
diff -u -r1.6 xmlrpc_example.info
--- xmlrpc_example.info	11 Aug 2010 23:16:28 -0000	1.6
+++ xmlrpc_example.info	18 Sep 2010 23:11:32 -0000
@@ -1,7 +1,7 @@
 ; $Id: xmlrpc_example.info,v 1.6 2010/08/11 23:16:28 rfay Exp $
 
 name = XMLRPC example
-description = This is an example of how to implement XML-RPC callbacks by implementing a validation suite.
+description = This is an example of how to implement client and server communications using XML-RPC.
 package = Example modules
 core = 7.x
 files[] = xmlrpc_example.module
Index: xmlrpc_example.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/xmlrpc_example/xmlrpc_example.module,v
retrieving revision 1.4
diff -u -r1.4 xmlrpc_example.module
--- xmlrpc_example.module	27 Aug 2010 10:37:15 -0000	1.4
+++ xmlrpc_example.module	18 Sep 2010 23:58:31 -0000
@@ -3,8 +3,9 @@
 
 /**
  * @file
- * This is an example of how to implement XML-RPC callbacks by registering
- * an XMLRPC server function and then calling it with the xmlrpc() function.
+ * This is an example of how to implement and XML-RPC server by registering
+ * callbacks to specific methods and how to make xmlrpc calls using the builtin
+ * xmlrpc() factory provided by Drupal.
  *
  * For experimentation you may be interested in the
  * @link http://drupal.org/project/xmlrpctester XMLRPC Tester module @endlink.
@@ -18,96 +19,201 @@
  * @see xmlrpc_error_msg()
  */
 
-// This is the server part of the module, implementing a silly
-// little xmlrpc server.
+// This is the common part of the module, implementing all the code required
+// for the client and the server part (most of this code is UI related). The
+// menu definition is the only part shared in this implementation.
+
+/**
+ * Implements hook_menu().
+ * Register all the demonstration forms.
+ */
+function xmlrpc_example_menu() {
+  // This is the server form menu entry. This form can be used to configure
+  // some options of the exposed services.
+  $items['examples/xmlrpc_server'] = array(
+    'title'           => 'XMLRPC Server',
+    'description'     => 'Demonstrates server side XMLRPC with Drupal',
+    'page callback'   => 'drupal_get_form',
+    'page arguments'  => array('xmlrpc_example_server_form'),
+    'access callback' => TRUE,
+    'weight'          => 0,
+  );
+  // This is the client form menu entry.
+  $items['examples/xmlrpc_client'] = array(
+    'title'           => 'XMLRPC Client',
+    'description'     => 'Demonstrates client side XMLRPC with Drupal',
+    'page callback'   => 'drupal_get_form',
+    'page arguments'  => array('xmlrpc_example_client_form'),
+    'access callback' => TRUE,
+    'weight'          => 1,
+  );
+  // This part is completely optional. It will allow the modification of services
+  // defined by this or other modules. This form is used to explain how to use
+  // hook_xmlrpc_alter
+  $items['examples/xmlrpc_alter'] = array(
+    'title'           => 'XMLRPC Alterations',
+    'description'     => 'Demonstrates how to alter defined XMLRPC services',
+    'page callback'   => 'drupal_get_form',
+    'page arguments'  => array('xmlrpc_example_alter_form'),
+    'access callback' => TRUE,
+    'weight'          => 2,
+  );
+  return $items;
+}
+
+
+// This is the server part of the module, implementing a simple and little
+// xmlrpc server with just two simple services. The serveris divided in two
+// different parts: the UI (settings form) and the xmlrpc implementation.
+// 
+// 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 settings form.
+// - add: perform the addition of two numbers. The minimum and maximum values
+// returned by the server can be configured in the settings form.
+//
+// If the result value for the operation is over the maximum limit, an error
+// 10001 is returned.
+// If the result value for the operation is below the minimum limit, an error
+// 10002 is returned.
+
+
+// User interface for the XMLRPC Server part.
+/**
+ * Present a form to configure the xmlrpc service options. In this case the max
+ * and min values for any of the operations (add or subtraction).
+ */
+function xmlrpc_example_server_form() {
+  $form = array();
+  $form['explanation'] = array(
+    '#markup' => "<div>" . t("This is the configuration page for the demonstration XMLRPC Server.<br />Here you may define the maximum and minimum values for the add or subtraction exposed services.<br />") . "</div>",
+  );
+  $form['xmlrpc_example_server_min'] = array(
+    '#type' => 'textfield',
+    '#title' => t("Enter the minimum value returned by sub or add methods"),
+    '#description' => t("An xmlrpc error will result if they sum to more than 10 or the difference is less than 0."),
+    '#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"),
+    '#default_value' => variable_get('xmlrpc_example_server_max', 10),
+    '#size' => 5,
+    '#required' => TRUE,
+  );
+  return system_settings_form($form);
+}
+
+// The following code is the XMLRPC implmentation of the server part. The fisrt
+// step is to define the methods. This methods should be associated to callbacks
+// that will be defined later.
+
 /**
  * Implements hook_xmlrpc().
  *
  * Provides Drupal with an array to map XML-RPC callbacks to the
  * functions implemented by this module.
+ *
  * @see hook_xmlrpc()
  */
 function xmlrpc_example_xmlrpc() {
   $methods[] =  array(
-    'xmlrpc_example.add',
-    '_xmlrpc_example_add',
-    array(
-      'int', // the type of the return value
-      'int', // the type of the first argument
+    'xmlrpc_example.add',  // Method name
+    '_xmlrpc_example_server_add', // Callback to execute
+    array(                 // Array of types for output/input parameteres
+      'int',  // the type of the return value
+      'int',  // the type of the first argument
       'int',  // the type of the second argument
     ),
-    t('Returns the sum of the two arguments.')
+    t('Returns the sum of the two arguments.') // Method description
   );
+  // The subtract method is similiar to the addition
   $methods[] =  array(
     'xmlrpc_example.subtract',
-    '_xmlrpc_example_subtract',
+    '_xmlrpc_example_server_subtract',
     array('int', 'int', 'int'),
     t('Return difference of the two arguments.')
   );
 
   return $methods;
 }
+
+// Here are defined the callbacks for each method in the XMLRPC implementation
+// of the server: _xmlrpc_example.add and _xmlrpc_example.subtract
+
 /**
  * Sum the two arguments.
+ *
+ * This is the callback for the xmlrpc_example.add xmlrpc method.
+ *
  * @param $num1
  * @param $num2
  * @return
- *   The sum of the arguments.
+ *   The sum of the arguments, or error if it is not in server defined bounds.
  *
  * @see xmlrpc_error()
  */
-function _xmlrpc_example_add($num1, $num2) {
+function _xmlrpc_example_server_add($num1, $num2) {
   $sum = $num1 + $num2;
-  if ($sum <= 10) {
-    return $sum;
+  // If result is not within maximum and minimum limits, return corresponding error
+  if ($sum > variable_get('xmlrpc_example_server_max', 10)) {
+    return xmlrpc_error(10001, t("Result is over the higher limit defined by the server."));
   }
-  else {
-    return xmlrpc_error(10001, t("Sorry, I only have ten fingers and can't count past that."));
+  if ($sum < variable_get('xmlrpc_example_server_min', 0)) {
+    return xmlrpc_error(10002, t("Result is under the lower limit defined by the server."));
   }
+  // Otherwise return the result.
+  return $sum;
 }
 
 /**
  * Return the difference of the two arguments.
+ *
+ * This is the callback for the xmlrpc_example.subtract xmlrpc method.
+ *
  * @param numeric $num1
  * @param numeric $num2
  * @return
- *   The difference of the two arguments.
+ *   The difference of the two arguments, or error if it is not in server defined bounds.
  *
  * @see xmlrpc_error()
  */
-function _xmlrpc_example_subtract($num1, $num2) {
-  $difference = $num1 - $num2;
-  if ($difference >= 0) {
-    return $difference;
+function _xmlrpc_example_server_subtract($num1, $num2) {
+  $diference = $num1 - $num2;
+  // If result is not within maximum and minimum limits, return corresponding error
+  if ($diference > variable_get('xmlrpc_example_server_max', 10)) {
+    return xmlrpc_error(10001, t("Result is over the higher limit defined by the server."));
   }
-  else {
-    return xmlrpc_error(10002, t("I have not learned to do negative numbers yet."));
+  if ($diference < variable_get('xmlrpc_example_server_min', 0)) {
+    return xmlrpc_error(10002, t("Result is under the lower limit defined by the server."));
   }
+  // Otherwise return the result.
+  return $diference;
 }
 
+// The server part of the module finishes here.
+
+
+
+
+
 
+
+
+// This is the client part of the module. If defines a form with two input fields
+// to call xmlrcp_example.add or xmlrpc_example.subtract methods on this host.
 // Now begins the client/UI portion of the module.
-/**
- * Implements hook_menu for a simple explanation page.
- */
-function xmlrpc_example_menu() {
-  $items['examples/xmlrpc_example'] = array(
-    'title' => 'XMLRPC example',
-    'description' => 'Demonstrates client and server side XMLRPC with Drupal',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('xmlrpc_example_demo_form'),
-    'access callback' => TRUE,
-  );
-  return $items;
-}
 
 /**
- * Present a form that uses xmlrpc to add or subtract.
+ * Present a form that makes use of xmlrpc services to add or subtract.
  */
-function xmlrpc_example_demo_form() {
+function xmlrpc_example_client_form() {
   $form = array();
   $form['explanation'] = array(
-    '#markup' => "<div>" . t("The XMLRPC example demonstrates the use of the XMLRPC client and server in Drupal. <br/>It uses the xmlrpc() function to act as a client, calling itself as a server to demonstrate both sides of the operation.") . "</div>",
+    '#markup' => "<div>" . t("The XMLRPC example demonstrates the use of the XMLRPC client in Drupal. <br/>It uses the xmlrpc() function to act as a client, calling itself for some defined methods.<br />An xmlrpc error will result if the result is out of bounds defined by the server.<br />") . "</div>",
   );
   $form['num1'] = array(
     '#type' => 'textfield',
@@ -119,7 +225,6 @@
   $form['num2'] = array(
     '#type' => 'textfield',
     '#title' => t("Enter a second integer"),
-    '#description' => t("An xmlrpc error will result if they sum to more than 10 or the difference is less than 0."),
     '#default_value' => 2,
     '#size' => 5,
     '#required' => TRUE,
@@ -127,18 +232,19 @@
   $form['add'] = array(
     '#type' => 'submit',
     '#value' => t("Add the integers"),
-    '#submit' => array('xmlrpc_example_add_submit'),
+    '#submit' => array('xmlrpc_example_client_add_submit'),
   );
   $form['subtract'] = array(
     '#type' => 'submit',
     '#value' => t("Subtract the integers"),
-    '#submit' => array('xmlrpc_example_subtract_submit'),
+    '#submit' => array('xmlrpc_example_client_subtract_submit'),
   );
   return $form;
 }
 
 /**
- * Submit: Call the xmlrpc method to add and report the result.
+ * Submit: query the xmlrpc endpoint for the method xmlrpc_example.add
+ * and report the result.
  * @param $form
  * @param $form_state
  *
@@ -146,12 +252,20 @@
  * @see xmlrpc_errno()
  * @see xmlrpc_error_msg()
  */
-function xmlrpc_example_add_submit($form, &$form_state) {
+function xmlrpc_example_client_add_submit($form, &$form_state) {
+  // First define the endpoint of the xmlrcp service, in this case is our
+  // own server.
   $server = url($GLOBALS['base_url'] . "/xmlrpc.php");
-  $result = xmlrpc($server, array('xmlrpc_example.add' => array(
-    (int) $form_state['values']['num1'],
-    (int) $form_state['values']['num2'],
-  )));
+  // Then we should define the methods to call. xmlrpc support several methods
+  // to be called in the form of 'method_name' => arguments_array
+  $methods = array(
+    'xmlrpc_example.add' => array(
+      (int) $form_state['values']['num1'],
+      (int) $form_state['values']['num2'],
+    ),
+  );
+  // Make the xmlrpc request and process the results.
+  $result = xmlrpc($server, $methods);
 
   if ($result === FALSE) {
     drupal_set_message(t("Error return from xmlrpc(): Error: @errno, Message: @message", array('@errno' => xmlrpc_errno(), '@message' => xmlrpc_error_msg())));
@@ -169,8 +283,9 @@
  * @see xmlrpc()
  * @see xmlrpc_errno()
  * @see xmlrpc_error_msg()
+ * @see xmlrpc_example_client_add_submit()
  */
-function xmlrpc_example_subtract_submit($form, &$form_state) {
+function xmlrpc_example_client_subtract_submit($form, &$form_state) {
   $server = url($GLOBALS['base_url'] . "/xmlrpc.php");
   $result = xmlrpc($server, array('xmlrpc_example.subtract' => array(
     (int) $form_state['values']['num1'],
@@ -185,3 +300,98 @@
   }
 }
 
+// End of client part of the module.
+
+
+
+
+
+
+// Here starts the alteration part of the module. The hook_xmlrpc_alter is
+// usefull when you want to extend, limit or alter methods defined by other
+// modules. This part is not required to have an xmlrcp server or client
+// working, but is usefull to understand what can we do using current xmlrpc
+// factory provided by drupal.
+//
+// This code can be defined in other module to alter the methods exposed by
+// this xmlrpc demonstration server.
+
+// This is the UI definition of the XMLRPCP alteration part of the module.
+/**
+ * Present a form to configure the xmlrpc service options. In this case the max
+ * and min values for any of the operations (add or subtraction).
+ */
+function xmlrpc_example_alter_form() {
+  $form = array();
+  $form['explanation'] = array(
+    '#markup' => "<div>" . t("This is the configuration page for the XMLRPC Server alter feature.<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("Enabling this checkbox, the default methods will be replaced with custom methods not considering the XMLRCP server limit settings."),
+    '#default_value' => variable_get('xmlrpc_example_alter_enabled', 0),
+  );
+  return system_settings_form($form);
+}
+
+// This is the XMLRCP code for the alteration part. We alter the current existing
+// methods setting our own callbacks if the option is enabled.
+/**
+ * Implements hook_xmlrpc_alter().
+ *
+ * Look for xmlrpc_example.add and xmlrpc_example.subtraction methods and replace
+ * their callbacks with custom code.
+ *
+ * @see hook_xmlrpc_alter()
+ */
+function xmlrpc_example_xmlrpc_alter(&$methods) {
+
+  // Only perform alterations if instructed to do so.
+  if (!variable_get('xmlrpc_example_alter_enabled', 0)) {
+    return;
+  }
+  // Loop all defined methods (other modules may have include additional methods)
+  foreach ($methods as $index => $method) {
+    // First element in the method array is the method name.
+    if ($method[0] == 'xmlrpc_example.add') {
+      // Replace current callback with custom callback (second argument of the array)
+      $methods[$index][1] = '_xmlrpc_example_alter_add';
+    }
+    // Do the same for the substraction method.
+    if ($method[0] == 'xmlrpc_example.subtract') {
+      $methods[$index][1] = '_xmlrpc_example_alter_subtract';
+    }
+  }
+}
+
+// Here are defined the callbacks that will overide legitimate methods of the
+// XMLRPC service: _xmlrpc_example.add and _xmlrpc_example.subtract
+
+/**
+ * Sum the two arguments.
+ *
+ * This is the replacement callback for the xmlrpc_example.add xmlrpc method.
+ *
+ * @param $num1
+ * @param $num2
+ * @return
+ *   The sum of the arguments
+ */
+function _xmlrpc_example_alter_add($num1, $num2) {
+  return $num1 + $num2;
+}
+
+/**
+ * Return the difference of the two arguments.
+ *
+ * This is the replacement callback for the xmlrpc_example.subtract xmlrpc method.
+ *
+ * @param numeric $num1
+ * @param numeric $num2
+ * @return
+ *   The difference of the two arguments
+ */
+function _xmlrpc_example_alter_subtract($num1, $num2) {
+  return $num1 - $num2;
+}
Index: xmlrpc_example.test
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/examples/xmlrpc_example/xmlrpc_example.test,v
retrieving revision 1.2
diff -u -r1.2 xmlrpc_example.test
--- xmlrpc_example.test	27 Aug 2010 10:37:15 -0000	1.2
+++ xmlrpc_example.test	18 Sep 2010 23:45:32 -0000
@@ -42,13 +42,71 @@
     $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.subtract' => array(4, 3)));
     $this->assertEqual($result, 1, t('Successfully subtracted 4-3 = 1'));
 
+    // 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 error: 10002'));
+
+    $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.add' => array(7, 4)));
+    $this->assertEqual(xmlrpc_errno(), 10001, t('Results beyond maximum return error: 10001'));
+  }
+
+
+  /**
+   * Perform several calls using xmlrpc UI client
+   */
+  function testXmlrpcExampleClient() {
     // Now test the UI.
     $edit = array('num1' => 3, 'num2' => 5);
-    $this->drupalPost('examples/xmlrpc_example', $edit, t('Add the integers'));
+    $this->drupalPost('examples/xmlrpc_client', $edit, t('Add the integers'));
     $this->assertText(t("The XMLRPC server returned this response: @num", array('@num' => 8)));
 
     $edit = array('num1' => 8, 'num2' => 3);
-    $this->drupalPost('examples/xmlrpc_example', $edit, t('Subtract the integers'));
+    $this->drupalPost('examples/xmlrpc_client', $edit, t('Subtract the integers'));
+    $this->assertText(t("The XMLRPC server returned this response: @num", array('@num' => 5)));
+  }
+
+  /**
+   * Perform several xmlrpc requests changing server settings
+   */
+  function testXmlrpcExampleServer() {
+    // Now test the UI.
+    $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"));
+
+    $edit = array('num1' => 8, 'num2' => 3);
+    $this->drupalPost('examples/xmlrpc_client', $edit, t('Subtract the integers'));
     $this->assertText(t("The XMLRPC 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'));
+
+    $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.subtract' => array(4, 3)));
+    $this->assertEqual(xmlrpc_errno(), 10002, t('subtracting 4-3 = 1 returns: 10002'));
+
+    $result = xmlrpc($this->xmlrpc_url, array('xmlrpc_example.add' => array(7, 4)));
+    $this->assertEqual(xmlrpc_errno(), 10001, t('Adding 7 + 4 = 11 returns: 10001'));
+  }
+  
+  /**
+   * Perform several xmlrpc requests changing server settings
+   */
+  function testXmlrpcExampleAlter() {
+    // Now test the UI.
+    $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"));
+
+    $edit = array('num1' => 80, 'num2' => 3);
+    $this->drupalPost('examples/xmlrpc_client', $edit, t('Subtract the integers'));
+    $this->assertText(t("The XMLRPC 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'));
+
   }
+
 }
--- xmlrpc_example.install
+++ xmlrpc_example.install
@@ -0,0 +1,24 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the simpletest_example module.
+ */
+
+/**
+ * Implements hook_uninstall().
+ *
+ * Removes all the variables created by this module.
+ */
+function xmlrpc_example_uninstall() {
+  $variables = array(
+    'xmlrpc_example_server_min',
+    'xmlrpc_example_server_max',
+    'xmlrpc_example_alter_enabled',
+  );
+  foreach ($variables as $variable) {
+    variable_del($variable);
+  }
+}
+


