Fix for Bug #5022 - Hidden resource/activity modules still show up in jump
[moodle.git] / mod / assignment / lib.php
CommitLineData
5c6b657a 1<?PHP // $Id$
b0f2597e 2/**
3 * assignment_base is the base class for assignment types
4 *
5 * This class provides all the functionality for an assignment
6 */
04eba58f 7
1884f2a6 8DEFINE ('ASSIGNMENT_COUNT_WORDS', 1);
9DEFINE ('ASSIGNMENT_COUNT_LETTERS', 2);
d699cd1e 10
4909e176 11if (!isset($CFG->assignment_maxbytes)) {
12 set_config("assignment_maxbytes", 1024000); // Default maximum size for all assignments
04eba58f 13}
1884f2a6 14if (!isset($CFG->assignment_itemstocount)) {
15 set_config("assignment_itemstocount", ASSIGNMENT_COUNT_WORDS); // Default item to count
16}
04eba58f 17
7af1e882 18/**
b0f2597e 19 * Standard base class for all assignment submodules (assignment types).
b0f2597e 20 */
21class assignment_base {
22
23 var $cm;
24 var $course;
25 var $assignment;
7af1e882 26 var $strassignment;
27 var $strassignments;
28 var $strsubmissions;
29 var $strlastmodified;
30 var $navigation;
31 var $pagetitle;
32 var $currentgroup;
33 var $usehtmleditor;
34 var $defaultformat;
b0f2597e 35
36 /**
37 * Constructor for the base assignment class
38 *
39 * Constructor for the base assignment class.
40 * If cmid is set create the cm, course, assignment objects.
7af1e882 41 * If the assignment is hidden and the user is not a teacher then
42 * this prints a page header and notice.
b0f2597e 43 *
44 * @param cmid integer, the current course module id - not set for new assignments
73097f07 45 * @param assignment object, usually null, but if we have it we pass it to save db access
7af1e882 46 * @param cm object, usually null, but if we have it we pass it to save db access
47 * @param course object, usually null, but if we have it we pass it to save db access
b0f2597e 48 */
73097f07 49 function assignment_base($cmid=0, $assignment=NULL, $cm=NULL, $course=NULL) {
b0f2597e 50
51 global $CFG;
52
53 if ($cmid) {
73097f07 54 if ($cm) {
55 $this->cm = $cm;
56 } else if (! $this->cm = get_record('course_modules', 'id', $cmid)) {
57 error('Course Module ID was incorrect');
b0f2597e 58 }
04eba58f 59
73097f07 60 if ($course) {
61 $this->course = $course;
62 } else if (! $this->course = get_record('course', 'id', $this->cm->course)) {
63 error('Course is misconfigured');
b0f2597e 64 }
04eba58f 65
73097f07 66 if ($assignment) {
67 $this->assignment = $assignment;
68 } else if (! $this->assignment = get_record('assignment', 'id', $this->cm->instance)) {
69 error('assignment ID was incorrect');
b0f2597e 70 }
e6a4906b 71
b0f2597e 72 $this->strassignment = get_string('modulename', 'assignment');
73 $this->strassignments = get_string('modulenameplural', 'assignment');
74 $this->strsubmissions = get_string('submissions', 'assignment');
75 $this->strlastmodified = get_string('lastmodified');
e6a4906b 76
b0f2597e 77 if ($this->course->category) {
78 $this->navigation = "<a target=\"{$CFG->framename}\" href=\"$CFG->wwwroot/course/view.php?id={$this->course->id}\">{$this->course->shortname}</a> -> ".
79 "<a target=\"{$CFG->framename}\" href=\"index.php?id={$this->course->id}\">$this->strassignments</a> ->";
80 } else {
81 $this->navigation = "<a target=\"{$CFG->framename}\" href=\"index.php?id={$this->course->id}\">$this->strassignments</a> ->";
82 }
e6a4906b 83
3898bc33 84 $this->pagetitle = strip_tags($this->course->shortname.': '.$this->strassignment.': '.format_string($this->assignment->name,true));
e6a4906b 85
b0f2597e 86 if (!$this->cm->visible and !isteacher($this->course->id)) {
87 $pagetitle = strip_tags($this->course->shortname.': '.$this->strassignment);
88 print_header($pagetitle, $this->course->fullname, "$this->navigation $this->strassignment",
89 "", "", true, '', navmenu($this->course, $this->cm));
90 notice(get_string("activityiscurrentlyhidden"), "$CFG->wwwroot/course/view.php?id={$this->course->id}");
91 }
b0f2597e 92 $this->currentgroup = get_current_group($this->course->id);
73097f07 93 }
e6a4906b 94
73097f07 95 /// Set up things for a HTML editor if it's needed
96 if ($this->usehtmleditor = can_use_html_editor()) {
97 $this->defaultformat = FORMAT_HTML;
98 } else {
99 $this->defaultformat = FORMAT_MOODLE;
e6a4906b 100 }
101 }
102
7af1e882 103 /**
104 * Display the assignment, used by view.php
105 *
106 * This in turn calls the methods producing individual parts of the page
b0f2597e 107 */
b0f2597e 108 function view() {
04eba58f 109
b0f2597e 110 add_to_log($this->course->id, "assignment", "view", "view.php?id={$this->cm->id}",
111 $this->assignment->id, $this->cm->id);
04eba58f 112
73097f07 113 $this->view_header();
04eba58f 114
f77cfb73 115 $this->view_intro();
04eba58f 116
f77cfb73 117 $this->view_dates();
04eba58f 118
b0f2597e 119 $this->view_feedback();
120
f77cfb73 121 $this->view_footer();
36eb856f 122 }
123
7af1e882 124 /**
125 * Display the header and top of a page
126 *
127 * (this doesn't change much for assignment types)
128 * This is used by the view() method to print the header of view.php but
129 * it can be used on other pages in which case the string to denote the
130 * page in the navigation trail should be passed as an argument
131 *
132 * @param $subpage string Description of subpage to be used in navigation trail
73097f07 133 */
134 function view_header($subpage='') {
135
136 global $CFG;
137
138 if ($subpage) {
139 $extranav = '<a target="'.$CFG->framename.'" href="view.php?id='.$this->cm->id.'">'.
3898bc33 140 format_string($this->assignment->name,true).'</a> -> '.$subpage;
73097f07 141 } else {
3898bc33 142 $extranav = ' '.format_string($this->assignment->name,true);
73097f07 143 }
144
145 print_header($this->pagetitle, $this->course->fullname, $this->navigation.$extranav, '', '',
146 true, update_module_button($this->cm->id, $this->course->id, $this->strassignment),
147 navmenu($this->course, $this->cm));
148
149 echo '<div class="reportlink">'.$this->submittedlink().'</div>';
150 }
151
152
7af1e882 153 /**
f77cfb73 154 * Display the assignment intro
7af1e882 155 *
156 * This will most likely be extended by assignment type plug-ins
157 * The default implementation prints the assignment description in a box
f77cfb73 158 */
159 function view_intro() {
160 print_simple_box_start('center', '', '', '', 'generalbox', 'intro');
1e4343a0 161 $formatoptions = new stdClass;
162 $formatoptions->noclean = true;
163 echo format_text($this->assignment->description, $this->assignment->format, $formatoptions);
f77cfb73 164 print_simple_box_end();
165 }
166
7af1e882 167 /**
f77cfb73 168 * Display the assignment dates
7af1e882 169 *
170 * Prints the assignment start and end dates in a box.
171 * This will be suitable for most assignment types
f77cfb73 172 */
173 function view_dates() {
174 if (!$this->assignment->timeavailable && !$this->assignment->timedue) {
175 return;
176 }
177
178 print_simple_box_start('center', '', '', '', 'generalbox', 'dates');
179 echo '<table>';
180 if ($this->assignment->timeavailable) {
181 echo '<tr><td class="c0">'.get_string('availabledate','assignment').':</td>';
182 echo ' <td class="c1">'.userdate($this->assignment->timeavailable).'</td></tr>';
183 }
184 if ($this->assignment->timedue) {
185 echo '<tr><td class="c0">'.get_string('duedate','assignment').':</td>';
186 echo ' <td class="c1">'.userdate($this->assignment->timedue).'</td></tr>';
187 }
188 echo '</table>';
189 print_simple_box_end();
190 }
191
192
7af1e882 193 /**
194 * Display the bottom and footer of a page
195 *
196 * This default method just prints the footer.
197 * This will be suitable for most assignment types
73097f07 198 */
199 function view_footer() {
200 print_footer($this->course);
201 }
202
7af1e882 203 /**
204 * Display the feedback to the student
205 *
206 * This default method prints the teacher picture and name, date when marked,
207 * grade and teacher comment.
208 *
209 * @param $submission object The submission object or NULL in which case it will be loaded
210 */
73097f07 211 function view_feedback($submission=NULL) {
b0f2597e 212 global $USER;
e6a4906b 213
73097f07 214 if (!$submission) { /// Get submission for this assignment
215 $submission = $this->get_submission($USER->id);
70b2c772 216 }
217
218 if (empty($submission->timemarked)) { /// Nothing to show, so print nothing
219 return;
9c48354d 220 }
e6a4906b 221
b0f2597e 222 /// We need the teacher info
223 if (! $teacher = get_record('user', 'id', $submission->teacher)) {
b0f2597e 224 error('Could not find the teacher');
225 }
e6a4906b 226
b0f2597e 227 /// Print the feedback
6d4ecaec 228 print_heading(get_string('feedbackfromteacher', 'assignment', $this->course->teacher));
229
b0f2597e 230 echo '<table cellspacing="0" class="feedback">';
231
232 echo '<tr>';
233 echo '<td class="left picture">';
234 print_user_picture($teacher->id, $this->course->id, $teacher->picture);
235 echo '</td>';
6d4ecaec 236 echo '<td class="topic">';
70b2c772 237 echo '<div class="from">';
73097f07 238 echo '<div class="fullname">'.fullname($teacher).'</div>';
6d4ecaec 239 echo '<div class="time">'.userdate($submission->timemarked).'</div>';
70b2c772 240 echo '</div>';
b0f2597e 241 echo '</td>';
242 echo '</tr>';
243
244 echo '<tr>';
245 echo '<td class="left side">&nbsp;</td>';
6d4ecaec 246 echo '<td class="content">';
b0f2597e 247 if ($this->assignment->grade) {
6d4ecaec 248 echo '<div class="grade">';
70b2c772 249 echo get_string("grade").': '.$this->display_grade($submission->grade);
6d4ecaec 250 echo '</div>';
52436fe1 251 echo '<div class="clearer"></div>';
e6a4906b 252 }
dcd338ff 253
6d4ecaec 254 echo '<div class="comment">';
ff8f7015 255 echo format_text($submission->comment, $submission->format);
6d4ecaec 256 echo '</div>';
b0f2597e 257 echo '</tr>';
258
259 echo '</table>';
e6a4906b 260 }
e6a4906b 261
7af1e882 262 /**
ba16713f 263 * Returns a link with info about the state of the assignment submissions
7af1e882 264 *
265 * This is used by view_header to put this link at the top right of the page.
266 * For teachers it gives the number of submitted assignments with a link
267 * For students it gives the time of their submission.
268 * This will be suitable for most assignment types.
269 * @return string
ba16713f 270 */
271 function submittedlink() {
272 global $USER;
273
274 $submitted = '';
275
276 if (isteacher($this->course->id)) {
277 if (!isteacheredit($this->course->id) and user_group($this->course->id, $USER->id)) {
278 $count = $this->count_real_submissions($this->currentgroup); // Only their group
279 } else {
280 $count = $this->count_real_submissions(); // Everyone
281 }
282 $submitted = '<a href="submissions.php?id='.$this->cm->id.'">'.
283 get_string('viewsubmissions', 'assignment', $count).'</a>';
284 } else {
285 if (isset($USER->id)) {
286 if ($submission = $this->get_submission($USER->id)) {
287 if ($submission->timemodified) {
1e4343a0 288 if ($submission->timemodified <= $this->assignment->timedue || empty($this->assignment->timedue)) {
ba16713f 289 $submitted = '<span class="early">'.userdate($submission->timemodified).'</span>';
290 } else {
291 $submitted = '<span class="late">'.userdate($submission->timemodified).'</span>';
292 }
293 }
294 }
295 }
296 }
297
298 return $submitted;
299 }
300
301
7af1e882 302 /**
303 * Print the setup form for the current assignment type
304 *
305 * Includes common.html and the assignment type's mod.html
306 * This will be suitable for all assignment types
307 *
308 * @param $form object The object used to fill the form
309 * @param $action url Default destination for this form
b0f2597e 310 */
311 function setup(&$form, $action='') {
312 global $CFG, $THEME;
313
314 if (empty($this->course)) {
315 if (! $this->course = get_record("course", "id", $form->course)) {
316 error("Course is misconfigured");
77db7e4c 317 }
318 }
f619f8c8 319
b0f2597e 320 if (empty($action)) { // Default destination for this form
321 $action = $CFG->wwwroot.'/course/mod.php';
322 }
77db7e4c 323
b0f2597e 324 if (empty($form->assignmenttype)) {
f619f8c8 325 $form->assignmenttype = '';
1e4343a0 326 } else {
327 $form->assignmenttype = clean_param($form->assignmenttype, PARAM_SAFEDIR);
b0f2597e 328 }
f619f8c8 329
330 if (empty($form->name)) {
331 $form->name = '';
332 } else {
333 $form->name = stripslashes($form->name);
334 }
335
b0f2597e 336 if (empty($form->description)) {
f619f8c8 337 $form->description = '';
338 } else {
339 $form->description = stripslashes($form->description);
b0f2597e 340 }
77db7e4c 341
b0f2597e 342 $strname = get_string('name');
343 $strassignments = get_string('modulenameplural', 'assignment');
3898bc33 344 $strheading = empty($form->name) ? get_string("type$form->assignmenttype",'assignment') : s(format_string(stripslashes($form->name),true));
77db7e4c 345
f619f8c8 346 print_header($this->course->shortname.': '.$strheading, $this->course->fullname,
b0f2597e 347 "<a href=\"$CFG->wwwroot/course/view.php?id={$this->course->id}\">{$this->course->shortname} </a> -> ".
348 "<a href=\"$CFG->wwwroot/mod/assignment/index.php?id={$this->course->id}\">$strassignments</a> -> $strheading");
77db7e4c 349
f77cfb73 350 print_simple_box_start('center', '70%');
351 print_heading(get_string('type'.$form->assignmenttype,'assignment'));
352 print_simple_box(get_string('help'.$form->assignmenttype, 'assignment'), 'center');
b0f2597e 353 include("$CFG->dirroot/mod/assignment/type/common.html");
f77cfb73 354
355 include("$CFG->dirroot/mod/assignment/type/".$form->assignmenttype."/mod.html");
356 $this->setup_end();
b0f2597e 357 }
358
7af1e882 359 /**
b0f2597e 360 * Print the end of the setup form for the current assignment type
7af1e882 361 *
362 * Includes common_end.html
b0f2597e 363 */
364 function setup_end() {
73097f07 365 global $CFG;
b0f2597e 366
367 include($CFG->dirroot.'/mod/assignment/type/common_end.html');
77db7e4c 368
369 print_simple_box_end();
370
73097f07 371 if ($this->usehtmleditor) {
b0f2597e 372 use_html_editor();
373 }
374
375 print_footer($this->course);
77db7e4c 376 }
77db7e4c 377
7af1e882 378 /**
379 * Create a new assignment activity
380 *
381 * Given an object containing all the necessary data,
382 * (defined by the form in mod.html) this function
383 * will create a new instance and return the id number
384 * of the new instance.
385 * The due data is added to the calendar
386 * This is common to all assignment types.
387 *
388 * @param $assignment object The data from the form on mod.html
389 * @return int The id of the assignment
390 */
b0f2597e 391 function add_instance($assignment) {
b0f2597e 392
393 $assignment->timemodified = time();
38147229 394 if (empty($assignment->dueenable)) {
395 $assignment->timedue = 0;
f00da052 396 $assignment->preventlate = 0;
38147229 397 } else {
398 $assignment->timedue = make_timestamp($assignment->dueyear, $assignment->duemonth,
399 $assignment->dueday, $assignment->duehour,
400 $assignment->dueminute);
401 }
402 if (empty($assignment->availableenable)) {
403 $assignment->timeavailable = 0;
38147229 404 } else {
405 $assignment->timeavailable = make_timestamp($assignment->availableyear, $assignment->availablemonth,
406 $assignment->availableday, $assignment->availablehour,
407 $assignment->availableminute);
408 }
409
736f191c 410 if ($returnid = insert_record("assignment", $assignment)) {
411
1e4343a0 412 if ($assignment->timedue) {
413 $event = NULL;
414 $event->name = $assignment->name;
415 $event->description = $assignment->description;
416 $event->courseid = $assignment->course;
417 $event->groupid = 0;
418 $event->userid = 0;
419 $event->modulename = 'assignment';
420 $event->instance = $returnid;
421 $event->eventtype = 'due';
422 $event->timestart = $assignment->timedue;
423 $event->timeduration = 0;
736f191c 424
1e4343a0 425 add_event($event);
426 }
736f191c 427 }
428
429 return $returnid;
b0f2597e 430 }
d699cd1e 431
7af1e882 432 /**
433 * Deletes an assignment activity
434 *
435 * Deletes all database records and calendar events for this assignment.
436 * @param $assignment object The assignment to be deleted
437 * @return boolean False indicates error
438 */
b0f2597e 439 function delete_instance($assignment) {
736f191c 440 $result = true;
441
442 if (! delete_records('assignment_submissions', 'assignment', $assignment->id)) {
443 $result = false;
444 }
445
446 if (! delete_records('assignment', 'id', $assignment->id)) {
447 $result = false;
448 }
449
450 if (! delete_records('event', 'modulename', 'assignment', 'instance', $assignment->id)) {
451 $result = false;
452 }
453
454 return $result;
b0f2597e 455 }
d699cd1e 456
7af1e882 457 /**
458 * Updates a new assignment activity
459 *
460 * Given an object containing all the necessary data,
461 * (defined by the form in mod.html) this function
462 * will update the assignment instance and return the id number
463 * The due date is updated in the calendar
464 * This is common to all assignment types.
465 *
466 * @param $assignment object The data from the form on mod.html
467 * @return int The assignment id
468 */
b0f2597e 469 function update_instance($assignment) {
b0f2597e 470
38147229 471 $assignment->timemodified = time();
472 if (empty($assignment->dueenable)) {
473 $assignment->timedue = 0;
5c6b657a 474 $assignment->preventlate = 0;
38147229 475 } else {
476 $assignment->timedue = make_timestamp($assignment->dueyear, $assignment->duemonth,
477 $assignment->dueday, $assignment->duehour,
478 $assignment->dueminute);
479 }
480 if (empty($assignment->availableenable)) {
481 $assignment->timeavailable = 0;
38147229 482 } else {
483 $assignment->timeavailable = make_timestamp($assignment->availableyear, $assignment->availablemonth,
484 $assignment->availableday, $assignment->availablehour,
485 $assignment->availableminute);
486 }
487
b0f2597e 488 $assignment->id = $assignment->instance;
736f191c 489
490 if ($returnid = update_record('assignment', $assignment)) {
491
1e4343a0 492 if ($assignment->timedue) {
493 $event = NULL;
736f191c 494
1e4343a0 495 if ($event->id = get_field('event', 'id', 'modulename', 'assignment', 'instance', $assignment->id)) {
736f191c 496
1e4343a0 497 $event->name = $assignment->name;
498 $event->description = $assignment->description;
499 $event->timestart = $assignment->timedue;
736f191c 500
1e4343a0 501 update_event($event);
47263937 502 } else {
503 $event = NULL;
504 $event->name = $assignment->name;
505 $event->description = $assignment->description;
506 $event->courseid = $assignment->course;
507 $event->groupid = 0;
508 $event->userid = 0;
509 $event->modulename = 'assignment';
510 $event->instance = $assignment->id;
511 $event->eventtype = 'due';
512 $event->timestart = $assignment->timedue;
513 $event->timeduration = 0;
514
515 add_event($event);
1e4343a0 516 }
47263937 517 } else {
518 delete_records('event', 'modulename', 'assignment', 'instance', $assignment->id);
736f191c 519 }
520 }
521
522 return $returnid;
b0f2597e 523 }
524
7af1e882 525 /**
b0f2597e 526 * Top-level function for handling of submissions called by submissions.php
7af1e882 527 *
528 * This is for handling the teacher interaction with the grading interface
529 * This should be suitable for most assignment types.
530 *
531 * @param $mode string Specifies the kind of teacher interaction taking place
b0f2597e 532 */
533 function submissions($mode) {
9bf660b3 534 ///The main switch is changed to facilitate
535 ///1) Batch fast grading
536 ///2) Skip to the next one on the popup
537 ///3) Save and Skip to the next one on the popup
538
539 //make user global so we can use the id
540 global $USER;
541
b0f2597e 542 switch ($mode) {
543 case 'grade': // We are in a popup window grading
544 if ($submission = $this->process_feedback()) {
5a36be8c 545 //IE needs proper header with encoding
546 print_header(get_string('feedback', 'assignment').':'.format_string($this->assignment->name));
b0f2597e 547 print_heading(get_string('changessaved'));
be86672d 548 $this->update_main_listing($submission);
b0f2597e 549 }
550 close_window();
551 break;
9cc9b7c1 552
b0f2597e 553 case 'single': // We are in a popup window displaying submission
554 $this->display_submission();
555 break;
a56d79cd 556
b0f2597e 557 case 'all': // Main window, display everything
558 $this->display_submissions();
559 break;
082215e6 560
9bf660b3 561 case 'fastgrade':
082215e6 562 ///do the fast grading stuff - this process should work for all 3 subclasses
39e11905 563 $grading = false;
16907e53 564 $commenting = false;
39e11905 565 $col = false;
16907e53 566 if (isset($_POST['comment'])) {
39e11905 567 $col = 'comment';
16907e53 568 $commenting = true;
569 }
570 if (isset($_POST['menu'])) {
39e11905 571 $col = 'menu';
16907e53 572 $grading = true;
573 }
39e11905 574 if (!$col) {
575 //both comment and grade columns collapsed..
576 $this->display_submissions();
16907e53 577 break;
578 }
39e11905 579 foreach ($_POST[$col] as $id => $unusedvalue){
77f4b17b 580
581 $id = (int)$id; //clean parameter name
39e11905 582 if (!$submission = $this->get_submission($id)) {
583 $submission = $this->prepare_new_submission($id);
584 $newsubmission = true;
585 } else {
586 $newsubmission = false;
587 }
588 unset($submission->data1); // Don't need to update this.
589 unset($submission->data2); // Don't need to update this.
16907e53 590
9bf660b3 591 //for fast grade, we need to check if any changes take place
16907e53 592 $updatedb = false;
593
594 if ($grading) {
595 $grade = $_POST['menu'][$id];
39e11905 596 $updatedb = $updatedb || ($submission->grade != $grade);
597 $submission->grade = $grade;
16907e53 598 } else {
39e11905 599 if (!$newsubmission) {
600 unset($submission->grade); // Don't need to update this.
601 }
16907e53 602 }
603 if ($commenting) {
39e11905 604 $commentvalue = trim($_POST['comment'][$id]);
605 $updatedb = $updatedb || ($submission->comment != stripslashes($commentvalue));
606 $submission->comment = $commentvalue;
16907e53 607 } else {
39e11905 608 unset($submission->comment); // Don't need to update this.
9bf660b3 609 }
610
39e11905 611 $submission->teacher = $USER->id;
612 $submission->mailed = $updatedb?0:$submission->mailed;//only change if it's an update
613 $submission->timemarked = time();
614
615 //if it is not an update, we don't change the last modified time etc.
9bf660b3 616 //this will also not write into database if no comment and grade is entered.
39e11905 617
16907e53 618 if ($updatedb){
39e11905 619 if ($newsubmission) {
620 if (!insert_record('assignment_submissions', $submission)) {
621 return false;
622 }
623 } else {
624 if (!update_record('assignment_submissions', $submission)) {
625 return false;
626 }
9bf660b3 627 }
39e11905 628 //add to log only if updating
9bf660b3 629 add_to_log($this->course->id, 'assignment', 'update grades',
39e11905 630 'submissions.php?id='.$this->assignment->id.'&user='.$submission->userid,
631 $submission->userid, $this->cm->id);
9bf660b3 632 }
633
e11dd872 634 }
635 print_heading(get_string('changessaved'));
9bf660b3 636 $this->display_submissions();
637 break;
39e11905 638
639
9bf660b3 640 case 'next':
641 /// We are currently in pop up, but we want to skip to next one without saving.
642 /// This turns out to be similar to a single case
643 /// The URL used is for the next submission.
644
645 $this->display_submission();
646 break;
647
648 case 'saveandnext':
649 ///We are in pop up. save the current one and go to the next one.
650 //first we save the current changes
651 if ($submission = $this->process_feedback()) {
652 //print_heading(get_string('changessaved'));
653 $this->update_main_listing($submission);
654 }
655
656 //then we display the next submission
657 $this->display_submission();
658 break;
659
660 default:
661 echo "something seriously is wrong!!";
662 break;
a56d79cd 663 }
b0f2597e 664 }
9bf660b3 665
7af1e882 666 /**
667 * Helper method updating the listing on the main script from popup using javascript
668 *
669 * @param $submission object The submission whose data is to be updated on the main page
670 */
be86672d 671 function update_main_listing($submission) {
672 global $SESSION;
9bf660b3 673
674 $perpage = get_user_preferences('assignment_perpage', 10);
be86672d 675
9bf660b3 676 $quickgrade = get_user_preferences('assignment_quickgrade', 0);
677
be86672d 678 /// Run some Javascript to try and update the parent page
679 echo '<script type="text/javascript">'."\n<!--\n";
be86672d 680 if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['comment'])) {
9bf660b3 681 if ($quickgrade){
682 echo 'opener.document.getElementById("comment['.$submission->userid.']").value="'
683 .trim($submission->comment).'";'."\n";
684 } else {
685 echo 'opener.document.getElementById("com'.$submission->userid.
686 '").innerHTML="'.shorten_text(trim(strip_tags($submission->comment)), 15)."\";\n";
687 }
be86672d 688 }
9bf660b3 689
690 if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['grade'])) {
691 //echo optional_param('menuindex');
692 if ($quickgrade){
693 echo 'opener.document.getElementById("menumenu['.$submission->userid.
16907e53 694 ']").selectedIndex="'.optional_param('menuindex', 0, PARAM_INT).'";'."\n";
9bf660b3 695 } else {
696 echo 'opener.document.getElementById("g'.$submission->userid.'").innerHTML="'.
697 $this->display_grade($submission->grade)."\";\n";
698 }
699 }
700 //need to add student's assignments in there too.
73097f07 701 if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['timemodified']) &&
702 $submission->timemodified) {
be86672d 703 echo 'opener.document.getElementById("ts'.$submission->userid.
9bf660b3 704 '").innerHTML="'.addslashes($this->print_student_answer($submission->userid)).userdate($submission->timemodified)."\";\n";
be86672d 705 }
9bf660b3 706
73097f07 707 if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['timemarked']) &&
708 $submission->timemarked) {
be86672d 709 echo 'opener.document.getElementById("tt'.$submission->userid.
710 '").innerHTML="'.userdate($submission->timemarked)."\";\n";
711 }
9bf660b3 712
be86672d 713 if (empty($SESSION->flextable['mod-assignment-submissions']->collapse['status'])) {
9bf660b3 714 echo 'opener.document.getElementById("up'.$submission->userid.'").className="s1";';
715 $buttontext = get_string('update');
16907e53 716 $button = link_to_popup_window ('/mod/assignment/submissions.php?id='.$this->cm->id.'&amp;userid='.$submission->userid.'&amp;mode=single'.'&amp;offset='.optional_param('offset', '', PARAM_INT),
9bf660b3 717 'grade'.$submission->userid, $buttontext, 450, 700, $buttontext, 'none', true, 'button'.$submission->userid);
718 echo 'opener.document.getElementById("up'.$submission->userid.'").innerHTML="'.addslashes($button).'";';
719 }
be86672d 720 echo "\n-->\n</script>";
3262ee10 721 flush();
be86672d 722 }
d699cd1e 723
7af1e882 724 /**
725 * Return a grade in user-friendly form, whether it's a scale or not
d59269cf 726 *
7af1e882 727 * @param $grade
728 * @return string User-friendly representation of grade
d59269cf 729 */
730 function display_grade($grade) {
731
c86aa2a4 732 static $scalegrades = array(); // Cache scales for each assignment - they might have different scales!!
d59269cf 733
734 if ($this->assignment->grade >= 0) { // Normal number
082215e6 735 if ($grade == -1) {
736 return '-';
737 } else {
738 return $grade.' / '.$this->assignment->grade;
739 }
d59269cf 740
741 } else { // Scale
c86aa2a4 742 if (empty($scalegrades[$this->assignment->id])) {
d59269cf 743 if ($scale = get_record('scale', 'id', -($this->assignment->grade))) {
c86aa2a4 744 $scalegrades[$this->assignment->id] = make_menu_from_list($scale->scale);
d59269cf 745 } else {
746 return '-';
747 }
748 }
c86aa2a4 749 if (isset($scalegrades[$this->assignment->id][$grade])) {
750 return $scalegrades[$this->assignment->id][$grade];
0f7d4e5e 751 }
39e11905 752 return '-';
d59269cf 753 }
754 }
755
7af1e882 756 /**
b0f2597e 757 * Display a single submission, ready for grading on a popup window
7af1e882 758 *
759 * This default method prints the teacher info and comment box at the top and
760 * the student info and submission at the bottom.
761 * This method also fetches the necessary data in order to be able to
762 * provide a "Next submission" button.
763 * Calls preprocess_submission() to give assignment type plug-ins a chance
764 * to process submissions before they are graded
765 * This method gets its arguments from the page parameters userid and offset
b0f2597e 766 */
767 function display_submission() {
9bf660b3 768
082215e6 769 global $CFG;
b0f2597e 770
4fdabdc3 771 $userid = required_param('userid', PARAM_INT);
772 $offset = required_param('offset', PARAM_INT);//offset for where to start looking for student.
d699cd1e 773
b0f2597e 774 if (!$user = get_record('user', 'id', $userid)) {
775 error('No such user!');
776 }
d699cd1e 777
39e11905 778 if (!$submission = $this->get_submission($user->id)) {
779 $submission = $this->prepare_new_submission($userid);
b0f2597e 780 }
a5a4cd60 781
b0f2597e 782 if ($submission->timemodified > $submission->timemarked) {
783 $subtype = 'assignmentnew';
784 } else {
785 $subtype = 'assignmentold';
786 }
d699cd1e 787
9bf660b3 788 ///construct SQL, using current offset to find the data of the next student
789 $course = $this->course;
790 $assignment = $this->assignment;
791 $cm = $this->cm;
792
793 if ($groupmode = groupmode($course, $cm)) { // Groups are being used
794 $currentgroup = setup_and_print_groups($course, $groupmode, 'submissions.php?id='.$this->cm->id);
795 } else {
796 $currentgroup = false;
797 }
d699cd1e 798
9bf660b3 799 /// Get all teachers and students
800 if ($currentgroup) {
801 $users = get_group_users($currentgroup);
802 } else {
803 $users = get_course_users($course->id);
804 }
5b48244f 805
082215e6 806 $select = 'SELECT u.id, u.id, u.firstname, u.lastname, u.picture,'.
807 's.id AS submissionid, s.grade, s.comment, s.timemodified, s.timemarked, ((s.timemarked > 0) AND (s.timemarked >= s.timemodified)) AS status ';
9bf660b3 808 $sql = 'FROM '.$CFG->prefix.'user u '.
809 'LEFT JOIN '.$CFG->prefix.'assignment_submissions s ON u.id = s.userid AND s.assignment = '.$this->assignment->id.' '.
082215e6 810 'WHERE u.id IN ('.implode(',', array_keys($users)).') ';
02828119 811
812 require_once($CFG->libdir.'/tablelib.php');
813 if($sort = flexible_table::get_sql_sort('mod-assignment-submissions')) {
814 $sort = 'ORDER BY '.$sort.' ';
815 }
816
817 $limit = sql_paging_limit($offset+1, 1);
818
082215e6 819 $nextid = 0;
81532b92 820 if (($auser = get_record_sql($select.$sql.$sort.$limit, false, true)) !== false) {
5b48244f 821 $nextid = $auser->id;
9bf660b3 822 }
81532b92 823
9bf660b3 824 print_header(get_string('feedback', 'assignment').':'.fullname($user, true).':'.format_string($this->assignment->name));
d699cd1e 825
9bf660b3 826 ///SOme javascript to help with setting up >.>
827
c9977d05 828 echo '<script type="text/javascript">'."\n";
9bf660b3 829 echo 'function setNext(){'."\n";
830 echo 'document.submitform.mode.value=\'next\';'."\n";
831 echo 'document.submitform.userid.value="'.$nextid.'";'."\n";
832 echo '}'."\n";
833
834 echo 'function saveNext(){'."\n";
835 echo 'document.submitform.mode.value=\'saveandnext\';'."\n";
836 echo 'document.submitform.userid.value="'.$nextid.'";'."\n";
837 echo 'document.submitform.saveuserid.value="'.$userid.'";'."\n";
838 echo 'document.submitform.menuindex.value = document.submitform.grade.selectedIndex;'."\n";
839 echo '}'."\n";
840
841 echo '</script>'."\n";
52436fe1 842 echo '<table cellspacing="0" class="feedback '.$subtype.'" >';
d699cd1e 843
9bf660b3 844 ///Start of teacher info row
c69cb506 845
b0f2597e 846 echo '<tr>';
847 echo '<td width="35" valign="top" class="picture teacher">';
848 if ($submission->teacher) {
849 $teacher = get_record('user', 'id', $submission->teacher);
850 } else {
851 global $USER;
852 $teacher = $USER;
853 }
854 print_user_picture($teacher->id, $this->course->id, $teacher->picture);
855 echo '</td>';
856 echo '<td class="content">';
9894b824 857 echo '<form name="submitform" action="submissions.php" method="post">';
858 echo '<input type="hidden" name="offset" value="'.++$offset.'">';
c9977d05 859 echo '<input type="hidden" name="userid" value="'.$userid.'" />';
860 echo '<input type="hidden" name="id" value="'.$this->cm->id.'" />';
861 echo '<input type="hidden" name="mode" value="grade" />';
862 echo '<input type="hidden" name="menuindex" value="0" />';//selected menu index
9bf660b3 863
864 //new hidden field, initialized to -1.
c9977d05 865 echo '<input type="hidden" name="saveuserid" value="-1" />';
52436fe1 866 if ($submission->timemarked) {
867 echo '<div class="from">';
868 echo '<div class="fullname">'.fullname($teacher, true).'</div>';
869 echo '<div class="time">'.userdate($submission->timemarked).'</div>';
870 echo '</div>';
871 }
872 echo '<div class="grade">'.get_string('grade').':';
082215e6 873 choose_from_menu(make_grades_menu($this->assignment->grade), 'grade', $submission->grade, get_string('nograde'), '', -1);
52436fe1 874 echo '</div>';
875 echo '<div class="clearer"></div>';
b0f2597e 876
01e2fdfe 877 $this->preprocess_submission($submission);
878
b0f2597e 879 echo '<br />';
a8166227 880 print_textarea($this->usehtmleditor, 14, 58, 0, 0, 'comment', $submission->comment, $this->course->id);
ff8f7015 881
ff8f7015 882 if ($this->usehtmleditor) {
ff8f7015 883 echo '<input type="hidden" name="format" value="'.FORMAT_HTML.'" />';
884 } else {
f77cfb73 885 echo '<div align="right" class="format">';
ff8f7015 886 choose_from_menu(format_text_menu(), "format", $submission->format, "");
f77cfb73 887 helpbutton("textformat", get_string("helpformatting"));
888 echo '</div>';
ff8f7015 889 }
b0f2597e 890
9bf660b3 891 ///Print Buttons in Single View
73097f07 892 echo '<div class="buttons" align="center">';
c9977d05 893 echo '<input type="submit" name="submit" value="'.get_string('savechanges').'" onclick = "document.submitform.menuindex.value = document.submitform.grade.selectedIndex" />';
b0f2597e 894 echo '<input type="submit" name="cancel" value="'.get_string('cancel').'" />';
9bf660b3 895 //if there are more to be graded.
082215e6 896 if ($nextid) {
5b48244f 897 echo '<input type="submit" name="saveandnext" value="'.get_string('saveandnext').'" onclick="saveNext()" />';
898 echo '<input type="submit" name="next" value="'.get_string('next').'" onclick="setNext();" />';
9bf660b3 899 }
73097f07 900 echo '</div>';
b0f2597e 901 echo '</form>';
73097f07 902 echo '</td></tr>';
9bf660b3 903
904 ///End of teacher info row, Start of student info row
905 echo '<tr>';
906 echo '<td width="35" valign="top" class="picture user">';
907 print_user_picture($user->id, $this->course->id, $user->picture);
908 echo '</td>';
909 echo '<td class="topic">';
910 echo '<div class="from">';
911 echo '<div class="fullname">'.fullname($user, true).'</div>';
912 if ($submission->timemodified) {
913 echo '<div class="time">'.userdate($submission->timemodified).
914 $this->display_lateness($submission->timemodified).'</div>';
915 }
916 echo '</div>';
917 $this->print_user_files($user->id);
918 echo '</td>';
919 echo '</tr>';
920
921 ///End of student info row
922
73097f07 923 echo '</table>';
924
73097f07 925 if ($this->usehtmleditor) {
926 use_html_editor();
927 }
928
b0f2597e 929 print_footer('none');
d699cd1e 930 }
931
7af1e882 932 /**
01e2fdfe 933 * Preprocess submission before grading
7af1e882 934 *
935 * Called by display_submission()
936 * The default type does nothing here.
937 * @param $submission object The submission object
01e2fdfe 938 */
939 function preprocess_submission(&$submission) {
940 }
d699cd1e 941
7af1e882 942 /**
b0f2597e 943 * Display all the submissions ready for grading
944 */
945 function display_submissions() {
3446205d 946
9bf660b3 947 global $CFG, $db, $USER;
3446205d 948
9bf660b3 949 /* first we check to see if the form has just been submitted
950 * to request user_preference updates
951 */
952
953 if (isset($_POST['updatepref'])){
16907e53 954 $perpage = optional_param('perpage', 10, PARAM_INT);
9bf660b3 955 $perpage = ($perpage <= 0) ? 10 : $perpage ;
956 set_user_preference('assignment_perpage', $perpage);
16907e53 957 set_user_preference('assignment_quickgrade', optional_param('quickgrade',0, PARAM_BOOL));
9bf660b3 958 }
1b5910c4 959
9bf660b3 960 /* next we get perpage and quickgrade (allow quick grade) params
961 * from database
962 */
963 $perpage = get_user_preferences('assignment_perpage', 10);
964 $quickgrade = get_user_preferences('assignment_quickgrade', 0);
965
966 $teacherattempts = true; /// Temporary measure
16907e53 967 $page = optional_param('page', 0, PARAM_INT);
b0f2597e 968 $strsaveallfeedback = get_string('saveallfeedback', 'assignment');
d0ac6bc2 969
b0f2597e 970 /// Some shortcuts to make the code read better
971
972 $course = $this->course;
973 $assignment = $this->assignment;
974 $cm = $this->cm;
9bf660b3 975
976 $tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet
91719320 977
b0f2597e 978 add_to_log($course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->assignment->id, $this->assignment->id, $this->cm->id);
979
3898bc33 980 print_header_simple(format_string($this->assignment->name,true), "", '<a href="index.php?id='.$course->id.'">'.$this->strassignments.'</a> -> <a href="view.php?a='.$this->assignment->id.'">'.format_string($this->assignment->name,true).'</a> -> '. $this->strsubmissions, '', '', true, update_module_button($cm->id, $course->id, $this->strassignment), navmenu($course, $cm));
fa22fd5f 981
982 ///Position swapped
983 if ($groupmode = groupmode($course, $cm)) { // Groups are being used
984 $currentgroup = setup_and_print_groups($course, $groupmode, 'submissions.php?id='.$this->cm->id);
985 } else {
986 $currentgroup = false;
987 }
988
989 /// Get all teachers and students
990 if ($currentgroup) {
991 $users = get_group_users($currentgroup);
992 } else {
993 $users = get_course_users($course->id);
994 }
91719320 995
9437c854 996 $tablecolumns = array('picture', 'fullname', 'grade', 'comment', 'timemodified', 'timemarked', 'status');
997 $tableheaders = array('', get_string('fullname'), get_string('grade'), get_string('comment', 'assignment'), get_string('lastmodified').' ('.$course->student.')', get_string('lastmodified').' ('.$course->teacher.')', get_string('status'));
91719320 998
b0f2597e 999 require_once($CFG->libdir.'/tablelib.php');
1000 $table = new flexible_table('mod-assignment-submissions');
1001
1002 $table->define_columns($tablecolumns);
1003 $table->define_headers($tableheaders);
fa22fd5f 1004 $table->define_baseurl($CFG->wwwroot.'/mod/assignment/submissions.php?id='.$this->cm->id.'&amp;currentgroup='.$currentgroup);
b0f2597e 1005
1006 $table->sortable(true);
1007 $table->collapsible(true);
1008 $table->initialbars(true);
1009
1010 $table->column_suppress('picture');
1011 $table->column_suppress('fullname');
1012
1013 $table->column_class('picture', 'picture');
9437c854 1014 $table->column_class('fullname', 'fullname');
1015 $table->column_class('grade', 'grade');
1016 $table->column_class('comment', 'comment');
1017 $table->column_class('timemodified', 'timemodified');
1018 $table->column_class('timemarked', 'timemarked');
1019 $table->column_class('status', 'status');
b0f2597e 1020
1021 $table->set_attribute('cellspacing', '0');
1022 $table->set_attribute('id', 'attempts');
9437c854 1023 $table->set_attribute('class', 'submissions');
b0f2597e 1024 $table->set_attribute('width', '90%');
1025 $table->set_attribute('align', 'center');
1026
1027 // Start working -- this is necessary as soon as the niceties are over
1028 $table->setup();
1029
b0f2597e 1030 /// Check to see if groups are being used in this assignment
05855091 1031
b0f2597e 1032 if (!$teacherattempts) {
1033 $teachers = get_course_teachers($course->id);
1034 if (!empty($teachers)) {
1035 $keys = array_keys($teachers);
1036 }
1037 foreach ($keys as $key) {
1038 unset($users[$key]);
1039 }
1040 }
1041
1042 if (empty($users)) {
c8dbfa5c 1043 print_heading(get_string('noattempts','assignment'));
b0f2597e 1044 return true;
1045 }
0f1a97c2 1046
b0f2597e 1047 /// Construct the SQL
0f1a97c2 1048
b0f2597e 1049 if ($where = $table->get_sql_where()) {
b0f2597e 1050 $where .= ' AND ';
1051 }
0f1a97c2 1052
b0f2597e 1053 if ($sort = $table->get_sql_sort()) {
86f65395 1054 $sort = ' ORDER BY '.$sort;
b0f2597e 1055 }
9fa49e22 1056
042311a9 1057 $select = 'SELECT u.id, u.id, u.firstname, u.lastname, u.picture, s.id AS submissionid, s.grade, s.comment, s.timemodified, s.timemarked, ((s.timemarked > 0) AND (s.timemarked >= s.timemodified)) AS status ';
b0f2597e 1058 $sql = 'FROM '.$CFG->prefix.'user u '.
306dc7e5 1059 'LEFT JOIN '.$CFG->prefix.'assignment_submissions s ON u.id = s.userid AND s.assignment = '.$this->assignment->id.' '.
1060 'WHERE '.$where.'u.id IN ('.implode(',', array_keys($users)).') ';
9bf660b3 1061
c5d36203 1062 $table->pagesize($perpage, count($users));
b0f2597e 1063
1064 if($table->get_page_start() !== '' && $table->get_page_size() !== '') {
1065 $limit = ' '.sql_paging_limit($table->get_page_start(), $table->get_page_size());
1066 }
1067 else {
1068 $limit = '';
1069 }
9bf660b3 1070
9bf660b3 1071 ///offset used to calculate index of student in that particular query, needed for the pop up to know who's next
1072 $offset = $page * $perpage;
1073
b0f2597e 1074 $strupdate = get_string('update');
9437c854 1075 $strgrade = get_string('grade');
b0f2597e 1076 $grademenu = make_grades_menu($this->assignment->grade);
1077
c5d36203 1078 if (($ausers = get_records_sql($select.$sql.$sort.$limit)) !== false) {
d59269cf 1079
d59269cf 1080 foreach ($ausers as $auser) {
1081 $picture = print_user_picture($auser->id, $course->id, $auser->picture, false, true);
9bf660b3 1082
39e11905 1083 if (empty($auser->submissionid)) {
1084 $auser->grade = -1; //no submission yet
9bf660b3 1085 }
1086
d59269cf 1087 if (!empty($auser->submissionid)) {
9bf660b3 1088 ///Prints student answer and student modified date
1089 ///attach file or print link to student answer, depending on the type of the assignment.
1090 ///Refer to print_student_answer in inherited classes.
1091 if ($auser->timemodified > 0) {
1092 $studentmodified = '<div id="ts'.$auser->id.'">'.$this->print_student_answer($auser->id).userdate($auser->timemodified).'</div>';
d59269cf 1093 } else {
9437c854 1094 $studentmodified = '<div id="ts'.$auser->id.'">&nbsp;</div>';
d59269cf 1095 }
9bf660b3 1096 ///Print grade, dropdown or text
d59269cf 1097 if ($auser->timemarked > 0) {
1098 $teachermodified = '<div id="tt'.$auser->id.'">'.userdate($auser->timemarked).'</div>';
9bf660b3 1099
1100 if ($quickgrade) {
1101 $grade = '<div id="g'.$auser->id.'">'.choose_from_menu(make_grades_menu($this->assignment->grade),
082215e6 1102 'menu['.$auser->id.']', $auser->grade, get_string('nograde'),'',-1,true,false,$tabindex++).'</div>';
9bf660b3 1103 } else {
1104 $grade = '<div id="g'.$auser->id.'">'.$this->display_grade($auser->grade).'</div>';
1105 }
1106
b0f2597e 1107 } else {
9437c854 1108 $teachermodified = '<div id="tt'.$auser->id.'">&nbsp;</div>';
9bf660b3 1109 if ($quickgrade){
1110 $grade = '<div id="g'.$auser->id.'">'.choose_from_menu(make_grades_menu($this->assignment->grade),
082215e6 1111 'menu['.$auser->id.']', $auser->grade, get_string('nograde'),'',-1,true,false,$tabindex++).'</div>';
9bf660b3 1112 } else {
1113 $grade = '<div id="g'.$auser->id.'">'.$this->display_grade($auser->grade).'</div>';
1114 }
1115 }
1116 ///Print Comment
1117 if ($quickgrade){
1118 $comment = '<div id="com'.$auser->id.'"><textarea tabindex="'.$tabindex++.'" name="comment['.$auser->id.']" id="comment['.$auser->id.']">'.($auser->comment).'</textarea></div>';
1119 } else {
1120 $comment = '<div id="com'.$auser->id.'">'.shorten_text(strip_tags($auser->comment),15).'</div>';
b0f2597e 1121 }
1122 } else {
9437c854 1123 $studentmodified = '<div id="ts'.$auser->id.'">&nbsp;</div>';
1124 $teachermodified = '<div id="tt'.$auser->id.'">&nbsp;</div>';
9bf660b3 1125 $status = '<div id="st'.$auser->id.'">&nbsp;</div>';
1126 if ($quickgrade){ // allow editing
1127 $grade = '<div id="g'.$auser->id.'">'.choose_from_menu(make_grades_menu($this->assignment->grade),
082215e6 1128 'menu['.$auser->id.']', $auser->grade, get_string('nograde'),'',-1,true,false,$tabindex++).'</div>';
9bf660b3 1129 } else {
39e11905 1130 $grade = '<div id="g'.$auser->id.'">-</div>';
9bf660b3 1131 }
1132 if ($quickgrade){
1133 $comment = '<div id="com'.$auser->id.'"><textarea tabindex="'.$tabindex++.'" name="comment['.$auser->id.']" id="comment['.$auser->id.']">'.($auser->comment).'</textarea></div>';
1134 } else {
1135 $comment = '<div id="com'.$auser->id.'">&nbsp;</div>';
1136 }
b0f2597e 1137 }
9fa49e22 1138
0f7d4e5e 1139 if ($auser->status === NULL) {
1140 $auser->status = 0;
1141 }
1142
9437c854 1143 $buttontext = ($auser->status == 1) ? $strupdate : $strgrade;
9bf660b3 1144
1145 ///No more buttons, we use popups ;-).
9894b824 1146 $button = link_to_popup_window ('/mod/assignment/submissions.php?id='.$this->cm->id.'&amp;userid='.$auser->id.'&amp;mode=single'.'&amp;offset='.$offset++,
a8166227 1147 'grade'.$auser->id, $buttontext, 500, 780, $buttontext, 'none', true, 'button'.$auser->id);
0f7d4e5e 1148
9437c854 1149 $status = '<div id="up'.$auser->id.'" class="s'.$auser->status.'">'.$button.'</div>';
9bf660b3 1150
9437c854 1151 $row = array($picture, fullname($auser), $grade, $comment, $studentmodified, $teachermodified, $status);
d59269cf 1152 $table->add_data($row);
1153 }
b0f2597e 1154 }
9bf660b3 1155
082215e6 1156 /// Print quickgrade form around the table
1157 if ($quickgrade){
1158 echo '<form action="submissions.php" name="fastg" method="post">';
1159 echo '<input type="hidden" name="id" value="'.$this->cm->id.'">';
1160 echo '<input type="hidden" name="mode" value="fastgrade">';
16907e53 1161 echo '<input type="hidden" name="page" value="'.$page.'">';
9e0f5d2a 1162 echo '<p align="center"><input type="submit" name="fastg" value="'.get_string('saveallfeedback', 'assignment').'" /></p>';
082215e6 1163 }
1164
1165 $table->print_html(); /// Print the whole table
1166
9bf660b3 1167 if ($quickgrade){
1168 echo '<p align="center"><input type="submit" name="fastg" value="'.get_string('saveallfeedback', 'assignment').'" /></p>';
082215e6 1169 echo '</form>';
9bf660b3 1170 }
082215e6 1171 /// End of fast grading form
9bf660b3 1172
082215e6 1173 /// Mini form for setting user preference
9bf660b3 1174 echo '<br />';
1175 echo '<form name="options" action="submissions.php?id='.$this->cm->id.'" method="post">';
c9977d05 1176 echo '<input type="hidden" id="updatepref" name="updatepref" value="1" />';
9bf660b3 1177 echo '<table id="optiontable" align="center">';
1178 echo '<tr align="right"><td>';
1179 echo '<label for="perpage">'.get_string('pagesize','assignment').'</label>';
1180 echo ':</td>';
9bf660b3 1181 echo '<td align="left">';
1182 echo '<input type="text" id="perpage" name="perpage" size="1" value="'.$perpage.'" />';
1183 helpbutton('pagesize', get_string('pagesize','assignment'), 'assignment');
1184 echo '</td></tr>';
1185 echo '<tr align="right">';
1186 echo '<td>';
1187 print_string('quickgrade','assignment');
1188 echo ':</td>';
1189 echo '<td align="left">';
1190 if ($quickgrade){
1191 echo '<input type="checkbox" name="quickgrade" value="1" checked="checked" />';
1192 } else {
1193 echo '<input type="checkbox" name="quickgrade" value="1" />';
1194 }
1195 helpbutton('quickgrade', get_string('quickgrade', 'assignment'), 'assignment').'</p></div>';
1196 echo '</td></tr>';
1197 echo '<tr>';
1198 echo '<td colspan="2" align="right">';
1199 echo '<input type="submit" value="'.get_string('savepreferences').'" />';
1200 echo '</td></tr></table>';
1201 echo '</form>';
1202 ///End of mini form
b0f2597e 1203 print_footer($this->course);
8e340cb0 1204 }
d699cd1e 1205
7af1e882 1206 /**
1207 * Process teacher feedback submission
1208 *
1209 * This is called by submissions() when a grading even has taken place.
1210 * It gets its data from the submitted form.
1211 * @return object The updated submission object
b0f2597e 1212 */
1213 function process_feedback() {
d699cd1e 1214
b0f2597e 1215 global $USER;
d699cd1e 1216
9894b824 1217 if (!$feedback = data_submitted()) { // No incoming data?
b0f2597e 1218 return false;
d699cd1e 1219 }
b7b42874 1220
9bf660b3 1221 ///For save and next, we need to know the userid to save, and the userid to go
1222 ///We use a new hidden field in the form, and set it to -1. If it's set, we use this
1223 ///as the userid to store
1224 if ((int)$feedback->saveuserid !== -1){
1225 $feedback->userid = $feedback->saveuserid;
1226 }
1227
b0f2597e 1228 if (!empty($feedback->cancel)) { // User hit cancel button
1229 return false;
1230 }
d699cd1e 1231
7af1e882 1232 $submission = $this->get_submission($feedback->userid, true); // Get or make one
d699cd1e 1233
7af1e882 1234 $submission->grade = $feedback->grade;
1235 $submission->comment = $feedback->comment;
1236 $submission->format = $feedback->format;
1237 $submission->teacher = $USER->id;
1238 $submission->mailed = 0; // Make sure mail goes out (again, even)
1239 $submission->timemarked = time();
d699cd1e 1240
7af1e882 1241 unset($submission->data1); // Don't need to update this.
1242 unset($submission->data2); // Don't need to update this.
d4156e80 1243
b0f2597e 1244 if (empty($submission->timemodified)) { // eg for offline assignments
7af1e882 1245 $submission->timemodified = time();
b0f2597e 1246 }
d699cd1e 1247
7af1e882 1248 if (! update_record('assignment_submissions', $submission)) {
b0f2597e 1249 return false;
1250 }
d699cd1e 1251
b0f2597e 1252 add_to_log($this->course->id, 'assignment', 'update grades',
1253 'submissions.php?id='.$this->assignment->id.'&user='.$feedback->userid, $feedback->userid, $this->cm->id);
1254
7af1e882 1255 return $submission;
d699cd1e 1256
d699cd1e 1257 }
d699cd1e 1258
7af1e882 1259 /**
1260 * Load the submission object for a particular user
1261 *
1262 * @param $userid int The id of the user whose submission we want or 0 in which case USER->id is used
1263 * @param $createnew boolean optional Defaults to false. If set to true a new submission object will be created in the database
1264 * @return object The submission
1265 */
f77cfb73 1266 function get_submission($userid=0, $createnew=false) {
1267 global $USER;
1268
1269 if (empty($userid)) {
1270 $userid = $USER->id;
1271 }
1272
b0f2597e 1273 $submission = get_record('assignment_submissions', 'assignment', $this->assignment->id, 'userid', $userid);
d699cd1e 1274
b0f2597e 1275 if ($submission || !$createnew) {
1276 return $submission;
1277 }
39e11905 1278 $newsubmission = $this->prepare_new_submission($userid);
b0f2597e 1279 if (!insert_record("assignment_submissions", $newsubmission)) {
1280 error("Could not insert a new empty submission");
1281 }
d699cd1e 1282
b0f2597e 1283 return get_record('assignment_submissions', 'assignment', $this->assignment->id, 'userid', $userid);
1284 }
d699cd1e 1285
7af1e882 1286 /**
1287 * Instantiates a new submission object for a given user
1288 *
1289 * Sets the assignment, userid and times, everything else is set to default values.
1290 * @param $userid int The userid for which we want a submission object
1291 * @return object The submission
1292 */
39e11905 1293 function prepare_new_submission($userid) {
1294 $submission = new Object;
1295 $submission->assignment = $this->assignment->id;
1296 $submission->userid = $userid;
1297 $submission->timecreated = time();
1298 $submission->timemodified = $submission->timecreated;
1299 $submission->numfiles = 0;
1300 $submission->data1 = '';
1301 $submission->data2 = '';
1302 $submission->grade = -1;
1303 $submission->comment = '';
1304 $submission->format = 0;
1305 $submission->teacher = 0;
1306 $submission->timemarked = 0;
1307 $submission->mailed = 0;
1308 return $submission;
1309 }
1310
7af1e882 1311 /**
1312 * Return all assignment submissions by ENROLLED students (even empty)
1313 *
1314 * @param $sort string optional field names for the ORDER BY in the sql query
1315 * @param $dir string optional specifying the sort direction, defaults to DESC
1316 * @return array The submission objects indexed by id
1317 */
b0f2597e 1318 function get_submissions($sort='', $dir='DESC') {
7af1e882 1319 return assignment_get_all_submissions($this->assignment, $sort, $dir);
b0f2597e 1320 }
1321
7af1e882 1322 /**
1323 * Counts all real assignment submissions by ENROLLED students (not empty ones)
1324 *
1325 * @param $groupid int optional If nonzero then count is restricted to this group
1326 * @return int The number of submissions
1327 */
b0f2597e 1328 function count_real_submissions($groupid=0) {
7af1e882 1329 return assignment_count_real_submissions($this->assignment, $groupid);
d59269cf 1330 }
d699cd1e 1331
7af1e882 1332 /**
1333 * Alerts teachers by email of new or changed assignments that need grading
1334 *
1335 * First checks whether the option to email teachers is set for this assignment.
1336 * Sends an email to ALL teachers in the course (or in the group if using separate groups).
1337 * Uses the methods email_teachers_text() and email_teachers_html() to construct the content.
1338 * @param $submission object The submission that has changed
1339 */
73097f07 1340 function email_teachers($submission) {
73097f07 1341 global $CFG;
1342
d8199f1d 1343 if (empty($this->assignment->emailteachers)) { // No need to do anything
73097f07 1344 return;
1345 }
1346
1347 $user = get_record('user', 'id', $submission->userid);
1348
d8199f1d 1349 if (groupmode($this->course, $this->cm) == SEPARATEGROUPS) { // Separate groups are being used
1350 if (!$group = user_group($this->course->id, $user->id)) { // Try to find a group
73097f07 1351 $group->id = 0; // Not in a group, never mind
1352 }
d8199f1d 1353 $teachers = get_group_teachers($this->course->id, $group->id); // Works even if not in group
73097f07 1354 } else {
d8199f1d 1355 $teachers = get_course_teachers($this->course->id);
73097f07 1356 }
1357
1358 if ($teachers) {
1359
1360 $strassignments = get_string('modulenameplural', 'assignment');
1361 $strassignment = get_string('modulename', 'assignment');
1362 $strsubmitted = get_string('submitted', 'assignment');
1363
1364 foreach ($teachers as $teacher) {
1365 unset($info);
1366 $info->username = fullname($user);
d8199f1d 1367 $info->assignment = format_string($this->assignment->name,true);
1368 $info->url = $CFG->wwwroot.'/mod/assignment/submissions.php?id='.$this->cm->id;
1369
1370 $postsubject = $strsubmitted.': '.$info->username.' -> '.$this->assignment->name;
1371 $posttext = $this->email_teachers_text($info);
1372 $posthtml = ($teacher->mailformat == 1) ? $this->email_teachers_html($info) : '';
73097f07 1373
1374 @email_to_user($teacher, $user, $postsubject, $posttext, $posthtml); // If it fails, oh well, too bad.
1375 }
1376 }
1377 }
1378
7af1e882 1379 /**
1380 * Creates the text content for emails to teachers
1381 *
1382 * @param $info object The info used by the 'emailteachermail' language string
1383 * @return string
1384 */
d8199f1d 1385 function email_teachers_text($info) {
1386 $posttext = $this->course->shortname.' -> '.$this->strassignments.' -> '.
1387 format_string($this->assignment->name, true)."\n";
1388 $posttext .= '---------------------------------------------------------------------'."\n";
1389 $posttext .= get_string("emailteachermail", "assignment", $info)."\n";
1390 $posttext .= '---------------------------------------------------------------------'."\n";
1391 return $posttext;
1392 }
1393
7af1e882 1394 /**
1395 * Creates the html content for emails to teachers
1396 *
1397 * @param $info object The info used by the 'emailteachermailhtml' language string
1398 * @return string
1399 */
d8199f1d 1400 function email_teachers_html($info) {
3554b5c2 1401 global $CFG;
d8199f1d 1402 $posthtml = '<p><font face="sans-serif">'.
3554b5c2 1403 '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$this->course->id.'">'.$this->course->shortname.'</a> ->'.
d8199f1d 1404 '<a href="'.$CFG->wwwroot.'/mod/assignment/index.php?id='.$this->course->id.'">'.$this->strassignments.'</a> ->'.
3554b5c2 1405 '<a href="'.$CFG->wwwroot.'/mod/assignment/view.php?id='.$this->cm->id.'">'.format_string($this->assignment->name,true).'</a></font></p>';
d8199f1d 1406 $posthtml .= '<hr /><font face="sans-serif">';
1407 $posthtml .= '<p>'.get_string('emailteachermailhtml', 'assignment', $info).'</p>';
1408 $posthtml .= '</font><hr />';
815b5ca6 1409 return $posthtml;
d8199f1d 1410 }
1411
7af1e882 1412 /**
1413 * Produces a list of links to the files uploaded by a user
1414 *
1415 * @param $userid int optional id of the user. If 0 then $USER->id is used.
1416 * @param $return boolean optional defaults to false. If true the list is returned rather than printed
1417 * @return string optional
1418 */
d8199f1d 1419 function print_user_files($userid=0, $return=false) {
d8199f1d 1420 global $CFG, $USER;
9bf660b3 1421
d8199f1d 1422 if (!$userid) {
1423 if (!isloggedin()) {
1424 return '';
1425 }
1426 $userid = $USER->id;
1427 }
73097f07 1428
70b2c772 1429 $filearea = $this->file_area_name($userid);
73097f07 1430
1431 $output = '';
1432
70b2c772 1433 if ($basedir = $this->file_area($userid)) {
73097f07 1434 if ($files = get_directory_list($basedir)) {
1ef048ae 1435 require_once($CFG->libdir.'/filelib.php');
73097f07 1436 foreach ($files as $key => $file) {
9bf660b3 1437
73097f07 1438 $icon = mimeinfo('icon', $file);
9bf660b3 1439
73097f07 1440 if ($CFG->slasharguments) {
d4156e80 1441 $ffurl = "$CFG->wwwroot/file.php/$filearea/$file";
73097f07 1442 } else {
d4156e80 1443 $ffurl = "$CFG->wwwroot/file.php?file=/$filearea/$file";
73097f07 1444 }
9bf660b3 1445
1ef048ae 1446 $output .= '<img align="middle" src="'.$CFG->pixpath.'/f/'.$icon.'" height="16" width="16" alt="'.$icon.'" />'.
9bf660b3 1447 '<a href="'.$ffurl.'" >'.$file.'</a><br />';
73097f07 1448 }
1449 }
1450 }
1451
1452 $output = '<div class="files">'.$output.'</div>';
1453
1454 if ($return) {
1455 return $output;
1456 }
1457 echo $output;
1458 }
1459
7af1e882 1460 /**
1461 * Count the files uploaded by a given user
1462 *
1463 * @param $userid int The user id
1464 * @return int
1465 */
70b2c772 1466 function count_user_files($userid) {
1467 global $CFG;
1468
1469 $filearea = $this->file_area_name($userid);
1470
1471 if ($basedir = $this->file_area($userid)) {
1472 if ($files = get_directory_list($basedir)) {
1473 return count($files);
1474 }
1475 }
1476 return 0;
1477 }
73097f07 1478
7af1e882 1479 /**
1480 * Creates a directory file name, suitable for make_upload_directory()
1481 *
1482 * @param $userid int The user id
1483 * @return string path to file area
1484 */
70b2c772 1485 function file_area_name($userid) {
73097f07 1486 global $CFG;
1487
70b2c772 1488 return $this->course->id.'/'.$CFG->moddata.'/assignment/'.$this->assignment->id.'/'.$userid;
73097f07 1489 }
7af1e882 1490
1491 /**
1492 * Makes an upload directory
1493 *
1494 * @param $userid int The user id
1495 * @return string path to file area.
1496 */
70b2c772 1497 function file_area($userid) {
1498 return make_upload_directory( $this->file_area_name($userid) );
73097f07 1499 }
1500
7af1e882 1501 /**
1502 * Returns true if the student is allowed to submit
1503 *
1504 * Checks that the assignment has started and, if the option to prevent late
1505 * submissions is set, also checks that the assignment has not yet closed.
1506 * @return boolean
1507 */
f77cfb73 1508 function isopen() {
1509 $time = time();
1e4343a0 1510 if ($this->assignment->preventlate && $this->assignment->timedue) {
f77cfb73 1511 return ($this->assignment->timeavailable <= $time && $time <= $this->assignment->timedue);
1512 } else {
1513 return ($this->assignment->timeavailable <= $time);
1514 }
1515 }
1516
7af1e882 1517 /**
1518 * Return an outline of the user's interaction with the assignment
1519 *
1520 * The default method prints the grade and timemodified
1521 * @param $user object
1522 * @return object with properties ->info and ->time
1523 */
73097f07 1524 function user_outline($user) {
1525 if ($submission = $this->get_submission($user->id)) {
39e11905 1526
1527 $result->info = get_string('grade').': '.$this->display_grade($submission->grade);
73097f07 1528 $result->time = $submission->timemodified;
1529 return $result;
1530 }
1531 return NULL;
1532 }
7af1e882 1533
1534 /**
1535 * Print complete information about the user's interaction with the assignment
1536 *
1537 * @param $user object
1538 */
73097f07 1539 function user_complete($user) {
1540 if ($submission = $this->get_submission($user->id)) {
70b2c772 1541 if ($basedir = $this->file_area($user->id)) {
73097f07 1542 if ($files = get_directory_list($basedir)) {
1543 $countfiles = count($files)." ".get_string("uploadedfiles", "assignment");
1544 foreach ($files as $file) {
1545 $countfiles .= "; $file";
1546 }
1547 }
1548 }
1549
1550 print_simple_box_start();
1551 echo get_string("lastmodified").": ";
9bf660b3 1552 echo userdate($submission->timemodified);
1553 echo $this->display_lateness($submission->timemodified);
73097f07 1554
70b2c772 1555 $this->print_user_files($user->id);
73097f07 1556
1557 echo '<br />';
1558
1559 if (empty($submission->timemarked)) {
1560 print_string("notgradedyet", "assignment");
1561 } else {
1562 $this->view_feedback($submission);
1563 }
1564
1565 print_simple_box_end();
1566
1567 } else {
1568 print_string("notsubmittedyet", "assignment");
1569 }
1570 }
1571
7af1e882 1572 /**
1573 * Return a string indicating how late a submission is
1574 *
1575 * @param $timesubmitted int
1576 * @return string
1577 */
70b2c772 1578 function display_lateness($timesubmitted) {
76a60031 1579 return assignment_display_lateness($timesubmitted, $this->assignment->timedue);
73097f07 1580 }
1581
1582
b0f2597e 1583} ////// End of the assignment_base class
d699cd1e 1584
18b8fbfa 1585
04eba58f 1586
b0f2597e 1587/// OTHER STANDARD FUNCTIONS ////////////////////////////////////////////////////////
1588
7af1e882 1589/**
1590 * Deletes an assignment instance
1591 *
1592 * This is done by calling the delete_instance() method of the assignment type class
1593 */
b0f2597e 1594function assignment_delete_instance($id){
26b90e70 1595 global $CFG;
1596
b0f2597e 1597 if (! $assignment = get_record('assignment', 'id', $id)) {
1598 return false;
26b90e70 1599 }
1600
b0f2597e 1601 require_once("$CFG->dirroot/mod/assignment/type/$assignment->assignmenttype/assignment.class.php");
1602 $assignmentclass = "assignment_$assignment->assignmenttype";
1603 $ass = new $assignmentclass();
1604 return $ass->delete_instance($assignment);
1605}
f466c9ed 1606
ac21ad39 1607
7af1e882 1608/**
1609 * Updates an assignment instance
1610 *
1611 * This is done by calling the update_instance() method of the assignment type class
1612 */
b0f2597e 1613function assignment_update_instance($assignment){
1614 global $CFG;
26b90e70 1615
200c19fb 1616 $assignment->assignmenttype = clean_param($assignment->assignmenttype, PARAM_SAFEDIR);
1617
b0f2597e 1618 require_once("$CFG->dirroot/mod/assignment/type/$assignment->assignmenttype/assignment.class.php");
1619 $assignmentclass = "assignment_$assignment->assignmenttype";
1620 $ass = new $assignmentclass();
1621 return $ass->update_instance($assignment);
1622}
26b90e70 1623
26b90e70 1624
7af1e882 1625/**
1626 * Adds an assignment instance
1627 *
1628 * This is done by calling the add_instance() method of the assignment type class
1629 */
b0f2597e 1630function assignment_add_instance($assignment) {
1631 global $CFG;
f466c9ed 1632
200c19fb 1633 $assignment->assignmenttype = clean_param($assignment->assignmenttype, PARAM_SAFEDIR);
1634
b0f2597e 1635 require_once("$CFG->dirroot/mod/assignment/type/$assignment->assignmenttype/assignment.class.php");
1636 $assignmentclass = "assignment_$assignment->assignmenttype";
1637 $ass = new $assignmentclass();
1638 return $ass->add_instance($assignment);
1639}
f466c9ed 1640
73097f07 1641
7af1e882 1642/**
1643 * Returns an outline of a user interaction with an assignment
1644 *
1645 * This is done by calling the user_outline() method of the assignment type class
1646 */
73097f07 1647function assignment_user_outline($course, $user, $mod, $assignment) {
1648 global $CFG;
1649
1650 require_once("$CFG->dirroot/mod/assignment/type/$assignment->assignmenttype/assignment.class.php");
1651 $assignmentclass = "assignment_$assignment->assignmenttype";
1652 $ass = new $assignmentclass($mod->id, $assignment, $mod, $course);
1653 return $ass->user_outline($user);
1654}
1655
7af1e882 1656/**
1657 * Prints the complete info about a user's interaction with an assignment
1658 *
1659 * This is done by calling the user_complete() method of the assignment type class
1660 */
73097f07 1661function assignment_user_complete($course, $user, $mod, $assignment) {
1662 global $CFG;
1663
1664 require_once("$CFG->dirroot/mod/assignment/type/$assignment->assignmenttype/assignment.class.php");
1665 $assignmentclass = "assignment_$assignment->assignmenttype";
1666 $ass = new $assignmentclass($mod->id, $assignment, $mod, $course);
1667 return $ass->user_complete($user);
1668}
1669
7af1e882 1670/**
1671 * Function to be run periodically according to the moodle cron
1672 *
1673 * Finds all assignment notifications that have yet to be mailed out, and mails them
1674 */
73097f07 1675function assignment_cron () {
73097f07 1676
1677 global $CFG, $USER;
1678
1679 /// Notices older than 1 day will not be mailed. This is to avoid the problem where
1680 /// cron has not been running for a long time, and then suddenly people are flooded
1681 /// with mail from the past few weeks or months
1682
1683 $timenow = time();
1684 $endtime = $timenow - $CFG->maxeditingtime;
1685 $starttime = $endtime - 24 * 3600; /// One day earlier
1686
1687 if ($submissions = assignment_get_unmailed_submissions($starttime, $endtime)) {
1688
1689 foreach ($submissions as $key => $submission) {
1690 if (! set_field("assignment_submissions", "mailed", "1", "id", "$submission->id")) {
1691 echo "Could not update the mailed field for id $submission->id. Not mailed.\n";
1692 unset($submissions[$key]);
1693 }
1694 }
1695
1696 $timenow = time();
1697
1698 foreach ($submissions as $submission) {
1699
1700 echo "Processing assignment submission $submission->id\n";
1701
1702 if (! $user = get_record("user", "id", "$submission->userid")) {
1703 echo "Could not find user $post->userid\n";
1704 continue;
1705 }
1706
1707 $USER->lang = $user->lang;
1708
1709 if (! $course = get_record("course", "id", "$submission->course")) {
1710 echo "Could not find course $submission->course\n";
1711 continue;
1712 }
1713
1714 if (! isstudent($course->id, $user->id) and !isteacher($course->id, $user->id)) {
1715 echo fullname($user)." not an active participant in $course->shortname\n";
1716 continue;
1717 }
1718
1719 if (! $teacher = get_record("user", "id", "$submission->teacher")) {
1720 echo "Could not find teacher $submission->teacher\n";
1721 continue;
1722 }
1723
1724 if (! $mod = get_coursemodule_from_instance("assignment", $submission->assignment, $course->id)) {
1725 echo "Could not find course module for assignment id $submission->assignment\n";
1726 continue;
1727 }
1728
1729 if (! $mod->visible) { /// Hold mail notification for hidden assignments until later
1730 continue;
1731 }
1732
1733 $strassignments = get_string("modulenameplural", "assignment");
1734 $strassignment = get_string("modulename", "assignment");
1735
1736 unset($assignmentinfo);
1737 $assignmentinfo->teacher = fullname($teacher);
1738 $assignmentinfo->assignment = format_string($submission->name,true);
1739 $assignmentinfo->url = "$CFG->wwwroot/mod/assignment/view.php?id=$mod->id";
1740
1741 $postsubject = "$course->shortname: $strassignments: ".format_string($submission->name,true);
1742 $posttext = "$course->shortname -> $strassignments -> ".format_string($submission->name,true)."\n";
1743 $posttext .= "---------------------------------------------------------------------\n";
1744 $posttext .= get_string("assignmentmail", "assignment", $assignmentinfo);
1745 $posttext .= "---------------------------------------------------------------------\n";
1746
1747 if ($user->mailformat == 1) { // HTML
1748 $posthtml = "<p><font face=\"sans-serif\">".
1749 "<a href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> ->".
1750 "<a href=\"$CFG->wwwroot/mod/assignment/index.php?id=$course->id\">$strassignments</a> ->".
1751 "<a href=\"$CFG->wwwroot/mod/assignment/view.php?id=$mod->id\">".format_string($submission->name,true)."</a></font></p>";
1752 $posthtml .= "<hr /><font face=\"sans-serif\">";
1753 $posthtml .= "<p>".get_string("assignmentmailhtml", "assignment", $assignmentinfo)."</p>";
1754 $posthtml .= "</font><hr />";
1755 } else {
1756 $posthtml = "";
1757 }
1758
1759 if (! email_to_user($user, $teacher, $postsubject, $posttext, $posthtml)) {
1760 echo "Error: assignment cron: Could not send out mail for id $submission->id to user $user->id ($user->email)\n";
1761 }
1762 }
1763 }
1764
1765 return true;
1766}
1767
7af1e882 1768/**
1769 * Return an array of grades, indexed by user, and a max grade.
1770 *
1771 * @param $assignmentid int
1772 * @return object with properties ->grades (an array of grades) and ->maxgrade.
1773 */
73097f07 1774function assignment_grades($assignmentid) {
73097f07 1775
082215e6 1776 if (!$assignment = get_record('assignment', 'id', $assignmentid)) {
73097f07 1777 return NULL;
1778 }
3262ee10 1779 if ($assignment->grade == 0) { // No grading
1780 return NULL;
1781 }
73097f07 1782
082215e6 1783 $grades = get_records_menu('assignment_submissions', 'assignment',
1784 $assignment->id, '', 'userid,grade');
73097f07 1785
3262ee10 1786 if ($assignment->grade > 0) {
fa22fd5f 1787 if ($grades) {
1788 foreach ($grades as $userid => $grade) {
1789 if ($grade == -1) {
1790 $grades[$userid] = '-';
1791 }
082215e6 1792 }
1793 }
73097f07 1794 $return->grades = $grades;
1795 $return->maxgrade = $assignment->grade;
1796
1797 } else { // Scale
1798 if ($grades) {
1799 $scaleid = - ($assignment->grade);
1800 if ($scale = get_record('scale', 'id', $scaleid)) {
1801 $scalegrades = make_menu_from_list($scale->scale);
082215e6 1802 foreach ($grades as $userid => $grade) {
de4564ee 1803 if (empty($scalegrades[$grade])) {
39e11905 1804 $grades[$userid] = '-';
0a3f66a2 1805 } else {
082215e6 1806 $grades[$userid] = $scalegrades[$grade];
0a3f66a2 1807 }
73097f07 1808 }
1809 }
1810 }
1811 $return->grades = $grades;
1812 $return->maxgrade = "";
1813 }
1814
1815 return $return;
1816}
1817
7af1e882 1818/**
1819 * Returns the users with data in one assignment (students and teachers)
1820 *
1821 * @param $assignmentid int
1822 * @return array of user objects
1823 */
73097f07 1824function assignment_get_participants($assignmentid) {
73097f07 1825
1826 global $CFG;
1827
1828 //Get students
1829 $students = get_records_sql("SELECT DISTINCT u.id, u.id
1830 FROM {$CFG->prefix}user u,
1831 {$CFG->prefix}assignment_submissions a
1832 WHERE a.assignment = '$assignmentid' and
1833 u.id = a.userid");
1834 //Get teachers
1835 $teachers = get_records_sql("SELECT DISTINCT u.id, u.id
1836 FROM {$CFG->prefix}user u,
1837 {$CFG->prefix}assignment_submissions a
1838 WHERE a.assignment = '$assignmentid' and
1839 u.id = a.teacher");
1840
1841 //Add teachers to students
1842 if ($teachers) {
1843 foreach ($teachers as $teacher) {
1844 $students[$teacher->id] = $teacher;
1845 }
1846 }
1847 //Return students array (it contains an array of unique users)
1848 return ($students);
1849}
1850
7af1e882 1851/**
1852 * Checks if a scale is being used by an assignment
1853 *
1854 * This is used by the backup code to decide whether to back up a scale
1855 * @param $assignmentid int
1856 * @param $scaleid int
1857 * @return boolean True if the scale is used by the assignment
1858 */
1859function assignment_scale_used ($assignmentid, $scaleid) {
73097f07 1860
1861 $return = false;
1862
1863 $rec = get_record('assignment','id',$assignmentid,'grade',-$scaleid);
1864
1865 if (!empty($rec) && !empty($scaleid)) {
1866 $return = true;
1867 }
1868
1869 return $return;
1870}
1871
7af1e882 1872/**
1873 * Make sure up-to-date events are created for all assignment instances
1874 *
1875 * This standard function will check all instances of this module
1876 * and make sure there are up-to-date events created for each of them.
1877 * If courseid = 0, then every assignment event in the site is checked, else
1878 * only assignment events belonging to the course specified are checked.
1879 * This function is used, in its new format, by restore_refresh_events()
1880 *
1881 * @param $courseid int optional If zero then all assignments for all courses are covered
1882 * @return boolean Always returns true
1883 */
73097f07 1884function assignment_refresh_events($courseid = 0) {
73097f07 1885
1886 if ($courseid == 0) {
1887 if (! $assignments = get_records("assignment")) {
1888 return true;
1889 }
1890 } else {
1891 if (! $assignments = get_records("assignment", "course", $courseid)) {
1892 return true;
1893 }
1894 }
1895 $moduleid = get_field('modules', 'id', 'name', 'assignment');
1896
1897 foreach ($assignments as $assignment) {
1898 $event = NULL;
1899 $event->name = addslashes($assignment->name);
1900 $event->description = addslashes($assignment->description);
1901 $event->timestart = $assignment->timedue;
1902
1903 if ($event->id = get_field('event', 'id', 'modulename', 'assignment', 'instance', $assignment->id)) {
1904 update_event($event);
1905
1906 } else {
1907 $event->courseid = $assignment->course;
1908 $event->groupid = 0;
1909 $event->userid = 0;
1910 $event->modulename = 'assignment';
1911 $event->instance = $assignment->id;
1912 $event->eventtype = 'due';
1913 $event->timeduration = 0;
1914 $event->visible = get_field('course_modules', 'visible', 'module', $moduleid, 'instance', $assignment->id);
1915 add_event($event);
1916 }
1917
1918 }
1919 return true;
1920}
1921
7af1e882 1922/**
1923 * Print recent activity from all assignments in a given course
1924 *
1925 * This is used by the recent activity block
1926 */
73097f07 1927function assignment_print_recent_activity($course, $isteacher, $timestart) {
1928 global $CFG;
1929
1930 $content = false;
1931 $assignments = NULL;
1932
1933 if (!$logs = get_records_select('log', 'time > \''.$timestart.'\' AND '.
1934 'course = \''.$course->id.'\' AND '.
1935 'module = \'assignment\' AND '.
1936 'action = \'upload\' ', 'time ASC')) {
1937 return false;
1938 }
1939
1940 foreach ($logs as $log) {
1941 //Create a temp valid module structure (course,id)
1942 $tempmod->course = $log->course;
1943 $tempmod->id = $log->info;
1944 //Obtain the visible property from the instance
1945 $modvisible = instance_is_visible($log->module,$tempmod);
1946
1947 //Only if the mod is visible
1948 if ($modvisible) {
1949 $assignments[$log->info] = assignment_log_info($log);
1950 $assignments[$log->info]->time = $log->time;
1951 $assignments[$log->info]->url = str_replace('&', '&amp;', $log->url);
1952 }
1953 }
1954
1955 if ($assignments) {
1956 print_headline(get_string('newsubmissions', 'assignment').':');
1957 foreach ($assignments as $assignment) {
1958 print_recent_activity_note($assignment->time, $assignment, $isteacher, $assignment->name,
1959 $CFG->wwwroot.'/mod/assignment/'.$assignment->url);
1960 }
1961 $content = true;
1962 }
1963
1964 return $content;
1965}
1966
1967
7af1e882 1968/**
1969 * Returns all assignments since a given time.
1970 *
1971 * If assignment is specified then this restricts the results
1972 */
73097f07 1973function assignment_get_recent_mod_activity(&$activities, &$index, $sincetime, $courseid, $assignment="0", $user="", $groupid="") {
73097f07 1974
1975 global $CFG;
1976
1977 if ($assignment) {
1978 $assignmentselect = " AND cm.id = '$assignment'";
1979 } else {
1980 $assignmentselect = "";
1981 }
1982 if ($user) {
1983 $userselect = " AND u.id = '$user'";
1984 } else {
1985 $userselect = "";
1986 }
1987
1988 $assignments = get_records_sql("SELECT asub.*, u.firstname, u.lastname, u.picture, u.id as userid,
1989 a.grade as maxgrade, name, cm.instance, cm.section, a.assignmenttype
1990 FROM {$CFG->prefix}assignment_submissions asub,
1991 {$CFG->prefix}user u,
1992 {$CFG->prefix}assignment a,
1993 {$CFG->prefix}course_modules cm
1994 WHERE asub.timemodified > '$sincetime'
1995 AND asub.userid = u.id $userselect
1996 AND a.id = asub.assignment $assignmentselect
1997 AND cm.course = '$courseid'
1998 AND cm.instance = a.id
1999 ORDER BY asub.timemodified ASC");
2000
2001 if (empty($assignments))
2002 return;
2003
2004 foreach ($assignments as $assignment) {
2005 if (empty($groupid) || ismember($groupid, $assignment->userid)) {
2006
2007 $tmpactivity = new Object;
2008
2009 $tmpactivity->type = "assignment";
2010 $tmpactivity->defaultindex = $index;
2011 $tmpactivity->instance = $assignment->instance;
2012 $tmpactivity->name = $assignment->name;
2013 $tmpactivity->section = $assignment->section;
2014
2015 $tmpactivity->content->grade = $assignment->grade;
2016 $tmpactivity->content->maxgrade = $assignment->maxgrade;
2017 $tmpactivity->content->type = $assignment->assignmenttype;
2018
2019 $tmpactivity->user->userid = $assignment->userid;
2020 $tmpactivity->user->fullname = fullname($assignment);
2021 $tmpactivity->user->picture = $assignment->picture;
2022
2023 $tmpactivity->timestamp = $assignment->timemodified;
2024
2025 $activities[] = $tmpactivity;
2026
2027 $index++;
2028 }
2029 }
2030
2031 return;
2032}
2033
7af1e882 2034/**
2035 * Print recent activity from all assignments in a given course
2036 *
2037 * This is used by course/recent.php
2038 */
73097f07 2039function assignment_print_recent_mod_activity($activity, $course, $detail=false) {
2040 global $CFG;
2041
2042 echo '<table border="0" cellpadding="3" cellspacing="0">';
2043
2044 echo "<tr><td class=\"userpicture\" width=\"35\" valign=\"top\">";
2045 print_user_picture($activity->user->userid, $course, $activity->user->picture);
2046 echo "</td><td width=\"100%\"><font size=2>";
2047
2048 if ($detail) {
2049 echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
2050 "height=16 width=16 alt=\"$activity->type\"> ";
2051 echo "<a href=\"$CFG->wwwroot/mod/assignment/view.php?id=" . $activity->instance . "\">"
2052 . format_string($activity->name,true) . "</a> - ";
2053
2054 }
2055
2056 if (isteacher($course)) {
2057 $grades = "(" . $activity->content->grade . " / " . $activity->content->maxgrade . ") ";
2058
2059 $assignment->id = $activity->instance;
2060 $assignment->course = $course;
2061 $user->id = $activity->user->userid;
2062
2063 echo $grades;
2064 echo "<br />";
2065 }
2066 echo "<a href=\"$CFG->wwwroot/user/view.php?id="
2067 . $activity->user->userid . "&amp;course=$course\">"
2068 . $activity->user->fullname . "</a> ";
2069
2070 echo " - " . userdate($activity->timestamp);
2071
2072 echo "</font></td></tr>";
2073 echo "</table>";
73097f07 2074}
2075
2076/// GENERIC SQL FUNCTIONS
2077
7af1e882 2078/**
2079 * Fetch info from logs
2080 *
2081 * @param $log object with properties ->info (the assignment id) and ->userid
2082 * @return array with assignment name and user firstname and lastname
2083 */
73097f07 2084function assignment_log_info($log) {
2085 global $CFG;
2086 return get_record_sql("SELECT a.name, u.firstname, u.lastname
2087 FROM {$CFG->prefix}assignment a,
2088 {$CFG->prefix}user u
2089 WHERE a.id = '$log->info'
2090 AND u.id = '$log->userid'");
2091}
2092
7af1e882 2093/**
2094 * Return list of marked submissions that have not been mailed out for currently enrolled students
2095 *
2096 * @return array
2097 */
73097f07 2098function assignment_get_unmailed_submissions($starttime, $endtime) {
7af1e882 2099
73097f07 2100 global $CFG;
2101 return get_records_sql("SELECT s.*, a.course, a.name
2102 FROM {$CFG->prefix}assignment_submissions s,
2103 {$CFG->prefix}assignment a,
2104 {$CFG->prefix}user_students us
2105 WHERE s.mailed = 0
2106 AND s.timemarked <= $endtime
2107 AND s.timemarked >= $starttime
2108 AND s.assignment = a.id
2109 AND s.userid = us.userid
2110 AND a.course = us.course");
2111}
2112
7af1e882 2113/**
2114 * Counts all real assignment submissions by ENROLLED students (not empty ones)
2115 *
2116 * There are also assignment type methods count_real_submissions() wich in the default
2117 * implementation simply call this function.
2118 * @param $groupid int optional If nonzero then count is restricted to this group
2119 * @return int The number of submissions
2120 */
73097f07 2121function assignment_count_real_submissions($assignment, $groupid=0) {
73097f07 2122 global $CFG;
2123
2124 if ($groupid) { /// How many in a particular group?
2125 return count_records_sql("SELECT COUNT(DISTINCT g.userid, g.groupid)
2126 FROM {$CFG->prefix}assignment_submissions a,
2127 {$CFG->prefix}groups_members g
2128 WHERE a.assignment = $assignment->id
2129 AND a.timemodified > 0
2130 AND g.groupid = '$groupid'
2131 AND a.userid = g.userid ");
2132 } else {
2133 $select = "s.course = '$assignment->course' AND";
2134 if ($assignment->course == SITEID) {
2135 $select = '';
2136 }
2137 return count_records_sql("SELECT COUNT(*)
2138 FROM {$CFG->prefix}assignment_submissions a,
2139 {$CFG->prefix}user_students s
2140 WHERE a.assignment = '$assignment->id'
2141 AND a.timemodified > 0
2142 AND $select a.userid = s.userid ");
2143 }
2144}
2145
7af1e882 2146
2147/**
2148 * Return all assignment submissions by ENROLLED students (even empty)
2149 *
2150 * There are also assignment type methods get_submissions() wich in the default
2151 * implementation simply call this function.
2152 * @param $sort string optional field names for the ORDER BY in the sql query
2153 * @param $dir string optional specifying the sort direction, defaults to DESC
2154 * @return array The submission objects indexed by id
2155 */
73097f07 2156function assignment_get_all_submissions($assignment, $sort="", $dir="DESC") {
2157/// Return all assignment submissions by ENROLLED students (even empty)
2158 global $CFG;
2159
2160 if ($sort == "lastname" or $sort == "firstname") {
2161 $sort = "u.$sort $dir";
2162 } else if (empty($sort)) {
2163 $sort = "a.timemodified DESC";
2164 } else {
2165 $sort = "a.$sort $dir";
2166 }
2167
2168 $select = "s.course = '$assignment->course' AND";
2169 if ($assignment->course == SITEID) {
2170 $select = '';
2171 }
2172 return get_records_sql("SELECT a.*
2173 FROM {$CFG->prefix}assignment_submissions a,
2174 {$CFG->prefix}user_students s,
2175 {$CFG->prefix}user u
2176 WHERE a.userid = s.userid
2177 AND u.id = a.userid
2178 AND $select a.assignment = '$assignment->id'
2179 ORDER BY $sort");
2180}
2181
2182
2183
2184
2185/// OTHER GENERAL FUNCTIONS FOR ASSIGNMENTS ///////////////////////////////////////
2186
7af1e882 2187/**
2188 * Returns an array of installed assignment types indexed and sorted by name
2189 *
2190 * @return array The index is the name of the assignment type, the value its full name from the language strings
2191 */
b0f2597e 2192function assignment_types() {
2193 $types = array();
2194 $names = get_list_of_plugins('mod/assignment/type');
2195 foreach ($names as $name) {
2196 $types[$name] = get_string('type'.$name, 'assignment');
ffeca120 2197 }
b0f2597e 2198 asort($types);
2199 return $types;
f466c9ed 2200}
2201
7af1e882 2202/**
2203 * Executes upgrade scripts for assignment types when necessary
2204 */
b0f2597e 2205function assignment_upgrade_submodules() {
f1c1d2f1 2206 global $CFG;
26b90e70 2207
b0f2597e 2208 $types = assignment_types();
26b90e70 2209
d175966b 2210 include($CFG->dirroot.'/mod/assignment/version.php'); // defines $module with version etc
26b90e70 2211
d175966b 2212 foreach ($types as $type => $typename) {
26b90e70 2213
b0f2597e 2214 $fullpath = $CFG->dirroot.'/mod/assignment/type/'.$type;
26b90e70 2215
b0f2597e 2216 /// Check for an external version file (defines $submodule)
26b90e70 2217
b0f2597e 2218 if (!is_readable($fullpath .'/version.php')) {
2219 continue;
ffeca120 2220 }
b0f2597e 2221 include_once($fullpath .'/version.php');
26b90e70 2222
b0f2597e 2223 /// Check whether we need to upgrade
26b90e70 2224
b0f2597e 2225 if (!isset($submodule->version)) {
2226 continue;
2227 }
26b90e70 2228
b0f2597e 2229 /// Make sure this submodule will work with this assignment version
26b90e70 2230
d175966b 2231 if (isset($submodule->requires) and ($submodule->requires > $module->version)) {
b0f2597e 2232 notify("Assignment submodule '$type' is too new for your assignment");
2233 continue;
2234 }
f466c9ed 2235
a86a538f 2236 /// If the submodule is new, then let's install it!
f466c9ed 2237
b0f2597e 2238 $currentversion = 'assignment_'.$type.'_version';
f466c9ed 2239
a86a538f 2240 if (!isset($CFG->$currentversion)) { // First install!
2241 set_config($currentversion, $submodule->version); // Must keep track of version
2242
2243 if (!is_readable($fullpath .'/db/'.$CFG->dbtype.'.sql')) {
2244 continue;
2245 }
2246
2247 $db->debug=true;
2248 if (!modify_database($fullpath .'/db/'.$CFG->dbtype.'.sql')) {
2249 notify("Error installing tables for submodule '$type'!");
2250 }
2251 $db->debug=false;
2252 continue;
f466c9ed 2253 }
f466c9ed 2254
b0f2597e 2255 /// See if we need to upgrade
2256
2257 if ($submodule->version <= $CFG->$currentversion) {
2258 continue;
59c005b7 2259 }
59c005b7 2260
b0f2597e 2261 /// Look for the upgrade file
59c005b7 2262
b0f2597e 2263 if (!is_readable($fullpath .'/db/'.$CFG->dbtype.'.php')) {
2264 continue;
2265 }
59c005b7 2266
b0f2597e 2267 include_once($fullpath .'/db/'. $CFG->dbtype .'.php'); // defines assignment_xxx_upgrade
59c005b7 2268
b0f2597e 2269 /// Perform the upgrade
59c005b7 2270
b0f2597e 2271 $upgrade_function = 'assignment_'.$type.'_upgrade';
2272 if (function_exists($upgrade_function)) {
2273 $db->debug=true;
2274 if ($upgrade_function($CFG->$currentversion)) {
2275 $db->debug=false;
2276 set_config($currentversion, $submodule->version);
59c005b7 2277 }
b0f2597e 2278 $db->debug=false;
59c005b7 2279 }
2280 }
2281}
2282
84fa8f5f 2283function assignment_print_overview($courses, &$htmlarray) {
2284 global $USER, $CFG;
2285
2286 if (empty($courses) || !is_array($courses) || count($courses) == 0) {
2287 return array();
2288 }
2289
2290 if (!$assignments = get_all_instances_in_courses('assignment',$courses)) {
2291 return;
2292 }
2293
2294 // Do assignment_base::isopen() here without loading the whole thing for speed
2295 foreach ($assignments as $key => $assignment) {
2296 $time = time();
2297 if ($assignment->preventlate && $assignment->timedue) {
2298 $isopen = ($assignment->timeavailable <= $time && $time <= $assignment->timedue);
2299 } else {
2300 $isopen = ($assignment->timeavailable <= $time);
2301 }
2302 if (!$isopen) {
2303 unset($assignments[$key]);
2304 }
2305 }
2306
2307 $strduedate = get_string('duedate', 'assignment');
2308 $strgraded = get_string('graded', 'assignment');
2309 $strnotgradedyet = get_string('notgradedyet', 'assignment');
2310 $strnotsubmittedyet = get_string('notsubmittedyet', 'assignment');
2311 $strsubmitted = get_string('submitted', 'assignment');
76a60031 2312 $strassignment = get_string('modulename', 'assignment');
84fa8f5f 2313
2314 foreach ($assignments as $assignment) {
a2a37336 2315 $str = '<div class="assignment overview"><div class="name">'.$strassignment. ': '.
a7a74d77 2316 '<a '.($assignment->visible ? '':' class="dimmed"').
76a60031 2317 'title="'.$strassignment.'" href="'.$CFG->wwwroot.
a7a74d77 2318 '/mod/assignment/view.php?id='.$assignment->coursemodule.'">'.
5d45c04f 2319 $assignment->name.'</a></div>';
2320 $str .= '<div class="info">'.$strduedate.': '.userdate($assignment->timedue).'</div>';
76a60031 2321
2322 if (isteacher($assignment->course)) {
2323 $submissions = count_records_sql("SELECT COUNT(*)
84fa8f5f 2324 FROM {$CFG->prefix}assignment_submissions a,
2325 {$CFG->prefix}user_students s,
2326 {$CFG->prefix}user u
2327 WHERE a.userid = s.userid
2328 AND u.id = a.userid
76a60031 2329 AND s.course = '{$assignment->course}'
2330 AND a.assignment = '{$assignment->id}'
84fa8f5f 2331 AND a.teacher = 0
2332 AND a.timemarked = 0");
2333 if ($submissions) {
2334 $str .= get_string('submissionsnotgraded', 'assignment', $submissions);
2335 }
2336 } else {
2337 $sql = "SELECT *
2338 FROM {$CFG->prefix}assignment_submissions
2339 WHERE userid = '$USER->id'";
2340 if ($submission = get_record_sql($sql)) {
2341 if ($submission->teacher == 0 && $submission->timemarked == 0) {
2342 $str .= $strsubmitted . ', ' . $strnotgradedyet;
2343 } else {
2344 $str .= $strsubmitted . ', ' . $strgraded;
2345 }
2346 } else {
76a60031 2347 $str .= $strnotsubmittedyet . ' ' . assignment_display_lateness(time(), $assignment->timedue);
84fa8f5f 2348 }
2349 }
a7a74d77 2350 $str .= '</div>';
76a60031 2351 if (empty($htmlarray[$assignment->course]['assignment'])) {
2352 $htmlarray[$assignment->course]['assignment'] = $str;
2353 } else {
2354 $htmlarray[$assignment->course]['assignment'] .= $str;
2355 }
2356 }
2357}
2358
2359function assignment_display_lateness($timesubmitted, $timedue) {
2360 if (!$timedue) {
2361 return '';
2362 }
2363 $time = $timedue - $timesubmitted;
2364 if ($time < 0) {
2365 $timetext = get_string('late', 'assignment', format_time($time));
2366 return ' (<span class="late">'.$timetext.'</span>)';
2367 } else {
2368 $timetext = get_string('early', 'assignment', format_time($time));
2369 return ' (<span class="early">'.$timetext.'</span>)';
84fa8f5f 2370 }
2371}
2372
76a60031 2373?>