Merge branch 'MDL-26031_20_wip_guestsession' of git://github.com/skodak/moodle
[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->switch = grade_get_setting($this->courseid, 'aggregationposition', $CFG->grade_aggregationposition);
105
106 // Grab the grade_tree for this course
4d27bc79 107 $this->gtree = new grade_tree($this->courseid, false, $this->switch, null, !$CFG->enableoutcomes);
26ed0305 108
5528217e 109 // Determine the number of rows and indentation
110 $this->maxdepth = 1;
111 $this->inject_rowspans($this->gtree->top_element);
112 $this->maxdepth++; // Need to account for the lead column that spans all children
113 for ($i = 1; $i <= $this->maxdepth; $i++) {
114 $this->evenodd[$i] = 0;
115 }
116
117 $this->tabledata = array();
e0724506 118
5528217e 119 $this->canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->courseid));
4faf5f99 120
f7998fbc 121 // get the user (for full name)
5c75a0a3 122 $this->user = $DB->get_record('user', array('id' => $userid));
f7998fbc 123
124 // base url for sorting by first/last name
125 $this->baseurl = $CFG->wwwroot.'/grade/report?id='.$courseid.'&amp;userid='.$userid;
90d3960c 126 $this->pbarurl = $this->baseurl;
f7998fbc 127
d1214909 128 // no groups on this report - rank is from all course users
90d3960c 129 $this->setup_table();
f7998fbc 130 }
131
5528217e 132 function inject_rowspans(&$element) {
133 if ($element['depth'] > $this->maxdepth) {
134 $this->maxdepth = $element['depth'];
135 }
136 if (empty($element['children'])) {
137 return 1;
138 }
139 $count = 1;
140 foreach ($element['children'] as $key=>$child) {
141 $count += $this->inject_rowspans($element['children'][$key]);
142 }
143 $element['rowspan'] = $count;
144 return $count;
145 }
146
147
f7998fbc 148 /**
149 * Prepares the headers and attributes of the flexitable.
150 */
5c75a0a3 151 public function setup_table() {
02973505 152 global $CFG;
f7998fbc 153 /*
d1214909 154 * Table has 5-6 columns
155 *| itemname/description | final grade | percentage final grade | rank (optional) | feedback |
156 */
f7998fbc 157
158 // setting up table headers
5528217e 159 $this->tablecolumns = array('itemname', 'grade');
160 $this->tableheaders = array($this->get_lang_string('gradeitem', 'grades'),
699da6b5 161 $this->get_lang_string('grade'));
162
5528217e 163 if ($this->showrange) {
164 $this->tablecolumns[] = 'range';
165 $this->tableheaders[] = $this->get_lang_string('range', 'grades');
166 }
167
699da6b5 168 if ($this->showpercentage) {
5528217e 169 $this->tablecolumns[] = 'percentage';
170 $this->tableheaders[] = $this->get_lang_string('percentage', 'grades');
699da6b5 171 }
172
26ed0305 173 if ($this->showrank) {
02973505 174 // TODO: this is broken if hidden grades present!!
5528217e 175 $this->tablecolumns[] = 'rank';
176 $this->tableheaders[] = $this->get_lang_string('rank', 'grades');
02973505 177 }
f7998fbc 178
5528217e 179 $this->tablecolumns[] = 'feedback';
180 $this->tableheaders[] = $this->get_lang_string('feedback', 'grades');
f7998fbc 181
5528217e 182 }
f7998fbc 183
5528217e 184 function fill_table() {
185 //print "<pre>";
186 //print_r($this->gtree->top_element);
187 $this->fill_table_recursive($this->gtree->top_element);
188 //print_r($this->tabledata);
189 //print "</pre>";
190 return true;
f7998fbc 191 }
192
5528217e 193 private function fill_table_recursive(&$element) {
bb776ef4 194 global $CFG, $DB;
f7998fbc 195
5528217e 196 $type = $element['type'];
197 $depth = $element['depth'];
198 $grade_object = $element['object'];
199 $eid = $grade_object->id;
200 $fullname = $this->gtree->get_element_header($element, true, true, true);
201 $data = array();
202 $hidden = '';
203 $excluded = '';
204 $class = '';
f7998fbc 205
a83507f2 206 // If this is a hidden grade category, hide it completely from the user
9e322c8c 207 if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && (
a83507f2
AD
208 $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN ||
209 ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil()))) {
653a8648 210 return false;
211 }
6391ebe7 212
5528217e 213 if ($type == 'category') {
214 $this->evenodd[$depth] = (($this->evenodd[$depth] + 1) % 2);
b89a70ce 215 }
5528217e 216 $alter = ($this->evenodd[$depth] == 0) ? 'even' : 'odd';
6391ebe7 217
5528217e 218 /// Process those items that have scores associated
219 if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') {
220 if (! $grade_grade = grade_grade::fetch(array('itemid'=>$grade_object->id,'userid'=>$this->user->id))) {
221 $grade_grade = new grade_grade();
222 $grade_grade->userid = $this->user->id;
223 $grade_grade->itemid = $grade_object->id;
d297269d 224 }
6391ebe7 225
5528217e 226 $grade_grade->load_grade_item();
f7998fbc 227
5528217e 228 /// Hidden Items
229 if ($grade_grade->grade_item->is_hidden()) {
230 $hidden = ' hidden';
ced5ee59 231 }
232
a83507f2 233 // If this is a hidden grade item, hide it completely from the user.
653a8648 234 if ($grade_grade->is_hidden() && !$this->canviewhidden && (
a83507f2
AD
235 $this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN ||
236 ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil()))) {
653a8648 237 // return false;
238 } else {
1762a264
AD
239 /// Excluded Item
240 if ($grade_grade->is_excluded()) {
241 $fullname .= ' ['.get_string('excluded', 'grades').']';
242 $excluded = ' excluded';
243 }
653a8648 244
1762a264
AD
245 /// Other class information
246 $class = "$hidden $excluded";
247 if ($this->switch) { // alter style based on whether aggregation is first or last
248 $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggt b2b" : " item b1b";
249 } else {
250 $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggb" : " item b1b";
251 }
f7998fbc 252
1762a264
AD
253 /// Name
254 $data['itemname']['content'] = $fullname;
255 $data['itemname']['class'] = $class;
256 $data['itemname']['colspan'] = ($this->maxdepth - $depth);
e0724506 257
1762a264
AD
258 /// Actual Grade
259 $gradeval = $grade_grade->finalgrade;
5528217e 260 if ($grade_grade->grade_item->needsupdate) {
1762a264
AD
261 $data['grade']['class'] = $class.' gradingerror';
262 $data['grade']['content'] = get_string('error');
263 } else if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden()
264 and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) {
265 // 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
266 $class .= ' datesubmitted';
267 $data['grade']['class'] = $class;
268 $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort')));
269
270 } elseif ($grade_grade->is_hidden()) {
271 $data['grade']['class'] = $class.' hidden';
272 $data['grade']['content'] = '-';
699da6b5 273 } else {
1762a264
AD
274 $data['grade']['class'] = $class;
275 $gradeval = $this->blank_hidden_total($this->courseid, $grade_grade->grade_item, $gradeval);
276 $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true);
699da6b5 277 }
6391ebe7 278
1762a264
AD
279 /// Percentage
280 if ($this->showpercentage) {
281 if ($grade_grade->grade_item->needsupdate) {
282 $data['percentage']['class'] = $class.' gradingerror';
283 $data['percentage']['content'] = get_string('error');
284 } elseif ($grade_grade->is_hidden()) {
285 $data['percentage']['class'] = $class.' hidden';
286 $data['percentage']['content'] = '-';
287 } else {
288 $data['percentage']['class'] = $class;
289 $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE);
290 }
291 }
292
293 /// Rank
294 if ($this->showrank) {
295 // TODO: this is broken if hidden grades present!!
296 if ($grade_grade->grade_item->needsupdate) {
297 $data['rank']['class'] = $class.' gradingerror';
298 $data['rank']['content'] = get_string('error');
299 } elseif ($grade_grade->is_hidden()) {
300 $data['rank']['class'] = $class.' hidden';
301 $data['rank']['content'] = '-';
302 } else if (is_null($gradeval)) {
303 // no grade, no rank
304 $data['rank']['class'] = $class;
653a8648 305 $data['rank']['content'] = '-';
26ed0305 306
1762a264
AD
307 } else {
308 /// find the number of users with a higher grade
309 $sql = "SELECT COUNT(DISTINCT(userid))
310 FROM {grade_grades}
311 WHERE finalgrade > ?
312 AND itemid = ?";
313 $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1;
314
315 $data['rank']['class'] = $class;
316 $data['rank']['content'] = "$rank/".$this->get_numusers(false); // total course users
317 }
02973505 318 }
f7998fbc 319
1762a264
AD
320 /// Feedback
321 if (empty($grade_grade->feedback) or (!$this->canviewhidden and $grade_grade->is_hidden())) {
322 $data['feedback']['class'] = $class.' feedbacktext';
323 $data['feedback']['content'] = '&nbsp;';
6391ebe7 324
1762a264
AD
325 } else {
326 $data['feedback']['class'] = $class.' feedbacktext';
367a75fa 327 $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat, array('overflowdiv'=>true));
1762a264 328 }
6391ebe7 329
1762a264
AD
330 /// Range
331 if ($this->showrange) {
332 $data['range']['class'] = $class;
333 $data['range']['content'] = $grade_grade->grade_item->get_formatted_range();
334 }
5528217e 335 }
f7998fbc 336 }
e0724506 337
5528217e 338 /// Category
339 if ($type == 'category') {
340 $data['leader']['class'] = $class.' '.$alter."d$depth b1t b2b b1l";
341 $data['leader']['rowspan'] = $element['rowspan'];
342
343 if ($this->switch) { // alter style based on whether aggregation is first or last
344 $data['itemname']['class'] = $class.' '.$alter."d$depth b1b b1t";
345 } else {
346 $data['itemname']['class'] = $class.' '.$alter."d$depth b2t";
347 }
348 $data['itemname']['colspan'] = ($this->maxdepth - $depth + count($this->tablecolumns) - 1);
349 $data['itemname']['content'] = $fullname;
350 }
351
352 /// Add this row to the overall system
353 $this->tabledata[] = $data;
354
355 /// Recursively iterate through all child elements
356 if (isset($element['children'])) {
357 foreach ($element['children'] as $key=>$child) {
358 $this->fill_table_recursive($element['children'][$key]);
359 }
360 }
f7998fbc 361 }
362
363 /**
364 * Prints or returns the HTML from the flexitable.
365 * @param bool $return Whether or not to return the data instead of printing it directly.
366 * @return string
367 */
5c75a0a3 368 public function print_table($return=false) {
5528217e 369 $maxspan = $this->maxdepth;
370
371 /// Build table structure
372 $html = "
81f4918e 373 <table cellspacing='0' cellpadding='0' class='boxaligncenter generaltable user-grade'>
5528217e 374 <thead>
375 <tr>
376 <th class=\"header\" colspan='$maxspan'>".$this->tableheaders[0]."</th>\n";
377
378 for ($i = 1; $i < count($this->tableheaders); $i++) {
379 $html .= "<th class=\"header\">".$this->tableheaders[$i]."</th>\n";
380 }
381
382 $html .= "
383 </tr>
384 </thead>
385 <tbody>\n";
386
387 /// Print out the table data
388 for ($i = 0; $i < count($this->tabledata); $i++) {
389 $html .= "<tr>\n";
390 if (isset($this->tabledata[$i]['leader'])) {
391 $rowspan = $this->tabledata[$i]['leader']['rowspan'];
392 $class = $this->tabledata[$i]['leader']['class'];
393 $html .= "<td class='$class' rowspan='$rowspan'></td>\n";
394 }
395 for ($j = 0; $j < count($this->tablecolumns); $j++) {
396 $name = $this->tablecolumns[$j];
397 $class = (isset($this->tabledata[$i][$name]['class'])) ? $this->tabledata[$i][$name]['class'] : '';
398 $colspan = (isset($this->tabledata[$i][$name]['colspan'])) ? "colspan='".$this->tabledata[$i][$name]['colspan']."'" : '';
399 $content = (isset($this->tabledata[$i][$name]['content'])) ? $this->tabledata[$i][$name]['content'] : null;
400 if (isset($content)) {
401 $html .= "<td class='$class' $colspan>$content</td>\n";
402 }
403 }
404 $html .= "</tr>\n";
405 }
406
407 $html .= "</tbody></table>";
408
f7998fbc 409 if ($return) {
410 return $html;
411 } else {
412 echo $html;
413 }
414 }
415
416 /**
417 * Processes the data sent by the form (grades and feedbacks).
418 * @var array $data
419 * @return bool Success or Failure (array of errors).
420 */
653a8648 421 function process_data($data) {
5c75a0a3 422 }
653a8648 423 function process_action($target, $action) {
f7998fbc 424 }
26ed0305 425}
426
427function grade_report_user_settings_definition(&$mform) {
428 global $CFG;
429
430 $options = array(-1 => get_string('default', 'grades'),
431 0 => get_string('hide'),
432 1 => get_string('show'));
433
cd52d909 434 if (empty($CFG->grade_report_user_showrank)) {
26ed0305 435 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
436 } else {
437 $options[-1] = get_string('defaultprev', 'grades', $options[1]);
438 }
439
440 $mform->addElement('select', 'report_user_showrank', get_string('showrank', 'grades'), $options);
1cc3165e 441 $mform->addHelpButton('report_user_showrank', 'showrank', 'grades');
26ed0305 442
699da6b5 443 if (empty($CFG->grade_report_user_showpercentage)) {
aea45e7e 444 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
5528217e 445 } else {
446 $options[-1] = get_string('defaultprev', 'grades', $options[1]);
699da6b5 447 }
448
449 $mform->addElement('select', 'report_user_showpercentage', get_string('showpercentage', 'grades'), $options);
ff268dcb 450 $mform->addHelpButton('report_user_showpercentage', 'showpercentage', 'grades');
699da6b5 451
26ed0305 452 $options = array(-1 => get_string('default', 'grades'),
d69ef28b 453 0 => get_string('shownohidden', 'grades'),
597f50e6 454 1 => get_string('showhiddenuntilonly', 'grades'),
d69ef28b 455 2 => get_string('showallhidden', 'grades'));
26ed0305 456
cd52d909 457 if (empty($CFG->grade_report_user_showhiddenitems)) {
26ed0305 458 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
459 } else {
d69ef28b 460 $options[-1] = get_string('defaultprev', 'grades', $options[$CFG->grade_report_user_showhiddenitems]);
26ed0305 461 }
462
463 $mform->addElement('select', 'report_user_showhiddenitems', get_string('showhiddenitems', 'grades'), $options);
69e3e962 464 $mform->addHelpButton('report_user_showhiddenitems', 'showhiddenitems', 'grades');
61541a5a
AD
465
466 //showtotalsifcontainhidden
467 $options = array(-1 => get_string('default', 'grades'),
468 GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN => get_string('hide'),
469 GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowexhiddenitems', 'grades'),
470 GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowinchiddenitems', 'grades') );
471
472 if (empty($CFG->grade_report_user_showtotalsifcontainhidden)) {
473 $options[-1] = get_string('defaultprev', 'grades', $options[0]);
474 } else {
475 $options[-1] = get_string('defaultprev', 'grades', $options[$CFG->grade_report_user_showtotalsifcontainhidden]);
476 }
477
478 $mform->addElement('select', 'report_user_showtotalsifcontainhidden', get_string('hidetotalifhiddenitems', 'grades'), $options);
0459cc4d 479 $mform->addHelpButton('report_user_showtotalsifcontainhidden', 'hidetotalifhiddenitems', 'grades');
0a784551 480}
481
482function grade_report_user_profilereport($course, $user) {
91152a35 483 global $OUTPUT;
0a784551 484 if (!empty($course->showgrades)) {
485
486 $context = get_context_instance(CONTEXT_COURSE, $course->id);
487
488 //first make sure we have proper final grades - this must be done before constructing of the grade tree
489 grade_regrade_final_grades($course->id);
490
491 /// return tracking object
492 $gpr = new grade_plugin_return(array('type'=>'report', 'plugin'=>'user', 'courseid'=>$course->id, 'userid'=>$user->id));
493 // Create a report instance
494 $report = new grade_report_user($course->id, $gpr, $context, $user->id);
f7998fbc 495
0a784551 496 // print the page
497 echo '<div class="grade-report-user">'; // css fix to share styles with real report page
b5e7b2bf 498 echo $OUTPUT->heading(get_string('pluginname', 'gradereport_user'). ' - '.fullname($report->user));
0a784551 499
500 if ($report->fill_table()) {
501 echo $report->print_table(true);
502 }
503 echo '</div>';
504 }
f7998fbc 505}
0a784551 506
6c3ef410 507