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