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