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