Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' of https://maziminke@github.com/LimeSurvey/Lime…

…Survey.git
  • Loading branch information...
commit 1dd4ef501493a2de3765ca000d6471b35e0c8569 2 parents 8636f32 + 9e518c9
@maziminke maziminke authored
Showing with 73,285 additions and 72,137 deletions.
  1. +84 −73 admin/activate_functions.php
  2. +2,783 −2,777 admin/dataentry.php
  3. +12 −0 admin/html.php
  4. +24 −14 admin/importlabel.php
  5. +1 −1  admin/iterate_survey.php
  6. +1 −1  admin/login_check.php
  7. +8 −33 admin/questiongrouphandling.php
  8. +4 −0 admin/questionhandling.php
  9. +2,681 −2,664 admin/tokens.php
  10. +1 −1  admin/usercontrol.php
  11. +1 −1  classes/adodb/datadict/datadict-mssql.inc.php
  12. +2 −2 classes/adodb/datadict/datadict-mssqlnative.inc.php
  13. +701 −107 classes/expressions/LimeExpressionManager.php
  14. +3 −0  classes/expressions/test/navigation_test.php
  15. +1 −1  classes/expressions/test/relevance.php
  16. +1 −1  classes/expressions/test/unit.php
  17. +36 −4 common_functions.php
  18. +9 −0 config-defaults.php
  19. +52 −0 docs/release_notes.txt
  20. +11 −7 group.php
  21. +3 −3 index.php
  22. BIN  locale/cs/LC_MESSAGES/cs.mo
  23. +10,377 −9,250 locale/cs/LC_MESSAGES/cs.po
  24. BIN  locale/da/LC_MESSAGES/da.mo
  25. +9,092 −9,040 locale/da/LC_MESSAGES/da.po
  26. BIN  locale/de-informal/LC_MESSAGES/de-informal.mo
  27. +333 −329 locale/de-informal/LC_MESSAGES/de-informal.po
  28. BIN  locale/de/LC_MESSAGES/de.mo
  29. +735 −731 locale/de/LC_MESSAGES/de.po
  30. BIN  locale/es-MX/LC_MESSAGES/es-MX.mo
  31. +9,749 −10,391 locale/es-MX/LC_MESSAGES/es-MX.po
  32. BIN  locale/fi/LC_MESSAGES/fi.mo
  33. +9,296 −9,002 locale/fi/LC_MESSAGES/fi.po
  34. BIN  locale/fr/LC_MESSAGES/fr.mo
  35. +8,810 −8,596 locale/fr/LC_MESSAGES/fr.po
  36. BIN  locale/gl/LC_MESSAGES/gl.mo
  37. +9,203 −9,167 locale/gl/LC_MESSAGES/gl.po
  38. BIN  locale/it/LC_MESSAGES/it.mo
  39. +9,094 −9,132 locale/it/LC_MESSAGES/it.po
  40. +4 −1 qanda.php
  41. +4 −2 replacements.php
  42. +2 −2 save.php
  43. +17 −6 {classes/expressions → scripts}/em_javascript.js
  44. +125 −778 scripts/jquery/jquery-ui.js
  45. +3 −3 scripts/modaldialog.js
  46. +1 −1  templates/basic/navigator.pstpl
  47. +6 −6 templates/basic/startpage.pstpl
  48. +5 −5 templates/citronade/template.js
  49. +10 −5 uploader.php
