Skip to content

Commit

Permalink
MDL-50794 workshop: Allow restricting submitted file types
Browse files Browse the repository at this point in the history
It was reported at the Open University that there have been some
problems with the formats of files submitted by students. Not all
students completed their outlines as a Word .doc (despite this being the
format of the template provided). Some formats (e.g. .pages) couldn’t be
translated by some of the reviewing students. Therefore, they were
unable to provide a review and the submitting students not receiving any
comments.

This patch allows the teacher to define list of allowed file types that
can be attached to submitted work and/or overall feedback in the
workshop.
  • Loading branch information
mkassaei authored and mudrd8mz committed Feb 25, 2016
1 parent 03b8b55 commit 1a28221
Show file tree
Hide file tree
Showing 13 changed files with 296 additions and 11 deletions.
4 changes: 2 additions & 2 deletions mod/workshop/backup/moodle2/backup_workshop_stepslib.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ protected function define_structure() {
'instructauthorsformat', 'instructreviewers',
'instructreviewersformat', 'timemodified', 'phase', 'useexamples',
'usepeerassessment', 'useselfassessment', 'grade', 'gradinggrade',
'strategy', 'evaluation', 'gradedecimals', 'nattachments',
'strategy', 'evaluation', 'gradedecimals', 'nattachments', 'submissionfiletypes',
'latesubmissions', 'maxbytes', 'examplesmode', 'submissionstart',
'submissionend', 'assessmentstart', 'assessmentend',
'conclusion', 'conclusionformat', 'overallfeedbackmode',
'overallfeedbackfiles', 'overallfeedbackmaxbytes'));
'overallfeedbackfiles', 'overallfeedbackfiletypes', 'overallfeedbackmaxbytes'));

