/**
* Creates an array with details on a particular response for display purposes
* Used in Print answers, Detailed response view and Detailed admin notification email
*
* @param mixed $iSurveyID
* @param mixed $iResponseID
* @param mixed $sLanguageCode
* @param boolean $bHonorConditions Apply conditions
*/
function getFullResponseTable($iSurveyID, $iResponseID, $sLanguageCode, $bHonorConditions = true)
{
    $aFieldMap = createFieldMap($iSurveyID, 'full', false, false, $sLanguageCode);
    //Get response data
    $idrow = SurveyDynamic::model($iSurveyID)->findByAttributes(array('id' => $iResponseID));
    // Create array of non-null values - those are the relevant ones
    $aRelevantFields = array();
    foreach ($aFieldMap as $sKey => $fname) {
        if (LimeExpressionManager::QuestionIsRelevant($fname['qid']) || $bHonorConditions == false) {
            $aRelevantFields[$sKey] = $fname;
        }
    }
    $aResultTable = array();
    $oldgid = 0;
    $oldqid = 0;
    foreach ($aRelevantFields as $sKey => $fname) {
        if (!empty($fname['qid'])) {
            $attributes = getQuestionAttributeValues($fname['qid']);
            if (getQuestionAttributeValue($attributes, 'hidden') == 1) {
                continue;
            }
        }
        $question = $fname['question'];
        $subquestion = '';
        if (isset($fname['gid']) && !empty($fname['gid'])) {
            //Check to see if gid is the same as before. if not show group name
            if ($oldgid !== $fname['gid']) {
                $oldgid = $fname['gid'];
                if (LimeExpressionManager::GroupIsRelevant($fname['gid']) || $bHonorConditions == false) {
                    $aResultTable['gid_' . $fname['gid']] = array($fname['group_name'], QuestionGroup::model()->getGroupDescription($fname['gid'], $sLanguageCode));
                }
            }
        }
        if (!empty($fname['qid'])) {
            if ($oldqid !== $fname['qid']) {
                $oldqid = $fname['qid'];
                if (isset($fname['subquestion']) || isset($fname['subquestion1']) || isset($fname['subquestion2'])) {
                    $aResultTable['qid_' . $fname['sid'] . 'X' . $fname['gid'] . 'X' . $fname['qid']] = array($fname['question'], '', '');
                } else {
                    $answer = getExtendedAnswer($iSurveyID, $fname['fieldname'], $idrow[$fname['fieldname']], $sLanguageCode);
                    $aResultTable[$fname['fieldname']] = array($question, '', $answer);
                    continue;
                }
            }
        } else {
            $answer = getExtendedAnswer($iSurveyID, $fname['fieldname'], $idrow[$fname['fieldname']], $sLanguageCode);
            $aResultTable[$fname['fieldname']] = array($question, '', $answer);
            continue;
        }
        if (isset($fname['subquestion'])) {
            $subquestion = "[{$fname['subquestion']}]";
        }
        if (isset($fname['subquestion1'])) {
            $subquestion = "[{$fname['subquestion1']}]";
        }
        if (isset($fname['subquestion2'])) {
            $subquestion .= "[{$fname['subquestion2']}]";
        }
        $answer = getExtendedAnswer($iSurveyID, $fname['fieldname'], $idrow[$fname['fieldname']], $sLanguageCode);
        $aResultTable[$fname['fieldname']] = array($question, $subquestion, $answer);
    }
    return $aResultTable;
}
 /**
  * This event is fired by when a response is submitted
  * available for a survey.
  * @param PluginEvent $event
  */
 public function afterSurveyComplete()
 {
     // This method will send a notification email
     $event = $this->getEvent();
     $surveyId = $event->get('surveyId');
     // Only process the afterSurveyComplete if the plugin is Enabled for this survey and if the survey is Active
     if ($this->get('emailCount', 'Survey', $surveyId) < 1 || Survey::model()->findByPk($surveyId)->active != "Y") {
         // leave gracefully
         return true;
     }
     // Retrieve response and survey properties
     $responseId = $event->get('responseId');
     $response = $this->pluginManager->getAPI()->getResponse($surveyId, $responseId);
     $sitename = $this->pluginManager->getAPI()->getConfigKey('sitename');
     $surveyInfo = getSurveyInfo($surveyId);
     $adminemail = $surveyInfo['adminemail'];
     $bounce_email = $surveyInfo['bounce_email'];
     $isHtmlEmail = $surveyInfo['htmlemail'] == 'Y';
     $baseLang = $surveyInfo['language'];
     for ($i = 1; $i <= $this->get('emailCount', 'Survey', $surveyId); $i++) {
         // Let's check if there is at least a valid destination email address
         $aTo = array();
         $aAttachTo = array();
         $aDestEmail = explode(';', $this->pluginManager->getAPI()->EMevaluateExpression($this->get('emailDestinations_' . $i, 'Survey', $surveyId)));
         $aDestEmail = array_map('trim', $aDestEmail);
         $aUploadQuestions = explode(';', $this->pluginManager->getAPI()->EMevaluateExpression($this->get('emailAttachFiles_' . $i, 'Survey', $surveyId)));
         $aUploadQuestions = array_map('trim', $aUploadQuestions);
         // prepare an array of valid destination email addresses
         foreach ($aDestEmail as $destemail) {
             if (validateEmailAddress($destemail)) {
                 $aTo[] = $destemail;
             }
         }
         // prepare an array of valid attached files from upload-questions
         foreach ($aUploadQuestions as $uploadQuestion) {
             $sgqa = 0;
             $qtype = '';
             if (isset($response[$uploadQuestion])) {
                 // get SGQA code from question-code. Ther might be a better way to do this though...
                 $sgqa = $this->pluginManager->getAPI()->EMevaluateExpression('{' . $uploadQuestion . '.sgqa}');
                 $qtype = $this->pluginManager->getAPI()->EMevaluateExpression('{' . $uploadQuestion . '.type}');
             }
             // Only add the file if question is relevant
             if ($sgqa != 0 && $qtype == "|" && \LimeExpressionManager::QuestionIsRelevant($sgqa)) {
                 $aFiles = json_decode($response[$uploadQuestion]);
                 if (!is_null($aFiles) && is_array($aFiles)) {
                     foreach ($aFiles as $file) {
                         if (property_exists($file, 'name') && property_exists($file, 'filename')) {
                             $name = $file->name;
                             $filename = $file->filename;
                             $aAttachTo[] = array(0 => $this->pluginManager->getAPI()->getConfigKey('uploaddir') . "/surveys/{$surveyId}/files/" . $filename, 1 => $name);
                         }
                     }
                 }
             }
         }
         if (count($aTo) >= 1) {
             // Retrieve the language to use for the notification email
             $emailLang = $this->get('emailLang_' . $i, 'Survey', $surveyId);
             if ($emailLang == '--') {
                 // in this case let's select the language used when submitting the response
                 $emailLang = $response['startlanguage'];
             }
             $subjectTemplate = $this->get("emailSubject_{$i}_{$emailLang}", 'Survey', $surveyId);
             if ($subjectTemplate == "") {
                 // If subject is not translated, use subject and body from the baseLang
                 $emailLang = $baseLang;
                 $subjectTemplate = $this->get("emailSubject_{$i}_{$emailLang}", 'Survey', $surveyId);
             }
             // Process the email subject and body through ExpressionManager
             $subject = $this->pluginManager->getAPI()->EMevaluateExpression($subjectTemplate);
             // Prepare an {ANSWERTABLE} variable
             if ($surveyInfo['datestamp'] == 'N') {
                 //$aFilteredFields=array('id', 'submitdate', 'lastpage', 'startlanguage');
                 // Let's filter submitdate if survey is not datestampped
                 $aFilteredFields = array('submitdate');
             } else {
                 //$aFilteredFields=array('id', 'lastpage', 'startlanguage');
                 $aFilteredFields = array();
             }
             $replacementfields = array('ANSWERTABLE' => $this->translateAnswerTable($surveyId, $responseId, $emailLang, $isHtmlEmail, $aFilteredFields));
             // Process emailBody through EM and replace {ANSWERTABLE}
             $body = \LimeExpressionManager::ProcessString($this->get("emailBody_{$i}_{$emailLang}", 'Survey', $surveyId), NULL, $replacementfields);
             // At last it's time to send the email
             SendEmailMessage($body, $subject, $aTo, $adminemail, $sitename, $isHtmlEmail, $bounce_email, $aAttachTo);
         }
         // END BLOCK 'if' aTo[] not emtpy
     }
     // END BLOCK 'for' emailCount
 }
