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