2b3c0a1b89ed343e6bc2f64f768087bb8d2970b6
[moodle.git] / mod / assignment / type / upload / assignment.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * Assignment upload type implementation
20  *
21  * @package   mod-assignment
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 require_once(dirname(__FILE__).'/upload_form.php');
26 require_once($CFG->libdir . '/portfoliolib.php');
27 require_once($CFG->dirroot . '/mod/assignment/lib.php');
29 define('ASSIGNMENT_STATUS_SUBMITTED', 'submitted'); // student thinks it is finished
30 define('ASSIGNMENT_STATUS_CLOSED', 'closed');       // teacher prevents more submissions
32 /**
33  * Extend the base assignment class for assignments where you upload a single file
34  *
35  */
36 class assignment_upload extends assignment_base {
38     function assignment_upload($cmid='staticonly', $assignment=NULL, $cm=NULL, $course=NULL) {
39         parent::assignment_base($cmid, $assignment, $cm, $course);
40         $this->type = 'upload';
41     }
43     function view() {
44         global $USER, $OUTPUT;
46         require_capability('mod/assignment:view', $this->context);
48         add_to_log($this->course->id, 'assignment', 'view', "view.php?id={$this->cm->id}", $this->assignment->id, $this->cm->id);
50         $this->view_header();
52         if ($this->assignment->timeavailable > time()
53           and !has_capability('mod/assignment:grade', $this->context)      // grading user can see it anytime
54           and $this->assignment->var3) {                                   // force hiding before available date
55             echo $OUTPUT->box_start('generalbox boxaligncenter', 'intro');
56             print_string('notavailableyet', 'assignment');
57             echo $OUTPUT->box_end();
58         } else {
59             $this->view_intro();
60         }
62         $this->view_dates();
64         if (is_enrolled($this->context, $USER)) {
65             if ($submission = $this->get_submission($USER->id)) {
66                 $filecount = $this->count_user_files($submission->id);
67             } else {
68                 $filecount = 0;
69             }
71             $this->view_feedback();
73             if (!$this->drafts_tracked() or !$this->isopen() or $this->is_finalized($submission)) {
74                 echo $OUTPUT->heading(get_string('submission', 'assignment'), 3);
75             } else {
76                 echo $OUTPUT->heading(get_string('submissiondraft', 'assignment'), 3);
77             }
79             if ($filecount and $submission) {
80                 echo $OUTPUT->box($this->print_user_files($USER->id, true), 'generalbox boxaligncenter', 'userfiles');
81             } else {
82                 if (!$this->isopen() or $this->is_finalized($submission)) {
83                     echo $OUTPUT->box(get_string('nofiles', 'assignment'), 'generalbox boxaligncenter nofiles', 'userfiles');
84                 } else {
85                     echo $OUTPUT->box(get_string('nofilesyet', 'assignment'), 'generalbox boxaligncenter nofiles', 'userfiles');
86                 }
87             }
89             if (has_capability('mod/assignment:submit', $this->context)) {
90                 $this->view_upload_form();
91             }
93             if ($this->notes_allowed()) {
94                 echo $OUTPUT->heading(get_string('notes', 'assignment'), 3);
95                 $this->view_notes();
96             }
98             $this->view_final_submission();
99         }
100         $this->view_footer();
101     }
104     function view_feedback($submission=NULL) {
105         global $USER, $CFG, $DB, $OUTPUT;
106         require_once($CFG->libdir.'/gradelib.php');
108         if (!$submission) { /// Get submission for this assignment
109             $submission = $this->get_submission($USER->id);
110         }
112         if (empty($submission->timemarked)) {   /// Nothing to show, so print nothing
113             if ($this->count_responsefiles($USER->id)) {
114                 echo $OUTPUT->heading(get_string('responsefiles', 'assignment'), 3);
115                 $responsefiles = $this->print_responsefiles($USER->id, true);
116                 echo $OUTPUT->box($responsefiles, 'generalbox boxaligncenter');
117             }
118             return;
119         }
121         $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $USER->id);
122         $item = $grading_info->items[0];
123         $grade = $item->grades[$USER->id];
125         if ($grade->hidden or $grade->grade === false) { // hidden or error
126             return;
127         }
129         if ($grade->grade === null and empty($grade->str_feedback)) {   /// Nothing to show yet
130             return;
131         }
133         $graded_date = $grade->dategraded;
134         $graded_by   = $grade->usermodified;
136     /// We need the teacher info
137         if (!$teacher = $DB->get_record('user', array('id'=>$graded_by))) {
138             print_error('cannotfindteacher');
139         }
141     /// Print the feedback
142         echo $OUTPUT->heading(get_string('submissionfeedback', 'assignment'), 3);
144         echo '<table cellspacing="0" class="feedback">';
146         echo '<tr>';
147         echo '<td class="left picture">';
148         echo $OUTPUT->user_picture($teacher);
149         echo '</td>';
150         echo '<td class="topic">';
151         echo '<div class="from">';
152         echo '<div class="fullname">'.fullname($teacher).'</div>';
153         echo '<div class="time">'.userdate($graded_date).'</div>';
154         echo '</div>';
155         echo '</td>';
156         echo '</tr>';
158         echo '<tr>';
159         echo '<td class="left side">&nbsp;</td>';
160         echo '<td class="content">';
161         if ($this->assignment->grade) {
162             echo '<div class="grade">';
163             echo get_string("grade").': '.$grade->str_long_grade;
164             echo '</div>';
165             echo '<div class="clearer"></div>';
166         }
168         echo '<div class="comment">';
169         echo $grade->str_feedback;
170         echo '</div>';
171         echo '</tr>';
173         echo '<tr>';
174         echo '<td class="left side">&nbsp;</td>';
175         echo '<td class="content">';
176         echo $this->print_responsefiles($USER->id, true);
177         echo '</tr>';
179         echo '</table>';
180     }
183     function view_upload_form() {
184         global $CFG, $USER, $OUTPUT;
186         $submission = $this->get_submission($USER->id);
188         if ($this->is_finalized($submission)) {
189             // no uploading
190             return;
191         }
193         if ($this->can_upload_file($submission)) {
194             $fs = get_file_storage();
195             // edit files in another page
196             if ($submission) {
197                 if ($files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id, "timemodified", false)) {
198                     $str = get_string('editthesefiles', 'assignment');
199                 } else {
200                     $str = get_string('uploadfiles', 'assignment');
201                 }
202             } else {
203                 $str = get_string('uploadfiles', 'assignment');
204             }
205             echo $OUTPUT->single_button(new moodle_url('/mod/assignment/type/upload/upload.php', array('contextid'=>$this->context->id, 'userid'=>$USER->id)), $str, 'get');
206         }
208     }
210     function view_notes() {
211         global $USER, $OUTPUT;
213         if ($submission = $this->get_submission($USER->id)
214           and !empty($submission->data1)) {
215             echo $OUTPUT->box(format_text($submission->data1, FORMAT_HTML), 'generalbox boxaligncenter boxwidthwide');
216         } else {
217             echo $OUTPUT->box(get_string('notesempty', 'assignment'), 'generalbox boxaligncenter');
218         }
219         if ($this->can_update_notes($submission)) {
220             $options = array ('id'=>$this->cm->id, 'action'=>'editnotes');
221             echo '<div style="text-align:center">';
222             echo $OUTPUT->single_button(new moodle_url('upload.php', $options), get_string('edit'));
223             echo '</div>';
224         }
225     }
227     function view_final_submission() {
228         global $CFG, $USER, $OUTPUT;
230         $submission = $this->get_submission($USER->id);
232         if ($this->isopen() and $this->can_finalize($submission)) {
233             //print final submit button
234             echo $OUTPUT->heading(get_string('submitformarking','assignment'), 3);
235             echo '<div style="text-align:center">';
236             echo '<form method="post" action="upload.php">';
237             echo '<fieldset class="invisiblefieldset">';
238             echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
239             echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
240             echo '<input type="hidden" name="action" value="finalize" />';
241             echo '<input type="submit" name="formarking" value="'.get_string('sendformarking', 'assignment').'" />';
242             echo '</fieldset>';
243             echo '</form>';
244             echo '</div>';
245         } else if (!$this->isopen()) {
246             echo $OUTPUT->heading(get_string('nomoresubmissions','assignment'), 3);
248         } else if ($this->drafts_tracked() and $state = $this->is_finalized($submission)) {
249             if ($state == ASSIGNMENT_STATUS_SUBMITTED) {
250                 echo $OUTPUT->heading(get_string('submitedformarking','assignment'), 3);
251             } else {
252                 echo $OUTPUT->heading(get_string('nomoresubmissions','assignment'), 3);
253             }
254         } else {
255             //no submission yet
256         }
257     }
260     /**
261      * Return true if var3 == hide description till available day
262      *
263      *@return boolean
264      */
265     function description_is_hidden() {
266         return ($this->assignment->var3 && (time() <= $this->assignment->timeavailable));
267     }
269     function print_student_answer($userid, $return=false){
270         global $CFG, $OUTPUT, $PAGE;
272         $submission = $this->get_submission($userid);
274         $output = '';
276         if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission)) {
277             $output .= '<strong>'.get_string('draft', 'assignment').':</strong> ';
278         }
280         if ($this->notes_allowed() and !empty($submission->data1)) {
281             $link = new moodle_url("/mod/assignment/type/upload/notes.php", array('id'=>$this->cm->id, 'userid'=>$userid));
282             $action = new popup_action('click', $link, 'notes', array('height' => 500, 'width' => 780));
283             $output .= $OUTPUT->action_link($link, get_string('notes', 'assignment'), $action, array('title'=>get_string('notes', 'assignment')));
285             $output .= '&nbsp;';
286         }
289         $renderer = $PAGE->get_renderer('mod_assignment');
290         $output = $OUTPUT->box_start('files').$output;
291         $output .= $renderer->assignment_files($this->context, $submission->id);
292         $output .= $OUTPUT->box_end();
294         return $output;
295     }
298     /**
299      * Produces a list of links to the files uploaded by a user
300      *
301      * @param $userid int optional id of the user. If 0 then $USER->id is used.
302      * @param $return boolean optional defaults to false. If true the list is returned rather than printed
303      * @return string optional
304      */
305     function print_user_files($userid=0, $return=false) {
306         global $CFG, $USER, $OUTPUT, $PAGE;
308         $mode    = optional_param('mode', '', PARAM_ALPHA);
309         $offset  = optional_param('offset', 0, PARAM_INT);
311         if (!$userid) {
312             if (!isloggedin()) {
313                 return '';
314             }
315             $userid = $USER->id;
316         }
318         $output = $OUTPUT->box_start('files');
320         $submission = $this->get_submission($userid);
322         // only during grading
323         if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission) and !empty($mode)) {
324             $output .= '<strong>'.get_string('draft', 'assignment').':</strong><br />';
325         }
327         if ($this->notes_allowed() and !empty($submission->data1) and !empty($mode)) { // only during grading
329             $npurl = $CFG->wwwroot."/mod/assignment/type/upload/notes.php?id={$this->cm->id}&amp;userid=$userid&amp;offset=$offset&amp;mode=single";
330             $output .= '<a href="'.$npurl.'">'.get_string('notes', 'assignment').'</a><br />';
332         }
334         if ($this->drafts_tracked() and $this->isopen() and has_capability('mod/assignment:grade', $this->context) and $mode != '') { // we do not want it on view.php page
335             if ($this->can_unfinalize($submission)) {
336                 //$options = array ('id'=>$this->cm->id, 'userid'=>$userid, 'action'=>'unfinalize', 'mode'=>$mode, 'offset'=>$offset);
337                 $output .= '<br /><input type="submit" name="unfinalize" value="'.get_string('unfinalize', 'assignment').'" />';
338                 $output .=  $OUTPUT->help_icon('unfinalize', 'assignment');
340             } else if ($this->can_finalize($submission)) {
341                 //$options = array ('id'=>$this->cm->id, 'userid'=>$userid, 'action'=>'finalizeclose', 'mode'=>$mode, 'offset'=>$offset);
342                 $output .= '<br /><input type="submit" name="finalize" value="'.get_string('finalize', 'assignment').'" />';
343             }
344         }
346         if ($submission) {
347             $renderer = $PAGE->get_renderer('mod_assignment');
348             $output .= $renderer->assignment_files($this->context, $submission->id);
349         }
350         $output .= $OUTPUT->box_end();
352         if ($return) {
353             return $output;
354         }
355         echo $output;
356     }
358     function submissions($mode) {
359         // redirects out of form to process (un)finalizing.
360         $unfinalize = optional_param('unfinalize', FALSE, PARAM_TEXT);
361         $finalize = optional_param('finalize', FALSE, PARAM_TEXT);
362         if ($unfinalize) {
363             $this->unfinalize('single');
364         } else if ($finalize) {
365             $this->finalize('single');
366         }
367         if ($unfinalize || $finalize) {
368             $mode = 'singlenosave';
369         }
370         parent::submissions($mode);
371     }
373     function process_feedback() {
374         if (!$feedback = data_submitted() or !confirm_sesskey()) {      // No incoming data?
375             return false;
376         }
377         $userid = required_param('userid', PARAM_INT);
378         $offset = required_param('offset', PARAM_INT);
379         $mform = $this->display_submission($offset, $userid, false);
380         parent::process_feedback($mform);
381     }
383     function print_responsefiles($userid, $return=false) {
384         global $CFG, $USER, $OUTPUT, $PAGE;
386         $mode    = optional_param('mode', '', PARAM_ALPHA);
387         $offset  = optional_param('offset', 0, PARAM_INT);
389         $output = $OUTPUT->box_start('responsefiles');
391         $candelete = $this->can_manage_responsefiles();
392         $strdelete   = get_string('delete');
394         $fs = get_file_storage();
395         $browser = get_file_browser();
397         if ($submission = $this->get_submission($userid)) {
398             $renderer = $PAGE->get_renderer('mod_assignment');
399             $output .= $renderer->assignment_files($this->context, $submission->id, 'response');
400         }
401         $output .= $OUTPUT->box_end();
403         if ($return) {
404             return $output;
405         }
406         echo $output;
407     }
410     /**
411      * Upload files
412      * upload_file function requires moodle form instance and file manager options
413      * @param object $mform
414      * @param array $options
415      */
416     function upload($mform = null, $filemanager_options = null) {
417         $action = required_param('action', PARAM_ALPHA);
418         switch ($action) {
419             case 'finalize':
420                 $this->finalize();
421                 break;
422             case 'finalizeclose':
423                 $this->finalizeclose();
424                 break;
425             case 'unfinalize':
426                 $this->unfinalize();
427                 break;
428             case 'uploadresponse':
429                 $this->upload_responsefile($mform, $filemanager_options);
430                 break;
431             case 'uploadfile':
432                 $this->upload_file($mform, $filemanager_options);
433             case 'savenotes':
434             case 'editnotes':
435                 $this->upload_notes();
436             default:
437                 print_error('unknowuploadaction', '', '', $action);
438         }
439     }
441     function upload_notes() {
442         global $CFG, $USER, $OUTPUT, $DB;
444         $action = required_param('action', PARAM_ALPHA);
446         $returnurl  = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
448         $mform = new mod_assignment_upload_notes_form();
450         $defaults = new stdClass();
451         $defaults->id = $this->cm->id;
453         if ($submission = $this->get_submission($USER->id)) {
454             $defaults->text = clean_text($submission->data1);
455         } else {
456             $defaults->text = '';
457         }
459         $mform->set_data($defaults);
461         if ($mform->is_cancelled()) {
462             $returnurl  = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
463             redirect($returnurl);
464         }
466         if (!$this->can_update_notes($submission)) {
467             $this->view_header(get_string('upload'));
468             echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
469             echo $OUTPUT->continue_button($returnurl);
470             $this->view_footer();
471             die;
472         }
474         if ($data = $mform->get_data() and $action == 'savenotes') {
475             $submission = $this->get_submission($USER->id, true); // get or create submission
476             $updated = new stdClass();
477             $updated->id           = $submission->id;
478             $updated->timemodified = time();
479             $updated->data1        = $data->text;
481             $DB->update_record('assignment_submissions', $updated);
482             add_to_log($this->course->id, 'assignment', 'upload', 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
483             redirect($returnurl);
484             $submission = $this->get_submission($USER->id);
485             $this->update_grade($submission);
486         }
488         /// show notes edit form
489         $this->view_header(get_string('notes', 'assignment'));
491         echo $OUTPUT->heading(get_string('notes', 'assignment'));
493         $mform->display();
495         $this->view_footer();
496         die;
497     }
499     function upload_responsefile($mform, $options) {
500         global $CFG, $USER, $OUTPUT, $PAGE;
502         $userid = required_param('userid', PARAM_INT);
503         $mode   = required_param('mode', PARAM_ALPHA);
504         $offset = required_param('offset', PARAM_INT);
506         $returnurl = new moodle_url("submissions.php", array('id'=>$this->cm->id,'userid'=>$userid,'mode'=>$mode,'offset'=>$offset)); //not xhtml, just url.
508         if ($formdata = $mform->get_data() and $this->can_manage_responsefiles()) {
509             $fs = get_file_storage();
510             $submission = $this->get_submission($userid, true, true);
511             if ($formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $this->context, 'mod_assignment', 'response', $submission->id)) {
512                 $returnurl = new moodle_url("/mod/assignment/submissions.php", array('id'=>$this->cm->id,'userid'=>$formdata->userid,'mode'=>$formdata->mode,'offset'=>$formdata->offset));
513                 redirect($returnurl->out(false));
514             }
515         }
516         $PAGE->set_title(get_string('upload'));
517         echo $OUTPUT->header();
518         echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
519         echo $OUTPUT->continue_button($returnurl->out(true));
520         echo $OUTPUT->footer();
521         die;
522     }
524     function upload_file($mform, $options) {
525         global $CFG, $USER, $DB, $OUTPUT;
527         $returnurl  = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
528         $submission = $this->get_submission($USER->id);
529         $filecount = $this->count_user_files($submission->id);
531         if (!$this->can_upload_file($submission)) {
532             $this->view_header(get_string('upload'));
533             echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
534             echo $OUTPUT->continue_button($returnurl);
535             $this->view_footer();
536             die;
537         }
539         if ($formdata = $mform->get_data()) {
540             $fs = get_file_storage();
541             $submission = $this->get_submission($USER->id, true); //create new submission if needed
542             $fs->delete_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id);
543             $formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $this->context, 'mod_assignment', 'submission', $submission->id);
544             $updates = new stdClass();
545             $updates->id = $submission->id;
546             $updates->timemodified = time();
547             $DB->update_record('assignment_submissions', $updates);
548             add_to_log($this->course->id, 'assignment', 'upload',
549                     'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
550             $this->update_grade($submission);
551             if (!$this->drafts_tracked()) {
552                 $this->email_teachers($submission);
553             }
555             // send files to event system
556             $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id);
557             // Let Moodle know that assessable files were  uploaded (eg for plagiarism detection)
558             $eventdata = new stdClass();
559             $eventdata->modulename   = 'assignment';
560             $eventdata->cmid         = $this->cm->id;
561             $eventdata->itemid       = $submission->id;
562             $eventdata->courseid     = $this->course->id;
563             $eventdata->userid       = $USER->id;
564             if ($files) {
565                 $eventdata->files        = $files;
566             }
567             events_trigger('assessable_file_uploaded', $eventdata);
568             $returnurl  = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
569             redirect($returnurl);
570         }
572         $this->view_header(get_string('upload'));
573         echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
574         echo $OUTPUT->continue_button($returnurl);
575         $this->view_footer();
576         die;
577     }
579     function send_file($filearea, $args) {
580         global $CFG, $DB, $USER;
581         require_once($CFG->libdir.'/filelib.php');
583         require_login($this->course, false, $this->cm);
585         if ($filearea === 'submission') {
586             $submissionid = (int)array_shift($args);
588             if (!$submission = $DB->get_record('assignment_submissions', array('assignment'=>$this->assignment->id, 'id'=>$submissionid))) {
589                 return false;
590             }
592             if ($USER->id != $submission->userid and !has_capability('mod/assignment:grade', $this->context)) {
593                 return false;
594             }
596             $relativepath = implode('/', $args);
597             $fullpath = "/{$this->context->id}/mod_assignment/submission/$submission->id/$relativepath";
599             $fs = get_file_storage();
600             if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
601                 return false;
602             }
603             send_stored_file($file, 0, 0, true); // download MUST be forced - security!
605         } else if ($filearea === 'response') {
606             $submissionid = (int)array_shift($args);
608             if (!$submission = $DB->get_record('assignment_submissions', array('assignment'=>$this->assignment->id, 'id'=>$submissionid))) {
609                 return false;
610             }
612             if ($USER->id != $submission->userid and !has_capability('mod/assignment:grade', $this->context)) {
613                 return false;
614             }
616             $relativepath = implode('/', $args);
617             $fullpath = "/{$this->context->id}/mod_assignment/response/$submission->id/$relativepath";
619             $fs = get_file_storage();
620             if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
621                 return false;
622             }
623             send_stored_file($file, 0, 0, true);
624         }
626         return false;
627     }
629     function finalize($forcemode=null) {
630         global $USER, $DB, $OUTPUT;
631         $userid = optional_param('userid', $USER->id, PARAM_INT);
632         $offset = optional_param('offset', 0, PARAM_INT);
633         $confirm    = optional_param('confirm', 0, PARAM_BOOL);
634         $returnurl  = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
635         $submission = $this->get_submission($userid);
637         if ($forcemode!=null) {
638             $returnurl  = new moodle_url('/mod/assignment/submissions.php',
639                 array('id'=>$this->cm->id,
640                     'userid'=>$userid,
641                     'mode'=>$forcemode,
642                     'offset'=>$offset
643                 ));
644         }
646         if (!$this->can_finalize($submission)) {
647             redirect($returnurl->out(false)); // probably already graded, redirect to assignment page, the reason should be obvious
648         }
650         if ($forcemode==null) {
651             if (!data_submitted() or !$confirm or !confirm_sesskey()) {
652                 $optionsno = array('id'=>$this->cm->id);
653                 $optionsyes = array ('id'=>$this->cm->id, 'confirm'=>1, 'action'=>'finalize', 'sesskey'=>sesskey());
654                 $this->view_header(get_string('submitformarking', 'assignment'));
655                 echo $OUTPUT->heading(get_string('submitformarking', 'assignment'));
656                 echo $OUTPUT->confirm(get_string('onceassignmentsent', 'assignment'), new moodle_url('upload.php', $optionsyes),new moodle_url( 'view.php', $optionsno));
657                 $this->view_footer();
658                 die;
659             }
660         }
661         $updated = new stdClass();
662         $updated->id           = $submission->id;
663         $updated->data2        = ASSIGNMENT_STATUS_SUBMITTED;
664         $updated->timemodified = time();
666         $DB->update_record('assignment_submissions', $updated);
667         add_to_log($this->course->id, 'assignment', 'upload', //TODO: add finalize action to log
668                 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
669         $submission = $this->get_submission($userid);
670         $this->update_grade($submission);
671         $this->email_teachers($submission);
673         // Trigger assessable_files_done event to show files are complete
674         $eventdata = new stdClass();
675         $eventdata->modulename   = 'assignment';
676         $eventdata->cmid         = $this->cm->id;
677         $eventdata->itemid       = $submission->id;
678         $eventdata->courseid     = $this->course->id;
679         $eventdata->userid       = $userid;
680         events_trigger('assessable_files_done', $eventdata);
682         if ($forcemode==null) {
683             redirect($returnurl->out(false));
684         }
685     }
687     function finalizeclose() {
688         global $DB;
690         $userid    = optional_param('userid', 0, PARAM_INT);
691         $mode      = required_param('mode', PARAM_ALPHA);
692         $offset    = required_param('offset', PARAM_INT);
693         $returnurl  = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'userid'=>$userid, 'mode'=>$mode, 'offset'=>$offset, 'forcerefresh'=>1));
695         // create but do not add student submission date
696         $submission = $this->get_submission($userid, true, true);
698         if (!data_submitted() or !$this->can_finalize($submission) or !confirm_sesskey()) {
699             redirect($returnurl); // probably closed already
700         }
702         $updated = new stdClass();
703         $updated->id    = $submission->id;
704         $updated->data2 = ASSIGNMENT_STATUS_CLOSED;
706         $DB->update_record('assignment_submissions', $updated);
707         add_to_log($this->course->id, 'assignment', 'upload', //TODO: add finalize action to log
708                 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
709         $submission = $this->get_submission($userid, false, true);
710         $this->update_grade($submission);
711         redirect($returnurl);
712     }
714     function unfinalize($forcemode=null) {
715         global $DB;
717         $userid = required_param('userid', PARAM_INT);
718         $mode   = required_param('mode', PARAM_ALPHA);
719         $offset = required_param('offset', PARAM_INT);
721         if ($forcemode!=null) {
722             $mode=$forcemode;
723         }
724         $returnurl = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'userid'=>$userid, 'mode'=>$mode, 'offset'=>$offset, 'forcerefresh'=>1) );
725         if (data_submitted()
726           and $submission = $this->get_submission($userid)
727           and $this->can_unfinalize($submission)
728           and confirm_sesskey()) {
730             $updated = new stdClass();
731             $updated->id = $submission->id;
732             $updated->data2 = '';
733             $DB->update_record('assignment_submissions', $updated);
734             //TODO: add unfinalize action to log
735             add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->assignment->id, $this->assignment->id, $this->cm->id);
736             $submission = $this->get_submission($userid);
737             $this->update_grade($submission);
738         }
740         if ($forcemode==null) {
741             redirect($returnurl);
742         }
743     }
746     function delete() {
747         $action   = optional_param('action', '', PARAM_ALPHA);
749         switch ($action) {
750             case 'response':
751                 $this->delete_responsefile();
752                 break;
753             default:
754                 $this->delete_file();
755         }
756         die;
757     }
760     function delete_responsefile() {
761         global $CFG, $OUTPUT,$PAGE;
763         $file     = required_param('file', PARAM_FILE);
764         $userid   = required_param('userid', PARAM_INT);
765         $mode     = required_param('mode', PARAM_ALPHA);
766         $offset   = required_param('offset', PARAM_INT);
767         $confirm  = optional_param('confirm', 0, PARAM_BOOL);
769         $returnurl  = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'userid'=>$userid, 'mode'=>$mode, 'offset'=>$offset));
771         if (!$this->can_manage_responsefiles()) {
772            redirect($returnurl);
773         }
775         $urlreturn = 'submissions.php';
776         $optionsreturn = array('id'=>$this->cm->id, 'offset'=>$offset, 'mode'=>$mode, 'userid'=>$userid);
778         if (!data_submitted() or !$confirm or !confirm_sesskey()) {
779             $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'action'=>'response', 'mode'=>$mode, 'offset'=>$offset, 'sesskey'=>sesskey());
780             $PAGE->set_title(get_string('delete'));
781             echo $OUTPUT->header();
782             echo $OUTPUT->heading(get_string('delete'));
783             echo $OUTPUT->confirm(get_string('confirmdeletefile', 'assignment', $file), new moodle_url('delete.php', $optionsyes), new moodle_url($urlreturn, $optionsreturn));
784             echo $OUTPUT->footer();
785             die;
786         }
788         if ($submission = $this->get_submission($userid)) {
789             $fs = get_file_storage();
790             if ($file = $fs->get_file($this->context->id, 'mod_assignment', 'response', $submission->id, '/', $file)) {
791                 $file->delete();
792             }
793         }
794         redirect($returnurl);
795     }
798     function delete_file() {
799         global $CFG, $DB, $OUTPUT, $PAGE;
801         $file     = required_param('file', PARAM_FILE);
802         $userid   = required_param('userid', PARAM_INT);
803         $confirm  = optional_param('confirm', 0, PARAM_BOOL);
804         $mode     = optional_param('mode', '', PARAM_ALPHA);
805         $offset   = optional_param('offset', 0, PARAM_INT);
807         require_login($this->course->id, false, $this->cm);
809         if (empty($mode)) {
810             $urlreturn = 'view.php';
811             $optionsreturn = array('id'=>$this->cm->id);
812             $returnurl  = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
813         } else {
814             $urlreturn = 'submissions.php';
815             $optionsreturn = array('id'=>$this->cm->id, 'offset'=>$offset, 'mode'=>$mode, 'userid'=>$userid);
816             $returnurl  = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'offset'=>$offset, 'userid'=>$userid));
817         }
819         if (!$submission = $this->get_submission($userid) // incorrect submission
820           or !$this->can_delete_files($submission)) {     // can not delete
821             $this->view_header(get_string('delete'));
822             echo $OUTPUT->notification(get_string('cannotdeletefiles', 'assignment'));
823             echo $OUTPUT->continue_button($returnurl);
824             $this->view_footer();
825             die;
826         }
828         if (!data_submitted() or !$confirm or !confirm_sesskey()) {
829             $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'sesskey'=>sesskey(), 'mode'=>$mode, 'offset'=>$offset, 'sesskey'=>sesskey());
830             if (empty($mode)) {
831                 $this->view_header(get_string('delete'));
832             } else {
833                 $PAGE->set_title(get_string('delete'));
834                 echo $OUTPUT->header();
835             }
836             echo $OUTPUT->heading(get_string('delete'));
837             echo $OUTPUT->confirm(get_string('confirmdeletefile', 'assignment', $file), new moodle_url('delete.php', $optionsyes), new moodle_url($urlreturn, $optionsreturn));
838             if (empty($mode)) {
839                 $this->view_footer();
840             } else {
841                 echo $OUTPUT->footer();
842             }
843             die;
844         }
846         $fs = get_file_storage();
847         if ($file = $fs->get_file($this->context->id, 'mod_assignment', 'submission', $submission->id, '/', $file)) {
848             $file->delete();
849             $submission->timemodified = time();
850             $DB->update_record('assignment_submissions', $submission);
851             add_to_log($this->course->id, 'assignment', 'upload', //TODO: add delete action to log
852                     'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
853             $this->update_grade($submission);
854         }
855         redirect($returnurl);
856     }
859     function can_upload_file($submission) {
860         global $USER;
862         if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
863           and $this->isopen()                                                 // assignment not closed yet
864           and (empty($submission) or ($submission->userid == $USER->id        // his/her own submission
865             and $this->count_user_files($submission->id) <= $this->assignment->var1))    // file limit not exceeded
866           and !$this->is_finalized($submission)) {                            // no uploading after final submission
867             return true;
868         } else {
869             return false;
870         }
871     }
873     function can_manage_responsefiles() {
874         if (has_capability('mod/assignment:grade', $this->context)) {
875             return true;
876         } else {
877             return false;
878         }
879     }
881     function can_delete_files($submission) {
882         global $USER;
884         if (has_capability('mod/assignment:grade', $this->context)) {
885             return true;
886         }
888         if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
889           and $this->isopen()                                      // assignment not closed yet
890           and $this->assignment->resubmit                          // deleting allowed
891           and $USER->id == $submission->userid                     // his/her own submission
892           and !$this->is_finalized($submission)) {                 // no deleting after final submission
893             return true;
894         } else {
895             return false;
896         }
897     }
899     function drafts_tracked() {
900         return !empty($this->assignment->var4);
901     }
903     /**
904      * Returns submission status
905      * @param object $submission - may be empty
906      * @return string submission state - empty, ASSIGNMENT_STATUS_SUBMITTED or ASSIGNMENT_STATUS_CLOSED
907      */
908     function is_finalized($submission) {
909         if (!$this->drafts_tracked()) {
910             return '';
912         } else if (empty($submission)) {
913             return '';
915         } else if ($submission->data2 == ASSIGNMENT_STATUS_SUBMITTED or $submission->data2 == ASSIGNMENT_STATUS_CLOSED) {
916             return $submission->data2;
918         } else {
919             return '';
920         }
921     }
923     function can_unfinalize($submission) {
924         if (!$this->drafts_tracked()) {
925             return false;
926         }
927         if (has_capability('mod/assignment:grade', $this->context)
928           and $this->isopen()
929           and $this->is_finalized($submission)) {
930             return true;
931         } else {
932             return false;
933         }
934     }
936     function can_finalize($submission) {
937         global $USER;
938         if (!$this->drafts_tracked()) {
939             return false;
940         }
942         if ($this->is_finalized($submission)) {
943             return false;
944         }
946         if (has_capability('mod/assignment:grade', $this->context)) {
947             return true;
949         } else if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
950           and $this->isopen()                                                 // assignment not closed yet
951           and !empty($submission)                                             // submission must exist
952           and $submission->userid == $USER->id                                // his/her own submission
953           and ($this->count_user_files($submission->id)
954             or ($this->notes_allowed() and !empty($submission->data1)))) {    // something must be submitted
956             return true;
957         } else {
958             return false;
959         }
960     }
962     function can_update_notes($submission) {
963         global $USER;
965         if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
966           and $this->notes_allowed()                                          // notesd must be allowed
967           and $this->isopen()                                                 // assignment not closed yet
968           and (empty($submission) or $USER->id == $submission->userid)        // his/her own submission
969           and !$this->is_finalized($submission)) {                            // no updateingafter final submission
970             return true;
971         } else {
972             return false;
973         }
974     }
976     function notes_allowed() {
977         return (boolean)$this->assignment->var2;
978     }
980     function count_responsefiles($userid) {
981         if ($submission = $this->get_submission($userid)) {
982             $fs = get_file_storage();
983             $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'response', $submission->id, "id", false);
984             return count($files);
985         } else {
986             return 0;
987         }
988     }
990     function setup_elements(&$mform) {
991         global $CFG, $COURSE;
993         $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes'));
995         $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes);
996         $choices[0] = get_string('courseuploadlimit') . ' ('.display_size($COURSE->maxbytes).')';
997         $mform->addElement('select', 'maxbytes', get_string('maximumsize', 'assignment'), $choices);
998         $mform->setDefault('maxbytes', $CFG->assignment_maxbytes);
1000         $mform->addElement('select', 'resubmit', get_string('allowdeleting', 'assignment'), $ynoptions);
1001         $mform->addHelpButton('resubmit', 'allowdeleting', 'assignment');
1002         $mform->setDefault('resubmit', 1);
1004         $options = array();
1005         for($i = 1; $i <= 20; $i++) {
1006             $options[$i] = $i;
1007         }
1008         $mform->addElement('select', 'var1', get_string('allowmaxfiles', 'assignment'), $options);
1009         $mform->addHelpButton('var1', 'allowmaxfiles', 'assignment');
1010         $mform->setDefault('var1', 3);
1012         $mform->addElement('select', 'var2', get_string('allownotes', 'assignment'), $ynoptions);
1013         $mform->addHelpButton('var2', 'allownotes', 'assignment');
1014         $mform->setDefault('var2', 0);
1016         $mform->addElement('select', 'var3', get_string('hideintro', 'assignment'), $ynoptions);
1017         $mform->addHelpButton('var3', 'hideintro', 'assignment');
1018         $mform->setDefault('var3', 0);
1020         $mform->addElement('select', 'emailteachers', get_string('emailteachers', 'assignment'), $ynoptions);
1021         $mform->addHelpButton('emailteachers', 'emailteachers', 'assignment');
1022         $mform->setDefault('emailteachers', 0);
1024         $mform->addElement('select', 'var4', get_string('trackdrafts', 'assignment'), $ynoptions);
1025         $mform->addHelpButton('var4', 'trackdrafts', 'assignment');
1026         $mform->setDefault('var4', 1);
1028         $course_context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
1029         plagiarism_get_form_elements_module($mform, $course_context);
1030     }
1032     function portfolio_exportable() {
1033         return true;
1034     }
1036     function extend_settings_navigation($node) {
1037         global $CFG, $USER, $OUTPUT;
1039         // get users submission if there is one
1040         $submission = $this->get_submission();
1041         if (is_enrolled($this->context, $USER, 'mod/assignment:submit')) {
1042             $editable = $this->isopen() && (!$submission || $this->assignment->resubmit || !$submission->timemarked);
1043         } else {
1044             $editable = false;
1045         }
1047         // If the user has submitted something add a bit more stuff
1048         if ($submission) {
1049             // Add a view link to the settings nav
1050             $link = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
1051             $node->add(get_string('viewmysubmission', 'assignment'), $link, navigation_node::TYPE_SETTING);
1052             if (!empty($submission->timemodified)) {
1053                 $submittednode = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified));
1054                 $submittednode->text = preg_replace('#([^,])\s#', '$1&nbsp;', $submittednode->text);
1055                 $submittednode->add_class('note');
1056                 if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) {
1057                     $submittednode->add_class('early');
1058                 } else {
1059                     $submittednode->add_class('late');
1060                 }
1061             }
1062         }
1064         // Check if the user has uploaded any files, if so we can add some more stuff to the settings nav
1065         if ($submission && is_enrolled($this->context, $USER, 'mod/assignment:submit') && $this->count_user_files($submission->id)) {
1066             $fs = get_file_storage();
1067             if ($files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id, "timemodified", false)) {
1068                 if (!$this->drafts_tracked() or !$this->isopen() or $this->is_finalized($submission)) {
1069                     $filenode = $node->add(get_string('submission', 'assignment'));
1070                 } else {
1071                     $filenode = $node->add(get_string('submissiondraft', 'assignment'));
1072                 }
1073                 foreach ($files as $file) {
1074                     $filename = $file->get_filename();
1075                     $mimetype = $file->get_mimetype();
1076                     $link = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_assignment/submission/'.$submission->id.'/'.$filename);
1077                     $filenode->add($filename, $link, navigation_node::TYPE_SETTING, null, null, new pix_icon(file_mimetype_icon($mimetype),''));
1078                 }
1079             }
1080         }
1082         // Show a notes link if they are enabled
1083         if ($this->notes_allowed()) {
1084             $link = new moodle_url('/mod/assignment/upload.php', array('id'=>$this->cm->id, 'action'=>'editnotes', 'sesskey'=>sesskey()));
1085             $node->add(get_string('notes', 'assignment'), $link);
1086         }
1087     }
1089     /**
1090      * creates a zip of all assignment submissions and sends a zip to the browser
1091      */
1092     public function download_submissions() {
1093         global $CFG,$DB;
1094         require_once($CFG->libdir.'/filelib.php');
1095         $submissions = $this->get_submissions('','');
1096         if (empty($submissions)) {
1097             error("there are no submissions to download");
1098         }
1099         $filesforzipping = array();
1100         $fs = get_file_storage();
1102         $groupmode = groupmode($this->course,$this->cm);
1103         $groupid = 0;   // All users
1104         $groupname = '';
1105         if($groupmode) {
1106             $group = get_current_group($this->course->id, true);
1107             $groupid = $group->id;
1108             $groupname = $group->name.'-';
1109         }
1110         $filename = str_replace(' ', '_', clean_filename($this->course->shortname.'-'.$this->assignment->name.'-'.$groupname.$this->assignment->id.".zip")); //name of new zip file.
1111         foreach ($submissions as $submission) {
1112             $a_userid = $submission->userid; //get userid
1113             if ((groups_is_member($groupid,$a_userid)or !$groupmode or !$groupid)) {
1114                 $a_assignid = $submission->assignment; //get name of this assignment for use in the file names.
1115                 $a_user = $DB->get_record("user", array("id"=>$a_userid),'id,username,firstname,lastname'); //get user firstname/lastname
1117                 $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id, "timemodified", false);
1118                 foreach ($files as $file) {
1119                     //get files new name.
1120                     $fileext = strstr($file->get_filename(), '.');
1121                     $fileoriginal = str_replace($fileext, '', $file->get_filename());
1122                     $fileforzipname =  clean_filename(fullname($a_user) . "_" . $fileoriginal."_".$a_userid.$fileext);
1123                     //save file name to array for zipping.
1124                     $filesforzipping[$fileforzipname] = $file;
1125                 }
1126             }
1127         } // end of foreach loop
1128         if ($zipfile = assignment_pack_files($filesforzipping)) {
1129             send_temp_file($zipfile, $filename); //send file and delete after sending.
1130         }
1131     }
1134 class mod_assignment_upload_notes_form extends moodleform {
1136     function get_data() {
1137         $data = parent::get_data();
1138         if ($data) {
1139             $data->format = $data->text['format'];
1140             $data->text = $data->text['text'];
1141         }
1142         return $data;
1143     }
1145     function set_data($data) {
1146         if (!isset($data->format)) {
1147             $data->format = FORMAT_HTML;
1148         }
1149         if (isset($data->text)) {
1150             $data->text = array('text'=>$data->text, 'format'=>$data->format);
1151         }
1152         parent::set_data($data);
1153     }
1155     function definition() {
1156         $mform = $this->_form;
1158         // visible elements
1159         $mform->addElement('editor', 'text', get_string('notes', 'assignment'), null, null);
1160         $mform->setType('text', PARAM_RAW); // to be cleaned before display
1162         // hidden params
1163         $mform->addElement('hidden', 'id', 0);
1164         $mform->setType('id', PARAM_INT);
1165         $mform->addElement('hidden', 'action', 'savenotes');
1166         $mform->setType('action', PARAM_ALPHA);
1168         // buttons
1169         $this->add_action_buttons();
1170     }
1173 class mod_assignment_upload_response_form extends moodleform {
1174     function definition() {
1175         $mform = $this->_form;
1176         $instance = $this->_customdata;
1178         // visible elements
1179         $mform->addElement('filemanager', 'files_filemanager', get_string('uploadafile'), null, $instance->options);
1181         // hidden params
1182         $mform->addElement('hidden', 'id', $instance->cm->id);
1183         $mform->setType('id', PARAM_INT);
1184         $mform->addElement('hidden', 'contextid', $instance->contextid);
1185         $mform->setType('contextid', PARAM_INT);
1186         $mform->addElement('hidden', 'action', 'uploadresponse');
1187         $mform->setType('action', PARAM_ALPHA);
1188         $mform->addElement('hidden', 'mode', $instance->mode);
1189         $mform->setType('mode', PARAM_ALPHA);
1190         $mform->addElement('hidden', 'offset', $instance->offset);
1191         $mform->setType('offset', PARAM_INT);
1192         $mform->addElement('hidden', 'forcerefresh' , $instance->forcerefresh);
1193         $mform->setType('forcerefresh', PARAM_INT);
1194         $mform->addElement('hidden', 'userid', $instance->userid);
1195         $mform->setType('userid', PARAM_INT);
1197         // buttons
1198         $this->add_action_buttons(false, get_string('uploadthisfile'));
1199     }