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