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