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