/** * Create HTML view of the survey, showing everything that uses EM * @param <type> $sid * @param <type> $gid * @param <type> $qid */ public static function ShowSurveyLogicFile($sid, $gid = NULL, $qid = NULL, $LEMdebugLevel = 0, $assessments = false) { // Title // Welcome // G1, name, relevance, text // *Q1, name [type], relevance [validation], text, help, default, help_msg // SQ1, name [scale], relevance [validation], text // A1, code, assessment_value, text // End Message $LEM =& LimeExpressionManager::singleton(); $LEM->sPreviewMode = 'logic'; $aSurveyInfo = getSurveyInfo($sid, $_SESSION['LEMlang']); $allErrors = array(); $warnings = 0; $surveyOptions = array('assessments' => $aSurveyInfo['assessments'] == 'Y', 'hyperlinkSyntaxHighlighting' => true); $varNamesUsed = array(); // keeps track of whether variables have been declared if (!is_null($qid)) { $surveyMode = 'question'; LimeExpressionManager::StartSurvey($sid, 'question', $surveyOptions, false, $LEMdebugLevel); $qseq = LimeExpressionManager::GetQuestionSeq($qid); $moveResult = LimeExpressionManager::JumpTo($qseq + 1, true, false, true); } else { if (!is_null($gid)) { $surveyMode = 'group'; LimeExpressionManager::StartSurvey($sid, 'group', $surveyOptions, false, $LEMdebugLevel); $gseq = LimeExpressionManager::GetGroupSeq($gid); $moveResult = LimeExpressionManager::JumpTo($gseq + 1, true, false, true); } else { $surveyMode = 'survey'; LimeExpressionManager::StartSurvey($sid, 'survey', $surveyOptions, false, $LEMdebugLevel); $moveResult = LimeExpressionManager::NavigateForwards(); } } $qtypes = getQuestionTypeList('', 'array'); if (is_null($moveResult) || is_null($LEM->currentQset) || count($LEM->currentQset) == 0) { return array('errors' => 1, 'html' => sprintf($LEM->gT('Invalid question - probably missing sub-questions or language-specific settings for language %s'), $_SESSION['LEMlang'])); } $surveyname = viewHelper::stripTagsEM(templatereplace('{SURVEYNAME}', array('SURVEYNAME' => $aSurveyInfo['surveyls_title']))); $out = '<div id="showlogicfilediv" class="table-responsive"><h3>' . $LEM->gT('Logic File for Survey # ') . '[' . $LEM->sid . "]: {$surveyname}</h3>\n"; $out .= "<table id='logicfiletable' class='table table-bordered'>"; if (is_null($gid) && is_null($qid)) { if ($aSurveyInfo['surveyls_description'] != '') { $LEM->ProcessString($aSurveyInfo['surveyls_description'], 0); $sPrint = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); $errClass = $LEM->em->HasErrors() ? 'danger' : ''; $out .= "<tr class='LEMgroup {$errClass}'><td colspan=2>" . $LEM->gT("Description:") . "</td><td colspan=2>" . $sPrint . "</td></tr>"; } if ($aSurveyInfo['surveyls_welcometext'] != '') { $LEM->ProcessString($aSurveyInfo['surveyls_welcometext'], 0); $sPrint = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); $errClass = $LEM->em->HasErrors() ? 'danger' : ''; $out .= "<tr class='LEMgroup {$errClass}'><td colspan=2>" . $LEM->gT("Welcome:") . "</td><td colspan=2>" . $sPrint . "</td></tr>"; } if ($aSurveyInfo['surveyls_endtext'] != '') { $LEM->ProcessString($aSurveyInfo['surveyls_endtext']); $sPrint = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); $errClass = $LEM->em->HasErrors() ? 'danger' : ''; $out .= "<tr class='LEMgroup {$errClass}'><td colspan=2>" . $LEM->gT("End message:") . "</td><td colspan=2>" . $sPrint . "</td></tr>"; } if ($aSurveyInfo['surveyls_url'] != '') { $LEM->ProcessString($aSurveyInfo['surveyls_urldescription'] . " - " . $aSurveyInfo['surveyls_url']); $sPrint = viewHelper::purified($LEM->GetLastPrettyPrintExpression()); $errClass = $LEM->em->HasErrors() ? 'danger' : ''; $out .= "<tr class='LEMgroup {$errClass}'><td colspan=2>" . $LEM->gT("End URL:") . "</td><td colspan=2>" . $sPrint . "</td></tr>"; } } $out .= "<tr><th>#</th><th>" . $LEM->gT('Name [ID]') . "</th><th>" . $LEM->gT('Relevance [Validation] (Default value)') . "</th><th>" . $LEM->gT('Text [Help] (Tip)') . "</th></tr>\n"; $_gseq = -1; foreach ($LEM->currentQset as $q) { $gseq = $q['info']['gseq']; $gid = $q['info']['gid']; $qid = $q['info']['qid']; $qseq = $q['info']['qseq']; $errorCount = 0; ////// // SHOW GROUP-LEVEL INFO ////// if ($gseq != $_gseq) { $bGroupHaveError = false; $errClass = ''; $LEM->ParseResultCache = array(); // reset for each group so get proper color coding? $_gseq = $gseq; $ginfo = $LEM->gseq2info[$gseq]; $sGroupRelevance = '{' . ($ginfo['grelevance'] == '' ? 1 : $ginfo['grelevance']) . '}'; $LEM->ProcessString($sGroupRelevance, $qid, NULL, false, 1, 1, false, false); $bGroupHaveError = $bGroupHaveError || $LEM->em->HasErrors(); $sGroupRelevance = viewHelper::stripTagsEM($LEM->GetLastPrettyPrintExpression()); $sGroupText = trim($ginfo['description']) == '' ? ' ' : $ginfo['description']; $LEM->ProcessString($sGroupText, $qid, NULL, false, 1, 1, false, false); $bGroupHaveError = $bGroupHaveError || $LEM->em->HasErrors(); $sGroupText = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); $editlink = Yii::app()->getController()->createUrl('admin/survey/sa/view/surveyid/' . $LEM->sid . '/gid/' . $gid); if ($bGroupHaveError) { $errClass = 'text-danger'; } $groupRow = "<tr class='LEMgroup'>" . "<td class='{$errClass}'>G-{$gseq}</td>" . "<td><b>" . $ginfo['group_name'] . "</b><br />[<a target='_blank' href='{$editlink}'>GID " . $gid . "</a>]</td>" . "<td>" . $sGroupRelevance . "</td>" . "<td>" . $sGroupText . "</td>" . "</tr>\n"; $out .= $groupRow; } ////// // SHOW QUESTION-LEVEL INFO ////// $mandatory = $q['info']['mandatory'] == 'Y' ? "<span class='mandatory'>*</span>" : ''; $type = $q['info']['type']; $typedesc = $qtypes[$type]['description']; $sgqas = explode('|', $q['sgqa']); if (count($sgqas) == 1 && !is_null($q['info']['default'])) { $LEM->ProcessString($q['info']['default'], $qid, NULL, false, 1, 1, false, false); // Default value is Y or answer code or go to input/textarea, then we can filter it $_default = viewHelper::stripTagsEM($LEM->GetLastPrettyPrintExpression()); if ($LEM->em->HasErrors()) { ++$errorCount; } $default = '<br />(' . $LEM->gT('Default:') . ' ' . $_default . ')'; } else { $default = ''; } $sQuestionText = $q['info']['qtext'] != '' ? $q['info']['qtext'] : ' '; $LEM->ProcessString($sQuestionText, $qid, NULL, false, 1, 1, false, false); $sQuestionText = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); if ($LEM->em->HasErrors()) { ++$errorCount; } $sQuestionHelp = ""; if (trim($q['info']['help']) != "") { $sQuestionHelp = $q['info']['help']; $LEM->ProcessString($sQuestionHelp, $qid, NULL, false, 1, 1, false, false); $sQuestionHelp = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); if ($LEM->em->HasErrors()) { ++$errorCount; } $sQuestionHelp = '<hr/>[' . $LEM->gT("Help:") . ' ' . $sQuestionHelp . ']'; } $prettyValidTip = $q['prettyValidTip'] == '' ? '' : '<hr/>(' . $LEM->gT("Tip:") . ' ' . viewHelper::stripTagsEM($q['prettyValidTip']) . ')'; // Unsure need to filter ////// // SHOW QUESTION ATTRIBUTES THAT ARE PROCESSED BY EM ////// $attrTable = ''; $attrs = isset($LEM->qattr[$qid]) ? $LEM->qattr[$qid] : array(); if (isset($LEM->q2subqInfo[$qid]['preg'])) { $attrs['regex_validation'] = $LEM->q2subqInfo[$qid]['preg']; } if (isset($LEM->questionSeq2relevance[$qseq]['other'])) { $attrs['other'] = $LEM->questionSeq2relevance[$qseq]['other']; } if (count($attrs) > 0) { $attrTable = "<table id='logicfileattributetable'><tr><th>" . $LEM->gT("Question attribute") . "</th><th>" . $LEM->gT("Value") . "</th></tr>\n"; $count = 0; foreach ($attrs as $key => $value) { if (is_null($value) || trim($value) == '') { continue; } switch ($key) { // @todo: Rather compares the current attribute value to the defaults in the question attributes array to decide which ones should show (only the ones that are non-standard) default: case 'exclude_all_others': case 'exclude_all_others_auto': case 'hidden': if ($value == false || $value == '0') { $value = NULL; // so can skip this one - just using continue here doesn't work. } break; case 'time_limit_action': if ($value == '1') { $value = NULL; // so can skip this one - just using continue here doesn't work. } case 'relevance': $value = NULL; // means an outdate database structure break; case 'array_filter': case 'array_filter_exclude': case 'code_filter': case 'date_max': case 'date_min': case 'em_validation_q_tip': case 'em_validation_sq_tip': break; case 'equals_num_value': case 'em_validation_q': case 'em_validation_sq': case 'max_answers': case 'max_num_value': case 'max_num_value_n': case 'min_answers': case 'min_num_value': case 'min_num_value_n': case 'min_num_of_files': case 'max_num_of_files': case 'multiflexible_max': case 'multiflexible_min': case 'slider_accuracy': case 'slider_min': case 'slider_max': case 'slider_default': $value = '{' . $value . '}'; $LEM->ProcessString($value, $qid, NULL, false, 1, 1, false, false); $value = viewHelper::stripTagsEM($LEM->GetLastPrettyPrintExpression()); if ($LEM->em->HasErrors()) { ++$errorCount; } break; case 'other_replace_text': case 'show_totals': case 'regex_validation': break; case 'other': if ($value == 'N') { $value = NULL; // so can skip this one } break; } if (is_null($value)) { continue; // since continuing from within a switch statement doesn't work } ++$count; $attrTable .= "<tr><td>{$key}</td><td>{$value}</td></tr>\n"; } $attrTable .= "</table>\n"; if ($count == 0) { $attrTable = ''; } } $qdetails = $sQuestionText . $sQuestionHelp . $prettyValidTip . $attrTable; ////// // SHOW RELEVANCE ////// // Must parse Relevance this way, otherwise if try to first split expressions, regex equations won't work $relevanceEqn = $q['info']['relevance'] == '' ? 1 : $q['info']['relevance']; if (!isset($LEM->ParseResultCache[$relevanceEqn])) { $result = $LEM->em->ProcessBooleanExpression($relevanceEqn, $gseq, $qseq); $prettyPrint = viewHelper::stripTagsEM($LEM->em->GetPrettyPrintString()); $hasErrors = $LEM->em->HasErrors(); $LEM->ParseResultCache[$relevanceEqn] = array('result' => $result, 'prettyprint' => $prettyPrint, 'hasErrors' => $hasErrors); } $relevance = $LEM->ParseResultCache[$relevanceEqn]['prettyprint']; if ($LEM->ParseResultCache[$relevanceEqn]['hasErrors']) { ++$errorCount; } ////// // SHOW VALIDATION EQUATION ////// // Must parse Validation this way so that regex (preg) works $prettyValidEqn = ''; if ($q['prettyValidEqn'] != '') { $validationEqn = $q['validEqn']; if (!isset($LEM->ParseResultCache[$validationEqn])) { $result = $LEM->em->ProcessBooleanExpression($validationEqn, $gseq, $qseq); $prettyPrint = viewHelper::stripTagsEM($LEM->em->GetPrettyPrintString()); $hasErrors = $LEM->em->HasErrors(); $LEM->ParseResultCache[$validationEqn] = array('result' => $result, 'prettyprint' => $prettyPrint, 'hasErrors' => $hasErrors); } $prettyValidEqn = '<hr/>(VALIDATION: ' . $LEM->ParseResultCache[$validationEqn]['prettyprint'] . ')'; if ($LEM->ParseResultCache[$validationEqn]['hasErrors']) { ++$errorCount; } } ////// // TEST VALIDITY OF ROOT VARIABLE NAME AND WHETHER HAS BEEN USED ////// $rootVarName = $q['info']['rootVarName']; $varNameErrorMsg = ''; $varNameError = NULL; if (isset($varNamesUsed[$rootVarName])) { $varNameErrorMsg .= $LEM->gT('This variable name has already been used.'); } else { $varNamesUsed[$rootVarName] = array('gseq' => $gseq, 'qid' => $qid); } if (!preg_match('/^[a-zA-Z][0-9a-zA-Z]*$/', $rootVarName)) { $varNameErrorMsg .= $LEM->gT('Starting in 2.05, variable names should only contain letters and numbers; and may not start with a number. This variable name is deprecated.'); } if ($varNameErrorMsg != '') { $varNameError = array('message' => $varNameErrorMsg, 'gseq' => $varNamesUsed[$rootVarName]['gseq'], 'qid' => $varNamesUsed[$rootVarName]['qid'], 'gid' => $gid); if (!$LEM->sgqaNaming) { ++$errorCount; } else { ++$warnings; } } ////// // SHOW ALL SUB-QUESTIONS ////// $sqRows = ''; $i = 0; $sawThis = array(); // array of rowdivids already seen so only show them once foreach ($sgqas as $sgqa) { $bSubQhasError = false; if ($LEM->knownVars[$sgqa]['qcode'] == $rootVarName) { continue; // so don't show the main question as a sub-question too } $rowdivid = $sgqa; $varName = $LEM->knownVars[$sgqa]['qcode']; switch ($q['info']['type']) { case '1': if (preg_match('/#1$/', $sgqa)) { $rowdivid = NULL; // so that doesn't show same message for second scale } else { $rowdivid = substr($sgqa, 0, -2); // strip suffix $varName = substr($LEM->knownVars[$sgqa]['qcode'], 0, -2); } break; case 'P': if (preg_match('/comment$/', $sgqa)) { $rowdivid = NULL; } break; case ':': case ';': $_rowdivid = $LEM->knownVars[$sgqa]['rowdivid']; if (isset($sawThis[$qid . '~' . $_rowdivid])) { $rowdivid = NULL; // so don't show again } else { $sawThis[$qid . '~' . $_rowdivid] = true; $rowdivid = $_rowdivid; $sgqa_len = strlen($sid . 'X' . $gid . 'X' . $qid); $varName = $rootVarName . '_' . substr($_rowdivid, $sgqa_len); } } if (is_null($rowdivid)) { continue; } ++$i; $subQeqn = ' '; if (isset($LEM->subQrelInfo[$qid][$rowdivid])) { $sq = $LEM->subQrelInfo[$qid][$rowdivid]; $subQeqn = viewHelper::stripTagsEM($sq['prettyPrintEqn']); // {' . $sq['eqn'] . '}'; // $sq['prettyPrintEqn']; if ($sq['hasErrors']) { ++$errorCount; } } $sgqaInfo = $LEM->knownVars[$sgqa]; $subqText = $sgqaInfo['subqtext']; $LEM->ProcessString($subqText, $qid, NULL, false, 1, 1, false, false); $subqText = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); if ($LEM->em->HasErrors()) { ++$errorCount; } if (isset($sgqaInfo['default']) && $sgqaInfo['default'] !== '') { $LEM->ProcessString($sgqaInfo['default'], $qid, NULL, false, 1, 1, false, false); $_default = viewHelper::stripTagsEM($LEM->GetLastPrettyPrintExpression()); if ($LEM->em->HasErrors()) { ++$errorCount; } $subQeqn .= '<br />(' . $LEM->gT('Default:') . ' ' . $_default . ')'; } $sqRows .= "<tr class='LEMsubq'>" . "<td>SQ-{$i}</td>" . "<td><b>" . $varName . "</b></td>" . "<td>{$subQeqn}</td>" . "<td>" . $subqText . "</td>" . "</tr>"; } ////// // SHOW ANSWER OPTIONS FOR ENUMERATED LISTS, AND FOR MULTIFLEXI ////// $answerRows = ''; if (isset($LEM->qans[$qid]) || isset($LEM->multiflexiAnswers[$qid])) { $_scale = -1; if (isset($LEM->multiflexiAnswers[$qid])) { $ansList = $LEM->multiflexiAnswers[$qid]; } else { $ansList = $LEM->qans[$qid]; } foreach ($ansList as $ans => $value) { $ansInfo = explode('~', $ans); $valParts = explode('|', $value); $valInfo[0] = array_shift($valParts); $valInfo[1] = implode('|', $valParts); if ($_scale != $ansInfo[0]) { $i = 1; $_scale = $ansInfo[0]; } $subQeqn = ''; $rowdivid = $sgqas[0] . $ansInfo[1]; if ($q['info']['type'] == 'R') { $rowdivid = $LEM->sid . 'X' . $gid . 'X' . $qid . $ansInfo[1]; } if (isset($LEM->subQrelInfo[$qid][$rowdivid])) { $sq = $LEM->subQrelInfo[$qid][$rowdivid]; $subQeqn = ' ' . viewHelper::stripTagsEM($sq['prettyPrintEqn']); if ($sq['hasErrors']) { ++$errorCount; } } $sAnswerText = $valInfo[1]; $LEM->ProcessString($sAnswerText, $qid, NULL, false, 1, 1, false, false); $sAnswerText = viewHelper::purified(viewHelper::filterScript($LEM->GetLastPrettyPrintExpression())); if ($LEM->em->HasErrors()) { ++$errorCount; } $answerRows .= "<tr class='LEManswer'>" . "<td>A[" . $ansInfo[0] . "]-" . $i++ . "</td>" . "<td><b>" . $ansInfo[1] . "</b></td>" . "<td>[VALUE: " . $valInfo[0] . "]" . $subQeqn . "</td>" . "<td>" . $sAnswerText . "</td>" . "</tr>\n"; } } ////// // FINALLY, SHOW THE QUESTION ROW(S), COLOR-CODING QUESTIONS THAT CONTAIN ERRORS ////// $errclass = $errorCount > 0 ? 'danger' : ''; $errText = $errorCount > 0 ? "<br><em class='label label-danger'>" . $LEM->ngT("This question has at least {n} error.|This question has at least {n} errors.", $errorCount) . "<em>" : ""; $questionRow = "<tr class='LEMquestion'>" . "<td class='{$errclass}'>Q-" . $q['info']['qseq'] . "</td>" . "<td><b>" . $mandatory; if ($varNameErrorMsg == '') { $editlink = Yii::app()->getController()->createUrl('admin/survey/sa/view/surveyid/' . $sid . '/gid/' . $gid . '/qid/' . $qid); $questionRow .= $rootVarName; } else { $editlink = Yii::app()->getController()->createUrl('admin/survey/sa/view/surveyid/' . $LEM->sid . '/gid/' . $varNameError['gid'] . '/qid/' . $varNameError['qid']); $questionRow .= "<span class='highlighterror' title='" . $varNameError['message'] . "' " . "onclick='window.open(\"{$editlink}\",\"_blank\")'>" . $rootVarName . "</span>"; } $questionRow .= "</b><br />[<a target='_blank' href='{$editlink}'>QID {$qid}</a>]<br/>{$typedesc} [{$type}] {$errText}</td>" . "<td>" . $relevance . $prettyValidEqn . $default . "</td>" . "<td>" . $qdetails . "</td>" . "</tr>\n"; $out .= $questionRow; $out .= $sqRows; $out .= $answerRows; if ($errorCount > 0) { $allErrors[$gid . '~' . $qid] = $errorCount; } } $out .= "</table>"; LimeExpressionManager::FinishProcessingPage(); if (($LEMdebugLevel & LEM_DEBUG_TIMING) == LEM_DEBUG_TIMING) { $out .= LimeExpressionManager::GetDebugTimingMessage(); } if (count($allErrors) > 0) { $out = "<p class='alert alert-danger'>" . $LEM->ngT("{n} question contains errors that need to be corrected.|{n} questions contain errors that need to be corrected.", count($allErrors)) . "</p>\n" . $out; } else { switch ($surveyMode) { case 'survey': $message = $LEM->gT('No syntax errors detected in this survey.'); break; case 'group': $message = $LEM->gT('This group, by itself, does not contain any syntax errors.'); break; case 'question': $message = $LEM->gT('This question, by itself, does not contain any syntax errors.'); break; } $out = "<p class='LEMheading'>{$message}</p>\n" . $out . "</div>"; } return array('errors' => $allErrors, 'html' => $out); }