Behavior, Content, Money – 3 Things you should never give away for free!!!

BCmoney MobileTV

Working with LimeSurvey’s RemoteControl2 JSON-RPC API in PHP

Posted by bcmoney on April 17, 2014 in Cloud Computing, JavaScript, Mobile, PHP, Semantic Web with 2 Comments

No Gravatar
Hideous LimeSurvey shirt

Hideous LimeSurvey shirt (Photo credit: juhansonin)

Recently, I needed to switch away from SurveyMonkey, which, while still a useful free service for quickly collecting some basic Survey results, leaves much to be desired in terms of what they offer in their basic version. Of course the fully paid versions offer significantly more functionality, but the upper-end of the pricing schemes that do everything I needed are just way out of my price range for small individually-funded and/or non-budget independent projects.

This lead me to LimeSurvey (formerly PHPsurveyor), the leading open source web-based Survey data collection software, with a back-end written entirely in PHP.

Getting LimeSurvey installed on my own server was incredibly easy, just download the latest release version and upload the files via FTP. Then load the installation script and it will guide you through the remaining install steps (which are basically just setting a username/password for the administrator account, as well as database configurations such as connection info, table naming, etc). Pretty standard fare for a long-running open source PHP project with a solid development community in place.

What really set LimeSurvey apart from the alternatives though, was the extensibility offered by its API, dubbed RemoteControl2 (with support for both XML-RPC and JSON-RPC).

I had initially started out with XML-RPC since I’m kind of a nerdcore “semantics” guy, and favour XML over JSON for most server-side integration use cases (unless I’m publishing data for client-side consumption, then I almost always favour JSON). The reason, well there simply are way more tools and methodologies already in place for XML than JSON and the reliability mechanisms built into XML such as well-defined schemas (DTD/XSD) which provide data validation, namespsaces (ns) which prevent conflicts in name/value label namings and help ensure you get the right values when parsing, stylesheets (XSL/XSLT)  which allow for on-the-fly transformations, query languages (XPath and XQuery) which simplify data filtering and extraction tasks, and XML security mechanisms such as Digital Signatures which enable better security. However that’s all sure to start a debate on here.

The point is, I wanted to go XML-RPC, I really did! However I have to say, the simplicity of their JSON-RPC API which seems particularly well-implemented won me over.

So here’s what I made, a simple Survey response submission script that I call “limesurvey.collector.php“:

include "xmlrpc-client.php";
include "jsonRPCClient.php";


$path = '/survey/index.php/admin/remotecontrol';
$host = '';
$port = 80;
$auth = base64_encode($username.":".$password); 

$survey_id = 527317; //survey ID to process

/* using LimeSurvey RemoteControly API version 2's XML-RPC method
$request = xmlrpc_encode_request('get_session_key', array($username,$password));
$response = do_call($path, $host, $port, $request, $auth);

$xml = substr($response,strpos($response,"<")); //parse out the XML from the response (starting at first occurence of opening XML tag "<"
$response_xml = simplexml_load_string($xml);
$response_text = $response_xml->params->param->value->string;

$output = "<pre>".htmlentities($request)."</pre><br/><hr/><br/><pre>".htmlentities($response)."</pre><br/><hr/><br/><div>".$response_text."</div>";
echo $output;

/* using LimeSurvey (LS) RemoteControly API version 2's JSON-RPC method */
define("LS_BASEURL", 'http://'.$host.$path);  // adjust this one to your actual LimeSurvey URL
define("LS_USER", $username);
define("LS_PASSWORD", $password);

//instantiate a new client
$jsonRPC = new jsonRPCClient(LS_BASEURL);

//receive session key
$sessionKey = $jsonRPC->get_session_key(LS_USER, LS_PASSWORD);

//get response data from FORM input values
$response_data = array(); 
echo "<pre>";
foreach ($_POST as $key => $value) {
  echo "{$key} = {$value}rn";
  $response_data[$key] = $value;
} //DEBUG:    
echo "</pre>";

//submit survey response data
$groups = $jsonRPC->add_response($sessionKey, $survey_id, $response_data);

//release the session key



Also, here is an example of the 3rd-party Survey I created which uses jQuery to submit the POSTed Survey data to the LimeSurvey JSON-RPC API:

<!DOCTYPE html> 
	<title>Doctor Burnout Questionnaire</title>  
	<meta name="viewport" content="width=device-width, initial-scale=1"> 
  jQuery Mobile  
	<link rel="stylesheet" href="" />
	<script src=""></script>
	<script src=""></script>
