Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * File containing the grade_report class 19 * 20 * @package core_grades 21 * @copyright 2007 Moodle Pty Ltd (http://moodle.com) 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 require_once($CFG->libdir.'/gradelib.php'); 26 27 /** 28 * An abstract class containing variables and methods used by all or most reports. 29 * @copyright 2007 Moodle Pty Ltd (http://moodle.com) 30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 */ 32 abstract class grade_report { 33 /** 34 * The courseid. 35 * @var int $courseid 36 */ 37 public $courseid; 38 39 /** 40 * The course. 41 * @var object $course 42 */ 43 public $course; 44 45 /** Grade plugin return tracking object. 46 * @var object $gpr 47 */ 48 public $gpr; 49 50 /** 51 * The context. 52 * 53 * @var context $context 54 */ 55 public $context; 56 57 /** 58 * The grade_tree object. 59 * @var grade_tree $gtree 60 */ 61 public $gtree; 62 63 /** 64 * User preferences related to this report. 65 * @var array $prefs 66 */ 67 public $prefs = array(); 68 69 /** 70 * The roles for this report. 71 * @var string $gradebookroles 72 */ 73 public $gradebookroles; 74 75 /** 76 * base url for sorting by first/last name. 77 * @var string $baseurl 78 */ 79 public $baseurl; 80 81 /** 82 * base url for paging. 83 * @var string $pbarurl 84 */ 85 public $pbarurl; 86 87 /** 88 * Current page (for paging). 89 * @var int $page 90 */ 91 public $page; 92 93 /** 94 * Array of cached language strings (using get_string() all the time takes a long time!). 95 * @var array $lang_strings 96 */ 97 public $lang_strings = array(); 98 99 // GROUP VARIABLES (including SQL) 100 101 /** 102 * The current group being displayed. 103 * @var int $currentgroup 104 */ 105 public $currentgroup; 106 107 /** 108 * The current groupname being displayed. 109 * @var string $currentgroupname 110 */ 111 public $currentgroupname; 112 113 /** 114 * Current course group mode 115 * @var int $groupmode 116 */ 117 public $groupmode; 118 119 /** 120 * A HTML select element used to select the current group. 121 * @var string $group_selector 122 */ 123 public $group_selector; 124 125 /** 126 * An SQL fragment used to add linking information to the group tables. 127 * @var string $groupsql 128 */ 129 protected $groupsql; 130 131 /** 132 * An SQL constraint to append to the queries used by this object to build the report. 133 * @var string $groupwheresql 134 */ 135 protected $groupwheresql; 136 137 /** 138 * The ordered params for $groupwheresql 139 * @var array $groupwheresql_params 140 */ 141 protected $groupwheresql_params = array(); 142 143 // USER VARIABLES (including SQL). 144 145 /** 146 * An SQL constraint to append to the queries used by this object to build the report. 147 * @var string $userwheresql 148 */ 149 protected $userwheresql; 150 151 /** 152 * The ordered params for $userwheresql 153 * @var array $userwheresql_params 154 */ 155 protected $userwheresql_params = array(); 156 157 /** 158 * Constructor. Sets local copies of user preferences and initialises grade_tree. 159 * @param int $courseid 160 * @param object $gpr grade plugin return tracking object 161 * @param string $context 162 * @param int $page The current page being viewed (when report is paged) 163 */ 164 public function __construct($courseid, $gpr, $context, $page=null) { 165 global $CFG, $COURSE, $DB; 166 167 if (empty($CFG->gradebookroles)) { 168 print_error('norolesdefined', 'grades'); 169 } 170 171 $this->courseid = $courseid; 172 if ($this->courseid == $COURSE->id) { 173 $this->course = $COURSE; 174 } else { 175 $this->course = $DB->get_record('course', array('id' => $this->courseid)); 176 } 177 178 $this->gpr = $gpr; 179 $this->context = $context; 180 $this->page = $page; 181 182 // roles to be displayed in the gradebook 183 $this->gradebookroles = $CFG->gradebookroles; 184 185 // Set up link to preferences page 186 $this->preferences_page = $CFG->wwwroot.'/grade/report/grader/preferences.php?id='.$courseid; 187 188 // init gtree in child class 189 } 190 191 /** 192 * Given the name of a user preference (without grade_report_ prefix), locally saves then returns 193 * the value of that preference. If the preference has already been fetched before, 194 * the saved value is returned. If the preference is not set at the User level, the $CFG equivalent 195 * is given (site default). 196 * Can be called statically, but then doesn't benefit from caching 197 * @param string $pref The name of the preference (do not include the grade_report_ prefix) 198 * @param int $objectid An optional itemid or categoryid to check for a more fine-grained preference 199 * @return mixed The value of the preference 200 */ 201 public function get_pref($pref, $objectid=null) { 202 global $CFG; 203 $fullprefname = 'grade_report_' . $pref; 204 $shortprefname = 'grade_' . $pref; 205 206 $retval = null; 207 208 if (!isset($this) OR get_class($this) != 'grade_report') { 209 if (!empty($objectid)) { 210 $retval = get_user_preferences($fullprefname . $objectid, self::get_pref($pref)); 211 } else if (isset($CFG->$fullprefname)) { 212 $retval = get_user_preferences($fullprefname, $CFG->$fullprefname); 213 } else if (isset($CFG->$shortprefname)) { 214 $retval = get_user_preferences($fullprefname, $CFG->$shortprefname); 215 } else { 216 $retval = null; 217 } 218 } else { 219 if (empty($this->prefs[$pref.$objectid])) { 220 221 if (!empty($objectid)) { 222 $retval = get_user_preferences($fullprefname . $objectid); 223 if (empty($retval)) { 224 // No item pref found, we are returning the global preference 225 $retval = $this->get_pref($pref); 226 $objectid = null; 227 } 228 } else { 229 $retval = get_user_preferences($fullprefname, $CFG->$fullprefname); 230 } 231 $this->prefs[$pref.$objectid] = $retval; 232 } else { 233 $retval = $this->prefs[$pref.$objectid]; 234 } 235 } 236 237 return $retval; 238 } 239 240 /** 241 * Uses set_user_preferences() to update the value of a user preference. If 'default' is given as the value, 242 * the preference will be removed in favour of a higher-level preference. 243 * @param string $pref The name of the preference. 244 * @param mixed $pref_value The value of the preference. 245 * @param int $itemid An optional itemid to which the preference will be assigned 246 * @return bool Success or failure. 247 */ 248 public function set_pref($pref, $pref_value='default', $itemid=null) { 249 $fullprefname = 'grade_report_' . $pref; 250 if ($pref_value == 'default') { 251 return unset_user_preference($fullprefname.$itemid); 252 } else { 253 return set_user_preference($fullprefname.$itemid, $pref_value); 254 } 255 } 256 257 /** 258 * Handles form data sent by this report for this report. Abstract method to implement in all children. 259 * @abstract 260 * @param array $data 261 * @return mixed True or array of errors 262 */ 263 abstract public function process_data($data); 264 265 /** 266 * Processes a single action against a category, grade_item or grade. 267 * @param string $target Sortorder 268 * @param string $action Which action to take (edit, delete etc...) 269 * @return 270 */ 271 abstract public function process_action($target, $action); 272 273 /** 274 * First checks the cached language strings, then returns match if found, or uses get_string() 275 * to get it from the DB, caches it then returns it. 276 * @param string $strcode 277 * @param string $section Optional language section 278 * @return string 279 */ 280 public function get_lang_string($strcode, $section=null) { 281 if (empty($this->lang_strings[$strcode])) { 282 $this->lang_strings[$strcode] = get_string($strcode, $section); 283 } 284 return $this->lang_strings[$strcode]; 285 } 286 287 /** 288 * Fetches and returns a count of all the users that will be shown on this page. 289 * @param boolean $groups include groups limit 290 * @param boolean $users include users limit - default false, used for searching purposes 291 * @return int Count of users 292 */ 293 public function get_numusers($groups = true, $users = false) { 294 global $CFG, $DB; 295 $userwheresql = ""; 296 $groupsql = ""; 297 $groupwheresql = ""; 298 299 // Limit to users with a gradeable role. 300 list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0'); 301 302 // Limit to users with an active enrollment. 303 list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context); 304 305 // We want to query both the current context and parent contexts. 306 list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); 307 308 $params = array_merge($gradebookrolesparams, $enrolledparams, $relatedctxparams); 309 310 if ($users) { 311 $userwheresql = $this->userwheresql; 312 $params = array_merge($params, $this->userwheresql_params); 313 } 314 315 if ($groups) { 316 $groupsql = $this->groupsql; 317 $groupwheresql = $this->groupwheresql; 318 $params = array_merge($params, $this->groupwheresql_params); 319 } 320 321 $sql = "SELECT DISTINCT u.id 322 FROM {user} u 323 JOIN ($enrolledsql) je 324 ON je.id = u.id 325 JOIN {role_assignments} ra 326 ON u.id = ra.userid 327 $groupsql 328 WHERE ra.roleid $gradebookrolessql 329 AND u.deleted = 0 330 $userwheresql 331 $groupwheresql 332 AND ra.contextid $relatedctxsql"; 333 $selectedusers = $DB->get_records_sql($sql, $params); 334 335 $count = 0; 336 // Check if user's enrolment is active and should be displayed. 337 if (!empty($selectedusers)) { 338 $coursecontext = $this->context->get_course_context(true); 339 340 $defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol); 341 $showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol); 342 $showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $coursecontext); 343 344 if ($showonlyactiveenrol) { 345 $useractiveenrolments = get_enrolled_users($coursecontext, '', 0, 'u.id', null, 0, 0, true); 346 } 347 348 foreach ($selectedusers as $id => $value) { 349 if (!$showonlyactiveenrol || ($showonlyactiveenrol && array_key_exists($id, $useractiveenrolments))) { 350 $count++; 351 } 352 } 353 } 354 return $count; 355 } 356 357 /** 358 * Shows support for being used as a 'Grades' report. 359 */ 360 public static function supports_mygrades() { 361 return false; 362 } 363 364 /** 365 * Sets up this object's group variables, mainly to restrict the selection of users to display. 366 */ 367 protected function setup_groups() { 368 // find out current groups mode 369 if ($this->groupmode = groups_get_course_groupmode($this->course)) { 370 if (empty($this->gpr->groupid)) { 371 $this->currentgroup = groups_get_course_group($this->course, true); 372 } else { 373 $this->currentgroup = $this->gpr->groupid; 374 } 375 $this->group_selector = groups_print_course_menu($this->course, $this->pbarurl, true); 376 377 if ($this->groupmode == SEPARATEGROUPS and !$this->currentgroup and !has_capability('moodle/site:accessallgroups', $this->context)) { 378 $this->currentgroup = -2; // means can not access any groups at all 379 } 380 if ($this->currentgroup) { 381 if ($group = groups_get_group($this->currentgroup)) { 382 $this->currentgroupname = $group->name; 383 } 384 $this->groupsql = " JOIN {groups_members} gm ON gm.userid = u.id "; 385 $this->groupwheresql = " AND gm.groupid = :gr_grpid "; 386 $this->groupwheresql_params = array('gr_grpid'=>$this->currentgroup); 387 } 388 } 389 } 390 391 /** 392 * Sets up this report's user criteria to restrict the selection of users to display. 393 */ 394 public function setup_users() { 395 global $SESSION, $DB; 396 397 $this->userwheresql = ""; 398 $this->userwheresql_params = array(); 399 if (isset($SESSION->gradereport['filterfirstname']) && !empty($SESSION->gradereport['filterfirstname'])) { 400 $this->userwheresql .= ' AND '.$DB->sql_like('u.firstname', ':firstname', false, false); 401 $this->userwheresql_params['firstname'] = $SESSION->gradereport['filterfirstname'].'%'; 402 } 403 if (isset($SESSION->gradereport['filtersurname']) && !empty($SESSION->gradereport['filtersurname'])) { 404 $this->userwheresql .= ' AND '.$DB->sql_like('u.lastname', ':lastname', false, false); 405 $this->userwheresql_params['lastname'] = $SESSION->gradereport['filtersurname'].'%'; 406 } 407 } 408 409 /** 410 * Returns an arrow icon inside an <a> tag, for the purpose of sorting a column. 411 * @param string $direction 412 * @param moodle_url $sortlink 413 */ 414 protected function get_sort_arrow($direction='move', $sortlink=null) { 415 global $OUTPUT; 416 $pix = array('up' => 't/sort_desc', 'down' => 't/sort_asc', 'move' => 't/sort'); 417 $matrix = array('up' => 'desc', 'down' => 'asc', 'move' => 'desc'); 418 $strsort = $this->get_lang_string('sort' . $matrix[$direction]); 419 420 $arrow = $OUTPUT->pix_icon($pix[$direction], '', '', ['class' => 'sorticon']); 421 return html_writer::link($sortlink, $arrow, ['title' => $strsort, 'aria-label' => $strsort]); 422 } 423 424 /** 425 * Optionally blank out course/category totals if they contain any hidden items 426 * @param string $courseid the course id 427 * @param string $course_item an instance of grade_item 428 * @param string $finalgrade the grade for the course_item 429 * @return array[] containing values for 'grade', 'grademax', 'grademin', 'aggregationstatus' and 'aggregationweight' 430 */ 431 protected function blank_hidden_total_and_adjust_bounds($courseid, $course_item, $finalgrade) { 432 global $CFG, $DB; 433 static $hiding_affected = null;//array of items in this course affected by hiding 434 435 // If we're dealing with multiple users we need to know when we've moved on to a new user. 436 static $previous_userid = null; 437 438 // If we're dealing with multiple courses we need to know when we've moved on to a new course. 439 static $previous_courseid = null; 440 441 $coursegradegrade = grade_grade::fetch(array('userid'=>$this->user->id, 'itemid'=>$course_item->id)); 442 $grademin = $course_item->grademin; 443 $grademax = $course_item->grademax; 444 if ($coursegradegrade) { 445 $grademin = $coursegradegrade->get_grade_min(); 446 $grademax = $coursegradegrade->get_grade_max(); 447 } else { 448 $coursegradegrade = new grade_grade(array('userid'=>$this->user->id, 'itemid'=>$course_item->id), false); 449 } 450 $hint = $coursegradegrade->get_aggregation_hint(); 451 $aggregationstatus = $hint['status']; 452 $aggregationweight = $hint['weight']; 453 454 if (!is_array($this->showtotalsifcontainhidden)) { 455 debugging('showtotalsifcontainhidden should be an array', DEBUG_DEVELOPER); 456 $this->showtotalsifcontainhidden = array($courseid => $this->showtotalsifcontainhidden); 457 } 458 459 if ($this->showtotalsifcontainhidden[$courseid] == GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN) { 460 return array('grade' => $finalgrade, 461 'grademin' => $grademin, 462 'grademax' => $grademax, 463 'aggregationstatus' => $aggregationstatus, 464 'aggregationweight' => $aggregationweight); 465 } 466 467 // If we've moved on to another course or user, reload the grades. 468 if ($previous_userid != $this->user->id || $previous_courseid != $courseid) { 469 $hiding_affected = null; 470 $previous_userid = $this->user->id; 471 $previous_courseid = $courseid; 472 } 473 474 if (!$hiding_affected) { 475 $items = grade_item::fetch_all(array('courseid'=>$courseid)); 476 $grades = array(); 477 $sql = "SELECT g.* 478 FROM {grade_grades} g 479 JOIN {grade_items} gi ON gi.id = g.itemid 480 WHERE g.userid = {$this->user->id} AND gi.courseid = {$courseid}"; 481 if ($gradesrecords = $DB->get_records_sql($sql)) { 482 foreach ($gradesrecords as $grade) { 483 $grades[$grade->itemid] = new grade_grade($grade, false); 484 } 485 unset($gradesrecords); 486 } 487 foreach ($items as $itemid => $unused) { 488 if (!isset($grades[$itemid])) { 489 $grade_grade = new grade_grade(); 490 $grade_grade->userid = $this->user->id; 491 $grade_grade->itemid = $items[$itemid]->id; 492 $grades[$itemid] = $grade_grade; 493 } 494 $grades[$itemid]->grade_item =& $items[$itemid]; 495 } 496 $hiding_affected = grade_grade::get_hiding_affected($grades, $items); 497 } 498 499 //if the item definitely depends on a hidden item 500 if (array_key_exists($course_item->id, $hiding_affected['altered']) || 501 array_key_exists($course_item->id, $hiding_affected['alteredgrademin']) || 502 array_key_exists($course_item->id, $hiding_affected['alteredgrademax']) || 503 array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus']) || 504 array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { 505 if (!$this->showtotalsifcontainhidden[$courseid] && array_key_exists($course_item->id, $hiding_affected['altered'])) { 506 // Hide the grade, but only when it has changed. 507 $finalgrade = null; 508 } else { 509 //use reprocessed marks that exclude hidden items 510 if (array_key_exists($course_item->id, $hiding_affected['altered'])) { 511 $finalgrade = $hiding_affected['altered'][$course_item->id]; 512 } 513 if (array_key_exists($course_item->id, $hiding_affected['alteredgrademin'])) { 514 $grademin = $hiding_affected['alteredgrademin'][$course_item->id]; 515 } 516 if (array_key_exists($course_item->id, $hiding_affected['alteredgrademax'])) { 517 $grademax = $hiding_affected['alteredgrademax'][$course_item->id]; 518 } 519 if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus'])) { 520 $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id]; 521 } 522 if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { 523 $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id]; 524 } 525 526 if (!$this->showtotalsifcontainhidden[$courseid]) { 527 // If the course total is hidden we must hide the weight otherwise 528 // it can be used to compute the course total. 529 $aggregationstatus = 'unknown'; 530 $aggregationweight = null; 531 } 532 } 533 } else if (array_key_exists($course_item->id, $hiding_affected['unknowngrades'])) { 534 //not sure whether or not this item depends on a hidden item 535 if (!$this->showtotalsifcontainhidden[$courseid]) { 536 //hide the grade 537 $finalgrade = null; 538 } else { 539 //use reprocessed marks that exclude hidden items 540 $finalgrade = $hiding_affected['unknowngrades'][$course_item->id]; 541 542 if (array_key_exists($course_item->id, $hiding_affected['alteredgrademin'])) { 543 $grademin = $hiding_affected['alteredgrademin'][$course_item->id]; 544 } 545 if (array_key_exists($course_item->id, $hiding_affected['alteredgrademax'])) { 546 $grademax = $hiding_affected['alteredgrademax'][$course_item->id]; 547 } 548 if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus'])) { 549 $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id]; 550 } 551 if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { 552 $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id]; 553 } 554 } 555 } 556 557 return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax, 'aggregationstatus'=>$aggregationstatus, 'aggregationweight'=>$aggregationweight); 558 } 559 560 /** 561 * Optionally blank out course/category totals if they contain any hidden items 562 * @deprecated since Moodle 2.8 - Call blank_hidden_total_and_adjust_bounds instead. 563 * @param string $courseid the course id 564 * @param string $course_item an instance of grade_item 565 * @param string $finalgrade the grade for the course_item 566 * @return string The new final grade 567 */ 568 protected function blank_hidden_total($courseid, $course_item, $finalgrade) { 569 // Note it is flawed to call this function directly because 570 // the aggregated grade does not make sense without the updated min and max information. 571 572 debugging('grade_report::blank_hidden_total() is deprecated. 573 Call grade_report::blank_hidden_total_and_adjust_bounds instead.', DEBUG_DEVELOPER); 574 $result = $this->blank_hidden_total_and_adjust_bounds($courseid, $course_item, $finalgrade); 575 return $result['grade']; 576 } 577 } 578
title
Description
Body
title
Description
Body
title
Description
Body
title
Body