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