MDL-15901 completion disabled by default
[moodle.git] / mod / assignment / type / upload / assignment.class.php
CommitLineData
fcdcc372 1<?php // $Id$
6117edc7 2require_once($CFG->libdir.'/formslib.php');
67a87e7d 3require_once($CFG->libdir . '/portfoliolib.php');
4require_once($CFG->dirroot . '/mod/assignment/lib.php');
fcdcc372 5
39bc9fbb 6define('ASSIGNMENT_STATUS_SUBMITTED', 'submitted'); // student thinks it is finished
7define('ASSIGNMENT_STATUS_CLOSED', 'closed'); // teacher prevents more submissions
55b4d096 8
fcdcc372 9/**
55b4d096 10 * Extend the base assignment class for assignments where you upload a single file
fcdcc372 11 *
12 */
13class assignment_upload extends assignment_base {
fcdcc372 14
7bddd4b7 15 function assignment_upload($cmid='staticonly', $assignment=NULL, $cm=NULL, $course=NULL) {
16 parent::assignment_base($cmid, $assignment, $cm, $course);
0b5a80a1 17 $this->type = 'upload';
fcdcc372 18 }
19
fcdcc372 20 function view() {
8ba4cbd2 21 global $USER;
fcdcc372 22
55b4d096 23 require_capability('mod/assignment:view', $this->context);
fcdcc372 24
55b4d096 25 add_to_log($this->course->id, 'assignment', 'view', "view.php?id={$this->cm->id}", $this->assignment->id, $this->cm->id);
26
27 $this->view_header();
29889a2a 28
29 if ($this->assignment->timeavailable > time()
8c408f8e 30 and !has_capability('mod/assignment:grade', $this->context) // grading user can see it anytime
31 and $this->assignment->var3) { // force hiding before available date
32776fef 32 print_simple_box_start('center', '', '', 0, 'generalbox', 'intro');
29889a2a 33 print_string('notavailableyet', 'assignment');
34 print_simple_box_end();
35 } else {
36 $this->view_intro();
37 }
38
55b4d096 39 $this->view_dates();
fcdcc372 40
55b4d096 41 if (has_capability('mod/assignment:submit', $this->context)) {
42 $filecount = $this->count_user_files($USER->id);
43 $submission = $this->get_submission($USER->id);
6fc73d59 44
55b4d096 45 $this->view_feedback();
6fc73d59 46
42f50aec 47 if (!$this->drafts_tracked() or !$this->isopen() or $this->is_finalized($submission)) {
a1c91f9a 48 print_heading(get_string('submission', 'assignment'), '', 3);
55b4d096 49 } else {
a1c91f9a 50 print_heading(get_string('submissiondraft', 'assignment'), '', 3);
fcdcc372 51 }
fcdcc372 52
55b4d096 53 if ($filecount and $submission) {
54 print_simple_box($this->print_user_files($USER->id, true), 'center');
55 } else {
39bc9fbb 56 if (!$this->isopen() or $this->is_finalized($submission)) {
55b4d096 57 print_simple_box(get_string('nofiles', 'assignment'), 'center');
58 } else {
59 print_simple_box(get_string('nofilesyet', 'assignment'), 'center');
60 }
61 }
6fc73d59 62
8ba4cbd2 63 $this->view_upload_form();
55b4d096 64
65 if ($this->notes_allowed()) {
a1c91f9a 66 print_heading(get_string('notes', 'assignment'), '', 3);
55b4d096 67 $this->view_notes();
68 }
69
70 $this->view_final_submission();
8ba4cbd2 71 }
72 $this->view_footer();
fcdcc372 73 }
74
fcdcc372 75
55b4d096 76 function view_feedback($submission=NULL) {
74d7d735 77 global $USER, $CFG, $DB;
12dce253 78 require_once($CFG->libdir.'/gradelib.php');
8ba4cbd2 79
55b4d096 80 if (!$submission) { /// Get submission for this assignment
81 $submission = $this->get_submission($USER->id);
82 }
8ba4cbd2 83
55b4d096 84 if (empty($submission->timemarked)) { /// Nothing to show, so print nothing
85 if ($this->count_responsefiles($USER->id)) {
86 print_heading(get_string('responsefiles', 'assignment', $this->course->teacher), '', 3);
87 $responsefiles = $this->print_responsefiles($USER->id, true);
88 print_simple_box($responsefiles, 'center');
89 }
90 return;
91 }
fcdcc372 92
12dce253 93 $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $USER->id);
94 $item = $grading_info->items[0];
95 $grade = $item->grades[$USER->id];
96
97 if ($grade->hidden or $grade->grade === false) { // hidden or error
98 return;
99 }
100
101 if ($grade->grade === null and empty($grade->str_feedback)) { /// Nothing to show yet
102 return;
103 }
104
105 $graded_date = $grade->dategraded;
106 $graded_by = $grade->usermodified;
107
55b4d096 108 /// We need the teacher info
74d7d735 109 if (!$teacher = $DB->get_record('user', array('id'=>$graded_by))) {
a939f681 110 print_error('cannotfindteacher');
55b4d096 111 }
fcdcc372 112
55b4d096 113 /// Print the feedback
114 print_heading(get_string('submissionfeedback', 'assignment'), '', 3);
8ba4cbd2 115
55b4d096 116 echo '<table cellspacing="0" class="feedback">';
fcdcc372 117
55b4d096 118 echo '<tr>';
119 echo '<td class="left picture">';
ad1e3409 120 print_user_picture($teacher, $this->course->id, $teacher->picture);
55b4d096 121 echo '</td>';
122 echo '<td class="topic">';
123 echo '<div class="from">';
124 echo '<div class="fullname">'.fullname($teacher).'</div>';
12dce253 125 echo '<div class="time">'.userdate($graded_date).'</div>';
55b4d096 126 echo '</div>';
127 echo '</td>';
128 echo '</tr>';
fcdcc372 129
55b4d096 130 echo '<tr>';
131 echo '<td class="left side">&nbsp;</td>';
132 echo '<td class="content">';
133 if ($this->assignment->grade) {
134 echo '<div class="grade">';
12dce253 135 echo get_string("grade").': '.$grade->str_long_grade;
55b4d096 136 echo '</div>';
137 echo '<div class="clearer"></div>';
138 }
8ba4cbd2 139
55b4d096 140 echo '<div class="comment">';
12dce253 141 echo $grade->str_feedback;
55b4d096 142 echo '</div>';
143 echo '</tr>';
8ba4cbd2 144
55b4d096 145 echo '<tr>';
146 echo '<td class="left side">&nbsp;</td>';
147 echo '<td class="content">';
148 echo $this->print_responsefiles($USER->id, true);
149 echo '</tr>';
8ba4cbd2 150
55b4d096 151 echo '</table>';
fcdcc372 152 }
153
fcdcc372 154
55b4d096 155 function view_upload_form() {
156 global $CFG, $USER;
fcdcc372 157
8ba4cbd2 158 $submission = $this->get_submission($USER->id);
fcdcc372 159
55b4d096 160 $struploadafile = get_string('uploadafile');
12dce253 161 $maxbytes = $this->assignment->maxbytes == 0 ? $this->course->maxbytes : $this->assignment->maxbytes;
ba9ad049 162 $strmaxsize = get_string('maxsize', '', display_size($maxbytes));
fcdcc372 163
55b4d096 164 if ($this->is_finalized($submission)) {
165 // no uploading
166 return;
167 }
8ba4cbd2 168
55b4d096 169 if ($this->can_upload_file($submission)) {
d9cb14b8 170 echo '<div style="text-align:center">';
55b4d096 171 echo '<form enctype="multipart/form-data" method="post" action="upload.php">';
d9cb14b8 172 echo '<fieldset class="invisiblefieldset">';
55b4d096 173 echo "<p>$struploadafile ($strmaxsize)</p>";
174 echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
175 echo '<input type="hidden" name="action" value="uploadfile" />';
176 require_once($CFG->libdir.'/uploadlib.php');
177 upload_print_form_fragment(1,array('newfile'),null,false,null,0,$this->assignment->maxbytes,false);
178 echo '<input type="submit" name="save" value="'.get_string('uploadthisfile').'" />';
d9cb14b8 179 echo '</fieldset>';
55b4d096 180 echo '</form>';
d9cb14b8 181 echo '</div>';
55b4d096 182 echo '<br />';
fcdcc372 183 }
8ba4cbd2 184
55b4d096 185 }
8ba4cbd2 186
55b4d096 187 function view_notes() {
188 global $USER;
189
190 if ($submission = $this->get_submission($USER->id)
191 and !empty($submission->data1)) {
192 print_simple_box(format_text($submission->data1, FORMAT_HTML), 'center', '630px');
193 } else {
194 print_simple_box(get_string('notesempty', 'assignment'), 'center');
195 }
196 if ($this->can_update_notes($submission)) {
197 $options = array ('id'=>$this->cm->id, 'action'=>'editnotes');
d9cb14b8 198 echo '<div style="text-align:center">';
55b4d096 199 print_single_button('upload.php', $options, get_string('edit'), 'post', '_self', false);
d9cb14b8 200 echo '</div>';
55b4d096 201 }
fcdcc372 202 }
203
55b4d096 204 function view_final_submission() {
fcdcc372 205 global $CFG, $USER;
206
8ba4cbd2 207 $submission = $this->get_submission($USER->id);
fcdcc372 208
39bc9fbb 209 if ($this->isopen() and $this->can_finalize($submission)) {
55b4d096 210 //print final submit button
211 print_heading(get_string('submitformarking','assignment'), '', 3);
d9cb14b8 212 echo '<div style="text-align:center">';
55b4d096 213 echo '<form method="post" action="upload.php">';
d9cb14b8 214 echo '<fieldset class="invisiblefieldset">';
55b4d096 215 echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
216 echo '<input type="hidden" name="action" value="finalize" />';
217 echo '<input type="submit" name="formarking" value="'.get_string('sendformarking', 'assignment').'" />';
d9cb14b8 218 echo '</fieldset>';
55b4d096 219 echo '</form>';
d9cb14b8 220 echo '</div>';
39bc9fbb 221 } else if (!$this->isopen()) {
222 print_heading(get_string('nomoresubmissions','assignment'), '', 3);
223
42f50aec 224 } else if ($this->drafts_tracked() and $state = $this->is_finalized($submission)) {
39bc9fbb 225 if ($state == ASSIGNMENT_STATUS_SUBMITTED) {
226 print_heading(get_string('submitedformarking','assignment'), '', 3);
227 } else {
228 print_heading(get_string('nomoresubmissions','assignment'), '', 3);
229 }
55b4d096 230 } else {
231 //no submission yet
232 }
233 }
39bc9fbb 234
235
236 /**
dc6cb74e 237 * Return true if var3 == hide description till available day
39bc9fbb 238 *
dc6cb74e 239 *@return boolean
39bc9fbb 240 */
dc6cb74e 241 function description_is_hidden() {
242 return ($this->assignment->var3 && (time() <= $this->assignment->timeavailable));
243 }
fcdcc372 244
55b4d096 245 function custom_feedbackform($submission, $return=false) {
246 global $CFG;
8ba4cbd2 247
55b4d096 248 $mode = optional_param('mode', '', PARAM_ALPHA);
249 $offset = optional_param('offset', 0, PARAM_INT);
250 $forcerefresh = optional_param('forcerefresh', 0, PARAM_BOOL);
8ba4cbd2 251
55b4d096 252 $output = get_string('responsefiles', 'assignment').': ';
8ba4cbd2 253
55b4d096 254 $output .= '<form enctype="multipart/form-data" method="post" '.
255 "action=\"$CFG->wwwroot/mod/assignment/upload.php\">";
2bf7e0b1 256 $output .= '<div>';
55b4d096 257 $output .= '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
258 $output .= '<input type="hidden" name="action" value="uploadresponse" />';
259 $output .= '<input type="hidden" name="mode" value="'.$mode.'" />';
260 $output .= '<input type="hidden" name="offset" value="'.$offset.'" />';
261 $output .= '<input type="hidden" name="userid" value="'.$submission->userid.'" />';
262 require_once($CFG->libdir.'/uploadlib.php');
263 $output .= upload_print_form_fragment(1,array('newfile'),null,false,null,0,0,true);
264 $output .= '<input type="submit" name="save" value="'.get_string('uploadthisfile').'" />';
2bf7e0b1 265 $output .= '</div>';
55b4d096 266 $output .= '</form>';
fcdcc372 267
55b4d096 268 if ($forcerefresh) {
269 $output .= $this->update_main_listing($submission);
270 }
fcdcc372 271
55b4d096 272 $responsefiles = $this->print_responsefiles($submission->userid, true);
273 if (!empty($responsefiles)) {
274 $output .= $responsefiles;
38ac07d2 275 }
55b4d096 276
277 if ($return) {
278 return $output;
38ac07d2 279 }
55b4d096 280 echo $output;
281 return;
282 }
fcdcc372 283
fcdcc372 284
55b4d096 285 function print_student_answer($userid, $return=false){
286 global $CFG;
fcdcc372 287
55b4d096 288 $filearea = $this->file_area_name($userid);
289 $submission = $this->get_submission($userid);
8ba4cbd2 290
55b4d096 291 $output = '';
8ba4cbd2 292
55b4d096 293 if ($basedir = $this->file_area($userid)) {
42f50aec 294 if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission)) {
55b4d096 295 $output .= '<strong>'.get_string('draft', 'assignment').':</strong> ';
296 }
297
298 if ($this->notes_allowed() and !empty($submission->data1)) {
299 $output .= link_to_popup_window ('/mod/assignment/type/upload/notes.php?id='.$this->cm->id.'&amp;userid='.$userid,
300 'notes'.$userid, get_string('notes', 'assignment'), 500, 780, get_string('notes', 'assignment'), 'none', true, 'notesbutton'.$userid);
301 $output .= '&nbsp;';
302 }
303
304 if ($files = get_directory_list($basedir, 'responses')) {
74369ab5 305 require_once($CFG->libdir.'/filelib.php');
55b4d096 306 foreach ($files as $key => $file) {
55b4d096 307 $icon = mimeinfo('icon', $file);
74369ab5 308 $ffurl = get_file_url("$filearea/$file");
a1c91f9a 309 $output .= '<a href="'.$ffurl.'" ><img class="icon" src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.$file.'</a>&nbsp;';
fcdcc372 310 }
fcdcc372 311 }
312 }
55b4d096 313 $output = '<div class="files">'.$output.'</div>';
314 $output .= '<br />';
8ba4cbd2 315
55b4d096 316 return $output;
fcdcc372 317 }
318
fcdcc372 319
55b4d096 320 /**
321 * Produces a list of links to the files uploaded by a user
322 *
323 * @param $userid int optional id of the user. If 0 then $USER->id is used.
324 * @param $return boolean optional defaults to false. If true the list is returned rather than printed
325 * @return string optional
326 */
327 function print_user_files($userid=0, $return=false) {
328 global $CFG, $USER;
329
330 $mode = optional_param('mode', '', PARAM_ALPHA);
331 $offset = optional_param('offset', 0, PARAM_INT);
fcdcc372 332
55b4d096 333 if (!$userid) {
334 if (!isloggedin()) {
335 return '';
8ba4cbd2 336 }
55b4d096 337 $userid = $USER->id;
8ba4cbd2 338 }
339
55b4d096 340 $filearea = $this->file_area_name($userid);
8ba4cbd2 341
55b4d096 342 $output = '';
8ba4cbd2 343
39bc9fbb 344 $submission = $this->get_submission($userid);
8ba4cbd2 345
39bc9fbb 346 $candelete = $this->can_delete_files($submission);
347 $strdelete = get_string('delete');
8ba4cbd2 348
42f50aec 349 if ($this->drafts_tracked() and $this->isopen() and !$this->is_finalized($submission) and !empty($mode)) { // only during grading
39bc9fbb 350 $output .= '<strong>'.get_string('draft', 'assignment').':</strong><br />';
351 }
fcdcc372 352
39bc9fbb 353 if ($this->notes_allowed() and !empty($submission->data1) and !empty($mode)) { // only during grading
fcdcc372 354
39bc9fbb 355 $npurl = $CFG->wwwroot."/mod/assignment/type/upload/notes.php?id={$this->cm->id}&amp;userid=$userid&amp;offset=$offset&amp;mode=single";
356 $output .= '<a href="'.$npurl.'">'.get_string('notes', 'assignment').'</a><br />';
fcdcc372 357
39bc9fbb 358 }
8ba4cbd2 359
39bc9fbb 360 if ($basedir = $this->file_area($userid)) {
361 if ($files = get_directory_list($basedir, 'responses')) {
362 require_once($CFG->libdir.'/filelib.php');
67a87e7d 363 $p = array(
364 'userid' => $userid,
365 'assignmentid' => $this->cm->id,
366 );
39bc9fbb 367 foreach ($files as $key => $file) {
8ba4cbd2 368
39bc9fbb 369 $icon = mimeinfo('icon', $file);
74369ab5 370 $ffurl = get_file_url("$filearea/$file");
fcdcc372 371
39bc9fbb 372 $output .= '<a href="'.$ffurl.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" class="icon" alt="'.$icon.'" />'.$file.'</a>';
8ba4cbd2 373
39bc9fbb 374 if ($candelete) {
375 $delurl = "$CFG->wwwroot/mod/assignment/delete.php?id={$this->cm->id}&amp;file=$file&amp;userid={$submission->userid}&amp;mode=$mode&amp;offset=$offset";
fcdcc372 376
39bc9fbb 377 $output .= '<a href="'.$delurl.'">&nbsp;'
378 .'<img title="'.$strdelete.'" src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt="" /></a> ';
6fc73d59 379 }
ed1fcf79 380 if (true) { // @todo penny replace with capability check
67a87e7d 381 $p['file'] = $file;
ed1fcf79 382 $output .= portfolio_add_button('assignment_portfolio_caller', $p, '/mod/assignment/lib.php', false, true);
67a87e7d 383 }
39bc9fbb 384 $output .= '<br />';
8ba4cbd2 385 }
ed1fcf79 386 if (true) { //@todo penny replace with check capability
67a87e7d 387 unset($p['file']);// for all files
ed1fcf79 388 $output .= '<br />' . portfolio_add_button('assignment_portfolio_caller', $p, '/mod/assignment/lib.php', true, true);
67a87e7d 389 }
55b4d096 390 }
39bc9fbb 391 }
03076be4 392
393 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
39bc9fbb 394 if ($this->can_unfinalize($submission)) {
395 $options = array ('id'=>$this->cm->id, 'userid'=>$userid, 'action'=>'unfinalize', 'mode'=>$mode, 'offset'=>$offset);
396 $output .= print_single_button('upload.php', $options, get_string('unfinalize', 'assignment'), 'post', '_self', true);
397 } else if ($this->can_finalize($submission)) {
398 $options = array ('id'=>$this->cm->id, 'userid'=>$userid, 'action'=>'finalizeclose', 'mode'=>$mode, 'offset'=>$offset);
399 $output .= print_single_button('upload.php', $options, get_string('finalize', 'assignment'), 'post', '_self', true);
55b4d096 400 }
55b4d096 401 }
8ba4cbd2 402
39bc9fbb 403 $output = '<div class="files">'.$output.'</div>';
404
55b4d096 405 if ($return) {
406 return $output;
407 }
408 echo $output;
409 }
8ba4cbd2 410
55b4d096 411 function print_responsefiles($userid, $return=false) {
412 global $CFG, $USER;
8ba4cbd2 413
55b4d096 414 $mode = optional_param('mode', '', PARAM_ALPHA);
415 $offset = optional_param('offset', 0, PARAM_INT);
8ba4cbd2 416
55b4d096 417 $filearea = $this->file_area_name($userid).'/responses';
fcdcc372 418
55b4d096 419 $output = '';
fcdcc372 420
55b4d096 421 $candelete = $this->can_manage_responsefiles();
422 $strdelete = get_string('delete');
fcdcc372 423
55b4d096 424 if ($basedir = $this->file_area($userid)) {
425 $basedir .= '/responses';
fcdcc372 426
55b4d096 427 if ($files = get_directory_list($basedir)) {
428 require_once($CFG->libdir.'/filelib.php');
429 foreach ($files as $key => $file) {
fcdcc372 430
55b4d096 431 $icon = mimeinfo('icon', $file);
fcdcc372 432
74369ab5 433 $ffurl = get_file_url("$filearea/$file");
fcdcc372 434
d9cf7323 435 $output .= '<a href="'.$ffurl.'" ><img src="'.$CFG->pixpath.'/f/'.$icon.'" alt="'.$icon.'" />'.$file.'</a>';
fcdcc372 436
55b4d096 437 if ($candelete) {
438 $delurl = "$CFG->wwwroot/mod/assignment/delete.php?id={$this->cm->id}&amp;file=$file&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset&amp;action=response";
fcdcc372 439
a1c91f9a 440 $output .= '<a href="'.$delurl.'">&nbsp;'
0d905d9f 441 .'<img title="'.$strdelete.'" src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt=""/></a> ';
55b4d096 442 }
fcdcc372 443
55b4d096 444 $output .= '&nbsp;';
445 }
446 }
fcdcc372 447
fcdcc372 448
55b4d096 449 $output = '<div class="responsefiles">'.$output.'</div>';
450
8ba4cbd2 451 }
fcdcc372 452
55b4d096 453 if ($return) {
454 return $output;
8ba4cbd2 455 }
55b4d096 456 echo $output;
457 }
fcdcc372 458
fcdcc372 459
55b4d096 460 function upload() {
461 $action = required_param('action', PARAM_ALPHA);
fcdcc372 462
55b4d096 463 switch ($action) {
464 case 'finalize':
465 $this->finalize();
466 break;
39bc9fbb 467 case 'finalizeclose':
03076be4 468 $this->finalizeclose();
39bc9fbb 469 break;
55b4d096 470 case 'unfinalize':
471 $this->unfinalize();
472 break;
473 case 'uploadresponse':
474 $this->upload_responsefile();
475 break;
476 case 'uploadfile':
477 $this->upload_file();
478 case 'savenotes':
479 case 'editnotes':
480 $this->upload_notes();
481 default:
a939f681 482 print_error('unknowuploadaction', '', '', $action);
8ba4cbd2 483 }
55b4d096 484 }
fcdcc372 485
55b4d096 486 function upload_notes() {
487 global $CFG, $USER;
fcdcc372 488
55b4d096 489 $action = required_param('action', PARAM_ALPHA);
fcdcc372 490
55b4d096 491 $returnurl = 'view.php?id='.$this->cm->id;
fcdcc372 492
1d284fbd 493 $mform = new mod_assignment_upload_notes_form();
6117edc7 494
495 $defaults = new object();
496 $defaults->id = $this->cm->id;
497
e38204d1 498 if ($submission = $this->get_submission($USER->id)) {
6117edc7 499 $defaults->text = $submission->data1;
e38204d1 500 } else {
6117edc7 501 $defaults->text = '';
e38204d1 502 }
503
beac4717 504 $mform->set_data($defaults);
6117edc7 505
5571b5f4 506 if ($mform->is_cancelled()) {
507 redirect('view.php?id='.$this->cm->id);
508 }
509
e38204d1 510 if (!$this->can_update_notes($submission)) {
511 $this->view_header(get_string('upload'));
512 notify(get_string('uploaderror', 'assignment'));
513 print_continue($returnurl);
514 $this->view_footer();
515 die;
516 }
517
294ce987 518 if ($data = $mform->get_data() and $action == 'savenotes') {
55b4d096 519 $submission = $this->get_submission($USER->id, true); // get or create submission
520 $updated = new object();
521 $updated->id = $submission->id;
522 $updated->timemodified = time();
6117edc7 523 $updated->data1 = $data->text;
fcdcc372 524
74d7d735 525 if ($DB->update_record('assignment_submissions', $updated)) {
55b4d096 526 add_to_log($this->course->id, 'assignment', 'upload', 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
527 redirect($returnurl);
12dce253 528 $submission = $this->get_submission($USER->id);
529 $this->update_grade($submission);
530
55b4d096 531 } else {
532 $this->view_header(get_string('notes', 'assignment'));
533 notify(get_string('notesupdateerror', 'assignment'));
534 print_continue($returnurl);
535 $this->view_footer();
536 die;
537 }
538 }
fcdcc372 539
55b4d096 540 /// show notes edit form
55b4d096 541 $this->view_header(get_string('notes', 'assignment'));
6117edc7 542
a1c91f9a 543 print_heading(get_string('notes', 'assignment'), '');
6fc73d59 544
6117edc7 545 $mform->display();
8ba4cbd2 546
55b4d096 547 $this->view_footer();
548 die;
fcdcc372 549 }
550
55b4d096 551 function upload_responsefile() {
552 global $CFG;
8ba4cbd2 553
55b4d096 554 $userid = required_param('userid', PARAM_INT);
555 $mode = required_param('mode', PARAM_ALPHA);
556 $offset = required_param('offset', PARAM_INT);
fcdcc372 557
55b4d096 558 $returnurl = "submissions.php?id={$this->cm->id}&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset";
fcdcc372 559
294ce987 560 if (data_submitted() and $this->can_manage_responsefiles()) {
55b4d096 561 $dir = $this->file_area_name($userid).'/responses';
8d942514 562 check_dir_exists($CFG->dataroot.'/'.$dir, true, true);
8ba4cbd2 563
55b4d096 564 require_once($CFG->dirroot.'/lib/uploadlib.php');
565 $um = new upload_manager('newfile',false,true,$this->course,false,0,true);
566
567 if (!$um->process_file_uploads($dir)) {
568 print_header(get_string('upload'));
569 notify(get_string('uploaderror', 'assignment'));
570 echo $um->get_errors();
571 print_continue($returnurl);
572 print_footer('none');
573 die;
38ac07d2 574 }
fcdcc372 575 }
55b4d096 576 redirect($returnurl);
fcdcc372 577 }
578
55b4d096 579 function upload_file() {
74d7d735 580 global $CFG, $USER, $DB;
8ba4cbd2 581
55b4d096 582 $mode = optional_param('mode', '', PARAM_ALPHA);
583 $offset = optional_param('offset', 0, PARAM_INT);
fcdcc372 584
55b4d096 585 $returnurl = 'view.php?id='.$this->cm->id;
8ba4cbd2 586
55b4d096 587 $filecount = $this->count_user_files($USER->id);
588 $submission = $this->get_submission($USER->id);
8ba4cbd2 589
55b4d096 590 if (!$this->can_upload_file($submission)) {
591 $this->view_header(get_string('upload'));
592 notify(get_string('uploaderror', 'assignment'));
593 print_continue($returnurl);
594 $this->view_footer();
595 die;
596 }
8ba4cbd2 597
55b4d096 598 $dir = $this->file_area_name($USER->id);
8d942514 599 check_dir_exists($CFG->dataroot.'/'.$dir, true, true); // better to create now so that student submissions do not block it later
fcdcc372 600
55b4d096 601 require_once($CFG->dirroot.'/lib/uploadlib.php');
602 $um = new upload_manager('newfile',false,true,$this->course,false,$this->assignment->maxbytes,true);
8ba4cbd2 603
55b4d096 604 if ($um->process_file_uploads($dir)) {
605 $submission = $this->get_submission($USER->id, true); //create new submission if needed
606 $updated = new object();
607 $updated->id = $submission->id;
608 $updated->timemodified = time();
609
74d7d735 610 if ($DB->update_record('assignment_submissions', $updated)) {
55b4d096 611 add_to_log($this->course->id, 'assignment', 'upload',
612 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
12dce253 613 $submission = $this->get_submission($USER->id);
614 $this->update_grade($submission);
91d8d9a0 615 if (!$this->drafts_tracked()) {
616 $this->email_teachers($submission);
617 }
55b4d096 618 } else {
619 $new_filename = $um->get_new_filename();
620 $this->view_header(get_string('upload'));
621 notify(get_string('uploadnotregistered', 'assignment', $new_filename));
622 print_continue($returnurl);
623 $this->view_footer();
624 die;
fcdcc372 625 }
55b4d096 626 redirect('view.php?id='.$this->cm->id);
fcdcc372 627 }
55b4d096 628 $this->view_header(get_string('upload'));
629 notify(get_string('uploaderror', 'assignment'));
630 echo $um->get_errors();
631 print_continue($returnurl);
632 $this->view_footer();
633 die;
fcdcc372 634 }
635
03076be4 636 function finalize() {
74d7d735 637 global $USER, $DB;
fcdcc372 638
03076be4 639 $confirm = optional_param('confirm', 0, PARAM_BOOL);
640 $returnurl = 'view.php?id='.$this->cm->id;
641 $submission = $this->get_submission($USER->id);
55b4d096 642
643 if (!$this->can_finalize($submission)) {
39bc9fbb 644 redirect($returnurl); // probably already graded, redirect to assignment page, the reason should be obvious
55b4d096 645 }
646
03076be4 647 if (!data_submitted() or !$confirm) {
55b4d096 648 $optionsno = array('id'=>$this->cm->id);
649 $optionsyes = array ('id'=>$this->cm->id, 'confirm'=>1, 'action'=>'finalize');
650 $this->view_header(get_string('submitformarking', 'assignment'));
651 print_heading(get_string('submitformarking', 'assignment'));
652 notice_yesno(get_string('onceassignmentsent', 'assignment'), 'upload.php', 'view.php', $optionsyes, $optionsno, 'post', 'get');
653 $this->view_footer();
654 die;
655
03076be4 656 }
657 $updated = new object();
658 $updated->id = $submission->id;
659 $updated->data2 = ASSIGNMENT_STATUS_SUBMITTED;
660 $updated->timemodified = time();
661
74d7d735 662 if ($DB->update_record('assignment_submissions', $updated)) {
03076be4 663 add_to_log($this->course->id, 'assignment', 'upload', //TODO: add finalize action to log
664 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
12dce253 665 $submission = $this->get_submission($USER->id);
666 $this->update_grade($submission);
03076be4 667 $this->email_teachers($submission);
55b4d096 668 } else {
03076be4 669 $this->view_header(get_string('submitformarking', 'assignment'));
670 notify(get_string('finalizeerror', 'assignment'));
671 print_continue($returnurl);
672 $this->view_footer();
673 die;
674 }
675 redirect($returnurl);
676 }
39bc9fbb 677
03076be4 678 function finalizeclose() {
74d7d735 679 global $DB;
680
03076be4 681 $userid = optional_param('userid', 0, PARAM_INT);
682 $mode = required_param('mode', PARAM_ALPHA);
683 $offset = required_param('offset', PARAM_INT);
684 $returnurl = "submissions.php?id={$this->cm->id}&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset&amp;forcerefresh=1";
685
12dce253 686 // create but do not add student submission date
03076be4 687 $submission = $this->get_submission($userid, true, true);
688
689 if (!data_submitted() or !$this->can_finalize($submission)) {
690 redirect($returnurl); // probably closed already
691 }
692
693 $updated = new object();
694 $updated->id = $submission->id;
695 $updated->data2 = ASSIGNMENT_STATUS_CLOSED;
696
74d7d735 697 if ($DB->update_record('assignment_submissions', $updated)) {
03076be4 698 add_to_log($this->course->id, 'assignment', 'upload', //TODO: add finalize action to log
699 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
12dce253 700 $submission = $this->get_submission($userid, false, true);
701 $this->update_grade($submission);
fcdcc372 702 }
55b4d096 703 redirect($returnurl);
704 }
fcdcc372 705
55b4d096 706 function unfinalize() {
74d7d735 707 global $DB;
8ba4cbd2 708
55b4d096 709 $userid = required_param('userid', PARAM_INT);
710 $mode = required_param('mode', PARAM_ALPHA);
711 $offset = required_param('offset', PARAM_INT);
fcdcc372 712
55b4d096 713 $returnurl = "submissions.php?id={$this->cm->id}&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset&amp;forcerefresh=1";
8ba4cbd2 714
74d7d735 715 if (data_submitted()
55b4d096 716 and $submission = $this->get_submission($userid)
717 and $this->can_unfinalize($submission)) {
fcdcc372 718
55b4d096 719 $updated = new object();
720 $updated->id = $submission->id;
721 $updated->data2 = '';
74d7d735 722 if ($DB->update_record('assignment_submissions', $updated)) {
39bc9fbb 723 //TODO: add unfinalize action to log
55b4d096 724 add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->assignment->id, $this->assignment->id, $this->cm->id);
12dce253 725 $submission = $this->get_submission($userid);
726 $this->update_grade($submission);
55b4d096 727 } else {
0de80bcf 728 $this->view_header(get_string('submitformarking', 'assignment'));
729 notify(get_string('unfinalizeerror', 'assignment'));
55b4d096 730 print_continue($returnurl);
731 $this->view_footer();
732 die;
fcdcc372 733 }
734 }
55b4d096 735 redirect($returnurl);
736 }
fcdcc372 737
fcdcc372 738
55b4d096 739 function delete() {
740 $action = optional_param('action', '', PARAM_ALPHA);
fcdcc372 741
55b4d096 742 switch ($action) {
743 case 'response':
744 $this->delete_responsefile();
745 break;
746 default:
747 $this->delete_file();
fcdcc372 748 }
55b4d096 749 die;
750 }
fcdcc372 751
fcdcc372 752
55b4d096 753 function delete_responsefile() {
754 global $CFG;
8ba4cbd2 755
55b4d096 756 $file = required_param('file', PARAM_FILE);
757 $userid = required_param('userid', PARAM_INT);
758 $mode = required_param('mode', PARAM_ALPHA);
759 $offset = required_param('offset', PARAM_INT);
760 $confirm = optional_param('confirm', 0, PARAM_BOOL);
fcdcc372 761
55b4d096 762 $returnurl = "submissions.php?id={$this->cm->id}&amp;userid=$userid&amp;mode=$mode&amp;offset=$offset";
fcdcc372 763
55b4d096 764 if (!$this->can_manage_responsefiles()) {
765 redirect($returnurl);
766 }
fcdcc372 767
55b4d096 768 $urlreturn = 'submissions.php';
769 $optionsreturn = array('id'=>$this->cm->id, 'offset'=>$offset, 'mode'=>$mode, 'userid'=>$userid);
fcdcc372 770
74d7d735 771 if (!data_submitted() or !$confirm) {
55b4d096 772 $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'action'=>'response', 'mode'=>$mode, 'offset'=>$offset);
773 print_header(get_string('delete'));
774 print_heading(get_string('delete'));
775 notice_yesno(get_string('confirmdeletefile', 'assignment', $file), 'delete.php', $urlreturn, $optionsyes, $optionsreturn, 'post', 'get');
776 print_footer('none');
777 die;
778 }
fcdcc372 779
55b4d096 780 $dir = $this->file_area_name($userid).'/responses';
781 $filepath = $CFG->dataroot.'/'.$dir.'/'.$file;
782 if (file_exists($filepath)) {
783 if (@unlink($filepath)) {
784 redirect($returnurl);
785 }
786 }
fcdcc372 787
55b4d096 788 // print delete error
789 print_header(get_string('delete'));
790 notify(get_string('deletefilefailed', 'assignment'));
791 print_continue($returnurl);
792 print_footer('none');
793 die;
fcdcc372 794
55b4d096 795 }
fcdcc372 796
fcdcc372 797
55b4d096 798 function delete_file() {
74d7d735 799 global $CFG, $DB;
fcdcc372 800
55b4d096 801 $file = required_param('file', PARAM_FILE);
802 $userid = required_param('userid', PARAM_INT);
803 $confirm = optional_param('confirm', 0, PARAM_BOOL);
804 $mode = optional_param('mode', '', PARAM_ALPHA);
805 $offset = optional_param('offset', 0, PARAM_INT);
fcdcc372 806
55b4d096 807 require_login($this->course->id, false, $this->cm);
fcdcc372 808
55b4d096 809 if (empty($mode)) {
810 $urlreturn = 'view.php';
811 $optionsreturn = array('id'=>$this->cm->id);
812 $returnurl = 'view.php?id='.$this->cm->id;
8ba4cbd2 813 } else {
55b4d096 814 $urlreturn = 'submissions.php';
815 $optionsreturn = array('id'=>$this->cm->id, 'offset'=>$offset, 'mode'=>$mode, 'userid'=>$userid);
816 $returnurl = "submissions.php?id={$this->cm->id}&amp;offset=$offset&amp;mode=$mode&amp;userid=$userid";
fcdcc372 817 }
818
55b4d096 819 if (!$submission = $this->get_submission($userid) // incorrect submission
820 or !$this->can_delete_files($submission)) { // can not delete
821 $this->view_header(get_string('delete'));
822 notify(get_string('cannotdeletefiles', 'assignment'));
823 print_continue($returnurl);
824 $this->view_footer();
825 die;
fcdcc372 826 }
55b4d096 827 $dir = $this->file_area_name($userid);
fcdcc372 828
74d7d735 829 if (!data_submitted() or !$confirm) {
55b4d096 830 $optionsyes = array ('id'=>$this->cm->id, 'file'=>$file, 'userid'=>$userid, 'confirm'=>1, 'sesskey'=>sesskey(), 'mode'=>$mode, 'offset'=>$offset);
831 if (empty($mode)) {
832 $this->view_header(get_string('delete'));
833 } else {
834 print_header(get_string('delete'));
fcdcc372 835 }
55b4d096 836 print_heading(get_string('delete'));
837 notice_yesno(get_string('confirmdeletefile', 'assignment', $file), 'delete.php', $urlreturn, $optionsyes, $optionsreturn, 'post', 'get');
838 if (empty($mode)) {
839 $this->view_footer();
840 } else {
841 print_footer('none');
fcdcc372 842 }
55b4d096 843 die;
fcdcc372 844 }
845
55b4d096 846 $filepath = $CFG->dataroot.'/'.$dir.'/'.$file;
847 if (file_exists($filepath)) {
848 if (@unlink($filepath)) {
849 $updated = new object();
850 $updated->id = $submission->id;
851 $updated->timemodified = time();
74d7d735 852 if ($DB->update_record('assignment_submissions', $updated)) {
55b4d096 853 add_to_log($this->course->id, 'assignment', 'upload', //TODO: add delete action to log
854 'view.php?a='.$this->assignment->id, $this->assignment->id, $this->cm->id);
12dce253 855 $submission = $this->get_submission($userid);
856 $this->update_grade($submission);
55b4d096 857 }
858 redirect($returnurl);
859 }
fcdcc372 860 }
861
55b4d096 862 // print delete error
863 if (empty($mode)) {
864 $this->view_header(get_string('delete'));
865 } else {
866 print_header(get_string('delete'));
8ba4cbd2 867 }
55b4d096 868 notify(get_string('deletefilefailed', 'assignment'));
869 print_continue($returnurl);
870 if (empty($mode)) {
871 $this->view_footer();
872 } else {
873 print_footer('none');
8ba4cbd2 874 }
55b4d096 875 die;
876 }
8ba4cbd2 877
8ba4cbd2 878
55b4d096 879 function can_upload_file($submission) {
880 global $USER;
fcdcc372 881
55b4d096 882 if (has_capability('mod/assignment:submit', $this->context) // can submit
883 and $this->isopen() // assignment not closed yet
55b4d096 884 and (empty($submission) or $submission->userid == $USER->id) // his/her own submission
83774723 885 and $this->count_user_files($USER->id) < $this->assignment->var1 // file limit not reached
886 and !$this->is_finalized($submission)) { // no uploading after final submission
55b4d096 887 return true;
888 } else {
889 return false;
8ba4cbd2 890 }
55b4d096 891 }
fcdcc372 892
55b4d096 893 function can_manage_responsefiles() {
894 if (has_capability('mod/assignment:grade', $this->context)) {
895 return true;
896 } else {
897 return false;
898 }
899 }
fcdcc372 900
55b4d096 901 function can_delete_files($submission) {
902 global $USER;
fcdcc372 903
55b4d096 904 if (has_capability('mod/assignment:grade', $this->context)) {
905 return true;
fcdcc372 906 }
907
55b4d096 908 if (has_capability('mod/assignment:submit', $this->context)
909 and $this->isopen() // assignment not closed yet
55b4d096 910 and $this->assignment->resubmit // deleting allowed
911 and $USER->id == $submission->userid // his/her own submission
912 and !$this->is_finalized($submission)) { // no deleting after final submission
913 return true;
914 } else {
915 return false;
fcdcc372 916 }
55b4d096 917 }
fcdcc372 918
42f50aec 919 function drafts_tracked() {
920 return !empty($this->assignment->var4);
921 }
922
39bc9fbb 923 /**
924 * Returns submission status
925 * @param object $submission - may be empty
926 * @return string submission state - empty, ASSIGNMENT_STATUS_SUBMITTED or ASSIGNMENT_STATUS_CLOSED
927 */
55b4d096 928 function is_finalized($submission) {
42f50aec 929 if (!$this->drafts_tracked()) {
930 return '';
931
932 } else if (empty($submission)) {
39bc9fbb 933 return '';
934
935 } else if ($submission->data2 == ASSIGNMENT_STATUS_SUBMITTED or $submission->data2 == ASSIGNMENT_STATUS_CLOSED) {
936 return $submission->data2;
937
55b4d096 938 } else {
39bc9fbb 939 return '';
fcdcc372 940 }
55b4d096 941 }
fcdcc372 942
55b4d096 943 function can_unfinalize($submission) {
42f50aec 944 if (!$this->drafts_tracked()) {
945 return false;
946 }
55b4d096 947 if (has_capability('mod/assignment:grade', $this->context)
ad1e3409 948 and $this->isopen()
949 and $this->is_finalized($submission)) {
55b4d096 950 return true;
fcdcc372 951 } else {
55b4d096 952 return false;
fcdcc372 953 }
fcdcc372 954 }
955
55b4d096 956 function can_finalize($submission) {
957 global $USER;
42f50aec 958 if (!$this->drafts_tracked()) {
959 return false;
960 }
8ba4cbd2 961
ad1e3409 962 if ($this->is_finalized($submission)) {
963 return false;
964 }
965
966 if (has_capability('mod/assignment:grade', $this->context)) {
967 return true;
968
969 } else if (has_capability('mod/assignment:submit', $this->context) // can submit
55b4d096 970 and $this->isopen() // assignment not closed yet
971 and !empty($submission) // submission must exist
55b4d096 972 and $submission->userid == $USER->id // his/her own submission
55b4d096 973 and ($this->count_user_files($USER->id)
ad1e3409 974 or ($this->notes_allowed() and !empty($submission->data1)))) { // something must be submitted
8ba4cbd2 975
55b4d096 976 return true;
8ba4cbd2 977 } else {
55b4d096 978 return false;
8ba4cbd2 979 }
55b4d096 980 }
8ba4cbd2 981
55b4d096 982 function can_update_notes($submission) {
983 global $USER;
8ba4cbd2 984
55b4d096 985 if (has_capability('mod/assignment:submit', $this->context)
ad1e3409 986 and $this->notes_allowed() // notesd must be allowed
55b4d096 987 and $this->isopen() // assignment not closed yet
55b4d096 988 and (empty($submission) or $USER->id == $submission->userid) // his/her own submission
989 and !$this->is_finalized($submission)) { // no updateingafter final submission
990 return true;
8ba4cbd2 991 } else {
55b4d096 992 return false;
8ba4cbd2 993 }
8ba4cbd2 994 }
995
55b4d096 996 function notes_allowed() {
997 return (boolean)$this->assignment->var2;
998 }
8ba4cbd2 999
55b4d096 1000 /**
1001 * Count the files uploaded by a given user
1002 *
1003 * @param $userid int The user id
1004 * @return int
1005 */
1006 function count_user_files($userid) {
1007 global $CFG;
8ba4cbd2 1008
55b4d096 1009 $filearea = $this->file_area_name($userid);
8ba4cbd2 1010
55b4d096 1011 if ( is_dir($CFG->dataroot.'/'.$filearea) && $basedir = $this->file_area($userid)) {
1012 if ($files = get_directory_list($basedir, 'responses')) {
1013 return count($files);
38ac07d2 1014 }
fcdcc372 1015 }
55b4d096 1016 return 0;
1017 }
8ba4cbd2 1018
55b4d096 1019 function count_responsefiles($userid) {
1020 global $CFG;
8ba4cbd2 1021
55b4d096 1022 $filearea = $this->file_area_name($userid).'/responses';
8ba4cbd2 1023
55b4d096 1024 if ( is_dir($CFG->dataroot.'/'.$filearea) && $basedir = $this->file_area($userid)) {
1025 $basedir .= '/responses';
1026 if ($files = get_directory_list($basedir)) {
1027 return count($files);
8ba4cbd2 1028 }
fcdcc372 1029 }
55b4d096 1030 return 0;
38ac07d2 1031 }
8ba4cbd2 1032
436cfa9f 1033 function setup_elements(&$mform) {
1034 global $CFG, $COURSE;
1035
1036 $ynoptions = array( 0 => get_string('no'), 1 => get_string('yes'));
1037
1038 $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes);
436cfa9f 1039 $choices[0] = get_string('courseuploadlimit') . ' ('.display_size($COURSE->maxbytes).')';
1040 $mform->addElement('select', 'maxbytes', get_string('maximumsize', 'assignment'), $choices);
1041 $mform->setDefault('maxbytes', $CFG->assignment_maxbytes);
1042
1043 $mform->addElement('select', 'resubmit', get_string("allowdeleting", "assignment"), $ynoptions);
1044 $mform->setHelpButton('resubmit', array('allowdeleting', get_string('allowdeleting', 'assignment'), 'assignment'));
1045 $mform->setDefault('resubmit', 1);
1046
1047 $options = array();
1048 for($i = 1; $i <= 20; $i++) {
1049 $options[$i] = $i;
1050 }
1051 $mform->addElement('select', 'var1', get_string("allowmaxfiles", "assignment"), $options);
1052 $mform->setHelpButton('var1', array('allowmaxfiles', get_string('allowmaxfiles', 'assignment'), 'assignment'));
1053 $mform->setDefault('var1', 3);
1054
1055 $mform->addElement('select', 'var2', get_string("allownotes", "assignment"), $ynoptions);
1056 $mform->setHelpButton('var2', array('allownotes', get_string('allownotes', 'assignment'), 'assignment'));
1057 $mform->setDefault('var2', 0);
1058
1059 $mform->addElement('select', 'var3', get_string("hideintro", "assignment"), $ynoptions);
1060 $mform->setHelpButton('var3', array('hideintro', get_string('hideintro', 'assignment'), 'assignment'));
1061 $mform->setDefault('var3', 0);
1062
1063 $mform->addElement('select', 'emailteachers', get_string("emailteachers", "assignment"), $ynoptions);
1064 $mform->setHelpButton('emailteachers', array('emailteachers', get_string('emailteachers', 'assignment'), 'assignment'));
1065 $mform->setDefault('emailteachers', 0);
1066
42f50aec 1067 $mform->addElement('select', 'var4', get_string("trackdrafts", "assignment"), $ynoptions);
1068 $mform->setHelpButton('var4', array('trackdrafts', get_string('trackdrafts', 'assignment'), 'assignment'));
1069 $mform->setDefault('trackdrafts', 1);
436cfa9f 1070
1071 }
1072
67a87e7d 1073 function portfolio_exportable() {
1074 return true;
1075 }
1076
6117edc7 1077}
1078
f07b9627 1079class mod_assignment_upload_notes_form extends moodleform {
6117edc7 1080 function definition() {
1081 $mform =& $this->_form;
1082
1083 // visible elements
1084 $mform->addElement('htmleditor', 'text', get_string('notes', 'assignment'), array('cols'=>85, 'rows'=>30));
1085 $mform->setType('text', PARAM_RAW); // to be cleaned before display
18a77361 1086 $mform->setHelpButton('text', array('reading', 'writing'), false, 'editorhelpbutton');
55b4d096 1087
6117edc7 1088 // hidden params
1089 $mform->addElement('hidden', 'id', 0);
1090 $mform->setType('id', PARAM_INT);
1091 $mform->addElement('hidden', 'action', 'savenotes');
1092 $mform->setType('id', PARAM_ALPHA);
1093
1094 // buttons
a23f0aaf 1095 $this->add_action_buttons();
6117edc7 1096 }
fcdcc372 1097}
8ba4cbd2 1098
67a87e7d 1099
1100
fcdcc372 1101?>