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