File: /var/www/apklausos/application/helpers/SurveyRuntimeHelper.php
<?php
use LimeSurvey\Models\Services\Quotas;
/**
* LimeSurvey
* Copyright (C) 2007-2011 The LimeSurvey Project Team / Carsten Schmitz
* All rights reserved.
* License: GNU/GPL License v2 or later, see LICENSE.php
* LimeSurvey is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*
*/
class SurveyRuntimeHelper
{
/**
* In the 2.x version of LimeSurvey and priors, the main run method was
* using a variable called redata fed via get_defined_vars. It was making
* hard to move piece of code to subfuntions.
* Those private variables are just a step to make easier refactorisation
* of this file, to have a global overview about what is set in this
* helper, and to move easily piece of code to new methods:
* The methods get/set the private variables, and defore calling
* get_defined_vars, variables are created from those private variables.
* It's just a first step. get_defined_vars should be removed, and most of
* the private variables here should be moved to the correct object:
* i.e: all the private variable concerning the survey should be moved to
* the survey model and replaced by a $oSurvey
*/
/**
* Parameters
* @var array
*/
private $param = [];
/**
* @var boolean
*/
private $previewquestion = false;
/**
* @var boolean
*/
private $previewgrp = false;
/**
* @var boolean
*/
private $preview = false;
/**
* Template configuration object (set in model TemplateConfiguration)
* @var Template|null
*/
private $oTemplate = null;
/**
* Path of the layout files in template
* @var string|null
*/
private $sTemplateViewPath = null;
/**
* @var int|null
*/
private $LEMsessid = null;
/**
* customizable debugging for Lime ExpressionScript Engine ; LEM_DEBUG_TIMING;
* (LEM_DEBUG_TIMING + LEM_DEBUG_VALIDATION_SUMMARY + LEM_DEBUG_VALIDATION_DETAIL);
* @var int
*/
private $LEMdebugLevel = 0;
/**
* true if used GetLastMoveResult to avoid generation of unneeded extra JavaScript
* @var boolean
*/
private $LEMskipReprocessing = false;
/**
* Survey settings:
* TODO: To respect object oriented design, all those "states" should be
* move to SurveyDynamic model, or its related models via relations.
* The only private variable here should be $oSurvey.
* Array returned by common_helper::getSurveyInfo(); (survey row + language settings );
* @var array|null
*/
private $aSurveyInfo = null;
/**
* The survey ID
* @var int|null
*/
private $iSurveyid = null;
/**
* True only when $_SESSION[$this->LEMsessid]['step'] == 0 ; Just a
* variable for a logic step ==> should not be a Class variable (for
* now, only here for the redata== get_defined_vars mess)
* @var boolean
*/
private $bShowEmptyGroup = false;
/**
* Group By Group, All in one, Question by question
* @var string|null
*/
private $sSurveyMode = null;
/**
* Few options comming from thissurvey, App->getConfig, LEM. Could be
* replaced by $oSurvey + relations ; the one coming from LEM and
* getConfig should be public variable on the surveyModel, set via
* public methods (active, allowsave, anonymized, assessments,
* datestamp, deletenonvalues, ipaddr, radix, refurl, savetimings,
* surveyls_dateformat, startlanguage, target, tempdir,timeadjust)
* @var array|null
*/
private $aSurveyOptions = null;
/**
* @var string|null
*/
private $sLangCode = null;
/**
* Contains the result of LimeExpressionManager::JumpTo() OR
* LimeExpressionManager::NavigateBackwards() OR
* NavigateForwards::LimeExpressionManager(). TODO: create a function
* LimeExpressionManager::MoveTo that call the right method
* @var array|boolean
*/
private $aMoveResult = false;
/**
* The move requested by user. Set by frontend_helper::getMove() from
* the POST request.
* @var int|null
*/
private $sMove = null;
/**
* Just a variable used to check if user submitted a survey while it's
* not finished. Just a variable for a logic step ==> should not be a Class
* variable (for now, only here for the redata== get_defined_vars mess)
* @var boolean
*/
private $bInvalidLastPage = false;
/**
* @var array|null
*/
private $aStepInfo = null;
// Popups: HTML of popus. If they are null, no popup. If they contains a string, a popup will be shown to participant.
// They could probably be merged.
private $backpopup = false; // "Please use the survey navigation buttons or index. It appears you attempted to use the browser back button to re-submit a page."
private $popup = false; // savedcontrol, mandatory_popup
private $notvalidated; // question validation error
// response
// TODO: To respect object oriented design, all those "states" should be move to Response model, or its related models via relations.
private $notanswered; // A global variable...Should be $oResponse->notanswered
private $filenotvalidated = false; // Same, but specific to file question type. (seems to be problematic by the past)
// strings
private $completed; // The string containing the completed message
// Boolean helpers
private $okToShowErrors; // true if we must show error in page : it's a submitted ($_POST) page and show the same page again for some reason
// Group
private $gid;
private $groupname;
private $groupdescription;
private $thissurvey;
/**
* Main function
*
* @param int $surveyid
* @param array $args
* @return void
*/
public function run($surveyid, $args)
{
// Survey settings
$this->setSurveySettings($surveyid, $args);
// Start rendering
$this->makeLanguageChanger(); // language changer can be used on any entry screen, so it must be set first
extract($args);
///////////////////////////////////////////////////////////
// 1: We check if token and/or captcha form shouls be shown
if (!isset($_SESSION[$this->LEMsessid]['step'])) {
$this->showTokenOrCaptchaFormsIfNeeded();
}
if (!$this->previewgrp && !$this->previewquestion) {
$this->initMove(); // main methods to init session, LEM, moves, errors, etc
$this->checkForDataSecurityAccepted(); // must be called after initMove to allow LEM to be initialized
if (EmCacheHelper::useCache()) {
$this->aSurveyInfo['emcache'] = true;
}
$this->checkQuotas(); // check quotas (then the process will stop here)
$this->displayFirstPageIfNeeded();
$this->saveAllIfNeeded();
$this->saveSubmitIfNeeded();
// TODO: move somewhere else
$this->setNotAnsweredAndNotValidated();
} else {
$this->setPreview();
}
$this->moveSubmitIfNeeded();
$this->setGroup();
$this->fixMaxStep();
//******************************************************************************************************
//PRESENT SURVEY
//******************************************************************************************************
$this->okToShowErrors = $okToShowErrors = (!($this->previewgrp || $this->previewquestion) && ($this->bInvalidLastPage || $_SESSION[$this->LEMsessid]['prevstep'] == $_SESSION[$this->LEMsessid]['step']));
Yii::app()->loadHelper('qanda');
setNoAnswerMode($this->aSurveyInfo);
// set tmpVars with global field for all EM done after
LimeExpressionManager::updateReplacementFields(getStandardsReplacementFields($this->aSurveyInfo));
//Iterate through the questions about to be displayed:
$inputnames = array();
$vpopup = $fpopup = false;
$upload_file = null;
$qanda = array();
foreach ($_SESSION[$this->LEMsessid]['grouplist'] as $gl) {
$gid = $gl['gid'];
$qnumber = 0;
if ($this->sSurveyMode != 'survey') {
$onlyThisGID = $this->aStepInfo['gid'];
if ($onlyThisGID != $gid) {
continue;
}
}
$upload_file = false;
if (isset($_SESSION[$this->LEMsessid]['fieldarray'])) {
foreach ($_SESSION[$this->LEMsessid]['fieldarray'] as $key => $ia) {
++$qnumber;
$ia[9] = $qnumber; // incremental question count;
// Make $qanda only for needed question $ia[10] is the randomGroup and $ia[5] the real group
if ((isset($ia[10]) && $ia[10] == $gid) || (!isset($ia[10]) && $ia[5] == $gid)) {
// In question by question mode, we only procceed current question
if ($this->sSurveyMode == 'question' && $ia[0] != $this->aStepInfo['qid']) {
continue;
}
// In group by group mode, we only procceed current group
if ($this->sSurveyMode == 'group' && $ia[5] != $this->aStepInfo['gid']) {
if (isset($_SESSION[$this->LEMsessid]['fieldmap-' . $this->iSurveyid . '-randMaster'])) {
// This is a randomized survey, don't continue.
} else {
continue;
}
}
$qidattributes = QuestionAttribute::model()->getQuestionAttributes($ia[0]);
if ($ia[4] != '*' && ($qidattributes === false || !isset($qidattributes['hidden']) || $qidattributes['hidden'] == 1)) {
continue;
}
//Get the answers/inputnames
// TMSW - can content of retrieveAnswers() be provided by LEM? Review scope of what it provides.
// TODO - retrieveAnswers is slow - queries database separately for each question. May be fixed in _CI or _YII ports, so ignore for now
list($plus_qanda, $plus_inputnames) = retrieveAnswers($ia);
if ($plus_qanda) {
$plus_qanda[] = $ia[4];
$plus_qanda[] = $ia[6]; // adds madatory identifyer for adding mandatory class to question wrapping div
// Add a finalgroup in qa array , needed for random attribute : TODO: find a way to have it in new quanda_helper in 2.1
if (isset($ia[10])) {
$plus_qanda['finalgroup'] = $ia[10];
} else {
$plus_qanda['finalgroup'] = $ia[5];
}
$qanda[] = $plus_qanda;
}
if ($plus_inputnames) {
$inputnames = addtoarray_single($inputnames, $plus_inputnames);
}
//Display the "mandatory" popup if necessary
// TMSW - get question-level error messages - don't call **_popup() directly
if ($okToShowErrors && $this->aStepInfo['mandViolation'] && empty(App()->request->getPost('mandSoft'))) {
list($mandatorypopup, $this->popup) = mandatory_popup($ia, $this->notanswered);
}
//Display the "validation" popup if necessary
if ($okToShowErrors && !$this->aStepInfo['valid'] && empty(App()->request->getPost('mandSoft'))) {
list($validationpopup, $vpopup) = validation_popup($ia, $this->notvalidated);
}
// Display the "file validation" popup if necessary
if ($okToShowErrors && ($this->filenotvalidated !== false)) {
list($filevalidationpopup, $fpopup) = file_validation_popup($ia, $this->filenotvalidated);
}
}
if ($ia[4] == "|") {
$upload_file = true;
}
} //end iteration
}
}
if ($this->sSurveyMode != 'survey' && isset($this->aSurveyInfo['showprogress']) && $this->aSurveyInfo['showprogress'] == 'Y') {
$totalSteps = $_SESSION[$this->LEMsessid]['totalsteps'] ?? 1;
$totalVisibleSteps = $_SESSION[$this->LEMsessid]['totalVisibleSteps'] ?? 0;
$step = $_SESSION[$this->LEMsessid]['step'] ?? 0;
$notRelevantSteps = $_SESSION[$this->LEMsessid]['notRelevantSteps'] ?? 0;
$hiddenSteps = $_SESSION[$this->LEMsessid]['hiddenSteps'] ?? 0;
if ($this->bShowEmptyGroup) {
$this->aSurveyInfo['progress']['currentstep'] = $totalSteps + 1;
$this->aSurveyInfo['progress']['total'] = $totalVisibleSteps ? $totalVisibleSteps : $totalSteps;
} else {
$this->aSurveyInfo['progress']['currentstep'] = $step - $notRelevantSteps - $hiddenSteps - 1;
$this->aSurveyInfo['progress']['total'] = $totalVisibleSteps ? $totalVisibleSteps - $notRelevantSteps : $totalSteps - $notRelevantSteps;
}
$progressValue = ($this->aSurveyInfo['progress']['currentstep']) / $this->aSurveyInfo['progress']['total'] * 100;
$progressValue = (int) round($progressValue);
$progressValue = max(0, min(100, $progressValue));
$this->aSurveyInfo['progress']['value'] = $progressValue;
/* String used in vanilla/views/subviews/header/progress_bar.twig : for autotranslation */
$this->aSurveyInfo['progress']['string'] = gT('You have completed %s%% of this survey');
}
/**
* create question index only in SurveyRuntime, not needed elsewhere, add it to GlobalVar : must be always set even if empty
*
*/
$this->aSurveyInfo['aQuestionIndex']['bShow'] = false;
if ($this->aSurveyInfo['questionindex']) {
if (!$this->previewquestion && !$this->previewgrp) {
$this->aSurveyInfo['aQuestionIndex']['items'] = LimeSurvey\Helpers\questionIndexHelper::getInstance()->getIndexItems();
if ($this->aSurveyInfo['questionindex'] > 1) {
$this->aSurveyInfo['aQuestionIndex']['type'] = 'full';
} else {
$this->aSurveyInfo['aQuestionIndex']['type'] = 'incremental';
}
}
if (isset($this->aSurveyInfo['aQuestionIndex']['items']) && count($this->aSurveyInfo['aQuestionIndex']['items']) > 0) {
$this->aSurveyInfo['aQuestionIndex']['bShow'] = true;
}
}
sendSurveyHttpHeaders();
Yii::app()->loadHelper('surveytranslator');
$this->aSurveyInfo['upload_file'] = $upload_file;
$hiddenfieldnames = $this->aSurveyInfo['hiddenfieldnames'] = implode("|", $inputnames);
App()->clientScript->registerScriptFile(App()->getConfig("generalscripts") . 'nojs.js', CClientScript::POS_BEGIN);
// Show question code/number
$this->aSurveyInfo['aShow'] = $this->getShowNumAndCode();
$aPopup = array(); // We can move this part where we want now
if ($this->backpopup != false) {
$aPopup[] = $this->backpopup; // If user click reload: no need other popup
} else {
if ($this->popup != false) {
$aPopup[] = $this->popup;
}
if ($vpopup != false) {
$aPopup[] = $vpopup;
}
if ($fpopup != false) {
$aPopup[] = $fpopup;
}
}
$this->aSurveyInfo['jPopup'] = json_encode($aPopup);
$this->aSurveyInfo['mandSoft'] = isset($this->aMoveResult['mandSoft']) ? $this->aMoveResult['mandSoft'] : false;
$this->aSurveyInfo['mandNonSoft'] = isset($this->aMoveResult['mandNonSoft']) ? $this->aMoveResult['mandNonSoft'] : false;
$this->aSurveyInfo['mandViolation'] = $this->aStepInfo['mandViolation'] && $this->okToShowErrors;
$this->aSurveyInfo['showPopups'] = $this->oTemplate != null ? $this->oTemplate->showpopups : false;
$aErrorHtmlMessage = $this->getErrorHtmlMessage();
$this->aSurveyInfo['errorHtml']['show'] = !empty($aErrorHtmlMessage) && $this->oTemplate->showpopups == 0;
$this->aSurveyInfo['errorHtml']['hiddenClass'] = $this->oTemplate->showpopups == 1 ? "ls-js-hidden " : "";
$this->aSurveyInfo['errorHtml']['messages'] = $aErrorHtmlMessage;
$_gseq = -1;
foreach ($_SESSION[$this->LEMsessid]['grouplist'] as $gl) {
++$_gseq;
$gid = $gl['gid'];
LimeExpressionManager::updateReplacementFields(array(
'GID' => $gid,
));
$aGroup = array();
if ($this->sSurveyMode != 'survey') {
$onlyThisGID = $this->aStepInfo['gid'];
if ($onlyThisGID != $gid) {
continue;
}
}
Yii::app()->setConfig('gid', $gid); // To be used in templaterplace in whole group. Attention : it's the actual GID (not the GID of the question)
$aGroup['class'] = "";
$gnoshow = LimeExpressionManager::GroupIsIrrelevantOrHidden($_gseq);
$redata = compact(array_keys(get_defined_vars()));
if ($gnoshow && !$this->previewgrp) {
$aGroup['class'] = ' ls-hidden';
}
$aGroup['name'] = $gl['group_name'];
LimeExpressionManager::updateReplacementFields(array(
'GROUPNAME' => $aGroup['name'],
));
$aGroup['gseq'] = $_gseq;
$showgroupinfo_global_ = getGlobalSetting('showgroupinfo');
$aSurveyinfo = getSurveyInfo($this->iSurveyid, App()->getLanguage());
// Look up if there is a global Setting to hide/show the Questiongroup => In that case Globals will override Local Settings
if (($aSurveyinfo['showgroupinfo'] == $showgroupinfo_global_) || ($showgroupinfo_global_ == 'choose')) {
$showgroupinfo_ = $aSurveyinfo['showgroupinfo'];
} else {
$showgroupinfo_ = $showgroupinfo_global_;
}
$showgroupdesc_ = $showgroupinfo_ == 'B' /* both */ || $showgroupinfo_ == 'D'; /* (group-) description */
$aGroup['showgroupinfo'] = $showgroupinfo_;
$aGroup['showdescription'] = (!$this->previewquestion && trim((string) $gl['description']) != "" && $showgroupdesc_);
$aGroup['description'] = $gl['description'];
// one entry per QID
foreach ($qanda as $qa) {
if ($gid == $qa[6] || ( isset($_SESSION[$this->LEMsessid]['fieldmap-' . $this->iSurveyid . '-randMaster']) && $this->sSurveyMode != 'survey' )) {
$qid = $qa[4];
$qinfo = LimeExpressionManager::GetQuestionStatus($qid);
$lemQuestionInfo = LimeExpressionManager::GetQuestionStatus($qid);
$lastgrouparray = explode("X", (string) $qa[7]);
$lastgroup = $lastgrouparray[0] . "X" . $lastgrouparray[1]; // id of the last group, derived from question id
$lastanswer = $qa[7];
if ($qinfo['hidden'] && $qinfo['info']['type'] != '*') {
continue; // skip this one
}
$question = $qa[0];
//===================================================================
// The following four variables offer the templating system the
// capacity to fully control the HTML output for questions making the
// above echo redundant if desired.
$question['sgq'] = $qa[7];
$question['aid'] = !empty($qinfo['info']['aid']) ? $qinfo['info']['aid'] : 0;
$question['sqid'] = !empty($qinfo['info']['sqid']) ? $qinfo['info']['sqid'] : 0;
//===================================================================
$aStandardsReplacementFields = array();
$this->aSurveyInfo['surveyls_url'] = $this->processString($this->aSurveyInfo['surveyls_url']);
LimeExpressionManager::updateReplacementFields(array(
'QID' => $qid,
'SGQ' => $qa[7],
'QUESTION_CODE' => $qa[0]['code'], // Always ?
));
// easier to understand for survey maker
$aGroup['aQuestions'][$qid]['qid'] = $qa[4];
$aGroup['aQuestions'][$qid]['gid'] = $qinfo['info']['gid'];
$aGroup['aQuestions'][$qid]['code'] = $qa[5];
$aGroup['aQuestions'][$qid]['type'] = $qinfo['info']['type'];
$aGroup['aQuestions'][$qid]['number'] = $qa[0]['number'];
$aGroup['aQuestions'][$qid]['SGQ'] = $qa[7];
$aGroup['aQuestions'][$qid]['text'] = LimeExpressionManager::ProcessString($qa[0]['text'], $qa[4], $aStandardsReplacementFields, 3, 1, false, true, false);
$aGroup['aQuestions'][$qid]['mandatory'] = $qa[0]['mandatory'];
$aGroup['aQuestions'][$qid]['class'] = $this->getCurrentQuestionClasses($qid);
$aGroup['aQuestions'][$qid]['input_error_class'] = $qa[0]['input_error_class'];
$aGroup['aQuestions'][$qid]['valid_message'] = LimeExpressionManager::ProcessString($qa[0]['valid_message']);
$aGroup['aQuestions'][$qid]['file_valid_message'] = $qa[0]['file_valid_message'];
$aGroup['aQuestions'][$qid]['man_message'] = $qa[0]['man_message'];
$aGroup['aQuestions'][$qid]['answer'] = $qa[1];
$aGroup['aQuestions'][$qid]['help']['show'] = (flattenText($lemQuestionInfo['info']['help'], true, true) != '');
$aGroup['aQuestions'][$qid]['help']['text'] = LimeExpressionManager::ProcessString($lemQuestionInfo['info']['help'], $qa[4], null, 3, 1, false, true, false);
$aGroup['aQuestions'][$qid] = $this->doBeforeQuestionRenderEvent($aGroup['aQuestions'][$qid]);
LimeExpressionManager::updateReplacementFields(array(
'QID' => null,
'SGQ' => null,
'QUESTION_CODE' => null,
));
}
$aGroup['show_last_group'] = $aGroup['show_last_answer'] = false;
$aGroup['lastgroup'] = $aGroup['lastanswer'] = '';
if (!empty($qanda)) {
if ($this->sSurveyMode == 'group') {
$aGroup['show_last_group'] = true;
$aGroup['lastgroup'] = $lastgroup;
}
if ($this->sSurveyMode == 'question') {
$aGroup['show_last_answer'] = true;
$aGroup['lastanswer'] = $lastanswer;
}
}
Yii::app()->setConfig('gid', '');
$this->aSurveyInfo['aGroups'][$gid] = $aGroup;
}
LimeExpressionManager::updateReplacementFields(array(
'GID' => null,
'GROUPNAME' => null,
));
}
/**
* ExpressionScript Engine Scrips and inputs
*/
$step = $_SESSION[$this->LEMsessid]['step'] ?? '';
$this->aSurveyInfo['EM']['ScriptsAndHiddenInputs'] = "<!-- emScriptsAndHiddenInputs -->";
/**
* Navigator
*/
$this->aSurveyInfo['aNavigator'] = array();
$this->aSurveyInfo['aNavigator']['show'] = $aNavigator['show'] = $this->aSurveyInfo['aNavigator']['save']['show'] = $this->aSurveyInfo['aNavigator']['load']['show'] = false;
if (!$this->previewgrp && !$this->previewquestion) {
$this->aSurveyInfo['aNavigator'] = getNavigatorDatas();
$this->aSurveyInfo['hiddenInputs'] = \CHtml::hiddenField('thisstep', $_SESSION[$this->LEMsessid]['step'], array('id' => 'thisstep'));
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('sid', $this->iSurveyid, array('id' => 'sid'));
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('start_time', time(), array('id' => 'start_time'));
$_SESSION[$this->LEMsessid]['LEMpostKey'] = $_POST['LEMpostKeyPreset'] ?? mt_rand();
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('LEMpostKey', $_SESSION[$this->LEMsessid]['LEMpostKey'], array('id' => 'LEMpostKey'));
/* Reset session with multiple tabs (show Token mismatch issue) , but only for not anonymous survey */
if (!empty($_SESSION[$this->LEMsessid]['token']) and $this->aSurveyInfo['anonymized'] != 'Y') {
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('token', $_SESSION[$this->LEMsessid]['token'], array('id' => 'token'));
}
}
// For "clear all" buttons
$this->aSurveyInfo['jYesNo'] = ls_json_encode(array('yes' => gT("Yes"), 'no' => gT("No")));
$this->aSurveyInfo['aLEM']['debugtimming']['show'] = false;
if (($this->LEMdebugLevel & LEM_DEBUG_TIMING) == LEM_DEBUG_TIMING) {
$this->aSurveyInfo['aLEM']['debugtimming']['show'] = true;
$this->aSurveyInfo['aLEM']['debugtimming']['script'] = LimeExpressionManager::GetDebugTimingMessage();
}
$this->aSurveyInfo['aLEM']['debugvalidation']['show'] = false;
if (($this->LEMdebugLevel & LEM_DEBUG_VALIDATION_SUMMARY) == LEM_DEBUG_VALIDATION_SUMMARY) {
$this->aSurveyInfo['aLEM']['debugvalidation']['show'] = true;
$this->aSurveyInfo['aLEM']['debugvalidation']['message'] = $this->aMoveResult['message'];
}
$this->aSurveyInfo['include_content'] = 'main';
Yii::app()->twigRenderer->renderTemplateFromFile("layout_global.twig", array(
'oSurvey' => Survey::model()->findByPk($this->iSurveyid),
'aSurveyInfo' => $this->aSurveyInfo,
'step' => $step,
'LEMskipReprocessing' => $this->LEMskipReprocessing,
), false);
}
/**
* @return array<string, boolean>
*/
public function getShowNumAndCode()
{
$showqnumcode_global_ = getGlobalSetting('showqnumcode');
$showqnumcode_survey_ = $this->aSurveyInfo['showqnumcode'];
// Check global setting to see if survey level setting should be applied
if ($showqnumcode_global_ == 'choose') {
// Use survey level settings
$showqnumcode_ = $showqnumcode_survey_; //B, N, C, or X
} else {
// Use global setting
$showqnumcode_ = $showqnumcode_global_; //both, number, code, or none
}
$aShow = [];
switch ($showqnumcode_) {
// Both
case 'both':
case 'B':
$aShow['question_code'] = true;
$aShow['question_number'] = true;
break;
// Number only
case 'number':
case 'N':
$aShow['question_code'] = false;
$aShow['question_number'] = true;
break;
// Code only
case 'code':
case 'C':
$aShow['question_code'] = true;
$aShow['question_number'] = false;
break;
// Neither
case 'none':
case 'X':
default:
$aShow['question_code'] = false;
$aShow['question_number'] = false;
break;
}
return $aShow;
}
/**
* Init session/params values depending of user moves
*
* - It init the needed variables for navigation: initFirstStep, initTotalAndMaxSteps, setMoveResult
* - Then perform all the needed checks before moving:
* + did the participant used browser navigation?
* + did he pressed clear cancel, is he a confirmed quota?
* + Is the previous step set?
* + Is the survey finished?
* + Are all the answer validated? (like: participant didn't answered to a mandatory question)
*/
private function initMove()
{
$this->initFirstStep(); // If it's the first time user load this survey, will init session and LEM
$this->initTotalAndMaxSteps();
$this->checkIfUseBrowserNav(); // Check if user used browser navigation, or relaoded page
if ($this->sMove != 'clearcancel' && $this->sMove != 'confirmquota') {
$this->checkPrevStep(); // Check if prev step is set, else set it
$this->setMoveResult();
$this->checkClearCancel();
$this->setPrevStep();
$this->checkIfFinished();
$this->setStep();
// CHECK UPLOADED FILES
// TMSW - Move this into LEM::NavigateForwards?
$this->filenotvalidated = checkUploadedFileValidity($this->iSurveyid, $this->sMove);
//SEE IF THIS GROUP SHOULD DISPLAY
if ($_SESSION[$this->LEMsessid]['step'] == 0) {
$this->bShowEmptyGroup = true;
}
}
}
/**
* Now it's ok ^^
*/
private function setArgs()
{
if ($this->sMove == "movesubmit") {
$aSurveyInfo = getSurveyInfo($this->iSurveyid, App()->getLanguage());
$this->aSurveyInfo = $aSurveyInfo;
if ($this->aSurveyInfo['refurl'] == "Y") {
//Only add this if it doesn't already exist
if ($this->LEMsessid && !in_array("refurl", $_SESSION[$this->LEMsessid]['insertarray'])) {
$_SESSION[$this->LEMsessid]['insertarray'][] = "refurl";
}
}
resetTimers();
//Before doing the "templatereplace()" function, check the $this->aSurveyInfo['url']
//field for limereplace stuff, and do transformations!
$this->aSurveyInfo['surveyls_url'] = passthruReplace($this->aSurveyInfo['surveyls_url'], $this->aSurveyInfo);
$this->aSurveyInfo['surveyls_url'] = templatereplace((string) $this->aSurveyInfo['surveyls_url'], array(), $redata, 'URLReplace', false, null, array(), true); // to do INSERTANS substitutions
}
}
/**
* Retrieve the survey format (mode?)
* TODO: move to survey model
*
* @return string
*/
private function setSurveyMode()
{
switch ($this->aSurveyInfo['format']) {
case "A": //All in one
$this->sSurveyMode = 'survey';
break;
default:
case "S": //One at a time
$this->sSurveyMode = 'question';
break;
case "G": //Group at a time
$this->sSurveyMode = 'group';
break;
}
}
/**
* Retrieve the radix
* @return string
*/
private function getRadix()
{
$radix = getRadixPointData($this->aSurveyInfo['surveyls_numberformat']);
$radix = $radix['separator'];
return $radix;
}
/**
* Retreives dew options comming from thissurvey, App->getConfig, LEM.
* TODO: move to survey model
*
*/
private function setSurveyOptions()
{
global $clienttoken;
$radix = $this->getRadix();
$timeadjust = Yii::app()->getConfig("timeadjust");
$this->aSurveyOptions = array(
'active' => ($this->aSurveyInfo['active'] == 'Y'),
'allowsave' => ($this->aSurveyInfo['allowsave'] == 'Y'),
'anonymized' => ($this->aSurveyInfo['anonymized'] != 'N'),
'assessments' => ($this->aSurveyInfo['assessments'] == 'Y'),
'datestamp' => ($this->aSurveyInfo['datestamp'] == 'Y'),
'deletenonvalues' => Yii::app()->getConfig('deletenonvalues'),
'hyperlinkSyntaxHighlighting' => (($this->LEMdebugLevel & LEM_DEBUG_VALIDATION_SUMMARY) == LEM_DEBUG_VALIDATION_SUMMARY), // TODO set this to true if in admin mode but not if running a survey
'ipaddr' => ($this->aSurveyInfo['ipaddr'] == 'Y'),
'radix' => $radix,
'refurl' => (($this->aSurveyInfo['refurl'] == "Y" && isset($_SESSION[$this->LEMsessid]['refurl'])) ? $_SESSION[$this->LEMsessid]['refurl'] : null),
'savetimings' => ($this->aSurveyInfo['savetimings'] == "Y"),
'surveyls_dateformat' => $this->aSurveyInfo['surveyls_dateformat'] ?? 1,
'startlanguage' => (App()->language ?? $this->aSurveyInfo['language']),
'target' => Yii::app()->getConfig('uploaddir') . DIRECTORY_SEPARATOR . 'surveys' . DIRECTORY_SEPARATOR . $this->aSurveyInfo['sid'] . DIRECTORY_SEPARATOR . 'files' . DIRECTORY_SEPARATOR,
'tempdir' => Yii::app()->getConfig('tempdir') . DIRECTORY_SEPARATOR,
'timeadjust' => $timeadjust,
'token' => $clienttoken,
);
}
/**
* If it's the first time the survey is loaded:
* - Init session, randomization and filed array
* - Check surveyid coherence
* - Init $LEM states.
* - Decide if Welcome page should be shown
*/
private function initFirstStep()
{
// First time the survey is loaded
if (!isset($_SESSION[$this->LEMsessid]['step'])) {
// Init session, randomization and filed array
buildsurveysession($this->iSurveyid);
$fieldmap = randomizationGroupsAndQuestions($this->iSurveyid);
initFieldArray($this->iSurveyid, $fieldmap);
// Check surveyid coherence
if ($this->iSurveyid != LimeExpressionManager::getLEMsurveyId()) {
LimeExpressionManager::SetDirtyFlag();
}
// Init $LEM states.
LimeExpressionManager::StartSurvey($this->iSurveyid, $this->sSurveyMode, $this->aSurveyOptions, false, $this->LEMdebugLevel);
$_SESSION[$this->LEMsessid]['step'] = 0;
// Welcome page.
if ($this->sSurveyMode == 'survey') {
LimeExpressionManager::JumpTo(1, false, false, true);
} elseif (isset($this->aSurveyInfo['showwelcome']) && $this->aSurveyInfo['showwelcome'] == 'N') {
$this->aMoveResult = LimeExpressionManager::NavigateForwards();
$_SESSION[$this->LEMsessid]['step'] = 1;
}
} elseif ($this->iSurveyid != LimeExpressionManager::getLEMsurveyId()) {
$this->initDirtyStep();
}
}
/**
* If a step is requested, but the survey ID in the session is different from the requested one
* It reload the needed infos for the requested survey and jump to the requested step.
*/
private function initDirtyStep()
{
//$_SESSION[$this->LEMsessid]['step'] can not be less than 0, fix it always #09772
$_SESSION[$this->LEMsessid]['step'] = $_SESSION[$this->LEMsessid]['step'] < 0 ? 0 : $_SESSION[$this->LEMsessid]['step'];
LimeExpressionManager::StartSurvey($this->iSurveyid, $this->sSurveyMode, $this->aSurveyOptions, true, $this->LEMdebugLevel);
if (isset($_SESSION[$this->LEMsessid]['LEMtokenResume'])) {
/* Move to max step in all condition with force */
if (isset($_SESSION[$this->LEMsessid]['maxstep']) && $_SESSION[$this->LEMsessid]['maxstep'] > $_SESSION[$this->LEMsessid]['step']) {
LimeExpressionManager::SetRelevanceTo($_SESSION[$this->LEMsessid]['maxstep']);
LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['maxstep'], false, false);
} else {
LimeExpressionManager::SetRelevanceTo($_SESSION[$this->LEMsessid]['step']);
}
unset($_SESSION[$this->LEMsessid]['LEMtokenResume']);
}
LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, false);
}
/**
* Seems to be a quick fix to avoid the total and max steps to be null...
*/
private function initTotalAndMaxSteps()
{
if (!isset($_SESSION[$this->LEMsessid]['totalsteps'])) {
$_SESSION[$this->LEMsessid]['totalsteps'] = 0;
}
if (!isset($_SESSION[$this->LEMsessid]['maxstep'])) {
$_SESSION[$this->LEMsessid]['maxstep'] = 0;
}
}
/**
* It checks if user used the browser navigation (prev, next, reload page etc)
* and feed te backpopup variable if needed
*/
private function checkIfUseBrowserNav()
{
// Possibility to suppress the warning when Ajax is used to get survey content.
if (isset($_GET['ignorebrowsernavigationwarning'])
|| isset($_SESSION[$this->LEMsessid]['ignorebrowsernavigationwarning'])
) {
$_SESSION[$this->LEMsessid]['ignorebrowsernavigationwarning'] = 1;
return;
}
// retrieve datas from local variable
if (isset($_SESSION[$this->LEMsessid]['LEMpostKey']) && App()->request->getPost('LEMpostKey', $_SESSION[$this->LEMsessid]['LEMpostKey']) != $_SESSION[$this->LEMsessid]['LEMpostKey']) {
// then trying to resubmit (e.g. Next, Previous, Submit) from a cached copy of the page
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, false, true); // We JumpTo current step without saving: see bug #11404
if (isset($this->aMoveResult['seq']) && App()->request->getPost('thisstep', $this->aMoveResult['seq']) == $this->aMoveResult['seq']) {
/* then pressing F5 or otherwise refreshing the current page, which is OK
* Seems OK only when movenext but not with move by index : same with $this->aMoveResult = LimeExpressionManager::GetLastMoveResult(true);
*/
$this->LEMskipReprocessing = true;
$this->sMove = "movenext"; // so will re-display the survey
} else {
// trying to use browser back buttons, which may be disallowed if no 'previous' button is present
$this->LEMskipReprocessing = true;
$this->sMove = "movenext"; // so will re-display the survey
$this->bInvalidLastPage = true;
$this->backpopup = gT("Please use the survey navigation buttons or index. It appears you attempted to use the browser back button to re-submit a page."); // TODO: twig
}
}
}
/**
* Check quotas
*/
private function checkQuotas()
{
/* quota submitted */
if ($this->sMove == 'confirmquota') {
Quotas::checkCompletedQuota($this->iSurveyid);
}
/* quota submitted */
if ($this->sMove == 'returnfromquota') {
LimeExpressionManager::JumpTo($this->param['thisstep']);
}
}
/**
* Check if the move is clearcancel or confirmquota
*/
private function checkClearCancel()
{
if ($this->sMove == "clearcancel") {
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, false);
}
}
/**
* Set prev step in session depending on move type
* If not in a specific page, prevstep stock the value of step just before it get updated
*/
private function setPrevStep()
{
if (isset($this->sMove)) {
if (!in_array($this->sMove, array("clearall", "changelang", "saveall", "reload"))) {
$_SESSION[$this->LEMsessid]['prevstep'] = $_SESSION[$this->LEMsessid]['step'];
} else {
// Accepted $move without error
$_SESSION[$this->LEMsessid]['prevstep'] = $this->sMove;
}
} else {
// $_SESSION[$this->LEMsessid]['prevstep'] = $_SESSION[$LEMsessid]['step']-1; // Is this needed ?
}
if (!isset($_SESSION[$this->LEMsessid]['prevstep'])) {
$_SESSION[$this->LEMsessid]['prevstep'] = $_SESSION[$this->LEMsessid]['prevstep'] - 1; // this only happens on re-load
}
}
/**
* Define prev step if not set in session.
*/
private function checkPrevStep()
{
if (!isset($_SESSION[$this->LEMsessid]['prevstep'])) {
$_SESSION[$this->LEMsessid]['prevstep'] = $_SESSION[$this->LEMsessid]['step'] - 1; // this only happens on re-load
}
}
/**
* Set the moveResult variable, depending on the user move request
*/
private function setMoveResult()
{
// retrieve datas from local variable
if (isset($_SESSION[$this->LEMsessid]['LEMtokenResume'])) {
LimeExpressionManager::StartSurvey($this->aSurveyInfo['sid'], $this->sSurveyMode, $this->aSurveyOptions, false, $this->LEMdebugLevel);
/* Move to max step in all condition with force */
if (isset($_SESSION[$this->LEMsessid]['maxstep']) && $_SESSION[$this->LEMsessid]['maxstep'] > $_SESSION[$this->LEMsessid]['step']) {
LimeExpressionManager::SetRelevanceTo($_SESSION[$this->LEMsessid]['maxstep']);
LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['maxstep'], false, false);
} else {
LimeExpressionManager::SetRelevanceTo($_SESSION[$this->LEMsessid]['step']);
}
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, false); // if late in the survey, will re-validate contents, which may be overkill
unset($_SESSION[$this->LEMsessid]['LEMtokenResume']);
} elseif (!$this->LEMskipReprocessing) {
//Move current step ###########################################################################
if ($this->sMove == 'moveprev' && ($this->aSurveyInfo['allowprev'] == 'Y' || $this->aSurveyInfo['questionindex'] > 0)) {
$this->aMoveResult = LimeExpressionManager::NavigateBackwards();
if ($this->aMoveResult['at_start']) {
$_SESSION[$this->LEMsessid]['step'] = 0;
$this->aMoveResult = false; // so display welcome page again
}
}
if ($this->sMove == "movenext") {
$this->aMoveResult = LimeExpressionManager::NavigateForwards();
}
if (($this->sMove == 'movesubmit')) {
if ($this->sSurveyMode == 'survey') {
$this->aMoveResult = LimeExpressionManager::NavigateForwards();
} else {
// may be submitting from the navigation bar, in which case need to process all intervening questions
// in order to update equations and ensure there are no intervening relevant mandatory or relevant invalid questions
if ($this->aSurveyInfo['questionindex'] == 2) {
// Save actual page ,
LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, true, true);
// Review whole before set finished to true (see #09906), index==1 don't need it because never force move
LimeExpressionManager::JumpTo(0, false, false, true); // no preview, no post and force
}
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['totalsteps'] + 1, false);
}
}
if ($this->sMove == 'clearall') {
$this->manageClearAll();
}
if ($this->sMove == 'changelang') {
// jump to current step using new language, processing POST values
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, true, true, true); // do process the POST data
}
if (isNumericInt($this->sMove) && $this->aSurveyInfo['questionindex'] == 1) {
$this->sMove = (int) $this->sMove;
if ($this->sMove > 0 && (($this->sMove <= $_SESSION[$this->LEMsessid]['step']) || (isset($_SESSION[$this->LEMsessid]['maxstep']) && $this->sMove <= $_SESSION[$this->LEMsessid]['maxstep']))) {
$this->aMoveResult = LimeExpressionManager::JumpTo($this->sMove, false);
}
} elseif (isNumericInt($this->sMove) && $this->aSurveyInfo['questionindex'] == 2) {
$this->sMove = (int) $this->sMove;
$this->aMoveResult = LimeExpressionManager::JumpTo($this->sMove, false, true, true);
}
if (!$this->aMoveResult && !($this->sSurveyMode != 'survey' && $_SESSION[$this->LEMsessid]['step'] == 0)) {
// Just in case not set via any other means, but don't do this if it is the welcome page
/* GetLastMoveResult reset substitutionNum in EM core if param is true, this break in all in one mode (see #13725) */
/* Then don't reset substitutionNum since seems some LimeExpressionManager::ProcessString already happen*/
$this->aMoveResult = LimeExpressionManager::GetLastMoveResult(false);
$this->LEMskipReprocessing = true;
}
}
}
/**
* Test if the the moveresult is finished, to decide to set the new $this->sMove value
*/
private function checkIfFinished()
{
// Reload at first page (welcome after click previous fill an empty $this->aMoveResult array
if ($this->aMoveResult && isset($this->aMoveResult['seq'])) {
// With complete index, we need to revalidate whole group bug #08806. It's actually the only mode where we JumpTo with force
// we already done if move == 'movesubmit', don't do it again
if ($this->aMoveResult['finished'] == true && $this->sMove != 'movesubmit' && $this->thissurvey['questionindex'] == 2) {
/* Issue #14855 : always reset submitdate of current response to null */
if (!empty($_SESSION[$this->LEMsessid]['srid'])) {
$oSurveyResponse = SurveyDynamic::model($this->iSurveyid)->findByAttributes(['id' => $_SESSION[$this->LEMsessid]['srid']]);
$oSurveyResponse->submitdate = null;
$oSurveyResponse->save();
}
/* Save current page */
LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, true, true);
/* Move to start */
LimeExpressionManager::JumpTo(0, false, false, true);
/* Try to move next again */
/* This reset $this->aMoveResult['finished'] to false if have an error */
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['totalsteps'] + 1, false, false, false); // no preview, no save data and NO force
if (!$this->aMoveResult['mandViolation'] && $this->aMoveResult['valid'] && empty($this->aMoveResult['invalidSQs'])) {
$this->aMoveResult['finished'] = true;
}
}
if ($this->aMoveResult['finished'] == true || (!empty($this->aMoveResult['mandSoft']) && App()->request->getPost('mandSoft') == 'movesubmit')) {
$this->sMove = 'movesubmit';
$this->aMoveResult['finished'] = true;
}
if ($this->sMove == "movesubmit" && $this->aMoveResult['finished'] == false) {
// then there are errors, so don't finalize the survey
$this->sMove = "movenext"; // so will re-display the survey
$this->bInvalidLastPage = true;
}
}
}
/**
* Increase step in session
*/
private function setStep()
{
if ($this->aMoveResult && isset($this->aMoveResult['seq'])) {
if ($this->aMoveResult['finished'] != true) {
$_SESSION[$this->LEMsessid]['step'] = $this->aMoveResult['seq'] + 1; // step is index base 1
$_SESSION[$this->LEMsessid]['notRelevantSteps'] = $this->aMoveResult['notRelevantSteps'] ?? 0;
$_SESSION[$this->LEMsessid]['hiddenSteps'] = $this->aMoveResult['hiddenSteps']?? 0;
$this->aStepInfo = LimeExpressionManager::GetStepIndexInfo($this->aMoveResult['seq']);
}
}
}
/**
* Display the first page if needed
*/
private function displayFirstPageIfNeeded()
{
$bDisplayFirstPage = ($this->sSurveyMode != 'survey' && $_SESSION[$this->LEMsessid]['step'] == 0);
$this->aSurveyInfo['move'] = $this->sMove ?? '';
if ($this->sSurveyMode == 'survey' || $bDisplayFirstPage) {
//Failsave to have a general standard value
if (empty($this->aSurveyInfo['datasecurity_notice_label'])) {
$this->aSurveyInfo['datasecurity_notice_label'] = gT("To continue please first accept our survey privacy policy.");
}
if (empty($this->aSurveyInfo['datasecurity_error'])) {
$this->aSurveyInfo['datasecurity_error'] = gT("We are sorry but you can't proceed without first agreeing to our survey privacy policy.");
}
$this->aSurveyInfo['datasecurity_notice_label'] = Survey::replacePolicyLink($this->aSurveyInfo['datasecurity_notice_label'], $this->aSurveyInfo['sid']);
}
if ($bDisplayFirstPage) {
$_SESSION[$this->LEMsessid]['test'] = time();
display_first_page($this->thissurvey, $this->aSurveyInfo);
Yii::app()->end(); // So we can still see debug messages
}
}
private function checkForDataSecurityAccepted()
{
$this->aSurveyInfo['datasecuritynotaccepted'] = false;
if ($this->param['thisstep'] === '0' && Survey::model()->findByPk($this->aSurveyInfo['sid'])->showsurveypolicynotice > 0) {
$data_security_accepted = App()->request->getPost('datasecurity_accepted', false);
$move_step = App()->request->getPost('move', false);
if ($data_security_accepted !== 'on' && ($move_step !== 'default')) {
$_SESSION[$this->LEMsessid]['step'] = 0;
$this->aSurveyInfo['datasecuritynotaccepted'] = true;
$this->displayFirstPageIfNeeded(true);
Yii::app()->end(); // So we can still see debug messages
}
}
}
/**
* Perform save all if user asked for it
*/
public function saveAllIfNeeded()
{
// save current survey data when clicking on "Load unfinished survey"
if (Yii::app()->request->getPost('loadall') && Yii::app()->request->getPost('loadall') == 'loadall') {
if ($this->iSurveyid === null) {
$this->iSurveyid = Yii::app()->request->getPost('sid', 0);
}
if ($this->aSurveyInfo === null) {
$this->aSurveyInfo = getSurveyInfo($this->iSurveyid, App()->getLanguage());
}
$this->LEMsessid = 'survey_' . $this->iSurveyid;
if ($this->aSurveyInfo['active'] == "Y" && isset($_SESSION[$this->LEMsessid])) {
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false); // by jumping to current step, saves data so far
}
return;
}
if (!Yii::app()->request->getPost('saveall')) {
return;
}
// Don't test if save is allowed … maybe must be done
if ($this->aSurveyInfo['active'] == "Y") {
$bAnonymized = $this->aSurveyInfo["anonymized"] == 'Y';
$bTokenAnswerPersitance = $this->aSurveyInfo['tokenanswerspersistence'] == 'Y' && $this->iSurveyid != null && tableExists('tokens_' . $this->iSurveyid);
// must do this here to process the POSTed values
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false); // by jumping to current step, saves data so far
if (!isset($_SESSION[$this->LEMsessid]['scid']) && (!$bTokenAnswerPersitance || $bAnonymized)) {
if (!isset($this->aSurveyInfo['EM']['ScriptsAndHiddenInputs'])) {
$this->aSurveyInfo['EM']['ScriptsAndHiddenInputs'] = "<!-- emScriptsAndHiddenInputs -->";
}
// There is a submit button at the beginning of the form (see start_form.twig) setting 'move' to 'default'.
// If the form is submitted without clicking a different button, the 'move' attribute is sent in the POST
// and causes the processing to fail. So we need to add a hidden field to override that.
$this->aSurveyInfo['EM']['ScriptsAndHiddenInputs'] .= CHtml::hiddenField('move', '');
Yii::import("application.libraries.Save");
$cSave = new Save();
// $cSave->showsaveform($this->aSurveyInfo['sid']); // generates a form and exits, awaiting input
$this->aSurveyInfo['aSaveForm'] = $cSave->getSaveFormDatas($this->aSurveyInfo['sid']);
$this->aSurveyInfo['include_content'] = 'save';
$this->aSurveyInfo['trackUrlPageName'] = 'save';
Yii::app()->twigRenderer->renderTemplateFromFile("layout_global.twig", array('oSurvey' => Survey::model()->findByPk($this->iSurveyid), 'aSurveyInfo' => $this->aSurveyInfo), false);
} else {
// Intentional retest of all conditions to be true, to make sure we do have tokens and surveyid
// Now update lastpage to $_SESSION[$this->LEMsessid]['step'] in SurveyDynamic, otherwise we land on
// the previous page when we return.
$iResponseID = $_SESSION[$this->LEMsessid]['srid'];
$oResponse = SurveyDynamic::model($this->iSurveyid)->findByPk($iResponseID);
$oResponse->lastpage = $_SESSION[$this->LEMsessid]['step'];
if ($oResponse->save()) {
$this->aSurveyInfo['saved'] = array(
'success' => true,
'title' => gT('Success'),
'text' => gT("Your responses were successfully saved.")
);
} else {
$this->aSurveyInfo['saved'] = array(
'success' => false,
'title' => gT('Error'),
'text' => gT("Your responses were not saved. Please contact the survey administrator.")
);
}
$oResponse->save();
}
} else {
$this->aSurveyInfo['saved'] = array(
'success' => false,
'title' => gT('Warning'),
'text' => gT("Saving responses is disabled if survey is not activated.")
);
}
}
/**
* perform save submit if asked by user
* called from save survey
*/
private function saveSubmitIfNeeded()
{
if ($this->aSurveyInfo['active'] == "Y" && Yii::app()->request->getParam('savesubmit')) {
// The response from the save form
// CREATE SAVED CONTROL RECORD USING SAVE FORM INFORMATION
Yii::import("application.libraries.Save");
$cSave = new Save();
// Try to save survey
$aResult = $cSave->saveSurvey();
if (!$aResult['success']) {
$aPopup = $this->popup = $aResult['aSaveErrors'];
} else {
$aPopup = $this->popup = array($aResult['message']);
}
Yii::app()->clientScript->registerScript('startPopup', "LSvar.startPopups=" . json_encode($aPopup) . ";", LSYii_ClientScript::POS_END);
Yii::app()->clientScript->registerScript('showStartPopups', "window.templateCore.showStartPopups();", LSYii_ClientScript::POS_POSTSCRIPT);
// reshow the form if there is an error
if (!empty($aResult['aSaveErrors'])) {
$this->aSurveyInfo['aSaveForm'] = $cSave->getSaveFormDatas($this->aSurveyInfo['sid']);
$this->aSurveyInfo['include_content'] = 'save';
Yii::app()->twigRenderer->renderTemplateFromFile("layout_global.twig", array('oSurvey' => Survey::model()->findByPk($this->iSurveyid), 'aSurveyInfo' => $this->aSurveyInfo), false);
}
$this->aMoveResult = LimeExpressionManager::GetLastMoveResult(true);
$this->LEMskipReprocessing = true;
}
}
/**
* check mandatory questions if necessary
* CHECK IF ALL CONDITIONAL MANDATORY QUESTIONS THAT APPLY HAVE BEEN ANSWERED
*/
private function setNotAnsweredAndNotValidated()
{
global $notanswered;
// TODO: check that line:
$this->notvalidated = $notanswered;
$this->notanswered = $notanswered;
if (!$this->aMoveResult['finished']) {
$unansweredSQList = $this->aMoveResult['unansweredSQs']; // A list of the unanswered responses created via the global variable $notanswered. Should be $oResponse->unanswereds
if (strlen((string) $unansweredSQList) > 0) {
$this->notanswered = explode('|', (string) $unansweredSQList);
} else {
$this->notanswered = array();
}
//CHECK INPUT
$invalidSQList = $this->aMoveResult['invalidSQs']; // Invalid answered, fed from $moveResult(LEM). Its logic should be in Response model.
if (strlen((string) $invalidSQList) > 0) {
$this->notvalidated = explode('|', (string) $invalidSQList);
} else {
$this->notvalidated = array();
}
}
}
/**
* Perform submit if asked by user
*/
private function moveSubmitIfNeeded()
{
if ($this->sMove == "movesubmit") {
/* Put active in var for next part */
$surveyActive = ($this->aSurveyInfo['active'] == "Y");
$oSurvey = Survey::model()->findByPk($this->iSurveyid);
// Parts needed for active and unactive
//Check for assessments
$this->aSurveyInfo['aAssessments']['show'] = false;
if ($this->aSurveyInfo['assessments'] == "Y") {
$this->aSurveyInfo['aAssessments'] = doAssessment($this->iSurveyid, false);
}
// End text
if (trim(str_replace(array('<p>', '</p>'), '', (string) $this->aSurveyInfo['surveyls_endtext'])) == '') {
$this->aSurveyInfo['aCompleted']['showDefault'] = true;
} else {
$this->aSurveyInfo['aCompleted']['showDefault'] = false;
// NOTE: If needed : move keywords from templatereplace to getStandardsReplacementFields function
//$this->aSurveyInfo['aCompleted']['sEndText'] = templatereplace($this->aSurveyInfo['surveyls_endtext'], array(), $redata, 'SubmitAssessment', false, null, array(), true);
$this->aSurveyInfo['aCompleted']['sEndText'] = $this->processString($this->aSurveyInfo['surveyls_endtext'], 3, 1);
}
//Update the token if needed and send a confirmation email
if ($surveyActive && $oSurvey->getHasTokensTable()) {
submittokens();
}
//Send notifications
if ($surveyActive) {
sendSubmitNotifications($this->iSurveyid);
}
// Link to Print Answer Preview **********
$this->aSurveyInfo['aCompleted']['aPrintAnswers']['show'] = false;
if ($this->aSurveyInfo['printanswers'] == 'Y') {
$this->aSurveyInfo['aCompleted']['aPrintAnswers']['show'] = true;
$this->aSurveyInfo['aCompleted']['aPrintAnswers']['sUrl'] = $surveyActive ? Yii::app()->getController()->createUrl("/printanswers/view", array('surveyid' => $this->iSurveyid)) : "#";
$this->aSurveyInfo['aCompleted']['aPrintAnswers']['sText'] = gT("Print your answers.");
$this->aSurveyInfo['aCompleted']['aPrintAnswers']['sTitle'] = $surveyActive ? $this->aSurveyInfo['aCompleted']['aPrintAnswers']['sText'] : gT("Note: This link only works if the survey is activated.");
}
// Link to Public statistics
$this->aSurveyInfo['aCompleted']['aPublicStatistics']['show'] = false;
if ($this->aSurveyInfo['publicstatistics'] == 'Y') {
$this->aSurveyInfo['aCompleted']['aPublicStatistics']['show'] = true;
$this->aSurveyInfo['aCompleted']['aPublicStatistics']['sUrl'] = $surveyActive ? Yii::app()->getController()->createUrl("/StatisticsUser/action/", array('surveyid' => $this->iSurveyid, 'language' => App()->getLanguage())) : "#";
$this->aSurveyInfo['aCompleted']['aPublicStatistics']['sText'] = gT("View the statistics for this survey.");
$this->aSurveyInfo['aCompleted']['aPublicStatistics']['sTitle'] = $surveyActive ? $this->aSurveyInfo['aCompleted']['aPublicStatistics']['sText'] : gT("Note: This link only works if the survey is activated.");
}
$this->completed = true;
$_SESSION[$this->LEMsessid]['finished'] = true;
$_SESSION[$this->LEMsessid]['sid'] = $this->iSurveyid;
// cookies
if ($surveyActive && $this->aSurveyInfo['usecookie'] == "Y") {
if (!$oSurvey->getHasTokensTable()) {
setcookie("LS_" . $this->iSurveyid . "_STATUS", "COMPLETE", time() + 31536000); //Cookie will expire in 365 days
}
}
$redata['completed'] = $this->completed;
// event afterSurveyComplete
$blocks = array();
$event = new PluginEvent('afterSurveyComplete');
if ($surveyActive && isset($_SESSION[$this->LEMsessid]['srid'])) {
$event->set('responseId', $_SESSION[$this->LEMsessid]['srid']);
}
$event->set('surveyId', $this->iSurveyid);
App()->getPluginManager()->dispatchEvent($event);
foreach ($event->getAllContent() as $blockData) {
/* @var $blockData PluginEventContent */
$blocks[] = CHtml::tag('div', array('id' => $blockData->getCssId(), 'class' => $blockData->getCssClass()), $blockData->getContent());
}
$validator = new LSYii_Validators();
$this->aSurveyInfo['aCompleted']['sPluginHTML'] = implode("\n", $blocks) . "\n";
$this->aSurveyInfo['surveyls_url'] = passthruReplace($this->aSurveyInfo['surveyls_url'], $this->aSurveyInfo);
$this->aSurveyInfo['surveyls_url'] = $this->processString($this->aSurveyInfo['surveyls_url'], 3, 1);
if ($validator->isXssUrl($this->aSurveyInfo['surveyls_url'])) {
$this->aSurveyInfo['surveyls_url'] = "";
}
$this->aSurveyInfo['aCompleted']['sSurveylsUrl'] = $this->aSurveyInfo['surveyls_url'];
$this->aSurveyInfo['surveyls_urldescription'] = $this->processString($this->aSurveyInfo['surveyls_urldescription'], 3, 1);
$this->aSurveyInfo['aCompleted']['sSurveylsUrlDescription'] = $this->aSurveyInfo['surveyls_urldescription'];
if ($this->aSurveyInfo['aCompleted']['sSurveylsUrlDescription'] == "") {
$this->aSurveyInfo['aCompleted']['sSurveylsUrlDescription'] = $this->aSurveyInfo['surveyls_url'];
}
// LEM debug (???? what is this usage …)
$this->aSurveyInfo['aLEM']['debugvalidation']['show'] = false;
if (($this->LEMdebugLevel & LEM_DEBUG_VALIDATION_SUMMARY) == LEM_DEBUG_VALIDATION_SUMMARY) {
$this->aSurveyInfo['aLEM']['debugvalidation']['show'] = true;
$this->aSurveyInfo['aLEM']['debugvalidation']['message'] = $this->aMoveResult['message'];
}
$this->aSurveyInfo['aLEM']['debugvalidation']['show'] = false;
$this->aSurveyInfo['aLEM']['debugvalidation']['message'] = '';
if ((($this->LEMdebugLevel & LEM_DEBUG_TIMING) == LEM_DEBUG_TIMING)) {
$this->aSurveyInfo['aLEM']['debugvalidation']['show'] = true;
$this->aSurveyInfo['aLEM']['debugvalidation']['message'] .= LimeExpressionManager::GetDebugTimingMessage();
;
}
if ((($this->LEMdebugLevel & LEM_DEBUG_VALIDATION_SUMMARY) == LEM_DEBUG_VALIDATION_SUMMARY)) {
$this->aSurveyInfo['aLEM']['debugvalidation']['message'] .= "<table><tr><td align='left'><b>Group/Question Validation Results:</b>" . $this->aMoveResult['message'] . "</td></tr></table>\n";
}
if (isset($this->aSurveyInfo['autoredirect']) && $this->aSurveyInfo['autoredirect'] == "Y" && $this->aSurveyInfo['surveyls_url']) {
// kill survey session before redirecting
if ($this->aSurveyInfo['printanswers'] != 'Y') {
killSurveySession($this->iSurveyid);
}
//Automatically redirect the page to the "url" setting for the survey
$headToSurveyUrl = htmlspecialchars_decode($this->aSurveyInfo['surveyls_url']);
$actualRedirect = $headToSurveyUrl;
if ($surveyActive) {
header("Access-Control-Allow-Origin: *");
if (Yii::app()->request->getParam('ajax') == 'on') {
header("X-Redirect: " . $headToSurveyUrl, false, 302);
} else {
header("Location: " . $actualRedirect, false, 302);
}
}
$this->aSurveyInfo['aCompleted']['sSurveylsUrlDescriptionExta'] = gT("Note: Automatically loading the end URL works only if the survey is activated.");
}
$this->aSurveyInfo['include_content'] = 'submit';
if (!$surveyActive) {
$this->aSurveyInfo['include_content'] = 'submit_preview';
}
$sHtml = Yii::app()->twigRenderer->renderTemplateFromFile("layout_global.twig", array('oSurvey' => $oSurvey, 'aSurveyInfo' => $this->aSurveyInfo), true);
$oTemplate = Template::getLastInstance();
// kill survey session after doing template : didn't work for all var, but for EM core var : it's OK.
if ($this->aSurveyInfo['printanswers'] != 'Y') {
killSurveySession($this->iSurveyid);
}
Yii::app()->twigRenderer->renderHtmlPage($sHtml, $oTemplate);
}
}
/**
* Check in a string if it uses expressions to replace them
* @param string|null $sString the string to evaluate
* @param integer $numRecursionLevels - the number of times to recursively subtitute values in this string
* @param boolean $static - return static string
* @return string
* @todo : find/get current qid for processing string
*/
private function processString($sString, $iRecursionLevel = 1, $static = false)
{
$sProcessedString = $sString;
if ((strpos((string) $sProcessedString, "{") !== false)) {
// process string anyway so that it can be pretty-printed
$aStandardsReplacementFields = getStandardsReplacementFields($this->aSurveyInfo);
$sProcessedString = LimeExpressionManager::ProcessStepString($sString, $aStandardsReplacementFields, $iRecursionLevel, $static);
}
return $sProcessedString;
}
/**
* The run method fed $redata with using get_defined_var(). So it was very hard to move a piece of code from the run method to a new one.
* To make it easier, private variables has been added to this class:
* So when a piece of code changes a variable (a variable that originally was finally added to redata get_defined_var()), now, it also changes its private variable version.
* Then, before performing the get_defined_var, the private variables are used to recreate those variables. So we can move piece of codes to sub methods.
* setVarFromArgs($args) will set the original state of those private variables using the parameter $args passed to the run() method
*
* @param array $args
*/
private function setVarFromArgs($args)
{
extract($args);
$this->param = $param;
// Todo: check which ones are really needed
$this->LEMskipReprocessing = $LEMskipReprocessing ?? null;
$this->thissurvey = $thissurvey ?? null;
$this->iSurveyid = $surveyid ?? null;
$this->LEMsessid = $this->iSurveyid ? 'survey_' . $this->iSurveyid : null;
$this->aSurveyOptions = $surveyOptions ?? null;
$this->aMoveResult = $moveResult ?? null;
$this->sMove = $move ?? null;
$this->bInvalidLastPage = $invalidLastPage ?? null;
$this->notanswered = $notanswered ?? null;
$this->filenotvalidated = $filenotvalidated ?? null;
$this->completed = $completed ?? null;
$this->notvalidated = $notvalidated ?? null;
}
/**
* setJavascriptVar
*
* @return void
* @param mixed $iSurveyId : the survey ID for the script
*/
public function setJavascriptVar($iSurveyId = '')
{
$aSurveyinfo = ($iSurveyId != '') ? getSurveyInfo($iSurveyId, App()->getLanguage()) : $this->thissurvey;
if (isset($aSurveyinfo['surveyls_numberformat'])) {
$aLSJavascriptVar = array();
$aLSJavascriptVar['bFixNumAuto'] = (int) (bool) Yii::app()->getConfig('bFixNumAuto', 1);
$aLSJavascriptVar['bNumRealValue'] = (int) (bool) Yii::app()->getConfig('bNumRealValue', 0);
$aRadix = getRadixPointData($aSurveyinfo['surveyls_numberformat']);
$aLSJavascriptVar['sLEMradix'] = $aRadix['separator'];
$aLSJavascriptVar['lang'] = [
"confirm" => [
"confirm_cancel" => gT('Cancel'),
"confirm_ok" => gT('OK'),
],
]; // To add more easily some lang string here
$aLSJavascriptVar['showpopup'] = $this->oTemplate != null ? $this->oTemplate->showpopups : false;
$aLSJavascriptVar['startPopups'] = new stdClass();
$aLSJavascriptVar['debugMode'] = Yii::app()->getConfig('debug');
$sLSJavascriptVar = "LSvar=" . json_encode($aLSJavascriptVar) . ';';
App()->clientScript->registerScript('sLSJavascriptVar', $sLSJavascriptVar, CClientScript::POS_HEAD);
App()->clientScript->registerScript('setJsVar', "setJsVar();", CClientScript::POS_BEGIN); // Ensure all js var is set before rendering the page (User can click before $.ready)
}
}
/**
* Html error message if needed/available in the page
* @return string (html)
* @todo : move to coreReplacements ? Can be good.
*/
private function getErrorHtmlMessage()
{
$aErrorsMandatory = array();
//Mandatory question(s) with unanswered answer
if ($this->aStepInfo['mandViolation'] && $this->okToShowErrors) {
if ($this->aStepInfo['mandNonSoft']) {
$aErrorsMandatory[] = gT("One or more mandatory questions have not been answered. You cannot proceed until these have been completed.");
} else {
$aErrorsMandatory[] = gT("One or more mandatory questions have not been answered. If possible, please complete them before continuing to the next page.");
}
}
// Question(s) with not valid answer(s)
if (!$this->aStepInfo['valid'] && $this->okToShowErrors) {
$aErrorsMandatory[] = gT("One or more questions have not been answered in a valid manner. You cannot proceed until these answers are valid.");
}
// Upload question(s) with invalid file(s)
if ($this->filenotvalidated && $this->okToShowErrors) {
$aErrorsMandatory[] = gT("One or more uploaded files are not in proper format/size. You cannot proceed until these files are valid.");
}
return $aErrorsMandatory;
}
/**
* clear all system (no js or broken js)
* @uses $this->iSurveyid
* @uses $this->sTemplateViewPath
* @return void
*/
private function manageClearAll()
{
global $token;
$sessionSurvey = Yii::app()->session["survey_{$this->iSurveyid}"];
if (App()->request->getPost('confirm-clearall') != 'confirm') {
/* Save current response, and come back to survey if clearll is not confirmed */
$this->aMoveResult = LimeExpressionManager::JumpTo($_SESSION[$this->LEMsessid]['step'], false, true, true, false);
/* Todo : add an error in HTML view … */
//~ $aErrorHtmlMessage = array(gT("You need to confirm clear all action"));
//~ $this->aSurveyInfo['errorHtml']['show'] = true;
//~ $this->aSurveyInfo['errorHtml']['hiddenClass'] = "ls-js-hidden";
//~ $this->aSurveyInfo['errorHtml']['messages'] = $aErrorHtmlMessage;
return;
}
if (App()->request->getPost('confirm-clearall') == 'confirm') {
// Previous behaviour (and javascript behaviour)
// delete the existing response but only if not already completed
if (
isset($sessionSurvey['srid'])
&& !SurveyDynamic::model($this->iSurveyid)->isCompleted($sessionSurvey['srid']) // see bug https://bugs.limesurvey.org/view.php?id=11978
) {
$oResponse = Response::model($this->iSurveyid)->find("id = :srid", array(":srid" => $sessionSurvey['srid']));
if ($oResponse) {
$oResponse->delete(true); /* delete response line + files uploaded , warninbg : beforeDelete don't happen with deleteAll */
}
if (Survey::model()->findByPk($this->iSurveyid)->savetimings == "Y") {
SurveyTimingDynamic::model($this->iSurveyid)->deleteAll("id=:srid", array(":srid" => $sessionSurvey['srid'])); /* delete timings ( @todo must move it to Response )*/
}
SavedControl::model()->deleteAll("sid=:sid and srid=:srid", array(":sid" => $this->iSurveyid, ":srid" => $sessionSurvey['srid'])); /* saved controls (think we can have only one , but maybe ....)( @todo must move it to Response )*/
}
killSurveySession($this->iSurveyid);
if ($token) {
$restartparam['token'] = Token::sanitizeToken($token);
}
if (!empty(App()->getLanguage())) {
$restartparam['lang'] = sanitize_languagecode(App()->getLanguage());
} else {
$s_lang = Yii::app()->session['survey_' . $this->iSurveyid]['s_lang'] ?? 'en';
$restartparam['lang'] = $s_lang;
}
$restartparam['newtest'] = "Y";
$restarturl = Yii::app()->getController()->createUrl("survey/index/sid/$this->iSurveyid", $restartparam);
$this->aSurveyInfo['surveyUrl'] = $restarturl;
$this->aSurveyInfo['include_content'] = 'clearall';
Yii::app()->twigRenderer->renderTemplateFromFile("layout_global.twig", array('oSurvey' => Survey::model()->findByPk($this->iSurveyid), 'aSurveyInfo' => $this->aSurveyInfo), false);
}
}
/**
* NOTE: right now, captcha works ONLY if reloaded... need to be debug.
* NOTE: I bet we have the same problem on 2.6x.x
* NOTE: when token + captcha: works fine
*/
private function showTokenOrCaptchaFormsIfNeeded()
{
$this->iSurveyid = $this->aSurveyInfo['sid'];
$preview = $this->preview;
// Template settings
$oTemplate = $this->oTemplate;
$this->sTemplateViewPath = $oTemplate->viewPath;
// TODO: find where they are defined before this call
global $clienttoken;
global $tokensexist;
/**
* This method has multiple outcomes that virtually do the same thing
* Possible scenarios/subscenarios are =>
* - No token required & no captcha required
* - No token required & captcha required
* > captcha may be wrong
* - token required & captcha required
* > token may be wrong/used
* > captcha may be wrong
*/
$scenarios = array(
"tokenRequired" => ($tokensexist == 1),
"captchaRequired" => (isCaptchaEnabled('surveyaccessscreen', $this->aSurveyInfo['usecaptcha']) && !isset($_SESSION['survey_' . $this->iSurveyid]['captcha_surveyaccessscreen']))
);
/**
* Set subscenarios depending on scenario outcome
*/
$subscenarios = array(
"captchaCorrect" => false,
"tokenValid" => false
);
//Check the scenario for token required
if ($scenarios['tokenRequired']) {
//Check for the token-validity
if ($this->aSurveyInfo['alloweditaftercompletion'] == 'Y') {
$oTokenEntry = Token::model($this->iSurveyid)->findByAttributes(array('token' => $clienttoken));
} else {
$oTokenEntry = Token::model($this->iSurveyid)->usable()->incomplete()->findByAttributes(array('token' => $clienttoken));
}
$subscenarios['tokenValid'] = ((!empty($oTokenEntry) && ($clienttoken != "")));
} else {
$subscenarios['tokenValid'] = true;
}
//Check the scenario for captcha required
if ($scenarios['captchaRequired']) {
//Check if the Captcha was correct
$captcha = Yii::app()->getController()->createAction('captcha');
$subscenarios['captchaCorrect'] = $captcha->validate(App()->getRequest()->getPost('loadsecurity'), false);
} else {
$subscenarios['captchaCorrect'] = true;
}
//RenderWay defines which html gets rendered to the user_error
// Possibilities are main,register,correct
$renderCaptcha = "";
$renderToken = "";
/**
* @todo : create 2 new function to create and call form
*/
//Define array to render the partials
$aEnterTokenData = array();
$aEnterTokenData['bNewTest'] = false;
$aEnterTokenData['bDirectReload'] = false;
$aEnterTokenData['iSurveyId'] = $this->iSurveyid;
$aEnterTokenData['sLangCode'] = App()->language;
if (isset($_GET['bNewTest']) && $_GET['newtest'] == "Y") {
$aEnterTokenData['bNewTest'] = true;
}
// TODO: check with markus why $loadall, it's never ever defined, even in master branch
/*
// If this is a direct Reload previous answers URL, then add hidden fields
if (isset($loadall) && isset($scid) && isset($loadname) && isset($loadpass)) {
$aEnterTokenData['bDirectReload'] = true;
$aEnterTokenData['sCid'] = $scid;
$aEnterTokenData['sLoadname'] = htmlspecialchars($loadname);
$aEnterTokenData['sLoadpass'] = htmlspecialchars($loadpass);
}
*/
$aEnterErrors = array();
$FlashError = false;
// Scenario => Captcha required
if ($scenarios['captchaRequired'] && !$preview) {
//Apply the captcYii::app()->getRequest()->getPost($id);haEnabled flag to the partial
$aEnterTokenData['bCaptchaEnabled'] = true;
// IF CAPTCHA ANSWER IS NOT CORRECT OR NOT SET
if (!$subscenarios['captchaCorrect']) {
if (App()->getRequest()->getPost('loadsecurity')) {
$aEnterErrors['captcha'] = gT("Your answer to the security question was not correct - please try again.");
} elseif (null !== App()->getRequest()->getPost('loadsecurity')) {
$aEnterErrors['captcha'] = gT("Your have to answer the security question - please try again.");
}
$renderCaptcha = 'main';
} else {
$_SESSION['survey_' . $this->iSurveyid]['captcha_surveyaccessscreen'] = true;
$renderCaptcha = 'correct';
}
}
// Scenario => Token required
if ($scenarios['tokenRequired'] && !$preview) {
//Test if token is valid
list($renderToken, $FlashError, $aEnterTokenData) = testIfTokenIsValid($subscenarios, $this->aSurveyInfo, $aEnterTokenData, $clienttoken);
}
if ($FlashError) {
$aEnterErrors['flash'] = $FlashError;
}
$aEnterTokenData['aEnterErrors'] = $aEnterErrors;
$renderWay = getRenderWay($renderToken, $renderCaptcha);
/* This function end if an form need to be shown */
renderRenderWayForm($renderWay, $scenarios, $this->sTemplateViewPath, $aEnterTokenData, $this->iSurveyid, $this->aSurveyInfo);
}
private function initTemplate()
{
$oTemplate = $this->oTemplate = Template::model()->getInstance('', $this->iSurveyid);
$this->sTemplateViewPath = $oTemplate->viewPath;
//$oTemplate->registerAssets();
}
/**
* Set alanguageChanger.show to true if we need to show
* the language changer.
* @return void
*/
private function makeLanguageChanger()
{
$this->aSurveyInfo['alanguageChanger']['show'] = false;
$alanguageChangerDatas = getLanguageChangerDatas($this->sLangCode);
if ($alanguageChangerDatas) {
$this->aSurveyInfo['alanguageChanger']['show'] = true;
$this->aSurveyInfo['alanguageChanger']['datas'] = $alanguageChangerDatas;
}
}
/**
* This method will set survey values in public property of the class
* So, any value here set as $this->xxx will be available as $xxx after :
* eg: $this->LEMsessid
* @param integer $surveyid;
* @param array $args;
*/
private function setSurveySettings($surveyid, $args)
{
$this->setVarFromArgs(array_merge($args, array('surveyid' => $surveyid))); // Set the private variable from $args, be sure to set surveyid
$this->initTemplate(); // Template settings
$this->setJavascriptVar();
$this->setArgs();
extract($args);
$this->aSurveyInfo = getSurveyInfo($this->iSurveyid, App()->getLanguage());
if (isset($args['popuppreview']) && $args['popuppreview']) {
$this->aSurveyInfo['showxquestions'] = 'N';
$this->aSurveyInfo['shownoanswer'] = 'N';
$this->aSurveyInfo['showwelcome'] = 'N';
$this->aSurveyInfo['showprogress'] = 'N';
$this->aSurveyInfo['format'] = 'A';
$this->aSurveyInfo['listpublic'] = 'N';
$this->aSurveyInfo['popupPreview'] = true;
}
$this->aSurveyInfo['surveyUrl'] = App()->createUrl("/survey/index", array("sid" => $this->iSurveyid));
// TODO: check this:
$this->aSurveyInfo['oTemplate'] = (array) $this->oTemplate;
$this->setSurveyMode();
$this->setSurveyOptions();
$this->previewgrp = (isset($this->param['action']) && $this->param['action'] == 'previewgroup') ? true : false;
$this->previewquestion = (isset($this->param['action']) && $this->param['action'] == 'previewquestion') ? true : false;
$this->preview = ($this->previewquestion || $this->previewgrp);
$this->sLangCode = App()->language;
}
private function setPreview()
{
$this->sSurveyMode = ($this->previewgrp) ? 'group' : 'question'; // Can be great to have a survey here …
buildsurveysession($this->iSurveyid, true); // Preview part disable SurveyURLParameter , why ? Work without
/* Set steps for PHP notice */
$_SESSION[$this->LEMsessid]['prevstep'] = 2;
$_SESSION[$this->LEMsessid]['maxstep'] = 0;
$_SESSION[$this->LEMsessid]['step'] = 0;
if ($this->previewgrp) {
$_gid = sanitize_int($this->param['gid']);
LimeExpressionManager::StartSurvey($this->aSurveyInfo['sid'], $this->sSurveyMode, $this->aSurveyOptions, false, $this->LEMdebugLevel);
$gseq = LimeExpressionManager::GetGroupSeq($_gid);
if ($gseq == -1) {
$sMessage = gT('Invalid group number for this survey: ') . $_gid;
renderError('', $sMessage, $this->aSurveyInfo, $this->sTemplateViewPath);
}
$this->aMoveResult = LimeExpressionManager::JumpTo($gseq + 1, 'group', false, true);
if (is_null($this->aMoveResult)) {
$sMessage = gT('This group contains no questions. You must add questions to this group before you can preview it');
renderError('', $sMessage, $this->aSurveyInfo, $this->sTemplateViewPath);
}
$_SESSION[$this->LEMsessid]['step'] = $this->aMoveResult['seq'] + 1; // step is index base 1?
$_SESSION[$this->LEMsessid]['notRelevantSteps'] = $this->aMoveResult['notRelevantSteps'] ?? 0;
$_SESSION[$this->LEMsessid]['hiddenSteps'] = $this->aMoveResult['hiddenSteps']?? 0;
$this->aStepInfo = LimeExpressionManager::GetStepIndexInfo($this->aMoveResult['seq']);
// #14595
if (empty($this->aStepInfo)) {
$sMessage = gT('This group is empty');
renderError('', $sMessage, $this->aSurveyInfo, $this->sTemplateViewPath);
}
} elseif ($this->previewquestion) {
$_qid = sanitize_int($this->param['qid']);
LimeExpressionManager::StartSurvey($this->iSurveyid, $this->sSurveyMode, $this->aSurveyOptions, true, $this->LEMdebugLevel);
$qSec = LimeExpressionManager::GetQuestionSeq($_qid);
$this->aMoveResult = LimeExpressionManager::JumpTo($qSec + 1, 'question', false, true);
$this->aStepInfo = LimeExpressionManager::GetStepIndexInfo($this->aMoveResult['seq']);
}
}
private function setGroup()
{
if (!$this->previewgrp && !$this->previewquestion) {
if (($this->bShowEmptyGroup) || !isset($_SESSION[$this->LEMsessid]['grouplist'])) {
$this->gid = -1; // Make sure the gid is unused. This will assure that the foreach (fieldarray as ia) has no effect.
$this->groupname = gT("Submit your answers");
$this->groupdescription = gT("There are no more questions. Please use the `Submit` button to finish this survey.");
} elseif ($this->sSurveyMode != 'survey') {
if ($this->sSurveyMode != 'group') {
$this->aStepInfo = LimeExpressionManager::GetStepIndexInfo($this->aMoveResult['seq']);
}
$this->gid = $this->aStepInfo['gid'];
$this->groupname = $this->aStepInfo['gname'];
$this->groupdescription = $this->aStepInfo['gtext'];
$this->groupname = LimeExpressionManager::ProcessString($this->groupname, null, null, 3, 1, false, true, false);
$this->groupdescription = LimeExpressionManager::ProcessString($this->groupdescription, null, null, 3, 1, false, true, false);
}
}
}
private function fixMaxStep()
{
// NOTE: must stay after setPreview because of ()$this->sSurveyMode == 'group' && $this->previewgrp) condition touching step
if ($_SESSION[$this->LEMsessid]['step'] > $_SESSION[$this->LEMsessid]['maxstep']) {
$_SESSION[$this->LEMsessid]['maxstep'] = $_SESSION[$this->LEMsessid]['step'];
}
}
/**
* Apply the plugin even beforeQuestionRender to
* question data.
*
* @see https://www.limesurvey.org/manual/BeforeQuestionRender
*
* @param array $data Question data
* @return array Question data modified by plugin
*/
protected function doBeforeQuestionRenderEvent($data)
{
$event = new PluginEvent('beforeQuestionRender');
$event->set('surveyId', $this->iSurveyid);
$event->set('type', $data['type']);
$event->set('code', $data['code']);
$event->set('qid', $data['qid']);
$event->set('gid', $data['gid']);
$event->set('text', $data['text']);
$event->set('class', $data['class']);
$event->set('input_error_class', $data['input_error_class']);
$event->set('answers', $data['answer']); // NB: "answers" in plugin, "answer" in $data.
$event->set('help', $data['help']['text']);
$event->set('man_message', $data['man_message']);
$event->set('valid_message', $data['valid_message']);
$event->set('file_valid_message', $data['file_valid_message']);
$event->set('aHtmlOptions', array()); // Set as empty array, not needed. Before 3.0 usage for EM style
App()->getPluginManager()->dispatchEvent($event);
$data['text'] = $event->get('text');
$data['mandatory'] = $event->get('mandatory', $data['mandatory']);
$data['class'] = $event->get('class');
$data['input_error_class'] = $event->get('input_error_class');
$data['valid_message'] = $event->get('valid_message');
$data['file_valid_message'] = $event->get('file_valid_message');
$data['man_message'] = $event->get('man_message');
$data['answer'] = $event->get('answers');
$data['help']['text'] = $event->get('help');
$data['help']['show'] = flattenText($data['help']['text'], true, true) != '';
$data['attributes'] = CHtml::renderAttributes(array_merge((array) $event->get('aHtmlOptions'), ['id' => "question{$data['qid']}"]));
return $data;
}
/**
* Retrieve the question classes for a given question id
*
* @param int $iQid the question id
* @return string the classes
*/
public function getCurrentQuestionClasses($iQid)
{
$lemQuestionInfo = LimeExpressionManager::GetQuestionStatus($iQid);
$sType = $lemQuestionInfo['info']['type'];
$aQuestionClass = Question::getQuestionClass($sType);
/* Add the relevance class */
if (!$lemQuestionInfo['relevant']) {
$aQuestionClass .= ' ls-irrelevant';
$aQuestionClass .= ' ls-hidden';
}
/* Can use aQuestionAttributes too */
if ($lemQuestionInfo['hidden']) {
$aQuestionClass .= ' ls-hidden-attribute'; /* another string ? */
$aQuestionClass .= ' ls-hidden';
}
if ($lemQuestionInfo['info']['mandatory'] == 'Y' || $lemQuestionInfo['info']['mandatory'] == 'S') {
$aQuestionClass .= ' mandatory';
}
if ($lemQuestionInfo['anyUnanswered'] && $_SESSION[$this->LEMsessid]['maxstep'] != $_SESSION[$this->LEMsessid]['step']) {
$aQuestionClass .= ' missing';
}
$aQuestionAttributes = QuestionAttribute::model()->getQuestionAttributes($iQid);
//add additional classes
if (isset($aQuestionAttributes['cssclass']) && $aQuestionAttributes['cssclass'] != "") {
/* Got to use static expression */
$emCssClass = trim(LimeExpressionManager::ProcessString($aQuestionAttributes['cssclass'], null, array(), 1, 1, false, false, true)); /* static var is the last one ...*/
if ($emCssClass != "") {
$aQuestionClass .= " " . CHtml::encode($emCssClass);
}
}
return $aQuestionClass;
}
}