PageRenderTime 71ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/mod/assignment/type/upload/assignment.class.php

https://bitbucket.org/ngmares/moodle
PHP | 1264 lines | 940 code | 222 blank | 102 comment | 177 complexity | bb13a3ffc8de91d1b6ee3a7116ead14d MD5 | raw file
Possible License(s): LGPL-2.1, AGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-3.0, Apache-2.0, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Assignment upload type implementation
  18. *
  19. * @package mod-assignment
  20. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21. */
  22. require_once(dirname(__FILE__).'/upload_form.php');
  23. require_once($CFG->libdir . '/portfoliolib.php');
  24. require_once($CFG->dirroot . '/mod/assignment/lib.php');
  25. define('ASSIGNMENT_STATUS_SUBMITTED', 'submitted'); // student thinks it is finished
  26. define('ASSIGNMENT_STATUS_CLOSED', 'closed'); // teacher prevents more submissions
  27. /**
  28. * Extend the base assignment class for assignments where you upload a single file
  29. *
  30. */
  31. class assignment_upload extends assignment_base {
  32. function assignment_upload($cmid='staticonly', $assignment=NULL, $cm=NULL, $course=NULL) {
  33. parent::assignment_base($cmid, $assignment, $cm, $course);
  34. $this->type = 'upload';
  35. }
  36. function view() {
  37. global $USER, $OUTPUT;
  38. require_capability('mod/assignment:view', $this->context);
  39. $cansubmit = has_capability('mod/assignment:submit', $this->context);
  40. add_to_log($this->course->id, 'assignment', 'view', "view.php?id={$this->cm->id}", $this->assignment->id, $this->cm->id);
  41. $this->view_header();
  42. if ($this->assignment->timeavailable > time()
  43. and !has_capability('mod/assignment:grade', $this->context) // grading user can see it anytime
  44. and $this->assignment->var3) { // force hiding before available date
  45. echo $OUTPUT->box_start('generalbox boxaligncenter', 'intro');
  46. print_string('notavailableyet', 'assignment');
  47. echo $OUTPUT->box_end();
  48. } else {
  49. $this->view_intro();
  50. }
  51. $this->view_dates();
  52. if (is_enrolled($this->context, $USER)) {
  53. if ($submission = $this->get_submission($USER->id)) {
  54. $filecount = $this->count_user_files($submission->id);
  55. } else {
  56. $filecount = 0;
  57. }
  58. if ($cansubmit or !empty($filecount)) { //if a user has submitted files using a previous role we should still show the files
  59. $this->view_feedback();
  60. if (!$this->drafts_tracked() or !$this->isopen() or $this->is_finalized($submission)) {
  61. echo $OUTPUT->heading(get_string('submission', 'assignment'), 3);
  62. } else {
  63. echo $OUTPUT->heading(get_string('submissiondraft', 'assignment'), 3);
  64. }
  65. if ($filecount and $submission) {
  66. echo $OUTPUT->box($this->print_user_files($USER->id, true), 'generalbox boxaligncenter', 'userfiles');
  67. } else {
  68. if (!$this->isopen() or $this->is_finalized($submission)) {
  69. echo $OUTPUT->box(get_string('nofiles', 'assignment'), 'generalbox boxaligncenter nofiles', 'userfiles');
  70. } else {
  71. echo $OUTPUT->box(get_string('nofilesyet', 'assignment'), 'generalbox boxaligncenter nofiles', 'userfiles');
  72. }
  73. }
  74. $this->view_upload_form();
  75. if ($this->notes_allowed()) {
  76. echo $OUTPUT->heading(get_string('notes', 'assignment'), 3);
  77. $this->view_notes();
  78. }
  79. $this->view_final_submission();
  80. }
  81. }
  82. $this->view_footer();
  83. }
  84. function view_feedback($submission=NULL) {
  85. global $USER, $CFG, $DB, $OUTPUT, $PAGE;
  86. require_once($CFG->libdir.'/gradelib.php');
  87. require_once("$CFG->dirroot/grade/grading/lib.php");
  88. if (!$submission) { /// Get submission for this assignment
  89. $userid = $USER->id;
  90. $submission = $this->get_submission($userid);
  91. } else {
  92. $userid = $submission->userid;
  93. }
  94. // Check the user can submit
  95. $canviewfeedback = ($userid == $USER->id && has_capability('mod/assignment:submit', $this->context, $USER->id, false));
  96. // If not then check if the user still has the view cap and has a previous submission
  97. $canviewfeedback = $canviewfeedback || (!empty($submission) && $submission->userid == $USER->id && has_capability('mod/assignment:view', $this->context));
  98. // Or if user can grade (is a teacher or admin)
  99. $canviewfeedback = $canviewfeedback || has_capability('mod/assignment:grade', $this->context);
  100. if (!$canviewfeedback) {
  101. // can not view or submit assignments -> no feedback
  102. return;
  103. }
  104. $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $userid);
  105. $item = $grading_info->items[0];
  106. $grade = $item->grades[$userid];
  107. if ($grade->hidden or $grade->grade === false) { // hidden or error
  108. return;
  109. }
  110. if ($grade->grade === null and empty($grade->str_feedback)) { // No grade to show yet
  111. if ($this->count_responsefiles($userid)) { // but possibly response files are present
  112. echo $OUTPUT->heading(get_string('responsefiles', 'assignment'), 3);
  113. $responsefiles = $this->print_responsefiles($userid, true);
  114. echo $OUTPUT->box($responsefiles, 'generalbox boxaligncenter');
  115. }
  116. return;
  117. }
  118. $graded_date = $grade->dategraded;
  119. $graded_by = $grade->usermodified;
  120. /// We need the teacher info
  121. if (!$teacher = $DB->get_record('user', array('id'=>$graded_by))) {
  122. print_error('cannotfindteacher');
  123. }
  124. /// Print the feedback
  125. echo $OUTPUT->heading(get_string('submissionfeedback', 'assignment'), 3);
  126. echo '<table cellspacing="0" class="feedback">';
  127. echo '<tr>';
  128. echo '<td class="left picture">';
  129. echo $OUTPUT->user_picture($teacher);
  130. echo '</td>';
  131. echo '<td class="topic">';
  132. echo '<div class="from">';
  133. echo '<div class="fullname">'.fullname($teacher).'</div>';
  134. echo '<div class="time">'.userdate($graded_date).'</div>';
  135. echo '</div>';
  136. echo '</td>';
  137. echo '</tr>';
  138. echo '<tr>';
  139. echo '<td class="left side">&nbsp;</td>';
  140. echo '<td class="content">';
  141. $gradestr = '<div class="grade">'. get_string("grade").': '.$grade->str_long_grade. '</div>';
  142. if (!empty($submission) && $controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) {
  143. $controller->set_grade_range(make_grades_menu($this->assignment->grade));
  144. echo $controller->render_grade($PAGE, $submission->id, $item, $gradestr, has_capability('mod/assignment:grade', $this->context));
  145. } else {
  146. echo $gradestr;
  147. }
  148. echo '<div class="clearer"></div>';
  149. echo '<div class="comment">';
  150. echo $grade->str_feedback;
  151. echo '</div>';
  152. echo '</tr>';
  153. echo '<tr>';
  154. echo '<td class="left side">&nbsp;</td>';
  155. echo '<td class="content">';
  156. echo $this->print_responsefiles($userid, true);
  157. echo '</tr>';
  158. echo '</table>';
  159. }
  160. function view_upload_form() {
  161. global $CFG, $USER, $OUTPUT;
  162. $submission = $this->get_submission($USER->id);
  163. if ($this->is_finalized($submission)) {
  164. // no uploading
  165. return;
  166. }
  167. if ($this->can_upload_file($submission)) {
  168. $fs = get_file_storage();
  169. // edit files in another page
  170. if ($submission) {
  171. if ($files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id, "timemodified", false)) {
  172. $str = get_string('editthesefiles', 'assignment');
  173. } else {
  174. $str = get_string('uploadfiles', 'assignment');
  175. }
  176. } else {
  177. $str = get_string('uploadfiles', 'assignment');
  178. }
  179. echo $OUTPUT->single_button(new moodle_url('/mod/assignment/type/upload/upload.php', array('contextid'=>$this->context->id, 'userid'=>$USER->id)), $str, 'get');
  180. }
  181. }
  182. function view_notes() {
  183. global $USER, $OUTPUT;
  184. if ($submission = $this->get_submission($USER->id)
  185. and !empty($submission->data1)) {
  186. echo $OUTPUT->box(format_text($submission->data1, FORMAT_HTML, array('overflowdiv'=>true)), 'generalbox boxaligncenter boxwidthwide');
  187. } else {
  188. echo $OUTPUT->box(get_string('notesempty', 'assignment'), 'generalbox boxaligncenter');
  189. }
  190. if ($this->can_update_notes($submission)) {
  191. $options = array ('id'=>$this->cm->id, 'action'=>'editnotes');
  192. echo '<div style="text-align:center">';
  193. echo $OUTPUT->single_button(new moodle_url('upload.php', $options), get_string('edit'));
  194. echo '</div>';
  195. }
  196. }
  197. function view_final_submission() {
  198. global $CFG, $USER, $OUTPUT;
  199. $submission = $this->get_submission($USER->id);
  200. if ($this->isopen() and $this->can_finalize($submission)) {
  201. //print final submit button
  202. echo $OUTPUT->heading(get_string('submitformarking','assignment'), 3);
  203. echo '<div style="text-align:center">';
  204. echo '<form method="post" action="upload.php">';
  205. echo '<fieldset class="invisiblefieldset">';
  206. echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
  207. echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
  208. echo '<input type="hidden" name="action" value="finalize" />';
  209. echo '<input type="submit" name="formarking" value="'.get_string('sendformarking', 'assignment').'" />';
  210. echo '</fieldset>';
  211. echo '</form>';
  212. echo '</div>';
  213. } else if (!$this->isopen()) {
  214. echo $OUTPUT->heading(get_string('nomoresubmissions','assignment'), 3);
  215. } else if ($this->drafts_tracked() and $state = $this->is_finalized($submission)) {
  216. if ($state == ASSIGNMENT_STATUS_SUBMITTED) {
  217. echo $OUTPUT->heading(get_string('submitedformarking','assignment'), 3);
  218. } else {
  219. echo $OUTPUT->heading(get_string('nomoresubmissions','assignment'), 3);
  220. }
  221. } else {
  222. //no submission yet
  223. }
  224. }
  225. /**
  226. * Return true if var3 == hide description till available day
  227. *
  228. *@return boolean
  229. */
  230. function description_is_hidden() {
  231. return ($this->assignment->var3 && (time() <= $this->assignment->timeavailable));
  232. }
  233. function print_student_answer($userid, $return=false){
  234. global $CFG, $OUTPUT, $PAGE;
  235. $submission = $this->get_submission($userid);
  236. $output = '';
  237. if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission)) {
  238. $output .= '<strong>'.get_string('draft', 'assignment').':</strong> ';
  239. }
  240. if ($this->notes_allowed() and !empty($submission->data1)) {
  241. $link = new moodle_url("/mod/assignment/type/upload/notes.php", array('id'=>$this->cm->id, 'userid'=>$userid));
  242. $action = new popup_action('click', $link, 'notes', array('height' => 500, 'width' => 780));
  243. $output .= $OUTPUT->action_link($link, get_string('notes', 'assignment'), $action, array('title'=>get_string('notes', 'assignment')));
  244. $output .= '&nbsp;';
  245. }
  246. $renderer = $PAGE->get_renderer('mod_assignment');
  247. $output = $OUTPUT->box_start('files').$output;
  248. $output .= $renderer->assignment_files($this->context, $submission->id);
  249. $output .= $OUTPUT->box_end();
  250. return $output;
  251. }
  252. /**
  253. * Produces a list of links to the files uploaded by a user
  254. *
  255. * @param $userid int optional id of the user. If 0 then $USER->id is used.
  256. * @param $return boolean optional defaults to false. If true the list is returned rather than printed
  257. * @return string optional
  258. */
  259. function print_user_files($userid=0, $return=false) {
  260. global $CFG, $USER, $OUTPUT, $PAGE;
  261. $mode = optional_param('mode', '', PARAM_ALPHA);
  262. $offset = optional_param('offset', 0, PARAM_INT);
  263. if (!$userid) {
  264. if (!isloggedin()) {
  265. return '';
  266. }
  267. $userid = $USER->id;
  268. }
  269. $output = $OUTPUT->box_start('files');
  270. $submission = $this->get_submission($userid);
  271. // only during grading
  272. if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission) and !empty($mode)) {
  273. $output .= '<strong>'.get_string('draft', 'assignment').':</strong><br />';
  274. }
  275. if ($this->notes_allowed() and !empty($submission->data1) and !empty($mode)) { // only during grading
  276. $npurl = $CFG->wwwroot."/mod/assignment/type/upload/notes.php?id={$this->cm->id}&amp;userid=$userid&amp;offset=$offset&amp;mode=single";
  277. $output .= '<a href="'.$npurl.'">'.get_string('notes', 'assignment').'</a><br />';
  278. }
  279. 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
  280. if ($this->can_unfinalize($submission)) {
  281. //$options = array ('id'=>$this->cm->id, 'userid'=>$userid, 'action'=>'unfinalize', 'mode'=>$mode, 'offset'=>$offset);
  282. $output .= '<br /><input type="submit" name="unfinalize" value="'.get_string('unfinalize', 'assignment').'" />';
  283. $output .= $OUTPUT->help_icon('unfinalize', 'assignment');
  284. } else if ($this->can_finalize($submission)) {
  285. //$options = array ('id'=>$this->cm->id, 'userid'=>$userid, 'action'=>'finalizeclose', 'mode'=>$mode, 'offset'=>$offset);
  286. $output .= '<br /><input type="submit" name="finalize" value="'.get_string('finalize', 'assignment').'" />';
  287. }
  288. }
  289. if ($submission) {
  290. $renderer = $PAGE->get_renderer('mod_assignment');
  291. $output .= $renderer->assignment_files($this->context, $submission->id);
  292. }
  293. $output .= $OUTPUT->box_end();
  294. if ($return) {
  295. return $output;
  296. }
  297. echo $output;
  298. }
  299. function submissions($mode) {
  300. // redirects out of form to process (un)finalizing.
  301. $unfinalize = optional_param('unfinalize', FALSE, PARAM_TEXT);
  302. $finalize = optional_param('finalize', FALSE, PARAM_TEXT);
  303. if ($unfinalize) {
  304. $this->unfinalize('single');
  305. } else if ($finalize) {
  306. $this->finalize('single');
  307. }
  308. if ($unfinalize || $finalize) {
  309. $mode = 'singlenosave';
  310. }
  311. parent::submissions($mode);
  312. }
  313. function process_feedback($formdata=null) {
  314. if (!$feedback = data_submitted() or !confirm_sesskey()) { // No incoming data?
  315. return false;
  316. }
  317. $userid = required_param('userid', PARAM_INT);
  318. $offset = required_param('offset', PARAM_INT);
  319. $mform = $this->display_submission($offset, $userid, false);
  320. parent::process_feedback($mform);
  321. }
  322. /**
  323. * Counts all complete (real) assignment submissions by enrolled students. This overrides assignment_base::count_real_submissions().
  324. * This is necessary for advanced file uploads where we need to check that the data2 field is equal to "submitted" to determine
  325. * if a submission is complete.
  326. *
  327. * @param int $groupid (optional) If nonzero then count is restricted to this group
  328. * @return int The number of submissions
  329. */
  330. function count_real_submissions($groupid=0) {
  331. global $DB;
  332. // Grab the context assocated with our course module
  333. $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
  334. // Get ids of users enrolled in the given course.
  335. list($enroledsql, $params) = get_enrolled_sql($context, 'mod/assignment:view', $groupid);
  336. $params['assignmentid'] = $this->cm->instance;
  337. // Get ids of users enrolled in the given course.
  338. return $DB->count_records_sql("SELECT COUNT('x')
  339. FROM {assignment_submissions} s
  340. LEFT JOIN {assignment} a ON a.id = s.assignment
  341. INNER JOIN ($enroledsql) u ON u.id = s.userid
  342. WHERE s.assignment = :assignmentid AND
  343. s.data2 = 'submitted'", $params);
  344. }
  345. function print_responsefiles($userid, $return=false) {
  346. global $CFG, $USER, $OUTPUT, $PAGE;
  347. $mode = optional_param('mode', '', PARAM_ALPHA);
  348. $offset = optional_param('offset', 0, PARAM_INT);
  349. $output = $OUTPUT->box_start('responsefiles');
  350. $candelete = $this->can_manage_responsefiles();
  351. $strdelete = get_string('delete');
  352. $fs = get_file_storage();
  353. $browser = get_file_browser();
  354. if ($submission = $this->get_submission($userid)) {
  355. $renderer = $PAGE->get_renderer('mod_assignment');
  356. $output .= $renderer->assignment_files($this->context, $submission->id, 'response');
  357. }
  358. $output .= $OUTPUT->box_end();
  359. if ($return) {
  360. return $output;
  361. }
  362. echo $output;
  363. }
  364. /**
  365. * Upload files
  366. * upload_file function requires moodle form instance and file manager options
  367. * @param object $mform
  368. * @param array $options
  369. */
  370. function upload($mform = null, $filemanager_options = null) {
  371. $action = required_param('action', PARAM_ALPHA);
  372. switch ($action) {
  373. case 'finalize':
  374. $this->finalize();
  375. break;
  376. case 'finalizeclose':
  377. $this->finalizeclose();
  378. break;
  379. case 'unfinalize':
  380. $this->unfinalize();
  381. break;
  382. case 'uploadresponse':
  383. $this->upload_responsefile($mform, $filemanager_options);
  384. break;
  385. case 'uploadfile':
  386. $this->upload_file($mform, $filemanager_options);
  387. case 'savenotes':
  388. case 'editnotes':
  389. $this->upload_notes();
  390. default:
  391. print_error('unknowuploadaction', '', '', $action);
  392. }
  393. }
  394. function upload_notes() {
  395. global $CFG, $USER, $OUTPUT, $DB;
  396. $action = required_param('action', PARAM_ALPHA);
  397. $returnurl = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  398. $mform = new mod_assignment_upload_notes_form();
  399. $defaults = new stdClass();
  400. $defaults->id = $this->cm->id;
  401. if ($submission = $this->get_submission($USER->id)) {
  402. $defaults->text = clean_text($submission->data1);
  403. } else {
  404. $defaults->text = '';
  405. }
  406. $mform->set_data($defaults);
  407. if ($mform->is_cancelled()) {
  408. $returnurl = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  409. redirect($returnurl);
  410. }
  411. if (!$this->can_update_notes($submission)) {
  412. $this->view_header(get_string('upload'));
  413. echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
  414. echo $OUTPUT->continue_button($returnurl);
  415. $this->view_footer();
  416. die;
  417. }
  418. if ($data = $mform->get_data() and $action == 'savenotes') {
  419. $submission = $this->get_submission($USER->id, true); // get or create submission
  420. $updated = new stdClass();
  421. $updated->id = $submission->id;
  422. $updated->timemodified = time();
  423. $updated->data1 = $data->text;
  424. $DB->update_record('assignment_submissions', $updated);
  425. add_to_log($this->course->id, 'assignment', 'upload', 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
  426. redirect($returnurl);
  427. $submission = $this->get_submission($USER->id);
  428. $this->update_grade($submission);
  429. }
  430. /// show notes edit form
  431. $this->view_header(get_string('notes', 'assignment'));
  432. echo $OUTPUT->heading(get_string('notes', 'assignment'));
  433. $mform->display();
  434. $this->view_footer();
  435. die;
  436. }
  437. function upload_responsefile($mform, $options) {
  438. global $CFG, $USER, $OUTPUT, $PAGE;
  439. $userid = required_param('userid', PARAM_INT);
  440. $mode = required_param('mode', PARAM_ALPHA);
  441. $offset = required_param('offset', PARAM_INT);
  442. $returnurl = new moodle_url("submissions.php", array('id'=>$this->cm->id,'userid'=>$userid,'mode'=>$mode,'offset'=>$offset)); //not xhtml, just url.
  443. if ($formdata = $mform->get_data() and $this->can_manage_responsefiles()) {
  444. $fs = get_file_storage();
  445. $submission = $this->get_submission($userid, true, true);
  446. if ($formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $this->context, 'mod_assignment', 'response', $submission->id)) {
  447. $returnurl = new moodle_url("/mod/assignment/submissions.php", array('id'=>$this->cm->id,'userid'=>$formdata->userid,'mode'=>$formdata->mode,'offset'=>$formdata->offset));
  448. redirect($returnurl->out(false));
  449. }
  450. }
  451. $PAGE->set_title(get_string('upload'));
  452. echo $OUTPUT->header();
  453. echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
  454. echo $OUTPUT->continue_button($returnurl->out(true));
  455. echo $OUTPUT->footer();
  456. die;
  457. }
  458. function upload_file($mform, $options) {
  459. global $CFG, $USER, $DB, $OUTPUT;
  460. $returnurl = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  461. $submission = $this->get_submission($USER->id);
  462. if (!$this->can_upload_file($submission)) {
  463. $this->view_header(get_string('upload'));
  464. echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
  465. echo $OUTPUT->continue_button($returnurl);
  466. $this->view_footer();
  467. die;
  468. }
  469. if ($formdata = $mform->get_data()) {
  470. $fs = get_file_storage();
  471. $submission = $this->get_submission($USER->id, true); //create new submission if needed
  472. $fs->delete_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id);
  473. $formdata = file_postupdate_standard_filemanager($formdata, 'files', $options, $this->context, 'mod_assignment', 'submission', $submission->id);
  474. $updates = new stdClass();
  475. $updates->id = $submission->id;
  476. $updates->timemodified = time();
  477. $DB->update_record('assignment_submissions', $updates);
  478. add_to_log($this->course->id, 'assignment', 'upload',
  479. 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
  480. $this->update_grade($submission);
  481. if (!$this->drafts_tracked()) {
  482. $this->email_teachers($submission);
  483. }
  484. // send files to event system
  485. $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id);
  486. // Let Moodle know that assessable files were uploaded (eg for plagiarism detection)
  487. $eventdata = new stdClass();
  488. $eventdata->modulename = 'assignment';
  489. $eventdata->cmid = $this->cm->id;
  490. $eventdata->itemid = $submission->id;
  491. $eventdata->courseid = $this->course->id;
  492. $eventdata->userid = $USER->id;
  493. if ($files) {
  494. $eventdata->files = $files; // This is depreceated - please use pathnamehashes instead!
  495. }
  496. $eventdata->pathnamehashes = array_keys($files);
  497. events_trigger('assessable_file_uploaded', $eventdata);
  498. $returnurl = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  499. redirect($returnurl);
  500. }
  501. $this->view_header(get_string('upload'));
  502. echo $OUTPUT->notification(get_string('uploaderror', 'assignment'));
  503. echo $OUTPUT->continue_button($returnurl);
  504. $this->view_footer();
  505. die;
  506. }
  507. function send_file($filearea, $args, $forcedownload, array $options=array()) {
  508. global $CFG, $DB, $USER;
  509. require_once($CFG->libdir.'/filelib.php');
  510. require_login($this->course, false, $this->cm);
  511. if ($filearea === 'submission') {
  512. $submissionid = (int)array_shift($args);
  513. if (!$submission = $DB->get_record('assignment_submissions', array('assignment'=>$this->assignment->id, 'id'=>$submissionid))) {
  514. return false;
  515. }
  516. if ($USER->id != $submission->userid and !has_capability('mod/assignment:grade', $this->context)) {
  517. return false;
  518. }
  519. $relativepath = implode('/', $args);
  520. $fullpath = "/{$this->context->id}/mod_assignment/submission/$submission->id/$relativepath";
  521. $fs = get_file_storage();
  522. if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
  523. return false;
  524. }
  525. send_stored_file($file, 0, 0, true, $options); // download MUST be forced - security!
  526. } else if ($filearea === 'response') {
  527. $submissionid = (int)array_shift($args);
  528. if (!$submission = $DB->get_record('assignment_submissions', array('assignment'=>$this->assignment->id, 'id'=>$submissionid))) {
  529. return false;
  530. }
  531. if ($USER->id != $submission->userid and !has_capability('mod/assignment:grade', $this->context)) {
  532. return false;
  533. }
  534. $relativepath = implode('/', $args);
  535. $fullpath = "/{$this->context->id}/mod_assignment/response/$submission->id/$relativepath";
  536. $fs = get_file_storage();
  537. if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
  538. return false;
  539. }
  540. send_stored_file($file, 0, 0, true, $options);
  541. }
  542. return false;
  543. }
  544. function finalize($forcemode=null) {
  545. global $USER, $DB, $OUTPUT;
  546. $userid = optional_param('userid', $USER->id, PARAM_INT);
  547. $offset = optional_param('offset', 0, PARAM_INT);
  548. $confirm = optional_param('confirm', 0, PARAM_BOOL);
  549. $returnurl = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  550. $submission = $this->get_submission($userid);
  551. if ($forcemode!=null) {
  552. $returnurl = new moodle_url('/mod/assignment/submissions.php',
  553. array('id'=>$this->cm->id,
  554. 'userid'=>$userid,
  555. 'mode'=>$forcemode,
  556. 'offset'=>$offset
  557. ));
  558. }
  559. if (!$this->can_finalize($submission)) {
  560. redirect($returnurl->out(false)); // probably already graded, redirect to assignment page, the reason should be obvious
  561. }
  562. if ($forcemode==null) {
  563. if (!data_submitted() or !$confirm or !confirm_sesskey()) {
  564. $optionsno = array('id'=>$this->cm->id);
  565. $optionsyes = array ('id'=>$this->cm->id, 'confirm'=>1, 'action'=>'finalize', 'sesskey'=>sesskey());
  566. $this->view_header(get_string('submitformarking', 'assignment'));
  567. echo $OUTPUT->heading(get_string('submitformarking', 'assignment'));
  568. echo $OUTPUT->confirm(get_string('onceassignmentsent', 'assignment'), new moodle_url('upload.php', $optionsyes),new moodle_url( 'view.php', $optionsno));
  569. $this->view_footer();
  570. die;
  571. }
  572. }
  573. $updated = new stdClass();
  574. $updated->id = $submission->id;
  575. $updated->data2 = ASSIGNMENT_STATUS_SUBMITTED;
  576. $updated->timemodified = time();
  577. $DB->update_record('assignment_submissions', $updated);
  578. add_to_log($this->course->id, 'assignment', 'upload', //TODO: add finalize action to log
  579. 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
  580. $submission = $this->get_submission($userid);
  581. $this->update_grade($submission);
  582. $this->email_teachers($submission);
  583. // Trigger assessable_files_done event to show files are complete
  584. $eventdata = new stdClass();
  585. $eventdata->modulename = 'assignment';
  586. $eventdata->cmid = $this->cm->id;
  587. $eventdata->itemid = $submission->id;
  588. $eventdata->courseid = $this->course->id;
  589. $eventdata->userid = $userid;
  590. events_trigger('assessable_files_done', $eventdata);
  591. if ($forcemode==null) {
  592. redirect($returnurl->out(false));
  593. }
  594. }
  595. function finalizeclose() {
  596. global $DB;
  597. $userid = optional_param('userid', 0, PARAM_INT);
  598. $mode = required_param('mode', PARAM_ALPHA);
  599. $offset = required_param('offset', PARAM_INT);
  600. $returnurl = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'userid'=>$userid, 'mode'=>$mode, 'offset'=>$offset, 'forcerefresh'=>1));
  601. // create but do not add student submission date
  602. $submission = $this->get_submission($userid, true, true);
  603. if (!data_submitted() or !$this->can_finalize($submission) or !confirm_sesskey()) {
  604. redirect($returnurl); // probably closed already
  605. }
  606. $updated = new stdClass();
  607. $updated->id = $submission->id;
  608. $updated->data2 = ASSIGNMENT_STATUS_CLOSED;
  609. $DB->update_record('assignment_submissions', $updated);
  610. add_to_log($this->course->id, 'assignment', 'upload', //TODO: add finalize action to log
  611. 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
  612. $submission = $this->get_submission($userid, false, true);
  613. $this->update_grade($submission);
  614. redirect($returnurl);
  615. }
  616. function unfinalize($forcemode=null) {
  617. global $DB;
  618. $userid = required_param('userid', PARAM_INT);
  619. $mode = required_param('mode', PARAM_ALPHA);
  620. $offset = required_param('offset', PARAM_INT);
  621. if ($forcemode!=null) {
  622. $mode=$forcemode;
  623. }
  624. $returnurl = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'userid'=>$userid, 'mode'=>$mode, 'offset'=>$offset, 'forcerefresh'=>1) );
  625. if (data_submitted()
  626. and $submission = $this->get_submission($userid)
  627. and $this->can_unfinalize($submission)
  628. and confirm_sesskey()) {
  629. $updated = new stdClass();
  630. $updated->id = $submission->id;
  631. $updated->data2 = '';
  632. $DB->update_record('assignment_submissions', $updated);
  633. //TODO: add unfinalize action to log
  634. add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->cm->id.'&userid='.$userid.'&mode='.$mode.'&offset='.$offset, $this->assignment->id, $this->cm->id);
  635. $submission = $this->get_submission($userid);
  636. $this->update_grade($submission);
  637. }
  638. if ($forcemode==null) {
  639. redirect($returnurl);
  640. }
  641. }
  642. function delete() {
  643. $action = optional_param('action', '', PARAM_ALPHA);
  644. switch ($action) {
  645. case 'response':
  646. $this->delete_responsefile();
  647. break;
  648. default:
  649. $this->delete_file();
  650. }
  651. die;
  652. }
  653. function delete_responsefile() {
  654. global $CFG, $OUTPUT,$PAGE;
  655. $file = required_param('file', PARAM_FILE);
  656. $userid = required_param('userid', PARAM_INT);
  657. $mode = required_param('mode', PARAM_ALPHA);
  658. $offset = required_param('offset', PARAM_INT);
  659. $confirm = optional_param('confirm', 0, PARAM_BOOL);
  660. $returnurl = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'userid'=>$userid, 'mode'=>$mode, 'offset'=>$offset));
  661. if (!$this->can_manage_responsefiles()) {
  662. redirect($returnurl);
  663. }
  664. $urlreturn = 'submissions.php';
  665. $optionsreturn = array('id'=>$this->cm->id, 'offset'=>$offset, 'mode'=>$mode, 'userid'=>$userid);
  666. if (!data_submitted() or !$confirm or !confirm_sesskey()) {
  667. $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'action'=>'response', 'mode'=>$mode, 'offset'=>$offset, 'sesskey'=>sesskey());
  668. $PAGE->set_title(get_string('delete'));
  669. echo $OUTPUT->header();
  670. echo $OUTPUT->heading(get_string('delete'));
  671. echo $OUTPUT->confirm(get_string('confirmdeletefile', 'assignment', $file), new moodle_url('delete.php', $optionsyes), new moodle_url($urlreturn, $optionsreturn));
  672. echo $OUTPUT->footer();
  673. die;
  674. }
  675. if ($submission = $this->get_submission($userid)) {
  676. $fs = get_file_storage();
  677. if ($file = $fs->get_file($this->context->id, 'mod_assignment', 'response', $submission->id, '/', $file)) {
  678. $file->delete();
  679. }
  680. }
  681. redirect($returnurl);
  682. }
  683. function delete_file() {
  684. global $CFG, $DB, $OUTPUT, $PAGE;
  685. $file = required_param('file', PARAM_FILE);
  686. $userid = required_param('userid', PARAM_INT);
  687. $confirm = optional_param('confirm', 0, PARAM_BOOL);
  688. $mode = optional_param('mode', '', PARAM_ALPHA);
  689. $offset = optional_param('offset', 0, PARAM_INT);
  690. require_login($this->course, false, $this->cm);
  691. if (empty($mode)) {
  692. $urlreturn = 'view.php';
  693. $optionsreturn = array('id'=>$this->cm->id);
  694. $returnurl = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  695. } else {
  696. $urlreturn = 'submissions.php';
  697. $optionsreturn = array('id'=>$this->cm->id, 'offset'=>$offset, 'mode'=>$mode, 'userid'=>$userid);
  698. $returnurl = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id, 'offset'=>$offset, 'userid'=>$userid));
  699. }
  700. if (!$submission = $this->get_submission($userid) // incorrect submission
  701. or !$this->can_delete_files($submission)) { // can not delete
  702. $this->view_header(get_string('delete'));
  703. echo $OUTPUT->notification(get_string('cannotdeletefiles', 'assignment'));
  704. echo $OUTPUT->continue_button($returnurl);
  705. $this->view_footer();
  706. die;
  707. }
  708. if (!data_submitted() or !$confirm or !confirm_sesskey()) {
  709. $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'sesskey'=>sesskey(), 'mode'=>$mode, 'offset'=>$offset, 'sesskey'=>sesskey());
  710. if (empty($mode)) {
  711. $this->view_header(get_string('delete'));
  712. } else {
  713. $PAGE->set_title(get_string('delete'));
  714. echo $OUTPUT->header();
  715. }
  716. echo $OUTPUT->heading(get_string('delete'));
  717. echo $OUTPUT->confirm(get_string('confirmdeletefile', 'assignment', $file), new moodle_url('delete.php', $optionsyes), new moodle_url($urlreturn, $optionsreturn));
  718. if (empty($mode)) {
  719. $this->view_footer();
  720. } else {
  721. echo $OUTPUT->footer();
  722. }
  723. die;
  724. }
  725. $fs = get_file_storage();
  726. if ($file = $fs->get_file($this->context->id, 'mod_assignment', 'submission', $submission->id, '/', $file)) {
  727. $file->delete();
  728. $submission->timemodified = time();
  729. $DB->update_record('assignment_submissions', $submission);
  730. add_to_log($this->course->id, 'assignment', 'upload', //TODO: add delete action to log
  731. 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
  732. $this->update_grade($submission);
  733. }
  734. redirect($returnurl);
  735. }
  736. function can_upload_file($submission) {
  737. global $USER;
  738. if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
  739. and $this->isopen() // assignment not closed yet
  740. and (empty($submission) or ($submission->userid == $USER->id)) // his/her own submission
  741. and !$this->is_finalized($submission)) { // no uploading after final submission
  742. return true;
  743. } else {
  744. return false;
  745. }
  746. }
  747. function can_manage_responsefiles() {
  748. if (has_capability('mod/assignment:grade', $this->context)) {
  749. return true;
  750. } else {
  751. return false;
  752. }
  753. }
  754. function can_delete_files($submission) {
  755. global $USER;
  756. if (has_capability('mod/assignment:grade', $this->context)) {
  757. return true;
  758. }
  759. if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
  760. and $this->isopen() // assignment not closed yet
  761. and $this->assignment->resubmit // deleting allowed
  762. and $USER->id == $submission->userid // his/her own submission
  763. and !$this->is_finalized($submission)) { // no deleting after final submission
  764. return true;
  765. } else {
  766. return false;
  767. }
  768. }
  769. function drafts_tracked() {
  770. return !empty($this->assignment->var4);
  771. }
  772. /**
  773. * Returns submission status
  774. * @param object $submission - may be empty
  775. * @return string submission state - empty, ASSIGNMENT_STATUS_SUBMITTED or ASSIGNMENT_STATUS_CLOSED
  776. */
  777. function is_finalized($submission) {
  778. if (!$this->drafts_tracked()) {
  779. return '';
  780. } else if (empty($submission)) {
  781. return '';
  782. } else if ($submission->data2 == ASSIGNMENT_STATUS_SUBMITTED or $submission->data2 == ASSIGNMENT_STATUS_CLOSED) {
  783. return $submission->data2;
  784. } else {
  785. return '';
  786. }
  787. }
  788. function can_unfinalize($submission) {
  789. if(is_bool($submission)) {
  790. return false;
  791. }
  792. if (!$this->drafts_tracked()) {
  793. return false;
  794. }
  795. if (has_capability('mod/assignment:grade', $this->context)
  796. and $this->isopen()
  797. and $this->is_finalized($submission)) {
  798. return true;
  799. } else {
  800. return false;
  801. }
  802. }
  803. function can_finalize($submission) {
  804. global $USER;
  805. if(is_bool($submission)) {
  806. return false;
  807. }
  808. if (!$this->drafts_tracked()) {
  809. return false;
  810. }
  811. if ($this->is_finalized($submission)) {
  812. return false;
  813. }
  814. if (has_capability('mod/assignment:grade', $this->context)) {
  815. return true;
  816. } else if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
  817. and $this->isopen() // assignment not closed yet
  818. and !empty($submission) // submission must exist
  819. and $submission->userid == $USER->id // his/her own submission
  820. and ($this->count_user_files($submission->id)
  821. or ($this->notes_allowed() and !empty($submission->data1)))) { // something must be submitted
  822. return true;
  823. } else {
  824. return false;
  825. }
  826. }
  827. function can_update_notes($submission) {
  828. global $USER;
  829. if (is_enrolled($this->context, $USER, 'mod/assignment:submit')
  830. and $this->notes_allowed() // notesd must be allowed
  831. and $this->isopen() // assignment not closed yet
  832. and (empty($submission) or $USER->id == $submission->userid) // his/her own submission
  833. and !$this->is_finalized($submission)) { // no updateingafter final submission
  834. return true;
  835. } else {
  836. return false;
  837. }
  838. }
  839. function notes_allowed() {
  840. return (boolean)$this->assignment->var2;
  841. }
  842. function count_responsefiles($userid) {
  843. if ($submission = $this->get_submission($userid)) {
  844. $fs = get_file_storage();
  845. $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'response', $submission->id, "id", false);
  846. return count($files);
  847. } else {
  848. return 0;
  849. }
  850. }
  851. function setup_elements(&$mform) {
  852. global $CFG, $COURSE;
  853. $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes'));
  854. $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes);
  855. $choices[0] = get_string('courseuploadlimit') . ' ('.display_size($COURSE->maxbytes).')';
  856. $mform->addElement('select', 'maxbytes', get_string('maximumsize', 'assignment'), $choices);
  857. $mform->setDefault('maxbytes', $CFG->assignment_maxbytes);
  858. $mform->addElement('select', 'resubmit', get_string('allowdeleting', 'assignment'), $ynoptions);
  859. $mform->addHelpButton('resubmit', 'allowdeleting', 'assignment');
  860. $mform->setDefault('resubmit', 1);
  861. $options = array();
  862. for($i = 1; $i <= 20; $i++) {
  863. $options[$i] = $i;
  864. }
  865. $mform->addElement('select', 'var1', get_string('allowmaxfiles', 'assignment'), $options);
  866. $mform->addHelpButton('var1', 'allowmaxfiles', 'assignment');
  867. $mform->setDefault('var1', 3);
  868. $mform->addElement('select', 'var2', get_string('allownotes', 'assignment'), $ynoptions);
  869. $mform->addHelpButton('var2', 'allownotes', 'assignment');
  870. $mform->setDefault('var2', 0);
  871. $mform->addElement('select', 'var3', get_string('hideintro', 'assignment'), $ynoptions);
  872. $mform->addHelpButton('var3', 'hideintro', 'assignment');
  873. $mform->setDefault('var3', 0);
  874. $mform->addElement('select', 'emailteachers', get_string('emailteachers', 'assignment'), $ynoptions);
  875. $mform->addHelpButton('emailteachers', 'emailteachers', 'assignment');
  876. $mform->setDefault('emailteachers', 0);
  877. $mform->addElement('select', 'var4', get_string('trackdrafts', 'assignment'), $ynoptions);
  878. $mform->addHelpButton('var4', 'trackdrafts', 'assignment');
  879. $mform->setDefault('var4', 1);
  880. $course_context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
  881. plagiarism_get_form_elements_module($mform, $course_context);
  882. }
  883. function portfolio_exportable() {
  884. return true;
  885. }
  886. function extend_settings_navigation($node) {
  887. global $CFG, $USER, $OUTPUT;
  888. // get users submission if there is one
  889. $submission = $this->get_submission();
  890. if (is_enrolled($this->context, $USER, 'mod/assignment:submit')) {
  891. $editable = $this->isopen() && (!$submission || $this->assignment->resubmit || !$submission->timemarked);
  892. } else {
  893. $editable = false;
  894. }
  895. // If the user has submitted something add some related links and data
  896. if (isset($submission->data2) AND $submission->data2 == 'submitted') {
  897. // Add a view link to the settings nav
  898. $link = new moodle_url('/mod/assignment/view.php', array('id'=>$this->cm->id));
  899. $node->add(get_string('viewmysubmission', 'assignment'), $link, navigation_node::TYPE_SETTING);
  900. if (!empty($submission->timemodified)) {
  901. $submittednode = $node->add(get_string('submitted', 'assignment') . ' ' . userdate($submission->timemodified));
  902. $submittednode->text = preg_replace('#([^,])\s#', '$1&nbsp;', $submittednode->text);
  903. $submittednode->add_class('note');
  904. if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) {
  905. $submittednode->add_class('early');
  906. } else {
  907. $submittednode->add_class('late');
  908. }
  909. }
  910. }
  911. // Check if the user has uploaded any files, if so we can add some more stuff to the settings nav
  912. if ($submission && is_enrolled($this->context, $USER, 'mod/assignment:submit') && $this->count_user_files($submission->id)) {
  913. $fs = get_file_storage();
  914. if ($files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id, "timemodified", false)) {
  915. if (!$this->drafts_tracked() or !$this->isopen() or $this->is_finalized($submission)) {
  916. $filenode = $node->add(get_string('submission', 'assignment'));
  917. } else {
  918. $filenode = $node->add(get_string('submissiondraft', 'assignment'));
  919. }
  920. foreach ($files as $file) {
  921. $filename = $file->get_filename();
  922. $mimetype = $file->get_mimetype();
  923. $link = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$this->context->id.'/mod_assignment/submission/'.$submission->id.'/'.$filename);
  924. $filenode->add($filename, $link, navigation_node::TYPE_SETTING, null, null, new pix_icon(file_file_icon($file),''));
  925. }
  926. }
  927. }
  928. // Show a notes link if they are enabled
  929. if ($this->notes_allowed()) {
  930. $link = new moodle_url('/mod/assignment/upload.php', array('id'=>$this->cm->id, 'action'=>'editnotes', 'sesskey'=>sesskey()));
  931. $node->add(get_string('notes', 'assignment'), $link);
  932. }
  933. }
  934. /**
  935. * creates a zip of all assignment submissions and sends a zip to the browser
  936. */
  937. public function download_submissions() {
  938. global $CFG,$DB;
  939. require_once($CFG->libdir.'/filelib.php');
  940. $submissions = $this->get_submissions('','');
  941. if (empty($submissions)) {
  942. print_error('errornosubmissions', 'assignment');
  943. }
  944. $filesforzipping = array();
  945. $fs = get_file_storage();
  946. $groupmode = groups_get_activity_groupmode($this->cm);
  947. $groupid = 0; // All users
  948. $groupname = '';
  949. if ($groupmode) {
  950. $groupid = groups_get_activity_group($this->cm, true);
  951. $groupname = groups_get_group_name($groupid).'-';
  952. }
  953. $filename = str_replace(' ', '_', clean_filename($this->course->shortname.'-'.$this->assignment->name.'-'.$groupname.$this->assignment->id.".zip")); //name of new zip file.
  954. foreach ($submissions as $submission) {
  955. $a_userid = $submission->userid; //get userid
  956. if ((groups_is_member($groupid,$a_userid)or !$groupmode or !$groupid)) {
  957. $a_assignid = $submission->assignment; //get name of this assignment for use in the file names.
  958. $a_user = $DB->get_record("user", array("id"=>$a_userid),'id,username,firstname,lastname'); //get user firstname/lastname
  959. $files = $fs->get_area_files($this->context->id, 'mod_assignment', 'submission', $submission->id, "timemodified", false);
  960. foreach ($files as $file) {
  961. //get files new name.
  962. $fileext = strstr($file->get_filename(), '.');
  963. $fileoriginal = str_replace($fileext, '', $file->get_filename());
  964. $fileforzipname = clean_filename(fullname($a_user) . "_" . $fileoriginal."_".$a_userid.$fileext);
  965. //save file name to array for zipping.
  966. $filesforzipping[$fileforzipname] = $file;
  967. }
  968. }
  969. } // end of foreach loop
  970. if ($zipfile = assignment_pack_files($filesforzipping)) {
  971. send_temp_file($zipfile, $filename); //send file and delete after sending.
  972. }
  973. }
  974. /**
  975. * Check the given submission is complete. Preliminary rows are often created in the assignment_submissions
  976. * table before a submission actually takes place. This function checks to see if the given submission has actually
  977. * been submitted.
  978. *
  979. * @param stdClass $submission The submission we want to check for completion
  980. * @return bool Indicates if the submission was found to be complete
  981. */
  982. public function is_submitted_with_required_data($submission) {
  983. return ($submission->

Large files files are truncated, but you can click here to view the full file