PageRenderTime 56ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

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

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

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