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