diff --git a/amd/build/submissionform.min.js b/amd/build/submissionform.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..6dbfeb52d52ab02230b3d5689ae72aa51a7e71b9
--- /dev/null
+++ b/amd/build/submissionform.min.js
@@ -0,0 +1 @@
+define(["jquery"],function(a){function b(b){"archive"==b?(a("#id_headersubmitarchive").show().removeClass("collapsed"),a("#id_headersubmitfiles").hide()):(a("#id_headersubmitarchive").hide(),a("#id_headersubmitfiles").show().removeClass("collapsed"))}return{setup:function(){var c=a('select[name="submitmethod"]');c.change(function(){b(c.val())}),b(c.val())}}});
\ No newline at end of file
diff --git a/amd/src/submissionform.js b/amd/src/submissionform.js
new file mode 100644
index 0000000000000000000000000000000000000000..87314689eb8eeff494983846af0d4d1d00cd1b15
--- /dev/null
+++ b/amd/src/submissionform.js
@@ -0,0 +1,21 @@
+define(['jquery'], function($) {
+    function updateHeaders(submitType) {
+        if (submitType == 'archive') {
+            $('#id_headersubmitarchive').show().removeClass('collapsed');
+            $('#id_headersubmitfiles').hide();
+        } else {
+            $('#id_headersubmitarchive').hide();
+            $('#id_headersubmitfiles').show().removeClass('collapsed');
+        }
+    }
+
+    return {
+        setup: function() {
+            var $select = $('select[name="submitmethod"]');
+            $select.change(function() {
+                updateHeaders($select.val());
+            });
+            updateHeaders($select.val());
+        }
+    };
+});
\ No newline at end of file
diff --git a/forms/submission.php b/forms/submission.php
index 984f8b5027b7244085da6848267c2ea43c4dcba3..019133ba847f95f1c70a428dac445ea9fcc47c2e 100644
--- a/forms/submission.php
+++ b/forms/submission.php
@@ -56,7 +56,7 @@ if ($userid == $USER->id) { // Make own submission.
 $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();
@@ -72,23 +72,79 @@ if ($fromform = $mform->get_data()) {
     $rfn = $vpl->get_fgm('required');
     $reqfiles = $rfn->getFileList();
     $files = array ();
-    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);
+    $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 = array();
+        $subotherfiles = array();
+        // Read archive and split between required / additional files.
+        for ($i = 0; $i < $zip->numFiles; $i++) {
+            $filename = $zip->statIndex($i)['name'];
+            $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);
                 }
-                $files [$name] = $data;
+            }
+            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 (in_array($name . '.b64', $reqfiles)) {
-                    $files [$name . '.b64'] = base64_encode($data);
-                } else {
-                    $files [$name] = $data;
+                $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;
+                        }
+                    }
                 }
             }
         }
diff --git a/forms/submission_form.php b/forms/submission_form.php
index 6096ec5e2911568d0a35390f1bdf7e5e5c1590b4..1537cdb448690cf36b73c4fd23bfc68895d25932 100644
--- a/forms/submission_form.php
+++ b/forms/submission_form.php
@@ -28,20 +28,22 @@ defined( 'MOODLE_INTERNAL' ) || die();
 global $CFG;
 require_once($CFG->libdir.'/formslib.php');
 require_once(dirname(__FILE__).'/../locallib.php');
+require_once(dirname(__FILE__).'/edit.class.php');
 
 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;
         $mform = & $this->_form;
-        $mform->addElement( 'header', 'headersubmission', get_string( 'submission', VPL ) );
         // Identification info.
         $mform->addElement( 'hidden', 'id' );
         $mform->setType( 'id', PARAM_INT );
@@ -54,18 +56,85 @@ class mod_vpl_submission_form extends moodleform {
         ) );
         $mform->setType( 'comments', PARAM_TEXT );
 