<!-- ===================== STYLE-SHEET Links & JavaScript functions ===================== -->
  <link rel="stylesheet" type="text/css" media="screen" href="./css/style2.css" />
  <link rel="stylesheet" type="text/css" media="screen,print" href="./css/content2.css" />
  <link rel="stylesheet" type="text/css" media="screen" href="./css/navigation2.css" />
  <link rel="stylesheet" type="text/css" media="screen" href="./css/bma.css" /> 
  <link rel="stylesheet" type="text/css" media="screen" href="./css/bma_noNav.css" /> 
  <link rel="stylesheet" type="text/css" media="print" href="./css/print.css" />
  <script type="text/javascript" language="javascript" src="./js/bma.js"></script>
  <script type="text/javascript" language="javascript" src="./js/formatTables.js" charset="UTF-8" ></script>
<!-- ===================== END OF STYLE-SHEET Links & JavaScript functions ===================== -->
<style type="text/css">
.ui-slider-input { display: none !important; }
.ui-slider-track { margin-left:30px; margin-right:30px; }
h2 { text-align: center }
#surveyMonkeyInfo {display:none;}
<script language="JavaScript" type="text/javascript">
var PROXY_URL = "../../proxy.php?e=utf-8&f=html&url=";
var SURVEY_COLLECTOR_URL = "http://localhost/ASSORTED/widgets/APPS/survey/limesurvey.collector.php";

