Commit | Line | Data |
---|---|---|
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 | |
4ba9941c | 18 | /** |
19 | * File in which the grader_report class is defined. | |
20 | * @package gradebook | |
21 | */ | |
22 | ||
eea6690a | 23 | require_once($CFG->dirroot . '/grade/report/lib.php'); |
4ba9941c | 24 | require_once($CFG->libdir.'/tablelib.php'); |
4ba9941c | 25 | |
26 | /** | |
27 | * Class providing an API for the grader report building and displaying. | |
eea6690a | 28 | * @uses grade_report |
4ba9941c | 29 | * @package gradebook |
30 | */ | |
eea6690a | 31 | class grade_report_grader extends grade_report { |
4ba9941c | 32 | /** |
33 | * The final grades. | |
b89a70ce | 34 | * @var array $grades |
4ba9941c | 35 | */ |
d24832f9 | 36 | public $grades; |
4ba9941c | 37 | |
38 | /** | |
39 | * Array of errors for bulk grades updating. | |
40 | * @var array $gradeserror | |
41 | */ | |
d24832f9 | 42 | public $gradeserror = array(); |
4ba9941c | 43 | |
4ba9941c | 44 | //// SQL-RELATED |
45 | ||
4ba9941c | 46 | /** |
47 | * The id of the grade_item by which this report will be sorted. | |
48 | * @var int $sortitemid | |
49 | */ | |
d24832f9 | 50 | public $sortitemid; |
4ba9941c | 51 | |
52 | /** | |
53 | * Sortorder used in the SQL selections. | |
54 | * @var int $sortorder | |
55 | */ | |
d24832f9 | 56 | public $sortorder; |
4ba9941c | 57 | |
58 | /** | |
59 | * An SQL fragment affecting the search for users. | |
60 | * @var string $userselect | |
61 | */ | |
d24832f9 | 62 | public $userselect; |
63 | ||
64 | /** | |
65 | * The bound params for $userselect | |
319770d7 | 66 | * @var array $userselectparams |
d24832f9 | 67 | */ |
319770d7 | 68 | public $userselectparams = array(); |
4ba9941c | 69 | |
4faf5f99 | 70 | /** |
384960dd | 71 | * List of collapsed categories from user preference |
4faf5f99 | 72 | * @var array $collapsed |
73 | */ | |
d24832f9 | 74 | public $collapsed; |
4faf5f99 | 75 | |
66ef0471 | 76 | /** |
77 | * A count of the rows, used for css classes. | |
78 | * @var int $rowcount | |
79 | */ | |
d24832f9 | 80 | public $rowcount = 0; |
66ef0471 | 81 | |
6cc3e350 | 82 | /** |
57068674 | 83 | * Capability check caching |
84 | * */ | |
d24832f9 | 85 | public $canviewhidden; |
57068674 | 86 | |
319770d7 | 87 | var $preferencespage=false; |
653a8648 | 88 | |
92ff6236 AD |
89 | /** |
90 | * Length at which feedback will be truncated (to the nearest word) and an ellipsis be added. | |
91 | * TODO replace this by a report preference | |
92 | * @var int $feedback_trunc_length | |
93 | */ | |
94 | protected $feedback_trunc_length = 50; | |
95 | ||
4ba9941c | 96 | /** |
97 | * Constructor. Sets local copies of user preferences and initialises grade_tree. | |
98 | * @param int $courseid | |
d30c4481 | 99 | * @param object $gpr grade plugin return tracking object |
eea6690a | 100 | * @param string $context |
101 | * @param int $page The current page being viewed (when report is paged) | |
102 | * @param int $sortitemid The id of the grade_item by which to sort the table | |
4ba9941c | 103 | */ |
d24832f9 | 104 | public function __construct($courseid, $gpr, $context, $page=null, $sortitemid=null) { |
4ba9941c | 105 | global $CFG; |
d24832f9 | 106 | parent::__construct($courseid, $gpr, $context, $page); |
4ba9941c | 107 | |
57068674 | 108 | $this->canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->course->id)); |
109 | ||
4faf5f99 | 110 | // load collapsed settings for this report |
111 | if ($collapsed = get_user_preferences('grade_report_grader_collapsed_categories')) { | |
112 | $this->collapsed = unserialize($collapsed); | |
4faf5f99 | 113 | } else { |
384960dd | 114 | $this->collapsed = array('aggregatesonly' => array(), 'gradesonly' => array()); |
4faf5f99 | 115 | } |
384960dd | 116 | |
aea4df41 | 117 | if (empty($CFG->enableoutcomes)) { |
118 | $nooutcomes = false; | |
119 | } else { | |
120 | $nooutcomes = get_user_preferences('grade_report_shownooutcomes'); | |
121 | } | |
122 | ||
e0724506 | 123 | // if user report preference set or site report setting set use it, otherwise use course or site setting |
124 | $switch = $this->get_pref('aggregationposition'); | |
05766b50 | 125 | if ($switch == '') { |
e0724506 | 126 | $switch = grade_get_setting($this->courseid, 'aggregationposition', $CFG->grade_aggregationposition); |
127 | } | |
128 | ||
4faf5f99 | 129 | // Grab the grade_tree for this course |
e0724506 | 130 | $this->gtree = new grade_tree($this->courseid, true, $switch, $this->collapsed, $nooutcomes); |
4faf5f99 | 131 | |
4ba9941c | 132 | $this->sortitemid = $sortitemid; |
133 | ||
4ba9941c | 134 | // base url for sorting by first/last name |
09cef06a | 135 | |
319770d7 | 136 | $this->baseurl = new moodle_url('index.php', array('id' => $this->courseid)); |
137 | ||
138 | $studentsperpage = $this->get_pref('studentsperpage'); | |
09cef06a | 139 | if (!empty($studentsperpage)) { |
319770d7 | 140 | $this->baseurl->params(array('perpage' => $studentsperpage, 'page' => $this->page)); |
09cef06a | 141 | } |
09cef06a | 142 | |
a6855934 | 143 | $this->pbarurl = new moodle_url('/grade/report/grader/index.php', array('id' => $this->courseid, 'perpage' => $studentsperpage)); |
4ba9941c | 144 | |
35079f53 | 145 | $this->setup_groups(); |
4ba9941c | 146 | |
147 | $this->setup_sortitemid(); | |
148 | } | |
149 | ||
4ba9941c | 150 | /** |
151 | * Processes the data sent by the form (grades and feedbacks). | |
6ef4878b | 152 | * Caller is responsible for all access control checks |
75674470 | 153 | * @param array $data form submission (with magic quotes) |
154 | * @return array empty array if success, array of warnings if something fails. | |
4ba9941c | 155 | */ |
d24832f9 | 156 | public function process_data($data) { |
5c75a0a3 | 157 | global $DB; |
75674470 | 158 | $warnings = array(); |
2cc773f5 | 159 | |
30ebb74f | 160 | $separategroups = false; |
161 | $mygroups = array(); | |
162 | if ($this->groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $this->context)) { | |
163 | $separategroups = true; | |
164 | $mygroups = groups_get_user_groups($this->course->id); | |
165 | $mygroups = $mygroups[0]; // ignore groupings | |
166 | // reorder the groups fro better perf bellow | |
167 | $current = array_search($this->currentgroup, $mygroups); | |
168 | if ($current !== false) { | |
169 | unset($mygroups[$current]); | |
170 | array_unshift($mygroups, $this->currentgroup); | |
171 | } | |
172 | } | |
173 | ||
4ba9941c | 174 | // always initialize all arrays |
175 | $queue = array(); | |
4ba9941c | 176 | foreach ($data as $varname => $postedvalue) { |
4ba9941c | 177 | |
178 | $needsupdate = false; | |
4ba9941c | 179 | |
180 | // skip, not a grade nor feedback | |
79eabc2a | 181 | if (strpos($varname, 'grade') === 0) { |
319770d7 | 182 | $datatype = 'grade'; |
79eabc2a | 183 | } else if (strpos($varname, 'feedback') === 0) { |
319770d7 | 184 | $datatype = 'feedback'; |
4ba9941c | 185 | } else { |
186 | continue; | |
187 | } | |
188 | ||
189 | $gradeinfo = explode("_", $varname); | |
4ba9941c | 190 | $userid = clean_param($gradeinfo[1], PARAM_INT); |
191 | $itemid = clean_param($gradeinfo[2], PARAM_INT); | |
192 | ||
29a5680e | 193 | $oldvalue = $data->{'old'.$varname}; |
194 | ||
195 | // was change requested? | |
99ccfda8 | 196 | if ($oldvalue == $postedvalue) { // string comparison |
29a5680e | 197 | continue; |
198 | } | |
199 | ||
319770d7 | 200 | if (!$gradeitem = grade_item::fetch(array('id'=>$itemid, 'courseid'=>$this->courseid))) { // we must verify course id here! |
14398fd6 | 201 | print_error('invalidgradeitmeid'); |
4ba9941c | 202 | } |
203 | ||
204 | // Pre-process grade | |
319770d7 | 205 | if ($datatype == 'grade') { |
4256a134 | 206 | $feedback = false; |
207 | $feedbackformat = false; | |
319770d7 | 208 | if ($gradeitem->gradetype == GRADE_TYPE_SCALE) { |
4ba9941c | 209 | if ($postedvalue == -1) { // -1 means no grade |
210 | $finalgrade = null; | |
211 | } else { | |
29a5680e | 212 | $finalgrade = $postedvalue; |
4ba9941c | 213 | } |
214 | } else { | |
76317c73 | 215 | $finalgrade = unformat_float($postedvalue); |
4ba9941c | 216 | } |
79eabc2a | 217 | |
0a2c8485 | 218 | $errorstr = ''; |
379ea949 | 219 | // Warn if the grade is out of bounds. |
220 | if (is_null($finalgrade)) { | |
221 | // ok | |
653a8648 | 222 | } else { |
319770d7 | 223 | $bounded = $gradeitem->bounded_grade($finalgrade); |
653a8648 | 224 | if ($bounded > $finalgrade) { |
0a2c8485 | 225 | $errorstr = 'lessthanmin'; |
653a8648 | 226 | } else if ($bounded < $finalgrade) { |
78946b9b PS |
227 | $errorstr = 'morethanmax'; |
228 | } | |
0a2c8485 | 229 | } |
230 | if ($errorstr) { | |
5c75a0a3 | 231 | $user = $DB->get_record('user', array('id' => $userid), 'id, firstname, lastname'); |
ace9051c | 232 | $gradestr = new stdClass(); |
0a2c8485 | 233 | $gradestr->username = fullname($user); |
319770d7 | 234 | $gradestr->itemname = $gradeitem->get_name(); |
9d35e66e | 235 | $warnings[] = get_string($errorstr, 'grades', $gradestr); |
0a2c8485 | 236 | } |
237 | ||
319770d7 | 238 | } else if ($datatype == 'feedback') { |
4256a134 | 239 | $finalgrade = false; |
79eabc2a | 240 | $trimmed = trim($postedvalue); |
241 | if (empty($trimmed)) { | |
29a5680e | 242 | $feedback = NULL; |
e1d2692a | 243 | } else { |
e6fec85d | 244 | $feedback = $postedvalue; |
e1d2692a | 245 | } |
4ba9941c | 246 | } |
4ba9941c | 247 | |
30ebb74f | 248 | // group access control |
249 | if ($separategroups) { | |
250 | // note: we can not use $this->currentgroup because it would fail badly | |
251 | // when having two browser windows each with different group | |
252 | $sharinggroup = false; | |
253 | foreach($mygroups as $groupid) { | |
254 | if (groups_is_member($groupid, $userid)) { | |
255 | $sharinggroup = true; | |
256 | break; | |
257 | } | |
258 | } | |
259 | if (!$sharinggroup) { | |
6ef4878b | 260 | // either group membership changed or somebody is hacking grades of other group |
30ebb74f | 261 | $warnings[] = get_string('errorsavegrade', 'grades'); |
262 | continue; | |
263 | } | |
264 | } | |
265 | ||
319770d7 | 266 | $gradeitem->update_final_grade($userid, $finalgrade, 'gradebook', $feedback, FORMAT_MOODLE); |
4ba9941c | 267 | } |
268 | ||
75674470 | 269 | return $warnings; |
4ba9941c | 270 | } |
271 | ||
4ba9941c | 272 | |
273 | /** | |
274 | * Setting the sort order, this depends on last state | |
275 | * all this should be in the new table class that we might need to use | |
276 | * for displaying grades. | |
277 | */ | |
d24832f9 | 278 | private function setup_sortitemid() { |
63d6efa2 | 279 | |
280 | global $SESSION; | |
281 | ||
4ba9941c | 282 | if ($this->sortitemid) { |
283 | if (!isset($SESSION->gradeuserreport->sort)) { | |
a80112f0 | 284 | if ($this->sortitemid == 'firstname' || $this->sortitemid == 'lastname') { |
285 | $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; | |
286 | } else { | |
287 | $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC'; | |
288 | } | |
4ba9941c | 289 | } else { |
290 | // this is the first sort, i.e. by last name | |
291 | if (!isset($SESSION->gradeuserreport->sortitemid)) { | |
a80112f0 | 292 | if ($this->sortitemid == 'firstname' || $this->sortitemid == 'lastname') { |
d24832f9 | 293 | $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; |
a80112f0 | 294 | } else { |
295 | $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC'; | |
296 | } | |
4ba9941c | 297 | } else if ($SESSION->gradeuserreport->sortitemid == $this->sortitemid) { |
298 | // same as last sort | |
299 | if ($SESSION->gradeuserreport->sort == 'ASC') { | |
300 | $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC'; | |
301 | } else { | |
302 | $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; | |
303 | } | |
304 | } else { | |
a80112f0 | 305 | if ($this->sortitemid == 'firstname' || $this->sortitemid == 'lastname') { |
306 | $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; | |
307 | } else { | |
308 | $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC'; | |
309 | } | |
4ba9941c | 310 | } |
311 | } | |
312 | $SESSION->gradeuserreport->sortitemid = $this->sortitemid; | |
313 | } else { | |
314 | // not requesting sort, use last setting (for paging) | |
315 | ||
316 | if (isset($SESSION->gradeuserreport->sortitemid)) { | |
317 | $this->sortitemid = $SESSION->gradeuserreport->sortitemid; | |
d20737e8 | 318 | }else{ |
319 | $this->sortitemid = 'lastname'; | |
4ba9941c | 320 | } |
d20737e8 | 321 | |
4ba9941c | 322 | if (isset($SESSION->gradeuserreport->sort)) { |
323 | $this->sortorder = $SESSION->gradeuserreport->sort; | |
324 | } else { | |
325 | $this->sortorder = 'ASC'; | |
326 | } | |
327 | } | |
328 | } | |
329 | ||
4ba9941c | 330 | /** |
b50371da | 331 | * pulls out the userids of the users to be display, and sorts them |
4ba9941c | 332 | */ |
d24832f9 | 333 | public function load_users() { |
334 | global $CFG, $DB; | |
b50371da | 335 | |
ef8f084c | 336 | //limit to users with a gradeable role |
337 | list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0'); | |
4ba9941c | 338 | |
ef8f084c | 339 | //limit to users with an active enrollment |
340 | list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context); | |
341 | ||
342 | //fields we need from the user table | |
25081b95 | 343 | $userfields = user_picture::fields('u', array('idnumber')); |
ef8f084c | 344 | |
345 | //if the user has clicked one of the sort asc/desc arrows | |
4ba9941c | 346 | if (is_numeric($this->sortitemid)) { |
ef8f084c | 347 | $params = array_merge(array('gitemid'=>$this->sortitemid), $gradebookrolesparams, $this->groupwheresql_params, $enrolledparams); |
b50371da | 348 | // the MAX() magic is required in order to please PG |
349 | $sort = "MAX(g.finalgrade) $this->sortorder"; | |
09d0ef21 | 350 | |
ef8f084c | 351 | $sql = "SELECT $userfields |
f2491a11 | 352 | FROM {user} u |
25081b95 SH |
353 | JOIN ($enrolledsql) je |
354 | ON je.id = u.id | |
355 | JOIN {role_assignments} ra | |
356 | ON ra.userid = u.id | |
357 | $this->groupsql | |
358 | LEFT JOIN {grade_grades} g | |
359 | ON (g.userid = u.id AND g.itemid = :gitemid) | |
f2491a11 | 360 | WHERE ra.roleid $gradebookrolessql |
361 | AND u.deleted = 0 | |
362 | AND ra.contextid ".get_related_contexts_string($this->context)." | |
363 | $this->groupwheresql | |
364 | GROUP BY $userfields | |
365 | ORDER BY $sort"; | |
09d0ef21 | 366 | |
4ba9941c | 367 | } else { |
09d0ef21 | 368 | switch($this->sortitemid) { |
369 | case 'lastname': | |
370 | $sort = "u.lastname $this->sortorder, u.firstname $this->sortorder"; break; | |
371 | case 'firstname': | |
372 | $sort = "u.firstname $this->sortorder, u.lastname $this->sortorder"; break; | |
373 | case 'idnumber': | |
374 | default: | |
375 | $sort = "u.idnumber $this->sortorder"; break; | |
c421ad4b | 376 | } |
4ba9941c | 377 | |
ef8f084c | 378 | $params = array_merge($gradebookrolesparams, $this->groupwheresql_params, $enrolledparams); |
d5afd6c9 | 379 | |
3a11c09f | 380 | $sql = "SELECT DISTINCT $userfields |
f2491a11 | 381 | FROM {user} u |
25081b95 SH |
382 | JOIN ($enrolledsql) je |
383 | ON je.id = u.id | |
384 | JOIN {role_assignments} ra | |
385 | ON u.id = ra.userid | |
f2491a11 | 386 | $this->groupsql |
25081b95 | 387 | WHERE ra.roleid $gradebookrolessql |
f2491a11 | 388 | AND u.deleted = 0 |
389 | AND ra.contextid ".get_related_contexts_string($this->context)." | |
390 | $this->groupwheresql | |
391 | ORDER BY $sort"; | |
4ba9941c | 392 | } |
393 | ||
09d0ef21 | 394 | |
d24832f9 | 395 | $this->users = $DB->get_records_sql($sql, $params, $this->get_pref('studentsperpage') * $this->page, $this->get_pref('studentsperpage')); |
09d0ef21 | 396 | |
4ba9941c | 397 | if (empty($this->users)) { |
398 | $this->userselect = ''; | |
399 | $this->users = array(); | |
0a695c36 | 400 | $this->userselect_params = array(); |
4ba9941c | 401 | } else { |
b50371da | 402 | list($usql, $params) = $DB->get_in_or_equal(array_keys($this->users), SQL_PARAMS_NAMED, 'usid0'); |
d24832f9 | 403 | $this->userselect = "AND g.userid $usql"; |
404 | $this->userselect_params = $params; | |
4ba9941c | 405 | } |
406 | ||
407 | return $this->users; | |
408 | } | |
409 | ||
4ba9941c | 410 | /** |
411 | * we supply the userids in this query, and get all the grades | |
412 | * pulls out all the grades, this does not need to worry about paging | |
413 | */ | |
d24832f9 | 414 | public function load_final_grades() { |
415 | global $CFG, $DB; | |
4ba9941c | 416 | |
6ef4878b | 417 | // please note that we must fetch all grade_grades fields if we want to construct grade_grade object from it! |
b50371da | 418 | $params = array_merge(array('courseid'=>$this->courseid), $this->userselect_params); |
b89a70ce | 419 | $sql = "SELECT g.* |
d24832f9 | 420 | FROM {grade_items} gi, |
421 | {grade_grades} g | |
b50371da | 422 | WHERE g.itemid = gi.id AND gi.courseid = :courseid {$this->userselect}"; |
b89a70ce | 423 | |
424 | $userids = array_keys($this->users); | |
4ba9941c | 425 | |
d297269d | 426 | |
d24832f9 | 427 | if ($grades = $DB->get_records_sql($sql, $params)) { |
b89a70ce | 428 | foreach ($grades as $graderec) { |
d24832f9 | 429 | if (in_array($graderec->userid, $userids) and array_key_exists($graderec->itemid, $this->gtree->get_items())) { // some items may not be present!! |
b89a70ce | 430 | $this->grades[$graderec->userid][$graderec->itemid] = new grade_grade($graderec, false); |
d24832f9 | 431 | $this->grades[$graderec->userid][$graderec->itemid]->grade_item =& $this->gtree->get_item($graderec->itemid); // db caching |
b89a70ce | 432 | } |
433 | } | |
434 | } | |
435 | ||
436 | // prefil grades that do not exist yet | |
437 | foreach ($userids as $userid) { | |
d24832f9 | 438 | foreach ($this->gtree->get_items() as $itemid=>$unused) { |
b89a70ce | 439 | if (!isset($this->grades[$userid][$itemid])) { |
440 | $this->grades[$userid][$itemid] = new grade_grade(); | |
478f4322 | 441 | $this->grades[$userid][$itemid]->itemid = $itemid; |
3b34f698 | 442 | $this->grades[$userid][$itemid]->userid = $userid; |
d24832f9 | 443 | $this->grades[$userid][$itemid]->grade_item =& $this->gtree->get_item($itemid); // db caching |
b89a70ce | 444 | } |
4ba9941c | 445 | } |
446 | } | |
447 | } | |
448 | ||
449 | /** | |
450 | * Builds and returns a div with on/off toggles. | |
451 | * @return string HTML code | |
452 | */ | |
d24832f9 | 453 | public function get_toggles_html() { |
319770d7 | 454 | global $CFG, $USER, $COURSE, $OUTPUT; |
aea4df41 | 455 | |
319770d7 | 456 | $html = ''; |
2cc773f5 | 457 | if ($USER->gradeediting[$this->courseid]) { |
458 | if (has_capability('moodle/grade:manage', $this->context) or has_capability('moodle/grade:hide', $this->context)) { | |
319770d7 | 459 | $html .= $this->print_toggle('eyecons'); |
2cc773f5 | 460 | } |
461 | if (has_capability('moodle/grade:manage', $this->context) | |
462 | or has_capability('moodle/grade:lock', $this->context) | |
463 | or has_capability('moodle/grade:unlock', $this->context)) { | |
319770d7 | 464 | $html .= $this->print_toggle('locks'); |
2cc773f5 | 465 | } |
2ca093fa | 466 | if (has_capability('moodle/grade:manage', $this->context)) { |
319770d7 | 467 | $html .= $this->print_toggle('quickfeedback'); |
2ca093fa | 468 | } |
469 | ||
2cc773f5 | 470 | if (has_capability('moodle/grade:manage', $this->context)) { |
319770d7 | 471 | $html .= $this->print_toggle('calculations'); |
2cc773f5 | 472 | } |
4ba9941c | 473 | } |
474 | ||
57068674 | 475 | if ($this->canviewhidden) { |
319770d7 | 476 | $html .= $this->print_toggle('averages'); |
57068674 | 477 | } |
aae94377 | 478 | |
319770d7 | 479 | $html .= $this->print_toggle('ranges'); |
aea4df41 | 480 | if (!empty($CFG->enableoutcomes)) { |
319770d7 | 481 | $html .= $this->print_toggle('nooutcomes'); |
aea4df41 | 482 | } |
319770d7 | 483 | |
484 | return $OUTPUT->container($html, 'grade-report-toggles'); | |
4ba9941c | 485 | } |
486 | ||
487 | /** | |
488 | * Shortcut function for printing the grader report toggles. | |
489 | * @param string $type The type of toggle | |
490 | * @param bool $return Whether to return the HTML string rather than printing it | |
491 | * @return void | |
492 | */ | |
319770d7 | 493 | public function print_toggle($type) { |
e63f88c9 | 494 | global $CFG, $OUTPUT; |
4ba9941c | 495 | |
319770d7 | 496 | $icons = array('eyecons' => 't/hide', |
497 | 'calculations' => 't/calc', | |
498 | 'locks' => 't/lock', | |
499 | 'averages' => 't/mean', | |
500 | 'quickfeedback' => 't/feedback', | |
501 | 'nooutcomes' => 't/outcomes'); | |
4ba9941c | 502 | |
319770d7 | 503 | $prefname = 'grade_report_show' . $type; |
aea4df41 | 504 | |
319770d7 | 505 | if (array_key_exists($prefname, $CFG)) { |
506 | $showpref = get_user_preferences($prefname, $CFG->$prefname); | |
aea4df41 | 507 | } else { |
319770d7 | 508 | $showpref = get_user_preferences($prefname); |
aea4df41 | 509 | } |
4ba9941c | 510 | |
388234f4 | 511 | $strshow = $this->get_lang_string('show' . $type, 'grades'); |
512 | $strhide = $this->get_lang_string('hide' . $type, 'grades'); | |
4ba9941c | 513 | |
319770d7 | 514 | $showhide = 'show'; |
515 | $toggleaction = 1; | |
4ba9941c | 516 | |
319770d7 | 517 | if ($showpref) { |
518 | $showhide = 'hide'; | |
519 | $toggleaction = 0; | |
4ba9941c | 520 | } |
521 | ||
522 | if (array_key_exists($type, $icons)) { | |
319770d7 | 523 | $imagename = $icons[$type]; |
4ba9941c | 524 | } else { |
b11681e0 | 525 | $imagename = "t/$type"; |
4ba9941c | 526 | } |
527 | ||
319770d7 | 528 | $string = ${'str' . $showhide}; |
4ba9941c | 529 | |
e1513ca9 | 530 | $url = new moodle_url($this->baseurl, array('toggle' => $toggleaction, 'toggle_type' => $type)); |
4ba9941c | 531 | |
c63923bd | 532 | $retval = $OUTPUT->container($OUTPUT->action_icon($url, new pix_icon($imagename, $string))); // TODO: this container looks wrong here |
4ba9941c | 533 | |
319770d7 | 534 | return $retval; |
4ba9941c | 535 | } |
536 | ||
537 | /** | |
6c096a49 NC |
538 | * Builds and returns the rows that will make up the left part of the grader report |
539 | * This consists of student names and icons, links to user reports and id numbers, as well | |
540 | * as header cells for these columns. It also includes the fillers required for the | |
541 | * categories displayed on the right side of the report. | |
542 | * @return array Array of html_table_row objects | |
4ba9941c | 543 | */ |
6c096a49 | 544 | public function get_left_rows() { |
319770d7 | 545 | global $CFG, $USER, $OUTPUT; |
4ba9941c | 546 | |
6c096a49 NC |
547 | $rows = array(); |
548 | ||
549 | $showuserimage = $this->get_pref('showuserimage'); | |
550 | $showuseridnumber = $this->get_pref('showuseridnumber'); | |
795b745a | 551 | $fixedstudents = $this->is_fixed_students(); |
203b7e2e | 552 | |
6c096a49 NC |
553 | $strfeedback = $this->get_lang_string("feedback"); |
554 | $strgrade = $this->get_lang_string('grade'); | |
203b7e2e | 555 | |
6c096a49 NC |
556 | $arrows = $this->get_sort_arrows(); |
557 | ||
558 | $colspan = 1; | |
559 | ||
3e7ca6b3 | 560 | if (has_capability('gradereport/'.$CFG->grade_profilereport.':view', $this->context)) { |
6c096a49 NC |
561 | $colspan++; |
562 | } | |
563 | ||
564 | if ($showuseridnumber) { | |
565 | $colspan++; | |
566 | } | |
567 | ||
568 | $levels = count($this->gtree->levels) - 1; | |
569 | ||
570 | for ($i = 0; $i < $levels; $i++) { | |
571 | $fillercell = new html_table_cell(); | |
16be8974 | 572 | $fillercell->attributes['class'] = 'fixedcolumn cell topleft'; |
6c096a49 NC |
573 | $fillercell->text = ' '; |
574 | $fillercell->colspan = $colspan; | |
8cea545e | 575 | $row = new html_table_row(array($fillercell)); |
6c096a49 NC |
576 | $rows[] = $row; |
577 | } | |
578 | ||
579 | $headerrow = new html_table_row(); | |
16be8974 | 580 | $headerrow->attributes['class'] = 'heading'; |
6c096a49 NC |
581 | |
582 | $studentheader = new html_table_cell(); | |
16be8974 | 583 | $studentheader->attributes['class'] = 'header'; |
6c096a49 NC |
584 | $studentheader->scope = 'col'; |
585 | $studentheader->header = true; | |
586 | $studentheader->id = 'studentheader'; | |
3e7ca6b3 | 587 | if (has_capability('gradereport/'.$CFG->grade_profilereport.':view', $this->context)) { |
6c096a49 NC |
588 | $studentheader->colspan = 2; |
589 | } | |
590 | $studentheader->text = $arrows['studentname']; | |
591 | ||
592 | $headerrow->cells[] = $studentheader; | |
593 | ||
594 | if ($showuseridnumber) { | |
0f4c64b7 PS |
595 | // TODO: weird, this is not used anywhere |
596 | $sortidnumberlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'idnumber')), get_string('idnumber')); | |
6c096a49 NC |
597 | |
598 | $idnumberheader = new html_table_cell(); | |
16be8974 | 599 | $idnumberheader->attributes['class'] = 'header useridnumber'; |
6c096a49 NC |
600 | $idnumberheader->scope = 'col'; |
601 | $idnumberheader->header = true; | |
602 | $idnumberheader->text = $arrows['idnumber']; | |
603 | ||
604 | $headerrow->cells[] = $idnumberheader; | |
605 | } | |
606 | ||
607 | $rows[] = $headerrow; | |
608 | ||
609 | $rows = $this->get_left_icons_row($rows, $colspan); | |
610 | ||
611 | $rowclasses = array('even', 'odd'); | |
612 | ||
613 | foreach ($this->users as $userid => $user) { | |
614 | $userrow = new html_table_row(); | |
fe213365 | 615 | $userrow->id = 'fixed_user_'.$userid; |
abfe77ea | 616 | $userrow->attributes['class'] = 'r'.$this->rowcount++.' '.$rowclasses[$this->rowcount % 2]; |
6c096a49 NC |
617 | |
618 | $usercell = new html_table_cell(); | |
16be8974 | 619 | $usercell->attributes['class'] = 'user'; |
6c096a49 NC |
620 | $usercell->header = true; |
621 | $usercell->scope = 'row'; | |
6c096a49 NC |
622 | |
623 | if ($showuserimage) { | |
812dbaf7 | 624 | $usercell->text = $OUTPUT->container($OUTPUT->user_picture($user), 'userpic'); |
203b7e2e | 625 | } |
626 | ||
a6855934 | 627 | $usercell->text .= html_writer::link(new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $this->course->id)), fullname($user)); |
6c096a49 NC |
628 | |
629 | $userrow->cells[] = $usercell; | |
630 | ||
3e7ca6b3 | 631 | if (has_capability('gradereport/'.$CFG->grade_profilereport.':view', $this->context)) { |
6c096a49 | 632 | $userreportcell = new html_table_cell(); |
16be8974 | 633 | $userreportcell->attributes['class'] = 'userreport'; |
6c096a49 | 634 | $userreportcell->header = true; |
ace9051c | 635 | $a = new stdClass(); |
6c096a49 NC |
636 | $a->user = fullname($user); |
637 | $strgradesforuser = get_string('gradesforuser', 'grades', $a); | |
a6855934 | 638 | $url = new moodle_url('/grade/report/'.$CFG->grade_profilereport.'/index.php', array('userid' => $user->id, 'id' => $this->course->id)); |
c63923bd | 639 | $userreportcell->text = $OUTPUT->action_icon($url, new pix_icon('t/grades', $strgradesforuser)); |
6c096a49 | 640 | $userrow->cells[] = $userreportcell; |
203b7e2e | 641 | } |
642 | ||
6c096a49 NC |
643 | if ($showuseridnumber) { |
644 | $idnumbercell = new html_table_cell(); | |
16be8974 | 645 | $idnumbercell->attributes['class'] = 'header useridnumber'; |
6c096a49 NC |
646 | $idnumbercell->header = true; |
647 | $idnumbercell->scope = 'row'; | |
82ec58ff | 648 | $idnumbercell->text = $user->idnumber; |
6c096a49 NC |
649 | $userrow->cells[] = $idnumbercell; |
650 | } | |
651 | ||
652 | $rows[] = $userrow; | |
203b7e2e | 653 | } |
4ba9941c | 654 | |
6c096a49 NC |
655 | $rows = $this->get_left_range_row($rows, $colspan); |
656 | $rows = $this->get_left_avg_row($rows, $colspan, true); | |
657 | $rows = $this->get_left_avg_row($rows, $colspan); | |
4ba9941c | 658 | |
6c096a49 NC |
659 | return $rows; |
660 | } | |
661 | ||
662 | /** | |
663 | * Builds and returns the rows that will make up the right part of the grader report | |
664 | * @return array Array of html_table_row objects | |
665 | */ | |
666 | public function get_right_rows() { | |
fe213365 | 667 | global $CFG, $USER, $OUTPUT, $DB, $PAGE; |
4ba9941c | 668 | |
6c096a49 NC |
669 | $rows = array(); |
670 | $this->rowcount = 0; | |
671 | $numrows = count($this->gtree->get_levels()); | |
672 | $numusers = count($this->users); | |
673 | $gradetabindex = 1; | |
319770d7 | 674 | $columnstounset = array(); |
6c096a49 | 675 | $strgrade = $this->get_lang_string('grade'); |
fe213365 | 676 | $strfeedback = $this->get_lang_string("feedback"); |
6c096a49 | 677 | $arrows = $this->get_sort_arrows(); |
cb7fe7b4 | 678 | |
fe213365 SH |
679 | $jsarguments = array( |
680 | 'id' => '#fixed_column', | |
681 | 'cfg' => array('ajaxenabled'=>false), | |
682 | 'items' => array(), | |
683 | 'users' => array(), | |
684 | 'feedback' => array() | |
685 | ); | |
686 | $jsscales = array(); | |
687 | ||
d24832f9 | 688 | foreach ($this->gtree->get_levels() as $key=>$row) { |
4ba9941c | 689 | if ($key == 0) { |
cb7fe7b4 | 690 | // do not display course grade category |
4ba9941c | 691 | // continue; |
692 | } | |
693 | ||
6c096a49 | 694 | $headingrow = new html_table_row(); |
16be8974 | 695 | $headingrow->attributes['class'] = 'heading_name_row'; |
4ba9941c | 696 | |
cb7fe7b4 | 697 | foreach ($row as $columnkey => $element) { |
319770d7 | 698 | $sortlink = clone($this->baseurl); |
2e3987a9 | 699 | if (isset($element['object']->id)) { |
319770d7 | 700 | $sortlink->param('sortitemid', $element['object']->id); |
2e3987a9 | 701 | } |
702 | ||
cb7fe7b4 | 703 | $eid = $element['eid']; |
704 | $object = $element['object']; | |
705 | $type = $element['type']; | |
438a5aa9 | 706 | $categorystate = @$element['categorystate']; |
8c5a416e | 707 | |
4ba9941c | 708 | if (!empty($element['colspan'])) { |
6c096a49 | 709 | $colspan = $element['colspan']; |
4ba9941c | 710 | } else { |
6c096a49 | 711 | $colspan = 1; |
4ba9941c | 712 | } |
713 | ||
714 | if (!empty($element['depth'])) { | |
6c096a49 | 715 | $catlevel = 'catlevel'.$element['depth']; |
4ba9941c | 716 | } else { |
717 | $catlevel = ''; | |
718 | } | |
719 | ||
cb7fe7b4 | 720 | // Element is a filler |
4ba9941c | 721 | if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') { |
6c096a49 | 722 | $fillercell = new html_table_cell(); |
16be8974 | 723 | $fillercell->attributes['class'] = $type . ' ' . $catlevel; |
6c096a49 NC |
724 | $fillercell->colspan = $colspan; |
725 | $fillercell->text = ' '; | |
726 | $fillercell->header = true; | |
727 | $fillercell->scope = 'col'; | |
728 | $headingrow->cells[] = $fillercell; | |
cb7fe7b4 | 729 | } |
730 | // Element is a category | |
4faf5f99 | 731 | else if ($type == 'category') { |
6c096a49 | 732 | $categorycell = new html_table_cell(); |
16be8974 | 733 | $categorycell->attributes['class'] = 'category ' . $catlevel; |
6c096a49 NC |
734 | $categorycell->colspan = $colspan; |
735 | $categorycell->text = shorten_text($element['object']->get_name()); | |
736 | $categorycell->text .= $this->get_collapsing_icon($element); | |
737 | $categorycell->header = true; | |
738 | $categorycell->scope = 'col'; | |
4ba9941c | 739 | |
740 | // Print icons | |
2cc773f5 | 741 | if ($USER->gradeediting[$this->courseid]) { |
6c096a49 | 742 | $categorycell->text .= $this->get_icons($element); |
4ba9941c | 743 | } |
744 | ||
6c096a49 | 745 | $headingrow->cells[] = $categorycell; |
cb7fe7b4 | 746 | } |
747 | // Element is a grade_item | |
4faf5f99 | 748 | else { |
7bac3777 AD |
749 | //$itemmodule = $element['object']->itemmodule; |
750 | //$iteminstance = $element['object']->iteminstance; | |
2e3987a9 | 751 | |
4ba9941c | 752 | if ($element['object']->id == $this->sortitemid) { |
753 | if ($this->sortorder == 'ASC') { | |
319770d7 | 754 | $arrow = $this->get_sort_arrow('up', $sortlink); |
4ba9941c | 755 | } else { |
319770d7 | 756 | $arrow = $this->get_sort_arrow('down', $sortlink); |
4ba9941c | 757 | } |
758 | } else { | |
319770d7 | 759 | $arrow = $this->get_sort_arrow('move', $sortlink); |
4ba9941c | 760 | } |
761 | ||
6c096a49 NC |
762 | $headerlink = $this->gtree->get_element_header($element, true, $this->get_pref('showactivityicons'), false); |
763 | ||
764 | $itemcell = new html_table_cell(); | |
16be8974 | 765 | $itemcell->attributes['class'] = $type . ' ' . $catlevel . 'highlightable'; |
6c096a49 | 766 | |
4ba9941c | 767 | if ($element['object']->is_hidden()) { |
16be8974 | 768 | $itemcell->attributes['class'] .= ' hidden'; |
4ba9941c | 769 | } |
770 | ||
6c096a49 NC |
771 | $itemcell->colspan = $colspan; |
772 | $itemcell->text = shorten_text($headerlink) . $arrow; | |
773 | $itemcell->header = true; | |
774 | $itemcell->scope = 'col'; | |
6c096a49 NC |
775 | |
776 | $headingrow->cells[] = $itemcell; | |
4ba9941c | 777 | } |
4ba9941c | 778 | } |
6c096a49 | 779 | $rows[] = $headingrow; |
4ba9941c | 780 | } |
4ba9941c | 781 | |
6c096a49 | 782 | $rows = $this->get_right_icons_row($rows); |
4ba9941c | 783 | |
388234f4 | 784 | // Preload scale objects for items with a scaleid |
319770d7 | 785 | $scaleslist = array(); |
c0c1e7c2 | 786 | $tabindices = array(); |
d297269d | 787 | |
fe213365 SH |
788 | foreach ($this->gtree->get_items() as $itemid=>$item) { |
789 | $scale = null; | |
388234f4 | 790 | if (!empty($item->scaleid)) { |
319770d7 | 791 | $scaleslist[] = $item->scaleid; |
fe213365 SH |
792 | $jsarguments['items'][$itemid] = array('id'=>$itemid, 'name'=>$item->get_name(true), 'type'=>'scale', 'scale'=>$item->scaleid, 'decimals'=>$item->get_decimals()); |
793 | } else { | |
794 | $jsarguments['items'][$itemid] = array('id'=>$itemid, 'name'=>$item->get_name(true), 'type'=>'value', 'scale'=>false, 'decimals'=>$item->get_decimals()); | |
388234f4 | 795 | } |
c0c1e7c2 | 796 | $tabindices[$item->id]['grade'] = $gradetabindex; |
797 | $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers; | |
798 | $gradetabindex += $numusers * 2; | |
388234f4 | 799 | } |
319770d7 | 800 | $scalesarray = array(); |
388234f4 | 801 | |
319770d7 | 802 | if (!empty($scaleslist)) { |
803 | $scalesarray = $DB->get_records_list('scale', 'id', $scaleslist); | |
388234f4 | 804 | } |
fe213365 | 805 | $jsscales = $scalesarray; |
d24832f9 | 806 | |
6c096a49 | 807 | $rowclasses = array('even', 'odd'); |
a47d38f2 | 808 | |
4ba9941c | 809 | foreach ($this->users as $userid => $user) { |
b89a70ce | 810 | |
57068674 | 811 | if ($this->canviewhidden) { |
d297269d | 812 | $altered = array(); |
813 | $unknown = array(); | |
b89a70ce | 814 | } else { |
319770d7 | 815 | $hidingaffected = grade_grade::get_hiding_affected($this->grades[$userid], $this->gtree->get_items()); |
816 | $altered = $hidingaffected['altered']; | |
817 | $unknown = $hidingaffected['unknown']; | |
818 | unset($hidingaffected); | |
b89a70ce | 819 | } |
820 | ||
bb9b58a6 | 821 | |
6c096a49 | 822 | $itemrow = new html_table_row(); |
fe213365 | 823 | $itemrow->id = 'user_'.$userid; |
16be8974 | 824 | $itemrow->attributes['class'] = $rowclasses[$this->rowcount % 2]; |
9d35e66e | 825 | |
fe213365 SH |
826 | $jsarguments['users'][$userid] = fullname($user); |
827 | ||
dc482cfa | 828 | foreach ($this->gtree->items as $itemid=>$unused) { |
829 | $item =& $this->gtree->items[$itemid]; | |
d297269d | 830 | $grade = $this->grades[$userid][$item->id]; |
b89a70ce | 831 | |
6c096a49 NC |
832 | $itemcell = new html_table_cell(); |
833 | ||
fe213365 SH |
834 | $itemcell->id = 'u'.$userid.'i'.$itemid; |
835 | ||
e50ce569 | 836 | // Get the decimal points preference for this item |
31a6c06c | 837 | $decimalpoints = $item->get_decimals(); |
4ba9941c | 838 | |
d297269d | 839 | if (in_array($itemid, $unknown)) { |
840 | $gradeval = null; | |
841 | } else if (array_key_exists($itemid, $altered)) { | |
842 | $gradeval = $altered[$itemid]; | |
843 | } else { | |
844 | $gradeval = $grade->finalgrade; | |
845 | } | |
4ba9941c | 846 | |
00374cc5 | 847 | // MDL-11274 |
848 | // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden' | |
57068674 | 849 | if (!$this->canviewhidden and $grade->is_hidden()) { |
6cc3e350 | 850 | if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$item->is_course_item()) { |
d297269d | 851 | // 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 |
26acc814 | 852 | $itemcell->text = html_writer::tag('span', userdate($grade->get_datesubmitted(),get_string('strftimedatetimeshort')), array('class'=>'datesubmitted')); |
bb49f77b | 853 | } else { |
6c096a49 | 854 | $itemcell->text = '-'; |
00374cc5 | 855 | } |
6c096a49 | 856 | $itemrow->cells[] = $itemcell; |
a5b8be62 | 857 | continue; |
00374cc5 | 858 | } |
859 | ||
2cc773f5 | 860 | // emulate grade element |
d3c3da1b | 861 | $eid = $this->gtree->get_grade_eid($grade); |
862 | $element = array('eid'=>$eid, 'object'=>$grade, 'type'=>'grade'); | |
2cc773f5 | 863 | |
16be8974 | 864 | $itemcell->attributes['class'] .= ' grade'; |
dff9d94d | 865 | if ($item->is_category_item()) { |
16be8974 | 866 | $itemcell->attributes['class'] .= ' cat'; |
b89a70ce | 867 | } |
dff9d94d | 868 | if ($item->is_course_item()) { |
16be8974 | 869 | $itemcell->attributes['class'] .= ' course'; |
b89a70ce | 870 | } |
4ba9941c | 871 | if ($grade->is_overridden()) { |
16be8974 | 872 | $itemcell->attributes['class'] .= ' overridden'; |
4ba9941c | 873 | } |
85db09fb | 874 | |
5ebce7bb | 875 | if ($grade->is_excluded()) { |
16be8974 | 876 | // $itemcell->attributes['class'] .= ' excluded'; |
5ebce7bb | 877 | } |
4ba9941c | 878 | |
fe213365 | 879 | if (!empty($grade->feedback)) { |
92ff6236 | 880 | //should we be truncating feedback? ie $short_feedback = shorten_text($feedback, $this->feedback_trunc_length); |
fe213365 | 881 | $jsarguments['feedback'][] = array('user'=>$userid, 'item'=>$itemid, 'content'=>wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >')); |
653a8648 | 882 | } |
883 | ||
23207a1a | 884 | if ($grade->is_excluded()) { |
26acc814 | 885 | $itemcell->text .= html_writer::tag('span', get_string('excluded', 'grades'), array('class'=>'excludedfloater')); |
23207a1a | 886 | } |
887 | ||
4ba9941c | 888 | // Do not show any icons if no grade (no record in DB to match) |
d3c3da1b | 889 | if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) { |
6c096a49 | 890 | $itemcell->text .= $this->get_icons($element); |
4ba9941c | 891 | } |
892 | ||
80fb1cf9 | 893 | $hidden = ''; |
894 | if ($grade->is_hidden()) { | |
895 | $hidden = ' hidden '; | |
896 | } | |
6cc3e350 | 897 | |
d24832f9 | 898 | $gradepass = ' gradefail '; |
46e6df89 | 899 | if ($grade->is_passed($item)) { |
900 | $gradepass = ' gradepass '; | |
e6477988 | 901 | } elseif (is_null($grade->is_passed($item))) { |
902 | $gradepass = ''; | |
46e6df89 | 903 | } |
904 | ||
6ef4878b | 905 | // if in editing mode, we need to print either a text box |
4ba9941c | 906 | // or a drop down (for scales) |
4ba9941c | 907 | // grades in item of type grade category or course are not directly editable |
d14ae855 | 908 | if ($item->needsupdate) { |
26acc814 | 909 | $itemcell->text .= html_writer::tag('span', get_string('error'), array('class'=>"gradingerror$hidden")); |
d14ae855 | 910 | |
2cc773f5 | 911 | } else if ($USER->gradeediting[$this->courseid]) { |
4ba9941c | 912 | |
319770d7 | 913 | if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { |
914 | $scale = $scalesarray[$item->scaleid]; | |
99ccfda8 | 915 | $gradeval = (int)$gradeval; // scales use only integers |
388234f4 | 916 | $scales = explode(",", $scale->scale); |
917 | // reindex because scale is off 1 | |
9d35e66e | 918 | |
914ea002 | 919 | // MDL-12104 some previous scales might have taken up part of the array |
920 | // so this needs to be reset | |
d48ebf97 | 921 | $scaleopt = array(); |
388234f4 | 922 | $i = 0; |
923 | foreach ($scales as $scaleoption) { | |
924 | $i++; | |
925 | $scaleopt[$i] = $scaleoption; | |
926 | } | |
927 | ||
928 | if ($this->get_pref('quickgrading') and $grade->is_editable()) { | |
0658afc9 | 929 | $oldval = empty($gradeval) ? -1 : $gradeval; |
930 | if (empty($item->outcomeid)) { | |
d4795a07 | 931 | $nogradestr = $this->get_lang_string('nograde'); |
0658afc9 | 932 | } else { |
d4795a07 | 933 | $nogradestr = $this->get_lang_string('nooutcome', 'grades'); |
0658afc9 | 934 | } |
fe213365 SH |
935 | $itemcell->text .= '<input type="hidden" id="oldgrade_'.$userid.'_'.$item->id.'" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$oldval.'"/>'; |
936 | $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id'=>'grade_'.$userid.'_'.$item->id); | |
064527d6 | 937 | $itemcell->text .= html_writer::select($scaleopt, 'grade_'.$userid.'_'.$item->id, $gradeval, array(-1=>$nogradestr), $attributes);; |
388234f4 | 938 | } elseif(!empty($scale)) { |
4ba9941c | 939 | $scales = explode(",", $scale->scale); |
4ba9941c | 940 | |
388234f4 | 941 | // invalid grade if gradeval < 1 |
99ccfda8 | 942 | if ($gradeval < 1) { |
26acc814 | 943 | $itemcell->text .= html_writer::tag('span', '-', array('class'=>"gradevalue$hidden$gradepass")); |
4ba9941c | 944 | } else { |
653a8648 | 945 | $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale |
26acc814 | 946 | $itemcell->text .= html_writer::tag('span', $scales[$gradeval-1], array('class'=>"gradevalue$hidden$gradepass")); |
4ba9941c | 947 | } |
388234f4 | 948 | } else { |
949 | // no such scale, throw error? | |
4ba9941c | 950 | } |
79eabc2a | 951 | |
bb384a8e | 952 | } else if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type |
936f1350 | 953 | if ($this->get_pref('quickgrading') and $grade->is_editable()) { |
76317c73 | 954 | $value = format_float($gradeval, $decimalpoints); |
fe213365 | 955 | $itemcell->text .= '<input type="hidden" id="oldgrade_'.$userid.'_'.$item->id.'" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$value.'" />'; |
6c096a49 | 956 | $itemcell->text .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] |
319770d7 | 957 | . '" type="text" class="text" title="'. $strgrade .'" name="grade_' |
fe213365 | 958 | .$userid.'_' .$item->id.'" id="grade_'.$userid.'_'.$item->id.'" value="'.$value.'" />'; |
4ba9941c | 959 | } else { |
26acc814 | 960 | $itemcell->text .= html_writer::tag('span', format_float($gradeval, $decimalpoints), array('class'=>"gradevalue$hidden$gradepass")); |
4ba9941c | 961 | } |
962 | } | |
963 | ||
964 | ||
965 | // If quickfeedback is on, print an input element | |
2ca093fa | 966 | if ($this->get_pref('showquickfeedback') and $grade->is_editable()) { |
dc482cfa | 967 | |
fe213365 SH |
968 | $itemcell->text .= '<input type="hidden" id="oldfeedback_'.$userid.'_'.$item->id.'" name="oldfeedback_'.$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />'; |
969 | $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'].'" id="feedback_'.$userid.'_'.$item->id | |
970 | . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_'.$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />'; | |
4ba9941c | 971 | } |
972 | ||
78a2d9f0 | 973 | } else { // Not editing |
41f22daa | 974 | $gradedisplaytype = $item->get_displaytype(); |
e50ce569 | 975 | |
fe213365 | 976 | if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { |
16be8974 | 977 | $itemcell->attributes['class'] .= ' grade_type_scale'; |
fe213365 | 978 | } else if ($item->gradetype != GRADE_TYPE_TEXT) { |
16be8974 | 979 | $itemcell->attributes['class'] .= ' grade_type_text'; |
fe213365 | 980 | } |
4ba9941c | 981 | |
6712973f AD |
982 | if ($this->get_pref('enableajax')) { |
983 | $itemcell->attributes['class'] .= ' clickable'; | |
984 | } | |
985 | ||
d14ae855 | 986 | if ($item->needsupdate) { |
26acc814 | 987 | $itemcell->text .= html_writer::tag('span', get_string('error'), array('class'=>"gradingerror$hidden$gradepass")); |
4ba9941c | 988 | } else { |
26acc814 | 989 | $itemcell->text .= html_writer::tag('span', grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null), array('class'=>"gradevalue$hidden$gradepass")); |
4ba9941c | 990 | } |
4ba9941c | 991 | } |
992 | ||
993 | if (!empty($this->gradeserror[$item->id][$userid])) { | |
6c096a49 | 994 | $itemcell->text .= $this->gradeserror[$item->id][$userid]; |
4ba9941c | 995 | } |
996 | ||
6c096a49 | 997 | $itemrow->cells[] = $itemcell; |
4ba9941c | 998 | } |
6c096a49 | 999 | $rows[] = $itemrow; |
4ba9941c | 1000 | } |
4ba9941c | 1001 | |
fe213365 SH |
1002 | if ($this->get_pref('enableajax')) { |
1003 | $jsarguments['cfg']['ajaxenabled'] = true; | |
1004 | $jsarguments['cfg']['scales'] = array(); | |
1005 | foreach ($jsscales as $scale) { | |
1006 | $jsarguments['cfg']['scales'][$scale->id] = explode(',',$scale->scale); | |
1007 | } | |
1008 | $jsarguments['cfg']['feedbacktrunclength'] = $this->feedback_trunc_length; | |
92ff6236 AD |
1009 | |
1010 | //feedbacks are now being stored in $jsarguments['feedback'] in get_right_rows() | |
1011 | //$jsarguments['cfg']['feedback'] = $this->feedbacks; | |
fe213365 SH |
1012 | } |
1013 | $jsarguments['cfg']['isediting'] = (bool)$USER->gradeediting[$this->courseid]; | |
1014 | $jsarguments['cfg']['courseid'] = $this->courseid; | |
1015 | $jsarguments['cfg']['studentsperpage'] = $this->get_pref('studentsperpage'); | |
1016 | $jsarguments['cfg']['showquickfeedback'] = (bool)$this->get_pref('showquickfeedback'); | |
1017 | ||
1018 | $module = array( | |
1019 | 'name' => 'gradereport_grader', | |
1020 | 'fullpath' => '/grade/report/grader/module.js', | |
1021 | 'requires' => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io', 'json-parse', 'overlay') | |
1022 | ); | |
1023 | $PAGE->requires->js_init_call('M.gradereport_grader.init_report', $jsarguments, false, $module); | |
1024 | $PAGE->requires->strings_for_js(array('addfeedback','feedback', 'grade'), 'grades'); | |
1025 | $PAGE->requires->strings_for_js(array('ajaxchoosescale','ajaxclicktoclose','ajaxerror','ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader'); | |
1026 | ||
6c096a49 NC |
1027 | $rows = $this->get_right_range_row($rows); |
1028 | $rows = $this->get_right_avg_row($rows, true); | |
1029 | $rows = $this->get_right_avg_row($rows); | |
dc482cfa | 1030 | |
6c096a49 NC |
1031 | return $rows; |
1032 | } | |
dc482cfa | 1033 | |
6c096a49 NC |
1034 | /** |
1035 | * Depending on the style of report (fixedstudents vs traditional one-table), | |
1036 | * arranges the rows of data in one or two tables, and returns the output of | |
1037 | * these tables in HTML | |
1038 | * @return string HTML | |
1039 | */ | |
1040 | public function get_grade_table() { | |
1041 | global $OUTPUT; | |
1042 | $fixedstudents = $this->is_fixed_students(); | |
dc482cfa | 1043 | |
6c096a49 NC |
1044 | $leftrows = $this->get_left_rows(); |
1045 | $rightrows = $this->get_right_rows(); | |
dc482cfa | 1046 | |
6c096a49 | 1047 | $html = ''; |
dc482cfa | 1048 | |
fe213365 | 1049 | |
203b7e2e | 1050 | if ($fixedstudents) { |
319770d7 | 1051 | $fixedcolumntable = new html_table(); |
1052 | $fixedcolumntable->id = 'fixed_column'; | |
6c096a49 | 1053 | $fixedcolumntable->data = $leftrows; |
16be8974 | 1054 | $html .= $OUTPUT->container(html_writer::table($fixedcolumntable), 'left_scroller'); |
dc482cfa | 1055 | |
6c096a49 NC |
1056 | $righttable = new html_table(); |
1057 | $righttable->id = 'user-grades'; | |
6c096a49 | 1058 | $righttable->data = $rightrows; |
319770d7 | 1059 | |
16be8974 | 1060 | $html .= $OUTPUT->container(html_writer::table($righttable), 'right_scroller'); |
6c096a49 NC |
1061 | } else { |
1062 | $fulltable = new html_table(); | |
16be8974 | 1063 | $fulltable->attributes['class'] = 'gradestable flexible boxaligncenter generaltable'; |
6c096a49 NC |
1064 | $fulltable->id = 'user-grades'; |
1065 | ||
1066 | // Extract rows from each side (left and right) and collate them into one row each | |
1067 | foreach ($leftrows as $key => $row) { | |
1068 | $row->cells = array_merge($row->cells, $rightrows[$key]->cells); | |
1069 | $fulltable->data[] = $row; | |
203b7e2e | 1070 | } |
16be8974 | 1071 | $html .= html_writer::table($fulltable); |
6c096a49 NC |
1072 | } |
1073 | return $OUTPUT->container($html, 'gradeparent'); | |
1074 | } | |
dc482cfa | 1075 | |
6c096a49 NC |
1076 | /** |
1077 | * Builds and return the row of icons for the left side of the report. | |
1078 | * It only has one cell that says "Controls" | |
1079 | * @param array $rows The Array of rows for the left part of the report | |
1080 | * @param int $colspan The number of columns this cell has to span | |
1081 | * @return array Array of rows for the left part of the report | |
1082 | */ | |
1083 | public function get_left_icons_row($rows=array(), $colspan=1) { | |
1084 | global $USER; | |
dc482cfa | 1085 | |
6c096a49 NC |
1086 | if ($USER->gradeediting[$this->courseid]) { |
1087 | $controlsrow = new html_table_row(); | |
16be8974 | 1088 | $controlsrow->attributes['class'] = 'controls'; |
6c096a49 | 1089 | $controlscell = new html_table_cell(); |
16be8974 | 1090 | $controlscell->attributes['class'] = 'header controls'; |
6c096a49 NC |
1091 | $controlscell->colspan = $colspan; |
1092 | $controlscell->text = $this->get_lang_string('controls','grades'); | |
1093 | ||
1094 | $controlsrow->cells[] = $controlscell; | |
1095 | $rows[] = $controlsrow; | |
1096 | } | |
1097 | return $rows; | |
1098 | } | |
dc482cfa | 1099 | |
6c096a49 NC |
1100 | /** |
1101 | * Builds and return the header for the row of ranges, for the left part of the grader report. | |
1102 | * @param array $rows The Array of rows for the left part of the report | |
1103 | * @param int $colspan The number of columns this cell has to span | |
1104 | * @return array Array of rows for the left part of the report | |
1105 | */ | |
1106 | public function get_left_range_row($rows=array(), $colspan=1) { | |
1107 | global $CFG, $USER; | |
bb9b58a6 | 1108 | |
6c096a49 NC |
1109 | if ($this->get_pref('showranges')) { |
1110 | $rangerow = new html_table_row(); | |
16be8974 | 1111 | $rangerow->attributes['class'] = 'range r'.$this->rowcount++; |
6c096a49 | 1112 | $rangecell = new html_table_cell(); |
16be8974 | 1113 | $rangecell->attributes['class'] = 'header range'; |
6c096a49 NC |
1114 | $rangecell->colspan = $colspan; |
1115 | $rangecell->header = true; | |
1116 | $rangecell->scope = 'row'; | |
1117 | $rangecell->text = $this->get_lang_string('range','grades'); | |
1118 | $rangerow->cells[] = $rangecell; | |
1119 | $rows[] = $rangerow; | |
1120 | } | |
dc482cfa | 1121 | |
6c096a49 NC |
1122 | return $rows; |
1123 | } | |
dc482cfa | 1124 | |
6c096a49 NC |
1125 | /** |
1126 | * Builds and return the headers for the rows of averages, for the left part of the grader report. | |
1127 | * @param array $rows The Array of rows for the left part of the report | |
1128 | * @param int $colspan The number of columns this cell has to span | |
1129 | * @param bool $groupavg If true, returns the row for group averages, otherwise for overall averages | |
1130 | * @return array Array of rows for the left part of the report | |
1131 | */ | |
1132 | public function get_left_avg_row($rows=array(), $colspan=1, $groupavg=false) { | |
1133 | if (!$this->canviewhidden) { | |
1134 | // totals might be affected by hiding, if user can not see hidden grades the aggregations might be altered | |
1135 | // better not show them at all if user can not see all hideen grades | |
1136 | return $rows; | |
1137 | } | |
dc482cfa | 1138 | |
6c096a49 NC |
1139 | $showaverages = $this->get_pref('showaverages'); |
1140 | $showaveragesgroup = $this->currentgroup && $showaverages; | |
1141 | $straveragegroup = get_string('groupavg', 'grades'); | |
319770d7 | 1142 | |
6c096a49 | 1143 | if ($groupavg) { |
319770d7 | 1144 | if ($showaveragesgroup) { |
1145 | $groupavgrow = new html_table_row(); | |
16be8974 | 1146 | $groupavgrow->attributes['class'] = 'groupavg r'.$this->rowcount++; |
319770d7 | 1147 | $groupavgcell = new html_table_cell(); |
16be8974 | 1148 | $groupavgcell->attributes['class'] = 'header range'; |
319770d7 | 1149 | $groupavgcell->colspan = $colspan; |
1150 | $groupavgcell->header = true; | |
1151 | $groupavgcell->scope = 'row'; | |
1152 | $groupavgcell->text = $straveragegroup; | |
1153 | $groupavgrow->cells[] = $groupavgcell; | |
6c096a49 | 1154 | $rows[] = $groupavgrow; |
203b7e2e | 1155 | } |
6c096a49 NC |
1156 | } else { |
1157 | $straverage = get_string('overallaverage', 'grades'); | |
dc482cfa | 1158 | |
203b7e2e | 1159 | if ($showaverages) { |
319770d7 | 1160 | $avgrow = new html_table_row(); |
16be8974 | 1161 | $avgrow->attributes['class'] = 'avg r'.$this->rowcount++; |
319770d7 | 1162 | $avgcell = new html_table_cell(); |
16be8974 | 1163 | $avgcell->attributes['class'] = 'header range'; |
319770d7 | 1164 | $avgcell->colspan = $colspan; |
1165 | $avgcell->header = true; | |
1166 | $avgcell->scope = 'row'; | |
1167 | $avgcell->text = $straverage; | |
1168 | $avgrow->cells[] = $avgcell; | |
6c096a49 | 1169 | $rows[] = $avgrow; |
203b7e2e | 1170 | } |
6c096a49 | 1171 | } |
203b7e2e | 1172 | |
6c096a49 NC |
1173 | return $rows; |
1174 | } | |
dc482cfa | 1175 | |
6c096a49 NC |
1176 | /** |
1177 | * Builds and return the row of icons when editing is on, for the right part of the grader report. | |
1178 | * @param array $rows The Array of rows for the right part of the report | |
1179 | * @return array Array of rows for the right part of the report | |
1180 | */ | |
1181 | public function get_right_icons_row($rows=array()) { | |
1182 | global $USER; | |
1183 | if ($USER->gradeediting[$this->courseid]) { | |
1184 | $iconsrow = new html_table_row(); | |
16be8974 | 1185 | $iconsrow->attributes['class'] = 'controls'; |
6c096a49 NC |
1186 | |
1187 | $showuseridnumber = $this->get_pref('showuseridnumber'); | |
dc482cfa | 1188 | |
6c096a49 NC |
1189 | foreach ($this->gtree->items as $itemid=>$unused) { |
1190 | // emulate grade element | |
1191 | $item =& $this->gtree->get_item($itemid); | |
1192 | ||
1193 | $eid = $this->gtree->get_item_eid($item); | |
1194 | $element = $this->gtree->locate_element($eid); | |
1195 | $itemcell = new html_table_cell(); | |
16be8974 | 1196 | $itemcell->attributes['class'] = 'controls icons'; |
6c096a49 NC |
1197 | $itemcell->text = $this->get_icons($element); |
1198 | $iconsrow->cells[] = $itemcell; | |
1199 | } | |
1200 | $rows[] = $iconsrow; | |
1201 | } | |
1202 | return $rows; | |
dc482cfa | 1203 | } |
1204 | ||
18a00359 | 1205 | /** |
6c096a49 NC |
1206 | * Builds and return the row of ranges for the right part of the grader report. |
1207 | * @param array $rows The Array of rows for the right part of the report | |
1208 | * @return array Array of rows for the right part of the report | |
18a00359 | 1209 | */ |
6c096a49 NC |
1210 | public function get_right_range_row($rows=array()) { |
1211 | global $OUTPUT; | |
18a00359 | 1212 | |
6c096a49 NC |
1213 | if ($this->get_pref('showranges')) { |
1214 | $rangesdisplaytype = $this->get_pref('rangesdisplaytype'); | |
1215 | $rangesdecimalpoints = $this->get_pref('rangesdecimalpoints'); | |
1216 | $rangerow = new html_table_row(); | |
16be8974 | 1217 | $rangerow->attributes['class'] = 'heading range'; |
18a00359 | 1218 | |
6c096a49 NC |
1219 | foreach ($this->gtree->items as $itemid=>$unused) { |
1220 | $item =& $this->gtree->items[$itemid]; | |
1221 | $itemcell = new html_table_cell(); | |
1222 | $itemcell->header = true; | |
16be8974 | 1223 | $itemcell->attributes['class'] .= ' header range'; |
6c096a49 NC |
1224 | |
1225 | $hidden = ''; | |
1226 | if ($item->is_hidden()) { | |
1227 | $hidden = ' hidden '; | |
1228 | } | |
1229 | ||
1230 | $formattedrange = $item->get_formatted_range($rangesdisplaytype, $rangesdecimalpoints); | |
1231 | ||
1232 | $itemcell->text = $OUTPUT->container($formattedrange, 'rangevalues'.$hidden); | |
1233 | $rangerow->cells[] = $itemcell; | |
1234 | } | |
1235 | $rows[] = $rangerow; | |
18a00359 | 1236 | } |
6c096a49 | 1237 | return $rows; |
18a00359 | 1238 | } |
1239 | ||
4ba9941c | 1240 | /** |
6c096a49 NC |
1241 | * Builds and return the row of averages for the right part of the grader report. |
1242 | * @param array $rows Whether to return only group averages or all averages. | |
1243 | * @param bool $grouponly Whether to return only group averages or all averages. | |
1244 | * @return array Array of rows for the right part of the report | |
4ba9941c | 1245 | */ |
6c096a49 NC |
1246 | public function get_right_avg_row($rows=array(), $grouponly=false) { |
1247 | global $CFG, $USER, $DB, $OUTPUT; | |
4ba9941c | 1248 | |
57068674 | 1249 | if (!$this->canviewhidden) { |
1250 | // totals might be affected by hiding, if user can not see hidden grades the aggregations might be altered | |
6ef4878b | 1251 | // better not show them at all if user can not see all hidden grades |
6c096a49 | 1252 | return $rows; |
57068674 | 1253 | } |
1254 | ||
6c096a49 NC |
1255 | $showaverages = $this->get_pref('showaverages'); |
1256 | $showaveragesgroup = $this->currentgroup && $showaverages; | |
1257 | ||
5b508a08 | 1258 | $averagesdisplaytype = $this->get_pref('averagesdisplaytype'); |
1259 | $averagesdecimalpoints = $this->get_pref('averagesdecimalpoints'); | |
098042ba | 1260 | $meanselection = $this->get_pref('meanselection'); |
1261 | $shownumberofgrades = $this->get_pref('shownumberofgrades'); | |
1262 | ||
5b508a08 | 1263 | $avghtml = ''; |
aae94377 | 1264 | $avgcssclass = 'avg'; |
3446013d | 1265 | |
5b508a08 | 1266 | if ($grouponly) { |
1267 | $straverage = get_string('groupavg', 'grades'); | |
35079f53 | 1268 | $showaverages = $this->currentgroup && $this->get_pref('showaverages'); |
5b508a08 | 1269 | $groupsql = $this->groupsql; |
1270 | $groupwheresql = $this->groupwheresql; | |
319770d7 | 1271 | $groupwheresqlparams = $this->groupwheresql_params; |
aae94377 | 1272 | $avgcssclass = 'groupavg'; |
3446013d | 1273 | } else { |
6308b91c | 1274 | $straverage = get_string('overallaverage', 'grades'); |
5b508a08 | 1275 | $showaverages = $this->get_pref('showaverages'); |
0dba6cb2 | 1276 | $groupsql = ""; |
1277 | $groupwheresql = ""; | |
319770d7 | 1278 | $groupwheresqlparams = array(); |
3446013d | 1279 | } |
1280 | ||
a5b8be62 | 1281 | if ($shownumberofgrades) { |
1282 | $straverage .= ' (' . get_string('submissions', 'grades') . ') '; | |
1283 | } | |
1284 | ||
f8ae1f86 | 1285 | $totalcount = $this->get_numusers($grouponly); |
04259694 | 1286 | |
387815db | 1287 | //limit to users with a gradeable role |
1288 | list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0'); | |
1289 | ||
1290 | //limit to users with an active enrollment | |
1291 | list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context); | |
d24832f9 | 1292 | |
5b508a08 | 1293 | if ($showaverages) { |
387815db | 1294 | $params = array_merge(array('courseid'=>$this->courseid), $gradebookrolesparams, $enrolledparams, $groupwheresqlparams); |
04259694 | 1295 | |
828af11c | 1296 | // find sums of all grade items in course |
1297 | $SQL = "SELECT g.itemid, SUM(g.finalgrade) AS sum | |
d24832f9 | 1298 | FROM {grade_items} gi |
25081b95 SH |
1299 | JOIN {grade_grades} g |
1300 | ON g.itemid = gi.id | |
1301 | JOIN {user} u | |
1302 | ON u.id = g.userid | |
1303 | JOIN ($enrolledsql) je | |
1304 | ON je.id = u.id | |
1305 | JOIN {role_assignments} ra | |
1306 | ON ra.userid = u.id | |
1307 | $groupsql | |
b50371da | 1308 | WHERE gi.courseid = :courseid |
387815db | 1309 | AND ra.roleid $gradebookrolessql |
828af11c | 1310 | AND ra.contextid ".get_related_contexts_string($this->context)." |
1311 | AND g.finalgrade IS NOT NULL | |
883187d0 | 1312 | $groupwheresql |
883187d0 | 1313 | GROUP BY g.itemid"; |
319770d7 | 1314 | $sumarray = array(); |
d24832f9 | 1315 | if ($sums = $DB->get_records_sql($SQL, $params)) { |
ab3444d7 | 1316 | foreach ($sums as $itemid => $csum) { |
319770d7 | 1317 | $sumarray[$itemid] = $csum->sum; |
7b61efbe | 1318 | } |
4ba9941c | 1319 | } |
66ef0471 | 1320 | |
883187d0 | 1321 | // MDL-10875 Empty grades must be evaluated as grademin, NOT always 0 |
1322 | // This query returns a count of ungraded grades (NULL finalgrade OR no matching record in grade_grades table) | |
1323 | $SQL = "SELECT gi.id, COUNT(u.id) AS count | |
d24832f9 | 1324 | FROM {grade_items} gi |
25081b95 SH |
1325 | CROSS JOIN {user} u |
1326 | JOIN ($enrolledsql) je | |
1327 | ON je.id = u.id | |
1328 | JOIN {role_assignments} ra | |
1329 | ON ra.userid = u.id | |
1330 | LEFT OUTER JOIN {grade_grades} g | |
1331 | ON (g.itemid = gi.id AND g.userid = u.id AND g.finalgrade IS NOT NULL) | |
1332 | $groupsql | |
b50371da | 1333 | WHERE gi.courseid = :courseid |
387815db | 1334 | AND ra.roleid $gradebookrolessql |
828af11c | 1335 | AND ra.contextid ".get_related_contexts_string($this->context)." |
1336 | AND g.id IS NULL | |
1337 | $groupwheresql | |
883187d0 | 1338 | GROUP BY gi.id"; |
828af11c | 1339 | |
319770d7 | 1340 | $ungradedcounts = $DB->get_records_sql($SQL, $params); |
883187d0 | 1341 | |
6c096a49 | 1342 | $avgrow = new html_table_row(); |
16be8974 | 1343 | $avgrow->attributes['class'] = 'avg'; |
653a8648 | 1344 | |
b89a70ce | 1345 | foreach ($this->gtree->items as $itemid=>$unused) { |
1346 | $item =& $this->gtree->items[$itemid]; | |
1347 | ||
67881aa8 | 1348 | if ($item->needsupdate) { |
6c096a49 NC |
1349 | $avgcell = new html_table_cell(); |
1350 | $avgcell->text = $OUTPUT->container(get_string('error'), 'gradingerror'); | |
1351 | $avgrow->cells[] = $avgcell; | |
67881aa8 | 1352 | continue; |
1353 | } | |
1354 | ||
319770d7 | 1355 | if (!isset($sumarray[$item->id])) { |
1356 | $sumarray[$item->id] = 0; | |
33a34cd4 | 1357 | } |
883187d0 | 1358 | |
319770d7 | 1359 | if (empty($ungradedcounts[$itemid])) { |
1360 | $ungradedcount = 0; | |
828af11c | 1361 | } else { |
319770d7 | 1362 | $ungradedcount = $ungradedcounts[$itemid]->count; |
d1556c09 | 1363 | } |
33a34cd4 | 1364 | |
c2efb501 | 1365 | if ($meanselection == GRADE_REPORT_MEAN_GRADED) { |
319770d7 | 1366 | $meancount = $totalcount - $ungradedcount; |
33a34cd4 | 1367 | } else { // Bump up the sum by the number of ungraded items * grademin |
319770d7 | 1368 | $sumarray[$item->id] += $ungradedcount * $item->grademin; |
1369 | $meancount = $totalcount; | |
33a34cd4 | 1370 | } |
1371 | ||
31a6c06c | 1372 | $decimalpoints = $item->get_decimals(); |
41f22daa | 1373 | |
bb384a8e | 1374 | // Determine which display type to use for this average |
2cc773f5 | 1375 | if ($USER->gradeediting[$this->courseid]) { |
1796708d | 1376 | $displaytype = GRADE_DISPLAY_TYPE_REAL; |
d622930b | 1377 | |
e0724506 | 1378 | } else if ($averagesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) { // no ==0 here, please resave the report and user preferences |
d622930b | 1379 | $displaytype = $item->get_displaytype(); |
1380 | ||
1381 | } else { | |
bb384a8e | 1382 | $displaytype = $averagesdisplaytype; |
1383 | } | |
1384 | ||
31a6c06c | 1385 | // Override grade_item setting if a display preference (not inherit) was set for the averages |
e0724506 | 1386 | if ($averagesdecimalpoints == GRADE_REPORT_PREFERENCE_INHERIT) { |
d622930b | 1387 | $decimalpoints = $item->get_decimals(); |
1388 | ||
1389 | } else { | |
5b508a08 | 1390 | $decimalpoints = $averagesdecimalpoints; |
1391 | } | |
1392 | ||
319770d7 | 1393 | if (!isset($sumarray[$item->id]) || $meancount == 0) { |
6c096a49 NC |
1394 | $avgcell = new html_table_cell(); |
1395 | $avgcell->text = '-'; | |
1396 | $avgrow->cells[] = $avgcell; | |
1397 | ||
4ba9941c | 1398 | } else { |
319770d7 | 1399 | $sum = $sumarray[$item->id]; |
1400 | $avgradeval = $sum/$meancount; | |
d622930b | 1401 | $gradehtml = grade_format_gradevalue($avgradeval, $item, true, $displaytype, $decimalpoints); |
bb384a8e | 1402 | |
098042ba | 1403 | $numberofgrades = ''; |
098042ba | 1404 | if ($shownumberofgrades) { |
319770d7 | 1405 | $numberofgrades = " ($meancount)"; |
098042ba | 1406 | } |
1407 | ||
6c096a49 NC |
1408 | $avgcell = new html_table_cell(); |
1409 | $avgcell->text = $gradehtml.$numberofgrades; | |
1410 | $avgrow->cells[] = $avgcell; | |
80fb1cf9 | 1411 | } |
9ecd4386 | 1412 | } |
6c096a49 | 1413 | $rows[] = $avgrow; |
9ecd4386 | 1414 | } |
6c096a49 | 1415 | return $rows; |
9ecd4386 | 1416 | } |
4ba9941c | 1417 | |
1418 | /** | |
1419 | * Given a grade_category, grade_item or grade_grade, this function | |
1420 | * figures out the state of the object and builds then returns a div | |
1421 | * with the icons needed for the grader report. | |
1422 | * | |
1423 | * @param object $object | |
4ba9941c | 1424 | * @return string HTML |
1425 | */ | |
d24832f9 | 1426 | protected function get_icons($element) { |
319770d7 | 1427 | global $CFG, $USER, $OUTPUT; |
4ba9941c | 1428 | |
2cc773f5 | 1429 | if (!$USER->gradeediting[$this->courseid]) { |
1430 | return '<div class="grade_icons" />'; | |
79eabc2a | 1431 | } |
4ba9941c | 1432 | |
2cc773f5 | 1433 | // Init all icons |
319770d7 | 1434 | $editicon = ''; |
dc482cfa | 1435 | |
1436 | if ($element['type'] != 'categoryitem' && $element['type'] != 'courseitem') { | |
319770d7 | 1437 | $editicon = $this->gtree->get_edit_icon($element, $this->gpr); |
dc482cfa | 1438 | } |
1439 | ||
319770d7 | 1440 | $editcalculationicon = ''; |
1441 | $showhideicon = ''; | |
1442 | $lockunlockicon = ''; | |
4ba9941c | 1443 | |
a5b8be62 | 1444 | if (has_capability('moodle/grade:manage', $this->context)) { |
4ba9941c | 1445 | |
a5b8be62 | 1446 | if ($this->get_pref('showcalculations')) { |
319770d7 | 1447 | $editcalculationicon = $this->gtree->get_calculation_icon($element, $this->gpr); |
a5b8be62 | 1448 | } |
1449 | ||
1450 | if ($this->get_pref('showeyecons')) { | |
319770d7 | 1451 | $showhideicon = $this->gtree->get_hiding_icon($element, $this->gpr); |
a5b8be62 | 1452 | } |
4ba9941c | 1453 | |
a5b8be62 | 1454 | if ($this->get_pref('showlocks')) { |
319770d7 | 1455 | $lockunlockicon = $this->gtree->get_locking_icon($element, $this->gpr); |
a5b8be62 | 1456 | } |
4ba9941c | 1457 | } |
1458 | ||
319770d7 | 1459 | return $OUTPUT->container($editicon.$editcalculationicon.$showhideicon.$lockunlockicon, 'grade_icons'); |
4faf5f99 | 1460 | } |
1461 | ||
1462 | /** | |
1463 | * Given a category element returns collapsing +/- icon if available | |
1464 | * @param object $object | |
1465 | * @return string HTML | |
1466 | */ | |
d24832f9 | 1467 | protected function get_collapsing_icon($element) { |
5d3b9994 | 1468 | global $OUTPUT; |
4faf5f99 | 1469 | |
319770d7 | 1470 | $icon = ''; |
2cc773f5 | 1471 | // If object is a category, display expand/contract icon |
384960dd | 1472 | if ($element['type'] == 'category') { |
2cc773f5 | 1473 | // Load language strings |
319770d7 | 1474 | $strswitchminus = $this->get_lang_string('aggregatesonly', 'grades'); |
1475 | $strswitchplus = $this->get_lang_string('gradesonly', 'grades'); | |
1476 | $strswitchwhole = $this->get_lang_string('fullmode', 'grades'); | |
384960dd | 1477 | |
8ae8bf8a | 1478 | $url = new moodle_url($this->gpr->get_return_url(null, array('target'=>$element['eid'], 'sesskey'=>sesskey()))); |
48b5d8f3 | 1479 | |
384960dd | 1480 | if (in_array($element['object']->id, $this->collapsed['aggregatesonly'])) { |
8ae8bf8a | 1481 | $url->param('action', 'switch_plus'); |
c63923bd | 1482 | $icon = $OUTPUT->action_icon($url, new pix_icon('t/switch_plus', $strswitchplus)); |
8ae8bf8a PS |
1483 | |
1484 | } else if (in_array($element['object']->id, $this->collapsed['gradesonly'])) { | |
1485 | $url->param('action', 'switch_whole'); | |
c63923bd | 1486 | $icon = $OUTPUT->action_icon($url, new pix_icon('t/switch_whole', $strswitchwhole)); |
8ae8bf8a PS |
1487 | |
1488 | } else { | |
1489 | $url->param('action', 'switch_minus'); | |
c63923bd | 1490 | $icon = $OUTPUT->action_icon($url, new pix_icon('t/switch_minus', $strswitchminus)); |
2cc773f5 | 1491 | } |
4ba9941c | 1492 | } |
319770d7 | 1493 | return $icon; |
4ba9941c | 1494 | } |
48b5d8f3 | 1495 | |
1496 | /** | |
1497 | * Processes a single action against a category, grade_item or grade. | |
1498 | * @param string $target eid ({type}{id}, e.g. c4 for category4) | |
1499 | * @param string $action Which action to take (edit, delete etc...) | |
1500 | * @return | |
1501 | */ | |
d24832f9 | 1502 | public function process_action($target, $action) { |
4faf5f99 | 1503 | // TODO: this code should be in some grade_tree static method |
48b5d8f3 | 1504 | $targettype = substr($target, 0, 1); |
1505 | $targetid = substr($target, 1); | |
4faf5f99 | 1506 | // TODO: end |
1507 | ||
1508 | if ($collapsed = get_user_preferences('grade_report_grader_collapsed_categories')) { | |
1509 | $collapsed = unserialize($collapsed); | |
1510 | } else { | |
384960dd | 1511 | $collapsed = array('aggregatesonly' => array(), 'gradesonly' => array()); |
4faf5f99 | 1512 | } |
1513 | ||
48b5d8f3 | 1514 | switch ($action) { |
384960dd | 1515 | case 'switch_minus': // Add category to array of aggregatesonly |
1516 | if (!in_array($targetid, $collapsed['aggregatesonly'])) { | |
1517 | $collapsed['aggregatesonly'][] = $targetid; | |
4faf5f99 | 1518 | set_user_preference('grade_report_grader_collapsed_categories', serialize($collapsed)); |
1519 | } | |
48b5d8f3 | 1520 | break; |
4faf5f99 | 1521 | |
384960dd | 1522 | case 'switch_plus': // Remove category from array of aggregatesonly, and add it to array of gradesonly |
1523 | $key = array_search($targetid, $collapsed['aggregatesonly']); | |
4faf5f99 | 1524 | if ($key !== false) { |
384960dd | 1525 | unset($collapsed['aggregatesonly'][$key]); |
4faf5f99 | 1526 | } |
384960dd | 1527 | if (!in_array($targetid, $collapsed['gradesonly'])) { |
1528 | $collapsed['gradesonly'][] = $targetid; | |
1529 | } | |
1530 | set_user_preference('grade_report_grader_collapsed_categories', serialize($collapsed)); | |
48b5d8f3 | 1531 | break; |
384960dd | 1532 | case 'switch_whole': // Remove the category from the array of collapsed cats |
1533 | $key = array_search($targetid, $collapsed['gradesonly']); | |
1534 | if ($key !== false) { | |
1535 | unset($collapsed['gradesonly'][$key]); | |
1536 | set_user_preference('grade_report_grader_collapsed_categories', serialize($collapsed)); | |
1537 | } | |
4faf5f99 | 1538 | |
384960dd | 1539 | break; |
48b5d8f3 | 1540 | default: |
1541 | break; | |
1542 | } | |
1543 | ||
1544 | return true; | |
1545 | } | |
03fcc729 | 1546 | |
795b745a | 1547 | /** |
1548 | * Returns whether or not to display fixed students column. | |
1549 | * Includes a browser check, because IE6 doesn't support the scrollbar. | |
1550 | * | |
1551 | * @return bool | |
1552 | */ | |
1553 | public function is_fixed_students() { | |
1554 | global $USER, $CFG; | |
03fcc729 | 1555 | return empty($USER->screenreader) && $CFG->grade_report_fixedstudents && |
1556 | (check_browser_version('MSIE', '7.0') || | |
795b745a | 1557 | check_browser_version('Firefox', '2.0') || |
1558 | check_browser_version('Gecko', '2006010100') || | |
1559 | check_browser_version('Camino', '1.0') || | |
1560 | check_browser_version('Opera', '6.0') || | |
06671035 PS |
1561 | check_browser_version('Chrome', '6') || |
1562 | check_browser_version('Safari', '300')); | |
795b745a | 1563 | } |
6c096a49 NC |
1564 | |
1565 | /** | |
1566 | * Refactored function for generating HTML of sorting links with matching arrows. | |
1567 | * Returns an array with 'studentname' and 'idnumber' as keys, with HTML ready | |
1568 | * to inject into a table header cell. | |
1569 | * @return array An associative array of HTML sorting links+arrows | |
1570 | */ | |
1571 | public function get_sort_arrows() { | |
1572 | global $OUTPUT; | |
1573 | $arrows = array(); | |
1574 | ||
1575 | $strsortasc = $this->get_lang_string('sortasc', 'grades'); | |
1576 | $strsortdesc = $this->get_lang_string('sortdesc', 'grades'); | |
1577 | $strfirstname = $this->get_lang_string('firstname'); | |
1578 | $strlastname = $this->get_lang_string('lastname'); | |
1579 | ||
0f4c64b7 PS |
1580 | $firstlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'firstname')), $strfirstname); |
1581 | $lastlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'lastname')), $strlastname); | |
1582 | $idnumberlink = html_writer::link(new moodle_url($this->baseurl, array('sortitemid'=>'idnumber')), get_string('idnumber')); | |
6c096a49 | 1583 | |
0f4c64b7 | 1584 | $arrows['studentname'] = $lastlink; |
6c096a49 NC |
1585 | |
1586 | if ($this->sortitemid === 'lastname') { | |
1587 | if ($this->sortorder == 'ASC') { | |
1588 | $arrows['studentname'] .= print_arrow('up', $strsortasc, true); | |
1589 | } else { | |
1590 | $arrows['studentname'] .= print_arrow('down', $strsortdesc, true); | |
1591 | } | |
1592 | } | |
1593 | ||
0f4c64b7 | 1594 | $arrows['studentname'] .= ' ' . $firstlink; |
6c096a49 NC |
1595 | |
1596 | if ($this->sortitemid === 'firstname') { | |
1597 | if ($this->sortorder == 'ASC') { | |
1598 | $arrows['studentname'] .= print_arrow('up', $strsortasc, true); | |
1599 | } else { | |
1600 | $arrows['studentname'] .= print_arrow('down', $strsortdesc, true); | |
1601 | } | |
1602 | } | |
1603 | ||
0f4c64b7 | 1604 | $arrows['idnumber'] = $idnumberlink; |
6c096a49 NC |
1605 | |
1606 | if ('idnumber' == $this->sortitemid) { | |
1607 | if ($this->sortorder == 'ASC') { | |
1608 | $arrows['idnumber'] .= print_arrow('up', $strsortasc, true); | |
1609 | } else { | |
1610 | $arrows['idnumber'] .= print_arrow('down', $strsortdesc, true); | |
1611 | } | |
1612 | } | |
1613 | ||
1614 | return $arrows; | |
1615 | } | |
4ba9941c | 1616 | } |
6c3ef410 | 1617 |