Blog

Customizing in LimeSurvey: A Developer’s Perspective

From door to door canvassing to electronic questionnaires, surveys are a tried and true way to collect customer information. There are several software solutions available for the conduct and management of electronic surveys, ranging from COTS products to open source implementations. Open source survey solutions allow for greater customization but often lack the support and updates that come with COTS products. LimeSurvey is a widely used open source survey project with an active community, giving survey designers and administrators enhanced flexibility in a feature rich platform. This post provides a first-hand account of how we customized and implemented LimeSurvey to support an enterprise application.

Question Type Customization

As of this writing, LimeSurvey contains 29 built in question types (https://manual.limesurvey.org/Question_types). These question types cover most of the common needs for a survey administrator or respondent. Occasionally, as was the case for our client, survey designers/administrators required additional unique question types to gather pertinent data. A question type is HTML that is populated with data points retrieved from the datastore and constructed in PHP.  JavaScript/JQuery can be added or referenced in a question type for front-end flexibility.

Because there was no documentation for adding new question types, we analyzed some of the LimeSurvey code to identify patterns, which we applied to all question creation and execution tasks. For example, we added a multi-select list question type using standard HTML. This involved:

  1. Defining the new question type with a unique identifier in the ‘Question.php’ model file
  2. Creating a JavaScript file within the ‘scripts’ directory that handles conditions for the question
  3. Creating a function in the ‘qanda_helper.php’ file which constructs HTML, retrieves database information, references the JavaScript file, and returns an object of the question ready to be rendered by the ‘SurveyRuntimeHelper.php’ file
  4. Defining the appropriate database type to store the new question in ‘activate_helper.php’
  5. Creating several references to the new question in the ‘em_manager_helper.php’ file

The result was a new question type that could be added to any survey, that is correctly rendered and stored in the database by the LimeSurvey platform.

Plugins

LimeSurvey contains plugins that “allow users to customize the functionality of their installation while still being able to benefit from regular software updates.”(https://manual.limesurvey.org/Plugins).  The existing Plugin architecture in LimeSurvey consists of event based function calls. There are several existing events (https://manual.limesurvey.org/Plugin_events) that are executed at various points in the code, whether in the admin interface or when respondents are surveyed. For example, if a developer wants custom code to run before a survey page is viewed by a respondent, he would use the ‘BeforeSurveyPage’ event.  This event is defined in the ‘index.php’ file:

 

$event = new PluginEvent('beforeSurveyPage');
$event->set('surveyId', $surveyid);
App()->getPluginManager()->dispatchEvent($event);

 

The $event->set(‘surveyId’, $surveyid);  function is where any variable can be passed to this event for use in the plugin file.  This should be defined in the ‘plugins’ directory. If a need arises that an existing plugin event cannot handle, LimeSurvey can be extended to add a new plugin event.As an example, suppose a client is interested in tracking when a survey is viewed or saved in a respondent audit log. The developer can identify several places in the ‘SurveyRuntimeHelper.php’ file where the Save action occurs. He can then name a new event ‘afterSavedResponse’ and add the following code:

 

$event = new PluginEvent('afterSavedResponse');
$sUsername = getenv('ENVIRONMENT_USERNAME');   // retrieve username from environement variable being passed from the webserver - this required separate implementation
$event->set('surveyID', $surveyid);
$event->set('username', $sUsername); //set the username retrived from the environment variable
App()->getPluginManager()->dispatchEvent($event);

 

The above snippet sends an event whenever a survey is saved. These events are handled by a plugin file. In this respondent audit log example, the developer could use the following file “TestTrackRespondents.php”:

<?php
class TestTrackRespondents extends \ls\pluginmanager\PluginBase {
   static protected $name = 'TestTrackRespondents';
   static protected $description = 'Test Plugin to Track Survey Respondents';

public function init() {
  $this->subscribe('beforeSurveyPage');
  $this->subscribe('afterSavedResponse');
  $this->subscribe('beforeActivate');
}
public function beforeSurveyPage()
{  $event = $this->getEvent();
  $oAutoLog = $this->api->newModel($this, 'log');
  $surveyID = $event->get('surveyId');
$oAutoLog->action='Viewed';
  $oAutoLog->surveyid = $surveyID;
  $oAutoLog->save();
}
public function afterSavedResponse()
{ $event = $this->getEvent();
  $oAutoLog = $this->api->newModel($this, 'log');
  $surveyID = $event->get('surveyID');
  $username = $event->get('username');
$oAutoLog->action='Saved';
  $oAutoLog->username = $username;
  $oAutoLog->surveyid = $surveyID;
  $oAutoLog->save();
}
public function beforeActivate()
{
  if (!$this->api->tableExists($this, 'log'))
  { $this->api->createTable($this, 'log', array('id'=>'pk',
          'created'=>'datetime',
          'username'=>'string',
          'surveyid'=>'integer',
          'action'=>'string'));
  }
}

The lines

'$this->subscribe('beforeSurveyPage');
$this->subscribe('afterSavedResponse')’

 

are are used to ‘subscribe’ to the events defined elsewhere in the code. Each of these events must then be defined as a function.  Each of these functions can then retrieve data passed to the event earlier $surveyID = $event->get(‘surveyID’); and this data can be used for a desired task. For our example of saving respondent information in an audit log, the developer would create a database table in public function beforeActivate() and use an object referencing that table $oAutoLog = $this->api->newModel($this, ‘log’) to save the data retrieved in the ‘’beforeSurveyPage’’ and ‘’afterSavedResponse’’ events. The data would be saved as log entries in a table initialized by the “beforeActivate” function shown above. The final step is to activate the Plugin in the admin interface.

 

plugin manager
Once active, the end result is an entry logged in a database table whenever a survey is opened or saved.

Closing thoughts

We’ve covered two cases of LimeSurvey customization. As an open source project, there are many additional ways the code can be extended to fill specific needs – from the more accessible, such as Plugins, to the more extensive, like adding a new question type. As with all customizations to a code base, it is important to keep in mind how the changes made will be affected by an upgrade to the underlying product. As LimeSurvey is an active project, this must be factored into any customization decision.

 

 

Categories: Blog, Software Development

Tags: , , ,

Uzair Madani
19 May, 2016