Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some flexibility on web submission, add archive submission #216

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions amd/build/submissionform.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions amd/build/submissionform.min.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions amd/src/submissionform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// This file is part of VPL for Moodle - http://vpl.dis.ulpgc.es/
//
// VPL for Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// VPL for Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with VPL for Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Submission form utility.
* @copyright 2024 Astor Bizard
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

define(['jquery'], function($) {
return {
setup: function() {
var updateHeaders = function(submitType) {
var archive = submitType == 'archive';
$('#id_headersubmitarchive').toggle(archive).toggleClass('collapsed', !archive);
$('#id_headersubmitfiles').toggle(!archive).toggleClass('collapsed', archive);
};
var $select = $('select[name="submitmethod"]');
$select.change(function() {
updateHeaders($select.val());
});
updateHeaders($select.val());
}
};
});
107 changes: 90 additions & 17 deletions forms/submission.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
$instance = $vpl->get_instance();
$vpl->print_header( get_string( 'submission', VPL ) );
$vpl->print_view_tabs( basename( __FILE__ ) );
$mform = new mod_vpl_submission_form( 'submission.php', $vpl );
$mform = new mod_vpl_submission_form( 'submission.php', $vpl, $userid );
if ($mform->is_cancelled()) {
vpl_inmediate_redirect( vpl_mod_href( 'view.php', 'id', $id ) );
die();
Expand All @@ -76,20 +76,94 @@
die();
}
\mod_vpl\util\phpconfig::increase_memory_limit();
$rfn = $vpl->get_required_fgm();
$reqfiles = $rfn->getFileList();
$files = [];
for ($i = 0; $i < $instance->maxfiles; $i ++) {
$attribute = 'file' . $i;
$name = $mform->get_new_filename( $attribute );
$data = $mform->get_file_content( $attribute );
if ($data !== false && $name !== false) {
$prevsub = $vpl->last_user_submission( $userid );
$firstsub = ($prevsub === false);
if (!$firstsub) {
$prevsubfiles = (new mod_vpl_submission( $vpl, $prevsub ))->get_submitted_fgm()->getAllFiles();
}
if ($fromform->submitmethod == 'archive') {
if (!$firstsub && $fromform->archiveaction == 'replace') {
// Use files of previous submission.
foreach ($prevsubfiles as $subfilename => $subfilecontent) {
$files[$subfilename] = $subfilecontent;
}
}
// Open archive.
$zipname = $mform->save_temp_file( 'archive' );
$zip = new ZipArchive();
$zip->open($zipname);
$subreqfiles = [];
$subotherfiles = [];
// Read archive and split between required / additional files.
for ($i = 0; $i < $zip->numFiles; $i++) {
$filename = $zip->statIndex($i)['name'];
if (substr($filename, -1) == '/') { // Directory.
continue;
}
$filecontent = file_get_contents('zip://' . $zipname . '#' . $filename);
// Autodetect text file encode if not binary.
if (! vpl_is_binary( $name )) {
$encode = mb_detect_encoding( $data, 'UNICODE, UTF-16, UTF-8, ISO-8859-1', true );
if ($encode > '') { // If code detected.
$data = iconv( $encode, 'UTF-8', $data );
if (! vpl_is_binary( $filename )) {
$encoding = mb_detect_encoding( $filecontent, 'UNICODE, UTF-16, UTF-8, ISO-8859-1', true );
if ($encoding > '') { // If code detected.
$filecontent = iconv( $encoding, 'UTF-8', $filecontent );
}
} else {
if (in_array($filename . '.b64', $reqfiles)) {
$filename = $filename . '.b64';
$filecontent = base64_encode($filecontent);
}
}
if (in_array($filename, $reqfiles)) {
$subreqfiles[$filename] = $filecontent;
} else {
$subotherfiles[$filename] = $filecontent;
}
}
foreach ($reqfiles as $reqfile) {
if (isset($subreqfiles[$reqfile])) {
$files[$reqfile] = $subreqfiles[$reqfile];
}
}
foreach ($subotherfiles as $filename => $filecontent) {
$files[$filename] = $filecontent;
}
// Close archive.
$zip->close();
unlink($zipname);
} else {
for ($i = 0; $i < $instance->maxfiles; $i ++) {
$field = 'file' . $i;
if (!$firstsub && isset($fromform->{$field . 'action'}) && $fromform->{$field . 'action'} == 'keep') {
$filename = $fromform->{$field . 'name'};
$files[$filename] = $prevsubfiles[$filename];
} else {
if (isset($fromform->{$field . 'action'}) && $fromform->{$field . 'action'} == 'replace'
|| !empty($fromform->{$field . 'rename'}) && !empty($fromform->{$field . 'name'})) {
$name = $fromform->{$field . 'name'};
} else {
$name = $mform->get_new_filename( $field );
}
$data = $mform->get_file_content( $field );
if ($data !== false && $name !== false) {
// Autodetect text file encode if not binary.
if (! vpl_is_binary( $name )) {
$encode = mb_detect_encoding( $data, 'UNICODE, UTF-16, UTF-8, ISO-8859-1', true );
if ($encode > '') { // If code detected.
$data = iconv( $encode, 'UTF-8', $data );
}
$files[$name] = $data;
} else {
if (in_array($name . '.b64', $reqfiles)) {
$files[$name . '.b64'] = base64_encode($data);
} else {
$files[$name] = $data;
}
}
}
}
$files[$name] = $data;
}
}
$errormessage = '';
Expand All @@ -102,15 +176,14 @@

// If evaluate on submission.
if ($instance->evaluate && $instance->evaluateonsubmission) {
vpl_redirect( vpl_mod_href( 'forms/evaluation.php', 'id', $id, 'userid', $userid ),
get_string( 'saved', VPL ));
$redirecturl = vpl_mod_href( 'forms/evaluation.php', 'id', $id, 'userid', $userid );
} else {
$redirecturl = vpl_mod_href( 'forms/submissionview.php', 'id', $id, 'userid', $userid );
}
vpl_redirect( vpl_mod_href( 'forms/submissionview.php', 'id', $id, 'userid', $userid ),
get_string( 'saved', VPL ));
vpl_redirect( $redirecturl, get_string( 'saved', VPL ));
} else {
vpl_notice( get_string( 'notsaved', VPL ) );
vpl_redirect( vpl_mod_href( 'forms/submission.php', 'id', $id, 'userid', $userid ),
$errormessage);
get_string( 'notsaved', VPL ) . '<br>' . $errormessage, 'error');
}
}

Expand Down
102 changes: 91 additions & 11 deletions forms/submission_form.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@

class mod_vpl_submission_form extends moodleform {
protected $vpl;
protected $userid;
protected function getinternalform() {
return $this->_form;
}
public function __construct($page, $vpl) {
public function __construct($page, $vpl, $userid) {
$this->vpl = $vpl;
$this->userid = $userid;
parent::__construct( $page );
}
protected function definition() {
global $CFG;
global $CFG, $OUTPUT, $PAGE;
$mform = & $this->_form;
$mform->addElement( 'header', 'headersubmission', get_string( 'submission', VPL ) );
// Identification info.
$mform->addElement( 'hidden', 'id' );
$mform->setType( 'id', PARAM_INT );
Expand All @@ -54,18 +55,97 @@ protected function definition() {
] );
$mform->setType( 'comments', PARAM_TEXT );

// Files upload.
$submission = $this->vpl->last_user_submission( $this->userid );
$firstsub = ($submission === false);
$instance = $this->vpl->get_instance();
$files = $this->vpl->get_required_files();
$nfiles = count( $files );
for ($i = 0; $i < $instance->maxfiles; $i ++) {
$reqfiles = $this->vpl->get_required_files();

$mform->addElement( 'select', 'submitmethod', get_string( 'submitmethod', VPL ),
[ 'archive' => get_string( 'archive', VPL ), 'files' => get_string( 'files' ) ] );
$mform->setDefault( 'submitmethod', count($reqfiles) == 1 ? 'files' : 'archive' );

$mform->addElement( 'header', 'headersubmitarchive', get_string( 'submitarchive', VPL ) );

$filepickertitle = get_string( 'submitarchive', VPL );
if (!$firstsub) {
$mform->addElement( 'radio', 'archiveaction', $filepickertitle,
get_string( 'archivereplacedelete', VPL ), 'replacedelete');
$mform->addElement( 'radio', 'archiveaction', '',
get_string( 'archivereplace', VPL ), 'replace' );
$mform->disabledIf( 'archiveaction', 'submitmethod', 'neq', 'archive' );
$filepickertitle = null;
}
$mform->addElement( 'filepicker', 'archive', $filepickertitle, null, [ 'accepted_types' => '.zip' ] );
$mform->disabledIf( 'archive', 'submitmethod', 'neq', 'archive' );

$mform->addElement( 'header', 'headersubmitfiles', get_string( 'submitfiles', VPL ) );

// Files upload.
$i = 0;
$requiredicon = $OUTPUT->pix_icon('requestedfiles', get_string('required'), 'mod_vpl', [ 'class' => 'text-info' ]);
foreach ($reqfiles as $reqfile) {
$field = 'file' . $i;
if ($i < $nfiles) {
$mform->addElement( 'filepicker', $field, $files[$i] );
} else {
$mform->addElement( 'filepicker', $field, get_string( 'anyfile', VPL ) );
$filepickertitle = $requiredicon . $reqfile;
if (!$firstsub) {
$mform->addElement( 'radio', $field . 'action', $filepickertitle,
get_string( 'keepcurrentfile', VPL ), 'keep');
$mform->addElement( 'radio', $field . 'action', '',
get_string( 'replacefile', VPL ), 'replace' );
$mform->disabledIf( $field . 'action', 'submitmethod', 'neq', 'files' );
$mform->addElement( 'hidden', $field . 'name', $reqfile );
$mform->setType( $field . 'name', PARAM_RAW );
$filepickertitle = null;
}
$mform->addElement( 'filepicker', $field, $filepickertitle );
$mform->disabledIf( $field, 'submitmethod', 'neq', 'files' );
if (!$firstsub) {
$mform->disabledIf( $field, $field . 'action', 'neq', 'replace' );
}
$i++;
}
if (!$firstsub) {
$subfiles = (new mod_vpl_submission( $this->vpl, $submission ))->get_submitted_fgm()->getFileList();
foreach ($subfiles as $subfile) {
if (!in_array($subfile, $reqfiles)) {
$field = 'file' . $i;
$mform->addElement( 'radio', $field . 'action', $subfile, get_string( 'keepcurrentfile', VPL ), 'keep');
$mform->addElement( 'radio', $field . 'action', '', get_string( 'deletefile', VPL ), 'delete' );
$mform->addElement( 'radio', $field . 'action', '', get_string( 'replacefile', VPL ), 'replace' );
$mform->disabledIf( $field . 'action', 'submitmethod', 'neq', 'files' );
$mform->addElement( 'hidden', $field . 'name', $subfile );
$mform->setType( $field . 'name', PARAM_PATH );
$mform->addElement( 'filepicker', $field );
$mform->disabledIf( $field, 'submitmethod', 'neq', 'files' );
$mform->disabledIf( $field, $field . 'action', 'neq', 'replace' );
$i++;
}
}
}

while ($i < $instance->maxfiles) {
$field = 'file' . $i;
$mform->addElement( 'filepicker', $field, get_string( 'anyfile', VPL ) );
$mform->disabledIf( $field, 'submitmethod', 'neq', 'files' );
$mform->addGroup([
$mform->createElement('advcheckbox', $field . 'rename', get_string('renameuploadedfile', VPL)),
$mform->createElement('text', $field . 'name', get_string('new_file_name', VPL),
[ 'size' => 32, 'placeholder' => get_string('new_file_name', VPL) ]),
]);
$mform->setType( $field . 'name', PARAM_PATH );
$mform->setDefault( $field . 'rename', 0 );
$mform->disabledIf( $field . 'name', $field . 'rename' );
$mform->disabledIf( $field . 'name', $field . 'name', 'neq', 'files' );
$i++;
}
$this->add_action_buttons( true, get_string( 'submit' ) );

$PAGE->requires->js_call_amd('mod_vpl/submissionform', 'setup');
}
public function set_data($data) {
for ($i = 0; $i < $this->vpl->get_instance()->maxfiles; $i++) {
$data->{'file'.$i.'action'} = 'keep';
$data->{'archiveaction'} = 'replacedelete';
}
parent::set_data($data);
}
}
10 changes: 10 additions & 0 deletions lang/en/vpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
$string['allfiles'] = 'All files';
$string['allsubmissions'] = 'All submissions';
$string['anyfile'] = 'Any file';
$string['archive'] = 'Archive';
$string['archivereplace'] = 'Replace only files present in archive';
$string['archivereplacedelete'] = 'Replace all files and delete files not present in archive';
$string['attemptnumber'] = 'Attempt number {$a}';
$string['autodetect'] = 'Autodetect';
$string['automaticevaluation'] = 'Automatic evaluation';
Expand Down Expand Up @@ -89,6 +92,7 @@
$string['delete_file_fq'] = "delete '{\$a}' file?";
$string['delete_file_q'] = 'Delete file?';
$string['deleteallsubmissions'] = 'Delete all submissions';
$string['deletefile'] = 'Delete file';
$string['description'] = 'Description';
$string['diff'] = 'diff';
$string['directory_not_renamed'] = 'Directory \'{$a}\' has not been renamed';
Expand Down Expand Up @@ -175,6 +179,7 @@
$string['jail_servers_config'] = 'Execution servers config';
$string['jail_servers_description'] = 'Write a line for each server';
$string['joinedfiles'] = 'Joined selected files';
$string['keepcurrentfile'] = 'Keep current file';
$string['keepfiles'] = 'Files to keep when running';
$string['keyboard'] = 'Keyboard';
$string['lasterror'] = 'Last error info';
Expand Down Expand Up @@ -294,7 +299,9 @@
$string['rename'] = 'Rename';
$string['rename_file'] = 'Rename file';
$string['rename_directory'] = 'Rename directory';
$string['renameuploadedfile'] = 'Rename uploaded file';
$string['replace_find'] = 'Replace/Find';
$string['replacefile'] = 'Replace contents';
$string['replacenewer'] = "A newer version was already saved.\nDo you want to replace the newer version with this one?";
$string['requestedfiles'] = 'Requested files';
$string['requirednet'] = 'Require network address';
Expand Down Expand Up @@ -347,6 +354,9 @@
$string['submissionselection'] = 'Submission selection';
$string['submissionslist'] = 'Submissions list';
$string['submissionview'] = 'Submission view';
$string['submitarchive'] = 'Submit archive';
$string['submitfiles'] = 'Submit files';
$string['submitmethod'] = 'Submit method';
$string['submittedby'] = 'Submitted by {$a}';
$string['submittedon'] = 'Submitted on';
$string['submittedonp'] = 'Submitted on {$a}';
Expand Down
2 changes: 1 addition & 1 deletion locallib.php
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,7 @@ function vpl_get_overrides($vplid) {
* @param array $parms Parameters to pass to the function
* @return mixed Value returned by the function or throw exception
*/
function vpl_call_with_lock(string $locktype, string $resource, string $function, array $parms) {
function vpl_call_with_lock(string $locktype, string $resource, string $function, array & $parms) {
$lockfactory = \core\lock\lock_config::get_lock_factory($locktype);
if ($lock = $lockfactory->get_lock($resource, VPL_LOCK_TIMEOUT)) {
try {
Expand Down
1 change: 0 additions & 1 deletion vpl.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,6 @@ public function pass_submission_restriction(& $alldata, & $error) {
public static function internal_add_submission($vpl, $userid, & $files, $comments, & $error) {
global $USER, $DB;
if (! $vpl->pass_submission_restriction( $files, $error )) {
$error = get_string('notavailable');
return false;
}
$group = false;
Expand Down