Index: mod/assignment/type/upload/assignment.class.php
===================================================================
--- mod/assignment/type/upload/assignment.class.php	(revision 30)
+++ mod/assignment/type/upload/assignment.class.php	(revision 39)
@@ -23,6 +23,7 @@
  */
 
 require_once(dirname(__FILE__).'/upload_form.php');
+require_once(dirname(__FILE__).'/bulkupload_form.php');
 require_once($CFG->libdir . '/portfoliolib.php');
 require_once($CFG->dirroot . '/mod/assignment/lib.php');
 
@@ -427,6 +428,9 @@
             case 'uploadresponse':
                 $this->upload_responsefile($mform, $filemanager_options);
                 break;
+            case 'uploadresponses':
+                $this->upload_responsefiles($mform, $filemanager_options);
+                break;
             case 'uploadfile':
                 $this->upload_file($mform, $filemanager_options);
             case 'savenotes':
@@ -520,6 +524,155 @@
         die;
     }
 
+    function upload_responsefiles($mform, $options) {
+        global $CFG, $USER, $OUTPUT, $PAGE;
+
+        $userid = $USER->id;
+        $mode   = required_param('mode', PARAM_ALPHA);
+        $offset = required_param('offset', PARAM_INT);
+        $overwritefeedback = required_param('overwritefeedback', PARAM_INT);
+
+        $returnurl = new moodle_url("/mod/assignment/submissions.php", array('id'=>$this->cm->id,'mode'=>$mode,'offset'=>$offset)); //not xhtml, just url.
+
+        if ($formdata = $mform->get_data() and $this->can_manage_responsefiles()) {            
+            $fs = get_file_storage();
+            // Empty the temp directory
+            $fs->delete_area_files($this->context->id, 'mod_assignment', 'temp');
+            // Store the uploaded files in the temp directory
+            $formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $this->context, 'mod_assignment', 'temp', $this->cm->id);
+
+            $packer = get_file_packer('application/zip');
+            // Retrieve the uploaded files as an array of stored file objects
+            $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'temp', $this->cm->id);
+
+            foreach ($files as $f) {
+                // $f is an instance of stored_file and '.' are directories
+                $f->get_filename();
+                // Extract the zip files
+                if ($newfile = $f->extract_to_storage($packer, $this->context->id, 'mod_assignment', 'temp', $this->cm->id, '/')) {
+                    // delete the zip files
+                    // This function removes the old database record. It does not
+                    // actually delete the file.
+                    $f->delete();
+                }
+            }
+
+            // Retrieve the extracted files as an array of stored file objects
+            $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'temp', $this->cm->id);
+            $errors = false;            
+
+            foreach ($files as  $f) {
+                $fullfilename = $f->get_filename();
+                if ($fullfilename != '.') {
+                    // filename should be of the format filename_userid.ext
+                    $filedetails = pathinfo($fullfilename);
+                    $filename = $filedetails['filename'];                    
+                    $userid = substr($filename, strrpos($filename, '_') + 1);
+                    $skip = false;
+                    $fail = false;
+
+                    if (!is_numeric($userid) || !$userexists = has_capability('mod/assignment:submit', $this->context, $userid)) {
+                        // Stays true if any file fails
+                        $errors = true;
+                        // Is reset for each file
+                        $fail = true;
+                        $error_array[] = $fullfilename;
+                        $log_message = get_string('bulkupload_failed', 'assignment', $fullfilename);
+                    } else {
+                    // Setting $createnew (param 2) to true creates an assignment
+                    // submission object record if one does not exist already
+                        $submission = $this->get_submission($userid, true, true);                    
+
+                        if ($oldfile = $fs->get_file($this->context->id, 'mod_assignment', 'response', $submission->id, '/', $fullfilename)) {
+                            switch ($overwritefeedback) {
+                                case 0:
+                                    $fullfilename = $this->rename_responsefile($filedetails, $userid, $fs, $submission);
+                                    $log_message = get_string('bulkupload_renamed', 'assignment', $fullfilename);
+                                    break;
+                                case 1:
+                                    $oldfile->delete();
+                                    $log_message = get_string('bulkupload_replaced', 'assignment', $fullfilename);
+                                    break;
+                                case 2:
+                                    $log_message = get_string('bulkupload_skipped', 'assignment', $fullfilename);
+                                    $skip = true;
+                                    break;
+                            }
+
+                        } else {
+                            $log_message = get_string('bulkupload_new', 'assignment', $fullfilename);
+                        }
+                    }
+
+                    if ($skip == false && $fail == false) {
+                        // This creates a new record in the database which determines
+                        // an additional virtual location for the file. The array contains
+                        // all the fields that are different from the existing record.
+                        $newfile = $fs->create_file_from_storedfile(array('userid'=>$userid, 'filearea'=>'response', 'itemid'=>$submission->id, 'filename'=>$fullfilename), $f);
+                    }
+
+                    $f->delete();
+                    add_to_log($this->course->id, 'assignment', 'feedback',
+                            '/submissions.php?id='.$this->cm->id.'&userid='.$userid.'&mode=single&filter=0&offset=0', $log_message, $this->cm->id);
+                }
+                
+            }            
+            
+
+            if (!$errors) {
+                redirect($returnurl->out(false));
+            }
+        }
+
+        $log_message = get_string('bulkupload_failed', 'assignment');
+        add_to_log($this->course->id, 'assignment', 'feedback',
+            '/submissions.php?id='.$submission->id.'&userid='.$userid, $log_message.implode(",", $error_array));
+
+        $PAGE->set_title(get_string('bulkupload', 'assignment'));
+        echo $OUTPUT->header();
+        echo $OUTPUT->notification(get_string('bulkuploaderror', 'assignment'));
+        echo $OUTPUT->notification(implode("<br />", $error_array));
+        echo $OUTPUT->continue_button($returnurl->out(true));
+        echo $OUTPUT->footer();
+        die;        
+    }
+
+    function rename_responsefile($filedetails, $userid, $fs, $submission) {
+        $filename = $filedetails['filename'];
+        // We want to keep the userid as the last _<n> in case a teacher downloads
+        // an existing feedback file to edit and re-upload
+        $filename = substr($filename, 0, strrpos($filename, '_'));
+        // If this file has been renamed before then it will be of the format
+        // anyfilename_<n>_<userid>.ext
+        // If the original file was of the same format, then the <n> will be
+        // incremented regardless. I considered using the following format
+        // anyfilename_version<n>_<userid>.ext
+        // but decided it was overkill
+        $version = substr($filename, strrpos($filename, '_') + 1);
+
+        if(is_numeric($version)) {
+            // The same file name has been used already, so we increment the
+            // version number
+            $version += 1;
+            $newfullfilename = substr_replace($filename, $version, strrpos($filename, '_') + 1);
+        } else {
+            // If we do not find the pattern _<n> at the end of $filename
+            // then we add it
+            $newfullfilename = $filename.'_1';
+        }
+        
+        $newfullfilename .= '_'.$userid.'.'.$filedetails['extension'];
+
+        // Now we need to repeat this process until we find a file name that does
+        // not already exist
+        if ($oldfile = $fs->get_file($this->context->id, 'mod_assignment', 'response', $submission->id, '/', $newfullfilename)) {
+            $filedetails = pathinfo($newfullfilename);
+            $newfullfilename = $this->rename_responsefile($filedetails, $userid, $fs, $submission);
+        }
+        return $newfullfilename;        
+    }
+
+
     function upload_file($mform, $options) {
         global $CFG, $USER, $DB, $OUTPUT;
 
Index: mod/assignment/type/upload/bulkupload.php
===================================================================
--- mod/assignment/type/upload/bulkupload.php	(revision 0)
+++ mod/assignment/type/upload/bulkupload.php	(revision 39)
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ *
+ * @package   mod-assignment
+ * @copyright 2010 Dongsheng Cai <dongsheng@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once(dirname(dirname(dirname(dirname(dirname(__FILE__))))).'/config.php');
+require_once(dirname(__FILE__).'/bulkupload_form.php');
+require_once(dirname(__FILE__).'/assignment.class.php');
+require_once("$CFG->dirroot/repository/lib.php");
+
+global $USER;
+
+$contextid = required_param('contextid', PARAM_INT);
+$id = optional_param('id', null, PARAM_INT);
+
+$formdata = new stdClass();
+$formdata->userid = $USER->id;
+$formdata->offset = optional_param('offset', null, PARAM_INT);
+$formdata->forcerefresh = optional_param('forcerefresh', null, PARAM_INT);
+$formdata->mode = optional_param('mode', null, PARAM_ALPHA);
+
+$url = new moodle_url('/mod/assignment/type/upload/bulkupload.php', array('contextid'=>$contextid,
+                            'id'=>$id,'offset'=>$formdata->offset,'forcerefresh'=>$formdata->forcerefresh,'userid'=>$formdata->userid,'mode'=>$formdata->mode));
+
+list($context, $course, $cm) = get_context_info_array($contextid);
+
+require_login($course, true, $cm);
+if (isguestuser()) {
+    die();
+}
+
+if (!$assignment = $DB->get_record('assignment', array('id'=>$cm->instance))) {
+    print_error('invalidid', 'assignment');
+}
+
+$PAGE->set_url($url);
+$PAGE->set_context($context);
+$title = strip_tags($course->fullname.': '.get_string('modulename', 'assignment').': '.format_string($assignment->name,true));
+$PAGE->set_title($title);
+$PAGE->set_heading($title);
+
+$instance = new assignment_upload($cm->id, $assignment, $cm, $course);
+
+$filemanager_options = array('subdirs'=>0, 'maxbytes'=>$assignment->maxbytes, 'maxfiles'=>-1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
+
+$mform = new mod_assignment_bulk_upload_responses_form(null, array('contextid'=>$contextid, 'userid'=>$formdata->userid, 'options'=>$filemanager_options));
+
+if ($mform->is_cancelled()) {
+   redirect($url);
+} else if ($formdata = $mform->get_data()) { 
+    $instance->upload($mform, $filemanager_options);
+    die;
+}
+
+echo $OUTPUT->header();
+
+echo $OUTPUT->box_start('generalbox');
+if ($instance->can_manage_responsefiles() && ($id==null)) {
+    $data = new stdClass();
+    // move feedback files to user draft area
+    $data = file_prepare_standard_filemanager($data, 'files', $filemanager_options, $context, 'mod_assignment', 'responses', $cm->id);
+    // set file manager itemid, so it will find the files in draft area
+    $mform->set_data($data);
+    $mform->display();
+}else {
+    echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
+    echo $OUTPUT->continue_button($url);
+}
+echo $OUTPUT->box_end();
+
+echo $OUTPUT->footer();
\ No newline at end of file
Index: mod/assignment/type/upload/bulkupload_form.php
===================================================================
--- mod/assignment/type/upload/bulkupload_form.php	(revision 0)
+++ mod/assignment/type/upload/bulkupload_form.php	(revision 39)
@@ -0,0 +1,44 @@
+<?php
+
+require_once($CFG->libdir.'/formslib.php');//putting this is as a safety as i got a class not found error.
+/**
+ * @package   mod-assignment
+ * @copyright 2010 Dongsheng Cai <dongsheng@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+class mod_assignment_bulk_upload_responses_form extends moodleform {
+    function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+
+        // visible elements
+        $mform->addElement('header', 'qgprefs', get_string('bulkupload', 'assignment'));
+        $mform->addElement('filemanager', 'files_filemanager', '', null, $instance['options']);
+
+        $choices = array(
+            0 => get_string('bulkupload_rename', 'assignment'),
+            1 => get_string('bulkupload_replace', 'assignment'),
+            2 => get_string('bulkupload_skip', 'assignment') );
+        $mform->addElement('select', 'overwritefeedback', get_string('bulkupload_overwrite', 'assignment'), $choices);
+        $mform->setType('overwritefeedback', PARAM_INT);
+        $mform->addHelpButton('overwritefeedback', 'bulkupload_overwrite', 'assignment');
+
+        // hidden params
+        $mform->addElement('hidden', 'contextid', $instance['contextid']);
+        $mform->setType('contextid', PARAM_INT);
+        $mform->addElement('hidden', 'userid', $instance['userid']);
+        $mform->setType('userid', PARAM_INT);
+        $mform->addElement('hidden', 'action', 'uploadresponses');
+        $mform->setType('action', PARAM_ALPHA);
+        $mform->addElement('hidden', 'mode', 'all');
+        $mform->setType('action', PARAM_ALPHA);
+        $mform->addElement('hidden', 'offset', -1);
+        $mform->setType('action', PARAM_ALPHA);
+
+        // buttons
+        $mform->addElement('submit', 'uploadzip', get_string('uploadzip', 'assignment'));
+
+    }
+}
Index: mod/assignment/lang/en/help/assignment/bulkupload_overwrite.html
===================================================================
--- mod/assignment/lang/en/help/assignment/bulkupload_overwrite.html	(revision 0)
+++ mod/assignment/lang/en/help/assignment/bulkupload_overwrite.html	(revision 39)
@@ -0,0 +1,15 @@
+<h2 class="main">
+File exists
+</h2>
+<p>
+You can specify the action to perform if a file by the same name already exists:
+<br /><br />
+Rename will add a new file with a version number added/incremented:
+<br />
+So filename_12345.doc (where 12345 is the user id) would become filename_1_12345.doc
+(where 1 is the first version uploaded with the same name)
+<br /><br />
+Replace will overwrite the old file with the new one.
+<br /><br />
+Skip will skip the upload of that file and leave the old one in place.
+</p>
\ No newline at end of file
Index: mod/assignment/lang/en/assignment.php
===================================================================
--- mod/assignment/lang/en/assignment.php	(revision 30)
+++ mod/assignment/lang/en/assignment.php	(revision 39)
@@ -53,6 +53,29 @@
 $string['assignmenttype'] = 'Assignment type';
 $string['assignment:view'] = 'View assignment';
 $string['availabledate'] = 'Available from';
+$string['bulkupload'] = 'Bulk upload feedback files';
+$string['bulkupload_failed'] = 'Bulk upload of feedback file {$a} (failed)';
+$string['bulkupload_new'] = 'Bulk upload of feedback file {$a}';
+$string['bulkupload_overwrite'] = 'If a file with the same name already exists:';
+$string['bulkupload_overwrite_help'] = '<p>
+You can specify the action to perform if a file by the same name already exists:
+<br /><br />
+Rename will add a new file with a version number added/incremented:
+<br />
+So filename_12345.doc (where 12345 is the user id) would become filename_1_12345.doc
+(where 1 is the first version uploaded with the same name)
+<br /><br />
+Replace will overwrite the old file with the new one.
+<br /><br />
+Skip will skip the upload of that file and leave the old one in place.
+</p>';
+$string['bulkupload_rename'] = 'Rename';
+$string['bulkupload_renamed'] = 'Bulk upload of feedback file {$a} (renamed)';
+$string['bulkupload_replace'] = 'Replace';
+$string['bulkupload_replaced'] = 'Bulk upload of feedback file {$a} (replaced)';
+$string['bulkupload_skip'] = 'Skip';
+$string['bulkupload_skipped'] = 'Bulk upload of feedback file {$a} (skipped)';
+$string['bulkuploaderror'] = 'The following files could not be uploaded:';
 $string['cannotdeletefiles'] = 'An error occurred and files could not be deleted';
 $string['cannotviewassignment'] = 'You can not view this assignment';
 $string['comment'] = 'Comment';
@@ -208,6 +231,7 @@
 $string['uploadnofilefound'] = 'No file was found - are you sure you selected one to upload?';
 $string['uploadnotregistered'] = '\'{$a}\' was uploaded OK but submission did not register!';
 $string['uploadsuccess'] = 'Uploaded \'{$a}\' successfully';
+$string['uploadzip'] = 'Upload feedback';
 $string['usermisconf'] = 'User is misconfigured';
 $string['usernosubmit'] = 'Sorry, you are not allowed to submit an assignment.';
 $string['viewfeedback'] = 'View assignment grades and feedback';
Index: mod/assignment/lib.php
===================================================================
--- mod/assignment/lib.php	(revision 30)
+++ mod/assignment/lib.php	(revision 39)
@@ -1477,11 +1477,6 @@
                     }
                     $currentposition++;
                 }
-                if ($hassubmission && ($this->assignment->assignmenttype=='upload' || $this->assignment->assignmenttype=='online' || $this->assignment->assignmenttype=='uploadsingle')) { //TODO: this is an ugly hack, where is the plugin spirit? (skodak)
-                    echo html_writer::start_tag('div', array('class' => 'mod-assignment-download-link'));
-                    echo html_writer::link(new moodle_url('/mod/assignment/submissions.php', array('id' => $this->cm->id, 'download' => 'zip')), get_string('downloadall', 'assignment'));
-                    echo html_writer::end_tag('div');
-                }
                 $table->print_html();  /// Print the whole table
             } else {
                 if ($filter == self::FILTER_SUBMITTED) {
@@ -1490,6 +1485,24 @@
                     echo html_writer::tag('div', get_string('norequiregrading', 'assignment'), array('class'=>'norequiregrading'));
                 }
             }
+
+            if ($hassubmission && ($this->assignment->assignmenttype=='upload' || $this->assignment->assignmenttype=='online' || $this->assignment->assignmenttype=='uploadsingle')) { //TODO: this is an ugly hack, where is the plugin spirit? (skodak)
+                echo html_writer::start_tag('div', array('class' => 'mod-assignment-download-link'));
+                echo html_writer::link(new moodle_url('/mod/assignment/submissions.php', array('id' => $this->cm->id, 'download' => 'zip')), get_string('downloadall', 'assignment'));
+                echo html_writer::end_tag('div');
+
+                echo html_writer::start_tag('div', array('class' => 'mod-assignment-download-link'));
+                echo html_writer::link(new moodle_url('/mod/assignment/type/upload/bulkupload.php', array('contextid' => $this->context->id)), get_string('bulkupload', 'assignment'));
+                echo html_writer::end_tag('div');
+            }
+            $table->print_html();  /// Print the whole table
+        } else {
+            if ($filter == self::FILTER_SUBMITTED) {
+                echo html_writer::tag('div', get_string('nosubmisson', 'assignment'), array('class'=>'nosubmisson'));
+            } else if ($filter == self::FILTER_REQUIRE_GRADING) {
+                echo html_writer::tag('div', get_string('norequiregrading', 'assignment'), array('class'=>'norequiregrading'));
+            }
+
         }
 
         /// Print quickgrade form around the table

 
