Updated the HEAD build version to 20100212
[moodle.git] / grade / report / user / lib.php
CommitLineData
e060e33d 1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
8ad36f4c 17
f7998fbc 18/**
19 * File in which the user_report class is defined.
20 * @package gradebook
21 */
22
23require_once($CFG->dirroot . '/grade/report/lib.php');
24require_once($CFG->libdir.'/tablelib.php');
25
a83507f2
AD
26//showhiddenitems values
27define("GRADE_REPORT_USER_HIDE_HIDDEN", 0);
28define("GRADE_REPORT_USER_HIDE_UNTIL", 1);
29define("GRADE_REPORT_USER_SHOW_HIDDEN", 2);
30
f7998fbc 31/**
32 * Class providing an API for the user report building and displaying.
33 * @uses grade_report
34 * @package gradebook
35 */
36class grade_report_user extends grade_report {
37
38 /**
39 * The user.
40 * @var object $user
41 */
5c75a0a3 42 public $user;
f7998fbc 43
44 /**
45 * A flexitable to hold the data.
46 * @var object $table
47 */
5c75a0a3 48 public $table;
f7998fbc 49
653a8648 50 var $gtree;
51
e0724506 52 /**
53 * Flat structure similar to grade tree
54 */
5c75a0a3 55 public $gseq;
e0724506 56
26ed0305 57 /**
58 * show student ranks
59 */
5c75a0a3 60 public $showrank;
26ed0305 61
699da6b5 62 /**
63 * show grade percentages
64 */
65 public $showpercentage;
66
5528217e 67 /**
68 * Show range
69 */
70 var $showrange;
71
72 var $tableheaders;
73 var $tablecolumns;
74
75 var $maxdepth;
76 var $evenodd;
77
78 var $tabledata;
79 var $canviewhidden;
80
81 var $switch;
82
26ed0305 83 /**
84 * Show hidden items even when user does not have required cap
85 */
5c75a0a3 86 public $showhiddenitems;
26ed0305 87
f7998fbc 88 /**
89 * Constructor. Sets local copies of user preferences and initialises grade_tree.
90 * @param int $courseid
d30c4481 91 * @param object $gpr grade plugin return tracking object
f7998fbc 92 * @param string $context
93 * @param int $userid The id of the user
94 */
5c75a0a3 95 public function __construct($courseid, $gpr, $context, $userid) {
96 global $CFG, $DB;
97 parent::__construct($courseid, $gpr, $context);
f7998fbc 98
597f50e6 99 $this->showrank = grade_get_setting($this->courseid, 'report_user_showrank', $CFG->grade_report_user_showrank);
699da6b5 100 $this->showpercentage = grade_get_setting($this->courseid, 'report_user_showpercentage', $CFG->grade_report_user_showpercentage);
597f50e6 101 $this->showhiddenitems = grade_get_setting($this->courseid, 'report_user_showhiddenitems', $CFG->grade_report_user_showhiddenitems);
61541a5a 102 $this->showtotalsifcontainhidden = grade_get_setting($this->courseid, 'report_user_showtotalsifcontainhidden', $CFG->grade_report_user_showtotalsifcontainhidden);
653a8648 103
5528217e 104 $this->showrange = true;
105
106 $this->switch = grade_get_setting($this->courseid, 'aggregationposition', $CFG->grade_aggregationposition);
107
108 // Grab the grade_tree for this course
ec3f2041 109 $this->gtree = new grade_tree($this->courseid, false, $this->switch, false, !$CFG->enableoutcomes);
26ed0305 110
5528217e 111 // Determine the number of rows and indentation
112 $this->maxdepth = 1;
113 $this->inject_rowspans($this->gtree->top_element);
114 $this->maxdepth++; // Need to account for the lead column that spans all children
115 for ($i = 1; $i <= $this->maxdepth; $i++) {
116 $this->evenodd[$i] = 0;
117 }
118
119 $this->tabledata = array();
e0724506 120
5528217e 121 $this->canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->courseid));
4faf5f99 122
f7998fbc 123 // get the user (for full name)
5c75a0a3 124 $this->user = $DB->get_record('user', array('id' => $userid));
f7998fbc 125
126 // base url for sorting by first/last name
127 $this->baseurl = $CFG->wwwroot.'/grade/report?id='.$courseid.'&amp;userid='.$userid;
90d3960c 128 $this->pbarurl = $this->baseurl;
f7998fbc 129
d1214909 130 // no groups on this report - rank is from all course users
90d3960c 131 $this->setup_table();
f7998fbc 132 }
133
5528217e 134 function inject_rowspans(&$element) {
135 if ($element['depth'] > $this->maxdepth) {
136 $this->maxdepth = $element['depth'];
137 }
138 if (empty($element['children'])) {
139 return 1;
140 }
141 $count = 1;
142 foreach ($element['children'] as $key=>$child) {
143 $count += $this->inject_rowspans($element['children'][$key]);
144 }
145 $element['rowspan'] = $count;
146 return $count;
147 }
148
149
f7998fbc 150 /**
151 * Prepares the headers and attributes of the flexitable.
152 */
5c75a0a3 153 public function setup_table() {
02973505 154 global $CFG;
f7998fbc 155 /*
d1214909 156 * Table has 5-6 columns
157 *| itemname/description | final grade | percentage final grade | rank (optional) | feedback |
158 */
f7998fbc 159
160 // setting up table headers
5528217e 161 $this->tablecolumns = array('itemname', 'grade');
162 $this->tableheaders = array($this->get_lang_string('gradeitem', 'grades'),
699da6b5 163 $this->get_lang_string('grade'));
164
5528217e 165 if ($this->showrange) {
166 $this->tablecolumns[] = 'range';
167 $this->tableheaders[] = $this->get_lang_string('range', 'grades');
168 }
169
699da6b5 170 if ($this->showpercentage) {
5528217e 171 $this->tablecolumns[] = 'percentage';
172 $this->tableheaders[] = $this->get_lang_string('percentage', 'grades');
699da6b5 173 }
174
26ed0305 175 if ($this->showrank) {
02973505 176 // TODO: this is broken if hidden grades present!!
5528217e 177 $this->tablecolumns[] = 'rank';
178 $this->tableheaders[] = $this->get_lang_string('rank', 'grades');
02973505 179 }
f7998fbc 180
5528217e 181 $this->tablecolumns[] = 'feedback';
182 $this->tableheaders[] = $this->get_lang_string('feedback', 'grades');
f7998fbc 183
5528217e 184 }
f7998fbc 185
5528217e 186 function fill_table() {
187 //print "<pre>";
188 //print_r($this->gtree->top_element);
189 $this->fill_table_recursive($this->gtree->top_element);
190 //print_r($this->tabledata);
191 //print "</pre>";
192 return true;
f7998fbc 193 }
194
5528217e 195 private function fill_table_recursive(&$element) {
bb776ef4 196 global $CFG, $DB;
f7998fbc 197
5528217e 198 $type = $element['type'];
199 $depth = $element['depth'];
200 $grade_object = $element['object'];
201 $eid = $grade_object->id;
202 $fullname = $this->gtree->get_element_header($element, true, true, true);
203 $data = array();
204 $hidden = '';
205 $excluded = '';
206 $class = '';
f7998fbc 207
a83507f2 208 // If this is a hidden grade category, hide it completely from the user
9e322c8c 209 if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && (
a83507f2
AD
210 $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN ||
211 ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil()))) {
653a8648 212 return false;
213 }
6391ebe7 214
5528217e 215 if ($type == 'category') {
216 $this->evenodd[$depth] = (($this->evenodd[$depth] + 1) % 2);
b89a70ce 217 }
5528217e 218 $alter = ($this->evenodd[$depth] == 0) ? 'even' : 'odd';
6391ebe7 219
5528217e 220 /// Process those items that have scores associated
221 if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') {
222 if (! $grade_grade = grade_grade::fetch(array('itemid'=>$grade_object->id,'userid'=>$this->user->id))) {
223 $grade_grade = new grade_grade();
224 $grade_grade->userid = $this->user->id;
225 $grade_grade->itemid = $grade_object->id;
d297269d 226 }
6391ebe7 227
5528217e 228 $grade_grade->load_grade_item();
f7998fbc 229
5528217e 230 /// Hidden Items
231 if ($grade_grade->grade_item->is_hidden()) {
232 $hidden = ' hidden';
ced5ee59 233 }
234
a83507f2 235 // If this is a hidden grade item, hide it completely from the user.
653a8648 236 if ($grade_grade->is_hidden() && !$this->canviewhidden && (
a83507f2
AD
237 $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN ||
238 ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil()))) {
653a8648 239 // return false;
240 } else {
241
5528217e 242 /// Excluded Item
243 if ($grade_grade->is_excluded()) {
244 $fullname .= ' ['.get_string('excluded', 'grades').']';
245 $excluded = ' excluded';
ced5ee59 246 }
4c8d9481 247
5528217e 248 /// Other class information
249 $class = "$hidden $excluded";
250 if ($this->switch) { // alter style based on whether aggregation is first or last
251 $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggt b2b" : " item b1b";
e0724506 252 } else {
5528217e 253 $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggb" : " item b1b";
e0724506 254 }
f7998fbc 255
5528217e 256 /// Name
257 $data['itemname']['content'] = $fullname;
258 $data['itemname']['class'] = $class;
259 $data['itemname']['colspan'] = ($this->maxdepth - $depth);
260
261 /// Actual Grade
262 $gradeval = $grade_grade->finalgrade;
263 if ($grade_grade->grade_item->needsupdate) {
264 $data['grade']['class'] = $class.' gradingerror';
265 $data['grade']['content'] = get_string('error');
266 } else if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden()
267 and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) {
d297269d 268 // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records
5528217e 269 $class .= ' datesubmitted';
270 $data['grade']['class'] = $class;
271 $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort')));
6391ebe7 272
61541a5a 273 } elseif ($grade_grade->is_hidden()) {
653a8648 274 $data['grade']['class'] = $class.' hidden';
275 $data['grade']['content'] = '-';
e0724506 276 } else {
5528217e 277 $data['grade']['class'] = $class;
61541a5a 278 $gradeval = $this->blank_hidden_total($this->courseid, $grade_grade->grade_item, $gradeval);
5528217e 279 $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true);
e0724506 280 }
281
5528217e 282 /// Percentage
699da6b5 283 if ($this->showpercentage) {
5528217e 284 if ($grade_grade->grade_item->needsupdate) {
285 $data['percentage']['class'] = $class.' gradingerror';
286 $data['percentage']['content'] = get_string('error');
653a8648 287 } elseif ($grade_grade->is_hidden()) {
288 $data['percentage']['class'] = $class.' hidden';
289 $data['percentage']['content'] = '-';
699da6b5 290 } else {
5528217e 291 $data['percentage']['class'] = $class;
292 $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE);
699da6b5 293 }
e0724506 294 }
6391ebe7 295
5528217e 296 /// Rank
26ed0305 297 if ($this->showrank) {
02973505 298 // TODO: this is broken if hidden grades present!!
5528217e 299 if ($grade_grade->grade_item->needsupdate) {
300 $data['rank']['class'] = $class.' gradingerror';
301 $data['rank']['content'] = get_string('error');
653a8648 302 } elseif ($grade_grade->is_hidden()) {
303 $data['rank']['class'] = $class.' hidden';
304 $data['rank']['content'] = '-';
02973505 305 } else if (is_null($gradeval)) {
306 // no grade, no rank
5528217e 307 $data['rank']['class'] = $class;
308 $data['rank']['content'] = '-';
26ed0305 309
02973505 310 } else {
311 /// find the number of users with a higher grade
312 $sql = "SELECT COUNT(DISTINCT(userid))
d24832f9 313 FROM {grade_grades}
ea1e551d 314 WHERE finalgrade > ?
315 AND itemid = ?";
bb776ef4 316 $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1;
26ed0305 317
5528217e 318 $data['rank']['class'] = $class;
319 $data['rank']['content'] = "$rank/".$this->get_numusers(false); // total course users
02973505 320 }
f7998fbc 321 }
322
5528217e 323 /// Feedback
324 if (empty($grade_grade->feedback) or (!$this->canviewhidden and $grade_grade->is_hidden())) {
325 $data['feedback']['class'] = $class.' feedbacktext';
326 $data['feedback']['content'] = '&nbsp;';
6391ebe7 327
328 } else {
5528217e 329 $data['feedback']['class'] = $class.' feedbacktext';
330 $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat);
e0724506 331 }
6391ebe7 332
5528217e 333 /// Range
334 if ($this->showrange) {
335 $data['range']['class'] = $class;
336 $data['range']['content'] = $grade_grade->grade_item->get_formatted_range();
337 }
f7998fbc 338 }
653a8648 339 }
e0724506 340
5528217e 341 /// Category
342 if ($type == 'category') {
343 $data['leader']['class'] = $class.' '.$alter."d$depth b1t b2b b1l";
344 $data['leader']['rowspan'] = $element['rowspan'];
345
346 if ($this->switch) { // alter style based on whether aggregation is first or last
347 $data['itemname']['class'] = $class.' '.$alter."d$depth b1b b1t";
348 } else {
349 $data['itemname']['class'] = $class.' '.$alter."d$depth b2t";
350 }
351 $data['itemname']['colspan'] = ($this->maxdepth - $depth + count($this->tablecolumns) - 1);
352 $data['itemname']['content'] = $fullname;
353 }
354
355 /// Add this row to the overall system
356 $this->tabledata[] = $data;
357
358 /// Recursively iterate through all child elements
359 if (isset($element['children'])) {
360 foreach ($element['children'] as $key=>$child) {
361 $this->fill_table_recursive($element['children'][$key]);
362 }
363 }
f7998fbc 364 }
365
366 /**
367 * Prints or returns the HTML from the flexitable.
368 * @param bool $return Whether or not to return the data instead of printing it directly.
369 * @return string
370 */
5c75a0a3 371 public function print_table($return=false) {
5528217e 372 $maxspan = $this->maxdepth;
373
374 /// Build table structure
375 $html = "
81f4918e 376 <table cellspacing='0' cellpadding='0' class='boxaligncenter generaltable user-grade'>
5528217e 377 <thead>
378 <tr>
379 <th class=\"header\" colspan='$maxspan'>".$this->tableheaders[0]."</th>\n";
380
381 for ($i = 1; $i < count($this->tableheaders); $i++) {
382 $html .= "<th class=\"header\">".$this->tableheaders[$i]."</th>\n";
383 }
384
385 $html .= "
386 </tr>
387 </thead>
388 <tbody>\n";
389
390 /// Print out the table data
391 for ($i = 0; $i < count($this->tabledata); $i++) {
392 $html .= "<tr>\n";
393 if (isset($this->tabledata[$i]['leader'])) {
394 $rowspan = $this->tabledata[$i]['leader']['rowspan'];
395 $class = $this->tabledata[$i]['leader']['class'];
396 $html .= "<td class='$class' rowspan='$rowspan'></td>\n";
397 }
398 for ($j = 0; $j < count($this->tablecolumns); $j++) {
399 $name = $this->tablecolumns[$j];
400 $class = (isset($this->tabledata[$i][$name]['class'])) ? $this->tabledata[$i][$name]['class'] : '';
401 $colspan = (isset($this->tabledata[$i][$name]['colspan'])) ? "colspan='".$this->tabledata[$i][$name]['colspan']."'" : '';
402 $content = (isset($this->tabledata[$i][$name]['content'])) ? $this->tabledata[$i][$name]['content'] : null;
403 if (isset($content)) {
404 $html .= "<td class='$class' $colspan>$content</td>\n";
405 }
406 }
407 $html .= "</tr>\n";
408 }
409
410 $html .= "</tbody></table>";
411
f7998fbc 412 if ($return) {
413 return $html;
414 } else {
415 echo $html;
416 }
417 }
418
419 /**
420 * Processes the data sent by the form (grades and feedbacks).
421 * @var array $data
422 * @return bool Success or Failure (array of errors).
423 */
653a8648 424 function process_data($data) {
5c75a0a3 425 }
653a8648 426 function process_action($target, $action) {
f7998fbc 427 }
26ed0305 428}
429
430function grade_report_user_settings_definition(&$mform) {
431 global $CFG;
432
433 $options = array(-1 => get_string('default', 'grades'),
434 0 => get_string('hide'),
435 1 => get_string('show'));
436
cd52d909 437 if (empty($CFG->grade_report_user_showrank)) {
26ed0305 438 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
439 } else {
440 $options[-1] = get_string('defaultprev', 'grades', $options[1]);
441 }
442
443 $mform->addElement('select', 'report_user_showrank', get_string('showrank', 'grades'), $options);
d24832f9 444 $mform->setHelpButton('report_user_showrank', array('showrank', get_string('showrank', 'grades'), 'grade'));
26ed0305 445
699da6b5 446 if (empty($CFG->grade_report_user_showpercentage)) {
aea45e7e 447 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
5528217e 448 } else {
449 $options[-1] = get_string('defaultprev', 'grades', $options[1]);
699da6b5 450 }
451
452 $mform->addElement('select', 'report_user_showpercentage', get_string('showpercentage', 'grades'), $options);
453 $mform->setHelpButton('report_user_showpercentage', array('showpercentage', get_string('showpercentage', 'grades'), 'grade'));
454
26ed0305 455 $options = array(-1 => get_string('default', 'grades'),
d69ef28b 456 0 => get_string('shownohidden', 'grades'),
597f50e6 457 1 => get_string('showhiddenuntilonly', 'grades'),
d69ef28b 458 2 => get_string('showallhidden', 'grades'));
26ed0305 459
cd52d909 460 if (empty($CFG->grade_report_user_showhiddenitems)) {
26ed0305 461 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
462 } else {
d69ef28b 463 $options[-1] = get_string('defaultprev', 'grades', $options[$CFG->grade_report_user_showhiddenitems]);
26ed0305 464 }
465
466 $mform->addElement('select', 'report_user_showhiddenitems', get_string('showhiddenitems', 'grades'), $options);
d24832f9 467 $mform->setHelpButton('report_user_showhiddenitems', array('showhiddenitems', get_string('showhiddenitems', 'grades'), 'grade'));
61541a5a
AD
468
469 //showtotalsifcontainhidden
470 $options = array(-1 => get_string('default', 'grades'),
471 GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN => get_string('hide'),
472 GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowexhiddenitems', 'grades'),
473 GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowinchiddenitems', 'grades') );
474
475 if (empty($CFG->grade_report_user_showtotalsifcontainhidden)) {
476 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
477 } else {
478 $options[-1] = get_string('defaultprev', 'grades', $options[$CFG->grade_report_user_showtotalsifcontainhidden]);
479 }
480
481 $mform->addElement('select', 'report_user_showtotalsifcontainhidden', get_string('hidetotalifhiddenitems', 'grades'), $options);
482 $mform->setHelpButton('report_user_showtotalsifcontainhidden', array('hidetotalifhiddenitems', get_string('hidetotalifhiddenitems', 'grades'), 'grade'));
0a784551 483}
484
485function grade_report_user_profilereport($course, $user) {
91152a35 486 global $OUTPUT;
0a784551 487 if (!empty($course->showgrades)) {
488
489 $context = get_context_instance(CONTEXT_COURSE, $course->id);
490
491 //first make sure we have proper final grades - this must be done before constructing of the grade tree
492 grade_regrade_final_grades($course->id);
493
494 /// return tracking object
495 $gpr = new grade_plugin_return(array('type'=>'report', 'plugin'=>'user', 'courseid'=>$course->id, 'userid'=>$user->id));
496 // Create a report instance
497 $report = new grade_report_user($course->id, $gpr, $context, $user->id);
f7998fbc 498
0a784551 499 // print the page
500 echo '<div class="grade-report-user">'; // css fix to share styles with real report page
c018f973 501 echo $OUTPUT->heading(get_string('modulename', 'gradereport_user'). ' - '.fullname($report->user));
0a784551 502
503 if ($report->fill_table()) {
504 echo $report->print_table(true);
505 }
506 echo '</div>';
507 }
f7998fbc 508}
0a784551 509
6c3ef410 510