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