Adding some traces here and there
[moodle.git] / grade / report / grader / lib.php
CommitLineData
4ba9941c 1<?php // $Id$
2/**
3 * File in which the grader_report class is defined.
4 * @package gradebook
5 */
6
eea6690a 7require_once($CFG->dirroot . '/grade/report/lib.php');
4ba9941c 8require_once($CFG->libdir.'/tablelib.php');
4ba9941c 9
10/**
11 * Class providing an API for the grader report building and displaying.
eea6690a 12 * @uses grade_report
4ba9941c 13 * @package gradebook
14 */
eea6690a 15class grade_report_grader extends grade_report {
4ba9941c 16 /**
17 * The final grades.
18 * @var array $finalgrades
19 */
20 var $finalgrades;
21
22 /**
23 * The grade items.
24 * @var array $items
25 */
26 var $items;
27
28 /**
29 * Array of errors for bulk grades updating.
30 * @var array $gradeserror
31 */
32 var $gradeserror = array();
33
4ba9941c 34//// SQL-RELATED
35
4ba9941c 36 /**
37 * The id of the grade_item by which this report will be sorted.
38 * @var int $sortitemid
39 */
40 var $sortitemid;
41
42 /**
43 * Sortorder used in the SQL selections.
44 * @var int $sortorder
45 */
46 var $sortorder;
47
48 /**
49 * An SQL fragment affecting the search for users.
50 * @var string $userselect
51 */
52 var $userselect;
53
4faf5f99 54 /**
384960dd 55 * List of collapsed categories from user preference
4faf5f99 56 * @var array $collapsed
57 */
58 var $collapsed;
59
66ef0471 60 /**
61 * A count of the rows, used for css classes.
62 * @var int $rowcount
63 */
64 var $rowcount = 0;
65
4ba9941c 66 /**
67 * Constructor. Sets local copies of user preferences and initialises grade_tree.
68 * @param int $courseid
d30c4481 69 * @param object $gpr grade plugin return tracking object
eea6690a 70 * @param string $context
71 * @param int $page The current page being viewed (when report is paged)
72 * @param int $sortitemid The id of the grade_item by which to sort the table
4ba9941c 73 */
d30c4481 74 function grade_report_grader($courseid, $gpr, $context, $page=null, $sortitemid=null) {
4ba9941c 75 global $CFG;
d30c4481 76 parent::grade_report($courseid, $gpr, $context, $page);
4ba9941c 77
4faf5f99 78 // load collapsed settings for this report
79 if ($collapsed = get_user_preferences('grade_report_grader_collapsed_categories')) {
80 $this->collapsed = unserialize($collapsed);
4faf5f99 81 } else {
384960dd 82 $this->collapsed = array('aggregatesonly' => array(), 'gradesonly' => array());
4faf5f99 83 }
384960dd 84
aea4df41 85 if (empty($CFG->enableoutcomes)) {
86 $nooutcomes = false;
87 } else {
88 $nooutcomes = get_user_preferences('grade_report_shownooutcomes');
89 }
90
4faf5f99 91 // Grab the grade_tree for this course
aea4df41 92 $this->gtree = new grade_tree($this->courseid, true, $this->get_pref('aggregationposition'), $this->collapsed, $nooutcomes);
4faf5f99 93
4ba9941c 94 $this->sortitemid = $sortitemid;
95
4ba9941c 96 // base url for sorting by first/last name
09cef06a 97 $studentsperpage = $this->get_pref('studentsperpage');
98 $perpage = '';
99 $curpage = '';
100
101 if (!empty($studentsperpage)) {
102 $perpage = '&amp;perpage='.$studentsperpage;
103 $curpage = '&amp;page='.$this->page;
104 }
105 $this->baseurl = 'index.php?id='.$this->courseid. $perpage.$curpage.'&amp;';
106
107 $this->pbarurl = 'index.php?id='.$this->courseid.$perpage.'&amp;';
4ba9941c 108
90d3960c 109 // Setup groups if requested
936f1350 110 if ($this->get_pref('showgroups')) {
4ba9941c 111 $this->setup_groups();
112 }
113
114 $this->setup_sortitemid();
115 }
116
4ba9941c 117 /**
118 * Processes the data sent by the form (grades and feedbacks).
119 * @var array $data
120 * @return bool Success or Failure (array of errors).
121 */
122 function process_data($data) {
2cc773f5 123
a5b8be62 124 if (!has_capability('moodle/grade:edit', $this->context)) {
2cc773f5 125 return false;
126 }
127
4ba9941c 128 // always initialize all arrays
129 $queue = array();
4ba9941c 130 foreach ($data as $varname => $postedvalue) {
4ba9941c 131
132 $needsupdate = false;
133 $note = false; // TODO implement note??
134
135 // skip, not a grade nor feedback
79eabc2a 136 if (strpos($varname, 'grade') === 0) {
4ba9941c 137 $data_type = 'grade';
79eabc2a 138 } else if (strpos($varname, 'feedback') === 0) {
4ba9941c 139 $data_type = 'feedback';
140 } else {
141 continue;
142 }
143
144 $gradeinfo = explode("_", $varname);
4ba9941c 145 $userid = clean_param($gradeinfo[1], PARAM_INT);
146 $itemid = clean_param($gradeinfo[2], PARAM_INT);
147
29a5680e 148 $oldvalue = $data->{'old'.$varname};
149
150 // was change requested?
151 if ($oldvalue == $postedvalue) {
152 continue;
153 }
154
4ba9941c 155 if (!$grade_item = grade_item::fetch(array('id'=>$itemid, 'courseid'=>$this->courseid))) { // we must verify course id here!
156 error('Incorrect grade item id');
157 }
158
159 // Pre-process grade
160 if ($data_type == 'grade') {
4256a134 161 $feedback = false;
162 $feedbackformat = false;
4ba9941c 163 if ($grade_item->gradetype == GRADE_TYPE_SCALE) {
164 if ($postedvalue == -1) { // -1 means no grade
165 $finalgrade = null;
166 } else {
29a5680e 167 $finalgrade = $postedvalue;
4ba9941c 168 }
169 } else {
76317c73 170 $finalgrade = unformat_float($postedvalue);
4ba9941c 171 }
79eabc2a 172
173 } else if ($data_type == 'feedback') {
4256a134 174 $finalgrade = false;
79eabc2a 175 $trimmed = trim($postedvalue);
176 if (empty($trimmed)) {
29a5680e 177 $feedback = NULL;
e1d2692a 178 } else {
29a5680e 179 $feedback = stripslashes($postedvalue);
e1d2692a 180 }
4ba9941c 181 }
4ba9941c 182
29a5680e 183 $grade_item->update_final_grade($userid, $finalgrade, 'gradebook', $note, $feedback);
4ba9941c 184 }
185
186 return true;
187 }
188
4ba9941c 189
190 /**
191 * Setting the sort order, this depends on last state
192 * all this should be in the new table class that we might need to use
193 * for displaying grades.
194 */
195 function setup_sortitemid() {
63d6efa2 196
197 global $SESSION;
198
4ba9941c 199 if ($this->sortitemid) {
200 if (!isset($SESSION->gradeuserreport->sort)) {
ca145cc8 201 $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC';
4ba9941c 202 } else {
203 // this is the first sort, i.e. by last name
204 if (!isset($SESSION->gradeuserreport->sortitemid)) {
ca145cc8 205 $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC';
4ba9941c 206 } else if ($SESSION->gradeuserreport->sortitemid == $this->sortitemid) {
207 // same as last sort
208 if ($SESSION->gradeuserreport->sort == 'ASC') {
209 $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC';
210 } else {
211 $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC';
212 }
213 } else {
859c7259 214 $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC';
4ba9941c 215 }
216 }
217 $SESSION->gradeuserreport->sortitemid = $this->sortitemid;
218 } else {
219 // not requesting sort, use last setting (for paging)
220
221 if (isset($SESSION->gradeuserreport->sortitemid)) {
222 $this->sortitemid = $SESSION->gradeuserreport->sortitemid;
223 }
224 if (isset($SESSION->gradeuserreport->sort)) {
225 $this->sortorder = $SESSION->gradeuserreport->sort;
226 } else {
227 $this->sortorder = 'ASC';
228 }
229 }
230 }
231
4ba9941c 232 /**
233 * pulls out the userids of the users to be display, and sort them
234 * the right outer join is needed because potentially, it is possible not
235 * to have the corresponding entry in grade_grades table for some users
236 * this is check for user roles because there could be some users with grades
237 * but not supposed to be displayed
238 */
239 function load_users() {
240 global $CFG;
241
242 if (is_numeric($this->sortitemid)) {
243 $sql = "SELECT u.id, u.firstname, u.lastname
244 FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN
245 {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $this->sortitemid)
246 LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid
247 $this->groupsql
248 WHERE ra.roleid in ($this->gradebookroles)
249 $this->groupwheresql
250 AND ra.contextid ".get_related_contexts_string($this->context)."
251 ORDER BY g.finalgrade $this->sortorder";
936f1350 252 $this->users = get_records_sql($sql, $this->get_pref('studentsperpage') * $this->page,
253 $this->get_pref('studentsperpage'));
4ba9941c 254 } else {
255 // default sort
256 // get users sorted by lastname
c421ad4b 257
258 // If lastname or firstname is given as sortitemid, add the other name (firstname or lastname respectively) as second sort param
259 $sort2 = '';
260 if ($this->sortitemid == 'lastname') {
261 $sort2 = ', u.firstname ' . $this->sortorder;
262 } elseif ($this->sortitemid == 'firstname') {
263 $sort2 = ', u.lastname ' . $this->sortorder;
264 }
265
266 $this->users = get_role_users($this->gradebookroles, $this->context, false,
267 'u.id, u.firstname, u.lastname', 'u.'.$this->sortitemid .' '. $this->sortorder . $sort2,
936f1350 268 false, $this->page * $this->get_pref('studentsperpage'), $this->get_pref('studentsperpage'),
e5161d0c 269 $this->currentgroup);
4ba9941c 270 // need to cut users down by groups
271
272 }
273
274 if (empty($this->users)) {
275 $this->userselect = '';
276 $this->users = array();
277 } else {
278 $this->userselect = 'AND g.userid in ('.implode(',', array_keys($this->users)).')';
279 }
280
281 return $this->users;
282 }
283
284 /**
90d3960c 285 * Fetches and returns a count of all the users that will be shown on this page.
66ef0471 286 * @param bool $groups Whether to apply groupsql
4ba9941c 287 * @return int Count of users
288 */
66ef0471 289 function get_numusers($groups=true) {
4ba9941c 290 global $CFG;
66b9da27 291
4ba9941c 292 $countsql = "SELECT COUNT(DISTINCT u.id)
293 FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN
294 {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $this->sortitemid)
66ef0471 295 LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid ";
296 if ($groups) {
297 $countsql .= $this->groupsql;
298 }
299 $countsql .= " WHERE ra.roleid in ($this->gradebookroles) ";
300 if ($groups) {
301 $countsql .= $this->groupwheresql;
302 }
303 $countsql .= " AND ra.contextid ".get_related_contexts_string($this->context);
4ba9941c 304 return count_records_sql($countsql);
305 }
306
307 /**
308 * we supply the userids in this query, and get all the grades
309 * pulls out all the grades, this does not need to worry about paging
310 */
311 function load_final_grades() {
312 global $CFG;
313
23207a1a 314 // please note that we must fetch all grade_grades fields if we want to contruct grade_grade object from it!
3f2b0c8a 315 $sql = "SELECT g.*, gi.grademin, gi.grademax
23207a1a 316 FROM {$CFG->prefix}grade_items gi,
317 {$CFG->prefix}grade_grades g
23207a1a 318 WHERE g.itemid = gi.id AND gi.courseid = $this->courseid $this->userselect";
4ba9941c 319
320 if ($grades = get_records_sql($sql)) {
321 foreach ($grades as $grade) {
322 $this->finalgrades[$grade->userid][$grade->itemid] = $grade;
323 }
324 }
325 }
326
327 /**
328 * Builds and returns a div with on/off toggles.
329 * @return string HTML code
330 */
331 function get_toggles_html() {
aea4df41 332 global $CFG, $USER;
333
4ba9941c 334 $html = '<div id="grade-report-toggles">';
2cc773f5 335 if ($USER->gradeediting[$this->courseid]) {
336 if (has_capability('moodle/grade:manage', $this->context) or has_capability('moodle/grade:hide', $this->context)) {
337 $html .= $this->print_toggle('eyecons', true);
338 }
339 if (has_capability('moodle/grade:manage', $this->context)
340 or has_capability('moodle/grade:lock', $this->context)
341 or has_capability('moodle/grade:unlock', $this->context)) {
342 $html .= $this->print_toggle('locks', true);
343 }
344 if (has_capability('moodle/grade:manage', $this->context)) {
345 $html .= $this->print_toggle('calculations', true);
346 }
4ba9941c 347 }
348
36e53792 349 $html .= $this->print_toggle('averages', true);
aae94377 350
351 if (has_capability('moodle/grade:viewall', $this->context)
352 and has_capability('moodle/site:accessallgroups', $this->context)
353 and $course_has_groups = true) { // TODO replace that last condition with proper check
354 $html .= $this->print_toggle('groups', true);
355 }
356
61649211 357 $html .= $this->print_toggle('ranges', true);
aea4df41 358 if (!empty($CFG->enableoutcomes)) {
359 $html .= $this->print_toggle('nooutcomes', true);
360 }
4ba9941c 361 $html .= '</div>';
362 return $html;
363 }
364
365 /**
366 * Shortcut function for printing the grader report toggles.
367 * @param string $type The type of toggle
368 * @param bool $return Whether to return the HTML string rather than printing it
369 * @return void
370 */
371 function print_toggle($type, $return=false) {
372 global $CFG;
373
aea4df41 374 $icons = array('eyecons' => 't/hide.gif',
375 'calculations' => 't/calc.gif',
376 'locks' => 't/lock.gif',
ece966f0 377 'averages' => 't/mean.gif',
d4795a07 378 'nooutcomes' => 't/outcomes.gif');
4ba9941c 379
380 $pref_name = 'grade_report_show' . $type;
aea4df41 381
382 if (array_key_exists($pref_name, $CFG)) {
383 $show_pref = get_user_preferences($pref_name, $CFG->$pref_name);
384 } else {
385 $show_pref = get_user_preferences($pref_name);
386 }
4ba9941c 387
388234f4 388 $strshow = $this->get_lang_string('show' . $type, 'grades');
389 $strhide = $this->get_lang_string('hide' . $type, 'grades');
4ba9941c 390
391 $show_hide = 'show';
392 $toggle_action = 1;
393
394 if ($show_pref) {
395 $show_hide = 'hide';
396 $toggle_action = 0;
397 }
398
399 if (array_key_exists($type, $icons)) {
400 $image_name = $icons[$type];
401 } else {
aea4df41 402 $image_name = "t/$type.gif";
4ba9941c 403 }
404
405 $string = ${'str' . $show_hide};
406
aea4df41 407 $img = '<img src="'.$CFG->pixpath.'/'.$image_name.'" class="iconsmall" alt="'
4ba9941c 408 .$string.'" title="'.$string.'" />'. "\n";
409
410 $retval = '<div class="gradertoggle">' . $img . '<a href="' . $this->baseurl . "&amp;toggle=$toggle_action&amp;toggle_type=$type\">"
411 . $string . '</a></div>';
412
413 if ($return) {
414 return $retval;
415 } else {
416 echo $retval;
417 }
418 }
419
420 /**
421 * Builds and returns the HTML code for the headers.
422 * @return string $headerhtml
423 */
d30c4481 424 function get_headerhtml() {
4ba9941c 425 global $CFG, $USER;
426
48b5d8f3 427 $strsortasc = $this->get_lang_string('sortasc', 'grades');
428 $strsortdesc = $this->get_lang_string('sortdesc', 'grades');
429 $strfirstname = $this->get_lang_string('firstname');
430 $strlastname = $this->get_lang_string('lastname');
431
4ba9941c 432 if ($this->sortitemid === 'lastname') {
433 if ($this->sortorder == 'ASC') {
434 $lastarrow = print_arrow('up', $strsortasc, true);
435 } else {
436 $lastarrow = print_arrow('down', $strsortdesc, true);
437 }
438 } else {
439 $lastarrow = '';
440 }
441
442 if ($this->sortitemid === 'firstname') {
443 if ($this->sortorder == 'ASC') {
444 $firstarrow = print_arrow('up', $strsortasc, true);
445 } else {
446 $firstarrow = print_arrow('down', $strsortdesc, true);
447 }
448 } else {
449 $firstarrow = '';
450 }
451 // Prepare Table Headers
452 $headerhtml = '';
453
454 $numrows = count($this->gtree->levels);
455
cb7fe7b4 456 $columns_to_unset = array();
457
66ef0471 458
4ba9941c 459 foreach ($this->gtree->levels as $key=>$row) {
66ef0471 460 $columncount = 0;
4ba9941c 461 if ($key == 0) {
cb7fe7b4 462 // do not display course grade category
4ba9941c 463 // continue;
464 }
465
66ef0471 466 $headerhtml .= '<tr class="heading r'.$this->rowcount++.'">';
4ba9941c 467
468 if ($key == $numrows - 1) {
66ef0471 469 $headerhtml .= '<th class="header c'.$columncount++.' user" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=firstname">'
470 . $strfirstname . '</a> ' //TODO: localize
48b5d8f3 471 . $firstarrow. '/ <a href="'.$this->baseurl.'&amp;sortitemid=lastname">' . $strlastname . '</a>'. $lastarrow .'</th>';
4ba9941c 472 } else {
66ef0471 473 $headerhtml .= '<td class="cell c'.$columncount++.' topleft">&nbsp;</td>';
4ba9941c 474 }
475
cb7fe7b4 476 foreach ($row as $columnkey => $element) {
2e3987a9 477 $sort_link = '';
478 if (isset($element['object']->id)) {
479 $sort_link = $this->baseurl.'&amp;sortitemid=' . $element['object']->id;
480 }
481
cb7fe7b4 482 $eid = $element['eid'];
483 $object = $element['object'];
484 $type = $element['type'];
438a5aa9 485 $categorystate = @$element['categorystate'];
2e3987a9 486 $itemmodule = null;
487 $iteminstance = null;
8c5a416e 488
66ef0471 489 $columnclass = 'c' . $columncount++;
4ba9941c 490 if (!empty($element['colspan'])) {
491 $colspan = 'colspan="'.$element['colspan'].'"';
66ef0471 492 $columnclass = '';
4ba9941c 493 } else {
494 $colspan = '';
495 }
496
497 if (!empty($element['depth'])) {
498 $catlevel = ' catlevel'.$element['depth'];
499 } else {
500 $catlevel = '';
501 }
502
cb7fe7b4 503// Element is a filler
4ba9941c 504 if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') {
66ef0471 505 $headerhtml .= '<th class="'.$columnclass.' '.$type.$catlevel.'" '.$colspan.' scope="col">&nbsp;</th>';
cb7fe7b4 506 }
507// Element is a category
4faf5f99 508 else if ($type == 'category') {
66ef0471 509 $headerhtml .= '<th class="header '. $columnclass.' category'.$catlevel.'" '.$colspan.' scope="col">'
510 . $element['object']->get_name();
4faf5f99 511 $headerhtml .= $this->get_collapsing_icon($element);
4ba9941c 512
513 // Print icons
2cc773f5 514 if ($USER->gradeediting[$this->courseid]) {
d30c4481 515 $headerhtml .= $this->get_icons($element);
4ba9941c 516 }
517
63d6efa2 518 $headerhtml .= '</th>';
cb7fe7b4 519 }
520// Element is a grade_item
4faf5f99 521 else {
2e3987a9 522 $itemmodule = $element['object']->itemmodule;
523 $iteminstance = $element['object']->iteminstance;
524
4ba9941c 525 if ($element['object']->id == $this->sortitemid) {
526 if ($this->sortorder == 'ASC') {
2e3987a9 527 $arrow = $this->get_sort_arrow('up', $sort_link);
4ba9941c 528 } else {
2e3987a9 529 $arrow = $this->get_sort_arrow('down', $sort_link);
4ba9941c 530 }
531 } else {
2e3987a9 532 $arrow = $this->get_sort_arrow('move', $sort_link);
4ba9941c 533 }
534
535 $dimmed = '';
536 if ($element['object']->is_hidden()) {
537 $dimmed = ' dimmed_text ';
538 }
539
540 if ($object->itemtype == 'mod') {
541 $icon = '<img src="'.$CFG->modpixpath.'/'.$object->itemmodule.'/icon.gif" class="icon" alt="'
388234f4 542 .$this->get_lang_string('modulename', $object->itemmodule).'"/>';
4ba9941c 543 } else if ($object->itemtype == 'manual') {
544 //TODO: add manual grading icon
2e3987a9 545 $icon = '<img src="'.$CFG->pixpath.'/t/edit.gif" class="icon" alt="'
546 .$this->get_lang_string('manualgrade', 'grades') .'"/>';
4ba9941c 547 }
548
2e3987a9 549 $headerlink = $this->get_module_link($element['object']->get_name(), $itemmodule, $iteminstance);
66ef0471 550 $headerhtml .= '<th class="header '.$columnclass.' '.$type.$catlevel.$dimmed.'" scope="col">'. $headerlink . $arrow;
d30c4481 551 $headerhtml .= $this->get_icons($element) . '</th>';
4ba9941c 552
553 $this->items[$element['object']->sortorder] =& $element['object'];
554 }
555
556 }
557
558 $headerhtml .= '</tr>';
559 }
560 return $headerhtml;
561 }
562
563 /**
564 * Builds and return the HTML rows of the table (grades headed by student).
565 * @return string HTML
566 */
d30c4481 567 function get_studentshtml() {
4ba9941c 568 global $CFG, $USER;
569 $studentshtml = '';
388234f4 570 $strfeedback = $this->get_lang_string("feedback");
be55a047 571 $strgrade = $this->get_lang_string('grade');
18effef4 572 $gradetabindex = 1;
e7536c92 573 $showuserimage = $this->get_pref('showuserimage');
c0c1e7c2 574 $numusers = count($this->users);
4ba9941c 575
388234f4 576 // Preload scale objects for items with a scaleid
577 $scales_list = '';
c0c1e7c2 578 $tabindices = array();
388234f4 579 foreach ($this->items as $item) {
580 if (!empty($item->scaleid)) {
581 $scales_list .= "$item->scaleid,";
582 }
c0c1e7c2 583 $tabindices[$item->id]['grade'] = $gradetabindex;
584 $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers;
585 $gradetabindex += $numusers * 2;
388234f4 586 }
587 $scales_array = array();
588
589 if (!empty($scales_list)) {
590 $scales_list = substr($scales_list, 0, -1);
591 $scales_array = get_records_list('scale', 'id', $scales_list);
592 }
a5b8be62 593
00374cc5 594 $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->course->id));
388234f4 595
4ba9941c 596 foreach ($this->users as $userid => $user) {
66ef0471 597 $columncount = 0;
4ba9941c 598 // Student name and link
e7536c92 599 $user_pic = null;
600 if ($showuserimage) {
601 $user_pic = '<div class="userpic">' . print_user_picture($user->id, $this->courseid, true, 0, true) . '</div>';
602 }
603
66ef0471 604 $studentshtml .= '<tr class="r'.$this->rowcount++.'"><th class="header c'.$columncount++.' user" scope="row">' . $user_pic
605 . '<a href="' . $CFG->wwwroot . '/user/view.php?id='
4ba9941c 606 . $user->id . '">' . fullname($user) . '</a></th>';
e7536c92 607
2cc773f5 608 foreach ($this->items as $itemid=>$item) {
e50ce569 609 // Get the decimal points preference for this item
31a6c06c 610 $decimalpoints = $item->get_decimals();
4ba9941c 611
612 if (isset($this->finalgrades[$userid][$item->id])) {
613 $gradeval = $this->finalgrades[$userid][$item->id]->finalgrade;
3ee5c201 614 $grade = new grade_grade($this->finalgrades[$userid][$item->id], false);
bb384a8e 615 $grade->feedback = stripslashes_safe($this->finalgrades[$userid][$item->id]->feedback);
bd6c9ddb 616 $grade->feedbackformat = $this->finalgrades[$userid][$item->id]->feedbackformat;
4ba9941c 617
618 } else {
619 $gradeval = null;
2cc773f5 620 $grade = new grade_grade(array('userid'=>$userid, 'itemid'=>$item->id), false);
4ba9941c 621 $grade->feedback = '';
622 }
623
00374cc5 624 // MDL-11274
625 // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden'
626 if ($grade->is_hidden() && !$canviewhidden) {
627 if (isset($grade->finalgrade)) {
bb49f77b 628 $studentshtml .= '<td class="cell c'.$columncount++.'">'.userdate($grade->timecreated,get_string('strftimedatetimeshort')).'</td>';
629 } else {
00374cc5 630 $studentshtml .= '<td class="cell c'.$columncount++.'">-</td>';
631 }
a5b8be62 632 continue;
00374cc5 633 }
634
2cc773f5 635 $grade->courseid = $this->courseid;
636 $grade->grade_item =& $this->items[$itemid]; // this speedsup is_hidden() and other grade_grade methods
637
638 // emulate grade element
d3c3da1b 639 $eid = $this->gtree->get_grade_eid($grade);
640 $element = array('eid'=>$eid, 'object'=>$grade, 'type'=>'grade');
2cc773f5 641
4ba9941c 642 if ($grade->is_overridden()) {
66ef0471 643 $studentshtml .= '<td class="overridden cell c'.$columncount++.'">';
4ba9941c 644 } else {
66ef0471 645 $studentshtml .= '<td class="cell c'.$columncount++.'">';
4ba9941c 646 }
647
23207a1a 648 if ($grade->is_excluded()) {
649 $studentshtml .= get_string('excluded', 'grades'); // TODO: improve visual representation of excluded grades
650 }
651
4ba9941c 652 // Do not show any icons if no grade (no record in DB to match)
d3c3da1b 653 if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) {
2cc773f5 654 $studentshtml .= $this->get_icons($element);
4ba9941c 655 }
656
657 // if in editting mode, we need to print either a text box
658 // or a drop down (for scales)
4ba9941c 659 // grades in item of type grade category or course are not directly editable
d14ae855 660 if ($item->needsupdate) {
661 $studentshtml .= '<span class="gradingerror">'.get_string('error').'</span>';
662
2cc773f5 663 } else if ($USER->gradeediting[$this->courseid]) {
4ba9941c 664 // We need to retrieve each grade_grade object from DB in order to
665 // know if they are hidden/locked
666
388234f4 667 if ($item->scaleid && !empty($scales_array[$item->scaleid])) {
668 $scale = $scales_array[$item->scaleid];
669
670 $scales = explode(",", $scale->scale);
671 // reindex because scale is off 1
672 $i = 0;
673 foreach ($scales as $scaleoption) {
674 $i++;
675 $scaleopt[$i] = $scaleoption;
676 }
677
678 if ($this->get_pref('quickgrading') and $grade->is_editable()) {
0658afc9 679 $oldval = empty($gradeval) ? -1 : $gradeval;
680 if (empty($item->outcomeid)) {
d4795a07 681 $nogradestr = $this->get_lang_string('nograde');
0658afc9 682 } else {
d4795a07 683 $nogradestr = $this->get_lang_string('nooutcome', 'grades');
0658afc9 684 }
29a5680e 685 $studentshtml .= '<input type="hidden" name="oldgrade_'.$userid.'_'
0658afc9 686 .$item->id.'" value="'.$oldval.'"/>';
388234f4 687 $studentshtml .= choose_from_menu($scaleopt, 'grade_'.$userid.'_'.$item->id,
0658afc9 688 $gradeval, $nogradestr, '', '-1',
c0c1e7c2 689 true, false, $tabindices[$item->id]['grade']);
388234f4 690 } elseif(!empty($scale)) {
4ba9941c 691 $scales = explode(",", $scale->scale);
4ba9941c 692
388234f4 693 // invalid grade if gradeval < 1
694 if ((int) $gradeval < 1) {
695 $studentshtml .= '-';
4ba9941c 696 } else {
465b310d 697 $gradeval = (int)bounded_number($grade->grade_item->grademin, $gradeval, $grade->grade_item->grademax); //just in case somebody changes scale
388234f4 698 $studentshtml .= $scales[$gradeval-1];
4ba9941c 699 }
388234f4 700 } else {
701 // no such scale, throw error?
4ba9941c 702 }
79eabc2a 703
bb384a8e 704 } else if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type
936f1350 705 if ($this->get_pref('quickgrading') and $grade->is_editable()) {
76317c73 706 $value = format_float($gradeval, $decimalpoints);
29a5680e 707 $studentshtml .= '<input type="hidden" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$value.'" />';
be55a047 708 $studentshtml .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade']
709 . '" type="text" title="'. $strgrade .'" name="grade_'
c0c1e7c2 710 .$userid.'_' .$item->id.'" value="'.$value.'" />';
4ba9941c 711 } else {
76317c73 712 $studentshtml .= format_float($gradeval, $decimalpoints);
4ba9941c 713 }
714 }
715
716
717 // If quickfeedback is on, print an input element
936f1350 718 if ($this->get_pref('quickfeedback') and $grade->is_editable()) {
719 if ($this->get_pref('quickgrading')) {
4ba9941c 720 $studentshtml .= '<br />';
721 }
29a5680e 722 $studentshtml .= '<input type="hidden" name="oldfeedback_'
723 .$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
be55a047 724 $studentshtml .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback']
725 . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_'
29a5680e 726 .$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
4ba9941c 727 }
728
78a2d9f0 729 } else { // Not editing
41f22daa 730 $gradedisplaytype = $item->get_displaytype();
e50ce569 731
2f61fc0e 732 $percentsign = '';
9580a21f 733 $grademin = $item->grademin;
734 $grademax = $item->grademax;
2f61fc0e 735
78a2d9f0 736 // If feedback present, surround grade with feedback tooltip: Open span here
4ba9941c 737 if (!empty($grade->feedback)) {
9fa46707 738 $overlib = '';
bd6c9ddb 739 if ($grade->feedbackformat == 1) {
bd6c9ddb 740 $overlib = "return overlib('" . s(ltrim($grade->feedback)) . "', FULLHTML);";
bb384a8e 741 } else {
9fa46707 742 $overlib = "return overlib('" . s($grade->feedback) . "', BORDER, 0, FGCLASS, 'feedback', "
743 . "CAPTIONFONTCLASS, 'caption', CAPTION, '$strfeedback');";
bd6c9ddb 744 }
745
746 $studentshtml .= '<span onmouseover="' . $overlib . '" onmouseout="return nd();">';
4ba9941c 747 }
748
d14ae855 749 if ($item->needsupdate) {
750 $studentshtml .= '<span class="gradingerror">'.get_string('error').'</span>';
78a2d9f0 751 } elseif ($item->scaleid && !empty($scales_array[$item->scaleid])) {
388234f4 752 $scale = $scales_array[$item->scaleid];
753 $scales = explode(",", $scale->scale);
4ba9941c 754
388234f4 755 // invalid grade if gradeval < 1
756 if ((int) $gradeval < 1) {
757 $studentshtml .= '-';
4ba9941c 758 } else {
388234f4 759 $studentshtml .= $scales[$gradeval-1];
4ba9941c 760 }
761 } else {
762 if (is_null($gradeval)) {
763 $studentshtml .= '-';
764 } else {
78a2d9f0 765 $studentshtml .= grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null);
4ba9941c 766 }
767 }
78a2d9f0 768
769 // Close feedback span
4ba9941c 770 if (!empty($grade->feedback)) {
771 $studentshtml .= '</span>';
772 }
773 }
774
775 if (!empty($this->gradeserror[$item->id][$userid])) {
776 $studentshtml .= $this->gradeserror[$item->id][$userid];
777 }
778
779 $studentshtml .= '</td>' . "\n";
780 }
781 $studentshtml .= '</tr>';
782 }
783 return $studentshtml;
784 }
785
786 /**
5b508a08 787 * Builds and return the HTML row of column totals.
788 * @param bool $grouponly Whether to return only group averages or all averages.
4ba9941c 789 * @return string HTML
790 */
5b508a08 791 function get_avghtml($grouponly=false) {
2f61fc0e 792 global $CFG, $USER;
4ba9941c 793
5b508a08 794 $averagesdisplaytype = $this->get_pref('averagesdisplaytype');
795 $averagesdecimalpoints = $this->get_pref('averagesdecimalpoints');
098042ba 796 $meanselection = $this->get_pref('meanselection');
797 $shownumberofgrades = $this->get_pref('shownumberofgrades');
798
d32100a5 799 $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->course->id));
800
0dba6cb2 801 if ($canviewhidden) {
802 $hidingsql1 = "";
803 $hidingsql2 = "";
804
805 } else {
806 $now = round(time(), -2); //100 sec gradularity, we need some db caching speedup here
807 $hidingsql1 = "AND g.hidden!=1 AND (g.hidden=0 OR g.hidden<$now)";
808 $hidingsql2 = "OR (g.hidden!=1 AND (g.hidden=0 OR g.hidden<$now))";
809 }
810
5b508a08 811 $avghtml = '';
aae94377 812 $avgcssclass = 'avg';
3446013d 813
5b508a08 814 if ($grouponly) {
815 $straverage = get_string('groupavg', 'grades');
816 $showaverages = $this->currentgroup && $this->get_pref('showgroups');
817 $groupsql = $this->groupsql;
818 $groupwheresql = $this->groupwheresql;
aae94377 819 $avgcssclass = 'groupavg';
3446013d 820 } else {
6308b91c 821 $straverage = get_string('overallaverage', 'grades');
5b508a08 822 $showaverages = $this->get_pref('showaverages');
0dba6cb2 823 $groupsql = "";
824 $groupwheresql = "";
3446013d 825 }
826
a5b8be62 827 if ($shownumberofgrades) {
828 $straverage .= ' (' . get_string('submissions', 'grades') . ') ';
829 }
830
f8ae1f86 831 $totalcount = $this->get_numusers($grouponly);
04259694 832
5b508a08 833 if ($showaverages) {
04259694 834
edc91676 835 // the first join on user is needed for groupsql
33a34cd4 836 $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum
edc91676 837 FROM {$CFG->prefix}grade_items gi LEFT JOIN
838 {$CFG->prefix}grade_grades g ON gi.id = g.itemid LEFT JOIN
839 {$CFG->prefix}user u ON g.userid = u.id
840 $groupsql
841 WHERE gi.courseid = $this->courseid
0dba6cb2 842 $hidingsql1
edc91676 843 $groupwheresql
04259694 844 AND g.userid IN (
845 SELECT DISTINCT(u.id)
edc91676 846 FROM {$CFG->prefix}user u LEFT JOIN
847 {$CFG->prefix}role_assignments ra ON u.id = ra.userid
848 WHERE ra.roleid in ($this->gradebookroles)
849 AND ra.contextid ".get_related_contexts_string($this->context)."
850 )
851 GROUP BY g.itemid";
5b508a08 852 $sum_array = array();
ab3444d7 853 if ($sums = get_records_sql($SQL)) {
854 foreach ($sums as $itemid => $csum) {
855 $sum_array[$itemid] = $csum->sum;
7b61efbe 856 }
4ba9941c 857 }
66ef0471 858
aae94377 859 $avghtml = '<tr class="' . $avgcssclass . ' r'.$this->rowcount++.'"><th class="header c0" scope="row">'.$straverage.'</th>';
66ef0471 860
861 $columncount=1;
6df5b04e 862
4ba9941c 863 foreach ($this->items as $item) {
d32100a5 864 // If the user shouldn't see this grade_item, hide the average as well
6df5b04e 865 // MDL-11576 If any of the grades are hidden and the user doesn't have permission to view them, hide average as well
0dba6cb2 866 if (!$canviewhidden and $item->is_hidden()) {
d32100a5 867 $avghtml .= '<td class="cell c' . $columncount++.'"> - </td>';
868 continue;
869 }
870
33a34cd4 871 if (empty($sum_array[$item->id])) {
872 $sum_array[$item->id] = 0;
873 }
33a34cd4 874 // MDL-10875 Empty grades must be evaluated as grademin, NOT always 0
875 // This query returns a count of ungraded grades (NULL finalgrade OR no matching record in grade_grades table)
0dba6cb2 876 // optionally plus number of hidden grades
33a34cd4 877 $SQL = "SELECT COUNT(*) AS count FROM {$CFG->prefix}user u
878 WHERE u.id NOT IN
0dba6cb2 879 (SELECT userid FROM {$CFG->prefix}grade_grades g
880 WHERE g.itemid = $item->id AND
881 (g.finalgrade IS NOT NULL $hidingsql2)
33a34cd4 882 )
883 AND u.id IN (
884 SELECT DISTINCT(u.id)
885 FROM {$CFG->prefix}user u LEFT JOIN
886 {$CFG->prefix}role_assignments ra ON u.id = ra.userid
f8ae1f86 887 $groupsql
33a34cd4 888 WHERE ra.roleid in ($this->gradebookroles)
889 AND ra.contextid ".get_related_contexts_string($this->context)."
f8ae1f86 890 $groupwheresql
33a34cd4 891 )";
892
893 $ungraded_count = get_field_sql($SQL);
894
c2efb501 895 if ($meanselection == GRADE_REPORT_MEAN_GRADED) {
33a34cd4 896 $mean_count = $totalcount - $ungraded_count;
897 } else { // Bump up the sum by the number of ungraded items * grademin
898 if (isset($sum_array[$item->id])) {
899 $sum_array[$item->id] += $ungraded_count * $item->grademin;
900 }
901 $mean_count = $totalcount;
902 }
903
31a6c06c 904 $decimalpoints = $item->get_decimals();
41f22daa 905
bb384a8e 906 // Determine which display type to use for this average
41f22daa 907 $gradedisplaytype = $item->get_displaytype();
908
2cc773f5 909 if ($USER->gradeediting[$this->courseid]) {
1796708d 910 $displaytype = GRADE_DISPLAY_TYPE_REAL;
e50ce569 911 } elseif ($averagesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) { // Inherit specific column or general preference
bb384a8e 912 $displaytype = $gradedisplaytype;
913 } else { // General preference overrides specific column display type
914 $displaytype = $averagesdisplaytype;
915 }
916
31a6c06c 917 // Override grade_item setting if a display preference (not inherit) was set for the averages
5b508a08 918 if ($averagesdecimalpoints != GRADE_REPORT_PREFERENCE_INHERIT) {
919 $decimalpoints = $averagesdecimalpoints;
920 }
921
33a34cd4 922 if (!isset($sum_array[$item->id]) || $mean_count == 0) {
66ef0471 923 $avghtml .= '<td class="cell c' . $columncount++.'">-</td>';
4ba9941c 924 } else {
5b508a08 925 $sum = $sum_array[$item->id];
ac04f85d 926
927 if ($item->scaleid) {
5b508a08 928 if ($grouponly) {
929 $finalsum = $sum_array[$item->id];
33a34cd4 930 $finalavg = $finalsum/$mean_count;
5b508a08 931 } else {
33a34cd4 932 $finalavg = $sum/$mean_count;
5b508a08 933 }
76317c73 934 $scaleval = round($finalavg);
acdc8e8a 935 $scale_object = new grade_scale(array('id' => $item->scaleid), false);
936 $gradehtml = $scale_object->get_nearest_item($scaleval);
2f61fc0e 937 $rawvalue = $scaleval;
3af29899 938 } else {
4581501e 939 $rawgradeval = $sum/$mean_count;
33a34cd4 940 $gradeval = format_float($sum/$mean_count, $decimalpoints);
d8c684de 941 $gradehtml = $gradeval;
2f61fc0e 942 }
bb384a8e 943
e9096dc2 944 if ($displaytype == GRADE_DISPLAY_TYPE_PERCENTAGE or $displaytype == GRADE_DISPLAY_TYPE_LETTER) {
945 $gradeshtml = grade_format_gradevalue($rawgradeval, $item, true, $displaytype, null);
3af29899 946 }
bb384a8e 947
098042ba 948 $numberofgrades = '';
949
950 if ($shownumberofgrades) {
951 $numberofgrades = " ($mean_count)";
952 }
953
954 $avghtml .= '<td class="cell c' . $columncount++.'">'.$gradehtml.$numberofgrades.'</td>';
4ba9941c 955 }
956 }
5b508a08 957 $avghtml .= '</tr>';
4ba9941c 958 }
5b508a08 959 return $avghtml;
4ba9941c 960 }
961
e5161d0c 962 /**
2f61fc0e 963 * Builds and return the HTML row of ranges for each column (i.e. range).
e5161d0c 964 * @return string HTML
965 */
2f61fc0e 966 function get_rangehtml() {
967 global $USER;
968
4ba9941c 969 $scalehtml = '';
61649211 970 if ($this->get_pref('showranges')) {
2f61fc0e 971 $rangesdisplaytype = $this->get_pref('rangesdisplaytype');
5b508a08 972 $rangesdecimalpoints = $this->get_pref('rangesdecimalpoints');
66ef0471 973 $scalehtml = '<tr class="r'.$this->rowcount++.'">'
974 . '<th class="header c0 range" scope="row">'.$this->get_lang_string('range','grades').'</th>';
5b508a08 975
66ef0471 976 $columncount = 1;
4ba9941c 977 foreach ($this->items as $item) {
66ef0471 978
2f61fc0e 979 // Determine which display type to use for this range
31a6c06c 980 $decimalpoints = $item->get_decimals();
41f22daa 981 $gradedisplaytype = $item->get_displaytype();
db4c7968 982
2cc773f5 983 if ($USER->gradeediting[$this->courseid]) {
1796708d 984 $displaytype = GRADE_DISPLAY_TYPE_REAL;
e50ce569 985 } elseif ($rangesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) { // Inherit specific column or general preference
2f61fc0e 986 $displaytype = $gradedisplaytype;
987 } else { // General preference overrides specific column display type
988 $displaytype = $rangesdisplaytype;
989 }
990
31a6c06c 991 // If ranges decimal points pref is set (but not to inherit), override grade_item setting
5b508a08 992 if ($rangesdecimalpoints != GRADE_REPORT_PREFERENCE_INHERIT) {
993 $decimalpoints = $rangesdecimalpoints;
994 }
995
d32100a5 996 $grademin = 0;
997 $grademax = 100;
998
1796708d 999 if ($displaytype == GRADE_DISPLAY_TYPE_REAL) {
76317c73 1000 $grademin = format_float($item->grademin, $decimalpoints);
1001 $grademax = format_float($item->grademax, $decimalpoints);
1796708d 1002 } elseif ($displaytype == GRADE_DISPLAY_TYPE_PERCENTAGE) {
2f61fc0e 1003 $grademin = 0;
1004 $grademax = 100;
1796708d 1005 } elseif ($displaytype == GRADE_DISPLAY_TYPE_LETTER) {
32b97bb2 1006 $letters = grade_report::get_grade_letters();
1007 $grademin = end($letters);
1008 $grademax = reset($letters);
2f61fc0e 1009 }
1010
66ef0471 1011 $scalehtml .= '<th class="header c'.$columncount++.' range">'. $grademin.'-'. $grademax.'</th>';
4ba9941c 1012 }
1013 $scalehtml .= '</tr>';
1014 }
1015 return $scalehtml;
1016 }
1017
1018 /**
1019 * Given a grade_category, grade_item or grade_grade, this function
1020 * figures out the state of the object and builds then returns a div
1021 * with the icons needed for the grader report.
1022 *
1023 * @param object $object
4ba9941c 1024 * @return string HTML
1025 */
2cc773f5 1026 function get_icons($element) {
1027 global $CFG, $USER;
4ba9941c 1028
2cc773f5 1029 if (!$USER->gradeediting[$this->courseid]) {
1030 return '<div class="grade_icons" />';
79eabc2a 1031 }
4ba9941c 1032
2cc773f5 1033 // Init all icons
1034 $edit_icon = $this->gtree->get_edit_icon($element, $this->gpr);
95d6df77 1035 $edit_calculation_icon = '';
2cc773f5 1036 $show_hide_icon = '';
2cc773f5 1037 $lock_unlock_icon = '';
4ba9941c 1038
a5b8be62 1039 if (has_capability('moodle/grade:manage', $this->context)) {
4ba9941c 1040
a5b8be62 1041 if ($this->get_pref('showcalculations')) {
1042 $edit_calculation_icon = $this->gtree->get_calculation_icon($element, $this->gpr);
1043 }
1044
1045 if ($this->get_pref('showeyecons')) {
1046 $show_hide_icon = $this->gtree->get_hiding_icon($element, $this->gpr);
1047 }
4ba9941c 1048
a5b8be62 1049 if ($this->get_pref('showlocks')) {
1050 $lock_unlock_icon = $this->gtree->get_locking_icon($element, $this->gpr);
1051 }
4ba9941c 1052 }
1053
4faf5f99 1054 return '<div class="grade_icons">'.$edit_icon.$edit_calculation_icon.$show_hide_icon.$lock_unlock_icon.'</div>';
1055 }
1056
1057 /**
1058 * Given a category element returns collapsing +/- icon if available
1059 * @param object $object
1060 * @return string HTML
1061 */
1062 function get_collapsing_icon($element) {
1063 global $CFG;
1064
1065 $contract_expand_icon = '';
2cc773f5 1066 // If object is a category, display expand/contract icon
384960dd 1067 if ($element['type'] == 'category') {
2cc773f5 1068 // Load language strings
384960dd 1069 $strswitch_minus = $this->get_lang_string('aggregatesonly', 'grades');
1070 $strswitch_plus = $this->get_lang_string('gradesonly', 'grades');
1071 $strswitch_whole = $this->get_lang_string('fullmode', 'grades');
1072
2cc773f5 1073 $expand_contract = 'switch_minus'; // Default: expanded
384960dd 1074 // $this->get_pref('aggregationview', $element['object']->id) == GRADE_REPORT_AGGREGATION_VIEW_COMPACT
48b5d8f3 1075
384960dd 1076 if (in_array($element['object']->id, $this->collapsed['aggregatesonly'])) {
2cc773f5 1077 $expand_contract = 'switch_plus';
384960dd 1078 } elseif (in_array($element['object']->id, $this->collapsed['gradesonly'])) {
1079 $expand_contract = 'switch_whole';
2cc773f5 1080 }
4faf5f99 1081 $url = $this->gpr->get_return_url(null, array('target'=>$element['eid'], 'action'=>$expand_contract, 'sesskey'=>sesskey()));
1082 $contract_expand_icon = '<a href="'.$url.'"><img src="'.$CFG->pixpath.'/t/'.$expand_contract.'.gif" class="iconsmall" alt="'
1083 .${'str'.$expand_contract}.'" title="'.${'str'.$expand_contract}.'" /></a>';
4ba9941c 1084 }
4faf5f99 1085 return $contract_expand_icon;
4ba9941c 1086 }
48b5d8f3 1087
1088 /**
1089 * Processes a single action against a category, grade_item or grade.
1090 * @param string $target eid ({type}{id}, e.g. c4 for category4)
1091 * @param string $action Which action to take (edit, delete etc...)
1092 * @return
1093 */
1094 function process_action($target, $action) {
4faf5f99 1095 // TODO: this code should be in some grade_tree static method
48b5d8f3 1096 $targettype = substr($target, 0, 1);
1097 $targetid = substr($target, 1);
4faf5f99 1098 // TODO: end
1099
1100 if ($collapsed = get_user_preferences('grade_report_grader_collapsed_categories')) {
1101 $collapsed = unserialize($collapsed);
1102 } else {
384960dd 1103 $collapsed = array('aggregatesonly' => array(), 'gradesonly' => array());
4faf5f99 1104 }
1105
48b5d8f3 1106 switch ($action) {
384960dd 1107 case 'switch_minus': // Add category to array of aggregatesonly
1108 if (!in_array($targetid, $collapsed['aggregatesonly'])) {
1109 $collapsed['aggregatesonly'][] = $targetid;
4faf5f99 1110 set_user_preference('grade_report_grader_collapsed_categories', serialize($collapsed));
1111 }
48b5d8f3 1112 break;
4faf5f99 1113
384960dd 1114 case 'switch_plus': // Remove category from array of aggregatesonly, and add it to array of gradesonly
1115 $key = array_search($targetid, $collapsed['aggregatesonly']);
4faf5f99 1116 if ($key !== false) {
384960dd 1117 unset($collapsed['aggregatesonly'][$key]);
4faf5f99 1118 }
384960dd 1119 if (!in_array($targetid, $collapsed['gradesonly'])) {
1120 $collapsed['gradesonly'][] = $targetid;
1121 }
1122 set_user_preference('grade_report_grader_collapsed_categories', serialize($collapsed));
48b5d8f3 1123 break;
384960dd 1124 case 'switch_whole': // Remove the category from the array of collapsed cats
1125 $key = array_search($targetid, $collapsed['gradesonly']);
1126 if ($key !== false) {
1127 unset($collapsed['gradesonly'][$key]);
1128 set_user_preference('grade_report_grader_collapsed_categories', serialize($collapsed));
1129 }
4faf5f99 1130
384960dd 1131 break;
48b5d8f3 1132 default:
1133 break;
1134 }
1135
1136 return true;
1137 }
4ba9941c 1138}
1139?>