Creating Limesurvey Plugins

Overview

The final product

Global settings
Survey settings
Debug respons

The basics

Note: Never develop plugins on production sites: once a plugin is activated it will break your entire Limesurvey application if you have errors in your code. You do not want your customers to see a broken site because you forgot a semicolon :)

Examining the example plugin

Extend pluginbase

class EventlyHook extends \ls\pluginmanager\PluginBase {

}
class EventlyHook extends PluginBase {

}

Registering the plugin

protected $storage = 'DbStorage';
static protected $description = 'Webhook for Limesurvey';
static protected $name = 'EventlyHook';

public function init() {
$this->subscribe('afterSurveyComplete');
$this->subscribe('beforeSurveySettings');
$this->subscribe('newSurveySettings');
}

Adding the general settings

protected $settings = array(
'bUse' => array(
'type' => 'select',
'options'=>array(
0=>'No',
1=>'Yes'
),
'default'=>1,
'label' => 'Send a hook for every survey by default?',
'help'=>'Overwritable in each Survey setting',
),
'sUrl' => array(
'type' => 'string',
'default'=>'https://zest.evently.nl/api/1/ping',
'label' => 'The default address to send the webhook to',
'help'=>'If you are using Zest, this should be https://zest.evently.nl/api/1/ping',
),
'sAuthToken' => array(
'type' => 'string',
'label' => 'Zest Platform API Token',
'help'=>'To get a token logon to your account and click on the Tokens tab',
),
'sServerToken' => array(
'type' => 'string',
'label' => 'Zest server id token',
'help'=>'To get a token logon to your account, go to your servers and copy the server id',
)

);

Adding survey specific settings

/**
* Add setting on survey level: send hook only for certain surveys / url setting per survey / auth code per survey / send user token / send question response
*/
public function beforeSurveySettings()
{
$oEvent = $this->event;
$oEvent->set("surveysettings.{$this->id}", array(
'name' => get_class($this),
'settings' => array(
'bUse' => array(
'type' => 'select',
'label' => 'Send a hook for this survey',
'options'=>array(
0=> 'No',
1=> 'Yes',
2=> 'Use site settings (default)'
),
'default'=>2,
'help'=>'Leave default to use global setting',
'current'=> $this->get('bUse','Survey',$oEvent->get('survey')),
),
'bUrlOverwrite' => array(
'type' => 'select',
'label' => 'Overwrite the global Hook Url',
'options'=>array(
0=> 'No',
1=> 'Yes'
),
'default'=>0,
'help'=>'Set to Yes if you want to use a specific URL for this survey',
'current'=> $this->get('bUrlOverwrite','Survey',$oEvent->get('survey')),
),
'sUrl' => array(
'type' => 'string',
'label' => 'The address to send the hook for this survey to:',
'help'=>'Leave blank to use global setting',
'current'=> $this->get('sUrl','Survey',$oEvent->get('survey')),
),
'bAuthTokenOverwrite' => array(
'type' => 'select',
'label' => 'Overwrite the global authorization token',
'options'=>array(
0=> 'No',
1=> 'Yes'
),
'default'=>0,
'help'=>'Set to Yes if you want to use a specific zest API token for this survey',
'current'=> $this->get('bAuthTokenOverwrite','Survey',$oEvent->get('survey')),
),
'sAuthToken' => array(
'type' => 'string',
'label' => 'Use a specific API Token for this survey (leave blank to use default)',
'help'=>'Leave blank to use default',
'current'=> $this->get('sAuthToken','Survey',$oEvent->get('survey')),
),
'bServerTokenOverwrite' => array(
'type' => 'select',
'label' => 'Overwrite the global server token',
'options'=>array(
0=> 'No',
1=> 'Yes'
),
'default'=>0,
'help'=>'Set to Yes if you want to use a specific Zest server-token for this survey',
'current'=> $this->get('bServerTokenOverwrite','Survey',$oEvent->get('survey')),
),
'sServerToken' => array(
'type' => 'string',
'label' => 'Zest server-token',
'help'=>'To get a token, log in to your account, go to your servers and copy the server token',
'current'=> $this->get('sServerToken','Survey',$oEvent->get('survey')),

),
'bSendToken' => array(
'type' => 'select',
'label' => 'Send the users\' token to the hook',
'options'=>array(
0=> 'No',
1=> 'Yes'
),
'default'=>0,
'help'=>'Set to Yes if you want to pass the users token along in the request',
'current'=> $this->get('bSendToken','Survey',$oEvent->get('survey')),
),
'sAnswersToSend' => array(
'type' => 'string',
'label' => 'Answers to send',
'help'=> 'Comma separated question codes of the answers you want to send along',
'current'=> $this->get('sAnswersToSend','Survey',$oEvent->get('survey')),
),
'bDebugMode' => array(
'type' => 'select',
'options' => array(
0 => 'No',
1 => 'Yes'),
'default' => 0,
'label' => 'Enable Debug Mode',
'help' => 'Enable debugmode to see what data is transmitted. Respondents will see this as well so you should turn this off for live surveys',
'current' => $this->get('bDebugMode', 'Survey', $oEvent->get('survey')),
)
),
));
}
),
));
}
/**
* Save the settings
*/
public function newSurveySettings()
{
$event = $this->event;
foreach ($event->get('settings') as $name => $value)
{
/* In order use survey setting, if not set, use global, if not set use default */
$default=$event->get($name,null,null,isset($this->settings[$name]['default'])?$this->settings[$name]['default']:NULL);
$this->set($name, $value, 'Survey', $event->get('survey'),$default);
}
}

