diff --git a/application/components/SurveySession.php b/application/components/SurveySession.php index 0cbc0c348c3..a0afb1db50b 100644 --- a/application/components/SurveySession.php +++ b/application/components/SurveySession.php @@ -46,6 +46,12 @@ class SurveySession extends CComponent { protected $_templateDir; protected $_postKey; protected $_finished = false; + /** + * This is used to decide if we should display errors. + * It gets reset when changing the step. + * @var int The number of times this step has been viewed without viewing other steps. + */ + protected $_viewCount = 1; protected $_token; /** * @param int $surveyId @@ -177,7 +183,21 @@ public function getQuestionIndex($id) { * @return Question */ public function getQuestionByIndex($index) { - return array_values($this->survey->questions)[$index]; + $i = 0; + // Get groups in order. + foreach($this->getGroups() as $group) { + foreach($this->getQuestions($group) as $question) { + if ($index == $i) { + $result = $question; + break 2; + } + $i++; + } + } + if (!isset($result)) { + throw new \Exception("Invalid step index: $index"); + } + return $result; } public function getStepCount() { @@ -207,11 +227,18 @@ public function getStep() { public function setStep($value) { if (!is_int($value)) { - throw new \BadMethodCallException('Parameter $value must be an integer.'); + throw new \InvalidArgumentException('Parameter $value must be an integer.'); + } + if ($value > $this->stepCount) { + throw new \InvalidArgumentException("Cannot set step to a value greater than stepCount"); + } + + if ($value != $this->_step) { + $this->_viewCount = 1; + $this->_prevStep = $this->_step; + $this->_step = $value; + $this->_maxStep = max($this->_step, $this->_maxStep); } - $this->_step = $value > 0 ? $value : 0; - $this->_prevStep = $this->_step; - $this->_maxStep = max($this->_step, $this->_maxStep); } @@ -219,6 +246,13 @@ public function getMaxStep() { return $this->_maxStep; } + /** + * @return int The number of times the current page has been viewed. + */ + public function getViewCount() { + return $this->_viewCount; + } + public function getPrevStep() { return $this->_prevStep; } @@ -227,6 +261,10 @@ public function setPrevStep($value) { $this->_prevStep = $value; } + public function __wakeup() { + $this->_viewCount++; + } + public function __sleep() { return [ 'surveyId', @@ -238,7 +276,8 @@ public function __sleep() { '_finished', '_language', '_postKey', - '_token' + '_token', + '_viewCount' ]; } @@ -346,7 +385,7 @@ public function getFormat() { /** - * Returns the questions in group $group, indexed by primary key. + * Returns the questions in group $group, indexed by primary key, ordered as they are shown in the survey. * @param QuestionGroup $group * @return Question[] */ @@ -389,6 +428,7 @@ public function getQuestions(QuestionGroup $group) { /** * This function will be deprecated, for now it is provided as a replacement of direct session access. + * @deprecated */ public function getFieldArray() { $result = []; @@ -472,10 +512,24 @@ public function getPostKey() { } return $this->_postKey; } + public function setPostKey($value) { $this->_postKey = $value; } + public function getCurrentGroup() { + if ($this->format == Survey::FORMAT_ALL_IN_ONE) { + throw new \UnexpectedValueException("An all in one survey does not have a current group."); + } + + if ($this->format == Survey::FORMAT_GROUP) { + $result = $this->getGroupByIndex($this->step); + } else { + $result = $this->getGroup($this->getQuestionByIndex($this->step)->gid); + } + return $result; + } + } diff --git a/application/helpers/SurveyRuntimeHelper.php b/application/helpers/SurveyRuntimeHelper.php index 622a38ef2b7..2ea9911c7a0 100644 --- a/application/helpers/SurveyRuntimeHelper.php +++ b/application/helpers/SurveyRuntimeHelper.php @@ -229,13 +229,7 @@ function run($surveyid,$args) { buildsurveysession($session->surveyId); $sTemplatePath = $session->templateDir; LimeExpressionManager::StartSurvey($session->surveyId, $session->format, $surveyOptions, false, $LEMdebugLevel); - if ($session->format == Survey::FORMAT_ALL_IN_ONE) { - $moveResult = LimeExpressionManager::JumpTo(1, false, false, true); - } elseif (!$session->survey->bool_showwelcome) { - - $moveResult = LimeExpressionManager::JumpTo(1, false, false, true); - $session->step = 1; - } + $moveResult = LimeExpressionManager::JumpTo(0, false, false, true); } @@ -262,25 +256,17 @@ function run($surveyid,$args) { if(isset($move) && $move=="clearcancel") { - $moveResult = LimeExpressionManager::JumpTo($session->getStep(), false, true, false, true); - //$backpopup=gT("Clear all need confirmation."); - } - if (isset($move)) - { - if(!in_array($move,array("changelang","saveall","reload"))) - $session->prevStep = $session->getStep(); - else // Accepted $move without error - $session->prevStep = $move; + $moveResult = LimeExpressionManager::JumpTo($session->step, false, true, false, true); } if (isset($_SESSION[$LEMsessid]['LEMtokenResume'])) { LimeExpressionManager::StartSurvey($session->surveyId, $session->format, $surveyOptions, false,$LEMdebugLevel); - if($ssm->current->getMaxStep() > $ssm->current->getStep()) + if($session->maxStep > $session->step) { - LimeExpressionManager::JumpTo($ssm->current->getMaxStep(), false, false); + LimeExpressionManager::JumpTo($session->maxStep, false, false); } - $moveResult = LimeExpressionManager::JumpTo($ssm->current->getStep(),false,false); // if late in the survey, will re-validate contents, which may be overkill + $moveResult = LimeExpressionManager::JumpTo($session->step,false,false); // if late in the survey, will re-validate contents, which may be overkill unset($_SESSION[$LEMsessid]['LEMtokenResume']); } else if (!$LEMskipReprocessing) @@ -340,21 +326,18 @@ function run($surveyid,$args) { if (isset($moveResult) && isset($moveResult['seq']) )// Reload at first page (welcome after click previous fill an empty $moveResult array { // With complete index, we need to revalidate whole group bug #08806. It's actually the only mode where we JumpTo with force - if($moveResult['finished'] == true && $session->survey->questionindex == Survey::INDEX_FULL) - { - //LimeExpressionManager::JumpTo(-1, false, false, true); - LimeExpressionManager::StartSurvey($session->surveyId, $session->format, $surveyOptions); - $moveResult = LimeExpressionManager::JumpTo($session->getStepCount() + 1, false, false, false);// no preview, no save data and NO force - if(!$moveResult['mandViolation'] && $moveResult['valid'] && empty($moveResult['invalidSQs'])) - $moveResult['finished'] = true; - } - if ($moveResult['finished'] == true) + if($moveResult['finished']) { + if ($session->survey->questionindex == Survey::INDEX_FULL) { + //LimeExpressionManager::JumpTo(-1, false, false, true); + LimeExpressionManager::StartSurvey($session->surveyId, $session->format, $surveyOptions); + $moveResult = LimeExpressionManager::JumpTo($session->getStepCount() + 1, false, false, false);// no preview, no save data and NO force + if(!$moveResult['mandViolation'] && $moveResult['valid'] && empty($moveResult['invalidSQs'])) + $moveResult['finished'] = true; + } $move = 'movesubmit'; - } - else - { - $session->setStep($moveResult['seq'] + 1); // step is index base 1 + } else { + $session->setStep($moveResult['seq']); $stepInfo = LimeExpressionManager::GetStepIndexInfo($moveResult['seq']); } if ($move == "movesubmit" && $moveResult['finished'] == false) @@ -364,10 +347,6 @@ function run($surveyid,$args) { $invalidLastPage = true; } } - if ($session->format != Survey::FORMAT_ALL_IN_ONE && $session->getStep() == 0) - { - $moveResult = LimeExpressionManager::JumpTo(1, false, false, true); - } // TODO FIXME if ($session->survey->bool_active) { @@ -411,9 +390,6 @@ function run($surveyid,$args) { // TODO - does this work automatically for token answer persistence? Used to be savedsilent() } - //Now, we check mandatory questions if necessary - //CHECK IF ALL CONDITIONAL MANDATORY QUESTIONS THAT APPLY HAVE BEEN ANSWERED - global $notanswered; if (isset($moveResult) && !$moveResult['finished']) { @@ -645,21 +621,15 @@ function run($surveyid,$args) { $gid = $stepInfo['gid']; $groupname = $stepInfo['gname']; $groupdescription = $stepInfo['gtext']; - } - else - { - if ($session->format != Survey::FORMAT_ALL_IN_ONE) - { - if ($previewquestion) { - $_qid = sanitize_int($param['qid']); - LimeExpressionManager::StartSurvey($session->surveyId, $session->format, $surveyOptions, false, $LEMdebugLevel); - $qSec = LimeExpressionManager::GetQuestionSeq($_qid); - $moveResult = LimeExpressionManager::JumpTo($qSec+1,true,false,true); - $stepInfo = LimeExpressionManager::GetStepIndexInfo($moveResult['qseq']); - } else { - $stepInfo = LimeExpressionManager::GetStepIndexInfo($session->step + 1); - } + } elseif ($session->format == Survey::FORMAT_QUESTION) { + if ($previewquestion) { + $_qid = sanitize_int($param['qid']); + LimeExpressionManager::StartSurvey($session->surveyId, $session->format, $surveyOptions, false, $LEMdebugLevel); + $qSec = LimeExpressionManager::GetQuestionSeq($_qid); + $moveResult = LimeExpressionManager::JumpTo($qSec+1,true,false,true); } + + $stepInfo = LimeExpressionManager::GetStepIndexInfo($moveResult['seq']); } @@ -680,7 +650,7 @@ function run($surveyid,$args) { //PRESENT SURVEY //****************************************************************************************************** - $okToShowErrors = (!$previewgrp && (isset($invalidLastPage) || $session->prevStep == $session->step)); + $okToShowErrors = (!$previewgrp && (isset($invalidLastPage) || !$moveResult['valid'] || $moveResult['mandViolation'])); App()->loadHelper('qanda'); setNoAnswerMode($thissurvey); @@ -765,16 +735,9 @@ function run($surveyid,$args) { } - if ($session->format != Survey::FORMAT_ALL_IN_ONE && isset($thissurvey['showprogress']) && $thissurvey['showprogress'] == 'Y') + if ($session->format != Survey::FORMAT_ALL_IN_ONE && $session->survey->bool_showprogress) { - if ($show_empty_group) - { - $percentcomplete = makegraph($session->getStepCount() + 1, $session->getStepCount()); - } - else - { - $percentcomplete = makegraph($session->step, $session->getStepCount()); - } + $percentcomplete = makegraph($session->step, $session->stepCount); } if (!(isset($languagechanger) && strlen($languagechanger) > 0) && function_exists('makeLanguageChangerSurvey')) { diff --git a/application/helpers/expressions/em_manager_helper.php b/application/helpers/expressions/em_manager_helper.php index 215d73b8253..966178ec25c 100644 --- a/application/helpers/expressions/em_manager_helper.php +++ b/application/helpers/expressions/em_manager_helper.php @@ -176,26 +176,6 @@ class LimeExpressionManager */ private $qans; - /** - * map question # to the group it is within, using an incremental count of group order - * - * @example [157] = 2 // means that qid 157 is in the 3rd page of questions (gseq = 2) - * - * @var type - */ - private $questionId2groupSeq; - /** - * array of info about each Group, indexed by GroupSeq - * - * @example [2] = array( - * 'qstart' => 9 // the first qseq within that group - * 'qend' => 13 //the last qseq within that group - * ); - * - * @var type - */ - private $_groupSeqInfo; - /** * tracks which groups have at least one relevant, non-hidden question * @@ -228,24 +208,6 @@ class LimeExpressionManager */ private $qid2validationEqn; - - /** - * current Group sequence (0-based index) - * @example 1 - * @var integer - */ - private $currentGroupSeq; - /** - * for Question-by-Question mode, the 0-based index - * @example 3 - * @var integer - */ - private $currentQuestionSeq; - /** - * used in Question-by-Question mode - * @var integer - */ - private $currentQID; /** * set of the current set of questions to be displayed, indexed by QID - at least one must be relevant * @@ -278,7 +240,7 @@ class LimeExpressionManager * * @var type */ - private $currentQset = null; + private $currentQset = []; /** * last result of NavigateForwards, NavigateBackwards, or JumpTo * Array of status information about last movement, whether at question, group, or survey level @@ -353,17 +315,7 @@ class LimeExpressionManager */ private $gseq2info; - /** - * the maximum groupSeq reached - this is needed for Index - * @var type - */ - private $maxGroupSeq; - /** - * the maximum Question reached sequencly ordered, used to show error to the user if we stop before this step with indexed survey. - * In question by question mode : $maxQuestionSeq==$_SESSION['survey_'.surveyid]['maxstep'], use it ? - * @var integer - */ - private $maxQuestionSeq = -1; + /** * /** * mapping of questions to information about their subquestions. @@ -495,16 +447,8 @@ class LimeExpressionManager */ private $sgqaNaming = true; - /** - * Numer of questions in survey (counting display-only ones?) - * @var integer - */ - private $numQuestions = 0; - /** - * String identifier for the active session - * @var type - */ - private $sessid; + + /** * Linked list of array filters * @var array @@ -515,11 +459,6 @@ class LimeExpressionManager * @var type */ private $qid2exclusiveAuto = array(); - /** - * Array of values to be updated - * @var type - */ - private $updatedValues = array(); /** * A private constructor; prevents direct creation of object @@ -527,7 +466,6 @@ class LimeExpressionManager private function __construct() { self::$instance =& $this; - $this->currentQset = []; $this->em = new ExpressionManager(); } @@ -538,9 +476,9 @@ private function __construct() public static function &singleton() { if (!isset(self::$instance)) { - \Yii::beginProfile('construct lem'); + bP(); self::$instance = new static(); - \Yii::endProfile('construct lem'); + eP(); } return self::$instance; @@ -548,24 +486,22 @@ public static function &singleton() public function getGroupSeqInfo() { - if (!isset($this->_groupSeqInfo)) { - $result = []; - $session = App()->surveySessionManager->current; - $questionCounter = 0; - foreach ($session->getGroups() as $index => $group) { - $start = $questionCounter; - foreach ($session->getQuestions($group) as $question) { - $questionCounter++; - } - $result[$index] = [ - 'qstart' => $start, - 'qend' => $questionCounter - 1 - ]; - } - $this->_groupSeqInfo = $result; + bP(); + $result = []; + $session = App()->surveySessionManager->current; + $questionCounter = 0; + foreach ($session->getGroups() as $index => $group) { + $start = $questionCounter; + foreach ($session->getQuestions($group) as $question) { + $questionCounter++; + } + $result[$index] = [ + 'qstart' => $start, + 'qend' => $questionCounter - 1 + ]; } - - return $this->_groupSeqInfo; + eP(); + return $result; } /** @@ -595,18 +531,6 @@ public static function SetDirtyFlag() $_SESSION['LEMforceRefresh'] = true;// For Expression manager string } - /** - * Set the SurveyId - really checks whether the survey you're about to work with is new, and if so, clears the LEM cache - * @param $sid - */ - public static function SetSurveyId($sid = null) - { - - self::singleton()->sid = $sid; - - } - - /** * Do bulk-update/save of Condition to Relevance @@ -672,7 +596,6 @@ public static function getLEMqcode2sgqa($iSurveyId) $LEM =& LimeExpressionManager::singleton(); $LEM->SetEMLanguage(Survey::model()->findByPk($iSurveyId)->language); - $LEM->SetSurveyId($iSurveyId); $LEM->StartProcessingPage(true); return $LEM->qcode2sgqa; @@ -893,7 +816,7 @@ public function _CreateSubQLevelRelevanceAndValidationEqns($onlyThisQseq = null) $qinfos = [$this->getSubQuestionInfo($session->getQuestionByIndex($onlyThisQseq))]; } foreach ($qinfos as $qinfo) { - if ($session->format != Survey::FORMAT_ALL_IN_ONE && $this->currentGroupSeq != $this->groupId2groupSeq[$qinfo['gid']]) { + if ($session->format != Survey::FORMAT_ALL_IN_ONE && $session->currentGroup->primaryKey != $qinfo['gid']) { continue; // only need subq relevance for current page. } $questionNum = $qinfo['qid']; @@ -3489,6 +3412,7 @@ public static function SubQuestionIsRelevant($sgqa) */ static function QuestionIsRelevant($qid) { + return true; $LEM =& LimeExpressionManager::singleton(); $qrel = (isset($_SESSION[$LEM->sessid]['relevanceStatus'][$qid]) ? $_SESSION[$LEM->sessid]['relevanceStatus'][$qid] : 1); $gseq = (isset($LEM->questionId2groupSeq[$qid]) ? $LEM->questionId2groupSeq[$qid] : -1); @@ -3504,6 +3428,7 @@ static function QuestionIsRelevant($qid) */ static function GroupIsRelevant($gid) { + return true; \Yii::beginProfile(__CLASS__ . '::' .__FUNCTION__); $LEM =& LimeExpressionManager::singleton(); $session = App()->surveySessionManager->current; @@ -3521,10 +3446,7 @@ static function GroupIsRelevant($gid) */ static function GroupIsIrrelevantOrHidden($gseq) { - $LEM =& LimeExpressionManager::singleton(); - $grel = (isset($_SESSION[$LEM->sessid]['relevanceStatus']['G' . $gseq]) ? $_SESSION[$LEM->sessid]['relevanceStatus']['G' . $gseq] : 1); // group-level relevance based upon grelevance equation - $gshow = (isset($LEM->indexGseq[$gseq]['show']) ? $LEM->indexGseq[$gseq]['show'] : true); // default to true? - return !($grel && $gshow); + return false; } /** @@ -3542,25 +3464,21 @@ function ProcessAllNeededRelevance($onlyThisQseq = null) if (!is_null($onlyThisQseq) && $onlyThisQseq != $questionSequence) { continue; } - $rel = $this->getQuestionRelevanceInfo($session->getQuestionByIndex($questionSequence)); - $qid = $rel['qid']; - - $gseq = $this->groupId2groupSeq[$rel['gid']]; - if ($session->format != Survey::FORMAT_ALL_IN_ONE && $gseq != $this->currentGroupSeq) { + $question = $session->getQuestionByIndex($questionSequence); + $rel = $this->getQuestionRelevanceInfo($question); + $qid = $question->primaryKey; + if ($session->format != Survey::FORMAT_ALL_IN_ONE && $question->gid != $session->currentGroup->primaryKey) { continue; } $result = $this->_ProcessRelevance(htmlspecialchars_decode($rel['relevance'], ENT_QUOTES), $qid, - $gseq, + $session->getGroupIndex($question->gid), $rel['jsResultVar'], $rel['type'], $rel['hidden'] ); - $_SESSION[$this->sessid]['relevanceStatus'][$qid] = $result; - - } } @@ -3819,7 +3737,6 @@ public function getQuestionRelevanceInfo(Question $question) $questionCounter = 0; -// var_dump($fieldMap); die(); foreach ($fieldMap as $fielddata) { $sgqa = $fielddata['fieldname']; $mandatory = $fielddata['mandatory']; @@ -4094,6 +4011,9 @@ public function getQuestionRelevanceInfo(Question $question) public function getGroupRelevanceInfo($groupId) { bP(); + if (!is_numeric($groupId)) { + throw new \InvalidArgumentException("Parameter groupId must be of type integer"); + } $session = App()->surveySessionManager->current; $eqn = $session->getGroup($groupId)->grelevance; @@ -4318,58 +4238,57 @@ static function StartSurvey( $LEM->setVariableAndTokenMappingsForExpressionManager($surveyid, $forceRefresh, $LEM->surveyOptions['anonymized']); $LEM->currentGroupSeq = -1; - $LEM->currentQuestionSeq = -1; // for question-by-question mode $LEM->indexGseq = array(); $LEM->qrootVarName2arrayFilter = array(); templatereplace("{}"); // Needed for coreReplacements in relevance equation (in all mode) - if (isset($_SESSION[$LEM->sessid]['startingValues']) && is_array($_SESSION[$surveyid]['startingValues']) && count($_SESSION[$surveyid]['startingValues']) > 0) { - $startingValues = array(); - foreach ($_SESSION[$LEM->sessid]['startingValues'] as $k => $value) { - if (isset($LEM->knownVars[$k])) { - $knownVar = $LEM->knownVars[$k]; - } else { - if (isset($LEM->qcode2sgqa[$k])) { - $knownVar = $LEM->knownVars[$LEM->qcode2sgqa[$k]]; - } else { - if (isset($LEM->tempVars[$k])) { - $knownVar = $LEM->tempVar[$k]; - } else { - continue; - } - } - } - if (!isset($knownVar['jsName'])) { - continue; - } - switch ($knownVar['type']) { - case 'D': //DATE - if (trim($value) == "") { - $value = null; - } elseif ($value != 'INVALID') { - $dateformatdatat = getDateFormatData($LEM->surveyOptions['surveyls_dateformat']); - $datetimeobj = new Date_Time_Converter($value, $dateformatdatat['phpdate']); - $value = $datetimeobj->convert("Y-m-d H:i"); - } - break; - case 'N': //NUMERICAL QUESTION TYPE - case 'K': //MULTIPLE NUMERICAL QUESTION - if (trim($value) == "") { - $value = null; - } else { - $value = sanitize_float($value); - } - break; - case '|': //File Upload - $value = null; // can't upload a file via GET - break; - } - $LEM->updatedValues[$knownVar['sgqa']] = array( - 'type' => $knownVar['type'], - 'value' => $value, - ); - } - $LEM->_UpdateValuesInDatabase(null); - } +// if (isset($_SESSION[$LEM->sessid]['startingValues']) && is_array($_SESSION[$surveyid]['startingValues']) && count($_SESSION[$surveyid]['startingValues']) > 0) { +// $startingValues = array(); +// foreach ($_SESSION[$LEM->sessid]['startingValues'] as $k => $value) { +// if (isset($LEM->knownVars[$k])) { +// $knownVar = $LEM->knownVars[$k]; +// } else { +// if (isset($LEM->qcode2sgqa[$k])) { +// $knownVar = $LEM->knownVars[$LEM->qcode2sgqa[$k]]; +// } else { +// if (isset($LEM->tempVars[$k])) { +// $knownVar = $LEM->tempVar[$k]; +// } else { +// continue; +// } +// } +// } +// if (!isset($knownVar['jsName'])) { +// continue; +// } +// switch ($knownVar['type']) { +// case 'D': //DATE +// if (trim($value) == "") { +// $value = null; +// } elseif ($value != 'INVALID') { +// $dateformatdatat = getDateFormatData($LEM->surveyOptions['surveyls_dateformat']); +// $datetimeobj = new Date_Time_Converter($value, $dateformatdatat['phpdate']); +// $value = $datetimeobj->convert("Y-m-d H:i"); +// } +// break; +// case 'N': //NUMERICAL QUESTION TYPE +// case 'K': //MULTIPLE NUMERICAL QUESTION +// if (trim($value) == "") { +// $value = null; +// } else { +// $value = sanitize_float($value); +// } +// break; +// case '|': //File Upload +// $value = null; // can't upload a file via GET +// break; +// } +// $LEM->updatedValues[$knownVar['sgqa']] = array( +// 'type' => $knownVar['type'], +// 'value' => $value, +// ); +// } +// $LEM->_UpdateValuesInDatabase(null); +// } return array( 'hasNext' => true, @@ -4462,8 +4381,6 @@ static function NavigateBackwards() // Set certain variables normally set by StartProcessingGroup() $LEM->groupRelevanceInfo = array(); // TODO only important thing from StartProcessingGroup? $qInfo = $LEM->questionSeq2relevance[$LEM->currentQuestionSeq]; - $LEM->currentQID = $qInfo['qid']; - $LEM->currentGroupSeq = $qInfo['gseq']; if ($LEM->currentGroupSeq > $LEM->maxGroupSeq)// Did we need it ? { $LEM->maxGroupSeq = $LEM->currentGroupSeq; @@ -4501,6 +4418,99 @@ static function NavigateBackwards() } } + private function navigateNextQuestion($force) { + $this->StartProcessingPage(); + $session = App()->surveySessionManager->current; + $this->processData($session->response, $_POST); + $message = ''; + if (!$force) { + // Validate current page. + $validateResult = $this->_ValidateQuestion($session->step); + $message .= $validateResult['message']; + + + $gRelInfo = $this->getGroupRelevanceInfo($session->getQuestionByIndex($session->step)->gid); + $grel = $gRelInfo['result']; + if ($grel && !is_null($validateResult) && ($validateResult['mandViolation'] || !$validateResult['valid'])) { + // redisplay the current question with all error + $message .= $this->_UpdateValuesInDatabase(false); + $result = [ + 'finished' => false, + 'message' => $message, + 'qseq' => $session->step, + 'gseq' => $session->getGroupIndex($session->currentGroup->primaryKey), + 'seq' => $session->step, + 'mandViolation' => $validateResult['mandViolation'], + 'valid' => $validateResult['valid'], + 'unansweredSQs' => $validateResult['unansweredSQs'], + 'invalidSQs' => $validateResult['invalidSQs'], + ]; + } + } + $step = $session->step; + + $stepCount = $session->stepCount; + if ($force || !isset($result)) { + for ($step = $session->step + 1; $step <= $stepCount; $step++) { + $this->currentQset = []; // reset active list of questions + + + if ($step >= $session->stepCount) // Move next with finished, but without submit. + { + $message .= $this->_UpdateValuesInDatabase(true); + $result = [ + 'finished' => true, + 'message' => $message, + 'qseq' => $step, + 'gseq' => $session->getGroupIndex($session->currentGroup->primaryKey), + 'seq' => $step, + 'mandViolation' => (($session->maxStep > $step) ? $result['mandViolation'] : false), + 'valid' => (($session->maxStep > $step) ? $result['valid'] : true), + 'unansweredSQs' => (isset($result['unansweredSQs']) ? $result['unansweredSQs'] : ''), + 'invalidSQs' => (isset($result['invalidSQs']) ? $result['invalidSQs'] : ''), + ]; + + break; + } + + // Set certain variables normally set by StartProcessingGroup() + + $this->ProcessAllNeededRelevance($step); + $this->_CreateSubQLevelRelevanceAndValidationEqns($step); + $validateResult = $this->_ValidateQuestion($step); + $message .= $validateResult['message']; + $gRelInfo = $this->getGroupRelevanceInfo($session->getQuestionByIndex($step)->gid); + $grel = $gRelInfo['result']; + + if (!$grel || !$validateResult['relevant'] || $validateResult['hidden']) { + // then skip this question, $this->updatedValues updated in _ValidateQuestion + continue; + } else { + // Display new question + // Show error only if this question are not viewed before (question hidden by condition before <= maxQuestionSeq>currentQuestionSeq) + $message .= $this->_UpdateValuesInDatabase(false); + $result = [ + 'finished' => false, + 'message' => $message, + 'qseq' => $step, + 'gseq' => $this->currentGroupSeq, + 'seq' => $step, + 'mandViolation' => (($session->maxStep >= $step) ? $validateResult['mandViolation'] : false), + 'valid' => (($session->maxStep >= $step) ? $validateResult['valid'] : false), + 'unansweredSQs' => $validateResult['unansweredSQs'], + 'invalidSQs' => $validateResult['invalidSQs'], + ]; + break; + + } + $step++; + } + } + if (!isset($result) || !array_key_exists('finished', $result)) { + throw new \UnexpectedValueException("Result should not be null, and should contain proper keys"); + } + return $result; + } /** * * @param $force - if true, continue to go forward even if there are violations to the mandatory and/or validity rules @@ -4614,96 +4624,14 @@ static function NavigateForwards($force = false) } break; case Survey::FORMAT_QUESTION: - $LEM->StartProcessingPage(); - $updatedValues = $LEM->ProcessCurrentResponses(); - $message = ''; - if (!$force && $session->step != -1) { - $result = $LEM->_ValidateQuestion($session->step); - $message .= $result['message']; - $updatedValues = array_merge($updatedValues, $result['updatedValues']); - - $gRelInfo = $LEM->getGroupRelevanceInfo($session->getQuestionByIndex($session->step)->group->primaryKey); - $grel = $gRelInfo['result']; - if ($grel && !is_null($result) && ($result['mandViolation'] || !$result['valid'])) { - // redisplay the current question with all error - $message .= $LEM->_UpdateValuesInDatabase(false); - $LEM->lastMoveResult = array( - 'finished' => false, - 'message' => $message, - 'qseq' => $LEM->currentQuestionSeq, - 'gseq' => $LEM->currentGroupSeq, - 'seq' => $LEM->currentQuestionSeq, - 'mandViolation' => $result['mandViolation'], - 'valid' => $result['valid'], - 'unansweredSQs' => $result['unansweredSQs'], - 'invalidSQs' => $result['invalidSQs'], - ); - - return $LEM->lastMoveResult; - } - } - while (true) { - $LEM->currentQset = []; // reset active list of questions - if (++$LEM->currentQuestionSeq >= $LEM->numQuestions) // Move next with finished, but without submit. - { - $message .= $LEM->_UpdateValuesInDatabase(true); - $LEM->lastMoveResult = array( - 'finished' => true, - 'message' => $message, - 'qseq' => $LEM->currentQuestionSeq, - 'gseq' => $LEM->currentGroupSeq, - 'seq' => $LEM->currentQuestionSeq, - 'mandViolation' => (($LEM->maxQuestionSeq > $LEM->currentQuestionSeq) ? $result['mandViolation'] : false), - 'valid' => (($LEM->maxQuestionSeq > $LEM->currentQuestionSeq) ? $result['valid'] : true), - 'unansweredSQs' => (isset($result['unansweredSQs']) ? $result['unansweredSQs'] : ''), - 'invalidSQs' => (isset($result['invalidSQs']) ? $result['invalidSQs'] : ''), - ); - - return $LEM->lastMoveResult; - } - - // Set certain variables normally set by StartProcessingGroup() - $LEM->groupRelevanceInfo = array(); // TODO only important thing from StartProcessingGroup? - $qInfo = $LEM->questionSeq2relevance[$LEM->currentQuestionSeq]; - $LEM->currentQID = $qInfo['qid']; - $LEM->currentGroupSeq = $qInfo['gseq']; - if ($LEM->currentGroupSeq > $LEM->maxGroupSeq) { - $LEM->maxGroupSeq = $LEM->currentGroupSeq; - } - - $LEM->ProcessAllNeededRelevance($LEM->currentQuestionSeq); - $LEM->_CreateSubQLevelRelevanceAndValidationEqns($LEM->currentQuestionSeq); - $result = $LEM->_ValidateQuestion($LEM->currentQuestionSeq); - $message .= $result['message']; - $updatedValues = array_merge($updatedValues, $result['updatedValues']); - $gRelInfo = $LEM->getGroupRelevanceInfo($LEM->currentGroupSeq); - $grel = $gRelInfo['result']; - - if (!$grel || !$result['relevant'] || $result['hidden']) { - // then skip this question, $LEM->updatedValues updated in _ValidateQuestion - continue; - } else { - // Display new question - // Show error only if this question are not viewed before (question hidden by condition before <= maxQuestionSeq>currentQuestionSeq) - $message .= $LEM->_UpdateValuesInDatabase(false); - $LEM->lastMoveResult = array( - 'finished' => false, - 'message' => $message, - 'qseq' => $LEM->currentQuestionSeq, - 'gseq' => $LEM->currentGroupSeq, - 'seq' => $LEM->currentQuestionSeq, - 'mandViolation' => (($LEM->maxQuestionSeq > $LEM->currentQuestionSeq) ? $result['mandViolation'] : false), - 'valid' => (($LEM->maxQuestionSeq > $LEM->currentQuestionSeq) ? $result['valid'] : false), - 'unansweredSQs' => $result['unansweredSQs'], - 'invalidSQs' => $result['invalidSQs'], - ); - - return $LEM->lastMoveResult; - } - } + $result = $LEM->navigateNextQuestion($force); break; default: throw new \Exception("Unknown survey format"); } + if ($result === null) { + throw new \UnexpectedValueException("Result should not be null"); + } + return $result; } /** @@ -4715,7 +4643,7 @@ private function _UpdateValuesInDatabase($finished = false) { $session = App()->surveySessionManager->current; // TODO - now that using $this->updatedValues, may be able to remove local copies of it (unless needed by other sub-systems) - $updatedValues = $this->updatedValues; + $updatedValues = []; if (array_key_exists('', $updatedValues)) { throw new \Exception("Empty string is not a valid key for updated values."); @@ -4728,17 +4656,7 @@ private function _UpdateValuesInDatabase($finished = false) if (count($updatedValues) > 0 || $finished) { $setter = array(); - switch ($session->format) { - case Survey::FORMAT_QUESTION: - $thisstep = $this->currentQuestionSeq; - break; - case Survey::FORMAT_GROUP: - $thisstep = $this->currentGroupSeq; - break; - case Survey::FORMAT_ALL_IN_ONE: - $thisstep = 1; - break; - } + $thisstep = $session->step; $response->lastpage = $session->step; @@ -4771,14 +4689,7 @@ private function _UpdateValuesInDatabase($finished = false) } $response->$key = $val; } - if (!$response->save()) { - echo submitfailed("Error in query: $query"); // TODO - report SQL error? - - if (($this->debugLevel & LEM_DEBUG_VALIDATION_SUMMARY) == LEM_DEBUG_VALIDATION_SUMMARY) { - $message .= $this->gT('Error in SQL update'); // TODO - add SQL error? - } - } // Save Timings if needed - elseif ($this->surveyOptions['savetimings']) { + if ($session->survey->bool_savetimings) { Yii::import("application.libraries.Save"); $cSave = new Save(); $cSave->set_answer_time(); @@ -4794,7 +4705,7 @@ private function _UpdateValuesInDatabase($finished = false) } } else { - if ($this->surveyOptions['allowsave'] && isset($_SESSION[$this->sessid]['scid'])) { + if ($session->survey->bool_allowsave && isset($_SESSION[$this->sessid]['scid'])) { SavedControl::model()->updateByPk($_SESSION[$this->sessid]['scid'], array('saved_thisstep' => $thisstep)); } @@ -4804,8 +4715,8 @@ private function _UpdateValuesInDatabase($finished = false) if ($aQuotas && !empty($aQuotas)) { checkCompletedQuota($this->sid); // will create a page and quit: why not use it directly ? } elseif ($finished) { - $session->response->markAsFinished(); - $session->response->save(); + $session->response->markAsFinished(); + $session->response->save(); } } @@ -4827,6 +4738,103 @@ static function GetLastMoveResult($clearSubstitutionInfo = false) return (isset($LEM->lastMoveResult) ? $LEM->lastMoveResult : null); } + private function processData(Response $response, array $data) { + $response->setAttributes($data); + $response->save(); + } + + private function jumpToQuestion($seq, $preview, $processPOST, $force) { + $this->StartProcessingPage(); + $session = App()->surveySessionManager->current; + if ($processPOST) { + $this->processData($session->response, $_POST); + } else { + $updatedValues = array(); + } + $message = ''; + // Validate if moving forward. + if (!$force && $seq > $session->step) { + $validateResult = $this->_ValidateQuestion($session->step, $force); + $message .= $validateResult['message']; + $gRelInfo = $this->getGroupRelevanceInfo($session->getQuestion($session->step)->gid); + $grel = $gRelInfo['result']; + if ($grel && ($validateResult['mandViolation'] || !$validateResult['valid'])) { + // Redisplay the current question, qhowning error + $message .= $this->_UpdateValuesInDatabase(false); + $result = $this->lastMoveResult = [ + 'finished' => false, + 'message' => $message, + 'mandViolation' => (($session->maxStep > $session->step) ? $validateResult['mandViolation'] : false), + 'valid' => (($session->maxStep > $session->step) ? $validateResult['valid'] : true), + 'unansweredSQs' => $validateResult['unansweredSQs'], + 'invalidSQs' => $validateResult['invalidSQs'], + ]; + } + } + $stepCount = $session->stepCount; + for ($step = $seq; $step < $stepCount; $step++) { + $this->currentQset = []; // reset active list of questions + + + $groupId = $session->getQuestionByIndex($step)->gid; + // Set certain variables normally set by StartProcessingGroup() + $qInfo = $this->getQuestionRelevanceInfo($session->getQuestionByIndex($step)); + $this->ProcessAllNeededRelevance($step); + $this->_CreateSubQLevelRelevanceAndValidationEqns($step); + $validateResult = $this->_ValidateQuestion($step, $force); + $message .= $validateResult['message']; + $updatedValues = array_merge($updatedValues, $validateResult['updatedValues']); + + $gRelInfo = $this->getGroupRelevanceInfo($groupId); + $grel = $gRelInfo['result']; + + if (!$preview && (!$grel || !$validateResult['relevant'] || $validateResult['hidden'])) { + // then skip this question + continue; + } elseif (!$preview && !($validateResult['mandViolation'] || !$validateResult['valid']) && $step < $seq) { + // if there is a violation while moving forward, need to stop and ask that set of questions + // if there are no violations, can skip this group as long as changed values are saved. + die('skip2'); + continue; + } else { +// die('break'); + // Display new question + // Showing error if question are before the maxstep + $message .= $this->_UpdateValuesInDatabase(false); + $result = [ + 'finished' => false, + 'message' => $message, + 'qseq' => $step, + 'gseq' => $session->getGroupIndex($session->getQuestionByIndex($step)->gid), + 'seq' => $step, + 'mandViolation' => (($session->maxStep > $step) ? $validateResult['mandViolation'] : false), + 'valid' => (($session->maxStep > $step) ? $validateResult['vaslid'] : true), + 'unansweredSQs' => $validateResult['unansweredSQs'], + 'invalidSQs' => $validateResult['invalidSQs'], + ]; + break; + } + } + if ($step >= $session->stepCount) { + die('noo finished?'); + $message .= $this->_UpdateValuesInDatabase(true); + $result = [ + 'finished' => true, + 'message' => $message, + 'qseq' => $step, + 'gseq' => $this->currentGroupSeq, + 'seq' => $step, + 'mandViolation' => (isset($result['mandViolation']) ? $result['mandViolation'] : false), + 'valid' => (isset($result['valid']) ? $result['valid'] : false), + 'unansweredSQs' => (isset($result['unansweredSQs']) ? $result['unansweredSQs'] : ''), + 'invalidSQs' => (isset($result['invalidSQs']) ? $result['invalidSQs'] : ''), + ]; + + + + } + return $result; + } /** * Jump to a specific question or group sequence. If jumping forward, it re-validates everything in between * @param $seq @@ -4837,6 +4845,9 @@ static function GetLastMoveResult($clearSubstitutionInfo = false) static function JumpTo($seq, $preview = false, $processPOST = true, $force = false, $changeLang = false) { bP(); + if ($seq < 0) { + throw new \InvalidArgumentException("Sequence must be >= 0"); + } $session = App()->surveySessionManager->current; $LEM =& LimeExpressionManager::singleton(); if (!$preview) { @@ -4847,13 +4858,12 @@ static function JumpTo($seq, $preview = false, $processPOST = true, $force = fal } if ($changeLang) { - $LEM->setVariableAndTokenMappingsForExpressionManager($LEM->sid, true, + $LEM->setVariableAndTokenMappingsForExpressionManager($session->surveyId, true, $LEM->surveyOptions['anonymized']); } $LEM->ParseResultCache = []; // to avoid running same test more than once for a given group $LEM->updatedValues = []; - --$seq; // convert to 0-based numbering switch ($session->format) { case Survey::FORMAT_ALL_IN_ONE: // This only happens if saving data so far, so don't want to submit it, just validate and return @@ -4870,10 +4880,9 @@ static function JumpTo($seq, $preview = false, $processPOST = true, $force = fal $result = $LEM->_ValidateSurvey($force); $message .= $result['message']; $updatedValues = array_merge($updatedValues, $result['updatedValues']); - $finished = false; - $message .= $LEM->_UpdateValuesInDatabase($finished);// This happen too for $processPOST=false : need to fix it ? + $message .= $LEM->_UpdateValuesInDatabase(false);// This happen too for $processPOST=false : need to fix it ? $LEM->lastMoveResult = array( - 'finished' => $finished, + 'finished' => false, 'message' => $message, 'gseq' => 1, 'seq' => 1, @@ -4890,7 +4899,7 @@ static function JumpTo($seq, $preview = false, $processPOST = true, $force = fal $LEM->StartProcessingPage(); $updatedValues = $processPOST ? $LEM->ProcessCurrentResponses() : []; $message = ''; - if (!$force && $LEM->currentGroupSeq != -1 && $seq > $LEM->currentGroupSeq) // only re-validate if jumping forward + if (!$force && $seq > $session->step) // only re-validate if jumping forward { $result = $LEM->_ValidateGroup($LEM->currentGroupSeq); $message .= $result['message']; @@ -4973,98 +4982,7 @@ static function JumpTo($seq, $preview = false, $processPOST = true, $force = fal } break; case Survey::FORMAT_QUESTION: - $LEM->StartProcessingPage(); - if ($processPOST) { - $updatedValues = $LEM->ProcessCurrentResponses(); - } else { - $updatedValues = array(); - } - $message = ''; - if (!$force && $session->step != -1 && $seq > $session->step) { - $result = $LEM->_ValidateQuestion($session->step, $force); - $message .= $result['message']; - $updatedValues = array_merge($updatedValues, $result['updatedValues']); - $gRelInfo = $LEM->getGroupRelevanceInfo($session->getQuestion($session->step)); - $grel = $gRelInfo['result']; - if ($grel && ($result['mandViolation'] || !$result['valid'])) { - // Redisplay the current question, qhowning error - $message .= $LEM->_UpdateValuesInDatabase(false); - $LEM->lastMoveResult = [ - 'finished' => false, - 'message' => $message, - 'mandViolation' => (($session->maxStep > $session->step) ? $result['mandViolation'] : false), - 'valid' => (($session->maxStep > $session->step) ? $result['valid'] : true), - 'unansweredSQs' => $result['unansweredSQs'], - 'invalidSQs' => $result['invalidSQs'], - ]; - - $result = $LEM->lastMoveResult; - break; - } - } - if ($seq <= $LEM->currentQuestionSeq || $preview) { - $LEM->currentQuestionSeq = $seq - 1; // Try to jump to the requested group, but navigate to next if needed - } - while (true) { - $LEM->currentQset = []; // reset active list of questions - - if (++$LEM->currentQuestionSeq >= $session->getStepCount()) { - $message .= $LEM->_UpdateValuesInDatabase(true); - $LEM->lastMoveResult = array( - 'finished' => true, - 'message' => $message, - 'qseq' => $LEM->currentQuestionSeq, - 'gseq' => $LEM->currentGroupSeq, - 'seq' => $LEM->currentQuestionSeq, - 'mandViolation' => (isset($result['mandViolation']) ? $result['mandViolation'] : false), - 'valid' => (isset($result['valid']) ? $result['valid'] : false), - 'unansweredSQs' => (isset($result['unansweredSQs']) ? $result['unansweredSQs'] : ''), - 'invalidSQs' => (isset($result['invalidSQs']) ? $result['invalidSQs'] : ''), - ); - - $result = $LEM->lastMoveResult; - break; - } - $groupId = $session->getQuestionByIndex($LEM->currentQuestionSeq)->group->primaryKey; - // Set certain variables normally set by StartProcessingGroup() - $LEM->groupRelevanceInfo = array(); // TODO only important thing from StartProcessingGroup? - $qInfo = $LEM->getQuestionRelevanceInfo($session->getQuestionByIndex($LEM->currentQuestionSeq)); - $LEM->currentQID = $qInfo['qid']; - $LEM->ProcessAllNeededRelevance($LEM->currentQuestionSeq); - $LEM->_CreateSubQLevelRelevanceAndValidationEqns($LEM->currentQuestionSeq); - $result = $LEM->_ValidateQuestion($LEM->currentQuestionSeq, $force); - $message .= $result['message']; - $updatedValues = array_merge($updatedValues, $result['updatedValues']); - - $gRelInfo = $LEM->getGroupRelevanceInfo($groupId); - $grel = $gRelInfo['result']; - - if (!$preview && (!$grel || !$result['relevant'] || $result['hidden'])) { - // then skip this question - continue; - } elseif (!$preview && !($result['mandViolation'] || !$result['valid']) && $LEM->currentQuestionSeq < $seq) { - // if there is a violation while moving forward, need to stop and ask that set of questions - // if there are no violations, can skip this group as long as changed values are saved. - continue; - } else { - // Display new question - // Showing error if question are before the maxstep - $message .= $LEM->_UpdateValuesInDatabase(false); - $LEM->lastMoveResult = array( - 'finished' => false, - 'message' => $message, - 'qseq' => $LEM->currentQuestionSeq, - 'gseq' => $LEM->currentGroupSeq, - 'seq' => $LEM->currentQuestionSeq, - 'mandViolation' => (($LEM->maxQuestionSeq > $LEM->currentQuestionSeq) ? $result['mandViolation'] : false), - 'valid' => (($LEM->maxQuestionSeq > $LEM->currentQuestionSeq) ? $result['valid'] : true), - 'unansweredSQs' => $result['unansweredSQs'], - 'invalidSQs' => $result['invalidSQs'], - ); - $result = $LEM->lastMoveResult; - break; - } - } + $result = $LEM->jumpToQuestion($seq, $preview, $processPOST, $force); break; default: throw new \Exception("Unknown survey mode: " . $session->format); @@ -5072,6 +4990,9 @@ static function JumpTo($seq, $preview = false, $processPOST = true, $force = fal eP(); + if ($result === null) { + throw new \UnexpectedValueException("Result should not be null"); + } return $result; } @@ -5317,17 +5238,13 @@ private function _ValidateQuestion($questionSeq, $force = false) $LEM =& $this; $session = App()->surveySessionManager->current; $knownVars = $this->getKnownVars(); + $question = $session->getQuestionByIndex($questionSeq); $qInfo = $this->getQuestionRelevanceInfo($session->getQuestionByIndex($questionSeq)); - // We try to validate this question, then update the maxQuestionSeq, TODO : validate if we can update the maxGroupSeq too. - if ($questionSeq > $LEM->maxQuestionSeq) // max() take a little time more (2/3) - { - $LEM->maxQuestionSeq = $questionSeq; - } $qrel = true; // assume relevant unless discover otherwise $prettyPrintRelEqn = ''; // assume no relevance eqn by default - $qid = $qInfo['qid']; - $gid = $qInfo['gid']; + $qid = $question->qid; + $gid = $question->gid; $gseq = $session->getGroupIndex($gid); $qhidden = $qInfo['hidden']; $debug_qmessage = ''; @@ -5343,31 +5260,17 @@ private function _ValidateQuestion($questionSeq, $force = false) } else { $relevanceEqn = $qInfo['relevance']; } - // cache results + $relevanceEqn = htmlspecialchars_decode($relevanceEqn, ENT_QUOTES); // TODO is this needed? - if (isset($LEM->ParseResultCache[$relevanceEqn])) { - $qrel = $LEM->ParseResultCache[$relevanceEqn]['result']; - if (($LEM->debugLevel & LEM_PRETTY_PRINT_ALL_SYNTAX) == LEM_PRETTY_PRINT_ALL_SYNTAX) { - $prettyPrintRelEqn = $LEM->ParseResultCache[$relevanceEqn]['prettyprint']; - } - } else { - // assumes safer to re-process relevance and not trust POST values - $qrel = $LEM->em->ProcessBooleanExpression($relevanceEqn, $gseq, $session->getQuestionIndex($qInfo['qid'])); + // assumes safer to re-process relevance and not trust POST values + $qrel = $LEM->em->ProcessBooleanExpression($relevanceEqn, $gseq, $question->primaryKey); +// var_dump("$relevanceEqn --> $qrel"); - $hasErrors = $LEM->em->HasErrors(); - if (($LEM->debugLevel & LEM_PRETTY_PRINT_ALL_SYNTAX) == LEM_PRETTY_PRINT_ALL_SYNTAX) { - $prettyPrintRelEqn = $LEM->em->GetPrettyPrintString(); - } - $LEM->ParseResultCache[$relevanceEqn] = array( - 'result' => $qrel, - 'prettyprint' => $prettyPrintRelEqn, - 'hasErrors' => $hasErrors, - ); - } - // Do NOT hide the questions if there is an error in the relevance equation - if ($LEM->ParseResultCache[$relevanceEqn]['hasErrors'] == true) { - $qrel = true; + $hasErrors = $LEM->em->HasErrors(); + if (($LEM->debugLevel & LEM_PRETTY_PRINT_ALL_SYNTAX) == LEM_PRETTY_PRINT_ALL_SYNTAX) { + $prettyPrintRelEqn = $LEM->em->GetPrettyPrintString(); } + ////////////////////////////////////// // ARE ANY SUB-QUESTION IRRELEVANT? // ////////////////////////////////////// @@ -5593,10 +5496,10 @@ function ($sqRankAnwsers) { // WHICH RELEVANT, VISIBLE (SUB)-QUESTIONS HAVEN'T BEEN ANSWERED? // //////////////////////////////////////////////////////////////////// // check that all mandatories have been fully answered (but don't require answers for sub-questions that are irrelevant - $unansweredSQs = array(); // list of sub-questions that weren't answered + $unansweredSQs = []; // list of sub-questions that weren't answered + foreach ($relevantSQs as $sgqa) { - if (($qInfo['type'] != '*') && empty(App()->surveySessionManager->current->response->$sgqa)) { - var_dump($sgqa); + if (($question->type != Question::TYPE_EQUATION) && empty($session->response->$sgqa)) { // then a relevant, visible, mandatory question hasn't been answered // Equations are ignored, since set automatically @@ -5607,19 +5510,17 @@ function ($sqRankAnwsers) { ////////////////////////////////////////////// // DETECT ANY VIOLATIONS OF MANDATORY RULES // ////////////////////////////////////////////// - $qmandViolation = false; // assume there is no mandatory violation until discover otherwise + + $qmandViolation = !$question->validateMandatory($session->response); $mandatoryTip = ''; - if ($qrel && !$qhidden && ($qInfo['mandatory'] == 'Y')) { + if ($qrel && !$qhidden && $qmandViolation) { $mandatoryTip = "
" . $LEM->gT('This question is mandatory') . '. '; - switch ($qInfo['type']) { - case 'M': - case 'P': - case '!': //List - dropdown - case 'L': //LIST drop-down/radio-button list + switch ($question->type) { + case Question::TYPE_MULTIPLE_CHOICE: + case Question::TYPE_MULTIPLE_CHOICE_WITH_COMMENT: + case Question::TYPE_DROPDOWN_LIST: + case Question::TYPE_RADIO_LIST: // If at least one checkbox is checked, we're OK - if (count($relevantSQs) > 0 && (count($relevantSQs) == count($unansweredSQs))) { - $qmandViolation = true; - } if (!($qInfo['type'] == '!' || $qInfo['type'] == 'L')) { $mandatoryTip .= $LEM->gT('Please check at least one item.'); } @@ -5704,14 +5605,9 @@ function ($sqRankAnwsers) { $qmandViolation = true; } break; - default: - if (count($unansweredSQs) > 0) { - $qmandViolation = true; - } - break; } $mandatoryTip .= "
\n"; - } + } ///////////////////////////////////////////////////////////// // DETECT WHETHER QUESTION SHOULD BE FLAGGED AS UNANSWERED // @@ -5867,16 +5763,14 @@ function ($sqRankAnwsers) { // CREATE ARRAY OF VALUES THAT NEED TO BE SILENTLY UPDATED // ///////////////////////////////////////////////////////////// $updatedValues = array(); - if ((!$qrel || !$grel) && $LEM->surveyOptions['deletenonvalues']) { + if ((!$qrel || !$grel) && SettingGlobal::get('deletenonvalues')) { // If not relevant, then always NULL it in the database $sgqas = explode('|', $LEM->qid2code[$qid]); foreach ($sgqas as $sgqa) { - $_SESSION[$LEM->sessid][$sgqa] = null; - $updatedValues[$sgqa] = null; + $session->response->$sgqa = null; if ($sgqa == '') { throw new \Exception("Invalid sgqa: ''"); } - $LEM->updatedValues[$sgqa] = null; } } elseif ($qInfo['type'] == '*') { // Process relevant equations, even if hidden, and write the result to the database @@ -5992,8 +5886,6 @@ function ($sqRankAnwsers) { 'mandViolation' => $qmandViolation, 'valid' => $qvalid, ); - $_SESSION[$LEM->sessid]['relevanceStatus'][$qid] = $qrel; - return $qStatus; } @@ -6156,14 +6048,14 @@ static function GetRelevanceAndTailoringJavaScript() $jsParts = array(); $allJsVarsUsed = array(); $rowdividList = array(); // list of subquestions needing relevance entries - App()->getClientScript()->registerScriptFile(SettingGlobal::get('generalscripts') . "expressions/em_javascript.js");; + App()->getClientScript()->registerScriptFile(SettingGlobal::get('generalscripts', '/scripts') . "/expressions/em_javascript.js");; $jsParts[] = "\n -EOD; - - print LimeExpressionManager::GetRelevanceAndTailoringJavaScript(); - - // Print Table of questions - print "

This is a test of dynamic relevance.

"; - print "Enter your name and age, and try all the permutations of answers to whether you have or want children.
\n"; - print "Note how the text and sum of ages changes dynamically; that prior answers are remembered; and that irrelevant values are not included in the sum of ages.
"; - print "
"; - foreach ($argInfo as $arg) { - $rel = LimeExpressionManager::QuestionIsRelevant($arg['num']); - print "
"; - LimeExpressionManager::SetDirtyFlag(); // so subsequent tests don't try to access these variables - } /** * Set the 'this' variable as an alias for SGQA within the code. @@ -7554,15 +7065,6 @@ static function ProcessCurrentResponses() $value = $oDateTimeConverter->convert("Y-m-d H:i"); // TODO : control if inverse function original value } break; -# case 'N': //NUMERICAL QUESTION TYPE -# case 'K': //MULTIPLE NUMERICAL QUESTION -# if (trim($value)=="") { -# $value = ""; -# } -# else { -# $value = sanitize_float($value); -# } - break; case '|': //File Upload if (!preg_match('/_filecount$/', $sq)) { $json = $value; @@ -7599,7 +7101,7 @@ static function ProcessCurrentResponses() 'type' => $type, 'value' => $value, ); - $updatedValues[$sq] = $LEM->updatedValues[$sq] = $_update; + $updatedValues[$sq] = $_update; } else { // irrelevant, so database will be NULLed separately // Must unset the value, rather than setting to '', so that EM can re-use the default value as needed. unset($session->response->$sq); @@ -7607,7 +7109,7 @@ static function ProcessCurrentResponses() 'type' => $type, 'value' => null, ); - $updatedValues[$sq] = $LEM->updatedValues[$sq] = $_update; + $updatedValues[$sq] = $_update; } } } @@ -7623,10 +7125,10 @@ static public function isValidVariable($varName) { $LEM =& LimeExpressionManager::singleton(); - if (isset($LEM->knownVars[$varName])) { + if (isset($LEM->getKnownVars()[$varName])) { return true; } else { - if (isset($LEM->qcode2sgqa[$varName])) { + if (null !== $LEM->getSgqa($varName)) { return true; } else { if (isset($LEM->tempVars[$varName])) { @@ -7640,29 +7142,44 @@ static public function isValidVariable($varName) static public function GetVarAttribute($name, $attr, $default, $gseq, $qseq) { - $LEM =& LimeExpressionManager::singleton(); - - return $LEM->_GetVarAttribute($name, $attr, $default, $gseq, $qseq); + return LimeExpressionManager::singleton()->_GetVarAttribute($name, $attr, $default, $gseq, $qseq); } + /** + * Gets the sgqa for a code, or null if code is not found. + * @param string $code + * @return string + */ + private function getSgqa($code) { + bP(); + $session = App()->surveySessionManager->current; + $result = null; + foreach($session->survey->questions as $question) { + if ($question->title == $code) { + $result = $question->sgqa; + break; + } + } + eP(); + return $result; + } private function _GetVarAttribute($name, $attr, $default, $gseq, $qseq) { $session = App()->surveySessionManager->current; + $response = $session->response; $args = explode(".", $name); $varName = $args[0]; $varName = preg_replace("/^(?:INSERTANS:)?(.*?)$/", "$1", $varName); $knownVars = $this->getKnownVars(); if (isset($knownVars[$varName])) { $var = $knownVars[$varName]; + } elseif (null !== $sgqa = $this->getSgqa($varName)) { + $var = $knownVars[$sgqa]; } else { - if (isset($this->qcode2sgqa[$varName])) { - $var = $knownVars[$this->qcode2sgqa[$varName]]; + if (isset($this->tempVars[$varName])) { + $var = $this->tempVars[$varName]; } else { - if (isset($this->tempVars[$varName])) { - $var = $this->tempVars[$varName]; - } else { - return '{' . $name . '}'; - } + return '{' . $name . '}'; } } $sgqa = isset($var['sgqa']) ? $var['sgqa'] : null; @@ -7690,7 +7207,7 @@ private function _GetVarAttribute($name, $attr, $default, $gseq, $qseq) if (isset($var['code'])) { return $var['code']; // for static values like TOKEN } else { - if (isset($_SESSION[$this->sessid][$sgqa])) { + if (isset($response->$sgqa)) { $type = $var['type']; switch ($type) { case 'Q': //MULTIPLE SHORT TEXT @@ -7699,8 +7216,8 @@ private function _GetVarAttribute($name, $attr, $default, $gseq, $qseq) case 'D': //DATE case 'T': //LONG FREE TEXT case 'U': //HUGE FREE TEXT - return htmlspecialchars($_SESSION[$this->sessid][$sgqa], - ENT_NOQUOTES);// Minimum sanitizing the string entered by user + // Minimum sanitizing the string entered by user + return htmlspecialchars($response->$sgqa, ENT_NOQUOTES); case '!': //List - dropdown case 'L': //LIST drop-down/radio-button list case 'O': //LIST WITH COMMENT drop-down/radio-button list + textarea @@ -7709,18 +7226,15 @@ private function _GetVarAttribute($name, $attr, $default, $gseq, $qseq) if (preg_match('/comment$/', $sgqa) || preg_match('/other$/', $sgqa) || preg_match('/_other$/', $name) ) { - return htmlspecialchars($_SESSION[$this->sessid][$sgqa], - ENT_NOQUOTES);// Minimum sanitizing the string entered by user - } else { - return $_SESSION[$this->sessid][$sgqa]; + // Minimum sanitizing the string entered by user + return htmlspecialchars($response->$sgqa, ENT_NOQUOTES); } default: - return $_SESSION[$this->sessid][$sgqa]; + return $response->$sgqa; } } elseif (isset($var['default']) && !is_null($var['default'])) { return $var['default']; } - return $default; } break; @@ -7894,15 +7408,12 @@ private function _GetVarAttribute($name, $attr, $default, $gseq, $qseq) $qid = (isset($var['qid'])) ? $var['qid'] : -1; $rowdivid = (isset($var['rowdivid']) && $var['rowdivid'] != '') ? $var['rowdivid'] : -1; if ($qid == -1 || $gseq == -1) { - return 1; + return true; } if (isset($args[1]) && $args[1] == 'NAOK') { - return 1; + return true; } - $grel = (isset($_SESSION[$this->sessid]['relevanceStatus']['G' . $gseq]) ? $_SESSION[$this->sessid]['relevanceStatus']['G' . $gseq] : 1); // true by default - $qrel = (isset($_SESSION[$this->sessid]['relevanceStatus'][$qid]) ? $_SESSION[$this->sessid]['relevanceStatus'][$qid] : 0); - $sqrel = (isset($_SESSION[$this->sessid]['relevanceStatus'][$rowdivid]) ? $_SESSION[$this->sessid]['relevanceStatus'][$rowdivid] : 1); // true by default - only want false if a subquestion is irrelevant - return ($grel && $qrel && $sqrel); + return true; case 'onlynum': if (isset($args[1]) && ($args[1] == 'value' || $args[1] == 'valueNAOK')) { return 1; @@ -7943,10 +7454,7 @@ public static function SetVariableValue($op, $name, $value) } $_result = $LEM->tempVars[$name]['code']; $session->response->$name = $_result; - $LEM->updatedValues[$name] = array( - 'type' => '*', - 'value' => $_result, - ); + return $_result; } else { @@ -7982,10 +7490,6 @@ public static function SetVariableValue($op, $name, $value) } $session->response->$name = $_result; $_type = $LEM->knownVars[$name]['type']; - $LEM->updatedValues[$name] = array( - 'type' => $_type, - 'value' => $_result, - ); return $_result; } @@ -9004,7 +8508,6 @@ public function getKnownVars() { $questionCounter = 0; - foreach ($fieldmap as $fielddata) { if (!isset($fielddata['fieldname']) || !preg_match('#^\d+X\d+X\d+#', $fielddata['fieldname'])) { continue; // not an SGQA value @@ -9401,9 +8904,6 @@ public function getPageRelevanceInfo() { foreach($session->getQuestions($session->getGroupByIndex($session->step)) as $question) { $pageRelevanceInfo[] = $this->processQuestionRelevance($question); } -// var_dump($pageRelevanceInfo); -// die(); -// $pageRelevanceInfo = [$LEM->getGroupRelevanceInfo($session->groups[$session->step - 1]->primaryKey)]; break; case Survey::FORMAT_ALL_IN_ONE: $pageRelevanceInfo = []; diff --git a/application/helpers/frontend_helper.php b/application/helpers/frontend_helper.php index ab0a2e765bf..2d50892d6cf 100644 --- a/application/helpers/frontend_helper.php +++ b/application/helpers/frontend_helper.php @@ -138,13 +138,13 @@ function loadanswers() } } -function makegraph($currentstep, $total) +function makegraph($step, $total) { global $thissurvey; Yii::app()->getClientScript()->registerCssFile(Yii::app()->getConfig('publicstyleurl') . 'lime-progress.css'); - $size = intval(($currentstep-1)/$total*100); + $size = intval(($step)/$total*100); $graph = '