View
157 admin/activate_functions.php
@@ -1,33 +1,32 @@
<?php
/*
- * LimeSurvey
- * Copyright (C) 2007 The LimeSurvey Project Team / Carsten Schmitz
- * All rights reserved.
- * License: GNU/GPL License v2 or later, see LICENSE.php
- * LimeSurvey is free software. This version may have been modified pursuant
- * to the GNU General Public License, and as distributed it includes or
- * is derivative of works licensed under the GNU General Public License or
- * other free or open source software licenses.
- * See COPYRIGHT.php for copyright notices and details.
- *
- * $Id: activate_functions.php 12339 2012-02-04 12:48:38Z c_schmitz $
- * Files Purpose: holds functions to activate a survey and precheck the consistency of the survey
- */
+* LimeSurvey
+* Copyright (C) 2007 The LimeSurvey Project Team / Carsten Schmitz
+* All rights reserved.
+* License: GNU/GPL License v2 or later, see LICENSE.php
+* LimeSurvey is free software. This version may have been modified pursuant
+* to the GNU General Public License, and as distributed it includes or
+* is derivative of works licensed under the GNU General Public License or
+* other free or open source software licenses.
+* See COPYRIGHT.php for copyright notices and details.
+*
+* Files Purpose: holds functions to activate a survey and precheck the consistency of the survey
+*/
/**
- * fixes the numbering of questions
- * @global $dbprefix $dbprefix
- * @global $connect $connect
- * @global $clang $clang
- * @param <type> $fixnumbering
- */
+* fixes the numbering of questions
+* @global $dbprefix $dbprefix
+* @global $connect $connect
+* @global $clang $clang
+* @param <type> $fixnumbering
+*/
function fixNumbering($fixnumbering)
{
global $dbprefix, $connect, $clang, $surveyid;
LimeExpressionManager::RevertUpgradeConditionsToRelevance($surveyid);
- //Fix a question id - requires renumbering a question
+ //Fix a question id - requires renumbering a question
$oldqid = $fixnumbering;
$query = "SELECT qid FROM {$dbprefix}questions ORDER BY qid DESC";
$result = db_select_limit_assoc($query, 1) or safe_die($query."<br />".$connect->ErrorMsg());
@@ -53,9 +52,9 @@ function fixNumbering($fixnumbering)
foreach ($switcher as $switch)
{
$query = "UPDATE {$dbprefix}conditions
- SET cqid=$newqid,
- cfieldname='".str_replace("X".$oldqid, "X".$newqid, $switch['cfieldname'])."'
- WHERE cqid=$oldqid";
+ SET cqid=$newqid,
+ cfieldname='".str_replace("X".$oldqid, "X".$newqid, $switch['cfieldname'])."'
+ WHERE cqid=$oldqid";
$result = $connect->Execute($query) or safe_die($query."<br />".$connect->ErrorMsg());
}
}
@@ -69,17 +68,17 @@ function fixNumbering($fixnumbering)
LimeExpressionManager::UpgradeConditionsToRelevance($surveyid);
}
/**
- * checks consistency of groups
- * @global $dbprefix
- * @global $connect
- * @global $clang
- * @return <type>
- */
+* checks consistency of groups
+* @global $dbprefix
+* @global $connect
+* @global $clang
+* @return <type>
+*/
function checkGroup($postsid)
{
global $dbprefix, $connect, $clang;
- $baselang = GetBaseLanguageFromSurveyID($postsid);
+ $baselang = GetBaseLanguageFromSurveyID($postsid);
$groupquery = "SELECT g.gid,g.group_name,count(q.qid) as count from {$dbprefix}questions as q RIGHT JOIN {$dbprefix}groups as g ON q.gid=g.gid AND g.language=q.language WHERE g.sid=$postsid AND g.language='$baselang' group by g.gid,g.group_name;";
$groupresult=db_execute_assoc($groupquery) or safe_die($groupquery."<br />".$connect->ErrorMsg());
while ($row=$groupresult->FetchRow())
@@ -96,20 +95,20 @@ function checkGroup($postsid)
}
/**
- * checks questions in a survey for consistency
- * @global <type> $dbprefix
- * @global <type> $connect
- * @global <type> $clang
- * @param <type> $postsid
- * @param <type> $surveyid
- * @return array $faildcheck
- */
+* checks questions in a survey for consistency
+* @global <type> $dbprefix
+* @global <type> $connect
+* @global <type> $clang
+* @param <type> $postsid
+* @param <type> $surveyid
+* @return array $faildcheck
+*/
function checkQuestions($postsid, $surveyid, $qtypes)
{
- global $dbprefix, $connect, $clang;
+ global $dbprefix, $connect, $clang;
- //CHECK TO MAKE SURE ALL QUESTION TYPES THAT REQUIRE ANSWERS HAVE ACTUALLY GOT ANSWERS
+ //CHECK TO MAKE SURE ALL QUESTION TYPES THAT REQUIRE ANSWERS HAVE ACTUALLY GOT ANSWERS
//THESE QUESTION TYPES ARE:
// # "L" -> LIST
// # "O" -> LIST WITH COMMENT
@@ -118,7 +117,7 @@ function checkQuestions($postsid, $surveyid, $qtypes)
// # "A", "B", "C", "E", "F", "H", "^" -> Various Array Types
// # "R" -> RANKING
// # "U" -> FILE CSV MORE
- // # "I" -> LANGUAGE SWITCH
+ // # "I" -> LANGUAGE SWITCH
// # ":" -> Array Multi Flexi Numbers
// # ";" -> Array Multi Flexi Text
// # "1" -> MULTI SCALE
@@ -259,22 +258,22 @@ function checkQuestions($postsid, $surveyid, $qtypes)
return false;
}
/**
- * Function to activate a survey
- * @global $dbprefix $dbprefix
- * @global $connect $connect
- * @global $clang $clang
- * @param int $postsid
- * @param int $surveyid
- * @return string
- */
+* Function to activate a survey
+* @global $dbprefix $dbprefix
+* @global $connect $connect
+* @global $clang $clang
+* @param int $postsid
+* @param int $surveyid
+* @return string
+*/
function activateSurvey($postsid,$surveyid, $scriptname='admin.php')
{
global $dbprefix, $connect, $clang, $databasetype,$databasetabletype, $uploaddir;
- $createsurvey='';
- $activateoutput='';
- $createsurveytimings='';
- $createsurveydirectory=false;
+ $createsurvey='';
+ $activateoutput='';
+ $createsurveytimings='';
+ $createsurveydirectory=false;
//Check for any additional fields for this survey and create necessary fields (token and datestamp)
$pquery = "SELECT anonymized, allowregister, datestamp, ipaddr, refurl, savetimings FROM {$dbprefix}surveys WHERE sid={$postsid}";
$presult=db_execute_assoc($pquery);
@@ -393,7 +392,7 @@ function activateSurvey($postsid,$surveyid, $scriptname='admin.php')
$tabname = "{$dbprefix}survey_{$postsid}"; # not using db_table_name as it quotes the table name (as does CreateTableSQL)
$taboptarray = array('mysql' => 'ENGINE='.$databasetabletype.' CHARACTER SET utf8 COLLATE utf8_unicode_ci',
- 'mysqli'=> 'ENGINE='.$databasetabletype.' CHARACTER SET utf8 COLLATE utf8_unicode_ci');
+ 'mysqli'=> 'ENGINE='.$databasetabletype.' CHARACTER SET utf8 COLLATE utf8_unicode_ci');
$dict = NewDataDictionary($connect);
$sqlarray = $dict->CreateTableSQL($tabname, $createsurvey, $taboptarray);
@@ -454,31 +453,43 @@ function activateSurvey($postsid,$surveyid, $scriptname='admin.php')
if ($createsurveydirectory)
if (!file_exists($uploaddir."/surveys/" . $postsid . "/files"))
{
- if (!(mkdir($uploaddir."/surveys/" . $postsid . "/files", 0777, true)))
- {
- $activateoutput .= "<div class='warningheader'>".
+ if (!(mkdir($uploaddir."/surveys/" . $postsid . "/files", 0777, true)))
+ {
+ $activateoutput .= "<div class='warningheader'>".
$clang->gT("The required directory for saving the uploaded files couldn't be created. Please check file premissions on the limesurvey/upload/surveys directory.") . "</div>";
- }
- else
- {
- file_put_contents($uploaddir."/surveys/" . $postsid . "/files/index.html",'<html><head></head><body></body></html>');
- }
+ }
+ else
+ {
+ file_put_contents($uploaddir."/surveys/" . $postsid . "/files/index.html",'<html><head></head><body></body></html>');
+ }
}
- $acquery = "UPDATE {$dbprefix}surveys SET active='Y' WHERE sid=".$surveyid;
+ $acquery = "UPDATE {$dbprefix}surveys SET active='Y' WHERE sid=".$surveyid;
$acresult = $connect->Execute($acquery);
+ $query=db_select_tables_like("{$dbprefix}old\_tokens\_".$surveyid."\_%");
+ $result=db_execute_num($query) or safe_die("Couldn't get old table list<br />".$query."<br />".$connect->ErrorMsg());
+ $tcount=$result->RecordCount();
+ if ($tcount==0)
+ {
+ $sTokenActivationLink="$scriptname?action=tokens&amp;sid={$postsid}&amp;createtable=Y";
+ }
+ else
+ {
+ $sTokenActivationLink="$scriptname?action=tokens&amp;sid={$postsid}";
+ }
+
if (isset($surveyallowsregistration) && $surveyallowsregistration == "TRUE")
{
$activateoutput .= $clang->gT("This survey allows public registration. A token table must also be created.")."<br /><br />\n";
- $activateoutput .= "<input type='submit' value='".$clang->gT("Initialise tokens")."' onclick=\"".get2post("$scriptname?action=tokens&amp;sid={$postsid}&amp;createtable=Y")."\" />\n";
+ $activateoutput .= "<input type='submit' value='".$clang->gT("Initialise tokens")."' onclick=\"".get2post($sTokenActivationLink)."\" />\n";
}
else
{
$activateoutput .= $clang->gT("This survey is now active, and responses can be recorded.")."<br /><br />\n";
$activateoutput .= "<strong>".$clang->gT("Open-access mode").":</strong> ".$clang->gT("No invitation code is needed to complete the survey.")."<br />".$clang->gT("You can switch to the closed-access mode by initialising a token table with the button below.")."<br /><br />\n";
- $activateoutput .= "<input type='submit' value='".$clang->gT("Switch to closed-access mode")."' onclick=\"".get2post("$scriptname?action=tokens&amp;sid={$postsid}&amp;createtable=Y")."\" />\n";
+ $activateoutput .= "<input type='submit' value='".$clang->gT("Switch to closed-access mode")."' onclick=\"".get2post($sTokenActivationLink)."\" />\n";
$activateoutput .= "<input type='submit' value='".$clang->gT("No, thanks.")."' onclick=\"".get2post("$scriptname?sid={$postsid}")."\" />\n";
}
$activateoutput .= "</div><br />&nbsp;\n";
@@ -504,11 +515,11 @@ function mssql_drop_constraint($fieldname, $tablename)
// find out the name of the default constraint
// Did I already mention that this is the most suckiest thing I have ever seen in MSSQL database?
$dfquery ="SELECT c_obj.name AS constraint_name
- FROM sys.sysobjects AS c_obj INNER JOIN
- sys.sysobjects AS t_obj ON c_obj.parent_obj = t_obj.id INNER JOIN
- sys.sysconstraints AS con ON c_obj.id = con.constid INNER JOIN
- sys.syscolumns AS col ON t_obj.id = col.id AND con.colid = col.colid
- WHERE (c_obj.xtype = 'D') AND (col.name = '$fieldname') AND (t_obj.name='{$dbprefix}{$tablename}')";
+ FROM sys.sysobjects AS c_obj INNER JOIN
+ sys.sysobjects AS t_obj ON c_obj.parent_obj = t_obj.id INNER JOIN
+ sys.sysconstraints AS con ON c_obj.id = con.constid INNER JOIN
+ sys.syscolumns AS col ON t_obj.id = col.id AND con.colid = col.colid
+ WHERE (c_obj.xtype = 'D') AND (col.name = '$fieldname') AND (t_obj.name='{$dbprefix}{$tablename}')";
$defaultname=$connect->GetRow($dfquery);
if ($defaultname!=false)
{
@@ -519,11 +530,11 @@ function mssql_drop_constraint($fieldname, $tablename)
function mssql_drop_primary_index($tablename)
{
- global $dbprefix, $connect, $modifyoutput;
+ global $dbprefix, $connect, $modifyoutput;
// find out the constraint name of the old primary key
$pkquery = "SELECT CONSTRAINT_NAME "
- ."FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS "
- ."WHERE (TABLE_NAME = '{$dbprefix}{$tablename}') AND (CONSTRAINT_TYPE = 'PRIMARY KEY')";
+ ."FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS "
+ ."WHERE (TABLE_NAME = '{$dbprefix}{$tablename}') AND (CONSTRAINT_TYPE = 'PRIMARY KEY')";
$primarykey=$connect->GetOne($pkquery);
if ($primarykey!=false)
View
5,560 admin/dataentry.php
2,783 additions, 2,777 deletions not shown
View
12 admin/html.php
@@ -787,6 +787,18 @@
templatereplace($surveyinfo['surveyls_welcometext']);
$surveysummary .= LimeExpressionManager::GetLastPrettyPrintExpression();
+ if (trim($surveyinfo['surveyls_endtext'])!='')
+ {
+ $surveysummary .= "</td></tr>\n"
+ . "<tr >\n"
+ . "<td align='right' valign='top'><strong>"
+ . $clang->gT("End message:")."</strong></td>\n"
+ . "<td align='left'>";
+
+ templatereplace($surveyinfo['surveyls_endtext']);
+ $surveysummary .= LimeExpressionManager::GetLastPrettyPrintExpression();
+ }
+
$surveysummary .= "</td></tr>\n"
. "<tr ><td align='right' valign='top'><strong>"
. $clang->gT("Administrator:")."</strong></td>\n"
View
38 admin/importlabel.php
@@ -61,23 +61,32 @@
if (isset($aImportResults))
{
- if (count($aImportResults['warnings'])>0)
+ if (isset($aImportResults['fatalerror']))
{
- $importlabeloutput .= "<br />\n<div class='warningheader'>".$clang->gT("Warnings")."</div><ul>\n";
- foreach ($aImportResults['warnings'] as $warning)
+ $importlabeloutput .= "<br />\n<div class='warningheader'>".$clang->gT("Error")."</div>
+ <p>{$aImportResults['fatalerror']}<br>";
+ }
+ else
+ {
+ if (count($aImportResults['warnings'])>0)
{
- $importlabeloutput .= '<li>'.$warning.'</li>';
+ $importlabeloutput .= "<br />\n<div class='warningheader'>".$clang->gT("Warnings")."</div><ul>\n";
+ foreach ($aImportResults['warnings'] as $warning)
+ {
+ $importlabeloutput .= '<li>'.$warning.'</li>';
+ }
+ $importlabeloutput .= "</ul>\n";
}
- $importlabeloutput .= "</ul>\n";
- }
- $importlabeloutput .= "<br />\n<div class='successheader'>".$clang->gT("Success")."</div><br />\n";
- $importlabeloutput .= "<strong><u>".$clang->gT("Label set import summary")."</u></strong><br />\n";
- $importlabeloutput .= "<ul style=\"text-align:left;\">\n\t<li>".$clang->gT("Label sets").": {$aImportResults['labelsets']}</li>\n";
- $importlabeloutput .= "\t<li>".$clang->gT("Labels").": {$aImportResults['labels']}</li></ul>\n";
- $importlabeloutput .= "<p><strong>".$clang->gT("Import of label set(s) is completed.")."</strong><br /><br />\n";
+ $importlabeloutput .= "<br />\n<div class='successheader'>".$clang->gT("Success")."</div><br />\n";
+ $importlabeloutput .= "<strong><u>".$clang->gT("Label set import summary")."</u></strong><br />\n";
+ $importlabeloutput .= "<ul style=\"text-align:left;\">\n\t<li>".$clang->gT("Label sets").": {$aImportResults['labelsets']}</li>\n";
+ $importlabeloutput .= "\t<li>".$clang->gT("Labels").": {$aImportResults['labels']}</li></ul>\n";
+ $importlabeloutput .= "<p><strong>".$clang->gT("Import of label set(s) is completed.")."</strong><br /><br />\n";
+
+ }
$importlabeloutput .= "<input type='submit' value='".$clang->gT("Return to label set administration")."' onclick=\"window.open('$scriptname?action=labels', '_top')\" />\n";
- $importlabeloutput .= "</div><br />\n";
+ $importlabeloutput .= "</p></div><br />\n";
}
@@ -100,7 +109,8 @@ function CSVImportLabelset($sFullFilepath, $options)
fclose($handle);
if (substr($bigarray[0], 0, 27) != "# LimeSurvey Label Set Dump" && substr($bigarray[0], 0, 28) != "# PHPSurveyor Label Set Dump")
{
- return $results['fatalerror']=$clang->gT("This file is not a LimeSurvey label set file. Import failed.");
+ $results['fatalerror']=$clang->gT("This file is not a LimeSurvey label set file. Import failed.");
+ return $results;
}
for ($i=0; $i<9; $i++) //skipping the first lines that are not needed
@@ -355,4 +365,4 @@ function XMLImportLabelsets($sFullFilepath, $options)
return $results;
}
-// Closing PHP tag intentionall left out
+// Closing PHP tag intentionall left out
View
2  admin/iterate_survey.php
@@ -54,7 +54,7 @@
$updateqr = "UPDATE $surveytable SET submitdate=NULL, lastpage=NULL;\n";
$updateres = $connect->Execute($updateqr) or safe_die("UnFinilize answers failed:<br />\n" . $connect->ErrorMsg() . "<br />$updateqr");
// Finally, reset the token completed and sent status
- $updateqr="UPDATE ".db_table_name("tokens_$surveyid")." SET sent='N', remindersent='N', remindercount=0, completed='N', usesleft=1";
+ $updateqr="UPDATE ".db_table_name("tokens_$surveyid")." SET sent='N', remindersent='N', remindercount=0, completed='N', usesleft=1 where usesleft=0";
$updateres=$connect->Execute($updateqr) or safe_die ("Couldn't reset token completed state<br />$updateqr<br />".$connect->ErrorMsg());
$iteratesurveyoutput .= "<br />\n";
$iteratesurveyoutput .= "<div class='header ui-widget-header'>".$clang->gT("Iterate survey")."</div>\n";
View
2  admin/login_check.php
@@ -183,7 +183,7 @@
//include("database.php");
- $sIp = $_SERVER['REMOTE_ADDR'];
+ $sIp = getIPAddress();
$query = "SELECT * FROM ".db_table_name('failed_login_attempts'). " WHERE ip='$sIp';";
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$result = $connect->query($query) or safe_die ($query."<br />".$connect->ErrorMsg());
View
41 admin/questiongrouphandling.php
@@ -36,7 +36,7 @@
if (bHasSurveyPermission($surveyid,'surveycontent','import'))
{
$newgroupoutput .= '<li><a href="#import">'.$clang->gT("Import question group")."</a></li>\n";
-
+
}
$newgroupoutput .= "</ul>";
@@ -62,7 +62,7 @@
. getEditor("group-desc","description_".$grouplang, "[".$clang->gT("Description:", "js")."](".$grouplang.")",$surveyid,'','',$action)
. "</li>\n"
// Group-Level Relevance
- . "<li><label for='relevance'>".$clang->gT("Relevance equation:")."</label>"
+ . "<li><label for='grelevance'>".$clang->gT("Relevance equation:")."</label>"
. "<textarea cols='50' rows='1' id='grelevance' name='grelevance'></textarea>"
. "</li>"
. "</ul>"
@@ -93,12 +93,12 @@
// End Import TABS
$newgroupoutput.= "</div>";
}
-
+
// End of TABS
$newgroupoutput.= "</div>";
-
+
}
@@ -148,10 +148,10 @@
. "</span><span class='settingentry'><textarea cols='70' rows='8' id='description_{$esrow['language']}' name='description_{$esrow['language']}'>{$esrow['description']}</textarea>\n"
. getEditor("group-desc","description_".$esrow['language'], "[".$clang->gT("Description:", "js")."](".$esrow['language'].")",$surveyid,$gid,'',$action)
. "</span></div>"
- . "<div class='settingrow'><span class='settingcaption'><label for='relevance'>".$clang->gT("Relevance equation:")."</label></span>\n"
+ . "<div class='settingrow'><span class='settingcaption'><label for='grelevance'>".$clang->gT("Relevance equation:")."</label></span>\n"
. "<span class='settingentry'><textarea cols='50' rows='1' id='grelevance' name='grelevance'>".$esrow['grelevance']."</textarea></span>"
. "</span></div>"
- . "\t</span></div><div style='clear:both'></div>";
+ . "\n<div style='clear:both'></div>";
$egquery = "SELECT * FROM ".db_table_name('groups')." WHERE sid=$surveyid AND gid=$gid AND language!='$baselang'";
$egresult = db_execute_assoc($egquery);
$i = 1;
@@ -175,14 +175,14 @@
foreach ($tab_title as $i=>$eachtitle){
$editgroup .= "\t<li style='clear:none'><a href='#editgrp$i'>$eachtitle</a></li>\n";
-
+
}
$editgroup.="</ul>\n";
foreach ($tab_content as $i=>$eachcontent){
$editgroup .= "\n<div id='editgrp$i'>$eachcontent</div>";
}
-
+
$editgroup .= "</div>\n\t<p><input type='submit' class='standardbtn' value='".$clang->gT("Update Group")."' />\n"
. "\t<input type='hidden' name='action' value='updategroup' />\n"
. "\t<input type='hidden' name='sid' value=\"{$surveyid}\" />\n"
@@ -265,31 +265,6 @@
$ordergroups = "<div class='header ui-widget-header'>".$clang->gT("Change Group Order")."</div><br />\n";
- // Get groups dependencies regarding conditions
- // => Get an array of groups containing questions with conditions outside the group
- // $groupdepsarray[dependent-gid][target-gid]['conditions'][qid-having-conditions]=Array(cids...)
-// $groupdepsarray = GetGroupDepsForConditions($surveyid);
-// $nicegroupdeps=array();
-// if (!is_null($groupdepsarray))
-// {
-// $ordergroups .= "<ul class='movableList'><li class='movableNode'><strong><font color='orange'>".$clang->gT("Warning").":</font> ".$clang->gT("Current survey has questions with conditions outside their own group")."</strong><br /><br /><i>".$clang->gT("Re-ordering groups is restricted to ensure that questions on which conditions are based aren't reordered after questions having the conditions set")."</i></strong><br /><br/>".$clang->gT("The following groups are concerned").":<ul>\n";
-// foreach ($groupdepsarray as $depgid => $depgrouprow)
-// {
-// foreach($depgrouprow as $targgid => $targrow)
-// {
-// $ordergroups .= "<li>".sprintf($clang->gT("Group %s depends on group %s, see the marked conditions on:"), "<a href='#' onclick=\"window.open('admin.php?sid=".$surveyid."&amp;gid=".$depgid."')\">".$targrow['depgpname']."</a>", "<a href='#' onclick=\"window.open('admin.php?sid=".$surveyid."&amp;gid=".$targgid."')\">".$targrow['targetgpname']."</a> ");
-// $nicegroupdeps[$depgid]=$targgid;
-// foreach($targrow['conditions'] as $depqid => $depqrow)
-// {
-// $listcid=implode("-",$depqrow);
-// $ordergroups .= " <a href='#' onclick=\"window.open('admin.php?sid=".$surveyid."&amp;gid=".$depgid."&amp;qid=".$depqid."&amp;action=conditions&amp;markcid=".$listcid."','_top')\"> [".$clang->gT("QID").": ".$depqid."]</a>";
-// }
-// $ordergroups .= "</li>\n";
-// }
-// }
-// $ordergroups .= "</ul></li></ul>";
-// }
-
$ordergroups .= "<form method='post' action=''><ul class='movableList'>";
//Get the groups from this survey
$s_lang = GetBaseLanguageFromSurveyID($surveyid);
View
4 admin/questionhandling.php
@@ -408,9 +408,11 @@
. "\t<div class='settingrow'><span class='settingcaption'>".$clang->gT("Help:")."</span>\n"
. "<span class='settingentry'><textarea cols='50' rows='4' name='help_{$eqrow['language']}'>{$eqrow['help']}</textarea>\n"
. getEditor("question-help","help_".$eqrow['language'], "[".$clang->gT("Help:", "js")."](".$eqrow['language'].")",$surveyid,$gid,$qid,$action)
+ . "<!--"
. "\t</span></div>\n"
. "\t<div class='settingrow'><span class='settingcaption'>&nbsp;</span>\n"
. "<span class='settingentry'>&nbsp;\n"
+ . "-->"
. "\t</span></div>\n";
$editquestion .= '&nbsp;</div>';
@@ -449,9 +451,11 @@
. "\t<div class='settingrow'><span class='settingcaption'>".$clang->gT("Help:")."</span>\n"
. "<span class='settingentry'><textarea cols='50' rows='4' name='help_{$addlanguage}'></textarea>\n"
. getEditor("question-help","help_".$addlanguage, "[".$clang->gT("Help:", "js")."](".$addlanguage.")",$surveyid,$gid,$qid,$action)
+ . "<!--"
. "\t</span></div>\n"
. "\t<div class='settingrow'><span class='settingcaption'>&nbsp;</span>\n"
. "<span class='settingentry'>&nbsp;\n"
+ . "-->"
. "\t</span></div>\n";
$editquestion .= '</div>';
}
View
5,345 admin/tokens.php
2,681 additions, 2,664 deletions not shown
View
2  admin/usercontrol.php
@@ -100,7 +100,7 @@
{
include("database.php");
- $sIp= $_SERVER['REMOTE_ADDR'];
+ $sIp = getIPAddress();
$query = "SELECT * FROM ".db_table_name('failed_login_attempts'). " WHERE ip='$sIp';";
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
$result = $connect->query($query);
View
2  classes/adodb/datadict/datadict-mssql.inc.php
@@ -88,7 +88,7 @@ function ActualType($meta)
case 'B': return 'IMAGE';
- case 'D': return 'DATETIME';
+ case 'D': return 'DATE';
case 'TS':
case 'T': return 'DATETIME';
View
4 classes/adodb/datadict/datadict-mssqlnative.inc.php
@@ -88,7 +88,7 @@ function ActualType($meta)
case 'B': return 'IMAGE';
- case 'D': return 'DATETIME';
+ case 'D': return 'DATE';
case 'T': return 'DATETIME';
case 'L': return 'BIT';
@@ -135,7 +135,7 @@ function AlterColumnSQL($tabname, $flds)
}
*/
- function DropColumnSQL($tabname, $flds)
+ function DropColumnSQL($tabname, $flds, $tableflds='', $tableoptions='')
{
$tabname = $this->TableName ($tabname);
if (!is_array($flds))
View
808 classes/expressions/LimeExpressionManager.php
@@ -15,63 +15,597 @@
define('LEM_DEFAULT_PRECISION',12);
+ /**
+ * LimeExpressionManager provides all of the interfaces between LimeSurvey and the ExpressionManager sub-system.
+ */
class LimeExpressionManager {
+ /**
+ * LimeExpressionManager is a singleton. $instance is its storage location.
+ * @var LimeExpressionManager
+ */
private static $instance;
- private $em; // Expression Manager
+ /**
+ * Implements the recursive descent parser that processes expressions
+ * @var ExpressionManager
+ */
+ private $em;
+ /**
+ *
+ * @var type
+ */
private $groupRelevanceInfo;
+ /**
+ * The survey ID
+ * @var integer
+ */
private $sid;
- private $debugLevel=0; // sum of LEM_DEBUG constants - use bitwise AND comparisons to identify which parts to use
- private $knownVars; // collection of variable attributes, indexed by SGQA code
- private $qcode2sgqa; // maps qcode varname to SGQA code
- private $tempVars; // variables temporarily set for substitution purposes
+ /**
+ * sum of LEM_DEBUG constants - use bitwise AND comparisons to identify which parts to use
+ * @var type
+ */
+ private $debugLevel=0;
+ /**
+ * Collection of variable attributes, indexed by SGQA code
+ *
+ * Actual variables are stored in this structure:
+ * $knownVars[$sgqa] = array(
+ * 'jsName_on' => // the name of the javascript variable if it is defined on the current page - often 'answerSGQA'
+ * 'jsName' => // the name of the javascript variable when referenced on different pages - usually 'javaSGQA'
+ * 'readWrite' => // 'Y' for yes, 'N' for no - currently not used
+ * 'hidden' => // 1 if the question attribute 'hidden' is true, otherwise 0
+ * 'question' => // the text of the question (or sub-question)
+ * 'qid' => // the numeric question id - e.g. the Q part of the SGQA name
+ * 'gid' => // the numeric group id - e.g. the G part of the SGQA name
+ * 'grelevance' => // the group level relevance string
+ * 'relevance' => // the question level relevance string
+ * 'qcode' => // the qcode-style variable name for this question (or sub-question)
+ * 'qseq' => // the 0-based index of the question within the survey
+ * 'gseq' => // the 0-based index of the group within the survey
+ * 'type' => // the single character type code for the question
+ * 'sgqa' => // the SGQA name for the variable
+ * 'ansList' => // ansArray converted to a JavaScript fragment - e.g. ",'answers':{ 'M':'Male','F':'Female'}"
+ * 'ansArray' => // PHP array of answer strings, keyed on the answer code = e.g. array['M']='Male';
+ * 'scale_id' => // '0' for most answers. '1' for second scale within dual-scale questions
+ * 'rootVarName' => // the root code / name / title for the question, without any sub-question or answer-level suffix. This is from the title column in the questions table
+ * 'subqtext' => // the sub-question text
+ * 'rowdivid' => // the JavaScript ID of the row identifier for a question. This is used to show/hide entire question rows
+ * 'onlynum' => // 1 if only numbers are allowed for this variable. If so, then extra processing is needed to ensure that can use comma as a decimal separator
+ * );
+ *
+ * Reserved variables (e.g. TOKEN:xxxx) are stored with this structure:
+ * $knownVars[$token] = array(
+ * 'code' => // the static value for the variable
+ * 'type' => // ''
+ * 'jsName_on' => // ''
+ * 'jsName' => // ''
+ * 'readWrite' => // 'N' - since these are always read-only variables
+ * );
+ *
+ * @var type
+ */
+ private $knownVars;
+ /**
+ * maps qcode varname to SGQA code
+ *
+ * @example ['gender'] = '38612X10X145'
+ * @var type
+ */
+ private $qcode2sgqa;
+ /**
+ * variables temporarily set for substitution purposes
+ *
+ * These are typically the LimeReplacement Fields passed in via templatereplace()
+ * Each has the following structure: array(
+ * 'code' => // the static value of the variable
+ * 'jsName_on' => // ''
+ * 'jsName' => // ''
+ * 'readWrite' => // 'N'
+ * );
+ *
+ * @var type
+ */
+ private $tempVars;
+ /**
+ * Array of relevance information for each page (gseq), indexed by gseq.
+ * Within a page, it contains a sequential list of the results of each relevance equation processed
+ * array(
+ * 'qid' => // question id -- e.g. 154
+ * 'gseq' => // 0-based group sequence -- e.g. 2
+ * 'eqn' => // the raw relevance equation parsed -- e.g. "!is_empty(p2_sex)"
+ * 'result' => // the Boolean result of parsing that equation in the current context -- e.g. 0
+ * 'numJsVars' => // the number of dynamic JavaScript variables used in that equation -- e.g. 1
+ * 'relevancejs' => // the actual JavaScript to insert for that relevance equation -- e.g. "LEMif(LEManyNA('p2_sex'),'',( ! LEMempty(LEMval('p2_sex') )))"
+ * 'relevanceVars' => // a pipe-delimited list of JavaScript variables upon which that equation depends -- e.g. "java38612X12X153"
+ * 'jsResultVar' => // the JavaScript variable in which that result will be stored -- e.g. "java38612X12X154"
+ * 'type' => // the single character type of the question -- e.g. 'S'
+ * 'hidden' => // 1 if the question should always be hidden
+ * 'hasErrors' => // 1 if there were parsing errors processing that relevance equation
+ * @var type
+ */
private $pageRelevanceInfo;
+ /**
+ *
+ * @var type
+ */
private $pageTailorInfo;
- private $allOnOnePage=false; // internally set to true for survey.php so get group-specific logging but keep javascript variable namings consistent on the page.
- private $surveyMode='group'; // survey mode
- private $surveyOptions=array(); // a set of global survey options passed from LimeSurvey
- private $qid2code; // array of mappings of Question # to list of SGQA codes used within it
- private $jsVar2qid; // reverse mapping of JavaScript Variable name to Question
- private $qcode2sgq; // maps name of the variable to the SGQ name
- private $alias2varName; // JavaScript array of mappings of aliases to the JavaScript variable names
- private $varNameAttr; // JavaScript array of mappings of canonical JavaScript variable name to key attributes.
-
- private $qans; // array of answer lists indexed by qid
- private $groupId2groupSeq; // map of gid to 0-based sequence number of groups
- private $questionId2questionSeq; // map question # to an incremental count of question order across the whole survey
- private $questionId2groupSeq; // map question # to the group it is within, using an incremental count of group order
- private $groupSeqInfo; // array of info about each Group, indexed by GroupSeq
-
- private $gseq2relevanceStatus; // tracks which groups have at least one relevant, non-hidden question
- private $qid2validationEqn; // maps question # to the validation equation for that question.
-
- private $questionSeq2relevance; // keeps relevance in proper sequence so can minimize relevance processing to see what should be see on page and in indexes
- private $currentGroupSeq; // current Group sequence (0-based index)
- private $currentQuestionSeq; // for Question-by-Question mode, the 0-based index
- private $currentQID; // used in Question-by-Question mode
- private $currentQset=NULL; // set of the current set of questions to be displayed, indexed by QID - at least one must be relevant
- private $lastMoveResult=NULL; // last result of NavigateForwards, NavigateBackwards, or JumpTo
- private $indexQseq; // array of information needed to generate navigation index in question-by-question mode
- private $indexGseq; // array of information needed to generate navigation index in group-by-group mode
- private $gseq2info; // array of group sequence number to static info
-
- private $maxGroupSeq; // the maximum groupSeq reached - this is needed for Index
- private $q2subqInfo; // mapping of questions to information about their subquestions.
- private $qattr; // array of attributes for each question
- private $subQrelInfo=array(); // list of needed sub-question relevance (e.g. array_filter)
- private $gRelInfo=array(); // array of Group-level relevance status
+ /**
+ * internally set to true (1) for survey.php so get group-specific logging but keep javascript variable namings consistent on the page.
+ * @var type
+ */
+ private $allOnOnePage=false;
+ /**
+ * survey mode. One of 'survey', 'group', or 'question'
+ * @var string
+ */
+ private $surveyMode='group';
+ /**
+ * a set of global survey options passed from LimeSurvey
+ *
+ * For example, array(
+ * 'rooturl' => // URL prefix needed to be able to click on a syntax-highlighted variable name and have it open the needed editting window
+ * 'hyperlinkSyntaxHighlighting' => // true if should be able to click on variables to edit them
+ * 'active' => // 0 for inactive, 1 for active survey
+ * 'allowsave' => // 0 for do not allow save; 1 for allow save
+ * 'anonymized' => // 1 for anonymous
+ * 'assessments' => // 1 for use assessments
+ * 'datestamp' => // 1 for use date stamps
+ * 'ipaddr' => // 1 for capture IP address
+ * 'radix' => // '.' for use period as decimal separator; ',' for use comma as decimal separator
+ * 'savetimings' => // "Y" if should save survey timings
+ * 'startlanguage' => // the starting language -- e.g. 'en'
+ * 'surveyls_dateformat' => // the index of the language specific date format -- e.g. 1
+ * 'tablename' => // the name of the table storing the survey data, if active -- e.g. lime_survey_38612
+ * 'target' => // the path for uploading files -- e.g. '/temp/files/'
+ * 'timeadjust' => // the time offset -- e.g. 0
+ * 'tempdir' => // the temporary directory for uploading files -- e.g. '/temp/'
+ * );
+ *
+ * @var type
+ */
+ private $surveyOptions=array();
+ /**
+ * array of mappings of Question # (qid) to pipe-delimited list of SGQA codes used within it
+ *
+ * @example [150] = "38612X11X150|38612X11X150other"
+ * @var type
+ */
+ private $qid2code;
+ /**
+ * array of mappings of JavaScript Variable names to Question number (qid)
+ *
+ * @example ['java38612X13X161other'] = '161'
+ * @var type
+ */
+ private $jsVar2qid;
+ /**
+ * maps name of the variable to the SGQ name (without the A suffix)
+ *
+ * @example ['p1_sex'] = "38612X10X147"
+ * @example ['afDS_sq1_1'] = "26626X37X705sq1#1"
+ * @var type
+ */
+ private $qcode2sgq;
+ /**
+ * array of mappings of knownVar aliases to the JavaScript variable names.
+ * This maps both the SGQA and qcode alias names to the same 2 dimensional array
+ *
+ * @example ['p1_sex'] = array(
+ * 'jsName' => // the JavaScript variable name used by EM -- e.g. "java38612X11X147"
+ * 'jsPart' => // the JavaScript fragment used in EM's ____ array -- e.g. "'p1_sex':'java38612X11X147'"
+ * );
+ * @example ['afDS_sq1_1] = array(
+ * 'jsName' => "java26626X37X705sq1#1"
+ * 'jsPart' => "'afDS_sq1_1':'java26626X37X705sq1#1'"
+ * );
+ * @var type
+ */
+ private $alias2varName;
+ /**
+ * JavaScript array of mappings of canonical JavaScript variable name to key attributes.
+ * These fragments are used to create the JavaScript varNameAttr array.
+ *
+ * @example ['java38612X11X147'] = "'java38612X11X147':{ 'jsName':'java38612X11X147','jsName_on':'java38612X11X147','sgqa':'38612X11X147','qid':147,'gid':11,'type':'G','default':'','rowdivid':'','onlynum':'','gseq':1,'answers':{ 'M':'Male','F':'Female'}}"
+ * @example ['java26626X37X705sq1#1'] = "'java26626X37X705sq1#1':{ 'jsName':'java26626X37X705sq1#1','jsName_on':'java26626X37X705sq1#1','sgqa':'26626X37X705sq1#1','qid':705,'gid':37,'type':'1','default':'','rowdivid':'26626X37X705sq1','onlynum':'','gseq':1,'answers':{ '0~1':'1|Low','0~2':'2|Medium','0~3':'3|High','1~1':'1|Never','1~2':'2|Sometimes','1~3':'3|Always'}}"
+ *
+ * @var type
+ */
+ private $varNameAttr;
+ /**
+ * array of enumerated answer lists indexed by qid
+ * These use a tilde syntax to indicate which scale the answer is part of.
+ *
+ * @example ['0~4'] = "4|Child" // this means that code 4 in scale 0 has a coded value of 4 and a display value of 'Child'
+ * @example (for [705]): ['1~2'] = '2|Sometimes' // this means that the second scale for this question uses the coded value of 2 to represent 'Sometimes'
+ * @example // TODO - add example from survey using assessments
+ *
+ * @var type
+ */
+ private $qans;
+ /**
+ * map of gid to 0-based sequence number of groups
+ *
+ * @example [10] = 0 // means that the first group (gseq=0) has gid=10
+ *
+ * @var type
+ */
+ private $groupId2groupSeq;
+ /**
+ * map question # to an incremental count of question order across the whole survey
+ *
+ * @example [157] = 13 // means that that 14th question in the survey has qid=157
+ *
+ * @var type
+ */
+ private $questionId2questionSeq;
+ /**
+ * 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
+ *
+ * @example [2] = 0 // means that the third group (gseq==2) is currently irrelevant
+ *
+ * @var type
+ */
+ private $gseq2relevanceStatus;
+ /**
+ * maps question # to the validation equation(s) for that question.
+ * These are grouped by qid then validation type, such as 'value_range', and 'num_answers'
+ *
+ * @example [703] = array(
+ * 'eqn' => array(
+ * 'value_range' = "((is_empty(26626X34X703.NAOK) || 26626X34X703.NAOK >= (0)) and (is_empty(26626X34X703.NAOK) || 26626X34X703.NAOK <= (5)))"
+ * ),
+ * 'tips' => array(
+ * 'value_range' = "Each answer must be between {fixnum(0)} and {fixnum(5)}"
+ * ),
+ * 'subqValidEqns' = array(
+ * [] = array(
+ * 'subqValidSelector' => '' //
+ * 'subqValidEqn' => "(is_empty(26626X34X703.NAOK) || 26626X34X703.NAOK >= (0)) && (is_empty(26626X34X703.NAOK) || 26626X34X703.NAOK <= (5))"
+ * ),
+ * 'sumEqn' => '' // the equation to compute the current sum of the responses
+ * 'sumRemainingEqn' => '' // the equation to how much is left (for the question attribute that lets you specify the exact value of the sum of the answers)
+ * );
+ *
+ * @var type
+ */
+ private $qid2validationEqn;
+
+ /**
+ * keeps relevance in proper sequence so can minimize relevance processing to see what should be see on page and in indexes
+ * Array is indexed on qseq
+ *
+ * @example [3] = array(
+ * 'relevance' => "!is_empty(num)" // the question-level relevance equation
+ * 'grelevance' => "" // the group-level relevance equation
+ * 'qid' => "699" // the question id
+ * 'qseq' => 3 // the 0-index question sequence
+ * 'gseq' => 0 // the 0-index group sequence
+ * 'jsResultVar_on' => 'answer26626X34X699' // the javascript variable holding the input value
+ * 'jsResultVar' => 'java26226X34X699' // the javascript variable (often hidden) holding the value to be submitted
+ * 'type' => 'N' // the one character question type
+ * 'hidden' => 0 // 1 if it should be always_hidden
+ * 'gid' => "34" // group id
+ * 'mandatory' => 'N' // 'Y' if mandatory
+ * 'eqn' => "" // TODO ??
+ * 'help' => "" // the help text
+ * 'qtext' => "Enter a larger number than {num}" // the question text
+ * 'code' => 'afDS_sq5_1' // the full variable name
+ * 'other' => 'N' // whether the question supports the 'other' option - 'Y' if true
+ * 'rootVarName' => 'afDS' // the root variable name
+ * 'rowdivid' => '2626X37X705sq5' // the javascript id for the row - in this case, the 5th sub-question
+ * 'aid' => 'sq5' // the answer id
+ * 'sqid' => '791' // the sub-question's qid (only populated for some question types)
+ * );
+ *
+ * @var type
+ */
+ private $questionSeq2relevance;
+ /**
+ * 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
+ *
+ * The array has N entries, where N is the number if qids in the Qset. Each has the following contents:
+ * @example [705] = array(
+ * 'info' => array() // this is an exact copy of $questionSeq2relevance[$qseq] -- TODO - remove redundancy
+ * 'relevant' => 1 // 1 if the question is currently relevant
+ * 'hidden' => 0 // 1 if the question is always hidden
+ * 'relEqn' => '' // the relevance equation -- TODO - how different from ['info']['relevance']?
+ * 'sgqa' => // pipe-separated list of SGQA codes for this question -- e.g. "26626X37X705sq1#0|26626X37X705sq1#1|26626X37X705sq2#0|26626X37X705sq2#1|26626X37X705sq3#0|26626X37X705sq3#1|26626X37X705sq4#0|26626X37X705sq4#1|26626X37X705sq5#0|26626X37X705sq5#1"
+ * 'unansweredSQs' => // pipe-separated list of currently unanswered SGQA codes for this question -- e.g. "26626X37X705sq1#0|26626X37X705sq1#1|26626X37X705sq3#0|26626X37X705sq3#1|26626X37X705sq5#0|26626X37X705sq5#1"
+ * 'valid' => 0 // 1 if the current answers pass all of the validation criteria for the question
+ * 'validEqn' => // the auto-generated validation criteria, based upon advanced question attributes -- e.g. "((count(if(count(26626X37X705sq1#0.NAOK,26626X37X705sq1#1.NAOK)==2,1,''), if(count(26626X37X705sq2#0.NAOK,26626X37X705sq2#1.NAOK)==2,1,''), if(count(26626X37X705sq3#0.NAOK,26626X37X705sq3#1.NAOK)==2,1,''), if(count(26626X37X705sq4#0.NAOK,26626X37X705sq4#1.NAOK)==2,1,''), if(count(26626X37X705sq5#0.NAOK,26626X37X705sq5#1.NAOK)==2,1,'')) >= (minSelect)) and (count(if(count(26626X37X705sq1#0.NAOK,26626X37X705sq1#1.NAOK)==2,1,''), if(count(26626X37X705sq2#0.NAOK,26626X37X705sq2#1.NAOK)==2,1,''), if(count(26626X37X705sq3#0.NAOK,26626X37X705sq3#1.NAOK)==2,1,''), if(count(26626X37X705sq4#0.NAOK,26626X37X705sq4#1.NAOK)==2,1,''), if(count(26626X37X705sq5#0.NAOK,26626X37X705sq5#1.NAOK)==2,1,'')) <= (maxSelect)))"
+ * 'prettyValidEqn' => // syntax-highlighted version of validEqn, only showing syntax errors
+ * 'validTip' => // html fragment to insert for the validation tip -- e.g. "<div id='vmsg_705_num_answers' class='em_num_answers'>Please select between 1 and 3 answer(s)</div>"
+ * 'prettyValidTip' => // version of validTip that can be parsed by EM to create dynmamic validation -- e.g. "<div id='vmsg_705_num_answers' class='em_num_answers'>Please select between {fixnum(minSelect)} and {fixnum(maxSelect)} answer(s)</div>"
+ * 'validJS' => // JavaScript fragment that can perform validation. This is the result of parsing validEqn -- e.g. "LEMif(LEManyNA('minSelect', 'maxSelect'),'',(((LEMcount(LEMif(LEMcount(LEMval('26626X37X705sq1#0.NAOK') , LEMval('26626X37X705sq1#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq2#0.NAOK') , LEMval('26626X37X705sq2#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq3#0.NAOK') , LEMval('26626X37X705sq3#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq4#0.NAOK') , LEMval('26626X37X705sq4#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq5#0.NAOK') , LEMval('26626X37X705sq5#1.NAOK') ) == 2, 1, '')) >= (LEMval('minSelect') )) && (LEMcount(LEMif(LEMcount(LEMval('26626X37X705sq1#0.NAOK') , LEMval('26626X37X705sq1#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq2#0.NAOK') , LEMval('26626X37X705sq2#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq3#0.NAOK') , LEMval('26626X37X705sq3#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq4#0.NAOK') , LEMval('26626X37X705sq4#1.NAOK') ) == 2, 1, ''), LEMif(LEMcount(LEMval('26626X37X705sq5#0.NAOK') , LEMval('26626X37X705sq5#1.NAOK') ) == 2, 1, '')) <= (LEMval('maxSelect') )))))"
+ * 'invalidSQs' => // current list of subquestions that fail validation criteria -- e.g. "26626X37X705sq1#0|26626X37X705sq1#1|26626X37X705sq2#0|26626X37X705sq2#1|26626X37X705sq3#0|26626X37X705sq3#1|26626X37X705sq4#0|26626X37X705sq4#1|26626X37X705sq5#0|26626X37X705sq5#1"
+ * 'relevantSQs' => // current list of subquestions that are relevant -- e.g. "26626X37X705sq1#0|26626X37X705sq1#1|26626X37X705sq2#0|26626X37X705sq2#1|26626X37X705sq3#0|26626X37X705sq3#1|26626X37X705sq4#0|26626X37X705sq4#1|26626X37X705sq5#0|26626X37X705sq5#1"
+ * 'irrelevantSQs' => // current list of subquestions that are irrelevant -- e.g. "26626X37X705sq2#0|26626X37X705sq2#1|26626X37X705sq4#0|26626X37X705sq4#1"
+ * 'subQrelEqn' => // TODO - ??
+ * 'mandViolation' => 0 // 1 if the question is mandatory and fails the mandatory criteria
+ * 'anyUnanswered' => 1 // 1 if any parts of the question are unanswered
+ * 'mandTip' => '' // message to display if the question fails mandatory criteria
+ * 'message' => '' // TODO ??
+ * 'updatedValues' => // array of values that should be updated for this question, as [$sgqa] = $value
+ * 'sumEqn' => '' //
+ * 'sumRemainingEqn' => '' //
+ * );
+ *
+ * @var type
+ */
+ private $currentQset=NULL;
+ /**
+ * last result of NavigateForwards, NavigateBackwards, or JumpTo
+ * Array of status information about last movement, whether at question, group, or survey level
+ *
+ * @example = array(
+ * 'finished' => 0 // 1 if the survey has been completed and needs to be finalized
+ * 'message' => '' // any error message that needs to be displayed
+ * 'seq' => 1 // the sequence count, using gseq, or qseq units if in 'group' or 'question' mode, respectively
+ * 'mandViolation' => 0 // whether there was any violation of mandatory constraints in the last movement
+ * 'valid' => 0 // 1 if the last movement passed all validation constraints. 0 if there were any validation errors
+ * 'unansweredSQs' => // pipe-separated list of any sub-questions that were not answered
+ * 'invalidSQs' => // pipe-separated list of any sub-questions that failed validation constraints
+ * );
+ *
+ * @var type
+ */
+ private $lastMoveResult=NULL;
+ /**
+ * array of information needed to generate navigation index in question-by-question mode
+ * One entry for each question, indexed by qseq
+ *
+ * @example [4] = array(
+ * 'qid' => "700" // the question id
+ * 'qtext' => 'How old are you?' // the question text
+ * 'qcode' => 'age' // the variable name
+ * 'qhelp' => '' // the help text
+ * 'anyUnanswered' => 0 // 1 if there are any sub-questions answered. Used for index display
+ * 'anyErrors' => 0 // 1 if there are any errors among the sub-questions. Could be used for index display
+ * 'show' => 1 // 1 if there are any relevant, non-hidden sub-questions. Only if so, then display the index entry
+ * 'gseq' => 0 // the group sequence
+ * 'gtext' => // text description for the group
+ * 'gname' => 'G1' // the group title
+ * 'gid' => "34" // the group id
+ * 'mandViolation' => 0 // 1 if the question as a whole fails the mandatory criteria
+ * 'valid' => 1 // 0 if any part of the question fails validation criteria.
+ * );
+ *
+ * @var type
+ */
+ private $indexQseq;
+ /**
+ * array of information needed to generate navigation index in group-by-group mode
+ * One entry for each group, indexed by gseq
+ *
+ * @example [0] = array(
+ * 'gtext' => // the description for the group
+ * 'gname' => 'G1' // the group title
+ * 'gid' => '34' // the group id
+ * 'anyUnanswered' => 0 // 1 if any questions within the group are unanswered
+ * 'anyErrors' => 0 // 1 if any of the questions within the group fail either validity or mandatory constraints
+ * 'valid' => 1 // 1 if at least question in the group is relevant and non-hidden
+ * 'mandViolation' => 0 // 1 if at least one relevant, non-hidden question in the group fails mandatory constraints
+ * 'show' => 1 // 1 if there is at least one relevant, non-hidden question within the group
+ * );
+ *
+ * @var type
+ */
+ private $indexGseq;
+ /**
+ * array of group sequence number to static info
+ * One entry per group, indexed on gseq
+ *
+ * @example [0] = array(
+ * 'group_order' => 0 // gseq
+ * 'gid' => "34" // group id
+ * 'group_name' => 'G2' // the group title
+ * 'description' => // the description of the group (e.g. gtitle)
+ * 'grelevance' => '' // the group-level relevance
+ * );
+ *
+ * @var type
+ */
+ private $gseq2info;
+
+ /**
+ * the maximum groupSeq reached - this is needed for Index
+ * @var type
+ */
+ private $maxGroupSeq;
+ /**
+ * mapping of questions to information about their subquestions.
+ * One entry per question, indexed on qid
+ *
+ * @example [702] = array(
+ * 'qid' => 702 // the question id
+ * 'qseq' => 6 // the question sequence
+ * 'gseq' => 0 // the group sequence
+ * 'sgqa' => '26626X34X702' // the root of the SGQA code (reallly just the SGQ)
+ * 'varName' => 'afSrcFilter_sq1' // the full qcode variable name - note, if there are sub-questions, don't use this one.
+ * 'type' => 'M' // the one-letter question type
+ * 'fieldname' => '26626X34X702sq1' // the fieldname (used as JavaScript variable name, and also as database column name
+ * 'subqs' => array() of sub-questions, where each contains:
+ * 'rowdivid' => '26626X34X702sq1' // the javascript id identifying the question row (so array_filter can hide rows)
+ * 'varName' => 'afSrcFilter_sq1' // the full variable name for the sub-question
+ * 'jsVarName_on' => 'java26626X34X702sq1' // the JavaScript variable name if the variable is defined on the current page
+ * 'jsVarName' => 'java26626X34X702sq1' // the JavaScript variable name to use if the variable is defined on a different page
+ * 'csuffix' => 'sq1' // the SGQ suffix to use for a fieldname
+ * 'sqsuffix' => '_sq1' // the suffix to use for a qcode variable name
+ * );
+ *
+ * @var type
+ */
+ private $q2subqInfo;
+ /**
+ * array of advanced question attributes for each question
+ * Indexed by qid; available for all quetions
+ *
+ * @example [784] = array(
+ * 'array_filter_exclude' => 'afSrcFilter'
+ * 'exclude_all_others' => 'sq5'
+ * 'max_answers' => '3'
+ * 'min_answers' => '1'
+ * 'other_replace_text' => '{afSrcFilter_other}'
+ * );
+ *
+ * @var type
+ */
+ private $qattr;
+ /**
+ * list of needed sub-question relevance (e.g. array_filter)
+ * Indexed by qid then sgqa; only generated for current group of questions
+ *
+ * @example [708][26626X37X708sq2] = array(
+ * 'qid' => '708' // the question id
+ * 'eqn' => "((26626X34X702sq2 != ''))" // the auto-generated sub-question-level relevance equation
+ * 'prettyPrintEqn' => '' // only generated if there errors - shows syntax highlighting of them
+ * 'result' => 0 // result of processing the sub-question-level relevance equation in the current context
+ * 'numJsVars' => 1 // the number of on-page javascript variables in 'eqn'
+ * 'relevancejs' => // the generated javascript from 'eqn' -- e.g. "LEMif(LEManyNA('26626X34X702sq2'),'',(((LEMval('26626X34X702sq2') != ""))))"
+ * 'relevanceVars' => "java26626X34X702sq2" // the pipe-separated list of on-page javascript variables in 'eqn'
+ * 'rowdivid' => "26626X37X708sq2" // the javascript id of the question row (so can apply array_filter)
+ * 'type' => 'array_filter' // semicolon delimited list of types of subquestion relevance filters applied
+ * 'qtype' => 'A' // the single character question type
+ * 'sgqa' => "26626X37X708" // the SGQ portion of the fieldname
+ * 'hasErrors' => 0 // 1 if there are any parse errors in the sub-question validation equations
+ * );
+ *
+ * @var type
+ */
+ private $subQrelInfo=array();
+ /**
+ * array of Group-level relevance status
+ * Indexed by gseq; only shows groups that have been visited
+ *
+ * @example [1] = array(
+ * 'gseq' => 1 // group sequence
+ * 'eqn' => '' // the group-level relevance
+ * 'result' => 1 // result of processing the group-level relevance
+ * 'numJsVars' => 0 // the number of on-page javascript variables in the group-level relevance equation
+ * 'relevancejs' => '' // the javascript version of the relevance equation
+ * 'relevanceVars' => '' // the pipe-delimited list of on-page javascript variable names used within the group-level relevance equation
+ * 'prettyPrint' => '' // a pretty-print version of the group-level relevance equation, only if there are errors
+ * );
+ *
+ * @var type
+ */
+ private $gRelInfo=array();
+
+ /**
+ * Array of timing information to debug how long it takes for portions of LEM to run.
+ * Array of timing information (in seconds) for EM to help with debugging
+ *
+ * @example [1] = array(
+ * [0]="LimeExpressionManager::NavigateForwards"
+ * [1]=1.7079849243164
+ * );
+ *
+ * @var type
+ */
private $runtimeTimings=array();
+ /**
+ * True (1) if calling LimeExpressionManager functions between StartSurvey and FinishProcessingPage
+ * Used (mostly deprecated) to detect calls to LEM which happen outside of the normal processing scope
+ * @var Boolean
+ */
private $initialized=false;
+ /**
+ * True (1) if have already processed the relevance equations (so don't need to do it again)
+ *
+ * @var Boolean
+ */
private $processedRelevance=false;
+ /**
+ * Message generated to show debug timing values, if debugLevel includes LEM_DEBUG_TIMING
+ * @var type
+ */
private $debugTimingMsg='';
- private $ParseResultCache; // temporary variable to reduce need to parse same equation multiple times. Used for relevance and validation
- private $multiflexiAnswers; // array of 2nd scale answer lists for types ':' and ';' -- needed for convenient print of logic file
+ /**
+ * temporary variable to reduce need to parse same equation multiple times. Used for relevance and validation
+ * Array, indexed on equation, providing the following information:
+ *
+ * @example ['!is_empty(num)'] = array(
+ * 'result' => 1 // result of processing the equation in the current scope
+ * 'prettyPrint' => '' // syntax-highlighted version of equation if there are any errors
+ * 'hasErrors' => 0 // 1 if there are any syntax errors
+ * );
+ *
+ * @var type
+ */
+ private $ParseResultCache;
+ /**
+ * array of 2nd scale answer lists for types ':' and ';' -- needed for convenient print of logic file
+ * Indexed on qid; available for all questions
+ *
+ * @example [706] = array(
+ * '1~1' => '1|Never',
+ * '1~2' => '2|Sometimes',
+ * '1~3' => '3|Always'
+ * );
+ *
+ * @var type
+ */
+ private $multiflexiAnswers;
- private $sgqaNaming = true; // used to specify whether to generate equations using SGQA codes or qcodes
+ /**
+ * used to specify whether to generate equations using SGQA codes or qcodes
+ * Default is to convert all qcode naming to sgqa naming when generating javascript, as that provides the greatest backwards compatibility
+ * Excel export of survey structure sets this to false so as to force use of qcode naming
+ *
+ * @var Boolean
+ */
+ private $sgqaNaming = true;
+ /**
+ * Number of groups in survey (number of possible pages to display)
+ * @var integer
+ */
private $numGroups=0;
+ /**
+ * Numer of questions in survey (counting display-only ones?)
+ * @var integer
+ */
private $numQuestions=0;
- // A private constructor; prevents direct creation of object
+ /**
+ * A private constructor; prevents direct creation of object
+ */
private function __construct()
{
self::$instance =& $this;
@@ -83,7 +617,7 @@ private function __construct()
/**
* Ensures there is only one instances of LEM. Note, if switch between surveys, have to clear this cache
- * @return <type>
+ * @return LimeExpressionManager
*/
public static function &singleton()
{
@@ -111,14 +645,16 @@ public static function &singleton()
return self::$instance;
}
- // Prevent users to clone the instance
+ /**
+ * Prevent users to clone the instance
+ */
public function __clone()
{
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
/**
- * Tells Expression Manager that something has changed enough that needs to eliminate caching
+ * Tells Expression Manager that something has changed enough that needs to eliminate internal caching
*/
public static function SetDirtyFlag()
{
@@ -128,7 +664,7 @@ public static function SetDirtyFlag()
/**
* Set the SurveyId - really checks whether the survey you're about to work with is new, and if so, clears the LEM cache
- * @param <type> $sid
+ * @param <integer> $sid
*/
public static function SetSurveyId($sid=NULL)
{
@@ -143,7 +679,7 @@ public static function SetSurveyId($sid=NULL)
/**
* Sets the language for Expression Manager. If the language has changed, then EM cache must be invalidated and refreshed
- * @param string $lang
+ * @param <string> $lang
*/
public static function SetEMLanguage($lang=NULL)
{
@@ -163,9 +699,9 @@ public static function SetEMLanguage($lang=NULL)
/**
* Do bulk-update/save of Conditions to Relevance
- * @param <type> $surveyId - if NULL, processes the entire database, otherwise just the specified survey
- * @param <type> $qid - if specified, just updates that one question
- * @return <type>
+ * @param <integer> $surveyId - if NULL, processes the entire database, otherwise just the specified survey
+ * @param <integer> $qid - if specified, just updates that one question
+ * @return array of query strings
*/
public static function UpgradeConditionsToRelevance($surveyId=NULL, $qid=NULL)
{
@@ -191,8 +727,8 @@ public static function UpgradeConditionsToRelevance($surveyId=NULL, $qid=NULL)
/**
* This reverses UpgradeConditionsToRelevance(). It removes Relevance for questions that have Conditions
- * @param <type> $surveyId
- * @param <type> $qid
+ * @param <integer> $surveyId
+ * @param <integer> $qid
*/
public static function RevertUpgradeConditionsToRelevance($surveyId=NULL, $qid=NULL)
{
@@ -213,9 +749,9 @@ public static function RevertUpgradeConditionsToRelevance($surveyId=NULL, $qid=N
/**
* If $qid is set, returns the relevance equation generated from conditions (or NULL if there are no conditions for that $qid)
* If $qid is NULL, returns an array of relevance equations generated from Conditions, keyed on the question ID
- * @param <type> $surveyId
- * @param <type> $qid - if passed, only generates relevance equation for that question - otherwise genereates for all questions with conditions
- * @return <type>
+ * @param <integer> $surveyId
+ * @param <integer> $qid - if passed, only generates relevance equation for that question - otherwise genereates for all questions with conditions
+ * @return array of generated relevance strings, indexed by $qid
*/
public static function ConvertConditionsToRelevance($surveyId=NULL, $qid=NULL)
{
@@ -373,9 +909,9 @@ public static function ConvertConditionsToRelevance($surveyId=NULL, $qid=NULL)
/**
* Return list of relevance equations generated from conditions
- * @param <type> $surveyId
- * @param <type> $qid
- * @return <type>
+ * @param <integer> $surveyId
+ * @param <integer> $qid
+ * @return array of relevance equations, indexed by $qid
*/
public static function UnitTestConvertConditionsToRelevance($surveyId=NULL, $qid=NULL)
{
@@ -384,10 +920,11 @@ public static function UnitTestConvertConditionsToRelevance($surveyId=NULL, $qid
}
/**
- * Process all question attributes that apply to EM
- * (1) Sub-question-level relevance: e.g. array_filter, array_filter_exclude
- * (2) Validations: e.g. min/max number of answers; min/max/eq sum of answers
- */
+ * Process all question attributes that apply to EM
+ * (1) Sub-question-level relevance: e.g. array_filter, array_filter_exclude
+ * (2) Validations: e.g. min/max number of answers; min/max/eq sum of answers
+ * @param <integer> $onlyThisQseq - only process these attributes for the specified question
+ */
public function _CreateSubQLevelRelevanceAndValidationEqns($onlyThisQseq=NULL)
{
// $now = microtime(true);
@@ -1748,10 +2285,10 @@ public function _CreateSubQLevelRelevanceAndValidationEqns($onlyThisQseq=NULL)
* Create the arrays needed by ExpressionManager to process LimeSurvey strings.
* The long part of this function should only be called once per page display (e.g. only if $fieldMap changes)
*
- * @param <type> $surveyid
- * @param <type> $forceRefresh
- * @param <type> $anonymized
- * @param <type> $allOnOnePage - if true (like for survey_format), uses certain optimizations
+ * @param <integer> $surveyid
+ * @param <Boolean> $forceRefresh
+ * @param <Boolean> $anonymized
+ * @param <Boolean> $allOnOnePage - if true (like for survey_format), uses certain optimizations
* @return boolean - true if $fieldmap had been re-created, so ExpressionManager variables need to be re-set
*/
@@ -2343,6 +2880,12 @@ private function setVariableAndTokenMappingsForExpressionManager($surveyid,$forc
{
//Gather survey data for tokenised surveys, for use in presenting questions
$_SESSION['thistoken']=getTokenData($surveyid, $_SESSION['token']);
+ $this->knownVars['TOKEN:TOKEN'] = array(
+ 'code'=>$_SESSION['token'],
+ 'jsName_on'=>'',
+ 'jsName'=>'',
+ 'readWrite'=>'N',
+ );
}
if (isset($_SESSION['thistoken']))
{
@@ -2368,7 +2911,9 @@ private function setVariableAndTokenMappingsForExpressionManager($surveyid,$forc
}
else
{
- // Explicitly set all tokens to blank
+ // Read list of available tokens from the tokens table so that preview and error checking works correctly
+ $attrs = GetAttributeFieldNames($surveyid,false);
+
$blankVal = array(
'code'=>'',
'type'=>'',
@@ -2376,13 +2921,13 @@ private function setVariableAndTokenMappingsForExpressionManager($surveyid,$forc
'jsName'=>'',
'readWrite'=>'N',
);
- $this->knownVars['TOKEN:FIRSTNAME'] = $blankVal;
- $this->knownVars['TOKEN:LASTNAME'] = $blankVal;
- $this->knownVars['TOKEN:EMAIL'] = $blankVal;
- $this->knownVars['TOKEN:USESLEFT'] = $blankVal;
- for ($i=1;$i<=100;++$i) // TODO - is there a way to know how many attributes are set? Looks like max is 100
+
+ foreach ($attrs as $key)
{
- $this->knownVars['TOKEN:ATTRIBUTE_' . $i] = $blankVal;
+ if (preg_match('/^(firstname|lastname|email|usesleft|token|attribute_\d+)$/',$key))
+ {
+ $this->knownVars['TOKEN:' . strtoupper($key)] = $blankVal;
+ }
}
}
// set default value for reserved 'this' variable
@@ -2517,7 +3062,7 @@ function ProcessAllNeededRelevance($onlyThisQseq=NULL)
* @param <type> $string - the string to be replaced
* @param <type> $questionNum - the $qid of question being replaced - needed for properly alignment of question-level relevance and tailoring
* @param <type> $replacementFields - optional replacement values
- * @param boolean $debug - if true,write translations for this page to html-formatted log file
+ * @param <boolean> $debug - if true,write translations for this page to html-formatted log file
* @param <type> $numRecursionLevels - the number of times to recursively subtitute values in this string
* @param <type> $whichPrettyPrintIteration - if want to pretty-print the source string, which recursion level should be pretty-printed
* @param <type> $noReplacements - true if we already know that no replacements are needed (e.g. there are no curly braces)
@@ -2809,6 +3354,7 @@ static function StartSurvey($surveyid,$surveyMode='group',$options=NULL,$forceRe
$LEM->surveyOptions['anonymized'] = (isset($options['anonymized']) ? $options['anonymized'] : false);
$LEM->surveyOptions['assessments'] = (isset($options['assessments']) ? $options['assessments'] : false);
$LEM->surveyOptions['datestamp'] = (isset($options['datestamp']) ? $options['datestamp'] : false);
+ $LEM->surveyOptions['deletenonvalues'] = (isset($options['deletenonvalues']) ? ($options['deletenonvalues']=='1') : true);
$LEM->surveyOptions['hyperlinkSyntaxHighlighting'] = (isset($options['hyperlinkSyntaxHighlighting']) ? $options['hyperlinkSyntaxHighlighting'] : false);
$LEM->surveyOptions['ipaddr'] = (isset($options['ipaddr']) ? $options['ipaddr'] : false);
$LEM->surveyOptions['radix'] = (isset($options['radix']) ? $options['radix'] : '.');
@@ -3220,6 +3766,22 @@ private function _UpdateValuesInDatabase($updatedValues, $finished=false,$setSub
// Update these values in the database
global $connect;
+ if (!$this->surveyOptions['deletenonvalues'])
+ {
+ $nonNullValues = array();
+ foreach($updatedValues as $key=>$value)
+ {
+ if (!is_null($value))
+ {
+ if (isset($value['value']) && !is_null($value['value']))
+ {
+ $nonNullValues[$key] = $value;
+ }
+ }
+ }
+ $updatedValues = $nonNullValues;
+ }
+
$message = '';
$_SESSION['datestamp']=date_shift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", $this->surveyOptions['timeadjust']);
if ($this->surveyOptions['active'] && !isset($_SESSION['srid']))
@@ -3228,7 +3790,7 @@ private function _UpdateValuesInDatabase($updatedValues, $finished=false,$setSub
$today = date_shift(date("Y-m-d H:i:s"), "Y-m-d H:i:s", $this->surveyOptions['timeadjust']);
$sdata = array(
"datestamp"=>$today,
- "ipaddr"=>(($this->surveyOptions['ipaddr'] && isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : ''),
+ "ipaddr"=>(($this->surveyOptions['ipaddr']) ? getIPAddress() : ''),
"startlanguage"=>$this->surveyOptions['startlanguage'],
"token"=>($this->surveyOptions['token']),
"datestamp"=>($this->surveyOptions['datestamp'] ? $_SESSION['datestamp'] : NULL),
@@ -3287,8 +3849,8 @@ private function _UpdateValuesInDatabase($updatedValues, $finished=false,$setSub
if ($this->surveyOptions['datestamp'] && isset($_SESSION['datestamp'])) {
$setter[] = db_quote_id('datestamp') . "=" . db_quoteall($_SESSION['datestamp']);
}
- if ($this->surveyOptions['ipaddr'] && isset($_SERVER['REMOTE_ADDR'])) {
- $setter[] = db_quote_id('ipaddr') . "=" . db_quoteall($_SERVER['REMOTE_ADDR']);
+ if ($this->surveyOptions['ipaddr']) {
+ $setter[] = db_quote_id('ipaddr') . "=" . db_quoteall(getIPAddress());
}
if ($finished) {
$setter[] = db_quote_id('submitdate') . "=" . db_quoteall($_SESSION['datestamp']);
@@ -4252,9 +4814,9 @@ function _ValidateQuestion($questionSeq)
if (isset($qattr['multiflexible_checkbox']) && $qattr['multiflexible_checkbox'] == 1)
{
// Need to check whether there is at least one checked box per row
- foreach ($LEM->subQrelInfo[$qid] as $sq)
+ foreach ($LEM->q2subqInfo[$qid]['subqs'] as $sq)
{
- if ($_SESSION['relevanceStatus'][$sq['rowdivid']])
+ if (!isset($_SESSION['relevanceStatus'][$sq['rowdivid']]) || $_SESSION['relevanceStatus'][$sq['rowdivid']])
{
$rowCount=0;
$numUnanswered=0;
@@ -4803,7 +5365,7 @@ static function GetRelevanceAndTailoringJavaScript()
$jsParts=array();
$allJsVarsUsed=array();
$rowdividList=array(); // list of subquestions needing relevance entries
- $jsParts[] = '<script type="text/javascript" src="'.$rooturl.'/classes/expressions/em_javascript.js"></script>';
+ $jsParts[] = '<script type="text/javascript" src="'.$rooturl.'/scripts/em_javascript.js"></script>';
$jsParts[] = "\n<script type='text/javascript'>\n<!--\n";
$jsParts[] = "var LEMmode='" . $LEM->surveyMode . "';\n";
if ($LEM->surveyMode == 'group')
@@ -5882,10 +6444,17 @@ private function getQuestionAttributesForEM($surveyid=NULL,$qid=NULL, $lang=NULL
if (!is_null($lang)) {
$lang = " and a.language='".$lang."' and b.language='".$lang."'";
}
+ global $databasetype;
+ if ($databasetype == 'odbc_mssql' || $databasetype == 'odbtp' || $databasetype == 'mssql_n' || $databasetype =='mssqlnative')
+ {
+ $query = "select distinct a.qid, a.attribute, CAST(a.value as varchar)";
+ }
+ else
+ {
+ $query = "select distinct a.qid, a.attribute, a.value";
+ }
- // TODO - does this need to be filtered by language?
- $query = "select distinct a.qid, a.attribute, a.value"
- ." from ".db_table_name('question_attributes')." as a, ".db_table_name('questions')." as b"
+ $query .= " from ".db_table_name('question_attributes')." as a, ".db_table_name('questions')." as b"
." where " . $where
.$lang
." order by a.qid, a.attribute";
@@ -6215,23 +6784,34 @@ private function _GetVarAttribute($name,$attr,$default,$gseq,$qseq)
case 'H': //ARRAY (Flexible) - Column Format
case 'F': //ARRAY (Flexible) - Row Format
case 'R': //RANKING STYLE
- $scale_id = $this->_GetVarAttribute($name,'scale_id','0',$gseq,$qseq);
- $which_ans = $scale_id . '~' . $code;
- $ansArray = $var['ansArray'];
- if (is_null($ansArray))
+ if ($type == 'O' && preg_match('/comment\.value/',$name))
+ {
+ $value = $code;
+ }
+ else if (($type == 'L' || $type == '!') && preg_match('/_other\.value/',$name))
{
- $value = $default;
+ $value = $code;
}
else
{
- if (isset($ansArray[$which_ans])) {
- $answerInfo = explode('|',$ansArray[$which_ans]);
- $answer = $answerInfo[0];
+ $scale_id = $this->_GetVarAttribute($name,'scale_id','0',$gseq,$qseq);
+ $which_ans = $scale_id . '~' . $code;
+ $ansArray = $var['ansArray'];
+ if (is_null($ansArray))
+ {
+ $value = $default;
}
- else {
- $answer = $default;
+ else
+ {
+ if (isset($ansArray[$which_ans])) {
+ $answerInfo = explode('|',$ansArray[$which_ans]);
+ $answer = $answerInfo[0];
+ }
+ else {
+ $answer = $default;
+ }
+ $value = $answer;
}
- $value = $answer;
}
break;
default:
@@ -6286,24 +6866,35 @@ private function _GetVarAttribute($name,$attr,$default,$gseq,$qseq)
case 'H': //ARRAY (Flexible) - Column Format
case 'F': //ARRAY (Flexible) - Row Format
case 'R': //RANKING STYLE
- $scale_id = $this->_GetVarAttribute($name,'scale_id','0',$gseq,$qseq);
- $which_ans = $scale_id . '~' . $code;
- $ansArray = $var['ansArray'];
- if (is_null($ansArray))
+ if ($type == 'O' && preg_match('/comment$/',$name))
+ {
+ $shown = $code;
+ }
+ else if (($type == 'L' || $type == '!') && preg_match('/_other$/',$name))
{
- $shown=$code;
+ $shown = $code;
}
else
{
- if (isset($ansArray[$which_ans])) {
- $answerInfo = explode('|',$ansArray[$which_ans]);
- array_shift($answerInfo);
- $answer = join('|',$answerInfo);
+ $scale_id = $this->_GetVarAttribute($name,'scale_id','0',$gseq,$qseq);
+ $which_ans = $scale_id . '~' . $code;
+ $ansArray = $var['ansArray'];
+ if (is_null($ansArray))
+ {
+ $shown=$code;
}
- else {
- $answer = $code;
+ else
+ {
+ if (isset($ansArray[$which_ans])) {
+ $answerInfo = explode('|',$ansArray[$which_ans]);
+ array_shift($answerInfo);
+ $answer = join('|',$answerInfo);
+ }
+ else {
+ $answer = $code;
+ }
+ $shown = $answer;
}
- $shown = $answer;
}
break;
case 'A': //ARRAY (5 POINT CHOICE) radio-buttons
@@ -6328,10 +6919,13 @@ private function _GetVarAttribute($name,$attr,$default,$gseq,$qseq)
break;
case 'M': //Multiple choice checkbox
case 'P': //Multiple choice with comments checkbox + text
- if ($code == 'Y' && isset($var['question']))
+ if ($code == 'Y' && isset($var['question']) && !preg_match('/comment$/',$sgqa))
{
$shown = $var['question'];
}
+ elseif (preg_match('/comment$/',$sgqa) && isset($_SESSION[$sgqa])) {
+ $shown = $_SESSION[$sgqa];
+ }
else
{
$shown = $default;
View
3  classes/expressions/test/navigation_test.php
@@ -44,6 +44,7 @@
<li><input type='checkbox' name='LEM_DEBUG_VALIDATION_SUMMARY' id='LEM_DEBUG_VALIDATION_SUMMARY' value='Y' checked="checked"/>Validation Summary</li>
<li><input type='checkbox' name='LEM_DEBUG_VALIDATION_DETAIL' id='LEM_DEBUG_VALIDATION_DETAIL' value='Y' checked="checked"/>Validation Detail (Validation Summary must also be checked to see detail)</li>
<li><input type='checkbox' name='LEM_PRETTY_PRINT_ALL_SYNTAX' id='LEM_PRETTY_PRINT_ALL_SYNTAX' value='Y' checked="checked"/>Pretty Print Syntax</li>
+<li><input type='checkbox' name='deletenonvalues' id='deletenonvalues' value='Y' checked="checked"/>Delete Non-Values</li>
</ul></td>
</tr>
<tr><td colspan='2'><input type='submit'/></td></tr>
@@ -71,6 +72,7 @@
((isset($_POST['LEM_DEBUG_VALIDATION_DETAIL']) && $_POST['LEM_DEBUG_VALIDATION_DETAIL'] == 'Y') ? LEM_DEBUG_VALIDATION_DETAIL : 0) +
((isset($_POST['LEM_PRETTY_PRINT_ALL_SYNTAX']) && $_POST['LEM_PRETTY_PRINT_ALL_SYNTAX'] == 'Y') ? LEM_PRETTY_PRINT_ALL_SYNTAX : 0)
);
+ $deletenonvalues = ((isset($_POST['deletenonvalues']) && $_POST['deletenonvalues']=='Y') ? 1 : 0);
$surveyOptions = array(
'active'=>false,
@@ -78,6 +80,7 @@
'anonymized'=>false,
'assessments'=>$assessments,
'datestamp'=>true,
+ 'deletenonvalues'=>$deletenonvalues,
'hyperlinkSyntaxHighlighting'=>true,
'ipaddr'=>true,
'rooturl'=>'../../..',
View
2  classes/expressions/test/relevance.php
@@ -7,7 +7,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ExpressionManager: Unit Test Relevance</title>
<script type="text/javascript" src="../scripts/jquery/jquery.js"></script>
- <script type="text/javascript" src="../classes/expressions/em_javascript.js"></script>
+ <script type="text/javascript" src="../scripts/em_javascript.js"></script>
<script type="text/javascript" src="../scripts/survey_runtime.js"></script>
</head>
<body id="limesurvey">
View
2  classes/expressions/test/unit.php
@@ -18,7 +18,7 @@
<title>ExpressionManager: Unit Test Core Evaluator</title>
</head>
<body onload="recompute()">
- <script type="text/javascript" src="../classes/expressions/em_javascript.js"></script>
+ <script type="text/javascript" src="../scripts/em_javascript.js"></script>
<?php
// include_once('../ExpressionManager.php');
ExpressionManager::UnitTestEvaluator();
View
40 common_functions.php
@@ -6357,7 +6357,7 @@ function getQuotaInformation($surveyid,$language,$quotaid='all')
'Limit' => $survey_quotas['qlimit'],
'Action' => $survey_quotas['action'],
'Message' => $survey_quotas['quotals_message'],
- 'Url' => passthruReplace(insertansReplace($survey_quotas['quotals_url']), $surveyinfo),
+ 'Url' => templatereplace(passthruReplace($survey_quotas['quotals_url'], $surveyinfo)),
'UrlDescrip' => $survey_quotas['quotals_urldescrip'],
'AutoloadUrl' => $survey_quotas['autoload_url']));
$query = "SELECT * FROM ".db_table_name('quota_members')." WHERE quota_id='{$survey_quotas['id']}'";
@@ -6514,7 +6514,7 @@ function filterforattributes ($fieldname)
* @param mixed $surveyid The survey ID
* @return array The fieldnames
*/
-function GetAttributeFieldNames($surveyid)
+function GetAttributeFieldNames($surveyid,$filter=true)
{
global $dbprefix, $connect;
if (tableExists('tokens_'.$surveyid) === false)
@@ -6522,7 +6522,11 @@ function GetAttributeFieldNames($surveyid)
return Array();
}
$tokenfieldnames = array_values($connect->MetaColumnNames("{$dbprefix}tokens_$surveyid", true));
- return array_filter($tokenfieldnames,'filterforattributes');
+ if ($filter)
+ {
+ return array_filter($tokenfieldnames,'filterforattributes');
+ }
+ return $tokenfieldnames;
}
/**
@@ -7243,7 +7247,7 @@ function fixSubquestions()
/**
* Need custom version of JSON encode to avoid having Expression Manager mangle it
* @param type $val
- * @return type
+ * @return type
*/
function ls_json_encode($val)
{
@@ -7252,4 +7256,32 @@ function ls_json_encode($val)
return $ans;
}
+/**
+* This function returns the real IP address under all configurations
+*
+*/
+function getIPAddress()
+{
+ global $bServerBehindProxy;
+ if ($bServerBehindProxy)
+ {
+ if (!empty($_SERVER['HTTP_CLIENT_IP'])) //check ip from share internet
+ {
+ return $_SERVER['HTTP_CLIENT_IP'];
+ }
+ elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) //to check ip is pass from proxy
+ {
+ return $_SERVER['HTTP_X_FORWARDED_FOR'];
+ }
+ }
+ if (!empty($_SERVER['REMOTE_ADDR']))
+ {
+ return $_SERVER['REMOTE_ADDR'];
+ }
+ else
+ {
+ return '127.0.0.1';
+ }
+}
+
// Closing PHP tag intentionally omitted - yes, it is okay
View
9 config-defaults.php
@@ -573,6 +573,15 @@
*/
$iFileUploadTotalSpaceMB=0;
+/**
+* Set this variable to true to indicate that your server is behind a proxy.
+* This will ensure the correct captioning of IP addresses instead of just the proxy address
+*
+* @var $bServerBehindProxy Default: false
+*
+*/
+$bServerBehindProxy=false;
+
//DO NOT EVER CHANGE THE FOLLOWING 5 LINES ---------------
require_once(dirname(__FILE__).'/config.php');
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']!='' && $_SERVER['HTTPS']!='off')
View
52 docs/release_notes.txt
@@ -78,6 +78,58 @@ HOW TO UPGRADE from a LimeSurvey 1.70(+) version to the latest 1.9x
Thank you to everyone who helped with this new release!
+Changes from 1.92+ (build 120418) to 1.92+ (build 120425) April 25, 2012
+-Fixed issue #5988: Problem displaying words with French accent within Expression Manager (Carsten Schmitz)
+-Fixed issue #6027: Users who have token creation permission can't create token table (Carsten Schmitz)
+-Fixed issue #6035: Insertans not working in multiple options with comments (Carsten Schmitz)
+-Fixed issue #6036: Reminder sent to people which opted out (Carsten Schmitz)
+-Fixed issue #6037: Zero uses left by default when importing tokens from CSV (Carsten Schmitz)
+-Fixed issue #6042: Check for existing token taables after activation -> switch to closed access (Carsten Schmitz)
+-Fixed issue #6043: Errors creating surveys in MSSQL (Carsten Schmitz)
+#Updated translation: Czech (lukas-slansky)
+#Updated translation: Danish - kindly provided by Søren O'Neill (Carsten Schmitz)
+#Updated translation: Finnish kindly provided by Flo Apps Oy (Carsten Schmitz)
+#Updated translation: German (Carsten Schmitz)
+#Updated translation: Italian - kindly provided by Alberto Danese (Carsten Schmitz)
+#Updated translation: Spanish (Mexico) (Carsten Schmitz)
+
+
+Changes from 1.92+ (build 120412) to 1.92+ (build 120418) April 18, 2012
+-Fixed issue #5896: Miscellaneous, rarely used, qcode.* attributes showing blank values (Thomas White)
+-Fixed issue #6006: Error in LImeExpressionManage.php anytime using an array (numbers) with checkboxes and Mandatory (Thomas White)
+-Fixed issue #6007: Token variables resolving as 'undefined variable' after Version 1.91+ Build 11026 (Thomas White)
+-Fixed issue #6013: passthru stopped working (Thomas White)
+-Fixed issue #6015: INSERTANS does not work in end message (Thomas White)
+-Fixed issue #6024: $deletenonvalues option no longer working in 1.92+ (Thomas White)
+-Fixed issue #5986: Import label set does not properly show an error for invalid files (Carsten Schmitz)
+-Fixed issue #6016: Load Unfinished Survey button does not display for existing survey (Carsten Schmitz)
+-Fixed issue #6018: Using mssqlnative the "date" question type uses DATETIME type and not DATE (Carsten Schmitz)
+-Fixed issue: Notice when using mssqlnative driver (Carsten Schmitz)
+-Fixed issue: Only set EM variables for {TOKEN:xxxx} when token table exists; and only for declared attributes (Thomas White)
+-Fixed issue: Participant IP address not correctly captured if server is behind proxy (Carsten Schmitz)
+-Fixed issue: Warning if upload file and there is no minimum number of files specified (Thomas White)
+#Updated translation: Czech kindly provided by Slansky Lukas
+#Updated translation: French kindly provided by Guillaume Jaouen
+#Updated translation: German
+#Updated translation: German (informal) (Carsten Schmitz)
+
+
+Changes from 1.92+ (build 120405) to 1.92+ (build 120412) April 12, 2012
+-Fixed issue #5962: View Response Details does not work correctly if responses are filtered (Statistics) (Thomas White)
+-Fixed issue #5985: Notifications using INSERTANS and/or QCODE don't work with all-in-one presentation of survey (Thomas White)
+-Fixed issue #5992: After upgrade to Version 1.92+ Build 120405 the uploaded file information gets corrupted. (Thomas White)
+-Fixed issue #6002: @@SURVEYURL@@ placeholder fails to translate (Thomas White)
+-Fixed issue #5976: Iterate survey sets all "uses_left" to "1" even if there has been set a larger number previously (Carsten Schmitz)
+-Fixed issue #5997 : upload windows are on default survey langage (Denis Chenu)
+-Fixed issue: Bad HTML in editgroup(Denis Chenu)
+-Fixed issue: Mssqlnative driver gives back weird date format in export (Carsten Schmitz)
+-Fixed issue: With certain Apache configurations then em_javascript.js file was not readable (Carsten Schmitz)
+#Updated translation: Danish - kindly provided by Søren O'Neill (Carsten Schmitz)
+#Updated translation: Galician - kindly provided by Carlos Neira Cortizas (Carsten Schmitz)
+#Updated translation: German Updated translation: German (informal) (Carsten Schmitz)
+#Updated translation: Greek by Yannis Kaskamanidis (Carsten Schmitz)
+#Updated translation: Spanish (Mexico) (Carsten Schmitz)
+
Changes from 1.92+ (build 120330) to 1.92+ (build 120405) April 5, 2012
-Fixed issue #5877: Resume later doesn't work with a survey allowing public registration (Carsten Schmitz)
View
18 group.php
@@ -35,12 +35,15 @@
$radix=getRadixPointData($thissurvey['surveyls_numberformat']);
$radix = $radix['seperator'];
+global $deletenonvalues; // set in config-defaults.php
+
$surveyOptions = array(
'active'=>($thissurvey['active']=='Y'),
'allowsave'=>($thissurvey['allowsave']=='Y'),
'anonymized'=>($thissurvey['anonymized']!='N'),
'assessments'=>($thissurvey['assessments']=='Y'),
'datestamp'=>($thissurvey['datestamp']=='Y'),
+'deletenonvalues'=>(isset($deletenonvalues)? $deletenonvalues: 1),
'hyperlinkSyntaxHighlighting'=>(($LEMdebugLevel & LEM_DEBUG_VALIDATION_SUMMARY) == LEM_DEBUG_VALIDATION_SUMMARY), // TODO set this to true if in admin mode but not if running a survey
'ipaddr'=>($thissurvey['ipaddr']=='Y'),
'radix'=>$radix,
@@ -318,10 +321,6 @@
{
$assessments = doAssessment($surveyid);
}
- if($thissurvey['printanswers'] != 'Y')
- {
- killSession();
- }
sendcacheheaders();
doHeader();