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