Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Trischi80 committed Feb 3, 2022
2 parents c5b5d47 + 868a032 commit cc2b85c
Show file tree
Hide file tree
Showing 15 changed files with 77 additions and 56 deletions.
8 changes: 5 additions & 3 deletions application/controllers/ResponsesController.php
Expand Up @@ -629,11 +629,13 @@ public function actionDownloadfile(int $surveyId, int $responseId, int $qid, int
if (isset($aQuestionFiles[$index])) {
$aFile = $aQuestionFiles[$index];
// Real path check from here: https://stackoverflow.com/questions/4205141/preventing-directory-traversal-in-php-but-allowing-paths
$sDir = Yii::app()->getConfig('uploaddir') . "/surveys/" . $surveyId . "/files/";
$sDir = Yii::app()->getConfig('uploaddir') . DIRECTORY_SEPARATOR . "surveys" . DIRECTORY_SEPARATOR . $surveyId . DIRECTORY_SEPARATOR . "files" . DIRECTORY_SEPARATOR;
$sFileRealName = $sDir . $aFile['filename'];
$sRealUserPath = realpath($sFileRealName);
if ($sRealUserPath === false || strpos($sRealUserPath, $sDir) !== 0) {
throw new CHttpException(403, "Disable for security reasons.");
if ($sRealUserPath === false) {
throw new CHttpException(404, "File not found.");
} elseif (strpos($sRealUserPath, $sDir) !== 0) {
throw new CHttpException(403, "File cannot be accessed.");
} else {
$mimeType = CFileHelper::getMimeType($sFileRealName, null, false);
if (is_null($mimeType)) {
Expand Down
18 changes: 7 additions & 11 deletions application/controllers/UploaderController.php
Expand Up @@ -140,9 +140,7 @@ public function run($actionID)
// Try to create
mkdir($sTempUploadDir);
}
$filename = $_FILES['uploadfile']['name'];
// Do we filter file name ? It's used on displaying only , but not save like that.
//$filename = sanitize_filename($_FILES['uploadfile']['name']);// This remove all non alpha numeric characters and replaced by _ . Leave only one dot .
$filename = sanitize_filename($_FILES['uploadfile']['name'], false, false, true);
$size = $_FILES['uploadfile']['size'] / 1024;
$preview = Yii::app()->session['preview'];
$aFieldMap = createFieldMap($oSurvey, 'short', false, false, $sLanguage);
Expand Down Expand Up @@ -272,12 +270,12 @@ public function run($actionID)
}
if (move_uploaded_file($_FILES['uploadfile']['tmp_name'], $randfileloc)) {
$return = array(
"success" => true,
"size" => $size,
"name" => rawurlencode(basename($filename)),
"ext" => $cleanExt,
"filename" => $randfilename,
"msg" => gT("The file has been successfully uploaded.")
"success" => true,
"size" => $size,
"name" => $filename,
"ext" => $cleanExt,
"filename" => $randfilename,
"msg" => gT("The file has been successfully uploaded.")
);
//header('Content-Type: application/json');
echo ls_json_encode($return);
Expand Down Expand Up @@ -341,8 +339,6 @@ public function run($actionID)
$oTemplate = Template::model()->getInstance('', $surveyid);
App()->getClientScript()->registerScript('sNeededScriptVar', $sNeededScriptVar, LSYii_ClientScript::POS_BEGIN);
App()->getClientScript()->registerScript('sLangScriptVar', $sLangScriptVar, LSYii_ClientScript::POS_BEGIN);
App()->getClientScript()->registerScriptFile(Yii::app()->getConfig('packages') . 'questions/upload/build/uploadquestion.js');
App()->getClientScript()->registerScriptFile(Yii::app()->getConfig('packages') . 'questions/upload/src/ajaxupload.js');

$header = getHeader($meta);

Expand Down
1 change: 1 addition & 0 deletions application/controllers/UserManagementController.php
Expand Up @@ -1189,6 +1189,7 @@ private function createAdminUser(array $aUser, bool $sendEmail = true): array
'success' => $success,
'message' => $sReturnMessage,
'href' => Yii::app()->getController()->createUrl('userManagement/userPermissions', ['userid' => $newUser['uid']]),
'modalsize' => 'modal-lg',
];
} else {
$data = [
Expand Down
2 changes: 1 addition & 1 deletion application/helpers/common_helper.php
Expand Up @@ -4521,7 +4521,7 @@ function fixSubquestions()
*/
function ls_json_encode($content)
{
$ans = json_encode($content);
$ans = json_encode($content, JSON_UNESCAPED_UNICODE);
$ans = str_replace(array('{', '}'), array('{ ', ' }'), $ans);
return $ans;
}
Expand Down
1 change: 1 addition & 0 deletions application/helpers/expressions/em_manager_helper.php
Expand Up @@ -8452,6 +8452,7 @@ public static function ProcessCurrentResponses()
// Move the (unmoved, temp) files from temp to files directory.
// Check all possible file uploads
for ($i = 0; $i < $iSize; $i++) {
$aFiles[$i]->name = sanitize_filename($aFiles[$i]->name, false, false, true);
$aFiles[$i]->filename = get_absolute_path($aFiles[$i]->filename);
if (file_exists($tmp . $aFiles[$i]->filename)) {
$sDestinationFileName = 'fu_' . randomChars(15);
Expand Down
6 changes: 4 additions & 2 deletions application/helpers/qanda_helper.php
Expand Up @@ -1782,10 +1782,12 @@ function do_file_upload($ia)
'value' => $_SESSION['survey_' . Yii::app()->getConfig('surveyID')][$ia[1]],
'filecountvalue' => $filecountvalue,
'coreClass' => $coreClass,
'maxFiles' => $aQuestionAttributes['max_num_of_files'],
'basename' => $ia[1],
'uploadurl' => $uploadurl,
'show_title' => $aQuestionAttributes['show_title'],
'show_comment' => $aQuestionAttributes['show_comment'],
'scriptloc' => Yii::app()->getController()->createUrl('/uploader/index/mode/upload/'),
'showTitle' => $aQuestionAttributes['show_title'],
'showComment' => $aQuestionAttributes['show_comment'],
'uploadButtonLabel' => ngT("Upload file|Upload files", $aQuestionAttributes['max_num_of_files'])
);
$answer = doRender('/survey/questions/answer/file_upload/answer', $fileuploadData, true);
Expand Down
12 changes: 4 additions & 8 deletions application/helpers/sanitize_helper.php
Expand Up @@ -119,14 +119,12 @@ function nice_addslashes($string)
function sanitize_filename($filename, $force_lowercase = true, $alphanumeric = false, $beautify = true)
{
// sanitize filename
$filename = preg_replace(
'~
[<>:"/\\|?*]|
$filename = mb_ereg_replace(
'[<>:"\\|?*]|
[\x00-\x1F]|
[\x7F\xA0\xAD]|
[#\[\]@!$&\'()+,;=]|
[{}^\~`]
~x',
[{}^\~`]',
'-',
$filename
);
Expand All @@ -141,7 +139,7 @@ function sanitize_filename($filename, $force_lowercase = true, $alphanumeric = f
// maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
$filename = ($alphanumeric) ? preg_replace("/[^a-zA-Z0-9]/", "", $filename) : $filename;
$filename = ($alphanumeric) ? mb_ereg_replace("[^a-zA-Z0-9]", "", $filename) : $filename;

if ($force_lowercase) {
$filename = mb_strtolower($filename, 'UTF-8');
Expand Down Expand Up @@ -171,8 +169,6 @@ function beautify_filename($filename)
// "file...name..zip" becomes "file.name.zip"
'/\.{2,}/'
), '.', $filename);
// lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
// ".file-name.-" becomes "file-name"
$filename = trim($filename, '.-');
return $filename;
Expand Down
38 changes: 25 additions & 13 deletions application/models/SurveyDynamic.php
Expand Up @@ -324,30 +324,42 @@ class='btn btn-default btn-sm'

/* deletefiles button */
if (Permission::model()->hasSurveyPermission(self::$sid, 'responses', 'update') && hasFileUploadQuestion(self::$sid) && Response::model(self::$sid)->findByPk($this->id)->someFileExists()) {
$buttons .= "<a
href='" . App()->createUrl("responses/deleteAttachments", ["surveyId" => self::$sid, "responseId" => $this->id]) . "'
class='btn btn-default btn-sm btn-deletefiles'
$buttons .= "
<span
data-toggle='modal'
data-target='#confirmation-modal'
data-btnclass='btn-danger'
data-post-url='" . App()->createUrl("responses/deleteAttachments") . "'
data-post-datas='" . json_encode(['surveyId' => self::$sid, 'responseId' => $this->id]) . "'
data-btntext='" . gt("Delete") . "'
data-message='" . gt("Do you want to delete all files of this response?") . "'>
<button
data-toggle='tooltip'
title='" . gt("Delete all files of this response") . "'
onclick='function(event){ window.LS.gridButton.confirmGridAction(event,$(this)); }'>
<i class='fa fa-paperclip text-danger'></i>
</a>";
class='btn btn-default btn-sm btn-deletefiles'
title='" . gt("Delete all files of this response") . "'>
<i class='fa fa-paperclip text-danger'></i>
</button>
</span>";
}

/* delete button */
if (Permission::model()->hasSurveyPermission(self::$sid, 'responses', 'delete')) {
$buttons .= "<button
class='btn btn-default btn-sm btn-delete'
$buttons .= "
<span
data-toggle='modal'
data-target='#confirmation-modal'
data-btnclass='btn-danger'
data-post-url='" . App()->createUrl("responses/deleteSingle") . "'
data-post-datas='" . json_encode(['surveyId' => self::$sid, 'responseId' => $this->id]) . "'
data-btntext='" . gt("Delete") . "'
title='" . gt("Delete this response") . "'
data-message='" . gt("Do you want to delete this response?") . "<br/>" . gT("Please note that if you delete an incomplete response during a running survey, the participant will not be able to complete it.") . "'>
<i class='fa fa-trash text-danger'></i>
</button>";
<button
data-toggle='tooltip'
class='btn btn-default btn-sm btn-delete'
title='" . gt("Delete this response") . "'>
<i class='fa fa-trash text-danger'></i>
</button>
</span>";
}
return $buttons;
}
Expand Down Expand Up @@ -385,7 +397,7 @@ public function getExtendedData($colName, $sLanguage, $base64jsonFieldMap)

// Upload question
if ($oFieldMap->type == Question::QT_VERTICAL_FILE_UPLOAD && strpos($oFieldMap->fieldname, 'filecount') === false) {
$sSurveyEntry = "<table class='table table-condensed upload-question'><tr>";
$sSurveyEntry = "<table class='table table-condensed upload-question'>";
$aQuestionAttributes = QuestionAttribute::model()->getQuestionAttributes($oFieldMap->qid);
$aFilesInfo = json_decode_ls($this->$colName);
for ($iFileIndex = 0; $iFileIndex < $aQuestionAttributes['max_num_of_files']; $iFileIndex++) {
Expand Down
Expand Up @@ -18,8 +18,8 @@
href='{{uploadurl}}'
data-toggle="modal"
data-target="file-upload-modal-{{fileid}}"
data-showtitle="{{show_title}}"
data-showcomment="{{show_comment}}"
data-showtitle="{{showTitle}}"
data-showcomment="{{showComment}}"
data-fieldname="{{basename}}"
>
<span class='fa fa-upload'></span>&nbsp;{{ uploadButtonLabel }}
Expand Down Expand Up @@ -88,9 +88,9 @@
$(document).on("ready pjax:scriptcomplete", function(){
var fieldname = "{{fileid}}",
filecount = $("#"+fieldname+"_filecount").val(),
show_title = "{{showTitle}}",
show_comment = "{{showComment}}";
window.uploadQuestionController_{{ fileid }}.displayUploadedFiles( filecount, fieldname, show_title, show_comment);
showTitle = "{{showTitle}}",
showComment = "{{showComment}}";
window.uploadQuestionController_{{ fileid }}.displayUploadedFiles( filecount, fieldname, showTitle, showComment);
});
$(".basic_{{fileid}}").change(function() {
Expand Down
14 changes: 7 additions & 7 deletions assets/packages/questions/upload/build/uploadquestion.js
Expand Up @@ -777,15 +777,15 @@

if (this.isValueInArray(image_extensions, item.ext)) {
imageOrPlaceholder = "image";
imageOrPlaceholderHtml = `<img src="${uploadurl}/filegetcontents/${decodeURIComponent(item.filename)}" class="uploaded" />`;
imageOrPlaceholderHtml = '<img src="'+ uploadurl +'/filegetcontents/'+ decodeURIComponent(item.filename) +'" class="uploaded" />';
} else {
imageOrPlaceholder = "placeholder";
imageOrPlaceholderHtml = `<div class="upload-placeholder"></div>`;
}

title = show_title != 0 ? item.title : '';
comment = show_comment != 0 ? item.comment : '';
name = item.name;
title = show_title != 0 ? escapeHtml(item.title) : '';
comment = show_comment != 0 ? escapeHtml(item.comment) : '';
name = escapeHtml(item.name);
filepointer = iterator;
const rowHtml = this.replaceWithObject(templateHtml, {
imageOrPlaceholder,
Expand All @@ -808,7 +808,7 @@
let outString = templateString;

for (let key in objectWithReplacements) {
outString = outString.replace(new RegExp(`\{${key}\}`), objectWithReplacements[key]);
outString = outString.replace(new RegExp("\{"+key+"\}"), objectWithReplacements[key]);
}

return outString;
Expand All @@ -835,7 +835,7 @@
var i = iterator;
var image_extensions = new Array('gif', 'jpeg', 'jpg', 'png', 'swf', 'psd', 'bmp', 'tiff', 'jp2', 'iff', 'bmp', 'xbm', 'ico', 'heic');
var previewblock = $('<li id="' + fieldname + '_li_' + i + '" class="previewblock file-element"></li>');
var previewContainer = $('<div class="file-preview"></div>');
var previewContainer = $('<div class="file-preview form-group"></div>');

if (isValueInArray(image_extensions, item.ext.toLowerCase())) {
previewContainer.append('<img src="' + options.uploadurl + '/filegetcontents/' + item.filename + '" class="uploaded" />');
Expand Down Expand Up @@ -865,7 +865,6 @@
var previewDeleteBlock = $('<div class="form-group"></div>').append($('<a class="btn btn-danger"></a>').html('<span class="fa fa-trash"></span>&nbsp;' + options.uploadLang.deleteFile).on('click', function () {
deletefile(fieldname, i);
}).wrap('<div class="input-container text-center"></div>'));
$('<fieldset></fieldset>').append(previewTitleContainer).append(previewCommentContainer).append(previewDeleteBlock).wrap('<div class="file-info"></div>').appendTo(previewContainer);
$('<input type="hidden" />').attr('id', fieldname + '_size_' + i).attr('value', item.size).appendTo(previewblock);
$('<input type="hidden" />').attr('id', fieldname + '_name_' + i).attr('value', item.name).appendTo(previewblock);
$('<input type="hidden" />').attr('id', fieldname + '_file_index_' + i).attr('value', i).appendTo(previewblock);
Expand All @@ -876,6 +875,7 @@
previewblock.append(previewContainer);
$('#field' + fieldname + '_listfiles').append(previewblock);
}
$('<fieldset></fieldset>').append(previewTitleContainer).append(previewCommentContainer).append(previewDeleteBlock).wrap('<div class="file-info"></div>').appendTo(previewblock);
};

var doFileUpload = function () {
Expand Down
3 changes: 1 addition & 2 deletions assets/packages/questions/upload/build/uploadquestion.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions assets/packages/questions/upload/src/modaldialog.js
Expand Up @@ -114,10 +114,10 @@ class UploadQuestionController {
let imageOrPlaceholder, imageOrPlaceholderHtml, title, comment, name, filepointer;
if (this.isValueInArray(image_extensions, item.ext)) {
imageOrPlaceholder = "image";
imageOrPlaceholderHtml = `<img src="${uploadurl}/filegetcontents/${decodeURIComponent(item.filename)}" class="uploaded" />`;
imageOrPlaceholderHtml = '<img src="'+uploadurl+'/filegetcontents/'+decodeURIComponent(item.filename)+'" class="uploaded" />';
} else {
imageOrPlaceholder = "placeholder";
imageOrPlaceholderHtml = `<div class="upload-placeholder"></div>`;
imageOrPlaceholderHtml = '<div class="upload-placeholder"></div>';
}

title = (show_title != 0) ? item.title : '';
Expand Down
9 changes: 8 additions & 1 deletion assets/packages/questions/upload/styles/uploader.css
Expand Up @@ -39,7 +39,13 @@ margin:0;
}
}


.file-preview {
display: flex;
align-items: center;
}
.file-preview span {
margin-left: 1em;
}

.file-info label{
float:left;
Expand Down Expand Up @@ -68,6 +74,7 @@ margin:0;
}
.upload-div{
text-align: center;
margin-bottom: 0.5em;
}
.uploadmsg {
text-align: center;
Expand Down
3 changes: 2 additions & 1 deletion assets/packages/usermanagement/js/usermanagement.js
Expand Up @@ -98,7 +98,8 @@ var UserManagement = function () {
window.LS.notifyFader(result.message, 'well-lg text-center ' + (result.success ? 'bg-primary' : 'bg-danger'));
if (result.hasOwnProperty('href')) {
setTimeout(function() {
openModal(result.href);
const modalSize = result.hasOwnProperty('modalsize') ? result.modalsize : '';
openModal(result.href, modalSize);
}, 500);
}
return;
Expand Down
4 changes: 4 additions & 0 deletions assets/styles-public/browse.css
Expand Up @@ -31,6 +31,10 @@
margin-left: 3px;
}

#responses-grid table.upload-question > tbody > tr:first-child > td {
border: none;
}

/*!
* Style for browsing response : in jqgrid
* TODO: Is not in use anymore
Expand Down

0 comments on commit cc2b85c

Please sign in to comment.