Sending the hook

/**
* Send the webhook on completion of a survey
* @return array | response
*/
public function afterSurveyComplete()
{
//do nothing if: hook is disabled on surveylevel, or survey uses site settings and site defaults to no
$oEvent = $this->getEvent();
$sSurveyId = $oEvent->get('surveyId');
if($this->isHookDisabled($sSurveyId))
{
return;
}

//more code

}
private function isHookDisabled($sSurveyId)
{
return ($this->get('bUse','Survey',$sSurveyId)==0)||(($this->get('bUse','Survey',$sSurveyId)==2) && ($this->get('bUse',null,null,$this->settings['bUse'])==0));
}
$this->get('bUse','Survey',$sSurveyId)
/**
* Send the webhook on completion of a survey
* @return array | response
*/
public function afterSurveyComplete()
{
$time_start = microtime(true);
$oEvent = $this->getEvent();
$sSurveyId = $oEvent->get('surveyId');
if($this->isHookDisabled($sSurveyId))
{
return;
}

$url = ($this->get('bUrlOverwrite','Survey',$sSurveyId)==='1') ? $this->get('sUrl','Survey',$sSurveyId) : $this->get('sUrl',null,null,$this->settings['sUrl']);
$auth = ($this->get('bAuthTokenOverwrite','Survey',$sSurveyId)==='1') ? $this->get('sAuthToken','Survey',$sSurveyId) : $this->get('sAuthToken',null,null,$this->settings['sAuthToken']);
$serverToken = ($this->get('bServerTokenOverwrite','Survey',$sSurveyId)==='1') ? $this->get('sServerToken','Survey',$sSurveyId) : $this->get('sServerToken',null,null,$this->settings['sServerToken']);
$additionalFields = $this->getAdditionalFields($sSurveyId);

if(($this->get('bSendToken','Survey',$sSurveyId)==='1')||(count($additionalFields) > 0))
{
$responseId = $oEvent->get('responseId');
$response = $this->api->getResponse($sSurveyId, $responseId);
$sToken = $this->getTokenString($sSurveyId,$response);
$additionalAnswers = $this->getAdditionalAnswers($additionalFields, $response);
}

$parameters = array(
"survey" => $sSurveyId,
"token" => (isset($sToken)) ? $sToken : null,
"api_token" => $auth,
"server_key" => $serverToken,
"additionalFields" => "additionalFields" => ($additionalFields) ? json_encode($additionalAnswers) : null
);

$hookSent = $this->httpPost($url,$parameters);

$this->debug($parameters, $hookSent, $time_start);

return;
}


/**
* httpPost function http://hayageek.com/php-curl-post-get/
* creates and executes a POST request
* returns the output
*/
private function httpPost($url,$params)
{
$postData = '';
//create name value pairs seperated by &
foreach($params as $k => $v)
{
$postData .= $k . '='.$v.'&';
}
$postData = rtrim($postData, '&');
$fp = fopen(dirname(__FILE__).'/errorlog.txt', 'w');
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
curl_setopt($ch,CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, count($postData));
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$output=curl_exec($ch);
curl_close($ch);
return $output;
}

/**
* check if the hook should be sent
* returns a boolean
*/

private function isHookEnabled($sSurveyId)
{
return ($this->get('bUse','Survey',$sSurveyId)==0)||(($this->get('bUse','Survey',$sSurveyId)==2) && ($this->get('bUse',null,null,$this->settings['bUse'])==0));
}


/**
*
*
*/
private function getAdditionalFields($sSurveyId)
{
$additionalFieldsString = $this->get('sAnswersToSend','Survey',$sSurveyId);
if($additionalFieldsString != ''||$additionalFieldsString != null)
{
return explode(',',$this->get('sAnswersToSend','Survey',$sSurveyId));
}
return null;
}


private function debug($parameters, $hookSent, $time_start)
{
if($this->get('bDebugMode',null,null,$this->settings['bDebugMode'])==1)
{
echo '<pre>';
var_dump($parameters);
echo "<br><br> ----------------------------- <br><br>";
var_dump($hookSent);
echo "<br><br> ----------------------------- <br><br>";
echo 'Total execution time in seconds: ' . (microtime(true) - $time_start);
echo '</pre>';
}
}

private function getTokenString($sSurveyId,$response)
{
return ($this->get('bSendToken','Survey',$sSurveyId)==='1') ? $response['token'] : null;
}

private function getAdditionalAnswers($additionalFields, $response)
{
if($additionalFields)
{
$additionalAnswers = array();
foreach($additionalFields as $field)
{
$additionalAnswers[$field] = htmlspecialchars($response[$field]);
}
return $additionalAnswers;
}
return null;
}

Afterthoughts

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Stefan Verweij

Stefan Verweij

More from Medium

Migration From an Existing Database(2) | Laravel

How To Enable PHP Debugging with XDebug and Visual Studio Code (Windows and Xampp)

PHP,MYSQL..

Building an Amazon Giftcard giveaway using php and API-Platform