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