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 | |
38b9e8a8 | 18 | /** |
eea6690a | 19 | * File containing the grade_report class. |
20 | * @package gradebook | |
38b9e8a8 | 21 | */ |
eea6690a | 22 | |
23 | require_once($CFG->libdir.'/gradelib.php'); | |
24 | ||
25 | /** | |
26 | * An abstract class containing variables and methods used by all or most reports. | |
27 | * @abstract | |
28 | * @package gradebook | |
29 | */ | |
d24832f9 | 30 | abstract class grade_report { |
eea6690a | 31 | /** |
32 | * The courseid. | |
33 | * @var int $courseid | |
34 | */ | |
d24832f9 | 35 | public $courseid; |
eea6690a | 36 | |
b2bc96d1 | 37 | /** |
38 | * The course. | |
39 | * @var object $course | |
40 | */ | |
d24832f9 | 41 | public $course; |
b2bc96d1 | 42 | |
d30c4481 | 43 | /** Grade plugin return tracking object. |
4889285b | 44 | * @var object $gpr |
45 | */ | |
46 | public $gpr; | |
d30c4481 | 47 | |
eea6690a | 48 | /** |
49 | * The context. | |
50 | * @var int $context | |
51 | */ | |
d24832f9 | 52 | public $context; |
eea6690a | 53 | |
54 | /** | |
55 | * The grade_tree object. | |
56 | * @var object $gtree | |
57 | */ | |
d24832f9 | 58 | public $gtree; |
eea6690a | 59 | |
60 | /** | |
61 | * User preferences related to this report. | |
e50ce569 | 62 | * @var array $prefs |
eea6690a | 63 | */ |
d24832f9 | 64 | public $prefs = array(); |
eea6690a | 65 | |
66 | /** | |
67 | * The roles for this report. | |
68 | * @var string $gradebookroles | |
69 | */ | |
d24832f9 | 70 | public $gradebookroles; |
eea6690a | 71 | |
72 | /** | |
73 | * base url for sorting by first/last name. | |
74 | * @var string $baseurl | |
75 | */ | |
d24832f9 | 76 | public $baseurl; |
eea6690a | 77 | |
78 | /** | |
79 | * base url for paging. | |
80 | * @var string $pbarurl | |
81 | */ | |
d24832f9 | 82 | public $pbarurl; |
eea6690a | 83 | |
84 | /** | |
85 | * Current page (for paging). | |
86 | * @var int $page | |
87 | */ | |
d24832f9 | 88 | public $page; |
eea6690a | 89 | |
388234f4 | 90 | /** |
91 | * Array of cached language strings (using get_string() all the time takes a long time!). | |
92 | * @var array $lang_strings | |
93 | */ | |
d24832f9 | 94 | public $lang_strings = array(); |
388234f4 | 95 | |
90d3960c | 96 | //// GROUP VARIABLES (including SQL) |
97 | ||
98 | /** | |
99 | * The current group being displayed. | |
100 | * @var int $currentgroup | |
101 | */ | |
d24832f9 | 102 | public $currentgroup; |
90d3960c | 103 | |
35079f53 | 104 | /** |
105 | * Current course group mode | |
106 | * @var int $groupmode | |
107 | */ | |
108 | var $groupmode; | |
109 | ||
90d3960c | 110 | /** |
111 | * A HTML select element used to select the current group. | |
112 | * @var string $group_selector | |
113 | */ | |
d24832f9 | 114 | public $group_selector; |
90d3960c | 115 | |
116 | /** | |
117 | * An SQL fragment used to add linking information to the group tables. | |
118 | * @var string $groupsql | |
119 | */ | |
d24832f9 | 120 | protected $groupsql; |
90d3960c | 121 | |
122 | /** | |
123 | * An SQL constraint to append to the queries used by this object to build the report. | |
124 | * @var string $groupwheresql | |
125 | */ | |
d24832f9 | 126 | protected $groupwheresql; |
127 | ||
128 | /** | |
129 | * The ordered params for $groupwheresql | |
130 | * @var array $groupwheresql_params | |
131 | */ | |
132 | protected $groupwheresql_params = array(); | |
90d3960c | 133 | |
134 | ||
eea6690a | 135 | /** |
136 | * Constructor. Sets local copies of user preferences and initialises grade_tree. | |
137 | * @param int $courseid | |
d30c4481 | 138 | * @param object $gpr grade plugin return tracking object |
eea6690a | 139 | * @param string $context |
140 | * @param int $page The current page being viewed (when report is paged) | |
141 | */ | |
d24832f9 | 142 | public function __construct($courseid, $gpr, $context, $page=null) { |
5c75a0a3 | 143 | global $CFG, $COURSE, $DB; |
eea6690a | 144 | |
dde8e548 | 145 | if (empty($CFG->gradebookroles)) { |
771dc7b2 | 146 | print_error('norolesdefined', 'grades'); |
0893aa30 | 147 | } |
284abb09 | 148 | |
0893aa30 | 149 | |
4faf5f99 | 150 | $this->courseid = $courseid; |
b2bc96d1 | 151 | if ($this->courseid == $COURSE->id) { |
152 | $this->course = $COURSE; | |
153 | } else { | |
5c75a0a3 | 154 | $this->course = $DB->get_record('course', array('id' => $this->courseid)); |
b2bc96d1 | 155 | } |
41f22daa | 156 | |
4faf5f99 | 157 | $this->gpr = $gpr; |
158 | $this->context = $context; | |
159 | $this->page = $page; | |
eea6690a | 160 | |
161 | // roles to be displayed in the gradebook | |
162 | $this->gradebookroles = $CFG->gradebookroles; | |
163 | ||
dc482cfa | 164 | // Set up link to preferences page |
165 | $this->preferences_page = $CFG->wwwroot.'/grade/report/grader/preferences.php?id='.$courseid; | |
166 | ||
4faf5f99 | 167 | // init gtree in child class |
38b9e8a8 | 168 | } |
169 | ||
eea6690a | 170 | /** |
171 | * Given the name of a user preference (without grade_report_ prefix), locally saves then returns | |
172 | * the value of that preference. If the preference has already been fetched before, | |
173 | * the saved value is returned. If the preference is not set at the User level, the $CFG equivalent | |
174 | * is given (site default). | |
501e0e34 | 175 | * @static (Can be called statically, but then doesn't benefit from caching) |
eea6690a | 176 | * @param string $pref The name of the preference (do not include the grade_report_ prefix) |
8c5a416e | 177 | * @param int $objectid An optional itemid or categoryid to check for a more fine-grained preference |
eea6690a | 178 | * @return mixed The value of the preference |
179 | */ | |
d24832f9 | 180 | public function get_pref($pref, $objectid=null) { |
eea6690a | 181 | global $CFG; |
501e0e34 | 182 | $fullprefname = 'grade_report_' . $pref; |
54294d3b | 183 | $shortprefname = 'grade_' . $pref; |
38b9e8a8 | 184 | |
e50ce569 | 185 | $retval = null; |
186 | ||
438a5aa9 | 187 | if (!isset($this) OR get_class($this) != 'grade_report') { |
8c5a416e | 188 | if (!empty($objectid)) { |
189 | $retval = get_user_preferences($fullprefname . $objectid, grade_report::get_pref($pref)); | |
54294d3b | 190 | } elseif (isset($CFG->$fullprefname)) { |
e50ce569 | 191 | $retval = get_user_preferences($fullprefname, $CFG->$fullprefname); |
54294d3b | 192 | } elseif (isset($CFG->$shortprefname)) { |
d24832f9 | 193 | $retval = get_user_preferences($fullprefname, $CFG->$shortprefname); |
54294d3b | 194 | } else { |
195 | $retval = null; | |
bb384a8e | 196 | } |
501e0e34 | 197 | } else { |
8c5a416e | 198 | if (empty($this->prefs[$pref.$objectid])) { |
e50ce569 | 199 | |
8c5a416e | 200 | if (!empty($objectid)) { |
201 | $retval = get_user_preferences($fullprefname . $objectid); | |
e50ce569 | 202 | if (empty($retval)) { |
203 | // No item pref found, we are returning the global preference | |
204 | $retval = $this->get_pref($pref); | |
8c5a416e | 205 | $objectid = null; |
e50ce569 | 206 | } |
501e0e34 | 207 | } else { |
e50ce569 | 208 | $retval = get_user_preferences($fullprefname, $CFG->$fullprefname); |
501e0e34 | 209 | } |
8c5a416e | 210 | $this->prefs[$pref.$objectid] = $retval; |
e50ce569 | 211 | } else { |
8c5a416e | 212 | $retval = $this->prefs[$pref.$objectid]; |
501e0e34 | 213 | } |
eea6690a | 214 | } |
e50ce569 | 215 | |
216 | return $retval; | |
eea6690a | 217 | } |
bb384a8e | 218 | |
eea6690a | 219 | /** |
501e0e34 | 220 | * Uses set_user_preferences() to update the value of a user preference. If 'default' is given as the value, |
221 | * the preference will be removed in favour of a higher-level preference. | |
222 | * @static | |
eea6690a | 223 | * @param string $pref_name The name of the preference. |
224 | * @param mixed $pref_value The value of the preference. | |
bb384a8e | 225 | * @param int $itemid An optional itemid to which the preference will be assigned |
eea6690a | 226 | * @return bool Success or failure. |
eea6690a | 227 | */ |
d24832f9 | 228 | public function set_pref($pref, $pref_value='default', $itemid=null) { |
bb384a8e | 229 | $fullprefname = 'grade_report_' . $pref; |
501e0e34 | 230 | if ($pref_value == 'default') { |
231 | return unset_user_preference($fullprefname.$itemid); | |
232 | } else { | |
233 | return set_user_preference($fullprefname.$itemid, $pref_value); | |
eea6690a | 234 | } |
38b9e8a8 | 235 | } |
38b9e8a8 | 236 | |
eea6690a | 237 | /** |
238 | * Handles form data sent by this report for this report. Abstract method to implement in all children. | |
239 | * @abstract | |
240 | * @param array $data | |
241 | * @return mixed True or array of errors | |
242 | */ | |
d24832f9 | 243 | abstract function process_data($data); |
38b9e8a8 | 244 | |
eea6690a | 245 | /** |
246 | * Processes a single action against a category, grade_item or grade. | |
247 | * @param string $target Sortorder | |
248 | * @param string $action Which action to take (edit, delete etc...) | |
249 | * @return | |
eea6690a | 250 | */ |
d24832f9 | 251 | abstract function process_action($target, $action); |
eea6690a | 252 | |
388234f4 | 253 | /** |
254 | * First checks the cached language strings, then returns match if found, or uses get_string() | |
255 | * to get it from the DB, caches it then returns it. | |
256 | * @param string $strcode | |
257 | * @param string $section Optional language section | |
258 | * @return string | |
259 | */ | |
d24832f9 | 260 | public function get_lang_string($strcode, $section=null) { |
388234f4 | 261 | if (empty($this->lang_strings[$strcode])) { |
262 | $this->lang_strings[$strcode] = get_string($strcode, $section); | |
263 | } | |
264 | return $this->lang_strings[$strcode]; | |
265 | } | |
266 | ||
90d3960c | 267 | /** |
268 | * Fetches and returns a count of all the users that will be shown on this page. | |
28bcbc38 | 269 | * @param boolean $groups include groups limit |
90d3960c | 270 | * @return int Count of users |
271 | */ | |
d24832f9 | 272 | public function get_numusers($groups=true) { |
273 | global $CFG, $DB; | |
90d3960c | 274 | |
28bcbc38 | 275 | $groupsql = ""; |
276 | $groupwheresql = ""; | |
b50371da | 277 | list($usql, $params) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0'); |
d24832f9 | 278 | |
28bcbc38 | 279 | if ($groups) { |
280 | $groupsql = $this->groupsql; | |
bbadce53 | 281 | $groupwheresql = $this->groupwheresql; |
d24832f9 | 282 | $params = array_merge($params, $this->groupwheresql_params); |
28bcbc38 | 283 | } |
284 | ||
90d3960c | 285 | $countsql = "SELECT COUNT(DISTINCT u.id) |
b50371da | 286 | FROM {user} u |
287 | JOIN {role_assignments} ra ON u.id = ra.userid | |
288 | $groupsql | |
289 | WHERE ra.roleid $usql AND u.deleted = 0 | |
290 | $groupwheresql | |
291 | AND ra.contextid ".get_related_contexts_string($this->context); | |
d24832f9 | 292 | return $DB->count_records_sql($countsql, $params); |
90d3960c | 293 | } |
294 | ||
295 | /** | |
296 | * Sets up this object's group variables, mainly to restrict the selection of users to display. | |
297 | */ | |
0fd8bc00 | 298 | protected function setup_groups() { |
90d3960c | 299 | /// find out current groups mode |
35079f53 | 300 | if ($this->groupmode = groups_get_course_groupmode($this->course)) { |
7a9ba4b4 | 301 | $this->currentgroup = groups_get_course_group($this->course, true); |
35079f53 | 302 | $this->group_selector = groups_print_course_menu($this->course, $this->pbarurl, true); |
7a9ba4b4 | 303 | |
304 | if ($this->groupmode == SEPARATEGROUPS and !$this->currentgroup and !has_capability('moodle/site:accessallgroups', $this->context)) { | |
305 | $this->currentgroup = -2; // means can not accesss any groups at all | |
306 | } | |
35079f53 | 307 | |
308 | if ($this->currentgroup) { | |
309 | $this->groupsql = " JOIN {groups_members} gm ON gm.userid = u.id "; | |
310 | $this->groupwheresql = " AND gm.groupid = :gr_grpid "; | |
311 | $this->groupwheresql_params = array('gr_grpid'=>$this->currentgroup); | |
312 | } | |
90d3960c | 313 | } |
314 | } | |
2e3987a9 | 315 | |
316 | /** | |
317 | * Returns an arrow icon inside an <a> tag, for the purpose of sorting a column. | |
318 | * @param string $direction | |
319770d7 | 319 | * @param moodle_url $sort_link |
2e3987a9 | 320 | * @param string HTML |
321 | */ | |
319770d7 | 322 | protected function get_sort_arrow($direction='move', $sortlink=null) { |
323 | global $OUTPUT; | |
dc482cfa | 324 | $matrix = array('up' => 'desc', 'down' => 'asc', 'move' => 'desc'); |
2e3987a9 | 325 | $strsort = $this->get_lang_string('sort' . $matrix[$direction]); |
dc482cfa | 326 | |
2e3987a9 | 327 | $arrow = print_arrow($direction, $strsort, true); |
75015e5f | 328 | return html_writer::link($sortlink, $arrow, array('title'=>$strsort)); |
2e3987a9 | 329 | } |
61541a5a AD |
330 | |
331 | /** | |
332 | * Optionally blank out course/category totals if they contain any hidden items | |
333 | * @param string $courseid the course id | |
334 | * @param string $course_item an instance of grade_item | |
335 | * @param string $finalgrade the grade for the course_item | |
336 | * @return string The new final grade | |
337 | */ | |
338 | protected function blank_hidden_total($courseid, $course_item, $finalgrade) { | |
339 | global $CFG, $DB; | |
340 | static $hiding_affected = null;//array of items in this course affected by hiding | |
341 | ||
342 | if( $this->showtotalsifcontainhidden==GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN ) { | |
343 | return $finalgrade; | |
344 | } | |
345 | ||
346 | if( !$hiding_affected ) { | |
347 | $items = grade_item::fetch_all(array('courseid'=>$courseid)); | |
348 | $grades = array(); | |
349 | $sql = "SELECT g.* | |
ffe50258 EL |
350 | FROM {grade_grades} g |
351 | JOIN {grade_items} gi ON gi.id = g.itemid | |
61541a5a AD |
352 | WHERE g.userid = {$this->user->id} AND gi.courseid = {$courseid}"; |
353 | if ($gradesrecords = $DB->get_records_sql($sql)) { | |
354 | foreach ($gradesrecords as $grade) { | |
355 | $grades[$grade->itemid] = new grade_grade($grade, false); | |
356 | } | |
357 | unset($gradesrecords); | |
358 | } | |
359 | foreach ($items as $itemid=>$unused) { | |
360 | if (!isset($grades[$itemid])) { | |
361 | $grade_grade = new grade_grade(); | |
362 | $grade_grade->userid = $this->user->id; | |
363 | $grade_grade->itemid = $items[$itemid]->id; | |
364 | $grades[$itemid] = $grade_grade; | |
365 | } | |
366 | $grades[$itemid]->grade_item =& $items[$itemid]; | |
367 | } | |
368 | $hiding_affected = grade_grade::get_hiding_affected($grades, $items); | |
369 | } | |
370 | ||
371 | //if the item definitely depends on a hidden item | |
372 | if (array_key_exists($course_item->id, $hiding_affected['altered'])) { | |
373 | if( !$this->showtotalsifcontainhidden ) { | |
374 | //hide the grade | |
375 | $finalgrade = null; | |
376 | } | |
377 | else { | |
378 | //use reprocessed marks that exclude hidden items | |
379 | $finalgrade = $hiding_affected['altered'][$course_item->id]; | |
380 | } | |
381 | } else if (!empty($hiding_affected['unknown'][$course_item->id])) { | |
382 | //not sure whether or not this item depends on a hidden item | |
383 | if( !$this->showtotalsifcontainhidden ) { | |
384 | //hide the grade | |
385 | $finalgrade = null; | |
386 | } | |
387 | else { | |
388 | //use reprocessed marks that exclude hidden items | |
389 | $finalgrade = $hiding_affected['unknown'][$course_item->id]; | |
390 | } | |
391 | } | |
392 | ||
393 | return $finalgrade; | |
394 | } | |
38b9e8a8 | 395 | } |
6c3ef410 | 396 |