/**
* checkUploadedFileValidity used in SurveyRuntimeHelper
*/
function checkUploadedFileValidity($surveyid, $move, $backok = null)
{
    global $thisstep;
    $clang = Yii::app()->lang;
    if (!isset($backok) || $backok != "Y") {
        $fieldmap = createFieldMap($surveyid, 'full', false, false, $_SESSION['survey_' . $surveyid]['s_lang']);
        if (isset($_POST['fieldnames']) && $_POST['fieldnames'] != "") {
            $fields = explode("|", $_POST['fieldnames']);
            foreach ($fields as $field) {
                if ($fieldmap[$field]['type'] == "|" && !strrpos($fieldmap[$field]['fieldname'], "_filecount")) {
                    $validation = getQuestionAttributeValues($fieldmap[$field]['qid']);
                    $filecount = 0;
                    $json = $_POST[$field];
                    // if name is blank, its basic, hence check
                    // else, its ajax, don't check, bypass it.
                    if ($json != "" && $json != "[]") {
                        $phparray = json_decode(stripslashes($json));
                        if ($phparray[0]->size != "") {
                            // ajax
                            $filecount = count($phparray);
                        } else {
                            // basic
                            for ($i = 1; $i <= $validation['max_num_of_files']; $i++) {
                                if (!isset($_FILES[$field . "_file_" . $i]) || $_FILES[$field . "_file_" . $i]['name'] == '') {
                                    continue;
                                }
                                $filecount++;
                                $file = $_FILES[$field . "_file_" . $i];
                                // File size validation
                                if ($file['size'] > $validation['max_filesize'] * 1000) {
                                    $filenotvalidated = array();
                                    $filenotvalidated[$field . "_file_" . $i] = sprintf($clang->gT("Sorry, the uploaded file (%s) is larger than the allowed filesize of %s KB."), $file['size'], $validation['max_filesize']);
                                    $append = true;
                                }
                                // File extension validation
                                $pathinfo = pathinfo(basename($file['name']));
                                $ext = $pathinfo['extension'];
                                $validExtensions = explode(",", $validation['allowed_filetypes']);
                                if (!in_array($ext, $validExtensions)) {
                                    if (isset($append) && $append) {
                                        $filenotvalidated[$field . "_file_" . $i] .= sprintf($clang->gT("Sorry, only %s extensions are allowed!"), $validation['allowed_filetypes']);
                                        unset($append);
                                    } else {
                                        $filenotvalidated = array();
                                        $filenotvalidated[$field . "_file_" . $i] .= sprintf($clang->gT("Sorry, only %s extensions are allowed!"), $validation['allowed_filetypes']);
                                    }
                                }
                            }
                        }
                    } else {
                        $filecount = 0;
                    }
                    if (isset($validation['min_num_of_files']) && $filecount < $validation['min_num_of_files'] && LimeExpressionManager::QuestionIsRelevant($fieldmap[$field]['qid'])) {
                        $filenotvalidated = array();
                        $filenotvalidated[$field] = $clang->gT("The minimum number of files has not been uploaded.");
                    }
                }
            }
        }
        if (isset($filenotvalidated)) {
            if (isset($move) && $move == "moveprev") {
                $_SESSION['survey_' . $surveyid]['step'] = $thisstep;
            }
            if (isset($move) && $move == "movenext") {
                $_SESSION['survey_' . $surveyid]['step'] = $thisstep;
            }
            return $filenotvalidated;
        }
    }
    if (!isset($filenotvalidated)) {
        return false;
    } else {
        return $filenotvalidated;
    }
}
    /**
     * Unit test Relevance using a simplified syntax to represent questions.
     */
    static function UnitTestRelevance()
    {
        // Tests:  varName~relevance~inputType~message
        $tests = <<<EOT
name~1~text~What is your name?
age~1~text~How old are you (must be 16-80)?
badage~1~expr~{badage=((age<16) || (age>80))}
agestop~!is_empty(age) && ((age<16) || (age>80))~message~Sorry, {name}, you are too {if((age<16),'young',if((age>80),'old','middle-aged'))} for this test.
kids~!((age<16) || (age>80))~yesno~Do you have children (Y/N)?
kidsO~!is_empty(kids) && !(kids=='Y' or kids=='N')~message~Please answer the question about whether you have children with 'Y' or 'N'.
wantsKids~kids=='N'~yesno~Do you hope to have kids some day (Y/N)?
wantsKidsY~wantsKids=='Y'~message~{name}, I hope you are able to have children some day!
wantsKidsN~wantsKids=='N'~message~{name}, I hope you have a wonderfully fulfilling life!
wantsKidsO~!is_empty(wantsKids) && !(wantsKids=='Y' or wantsKids=='N')~message~Please answer the question about whether you want children with 'Y' or 'N'.
parents~1~expr~{parents = (!badage && kids=='Y')}
numKids~kids=='Y'~text~How many children do you have?
numKidsValidation~parents and strlen(numKids) > 0 and numKids <= 0~message~{name}, please check your entries.  You said you do have children, {numKids} of them, which makes no sense.
kid1~numKids >= 1~text~How old is your first child?
kid2~numKids >= 2~text~How old is your second child?
kid3~numKids >= 3~text~How old is your third child?
kid4~numKids >= 4~text~How old is your fourth child?
kid5~numKids >= 5~text~How old is your fifth child?
sumage~1~expr~{sumage=sum(kid1.NAOK,kid2.NAOK,kid3.NAOK,kid4.NAOK,kid5.NAOK)}
report~numKids > 0~message~{name}, you said you are {age} and that you have {numKids} kids.  The sum of ages of your first {min(numKids,5)} kids is {sumage}.
EOT;
        $vars = array();
        $varsNAOK = array();
        $varSeq = array();
        $testArgs = array();
        $argInfo = array();
        LimeExpressionManager::SetDirtyFlag();
        $LEM =& LimeExpressionManager::singleton();
        LimeExpressionManager::StartProcessingPage(true);
        LimeExpressionManager::StartProcessingGroup(1);
        // pretending this is group 1
        // collect variables
        $i = 0;
        foreach (explode("\n", $tests) as $test) {
            $args = explode("~", $test);
            $type = $args[1] == 'expr' ? '*' : $args[1] == 'message' ? 'X' : 'S';
            $vars[$args[0]] = array('sgqa' => $args[0], 'code' => '', 'jsName' => 'java' . $args[0], 'jsName_on' => 'java' . $args[0], 'readWrite' => 'Y', 'type' => $type, 'relevanceStatus' => '1', 'gid' => 1, 'gseq' => 1, 'qseq' => $i, 'qid' => $i);
            $varSeq[] = $args[0];
            $testArgs[] = $args;
            $LEM->questionId2questionSeq[$i] = $i;
            $LEM->questionId2groupSeq[$i] = 1;
            $LEM->questionSeq2relevance[$i] = array('relevance' => htmlspecialchars(preg_replace('/[[:space:]]/', ' ', $args[1]), ENT_QUOTES), 'qid' => $i, 'qseq' => $i, 'gseq' => 1, 'jsResultVar' => 'java' . $args[0], 'type' => $type, 'hidden' => false, 'gid' => 1);
            ++$i;
        }
        $LEM->knownVars = $vars;
        $LEM->gRelInfo[1] = array('gid' => 1, 'gseq' => 1, 'eqn' => '', 'result' => 1, 'numJsVars' => 0, 'relevancejs' => '', 'relevanceVars' => '', 'prettyPrint' => '');
        $LEM->ProcessAllNeededRelevance();
        // collect relevance
        $alias2varName = array();
        $varNameAttr = array();
        for ($i = 0; $i < count($testArgs); ++$i) {
            $testArg = $testArgs[$i];
            $var = $testArg[0];
            $rel = LimeExpressionManager::QuestionIsRelevant($i);
            $question = LimeExpressionManager::ProcessString($testArg[3], $i, NULL, true, 1, 1);
            $jsVarName = 'java' . str_replace('#', '_', $testArg[0]);
            $argInfo[] = array('num' => $i, 'name' => $jsVarName, 'sgqa' => $testArg[0], 'type' => $testArg[2], 'question' => $question, 'relevance' => $testArg[1], 'relevanceStatus' => $rel);
            $alias2varName[$var] = array('jsName' => $jsVarName, 'jsPart' => "'" . $var . "':'" . $jsVarName . "'");
            $alias2varName[$jsVarName] = array('jsName' => $jsVarName, 'jsPart' => "'" . $jsVarName . "':'" . $jsVarName . "'");
            $varNameAttr[$jsVarName] = "'" . $jsVarName . "':{" . "'jsName':'" . $jsVarName . "','jsName_on':'" . $jsVarName . "','sgqa':'" . substr($jsVarName, 4) . "','qid':" . $i . ",'gid':" . 1 . "}";
        }
        $LEM->alias2varName = $alias2varName;
        $LEM->varNameAttr = $varNameAttr;
        LimeExpressionManager::FinishProcessingGroup();
        LimeExpressionManager::FinishProcessingPage();
        print <<<EOD
    <script type='text/javascript'>
    <!--
    var LEMradix='.';
    function checkconditions(value, name, type, evt_type)
    {
        if (typeof evt_type === 'undefined')
        {
            evt_type = 'onchange';
        }
        ExprMgr_process_relevance_and_tailoring(evt_type,name,type);
    }
    // -->
    </script>
EOD;
        print LimeExpressionManager::GetRelevanceAndTailoringJavaScript();
        // Print Table of questions
        print "<h3>This is a test of dynamic relevance.</h3>";
        print "Enter your name and age, and try all the permutations of answers to whether you have or want children.<br />\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.<br />";
        print "<table class='table' border='1'><tr><td>";
        foreach ($argInfo as $arg) {
            $rel = LimeExpressionManager::QuestionIsRelevant($arg['num']);
            print "<div id='question" . $arg['num'] . ($rel ? "'" : "' style='display: none'") . ">\n";
            print "<input type='hidden' id='display" . $arg['num'] . "' name='" . $arg['num'] . "' value='" . ($rel ? 'on' : '') . "'/>\n";
            if ($arg['type'] == 'expr') {
                // Hack for testing purposes - rather than using LimeSurvey internals to store the results of equations, process them via a hidden <div>
                print "<div style='display: none' id='hack_" . $arg['name'] . "'>" . $arg['question'];
                print "<input type='hidden' id='" . $arg['name'] . "' name='" . $arg['name'] . "' value=''/></div>\n";
            } else {
                print "<table class='table' border='1' width='100%'>\n<tr>\n<td>[Q" . $arg['num'] . "] " . $arg['question'] . "</td>\n";
                switch ($arg['type']) {
                    case 'yesno':
                    case 'text':
                        print "<td><input type='text' id='" . $arg['name'] . "' name='" . $arg['sgqa'] . "' value='' onchange='checkconditions(this.value, this.name, this.type)'/></td>\n";
                        break;
                    case 'message':
                        print "<input type='hidden' id='" . $arg['name'] . "' name='" . $arg['sgqa'] . "' value=''/>\n";
                        break;
                }
                print "</tr>\n</table>\n";
            }
            print "</div>\n";
        }
        print "</table>";
        LimeExpressionManager::SetDirtyFlag();
        // so subsequent tests don't try to access these variables
    }