mirror of
https://github.com/moodle/moodle.git
synced 2025-07-23 07:11:28 +02:00
MDL-50794 workshop: Improve the file type restricting implementation
This is basically a clean up and what I think improved version of the original Mahmoud's patch. The actual checking for allowed file extensions has been re-implemented and is now covered by unit tests. The list of allowed extensions is now also assed to the filemanager element's accepted_types option to prevent picking other files (we still need the in-place validation though). The form validation is simplified a bit. The custom validation of file size introduced in the previous patch has been removed as not related to this issue (also I believe it should not be done at this level).
This commit is contained in:
@@ -49,27 +49,24 @@ function xmldb_workshop_upgrade($oldversion) {
|
|||||||
$dbman = $DB->get_manager();
|
$dbman = $DB->get_manager();
|
||||||
|
|
||||||
if ($oldversion < 2016022200) {
|
if ($oldversion < 2016022200) {
|
||||||
|
// Add field submissionfiletypes to the table workshop.
|
||||||
// Define field submissionfiletypes to be added to workshop.
|
|
||||||
$table = new xmldb_table('workshop');
|
$table = new xmldb_table('workshop');
|
||||||
$field = new xmldb_field('submissionfiletypes', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'nattachments');
|
$field = new xmldb_field('submissionfiletypes', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'nattachments');
|
||||||
|
|
||||||
// Conditionally launch add field submissionfiletypes.
|
|
||||||
if (!$dbman->field_exists($table, $field)) {
|
if (!$dbman->field_exists($table, $field)) {
|
||||||
$dbman->add_field($table, $field);
|
$dbman->add_field($table, $field);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define field overallfeedbackfiletypes to be added to workshop.
|
// Add field overallfeedbackfiletypes to the table workshop.
|
||||||
$field = new xmldb_field('overallfeedbackfiletypes',
|
$field = new xmldb_field('overallfeedbackfiletypes',
|
||||||
XMLDB_TYPE_CHAR, '255', null, null, null, null, 'overallfeedbackfiles');
|
XMLDB_TYPE_CHAR, '255', null, null, null, null, 'overallfeedbackfiles');
|
||||||
|
|
||||||
// Conditionally launch add field overallfeedbackfiletypes.
|
|
||||||
if (!$dbman->field_exists($table, $field)) {
|
if (!$dbman->field_exists($table, $field)) {
|
||||||
$dbman->add_field($table, $field);
|
$dbman->add_field($table, $field);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workshop savepoint reached.
|
|
||||||
upgrade_mod_savepoint(true, 2016022200, 'workshop');
|
upgrade_mod_savepoint(true, 2016022200, 'workshop');
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -115,25 +115,14 @@ if ($id and $assess and $canassess) {
|
|||||||
if ($edit and $canmanage) {
|
if ($edit and $canmanage) {
|
||||||
require_once(dirname(__FILE__).'/submission_form.php');
|
require_once(dirname(__FILE__).'/submission_form.php');
|
||||||
|
|
||||||
$maxfiles = $workshop->nattachments;
|
$example = file_prepare_standard_editor($example, 'content', $workshop->submission_content_options(),
|
||||||
$maxbytes = $workshop->maxbytes;
|
$workshop->context, 'mod_workshop', 'submission_content', $example->id);
|
||||||
$contentopts = array(
|
|
||||||
'trusttext' => true,
|
|
||||||
'subdirs' => false,
|
|
||||||
'maxfiles' => $maxfiles,
|
|
||||||
'filetypes' => $workshop->submissionfiletypes,
|
|
||||||
'maxbytes' => $maxbytes,
|
|
||||||
'context' => $workshop->context
|
|
||||||
);
|
|
||||||
|
|
||||||
$attachmentopts = array('subdirs' => true, 'maxfiles' => $maxfiles, 'maxbytes' => $maxbytes);
|
$example = file_prepare_standard_filemanager($example, 'attachment', $workshop->submission_attachment_options(),
|
||||||
$example = file_prepare_standard_editor($example, 'content', $contentopts, $workshop->context,
|
$workshop->context, 'mod_workshop', 'submission_attachment', $example->id);
|
||||||
'mod_workshop', 'submission_content', $example->id);
|
|
||||||
$example = file_prepare_standard_filemanager($example, 'attachment', $attachmentopts, $workshop->context,
|
|
||||||
'mod_workshop', 'submission_attachment', $example->id);
|
|
||||||
|
|
||||||
$mform = new workshop_submission_form($PAGE->url, array('current' => $example, 'workshop' => $workshop,
|
$mform = new workshop_submission_form($PAGE->url, array('current' => $example, 'workshop' => $workshop,
|
||||||
'contentopts' => $contentopts, 'attachmentopts' => $attachmentopts));
|
'contentopts' => $workshop->submission_content_options(), 'attachmentopts' => $workshop->submission_attachment_options()));
|
||||||
|
|
||||||
if ($mform->is_cancelled()) {
|
if ($mform->is_cancelled()) {
|
||||||
redirect($workshop->view_url());
|
redirect($workshop->view_url());
|
||||||
|
@@ -122,50 +122,38 @@ class workshop_assessment_form extends moodleform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate incoming data.
|
* Validate assessment form data.
|
||||||
*
|
*
|
||||||
* @param array $data
|
* @param array $data
|
||||||
* @param array $files
|
* @param array $files
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function validation($data, $files) {
|
public function validation($data, $files) {
|
||||||
|
|
||||||
$errors = parent::validation($data, $files);
|
$errors = parent::validation($data, $files);
|
||||||
|
|
||||||
if (isset($data['feedbackauthorattachment_filemanager'])) {
|
if (isset($data['feedbackauthorattachment_filemanager']) and isset($this->workshop->overallfeedbackfiletypes)) {
|
||||||
$draftitemid = $data['feedbackauthorattachment_filemanager'];
|
$whitelist = workshop::normalize_file_extensions($this->workshop->overallfeedbackfiletypes);
|
||||||
|
if ($whitelist) {
|
||||||
// If we have draft files, then make sure they are the correct ones.
|
$draftfiles = file_get_drafarea_files($data['feedbackauthorattachment_filemanager']);
|
||||||
if ($draftfiles = file_get_drafarea_files($draftitemid)) {
|
if ($draftfiles) {
|
||||||
|
$wrongfiles = array();
|
||||||
if (!$validfileextensions = workshop::get_array_of_file_extensions($this->workshop->overallfeedbackfiletypes)) {
|
foreach ($draftfiles->list as $file) {
|
||||||
return $errors;
|
if (!workshop::is_allowed_file_type($file->filename, $whitelist)) {
|
||||||
}
|
$wrongfiles[] = $file->filename;
|
||||||
$wrongfileextensions = null;
|
}
|
||||||
$bigfiles = null;
|
|
||||||
|
|
||||||
// Check the size of each file.
|
|
||||||
foreach ($draftfiles->list as $file) {
|
|
||||||
$a = new stdClass();
|
|
||||||
$a->maxbytes = $this->workshop->overallfeedbackmaxbytes;
|
|
||||||
$a->currentbytes = $file->size;
|
|
||||||
$a->filename = $file->filename;
|
|
||||||
$a->validfileextensions = implode(',', $validfileextensions);
|
|
||||||
|
|
||||||
// Check whether the extension of uploaded file is in the list.
|
|
||||||
$thisextension = substr(strrchr($file->filename, '.'), 1);
|
|
||||||
if (!in_array($thisextension, $validfileextensions)) {
|
|
||||||
$wrongfileextensions .= get_string('err_wrongfileextension', 'workshop', $a) . '<br/>';
|
|
||||||
}
|
}
|
||||||
if ($file->size > $this->workshop->overallfeedbackmaxbytes) {
|
if ($wrongfiles) {
|
||||||
$bigfiles .= get_string('err_maxbytes', 'workshop', $a) . '<br/>';
|
$a = array(
|
||||||
|
'whitelist' => workshop::clean_file_extensions($whitelist),
|
||||||
|
'wrongfiles' => implode(', ', $wrongfiles),
|
||||||
|
);
|
||||||
|
$errors['feedbackauthorattachment_filemanager'] = get_string('err_wrongfileextension', 'mod_workshop', $a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($bigfiles || $wrongfileextensions) {
|
|
||||||
$errors['feedbackauthorattachment_filemanager'] = $bigfiles . $wrongfileextensions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -31,9 +31,15 @@ $string['allocationdone'] = 'Allocation done';
|
|||||||
$string['allocationerror'] = 'Allocation error';
|
$string['allocationerror'] = 'Allocation error';
|
||||||
$string['allocationconfigured'] = 'Allocation configured';
|
$string['allocationconfigured'] = 'Allocation configured';
|
||||||
$string['allowedfiletypesforoverallfeedback'] = 'Feedback attachment allowed file types';
|
$string['allowedfiletypesforoverallfeedback'] = 'Feedback attachment allowed file types';
|
||||||
$string['allowedfiletypesforoverallfeedback_help'] = 'Feedback attachment allowed file types can be restricted by entering a comma-separated list of file extensions, for example \'mp4, mp3, jpg, jpeg\'. If the field is left empty, then all file types are allowed.';
|
$string['allowedfiletypesforoverallfeedback_help'] = 'Feedback attachment allowed file types can be restricted by entering a comma-separated list of file extensions, for example \'png, jpg, jpeg, gif\'. If the field is left empty, then all file types are allowed.
|
||||||
|
|
||||||
|
Additional supported file extensions can be configured in the server administration';
|
||||||
|
$string['allowedfiletypesforoverallfeedback_link'] = 'admin/tool/filetypes/index';
|
||||||
$string['allowedfiletypesforsubmission'] = 'Submission attachment allowed file types';
|
$string['allowedfiletypesforsubmission'] = 'Submission attachment allowed file types';
|
||||||
$string['allowedfiletypesforsubmission_help'] = 'Submission attachment allowed file types can be restricted by entering a comma-separated list of file extensions, for example \'mp4, mp3, jpg, jpeg\'. If the field is left empty, then all file types are allowed.';
|
$string['allowedfiletypesforsubmission_help'] = 'Submission attachment allowed file types can be restricted by entering a comma-separated list of file extensions, for example `png, jpg, jpeg, gif`. If the field is left empty, then all file types are allowed.
|
||||||
|
|
||||||
|
Additional supported file extensions can be configured in the server administration';
|
||||||
|
$string['allowedfiletypesforsubmission_link'] = 'admin/tool/filetypes/index';
|
||||||
$string['allsubmissions'] = 'All submissions ({$a})';
|
$string['allsubmissions'] = 'All submissions ({$a})';
|
||||||
$string['alreadygraded'] = 'Already graded';
|
$string['alreadygraded'] = 'Already graded';
|
||||||
$string['areaconclusion'] = 'Conclusion text';
|
$string['areaconclusion'] = 'Conclusion text';
|
||||||
@@ -104,9 +110,8 @@ $string['editingsubmission'] = 'Editing submission';
|
|||||||
$string['editsubmission'] = 'Edit submission';
|
$string['editsubmission'] = 'Edit submission';
|
||||||
$string['err_multiplesubmissions'] = 'While editing this form, another version of the submission has been saved. Multiple submissions per user are not allowed.';
|
$string['err_multiplesubmissions'] = 'While editing this form, another version of the submission has been saved. Multiple submissions per user are not allowed.';
|
||||||
$string['err_removegrademappings'] = 'Unable to remove the unused grade mappings';
|
$string['err_removegrademappings'] = 'Unable to remove the unused grade mappings';
|
||||||
$string['err_maxbytes'] = 'The attachment "{$a->filename} ({$a->currentbytes} bytes)" exeeds the maximum allowed file size ({$a->maxbytes} bytes)';
|
$string['err_unknownfileextension'] = 'Unknown file extension: {$a}';
|
||||||
$string['err_notallowedfiletype'] = 'The file extension "{$a}" is not allowed';
|
$string['err_wrongfileextension'] = 'Some files ({$a->wrongfiles}) cannot be uploaded. Only file types {$a->whitelist} are allowed.';
|
||||||
$string['err_wrongfileextension'] = 'The file "{$a->filename}" cannot be uploaded, only file types "{$a->validfileextensions}" are allowed';
|
|
||||||
$string['evaluategradeswait'] = 'Please wait until the assessments are evaluated and the grades are calculated';
|
$string['evaluategradeswait'] = 'Please wait until the assessments are evaluated and the grades are calculated';
|
||||||
$string['evaluation'] = 'Grading evaluation';
|
$string['evaluation'] = 'Grading evaluation';
|
||||||
$string['evaluationmethod'] = 'Grading evaluation method';
|
$string['evaluationmethod'] = 'Grading evaluation method';
|
||||||
|
@@ -81,6 +81,14 @@ function workshop_add_instance(stdclass $workshop) {
|
|||||||
$workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
|
$workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
|
||||||
$workshop->evaluation = 'best';
|
$workshop->evaluation = 'best';
|
||||||
|
|
||||||
|
if (isset($workshop->submissionfiletypes)) {
|
||||||
|
$workshop->submissionfiletypes = workshop::clean_file_extensions($workshop->submissionfiletypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($workshop->overallfeedbackfiletypes)) {
|
||||||
|
$workshop->overallfeedbackfiletypes = workshop::clean_file_extensions($workshop->overallfeedbackfiletypes);
|
||||||
|
}
|
||||||
|
|
||||||
// insert the new record so we get the id
|
// insert the new record so we get the id
|
||||||
$workshop->id = $DB->insert_record('workshop', $workshop);
|
$workshop->id = $DB->insert_record('workshop', $workshop);
|
||||||
|
|
||||||
@@ -141,6 +149,14 @@ function workshop_update_instance(stdclass $workshop) {
|
|||||||
$workshop->latesubmissions = (int)!empty($workshop->latesubmissions);
|
$workshop->latesubmissions = (int)!empty($workshop->latesubmissions);
|
||||||
$workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
|
$workshop->phaseswitchassessment = (int)!empty($workshop->phaseswitchassessment);
|
||||||
|
|
||||||
|
if (isset($workshop->submissionfiletypes)) {
|
||||||
|
$workshop->submissionfiletypes = workshop::clean_file_extensions($workshop->submissionfiletypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($workshop->overallfeedbackfiletypes)) {
|
||||||
|
$workshop->overallfeedbackfiletypes = workshop::clean_file_extensions($workshop->overallfeedbackfiletypes);
|
||||||
|
}
|
||||||
|
|
||||||
// todo - if the grading strategy is being changed, we may want to replace all aggregated peer grades with nulls
|
// todo - if the grading strategy is being changed, we may want to replace all aggregated peer grades with nulls
|
||||||
|
|
||||||
$DB->update_record('workshop', $workshop);
|
$DB->update_record('workshop', $workshop);
|
||||||
|
@@ -418,37 +418,116 @@ class workshop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split a list of file extensions to an array
|
* Converts the argument into an array (list) of file extensions.
|
||||||
* @param string $listofextensions
|
*
|
||||||
* @return array of extensions
|
* The list can be separated by whitespace, end of lines, commas colons and semicolons.
|
||||||
|
* Empty values are not returned. Values are converted to lowercase.
|
||||||
|
* Duplicates are removed. Glob evaluation is not supported.
|
||||||
|
*
|
||||||
|
* @param string|array $extensions list of file extensions
|
||||||
|
* @return array of strings
|
||||||
*/
|
*/
|
||||||
public static function get_array_of_file_extensions($listofextensions) {
|
public static function normalize_file_extensions($extensions) {
|
||||||
return preg_split("/[\s,;:\"']+/", strtolower($listofextensions), null, PREG_SPLIT_NO_EMPTY);
|
|
||||||
|
if ($extensions === '') {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($extensions)) {
|
||||||
|
$extensions = preg_split('/[\s,;:"\']+/', $extensions, null, PREG_SPLIT_NO_EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($extensions as $i => $extension) {
|
||||||
|
$extension = str_replace('*.', '', $extension);
|
||||||
|
$extension = strtolower($extension);
|
||||||
|
$extension = ltrim($extension, '.');
|
||||||
|
$extension = trim($extension);
|
||||||
|
$extensions[$i] = $extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($extensions as $i => $extension) {
|
||||||
|
if (strpos($extension, '*') !== false or strpos($extension, '?') !== false) {
|
||||||
|
unset($extensions[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$extensions = array_filter($extensions, 'strlen');
|
||||||
|
$extensions = array_keys(array_flip($extensions));
|
||||||
|
|
||||||
|
foreach ($extensions as $i => $extension) {
|
||||||
|
$extensions[$i] = '.'.$extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check allowed file types and return an error when invalid file extensions found in the list
|
* Cleans the user provided list of file extensions.
|
||||||
*
|
*
|
||||||
* @param string $extensionlist
|
* @param string $extensions
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function check_allowed_file_types($extensionlist) {
|
public static function clean_file_extensions($extensions) {
|
||||||
if (!$extensionlist) {
|
|
||||||
return '';
|
$extensions = self::normalize_file_extensions($extensions);
|
||||||
|
|
||||||
|
foreach ($extensions as $i => $extension) {
|
||||||
|
$extensions[$i] = ltrim($extension, '.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($extensions = self::get_array_of_file_extensions($extensionlist)) {
|
return implode(', ', $extensions);
|
||||||
$coreextensions = array_keys(get_mimetypes_array());
|
}
|
||||||
foreach ($extensions as $ext) {
|
|
||||||
$ext = ltrim($ext, '.');
|
/**
|
||||||
if (!$ext) {
|
* Check given file types and return invalid/unknown ones.
|
||||||
continue;
|
*
|
||||||
}
|
* Empty whitelist is interpretted as "any extension is valid".
|
||||||
// Use strtolower(), because all extensions are in lower case.
|
*
|
||||||
if (!in_array($ext, $coreextensions)) {
|
* @param string|array $extensions list of file extensions
|
||||||
return get_string('err_notallowedfiletype', 'workshop', $ext);
|
* @param string|array $whitelist list of valid extensions
|
||||||
}
|
* @return array list of invalid extensions not found in the whitelist
|
||||||
|
*/
|
||||||
|
public static function invalid_file_extensions($extensions, $whitelist) {
|
||||||
|
|
||||||
|
$extensions = self::normalize_file_extensions($extensions);
|
||||||
|
$whitelist = self::normalize_file_extensions($whitelist);
|
||||||
|
|
||||||
|
if (empty($extensions) or empty($whitelist)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return those items from $extensions that are not present in $whitelist.
|
||||||
|
return array_keys(array_diff_key(array_flip($extensions), array_flip($whitelist)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the file have allowed to be uploaded to the workshop?
|
||||||
|
*
|
||||||
|
* Empty whitelist is interpretted as "any file type is allowed" rather
|
||||||
|
* than "no file can be uploaded".
|
||||||
|
*
|
||||||
|
* @param string $filename the file name
|
||||||
|
* @param string|array $whitelist list of allowed file extensions
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
public static function is_allowed_file_type($filename, $whitelist) {
|
||||||
|
|
||||||
|
$whitelist = self::normalize_file_extensions($whitelist);
|
||||||
|
|
||||||
|
if (empty($whitelist)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$haystack = strrev(trim(strtolower($filename)));
|
||||||
|
|
||||||
|
foreach ($whitelist as $extension) {
|
||||||
|
if (strpos($haystack, strrev($extension)) === 0) {
|
||||||
|
// The file name ends with the extension.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -2401,6 +2480,43 @@ class workshop {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the editor options for the submission content field.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function submission_content_options() {
|
||||||
|
return array(
|
||||||
|
'trusttext' => true,
|
||||||
|
'subdirs' => false,
|
||||||
|
'maxfiles' => $this->nattachments,
|
||||||
|
'maxbytes' => $this->maxbytes,
|
||||||
|
'context' => $this->context,
|
||||||
|
'return_types' => FILE_INTERNAL | FILE_EXTERNAL,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the filemanager options for the submission attachments field.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function submission_attachment_options() {
|
||||||
|
|
||||||
|
$options = array(
|
||||||
|
'subdirs' => true,
|
||||||
|
'maxfiles' => $this->nattachments,
|
||||||
|
'maxbytes' => $this->maxbytes,
|
||||||
|
'return_types' => FILE_INTERNAL,
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($acceptedtypes = self::normalize_file_extensions($this->submissionfiletypes)) {
|
||||||
|
$options['accepted_types'] = $acceptedtypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the editor options for the overall feedback for the author.
|
* Return the editor options for the overall feedback for the author.
|
||||||
*
|
*
|
||||||
@@ -2410,7 +2526,6 @@ class workshop {
|
|||||||
return array(
|
return array(
|
||||||
'subdirs' => 0,
|
'subdirs' => 0,
|
||||||
'maxbytes' => $this->overallfeedbackmaxbytes,
|
'maxbytes' => $this->overallfeedbackmaxbytes,
|
||||||
'filetypes' => $this->overallfeedbackfiletypes,
|
|
||||||
'maxfiles' => $this->overallfeedbackfiles,
|
'maxfiles' => $this->overallfeedbackfiles,
|
||||||
'changeformat' => 1,
|
'changeformat' => 1,
|
||||||
'context' => $this->context,
|
'context' => $this->context,
|
||||||
@@ -2423,13 +2538,19 @@ class workshop {
|
|||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function overall_feedback_attachment_options() {
|
public function overall_feedback_attachment_options() {
|
||||||
return array(
|
|
||||||
|
$options = array(
|
||||||
'subdirs' => 1,
|
'subdirs' => 1,
|
||||||
'maxbytes' => $this->overallfeedbackmaxbytes,
|
'maxbytes' => $this->overallfeedbackmaxbytes,
|
||||||
'filetypes' => $this->overallfeedbackfiletypes,
|
|
||||||
'maxfiles' => $this->overallfeedbackfiles,
|
'maxfiles' => $this->overallfeedbackfiles,
|
||||||
'return_types' => FILE_INTERNAL,
|
'return_types' => FILE_INTERNAL,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ($acceptedtypes = self::normalize_file_extensions($this->overallfeedbackfiletypes)) {
|
||||||
|
$options['accepted_types'] = $acceptedtypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -361,19 +361,14 @@ class mod_workshop_mod_form extends moodleform_mod {
|
|||||||
public function validation($data, $files) {
|
public function validation($data, $files) {
|
||||||
$errors = parent::validation($data, $files);
|
$errors = parent::validation($data, $files);
|
||||||
|
|
||||||
// Check the input of submissionfiletypes field.
|
// Validate lists of allowed extensions.
|
||||||
if ($data['submissionfiletypes']) {
|
foreach (array('submissionfiletypes', 'overallfeedbackfiletypes') as $fieldname) {
|
||||||
$invalidextension = workshop::check_allowed_file_types($data['submissionfiletypes']);
|
if (isset($data[$fieldname])) {
|
||||||
if ($invalidextension) {
|
$invalidextensions = workshop::invalid_file_extensions($data[$fieldname], array_keys(core_filetypes::get_types()));
|
||||||
$errors['submissionfiletypes'] = $invalidextension;
|
if ($invalidextensions) {
|
||||||
}
|
$errors[$fieldname] = get_string('err_unknownfileextension', 'mod_workshop',
|
||||||
}
|
workshop::clean_file_extensions($invalidextensions));
|
||||||
|
}
|
||||||
// Check the input of overallfeedbackfiletypes field.
|
|
||||||
if ($data['overallfeedbackfiletypes']) {
|
|
||||||
$invalidextension = workshop::check_allowed_file_types($data['overallfeedbackfiletypes']);
|
|
||||||
if ($invalidextension) {
|
|
||||||
$errors['overallfeedbackfiletypes'] = $invalidextension;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -183,28 +183,14 @@ if ($assess and $submission->id and !$isreviewer and $canallocate and $workshop-
|
|||||||
if ($edit) {
|
if ($edit) {
|
||||||
require_once(dirname(__FILE__).'/submission_form.php');
|
require_once(dirname(__FILE__).'/submission_form.php');
|
||||||
|
|
||||||
$maxfiles = $workshop->nattachments;
|
$submission = file_prepare_standard_editor($submission, 'content', $workshop->submission_content_options(),
|
||||||
$filetypes = $workshop->submissionfiletypes;
|
$workshop->context, 'mod_workshop', 'submission_content', $submission->id);
|
||||||
$maxbytes = $workshop->maxbytes;
|
|
||||||
$contentopts = array(
|
|
||||||
'trusttext' => true,
|
|
||||||
'subdirs' => false,
|
|
||||||
'maxfiles' => $maxfiles,
|
|
||||||
'filetypes' => $filetypes,
|
|
||||||
'maxbytes' => $maxbytes,
|
|
||||||
'context' => $workshop->context,
|
|
||||||
'return_types' => FILE_INTERNAL | FILE_EXTERNAL
|
|
||||||
);
|
|
||||||
|
|
||||||
$attachmentopts = array('subdirs' => true, 'maxfiles' => $maxfiles, 'filetypes' => $filetypes, 'maxbytes' => $maxbytes,
|
$submission = file_prepare_standard_filemanager($submission, 'attachment', $workshop->submission_attachment_options(),
|
||||||
'return_types' => FILE_INTERNAL);
|
$workshop->context, 'mod_workshop', 'submission_attachment', $submission->id);
|
||||||
$submission = file_prepare_standard_editor($submission, 'content', $contentopts, $workshop->context,
|
|
||||||
'mod_workshop', 'submission_content', $submission->id);
|
|
||||||
$submission = file_prepare_standard_filemanager($submission, 'attachment', $attachmentopts, $workshop->context,
|
|
||||||
'mod_workshop', 'submission_attachment', $submission->id);
|
|
||||||
|
|
||||||
$mform = new workshop_submission_form($PAGE->url, array('current' => $submission, 'workshop' => $workshop,
|
$mform = new workshop_submission_form($PAGE->url, array('current' => $submission, 'workshop' => $workshop,
|
||||||
'contentopts' => $contentopts, 'attachmentopts' => $attachmentopts));
|
'contentopts' => $workshop->submission_content_options(), 'attachmentopts' => $workshop->submission_attachment_options()));
|
||||||
|
|
||||||
if ($mform->is_cancelled()) {
|
if ($mform->is_cancelled()) {
|
||||||
redirect($workshop->view_url());
|
redirect($workshop->view_url());
|
||||||
@@ -257,11 +243,14 @@ if ($edit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$params['objectid'] = $submission->id;
|
$params['objectid'] = $submission->id;
|
||||||
// save and relink embedded images and save attachments
|
|
||||||
$formdata = file_postupdate_standard_editor($formdata, 'content', $contentopts, $workshop->context,
|
// Save and relink embedded images and save attachments.
|
||||||
'mod_workshop', 'submission_content', $submission->id);
|
$formdata = file_postupdate_standard_editor($formdata, 'content', $workshop->submission_content_options(),
|
||||||
$formdata = file_postupdate_standard_filemanager($formdata, 'attachment', $attachmentopts, $workshop->context,
|
$workshop->context, 'mod_workshop', 'submission_content', $submission->id);
|
||||||
'mod_workshop', 'submission_attachment', $submission->id);
|
|
||||||
|
$formdata = file_postupdate_standard_filemanager($formdata, 'attachment', $workshop->submission_attachment_options(),
|
||||||
|
$workshop->context, 'mod_workshop', 'submission_attachment', $submission->id);
|
||||||
|
|
||||||
if (empty($formdata->attachment)) {
|
if (empty($formdata->attachment)) {
|
||||||
// explicit cast to zero integer
|
// explicit cast to zero integer
|
||||||
$formdata->attachment = 0;
|
$formdata->attachment = 0;
|
||||||
|
@@ -37,8 +37,6 @@ class workshop_submission_form extends moodleform {
|
|||||||
$contentopts = $this->_customdata['contentopts'];
|
$contentopts = $this->_customdata['contentopts'];
|
||||||
$attachmentopts = $this->_customdata['attachmentopts'];
|
$attachmentopts = $this->_customdata['attachmentopts'];
|
||||||
|
|
||||||
$this->attachmentopts = $attachmentopts;
|
|
||||||
|
|
||||||
$mform->addElement('header', 'general', get_string('submission', 'workshop'));
|
$mform->addElement('header', 'general', get_string('submission', 'workshop'));
|
||||||
|
|
||||||
$mform->addElement('text', 'title', get_string('submissiontitle', 'workshop'));
|
$mform->addElement('text', 'title', get_string('submissiontitle', 'workshop'));
|
||||||
@@ -90,42 +88,28 @@ class workshop_submission_form extends moodleform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset ($data['attachment_filemanager'])) {
|
if (isset($data['attachment_filemanager']) and isset($this->_customdata['workshop']->submissionfiletypes)) {
|
||||||
$draftitemid = $data['attachment_filemanager'];
|
$whitelist = workshop::normalize_file_extensions($this->_customdata['workshop']->submissionfiletypes);
|
||||||
|
if ($whitelist) {
|
||||||
// If we have draft files, then make sure they are the correct ones.
|
$draftfiles = file_get_drafarea_files($data['attachment_filemanager']);
|
||||||
if ($draftfiles = file_get_drafarea_files($draftitemid)) {
|
if ($draftfiles) {
|
||||||
|
$wrongfiles = array();
|
||||||
if (!$validfileextensions = workshop::get_array_of_file_extensions($this->attachmentopts['filetypes'])) {
|
foreach ($draftfiles->list as $file) {
|
||||||
return $errors;
|
if (!workshop::is_allowed_file_type($file->filename, $whitelist)) {
|
||||||
}
|
$wrongfiles[] = $file->filename;
|
||||||
$wrongfileextensions = null;
|
}
|
||||||
$bigfiles = null;
|
|
||||||
|
|
||||||
// Check the size and type of each file.
|
|
||||||
foreach ($draftfiles->list as $file) {
|
|
||||||
$a = new stdClass();
|
|
||||||
$a->maxbytes = $this->attachmentopts['maxbytes'];
|
|
||||||
$a->currentbytes = $file->size;
|
|
||||||
$a->filename = $file->filename;
|
|
||||||
$a->validfileextensions = implode(',', $validfileextensions);
|
|
||||||
|
|
||||||
// Check whether the extension of uploaded file is in the list.
|
|
||||||
$thisextension = substr(strrchr($file->filename, '.'), 1);
|
|
||||||
if (!in_array($thisextension, $validfileextensions)) {
|
|
||||||
$wrongfileextensions .= get_string('err_wrongfileextension', 'workshop', $a) . '<br/>';
|
|
||||||
}
|
}
|
||||||
|
if ($wrongfiles) {
|
||||||
// Check whether the file size exceeds the maximum submission attachment size.
|
$a = array(
|
||||||
if ($file->size > $this->attachmentopts['maxbytes']) {
|
'whitelist' => workshop::clean_file_extensions($whitelist),
|
||||||
$bigfiles .= get_string('err_maxbytes', 'workshop', $a) . '<br/>';
|
'wrongfiles' => implode(', ', $wrongfiles),
|
||||||
|
);
|
||||||
|
$errors['attachment_filemanager'] = get_string('err_wrongfileextension', 'mod_workshop', $a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($bigfiles || $wrongfileextensions) {
|
|
||||||
$errors['attachment_filemanager'] = $bigfiles . $wrongfileextensions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $errors;
|
return $errors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -624,90 +624,98 @@ class mod_workshop_internal_api_testcase extends advanced_testcase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test converting the string to array.
|
* Test normalizing list of extensions.
|
||||||
*/
|
*/
|
||||||
public function test_get_array_of_file_extensions() {
|
public function test_normalize_file_extensions() {
|
||||||
$this->resetAfterTest(true);
|
$this->resetAfterTest(true);
|
||||||
|
|
||||||
$listofextensions = 'doc, jpg, mp3';
|
$this->assertSame(['.odt'], workshop::normalize_file_extensions('odt'));
|
||||||
$actual = workshop::get_array_of_file_extensions($listofextensions);
|
$this->assertSame(['.odt'], workshop::normalize_file_extensions('.odt'));
|
||||||
$expected = array('doc', 'jpg', 'mp3');
|
$this->assertSame(['.odt'], workshop::normalize_file_extensions('.ODT'));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions('doc, jpg, mp3'));
|
||||||
|
$this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions(['.doc', '.jpg', '.mp3']));
|
||||||
$listofextensions = 'mp4,; docx,; gif';
|
$this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions('doc, *.jpg, mp3'));
|
||||||
$actual = workshop::get_array_of_file_extensions($listofextensions);
|
$this->assertSame(['.doc', '.jpg', '.mp3'], workshop::normalize_file_extensions(['doc ', ' JPG ', '.mp3']));
|
||||||
$expected = array('mp4', 'docx', 'gif');
|
$this->assertSame(['.rtf', '.pdf', '.docx'], workshop::normalize_file_extensions("RTF,.pdf\n...DocX,,,;\rPDF\trtf ...Rtf"));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertSame(['.tgz', '.tar.gz'], workshop::normalize_file_extensions('tgz,TAR.GZ tar.gz .tar.gz tgz TGZ'));
|
||||||
|
$this->assertSame(['.notebook'], workshop::normalize_file_extensions('"Notebook":notebook;NOTEBOOK;,\'NoTeBook\''));
|
||||||
$listofextensions = 'mp4 docx gif';
|
$this->assertSame([], workshop::normalize_file_extensions(''));
|
||||||
$actual = workshop::get_array_of_file_extensions($listofextensions);
|
$this->assertSame([], workshop::normalize_file_extensions([]));
|
||||||
$expected = array('mp4', 'docx', 'gif');
|
$this->assertSame(['.0'], workshop::normalize_file_extensions(0));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertSame(['.0'], workshop::normalize_file_extensions('0'));
|
||||||
|
$this->assertSame(['.odt'], workshop::normalize_file_extensions('*.odt'));
|
||||||
$listofextensions = 'MP4 DOCx Gif';
|
$this->assertSame([], workshop::normalize_file_extensions('.'));
|
||||||
$actual = workshop::get_array_of_file_extensions($listofextensions);
|
$this->assertSame(['.foo'], workshop::normalize_file_extensions('. foo'));
|
||||||
$expected = array('mp4', 'docx', 'gif');
|
$this->assertSame([], workshop::normalize_file_extensions('*'));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertSame([], workshop::normalize_file_extensions('*~'));
|
||||||
|
$this->assertSame(['.pdf', '.ps'], workshop::normalize_file_extensions('* pdf *.ps foo* *bar .r??'));
|
||||||
$listofextensions = '.doc; .jpg; Mp4 "mp3"';
|
|
||||||
$actual = workshop::get_array_of_file_extensions($listofextensions);
|
|
||||||
$expected = array('.doc', '.jpg', 'mp4', 'mp3');
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
|
|
||||||
$listofextensions = '.doc,;.jpg; .Mp3, ".Avi"';
|
|
||||||
$actual = workshop::get_array_of_file_extensions($listofextensions);
|
|
||||||
$expected = array('.doc', '.jpg', '.mp3', '.avi');
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the list of allowed file extensions.
|
* Test cleaning list of extensions.
|
||||||
*/
|
*/
|
||||||
public function test_check_allowed_file_types() {
|
public function test_clean_file_extensions() {
|
||||||
$this->resetAfterTest(true);
|
$this->resetAfterTest(true);
|
||||||
|
|
||||||
// Valid file extensions.
|
$this->assertSame('', workshop::clean_file_extensions(''));
|
||||||
$listofextensions = '';
|
$this->assertSame('', workshop::clean_file_extensions(null));
|
||||||
$expected = '';
|
$this->assertSame('', workshop::clean_file_extensions(' '));
|
||||||
// The function returns '' when file extensions are valid or the input field is empty.
|
$this->assertSame('0', workshop::clean_file_extensions(0));
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
$this->assertSame('0', workshop::clean_file_extensions('0'));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertSame('doc, rtf, pdf', workshop::clean_file_extensions('*.Doc, RTF, PDF, .rtf'.PHP_EOL.'PDF '));
|
||||||
|
$this->assertSame('doc, rtf, pdf', 'doc, rtf, pdf');
|
||||||
|
}
|
||||||
|
|
||||||
$listofextensions = 'doc, jpg, mp3';
|
/**
|
||||||
$expected = '';
|
* Test validation of the list of file extensions.
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
*/
|
||||||
$this->assertEquals($expected, $actual);
|
public function test_invalid_file_extensions() {
|
||||||
|
$this->resetAfterTest(true);
|
||||||
|
|
||||||
$listofextensions = 'doc; ".jpg"; mp4 ...mp3';
|
$this->assertSame([], workshop::invalid_file_extensions('', ''));
|
||||||
$expected = '';
|
$this->assertSame([], workshop::invalid_file_extensions('', '.doc'));
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
$this->assertSame([], workshop::invalid_file_extensions('odt', ''));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertSame([], workshop::invalid_file_extensions('odt', '*'));
|
||||||
|
$this->assertSame([], workshop::invalid_file_extensions('odt', 'odt'));
|
||||||
|
$this->assertSame([], workshop::invalid_file_extensions('doc, odt, pdf', ['pdf', 'doc', 'odt']));
|
||||||
|
$this->assertSame([], workshop::invalid_file_extensions(['doc', 'odt', 'PDF'], ['.doc', '.pdf', '.odt']));
|
||||||
|
$this->assertSame([], workshop::invalid_file_extensions('*~ .docx, Odt PDF :doc .pdf', '*.docx *.odt *.pdf *.doc'));
|
||||||
|
$this->assertSame(['.00001-wtf-is-this'], workshop::invalid_file_extensions('docx tgz .00001-wtf-is-this', 'tgz docx'));
|
||||||
|
$this->assertSame(['.foobar', '.wtfisthis'], workshop::invalid_file_extensions(['.pdf', '.foobar', 'wtfisthis'], 'pdf'));
|
||||||
|
$this->assertSame([], workshop::invalid_file_extensions('', ''));
|
||||||
|
$this->assertSame(['.odt'], workshop::invalid_file_extensions(['.PDF', 'PDF', '.ODT'], 'jpg pdf png gif'));
|
||||||
|
$this->assertSame(['.odt'], workshop::invalid_file_extensions(['.PDF', 'PDF', '.ODT'], '.jpg,.pdf, .png .gif'));
|
||||||
|
$this->assertSame(['.exe', '.bat'], workshop::invalid_file_extensions(['.exe', '.odt', '.bat', ''], 'odt'));
|
||||||
|
}
|
||||||
|
|
||||||
// Error handling.
|
/**
|
||||||
$listofextensions = 'doc.jpg .mp3 .avi';
|
* Test checking file name against the list of allowed extensions.
|
||||||
$expected = get_string('err_notallowedfiletype', 'workshop', 'doc.jpg');
|
*/
|
||||||
// The function returns and error on the form-field: 'The file extension "doc.jpg" is not allowed'.
|
public function test_is_allowed_file_type() {
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
$this->resetAfterTest(true);
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
|
|
||||||
$listofextensions = 'doc, jpg, mp3, unusual';
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', ''));
|
||||||
$expected = get_string('err_notallowedfiletype', 'workshop', 'unusual');
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', ['']));
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
$this->assertFalse(workshop::is_allowed_file_type('README.txt', '0'));
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
|
|
||||||
$listofextensions = 'doc,; unusual1, unusual2';
|
$this->assertFalse(workshop::is_allowed_file_type('README.txt', 'xt'));
|
||||||
$expected = get_string('err_notallowedfiletype', 'workshop', 'unusual1');
|
$this->assertFalse(workshop::is_allowed_file_type('README.txt', 'old.txt'));
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
|
||||||
$this->assertEquals($expected, $actual);
|
|
||||||
|
|
||||||
$listofextensions = 'unusual1,; unsusual2, doc, jpg';
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', 'txt'));
|
||||||
$expected = get_string('err_notallowedfiletype', 'workshop', 'unusual1');
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', '.TXT'));
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
$this->assertTrue(workshop::is_allowed_file_type('README.TXT', 'txt'));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', '.txt .md'));
|
||||||
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', 'HTML TXT DOC RTF'));
|
||||||
|
$this->assertTrue(workshop::is_allowed_file_type('README.txt', ['HTML', '...TXT', 'DOC', 'RTF']));
|
||||||
|
|
||||||
$listofextensions = 'unusual1; unusual2; mp4';
|
$this->assertTrue(workshop::is_allowed_file_type('C:\Moodle\course-data.tar.gz', 'gzip zip 7z tar.gz'));
|
||||||
$expected = get_string('err_notallowedfiletype', 'workshop', 'unusual1');
|
$this->assertFalse(workshop::is_allowed_file_type('C:\Moodle\course-data.tar.gz', 'gzip zip 7z tar'));
|
||||||
$actual = workshop::check_allowed_file_types($listofextensions);
|
$this->assertTrue(workshop::is_allowed_file_type('~/course-data.tar.gz', 'gzip zip 7z gz'));
|
||||||
$this->assertEquals($expected, $actual);
|
$this->assertFalse(workshop::is_allowed_file_type('~/course-data.tar.gz', 'gzip zip 7z'));
|
||||||
|
|
||||||
|
$this->assertFalse(workshop::is_allowed_file_type('Alice on the beach.jpg.exe', 'png gif jpg bmp'));
|
||||||
|
$this->assertFalse(workshop::is_allowed_file_type('xfiles.exe.jpg', 'exe com bat sh'));
|
||||||
|
$this->assertFalse(workshop::is_allowed_file_type('solution.odt~', 'odt, xls'));
|
||||||
|
$this->assertTrue(workshop::is_allowed_file_type('solution.odt~', 'odt, odt~'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user