Commits (35)
......@@ -11,10 +11,11 @@
* See COPYRIGHT.php for copyright notices and details.
*/
$config['versionnumber'] = '3.27.20-Tika-1.0.2';
$config['versionnumber'] = '3.27.26-Tika-1.0.2';
$config['dbversionnumber'] = 365;
$config['buildnumber'] = '';
$config['updatable'] = false;
$config['templateapiversion'] = 3;
$config['assetsversionnumber'] = '30212';
$config['assetsversionnumber'] = '30218';
return $config;
......@@ -957,10 +957,10 @@ class database extends Survey_Common_Action
$data['surveyls_policy_notice_label'] = $dataseclabel;
}
if ($sURL !== null) {
$data['surveyls_url'] = htmlspecialchars($sURL);
$data['surveyls_url'] = $sURL;
}
if ($sURLDescription !== null) {
$data['surveyls_urldescription'] = htmlspecialchars($sURLDescription);
$data['surveyls_urldescription'] = $sURLDescription;
}
if ($dateformat !== null) {
$data['surveyls_dateformat'] = $dateformat;
......
......@@ -59,10 +59,13 @@ class SurveyAdmin extends Survey_Common_Action
$aSurveys = json_decode(Yii::app()->request->getPost('sItems'));
$aResults = array();
foreach ($aSurveys as $iSurveyID) {
$oSurvey = Survey::model()->findByPk($iSurveyID);
$aResults[$iSurveyID]['title'] = $oSurvey->correct_relation_defaultlanguage->surveyls_title;
if (Permission::model()->hasSurveyPermission($iSurveyID, 'survey', 'delete')) {
$oSurvey = Survey::model()->findByPk($iSurveyID);
$aResults[$iSurveyID]['title'] = $oSurvey->correct_relation_defaultlanguage->surveyls_title;
$aResults[$iSurveyID]['result'] = Survey::model()->deleteSurvey($iSurveyID);
} else {
$aResults[$iSurveyID]['result'] = false;
$aResults[$iSurveyID]['error'] = gT("User does not have valid permissions");
}
}
......@@ -75,6 +78,37 @@ class SurveyAdmin extends Survey_Common_Action
);
}
/**
* Render selected items for massive action
* @return void
*/
public function renderItemsSelected()
{
$surveyIds = json_decode(Yii::app()->request->getPost('$oCheckedItems'), true);
if(!is_array($surveyIds)) {
throw new CHttpException(400, gT('Invalid list of checked items'));
}
$results = [];
$tableLabels = [gT('Survey ID'), gT('Survey Title'), gT('Status')];
foreach ($surveyIds as $surveyId) {
if (Permission::model()->hasSurveyPermission($surveyId, 'survey', 'delete')) {
$survey = Survey::model()->findByPk($surveyId);
$results[$surveyId]['title'] = $survey->correct_relation_defaultlanguage->surveyls_title;
$results[$surveyId]['result'] = 'selected';
}
}
Yii::app()->getController()->renderPartial(
'ext.admin.survey.ListSurveysWidget.views.massive_actions._selected_survey',
[
'aResults' => $results,
'successLabel' => gT('Seleted'),
'tableLabels' => $tableLabels
]
);
}
/**
* @todo
*/
......@@ -366,7 +400,12 @@ class SurveyAdmin extends Survey_Common_Action
$oSurvey = Survey::model()->findByPk($iSurveyID);
$oSurvey->gsid = $iSurveyGroupId;
$aResults[$iSurveyID]['title'] = $oSurvey->correct_relation_defaultlanguage->surveyls_title;
$aResults[$iSurveyID]['result']= $oSurvey->save();
if ($oSurvey->save()) {
$aResults[$iSurveyID]['result'] = true;
} else {
$aResults[$iSurveyID]['result'] = false;
$aResults[$iSurveyID]['error'] = gT("Survey update failed");
}
}
Yii::app()->getController()->renderPartial('ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results', array('aResults'=>$aResults,'successLabel'=>gT("Success")));
......@@ -396,6 +435,7 @@ class SurveyAdmin extends Survey_Common_Action
if (!empty($bReturn)){
$aResults[$iSurveyID]['title'] = $survey->correct_relation_defaultlanguage->surveyls_title;
$aResults[$iSurveyID]['result'] = false;
$aResults[$iSurveyID]['error'] = gT("User does not have valid permissions");
return $aResults;
}else{
die('No permission');
......@@ -1995,12 +2035,19 @@ class SurveyAdmin extends Survey_Common_Action
foreach ($aSIDs as $sid) {
$survey = Survey::model()->findByPk($sid);
$survey->expires = $expires;
$aResults[$survey->primaryKey]['title'] = ellipsize($survey->correct_relation_defaultlanguage->surveyls_title, 30);
// We check the permission after retrieving the title because we need it anyway.
if (!Permission::model()->hasSurveyPermission($sid, 'surveysettings', 'update')) {
$aResults[$survey->primaryKey]['result'] = false;
$aResults[$survey->primaryKey]['error'] = gT("User does not have valid permissions");
continue;
}
$survey->expires = $expires;
if ($survey->save()) {
$aResults[$survey->primaryKey]['result'] = true;
} else {
$aResults[$survey->primaryKey]['result'] = false;
$aResults[$survey->primaryKey]['error'] = gT("Survey update failed");
}
}
Yii::app()->getController()->renderPartial('ext.admin.survey.ListSurveysWidget.views.massive_actions._action_results', array('aResults'=>$aResults, 'successLabel'=>gT('OK')));
......
......@@ -35,8 +35,13 @@ class index extends CAction
$surveyid = $param['sid'];
$thisstep = $param['thisstep'];
$move = getMove();
$clienttoken = trim($param['token']);
/* Get client token by POST or GET value */
$clienttoken = trim($param['token']);
/* If not set : get by SESSION to avoid multiple submit of same token in different navigator */
if(empty($clienttoken) && !empty($_SESSION['survey_' . $surveyid]['token'] )) {
$clienttoken = $_SESSION['survey_' . $surveyid]['token'];
}
$oSurvey = Survey::model()->findByPk($surveyid);
if(empty($oSurvey)) {
......
......@@ -58,7 +58,7 @@ class LSYii_Validators extends CValidator
if ($this->xssfilter) {
$object->$attribute = $this->xssFilter($object->$attribute);
if ($this->isUrl) {
$object->$attribute = str_replace('javascript:', '', html_entity_decode($object->$attribute, ENT_QUOTES, "UTF-8"));
$object->$attribute = str_replace('javascript:', '', $object->$attribute);
}
}
// Note that URL checking only checks basic URL properties. As a URL can contain EM expression there needs to be a lot of freedom.
......
......@@ -23,9 +23,11 @@ var onClickListAction = function () {
var $actionUrl = $that.data('url'); // The url of the Survey Controller action to call
var onSuccess = $that.data('on-success');
var $gridid = $('#'+$(this).closest('div.listActions').data('grid-id'));
var grididvalue = $gridid.attr('id');
var $oCheckedItems = $gridid.yiiGridView('getChecked', $(this).closest('div.listActions').data('pk')); // List of the clicked checkbox
var $oCheckedItems = JSON.stringify($oCheckedItems);
var actionType = $that.data('actionType');
var selectedList = $(".selected-items-list");
if( $oCheckedItems == '[]' ) {
//If no item selected, the error modal "please select first an item" is shown
......@@ -106,6 +108,31 @@ var onClickListAction = function () {
var $oldModalTitle = $modalTitle.text();
var $oldModalBody = $modalBody.html();
var $oldModalButtons = $modal.find('.modal-footer-buttons'); // Modal footer with yes/no buttons
var modalShowSelected = $modal.data('show-selected');
var modalSelectedUrl = $modal.data('selected-url');
//Display selected data in modals after clicked on action
if (modalShowSelected == 'yes' && modalSelectedUrl) {
//set csrfToken for ajaxpost
var csrfToken = $('meta[name="csrf-token"]').attr("content");
//clear selected list view
selectedList.empty();
//ajaxpost to set data in the selected items div
$.ajax({
url :modalSelectedUrl,
type : 'POST',
data : {grididvalue, $oCheckedItems, csrfToken},
success: function(html, status) {
selectedList.html(html);
},
error: function(requestObject, error, errorThrown) {
console.log(error);
}
});
}
// When user close the modal, we put it back to its original state
$modal.on('hidden.bs.modal', function (e) {
......@@ -152,6 +179,7 @@ var onClickListAction = function () {
$oldModalButtons.hide(); // Hide the 'Yes/No' buttons
$modalClose.show(); // Show the 'close' button
$ajaxLoader.show(); // Show the ajax loader
selectedList.empty(); //clear selected Item list
// Ajax request
$.ajax({
......
<!-- Modal confirmation for <?php echo $aAction['action'];?> -->
<div id="massive-actions-modal-<?php echo $aAction['action'];?>-<?php echo $key; ?>" class="modal fade" role="dialog" data-keepopen="<?php echo $aAction['keepopen'];?>">
<div id="massive-actions-modal-<?php echo $aAction['action'];?>-<?php echo $key; ?>"
class="modal fade"
role="dialog"
data-keepopen="<?php echo $aAction['keepopen'];?>"
data-show-selected="<?php if(isset($aAction['showSelected'])){ echo $aAction['showSelected']; }?>"
data-selected-url="<?php if(isset($aAction['selectedUrl'])){ echo $aAction['selectedUrl']; }?>"
>
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
......@@ -10,6 +16,9 @@
<div class="modal-body">
<div class='modal-body-text'><?php echo $aAction['htmlModalBody']; ?></div>
<!-- shows list of selected items in the modal-->
<div class="selected-items-list"></div>
<?php if (isset($aAction['aCustomDatas'])):?>
<!--
Custom datas needed for action defined directly in the widget call.
......
......@@ -30,7 +30,7 @@
</td>
<?php else: ?>
<td class="text-warning">
<?php eT('Error'); ?>
<?= empty($result['error']) ? gT('Error') : sprintf(gT("Unable to perform function - %s"), $result['error']) ?>
</td>
<?php endif;?>
</tr>
......
<?php
/**
* This view display the result of selected surveys. It's rendered via ajax for the confirmation modal in survey list
*
* @var $aResults The array containing the result of each survey selection
*/
?>
<hr>
<table class="table table-striped">
<thead>
<th><?php eT('Survey ID');?></th>
<th><?php eT('Title');?></th>
<th><?php eT('Status');?></th>
</thead>
<tbody>
<?php foreach($aResults as $iSid => $result):?>
<tr>
<td>
<?php echo $iSid;?>
</td>
<td>
<?php echo $result['title'];?>
</td>
<?php if ($result['result']):?>
<td class="text-success">
<?php eT('Selected'); ?>
</td>
<?php else: ?>
<td class="text-warning">
<?php echo $result['error'] ; ?>
</td>
<?php endif;?>
</tr>
<?php endforeach;?>
</tbody>
</table>
\ No newline at end of file
......@@ -29,6 +29,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Delete surveys'),
'htmlModalBody' => gT('Are you sure you want to delete all those surveys?'),
),
......@@ -53,6 +55,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Set survey theme'),
'htmlModalBody' => $this->controller->renderFile(__DIR__.'/_select_survey_theme.php',array(),true),
),
......@@ -70,6 +74,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Change survey group'),
'htmlModalBody' => $this->controller->renderFile(__DIR__.'/_change_survey_group.php',array(),true),
),
......@@ -102,6 +108,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Export survey archive'),
'htmlModalBody' => gT('This will export the survey archive (.lsa) for all selected active surveys. They will be provided in a single ZIP archive.').' '.gT('Continue?'),
),
......@@ -120,6 +128,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Export survey structure'),
'htmlModalBody' => gT('This will export the survey structure (.lss) for all selected active surveys. They will be provided in a single ZIP archive.').' '.gT('Continue?'),
......@@ -136,6 +146,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Export printable survey'),
'htmlModalBody' => gT('This will export a printable version of your survey.').' '.gT('Continue?'),
),
......@@ -152,6 +164,8 @@
'actionType' => 'modal',
'modalType' => 'yes-no',
'keepopen' => 'yes',
'showSelected' => 'yes',
'selectedUrl' => App()->createUrl('/admin/survey/sa/renderItemsSelected/'),
'sModalTitle' => gT('Set expiry date'),
'htmlModalBody' => $this->controller->renderFile(__DIR__.'/_expiry_dialog.php',array(),true),
),
......
......@@ -444,7 +444,8 @@ class SurveyRuntimeHelper
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('start_time', time(), array('id'=>'start_time'));
$_SESSION[$this->LEMsessid]['LEMpostKey'] = mt_rand();
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('LEMpostKey', $_SESSION[$this->LEMsessid]['LEMpostKey'], array('id'=>'LEMpostKey'));
if (!empty($_SESSION[$this->LEMsessid]['token'])) {
/* Reset session with multiple tabs (show Token mismatch issue) , but only for not anonymous survey */
if (!empty($_SESSION[$this->LEMsessid]['token']) and $this->aSurveyInfo['anonymized'] != 'Y') {
$this->aSurveyInfo['hiddenInputs'] .= \CHtml::hiddenField('token', $_SESSION[$this->LEMsessid]['token'], array('id'=>'token'));
}
}
......
......@@ -34,12 +34,14 @@ function XMLImportGroup($sFullFilePath, $iNewSID)
$iDBVersion = (int) $xml->DBVersion;
$aQIDReplacements = array();
$aQuestionCodeReplacements = [];
$results['defaultvalues'] = 0;
$results['answers'] = 0;
$results['question_attributes'] = 0;
$results['subquestions'] = 0;
$results['conditions'] = 0;
$results['groups'] = 0;
$results['importwarnings'] = [];
if ($iDBVersion>=400) {
$results['fatalerror'] = gT("The file is not compatible with this LimeSurvey version.");
......@@ -142,13 +144,58 @@ function XMLImportGroup($sFullFilePath, $iNewSID)
switchMSSQLIdentityInsert('questions', true);
}
Yii::app()->db->createCommand()->insert('{{questions}}', $insertdata);
$oQuestion = new Question('import');
$oQuestion->setAttributes($insertdata, false);
// Try to fix question title for valid question code enforcement
if (!$oQuestion->validate(['title'])) {
$sOldTitle = $oQuestion->title;
$sNewTitle = preg_replace("/[^A-Za-z0-9]/", '', $sOldTitle);
if (is_numeric(substr($sNewTitle, 0, 1))) {
$sNewTitle = 'q'.$sNewTitle;
}
$oQuestion->title = $sNewTitle;
}
$attempts = 0;
// Try to fix question title for unique question code enforcement
$index = 0;
$rand = mt_rand(0, 1024);
while (!$oQuestion->validate(['title'])) {
$sNewTitle = 'r'.$rand.'q'.$index;
$index++;
$oQuestion->title = $sNewTitle;
$attempts++;
if ($attempts > 10) {
safeDie(gT("Error").": Failed to resolve question code problems after 10 attempts.<br />");
}
}
if (!$oQuestion->save()) {
// safeDie(gT("Error while saving: "). print_r($oQuestion->errors, true));
//
// In PHP 5.2.10 a bug is triggered that resets the foreach loop when inserting a record
// For this reason we ignore insertion errors (because it is most likely a duplicate)
// and continue with the next one
continue;
}
// Set a warning if question title was updated
if (isset($sNewTitle) && isset($sOldTitle)) {
$results['importwarnings'][] = sprintf(gT("Question code %s was updated to %s."), $sOldTitle, $sNewTitle);
$aQuestionCodeReplacements[$sOldTitle] = $sNewTitle;
unset($sNewTitle);
unset($sOldTitle);
}
if (isset($insertdata['qid'])) {
switchMSSQLIdentityInsert('questions', false);
}
$newqid = $oQuestion->qid;
if (!isset($aQIDReplacements[$oldqid])) {
$newqid = getLastInsertID('{{questions}}');
$aQIDReplacements[$oldqid] = $newqid; // add old and new qid to the mapping array
$aQIDReplacements[$oldqid] = $newqid;
$results['questions']++;
}
}
......@@ -186,14 +233,56 @@ function XMLImportGroup($sFullFilePath, $iNewSID)
switchMSSQLIdentityInsert('questions', true);
}
Yii::app()->db->createCommand()->insert('{{questions}}', $insertdata);
$newsqid = getLastInsertID('{{questions}}');
if (isset($insertdata['qid'])) {
switchMSSQLIdentityInsert('questions', false);
$oQuestion = new Question('import');
$oQuestion->setAttributes($insertdata, false);
// Try to fix question title for valid question code enforcement
if (!$oQuestion->validate(['title'])) {
$sOldTitle = $oQuestion->title;
$sNewTitle = preg_replace("/[^A-Za-z0-9]/", '', $sOldTitle);
if (is_numeric(substr($sNewTitle, 0, 1))) {
$sNewTitle = 'sq'.$sNewTitle;
}
$oQuestion->title = $sNewTitle;
}
$attempts = 0;
// Try to fix question title for unique question code enforcement
$index = 0;
$rand = mt_rand(0, 1024);
while (!$oQuestion->validate(['title'])) {
$sNewTitle = 'r'.$rand.'sq'.$index;
$index++;
$oQuestion->title = $sNewTitle;
$attempts++;
if ($attempts > 10) {
safeDie(gT("Error").": Failed to resolve question code problems after 10 attempts.<br />");
}
}
if (!$oQuestion->save()) {
// safeDie(gT("Error while saving: "). print_r($oQuestion->errors, true));
//
// In PHP 5.2.10 a bug is triggered that resets the foreach loop when inserting a record
// For this reason we ignore insertion errors (because it is most likely a duplicate)
// and continue with the next one
continue;
}
// Set a warning if question title was updated
if (isset($sNewTitle) && isset($sOldTitle)) {
$results['importwarnings'][] = sprintf(gT("Title of subquestion %s was updated to %s."), $sOldTitle, $sNewTitle); // Maybe add the question title ?
$aQuestionCodeReplacements[$sOldTitle] = $sNewTitle;
unset($sNewTitle);
unset($sOldTitle);
}
$newsqid = $oQuestion->qid;
if (!isset($insertdata['qid'])) {
$aQIDReplacements[$oldsqid] = $newsqid; // add old and new qid to the mapping array
} else {
switchMSSQLIdentityInsert('questions', false);
}
$results['subquestions']++;
......@@ -328,6 +417,9 @@ function XMLImportGroup($sFullFilePath, $iNewSID)
$results['conditions']++;
}
}
replaceExpressionCodes($iNewSID, $aQuestionCodeReplacements);
LimeExpressionManager::RevertUpgradeConditionsToRelevance($iNewSID);
LimeExpressionManager::UpgradeConditionsToRelevance($iNewSID);
......@@ -1905,11 +1997,16 @@ function CSVImportResponses($sFullFilePath, $iSurveyId, $aOptions = array())
$iIdKey = array_search('id', $aCsvHeader); // the id is allways needed and used a lot
if (is_int($iIdKey)) {
unset($aKeyForFieldNames['id']);
/* Unset it if option is ignore */
if($aOptions['sExistingId'] == 'ignore') {
$iIdKey = false;
}
}
$iSubmitdateKey = array_search('submitdate', $aCsvHeader); // submitdate can be forced to null
if (is_int($iSubmitdateKey)) {
unset($aKeyForFieldNames['submitdate']);
}
$iIdReponsesKey = (is_int($iIdKey)) ? $iIdKey : 0; // The key for reponses id: id column or first column if not exist
// Import each responses line here
......@@ -1931,15 +2028,14 @@ function CSVImportResponses($sFullFilePath, $iSurveyId, $aOptions = array())
break;
case 'replaceanswers':
break;
case 'skip':
$oSurvey = false; // Remove existing survey : don't import again
break;
case 'renumber':
default: // Must not happen, keep it in case
SurveyDynamic::sid($iSurveyId);
$oSurvey = new SurveyDynamic;
break;
case 'skip':
case 'ignore':
default:
$oSurvey = false; // Remove existing survey : don't import again
break;
}
} else {
SurveyDynamic::sid($iSurveyId);
......
......@@ -401,11 +401,11 @@ function SPSSGetValues($field, $qidattributes, $language)
if ($oQuestion->other == 'Y') {
$spsstype = 'A';
$size = 6;
$answers['needsAlterType'] = true;
}
}
$answers['SPSStype'] = $spsstype;
$answers['size'] = $size;
$answers['needsAlterType'] = true;
return $answers;
} else {
/* Not managed (currently): url, IP, … */
......
......@@ -53,6 +53,7 @@ class ExpressionManager
private $RDP_result; // final result of evaluating the expression;
private $RDP_evalStatus; // true if $RDP_result is a valid result, and there are no serious errors
private $varsUsed; // list of variables referenced in the equation
public $resetErrorsAndWarningsOnEachPart = true;
// These variables are only used by sProcessStringContainingExpressions
private $allVarsUsed; // full list of variables used within the string, even if contains multiple expressions
......@@ -1847,7 +1848,7 @@ class ExpressionManager
} else {
++$this->substitutionNum;
$expr = $this->ExpandThisVar(substr($stringPart[0], 1, -1));
if ($this->RDP_Evaluate($expr, false, false)) { // We call RDP_Evaluate with $resetErrorsAndWarnings = false because, if $src has more than one expression, error information could be lost
if ($this->RDP_Evaluate($expr, false, $this->resetErrorsAndWarningsOnEachPart)) {
$resolvedPart = $this->GetResult();
} else {
// show original and errors in-line only if user have the rigth to update survey content
......
......@@ -7869,24 +7869,27 @@
$qrelgseqs[] = 'relChangeG' . $knownVar['gseq'];
}
}
/* If group of current question relevance updated: must check too. See mantis #14955 */
$qrelgseqs[] = 'relChangeG' . $arg['gseq'];
$qrelgseqs = array_unique($qrelgseqs);
$qrelQIDs = array_unique($qrelQIDs);
$aQuestionsWithDependencies = array_unique($aQuestionsWithDependencies);
if ($LEM->surveyMode=='question') {
$qrelQIDs=array(); // in question-by-questin mode, should never test for dependencies on self or other questions.
}
if ($LEM->surveyMode!='survey') {
$qrelgseqs=array(); // in question-by-questin mode, should never test for dependencies on self or other questions.
$qrelgseqs=array(); // javascript dependencies on groups only for survey mode
}
$qrelJS = "function LEMrel" . $arg['qid'] . "(sgqa){\n";
$qrelJS .= " var UsesVars = ' " . implode(' ', $relJsVarsUsed) . " ';\n";
//Normally trigger reevaluation only for relevant questions except for equation questions
if($arg['type'] != '*') {
/* If current relevance are updated in a previous function : must appy this one */
/* If current relevance are updated in a previous function : must apply this one */
/* See issue #14465 . Warning : expression manager trigger this by order of question */
if (count($qrelQIDs) > 0) {
$qrelJS .= " if(" . implode(' || ', $qrelQIDs) . "){\n ;\n }\n else";
}
if (count($qrelgseqs) > 0) {
if (count($qrelgseqs) > 0) { // If some of related group are updated : must check relevance (continue)
$qrelJS .= " if(" . implode(' || ', $qrelgseqs) . "){\n ;\n }\n else";
}
$qrelJS .= " if (typeof sgqa !== 'undefined' && !LEMregexMatch('/ java' + sgqa + ' /', UsesVars)) {\n";
......@@ -9485,6 +9488,8 @@ report~numKids > 0~message~{name}, you said you are {age} and that you have {num
 
$LEM =& LimeExpressionManager::singleton();
$LEM->sPreviewMode='logic';
// We set $LEM->em->resetErrorsAndWarningsOnEachPart = false because, if a string has more than one expression, error information could be lost
$LEM->em->resetErrorsAndWarningsOnEachPart = false;
$aSurveyInfo=getSurveyInfo($sid,$_SESSION['LEMlang']);
$aAttributesDefinitions=\LimeSurvey\Helpers\questionHelper::getAttributesDefinitions();
/* All final survey string must be shown in survey language #12208 */
......
......@@ -428,7 +428,9 @@ function submittokens($quotaexit = false)
}
$token->usesleft--;
}
$token->save();
// We need to specify the attributes we are updating because validation could fail on other (untouched) attributes,
// preventing the update (eg.: the participant could have an invalid email).
$token->save(true, ['completed', 'usesleft']);
if ($quotaexit == false) {
if ($token && trim(strip_tags($thissurvey['email_confirm'])) != "" && $thissurvey['sendconfirmation'] == "Y") {
......@@ -2041,7 +2043,7 @@ function display_first_page($thissurvey, $aSurveyInfo)
$thissurvey['EM']['ScriptsAndHiddenInputs'] .= \CHtml::hiddenField('lastgroupname', '_WELCOME_SCREEN_', array('id'=>'lastgroupname')); //This is to ensure consistency with mandatory checks, and new group test
$thissurvey['EM']['ScriptsAndHiddenInputs'] .= \CHtml::hiddenField('LEMpostKey', $_SESSION['survey_'.$surveyid]['LEMpostKey'], array('id'=>'LEMpostKey'));
$thissurvey['EM']['ScriptsAndHiddenInputs'] .= \CHtml::hiddenField('thisstep', 0, array('id'=>'thisstep'));
if (!empty($_SESSION['survey_'.$surveyid]['token'])) {
if (!empty($_SESSION['survey_'.$surveyid]['token']) && $thissurvey['anonymized'] != "Y") {
$thissurvey['EM']['ScriptsAndHiddenInputs'] .= \CHtml::hiddenField('token', $_SESSION['survey_'.$surveyid]['token'], array('id'=>'token'));
}
......
......@@ -11,7 +11,7 @@ namespace ls\mersenne;
*/
function setSeed($surveyid)
{
//traceVar(@$_SESSION['survey_' . $surveyid]['srid']);
/* In started survey : get seed from response table */
if (isset($_SESSION['survey_'.$surveyid]['srid'])) {
$oResponse = \Response::model($surveyid)->findByPk($_SESSION['survey_'.$surveyid]['srid']);
$seed = $oResponse->seed;
......@@ -23,13 +23,12 @@ function setSeed($surveyid)
}
} else {
$seed = mt_rand();
// Only set seed if corresponding database column exists.
// This mismatch can happen if survey is activated before update to
// new version that uses seed.
$table = \Yii::app()->db->schema->getTable('{{survey_'.$surveyid.'}}');
if (isset($table->columns['seed'])) {
$_SESSION['survey_'.$surveyid]['startingValues']['seed'] = $seed;
/* On activated (but not started) survey : set seed in startingValues */
if (\Survey::model()->findByPk($surveyid)->getIsActive()) {
$table = \Yii::app()->db->schema->getTable('{{survey_'.$surveyid.'}}');
if (isset($table->columns['seed'])) {
$_SESSION['survey_'.$surveyid]['startingValues']['seed'] = $seed;
}
}
}
MersenneTwister::init($seed);
......
......@@ -19,8 +19,6 @@
class LSActiveRecord extends CActiveRecord
{
/** @var string[] Array of attributes that should be XSS filtered on mass updates */
protected $xssFilterAttributes = [];
/**
* Lists the behaviors of this model
......@@ -211,29 +209,4 @@ class LSActiveRecord extends CActiveRecord
return parent::deleteAllByAttributes(array(), $criteria, array());
}
/**
* Updates records with the specified condition.
* XSS filtering is enforced for attributes listed in model's $xssFilterAttributes property.
* See {@link find()} for detailed explanation about $condition and $params.
* Note, the attributes are not checked for safety and no validation is done.
* @param array $attributes list of attributes (name=>$value) to be updated
* @param mixed $condition query condition or criteria.
* @param array $params parameters to be bound to an SQL statement.
* @return integer the number of rows being updated
*/
public function updateAll($attributes, $condition = '', $params = array())
{
if (!empty($this->xssFilterAttributes)) {
$validator = new LSYii_Validators;
if ($validator->xssfilter) {
$attributeNames = array_keys($attributes);
$attributesToFilter = array_intersect($attributeNames, $this->xssFilterAttributes);
foreach ($attributesToFilter as $attribute) {
$attributes[$attribute] = $validator->xssFilter($attributes[$attribute]);
}
}
}
return parent::updateAll($attributes, $condition, $params);
}
}
......@@ -1083,7 +1083,9 @@ class Participant extends LSActiveRecord
if (!empty($tokenAndId['participant_id']) && $surveyTableExists) {
/** @var Token $token */
$token = Token::model($aSurveyID)->find('participant_id = :pid', [':pid' => $tokenAndId['participant_id']]);
$token->delete();
if (!is_null($token)) {
$token->delete();
}
}
$iDeletedParticipants = $this->deleteParticipants($sParticipantsIDs, false);
}
......
......@@ -30,8 +30,6 @@
*/
class QuestionAttribute extends LSActiveRecord
{
protected $xssFilterAttributes = ['value'];
/**
* @inheritdoc
* @return QuestionAttribute
......