// assessment forms definition
$this->add_subplugin_structure('workshopform', $workshop, true);
Expand Down
2 changes: 2 additions & 0 deletions mod/workshop/db/install.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<FIELD NAME="evaluation" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" COMMENT="The recently used grading evaluation method"/>
<FIELD NAME="gradedecimals" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Number of digits that should be shown after the decimal point when displaying grades"/>
<FIELD NAME="nattachments" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Number of required submission attachments"/>
<FIELD NAME="submissionfiletypes" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="comma separated list of file extensions"/>
<FIELD NAME="latesubmissions" TYPE="int" LENGTH="2" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Allow submitting the work after the deadline"/>
<FIELD NAME="maxbytes" TYPE="int" LENGTH="10" NOTNULL="false" DEFAULT="100000" SEQUENCE="false" COMMENT="Maximum size of the one attached file"/>
<FIELD NAME="examplesmode" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="0 = example assessments are voluntary, 1 = examples must be assessed before submission, 2 = examples are available after own submission and must be assessed before peer/self assessment phase"/>
Expand All @@ -38,6 +39,7 @@
<FIELD NAME="conclusionformat" TYPE="int" LENGTH="3" NOTNULL="true" DEFAULT="1" SEQUENCE="false" COMMENT="The format of the conclusion field content."/>
<FIELD NAME="overallfeedbackmode" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="1" SEQUENCE="false" COMMENT="Mode of the overall feedback support."/>
<FIELD NAME="overallfeedbackfiles" TYPE="int" LENGTH="3" NOTNULL="false" DEFAULT="0" SEQUENCE="false" COMMENT="Number of allowed attachments to the overall feedback."/>
<FIELD NAME="overallfeedbackfiletypes" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="comma separated liat of file extensions"/>
<FIELD NAME="overallfeedbackmaxbytes" TYPE="int" LENGTH="10" NOTNULL="false" DEFAULT="100000" SEQUENCE="false" COMMENT="Maximum size of one file attached to the overall feedback."/>
</FIELDS>
<KEYS>
Expand Down
27 changes: 26 additions & 1 deletion mod/workshop/db/upgrade.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
* @return bool result
*/
function xmldb_workshop_upgrade($oldversion) {
global $CFG;
global $CFG, $DB;

// Moodle v2.8.0 release upgrade line.
// Put any upgrade step following this.
Expand All @@ -46,5 +46,30 @@ function xmldb_workshop_upgrade($oldversion) {
// Moodle v3.0.0 release upgrade line.
// Put any upgrade step following this.

$dbman = $DB->get_manager();

if ($oldversion < 2016022200) {

// Define field submissionfiletypes to be added to workshop.
$table = new xmldb_table('workshop');
$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)) {
$dbman->add_field($table, $field);
}

// Define field overallfeedbackfiletypes to be added to workshop.
$field = new xmldb_field('overallfeedbackfiletypes',
XMLDB_TYPE_CHAR, '255', null, null, null, null, 'overallfeedbackfiles');

// Conditionally launch add field overallfeedbackfiletypes.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}

// Workshop savepoint reached.
upgrade_mod_savepoint(true, 2016022200, 'workshop');
}
return true;
}
1 change: 1 addition & 0 deletions mod/workshop/exsubmission.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
'trusttext' => true,
'subdirs' => false,
'maxfiles' => $maxfiles,
'filetypes' => $workshop->submissionfiletypes,
'maxbytes' => $maxbytes,
'context' => $workshop->context
);
Expand Down
48 changes: 48 additions & 0 deletions mod/workshop/form/assessment_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,52 @@ protected function definition_inner(&$mform) {
public function is_editable() {
return !$this->_form->isFrozen();
}

/**
* Validate incoming data.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);

if (isset($data['feedbackauthorattachment_filemanager'])) {
$draftitemid = $data['feedbackauthorattachment_filemanager'];

// If we have draft files, then make sure they are the correct ones.
if ($draftfiles = file_get_drafarea_files($draftitemid)) {

if (!$validfileextensions = workshop::get_array_of_file_extensions($this->workshop->overallfeedbackfiletypes)) {
return $errors;
}
$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) {
$bigfiles .= get_string('err_maxbytes', 'workshop', $a) . '<br/>';
}
}
if ($bigfiles || $wrongfileextensions) {
$errors['feedbackauthorattachment_filemanager'] = $bigfiles . $wrongfileextensions;
}
}
}
return $errors;
}

}
7 changes: 7 additions & 0 deletions mod/workshop/lang/en/workshop.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
$string['allocationdone'] = 'Allocation done';
$string['allocationerror'] = 'Allocation error';
$string['allocationconfigured'] = 'Allocation configured';
$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['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['allsubmissions'] = 'All submissions ({$a})';
$string['alreadygraded'] = 'Already graded';
$string['areaconclusion'] = 'Conclusion text';
Expand Down Expand Up @@ -100,6 +104,9 @@
$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_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_notallowedfiletype'] = 'The file extension "{$a}" is not 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['evaluation'] = 'Grading evaluation';
$string['evaluationmethod'] = 'Grading evaluation method';
Expand Down
43 changes: 42 additions & 1 deletion mod/workshop/locallib.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -124,6 +123,9 @@ class workshop {
/** @var int number of allowed submission attachments and the files embedded into submission */
public $nattachments;

/** @var string list of allowed file types that are allowed to be embedded into submission */
public $submissionfiletypes = null;

/** @var bool allow submitting the work after the deadline */
public $latesubmissions;

Expand Down Expand Up @@ -160,6 +162,9 @@ class workshop {
/** @var int maximum number of overall feedback attachments */
public $overallfeedbackfiles;

/** @var string list of allowed file types that can be attached to the overall feedback */
public $overallfeedbackfiletypes = null;

/** @var int maximum size of one file attached to the overall feedback */
public $overallfeedbackmaxbytes;

Expand Down Expand Up @@ -412,6 +417,40 @@ public static function timestamp_formats($timestamp) {
return $a;
}

/**
* Split a list of file extensions to an array
* @param string $listofextensions
* @return array of extensions
*/
public static function get_array_of_file_extensions($listofextensions) {
return preg_split("/[\s,;:\"']+/", strtolower($listofextensions), null, PREG_SPLIT_NO_EMPTY);
}

/**
* Check allowed file types and return an error when invalid file extensions found in the list
*
* @param string $extensionlist
*/
public static function check_allowed_file_types($extensionlist) {
if (!$extensionlist) {
return '';
}

if ($extensions = self::get_array_of_file_extensions($extensionlist)) {
$coreextensions = array_keys(get_mimetypes_array());
foreach ($extensions as $ext) {
$ext = ltrim($ext, '.');
if (!$ext) {
continue;
}
// Use strtolower(), because all extensions are in lower case.
if (!in_array($ext, $coreextensions)) {
return get_string('err_notallowedfiletype', 'workshop', $ext);
}
}
}
}

////////////////////////////////////////////////////////////////////////////////
// Workshop API //
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2371,6 +2410,7 @@ public function overall_feedback_content_options() {
return array(
'subdirs' => 0,
'maxbytes' => $this->overallfeedbackmaxbytes,
'filetypes' => $this->overallfeedbackfiletypes,
'maxfiles' => $this->overallfeedbackfiles,
'changeformat' => 1,
'context' => $this->context,
Expand All @@ -2386,6 +2426,7 @@ public function overall_feedback_attachment_options() {
return array(
'subdirs' => 1,
'maxbytes' => $this->overallfeedbackmaxbytes,
'filetypes' => $this->overallfeedbackfiletypes,
'maxfiles' => $this->overallfeedbackfiles,
'return_types' => FILE_INTERNAL,
);
Expand Down
40 changes: 35 additions & 5 deletions mod/workshop/mod_form.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php

// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -65,7 +64,7 @@ public function definition() {

// Workshop name
$label = get_string('workshopname', 'workshop');
$mform->addElement('text', 'name', $label, array('size'=>'64'));
$mform->addElement('text', 'name', $label, array('size' => '64'));
if (!empty($CFG->formatstringstriptags)) {
$mform->setType('name', PARAM_TEXT);
} else {
Expand Down Expand Up @@ -118,7 +117,7 @@ public function definition() {
$mform->addRule('gradinggradepass', null, 'numeric', null, 'client');

$options = array();
for ($i=5; $i>=0; $i--) {
for ($i = 5; $i >= 0; $i--) {
$options[$i] = $i;
}
$label = get_string('gradedecimals', 'workshop');
Expand All @@ -133,16 +132,24 @@ public function definition() {
workshop::instruction_editors_options($this->context));

$options = array();
for ($i=7; $i>=0; $i--) {
for ($i = 7; $i >= 0; $i--) {
$options[$i] = $i;
}
$label = get_string('nattachments', 'workshop');
$mform->addElement('select', 'nattachments', $label, $options);
$mform->setDefault('nattachments', 1);

$label = get_string('allowedfiletypesforsubmission', 'workshop');
$mform->addElement('text', 'submissionfiletypes', $label, array('maxlength' => 255, 'size' => 64));
$mform->addHelpButton('submissionfiletypes', 'allowedfiletypesforsubmission', 'workshop');
$mform->setType('submissionfiletypes', PARAM_TEXT);
$mform->addRule('submissionfiletypes', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->disabledIf('submissionfiletypes', 'nattachments', 'eq', 0);

$options = get_max_upload_sizes($CFG->maxbytes, $this->course->maxbytes, 0, $workshopconfig->maxbytes);
$mform->addElement('select', 'maxbytes', get_string('maxbytes', 'workshop'), $options);
$mform->setDefault('maxbytes', $workshopconfig->maxbytes);
$mform->disabledIf('maxbytes', 'nattachments', 'eq', 0);

$label = get_string('latesubmissions', 'workshop');
$text = get_string('latesubmissions_desc', 'workshop');
Expand Down Expand Up @@ -179,6 +186,13 @@ public function definition() {
$mform->setDefault('overallfeedbackfiles', 0);
$mform->disabledIf('overallfeedbackfiles', 'overallfeedbackmode', 'eq', 0);

$label = get_string('allowedfiletypesforoverallfeedback', 'workshop');
$mform->addElement('text', 'overallfeedbackfiletypes', $label, array('maxlength' => 255, 'size' => 64));
$mform->addHelpButton('overallfeedbackfiletypes', 'allowedfiletypesforoverallfeedback', 'workshop');
$mform->setType('overallfeedbackfiletypes', PARAM_TEXT);
$mform->addRule('overallfeedbackfiletypes', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->disabledIf('overallfeedbackfiletypes', 'overallfeedbackfiles', 'eq', 0);

$options = get_max_upload_sizes($CFG->maxbytes, $this->course->maxbytes);
$mform->addElement('select', 'overallfeedbackmaxbytes', get_string('overallfeedbackmaxbytes', 'workshop'), $options);
$mform->setDefault('overallfeedbackmaxbytes', $workshopconfig->maxbytes);
Expand Down Expand Up @@ -229,7 +243,7 @@ public function definition() {

// Common module settings, Restrict availability, Activity completion etc. ----
$features = array('groups' => true, 'groupings' => true,
'outcomes'=>true, 'gradecat'=>false, 'idnumber'=>false);
'outcomes' => true, 'gradecat' => false, 'idnumber' => false);

$this->standard_coursemodule_elements();

Expand Down Expand Up @@ -347,6 +361,22 @@ public function definition_after_data() {
public function validation($data, $files) {
$errors = parent::validation($data, $files);

// Check the input of submissionfiletypes field.
if ($data['submissionfiletypes']) {
$invalidextension = workshop::check_allowed_file_types($data['submissionfiletypes']);
if ($invalidextension) {
$errors['submissionfiletypes'] = $invalidextension;
}
}

// Check the input of overallfeedbackfiletypes field.
if ($data['overallfeedbackfiletypes']) {
$invalidextension = workshop::check_allowed_file_types($data['overallfeedbackfiletypes']);
if ($invalidextension) {
$errors['overallfeedbackfiletypes'] = $invalidextension;
}
}

// check the phases borders are valid
if ($data['submissionstart'] > 0 and $data['submissionend'] > 0 and $data['submissionstart'] >= $data['submissionend']) {
$errors['submissionend'] = get_string('submissionendbeforestart', 'mod_workshop');
Expand Down
5 changes: 4 additions & 1 deletion mod/workshop/submission.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,20 @@
require_once(dirname(__FILE__).'/submission_form.php');

$maxfiles = $workshop->nattachments;
$filetypes = $workshop->submissionfiletypes;
$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, 'maxbytes' => $maxbytes, 'return_types' => FILE_INTERNAL);
$attachmentopts = array('subdirs' => true, 'maxfiles' => $maxfiles, 'filetypes' => $filetypes, 'maxbytes' => $maxbytes,
'return_types' => FILE_INTERNAL);
$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,
Expand Down
Loading

0 comments on commit 1a28221

Please sign in to comment.