function f_Submit()

	// Form validation
	var message = ""; 

	var fldChecked="" ;
	var fldToCheck = document.getElementsByName("Gender"); 
	len = fldToCheck.length ; 
	for (i=0; i<len; i++) {
		if (fldToCheck[i].checked) {
	if (fldChecked=="") {
		message = message + "nGender not selected" ;

	//Years experience
	if (document.forms[0].YearsWork.selectedIndex==0) {
		message = message + "nYears of working experience not chosen" ; 

	//check Specialty has been selected
	if (document.forms[0].Specialty.selectedIndex==0) {
		message = message + "nSpecialty not chosen" ; 

	//check WorkHours has been selected
	if (document.forms[0].WorkHours.selectedIndex==0) {
		message = message + "nHours per week not chosen";

	//check fields - loop
	var fldsText = ""
	for (j=1; j<=16; j++) {
		var fldName = "Q" + j.toString()
		var fldToCheck = document.getElementsByName(fldName); 
		len = fldToCheck.length; 
		fldChecked = "";		
		for (i=0; i<len; i++) {
			if (fldToCheck[i].value != 0) {
				fldChecked = fldToCheck[i].value;
		if (fldChecked=="") {
			if (fldsText=="") {
				fldsText = "Question: " + fldName;
			} else {
				fldsText = fldsText + ", " + fldName;
	if (fldsText!="") {
		fldsText = fldsText + " not selected"; 
		message = message + "n" + fldsText; 

	if (message!="") {
			alert("The form has the following errors:" + message);
			return false;
    } else {

/* get choices and convert to LimeSurvey expected format, then send via jQuery POST */      
        var q1 = parseInt($('#Q1').val(),10);
        var q2 = parseInt($('#Q2').val(),10);
        var q3 = parseInt($('#Q3').val(),10);
        var q4 = parseInt($('#Q4').val(),10);
        var q5 = parseInt($('#Q5').val(),10);
        var q6 = parseInt($('#Q6').val(),10);
        var q7 = parseInt($('#Q7').val(),10);
        var q8 = parseInt($('#Q8').val(),10);
        var q9 = parseInt($('#Q9').val(),10);
        var q10 = parseInt($('#Q10').val(),10);
        var q11 = parseInt($('#Q11').val(),10);
        var q12 = parseInt($('#Q12').val(),10);
        var q13 = parseInt($('#Q13').val(),10);
        var q14 = parseInt($('#Q14').val(),10);
        var q15 = parseInt($('#Q15').val(),10);
        var q16 = parseInt($('#Q16').val(),10);

		var gender = $('#Gender').val();
		var yearsWork = $('#YearsWork').val();
		var specialty = $('#Specialty').val();
		var workHours = $('#WorkHours').val();

        var POST_DATA = { 
			java527317X1X11 : q1, 
			"527317X1X11" : q1, 
			java527317X1X12 : q2, 
			"527317X1X12" : q2, 
			java527317X1X13 : q3, 
			"527317X1X13" : q3, 
			java527317X1X14 : q4, 
			"527317X1X14" : q4, 
			java527317X1X15 : q5, 
			"527317X1X15" : q5, 
			java527317X1X16 : q6, 
			"527317X1X16" : q6, 
			java527317X1X17 : q7, 
			"527317X1X17" : q7, 
			java527317X1X18 : q8, 
			"527317X1X18" : q8, 
			java527317X1X19 : q9, 
			"527317X1X19" : q9, 
			java527317X1X110 : q10, 
			"527317X1X110" : q10, 
			java527317X1X111 : q11, 
			"527317X1X111" : q11, 
			java527317X1X112 : q12, 
			"527317X1X112" : q12, 
			java527317X1X113 : q13, 
			"527317X1X113" : q13, 
			java527317X1X114 : q14, 
			"527317X1X114" : q14, 
			java527317X1X115 : q15, 
			"527317X1X115" : q15, 
			java527317X1X116 : q16, 
			"527317X1X116" : q16, 		
			java527317X1X38 : gender, 
			"527317X1X38" : gender, 			
			java527317X1X39 : yearsWork, 
			"527317X1X39" : yearsWork, 			
			java527317X1X40 : specialty, 
			"527317X1X40" : specialty, 			
			java527317X1X41 : workHours, 
			"527317X1X41" : workHours, 
			lastgroup : "527317X1", 
			relevance1 : "1", 
			relevance38 : "1", 
			relevance39 : "1", 
			relevance40 : "1", 
			relevance41 : "1", 
			relevanceG0 : "1", 
			movesubmit : "qmovesubmit", 
			thisstep : "1", 
			sid : "527317", 
			start_time : "1397651769", 
			LEMpostKey : "145856491"

		  type: "POST",
		  data: POST_DATA,
		  complete: function(xmlHttp) {
			if(xmlHttp.status.toString() == '200') { 				
				//$("#results").attr('src', PROXY_URL+"");
				return true;
			} else if(xmlHttp.status.toString()[0] == '3') { 
				//try to open new window to 302 redirect location'Location'));
				return false;			
			else { 
				console.log("Status: " + xmlHttp.status);

		return false;		

// -->
<body text="#000000" bgcolor="#FFFFFF" class="content">
<div id="framediv" style="display:none">
 <iframe height="900px" width="1100px"  name="results" id="results" border="0px" src=""></iframe>
<div data-role="page">

	<div data-role="header">
		<h1>Burnout Questionnaire</h1>
	</div><!-- /header -->

	<div data-role="content">	
  <h2>Oldenburg Burnout Inventory</h2>
	<!-- POST to 3rd-party collector (BMA): -->
	<form method="post" action="" name="_quest">
	<!-- POST to LimeSurvey collector: <form method="post" action="./limesurvey.collector.php" name="survey"> -->
    <!-- POST to basic test collector: <form method="post" action="./collector.php" name="survey"> -->	
	<!-- POST to 3rd-party collector via iFrame:  <form method="post" action="" name="survey" target="results" method="post"> -->	
		<input type="hidden" name="__Click" value="0">  
    <div class="pageWrapper">
    This confidential questionnaire can help you screen yourself for symptoms of burnout. It will help you identify feelings and experiences you have about your work, so that you can get a feel for whether you might be vulnerable to burnout. 
    <br /><br /> Burnout can occur in a range of occupations, but most frequently occurs in the caring professions. 
    <br /><br /> Factors contributing to burnout include excessive workloads, patients pressures, lack of control, interference from managers, insecurity; reorganisation, poor support, front line practice, perceived threats of complaints or violence and dysfunctional workplaces. 
    <strong>Instructions</strong>:<br />Below you will find a series of statements with which you may agree or disagree. Please select a radio button under the column that best matches your agreement with each statement. 

  <div id="content" class="clearfix">             
            <div class="colB">
                <h2 class="hidden">Main content</h2>
                <div id="mainContent" class="box boxB clearfix">
                    <div class="boxContent clearfix wysiwyg">

<!-- ================ FORM START ================ -->                    
 <table class="altRow width-5%-47%-12%-12%-12%-12% align-l-l-c-c-c-c">
  <td> </td><td style="border-left:none;">Question</td><td>Strongly<br />Disagree</td><td>Disagree</td><td>Agree</td><td>Strongly<br />Agree</td>
	<td>I rarely find new or interesting aspects in my work.</td>
  <td colspan="4"><input type="range" name="Q1" id="Q1" value="0" min="1" max="4" /></td>
	<td>There are days when I feel tired before I arrive at work.</td>
  <td colspan="4"><input type="range" name="Q2" id="Q2" value="0" min="1" max="4" /></td>
	<td>I talk about my work in a negative way more often than I did before. </td>
  <td colspan="4"><input type="range" name="Q3" id="Q3" value="0" min="1" max="4" /></td>	
	<td>After work, I tend to need more time than in the past in order to relax and feel better. </td>
  <td colspan="4"><input type="range" name="Q4" id="Q4" value="0" min="1" max="4" /></td>
	<td>The pressure of my work becomes intolerable. </td>
  <td colspan="4"><input type="range" name="Q5" id="Q5" value="0" min="1" max="4" /></td>
	<td>Lately, I tend to think less at work and do my job almost mechanically. </td>
  <td colspan="4"><input type="range" name="Q6" id="Q6" value="0" min="1" max="4" /></td>
	<td>I do not find my work to be a positive challenge. </td>
  <td colspan="4"><input type="range" name="Q7" id="Q7" value="0" min="1" max="4" /></td>
	<td>During my work, I often feel emotionally drained. </td>
  <td colspan="4"><input type="range" name="Q8" id="Q8" value="0" min="1" max="4" /></td>
	<td>Over time, one can become disconnected from this type of work.</td>
	<td colspan="4"><input type="range" name="Q9" id="Q9" value="0" min="1" max="4" /></td>
	<td>After working, I typically lack energy for my leisure activities.</td>
  <td colspan="4"><input type="range" name="Q10" id="Q10" value="0" min="1" max="4" /></td>
	<td>Sometimes I feel sickened by my work tasks. </td>
  <td colspan="4"><input type="range" name="Q11" id="Q11" value="0" min="1" max="4" /></td>
	<td>After my work, I usually feel worn out and weary. </td>
  <td colspan="4"><input type="range" name="Q12" id="Q12" value="0" min="1" max="4" /></td>
	<td>I can easily imagine myself doing other type of work. </td>
  <td colspan="4"><input type="range" name="Q13" id="Q13" value="0" min="1" max="4" /></td>
	<td>Usually, I have trouble managing the amount of work. </td>
  <td colspan="4"><input type="range" name="Q14" id="Q14" value="0" min="1" max="4" /></td>
	<td>I feel less and less engaged in my work.</td>
  <td colspan="4"><input type="range" name="Q15" id="Q15" value="0" min="1" max="4" /></td>
	<td>When I work, I tend to lack an energized feeling. </td>
  <td colspan="4"><input type="range" name="Q16" id="Q16" value="0" min="1" max="4" /></td>
<br /><br /> So that we may gather an idea of the type of person who is completing this questionnaire, please answer the questions below. <br /><br />
<table class="altRow2 width-160px-400px" width="100%" border="1">
<tr valign="top"><td width="50%"><strong>Gender</strong>:</td><td width="50%">
<input name="%%Surrogate_Gender" type="hidden" value="1"><label>
<input type="radio" name="Gender" value="Female" id="Gender">Female</label><label>
<input type="radio" name="Gender" value="Male" id="Gender">Male</label></td></tr>

<tr valign="top"><td width="50%"><strong>Years of working experience</strong>:</td><td width="50%">
<input name="%%Surrogate_YearsWork" type="hidden" value="1">
<select name="YearsWork" id="YearsWork">
<option selected>Please select:
<option>0-5 years
<option>6-10 years
<option>11-15 years
<option>16-20 years
<option>21-25 years
<option>26-30 years
<option>31-35 years
<option>36-40 years
<option>40+ years</select>

<tr valign="top"><td width="50%"><strong>Specialty</strong>:</td><td width="50%">
<input name="%%Surrogate_Specialty" type="hidden" value="1">
<select name="Specialty" id="Specialty">
<option selected>Please select:
<option>Accident & emergency
<option>Community health service
<option>General medicine
<option>General practice
<option>Obstetrics & gynaecology
<option>Public health medicine

<tr valign="top"><td width="50%"><strong>Number of working hours per week</strong>:</td><td width="50%">
<input name="%%Surrogate_WorkHours" type="hidden" value="1">
<select name="WorkHours" id="WorkHours">
<option selected>Please select:
<option>0-5 hours
<option>6-10 hours
<option>11-15 hours
<option>16-20 hours
<option>21-25 hours
<option>26-30 hours
<option>31-35 hours
<option>36-40 hours
<option>41-45 hours
<option>46-50 hours
<option>50+ hours</select>
<input type="submit" onclick="return f_Submit();" value="Calculate score" class="button" title="Calculate score" />
 <br /><br /><strong>This questionnaire is based on the Oldenburg Burnout Inventory (OLBI) developed by Dr. Evangelia Demerouti.</strong>       
<!-- ================ FORM END ================ -->                    
                    </div><!-- div .(boxContent clearfix wysiwyg) -->
                    <div class="boxBottom"></div>
                </div><!-- div #mainContent .(box boxB clearfix) -->
            </div><!-- div .colB -->
            <!-- div class="colC"> </div -->

        </div><!-- div #content .clearfix -->

    </div><!-- div .pageWrapper -->

<!-- ======================== Footer ======================== -->
    <div id="footer">
        <div class="pageWrapper">
            <ul id="footerNav">

        </div> <!-- .pageWrapper -->
    </div> <!-- #footer -->
<!-- ======================== Footer End ======================== -->       
	</div><!-- /content -->

</div><!-- /page -->

Hopefully it is of use to someone!