diff --git a/application/helpers/expressions/em_core_helper.php b/application/helpers/expressions/em_core_helper.php index c52679b74a1..84a01b7809d 100644 --- a/application/helpers/expressions/em_core_helper.php +++ b/application/helpers/expressions/em_core_helper.php @@ -29,7 +29,7 @@ class ExpressionManager { // These are the allowable suffixes for variables - each represents an attribute of a variable. - private static $RDP_regex_var_attr = 'code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value'; + static $RDP_regex_var_attr = 'code|gid|grelevance|gseq|jsName|mandatory|NAOK|qid|qseq|question|readWrite|relevanceStatus|relevance|rowdivid|sgqa|shown|type|valueNAOK|value'; // These three variables are effectively static once constructed private $RDP_ExpressionRegex; @@ -82,7 +82,7 @@ function __construct() $RDP_regex_compare = '<=|<|>=|>|==|!=|\ble\b|\blt\b|\bge\b|\bgt\b|\beq\b|\bne\b'; $RDP_regex_assign = '='; // '=|\+=|-=|\*=|/='; $RDP_regex_sgqa = '(?:INSERTANS:)?[0-9]+X[0-9]+X[0-9]+[A-Z0-9_]*\#?[01]?(?:\.(?:' . ExpressionManager::$RDP_regex_var_attr . '))?'; - $RDP_regex_word = '(?:TOKEN:)?(?:[A-Z][A-Z0-9_]*)?(?:\.(?:' . ExpressionManager::$RDP_regex_var_attr . '))?'; + $RDP_regex_word = '(?:TOKEN:)?(?:[A-Z][A-Z0-9_]*)?(?:\.(?:[A-Z][A-Z0-9_]*))*(?:\.(?:' . ExpressionManager::$RDP_regex_var_attr . '))?'; $RDP_regex_number = '[0-9]+\.?[0-9]*|\.[0-9]+'; $RDP_regex_andor = '\band\b|\bor\b|&&|\|\|'; $RDP_regex_lcb = '{'; @@ -1755,6 +1755,7 @@ public function ProcessBooleanExpression($expr,$groupSeq=-1,$questionSeq=-1) $this->groupSeq = $groupSeq; $this->questionSeq = $questionSeq; + $expr = $this->ExpandThisVar($expr); $status = $this->RDP_Evaluate($expr); if (!$status) { return false; // if there are errors in the expression, hide it? @@ -1864,7 +1865,8 @@ public function sProcessStringContainingExpressionsHelper($src, $questionNum, $s } else { ++$this->substitutionNum; - if ($this->RDP_Evaluate(substr($stringPart[0],1,-1))) + $expr = $this->ExpandThisVar(substr($stringPart[0],1,-1)); + if ($this->RDP_Evaluate($expr)) { $resolvedPart = $this->GetResult(); } @@ -1892,7 +1894,7 @@ public function sProcessStringContainingExpressionsHelper($src, $questionNum, $s 'raw' => $stringPart[0], 'result' => $resolvedPart, 'vars' => implode('|',$jsVarsUsed), - 'js' => $this->GetJavaScriptFunctionForReplacement($questionNum, $idName, substr($stringPart[0],1,-1)), + 'js' => $this->GetJavaScriptFunctionForReplacement($questionNum, $idName, $expr), ); } else @@ -1907,6 +1909,30 @@ public function sProcessStringContainingExpressionsHelper($src, $questionNum, $s return $result; // recurse in case there are nested ones, avoiding infinite loops? } + /** + * If the equation contains refernece to this, expand to comma separated list if needed. + * @param type $eqn + */ + function ExpandThisVar($src) + { + $splitter = '(?:self|that)(?:\.(?:[A-Z0-9_]+))*'; + $parts = preg_split("/(" . $splitter . ")/i",$src,-1,(PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE)); + $result = ''; + foreach ($parts as $part) + { + if (preg_match("/" . $splitter . "/",$part)) + { + $result .= LimeExpressionManager::GetAllVarNamesForQ($this->questionSeq,$part); + } + else + { + $result .= $part; + } + } + + return $result; + } + /** * Get info about all elements needed for dynamic tailoring * @return diff --git a/application/helpers/expressions/em_manager_helper.php b/application/helpers/expressions/em_manager_helper.php index b1a8cecee45..ef2d2a43bb9 100644 --- a/application/helpers/expressions/em_manager_helper.php +++ b/application/helpers/expressions/em_manager_helper.php @@ -3783,6 +3783,120 @@ static function GetLastPrettyPrintExpression() return $LEM->em->GetLastPrettyPrintExpression(); } + /** + * Expand "self.suffix" and "that.qcode.suffix" into canonical list of variable names + * @param type $qseq + * @param type $varname + */ + static function GetAllVarNamesForQ($qseq,$varname) + { + $LEM =& LimeExpressionManager::singleton(); + + $parts = explode('.',$varname); + $qroot = ''; + $suffix = ''; + $sqpatt = ''; + $comments = ''; + + if ($parts[0] == 'self') + { + $type = 'self'; + } + else + { + $type = 'that'; + array_shift($parts); + if (isset($parts[0])) + { + $qroot = $parts[0]; + } + else + { + return $varname; + } + } + + if (count($parts) > 3) + { + return $varname; // invalid + } + if (count($parts) == 3) + { + if (preg_match('/^' . ExpressionManager::$RDP_regex_var_attr . '$/',$parts[2])) + { + $suffix = '.' . $parts[2]; + } + else + { + return $varname; // invalid + } + } + if (count($parts) >= 2) + { + if (count($parts) == 2 && preg_match('/^' . ExpressionManager::$RDP_regex_var_attr . '$/',$parts[1])) + { + $suffix = '.' . $parts[1]; + } + else if (preg_match('/^sq_.+$/',$parts[1])) + { + $sqpatt = substr($parts[1],3); + } + else if ($parts[1] == 'nocomments') + { + $comments = 'N'; + } + else if ($parts[1] == 'comments') + { + $comments = 'Y'; + } + else + { + return $varname; // invalid + } + } + $vars = array(); + + foreach ($LEM->knownVars as $kv) + { + if ($type == 'self') + { + if (!isset($kv['qseq']) || $kv['qseq'] != $qseq || trim($kv['sgqa']) == '') + { + continue; + } + } + else + { + if (!isset($kv['rootVarName']) || $kv['rootVarName'] != $qroot) + { + continue; + } + } + if ($comments != '') + { + if ($comments == 'Y' && !preg_match('/comment$/',$kv['sgqa'])) + { + continue; + } + if ($comments == 'N' && preg_match('/comment$/',$kv['sgqa'])) + { + continue; + } + } + if ($sqpatt != '') + { + $sgq = $LEM->sid . 'X' . $kv['gid'] . 'X' . $kv['qid']; + $ext = substr($kv['sgqa'],strlen($sgq)); + if (!preg_match('/'.$sqpatt.'/',$ext)) + { + continue; + } + } + $vars[] = $kv['sgqa'] . $suffix; + } + return implode(',',$vars); + } + /** * Should be first function called on each page - sets/clears internally needed variables * @param $allOnOnePage - true if StartProcessingGroup will be called multiple times on this page - does some optimizatinos @@ -6173,7 +6287,7 @@ static function GetRelevanceAndTailoringJavaScript() if ($_veq['subqValidSelector'] == '') { continue; } - $isValid = $LEM->em->ProcessBooleanExpression($_veq['subqValidEqn']); + $isValid = $LEM->em->ProcessBooleanExpression($_veq['subqValidEqn'],$arg['gseq'],$LEM->questionId2questionSeq[$arg['qid']]); $_sqValidVars = $LEM->em->GetJSVarsUsed(); $allJsVarsUsed = array_merge($allJsVarsUsed,$_sqValidVars); $valJsVarsUsed = array_merge($valJsVarsUsed,$_sqValidVars); @@ -6212,7 +6326,7 @@ static function GetRelevanceAndTailoringJavaScript() $_hasOtherValidation = true; } - $_isValid = $LEM->em->ProcessBooleanExpression($validationEqn); + $_isValid = $LEM->em->ProcessBooleanExpression($validationEqn,$arg['gseq'],$LEM->questionId2questionSeq[$arg['qid']]); $_vars = $LEM->em->GetJSVarsUsed(); $allJsVarsUsed = array_merge($allJsVarsUsed,$_vars); $valJsVarsUsed = array_merge($valJsVarsUsed,$_vars);