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