+        $firstsub = ($this->vpl->last_user_submission( $this->userid ) === false);
+
+        $mform->addElement( 'select', 'submitmethod', get_string( 'submitmethod', VPL ),
+                array('archive' => get_string( 'archive', VPL ), 'files' => get_string( 'files' )) );
+
+        $mform->addElement( 'header', 'headersubmitarchive', get_string( 'submitarchive', VPL ) );
+
+        if (!$firstsub) {
+            $mform->addElement( 'radio', 'archiveaction', get_string( 'submitarchive', VPL ),
+                    get_string( 'archivereplacedelete', VPL ), 'replacedelete');
+            $mform->addElement( 'radio', 'archiveaction', '',
+                    get_string( 'archivereplace', VPL ), 'replace' );
+            $mform->disabledIf( 'archiveaction', 'submitmethod', 'neq', 'archive' );
+        }
+        $mform->addElement( 'filepicker', 'archive', null, null, array('accepted_types' => '.zip') );
+        $mform->disabledIf( 'archive', 'submitmethod', 'neq', 'archive' );
+
+        $mform->addElement( 'header', 'headersubmitfiles', get_string( 'submitfiles', VPL ) );
+
         // Files upload.
         $instance = $this->vpl->get_instance();
-        $files = $this->vpl->get_files('required');
-        $nfiles = count( $files );
-        for ($i = 0; $i < $instance->maxfiles; $i ++) {
+        $reqfiles = $this->vpl->get_files('required');
+        $i = 0;
+        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 ) );
+            if (!$firstsub) {
+                $mform->addElement( 'radio', $field . 'action', $reqfile,
+                        get_string( 'keepcurrentfile', VPL ), 'keep');
+                $mform->addElement( 'radio', $field . 'action', '',
+                        get_string( 'replacefile', VPL ), 'replace' );
+                $mform->disabledIf( $field . 'action', 'submitmethod', 'neq', 'files' );
             }
+            $mform->addElement( 'filepicker', $field );
+            $mform->disabledIf( $field, 'submitmethod', 'neq', 'files' );
+            if (!$firstsub) {
+                $mform->disabledIf( $field, $field . 'action', 'neq', 'replace' );
+            }
+            $i++;
+        }
+        $submission = $this->vpl->last_user_submission( $this->userid );
+        if ($submission !== false) {
+            $subfiles = (new mod_vpl_submission( $this->vpl, $submission ))->get_submitted_fgm()->getFileList();
+            foreach ($subfiles as $subfile) {
+                if (!in_array($subfile, $reqfiles)) {
+                    $field = 'file' . $i;
+                    if (!$firstsub) {
+                        $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_RAW );
+                    }
+                    $mform->addElement( 'filepicker', $field );
+                    $mform->disabledIf( $field, 'submitmethod', 'neq', 'files' );
+                    if (!$firstsub) {
+                        $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' );
+            $i++;
         }
         $this->add_action_buttons( true, get_string( 'submit' ) );
+
+        global $PAGE;
+        $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);
     }
 }
diff --git a/lang/en/vpl.php b/lang/en/vpl.php
index 19b8ab3b6b47ee9a9d87a16f30481c551084b2f1..848174cc552706afaa131e4e37bb081dd241da43 100644
--- a/lang/en/vpl.php
+++ b/lang/en/vpl.php
@@ -28,6 +28,9 @@ $string ['allfiles'] = 'All files';
 $string ['allowshowprevious'] = 'Allow student show previous submissions';
 $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';
@@ -80,6 +83,7 @@ $string ['delete'] = 'Delete';
 $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 ['discard_submission_period'] = 'Discard submission period';
@@ -159,6 +163,7 @@ $string ['jail_servers'] = 'Execution servers list';
 $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';
@@ -252,6 +257,7 @@ $string ['removegrade'] = 'Remove grade';
 $string ['rename'] = 'Rename';
 $string ['rename_file'] = 'Rename file';
 $string ['replace_find'] = 'Replace/Find';
+$string ['replacefile'] = 'Replace';
 $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'] = 'Allowed submission from net';
@@ -301,6 +307,9 @@ $string ['submissionscharts'] = 'Submissions charts';
 $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}';