Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402] [Versions 402 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 * Definition of the grade_overview_report class 19 * 20 * @package gradereport_overview 21 * @copyright 2007 Nicolas Connault 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 require_once($CFG->dirroot . '/grade/report/lib.php'); 26 require_once($CFG->libdir.'/tablelib.php'); 27 28 /** 29 * Class providing an API for the overview report building and displaying. 30 * @uses grade_report 31 * @package gradereport_overview 32 */ 33 class grade_report_overview extends grade_report { 34 35 /** 36 * The user's courses 37 * @var array $courses 38 */ 39 public $courses; 40 41 /** 42 * A flexitable to hold the data. 43 * @var object $table 44 */ 45 public $table; 46 47 /** 48 * Show student ranks within each course. 49 * @var array $showrank 50 */ 51 public $showrank; 52 53 /** 54 * An array of course ids that the user is a student in. 55 * @var array $studentcourseids 56 */ 57 public $studentcourseids; 58 59 /** 60 * An array of courses that the user is a teacher in. 61 * @var array $teachercourses 62 */ 63 public $teachercourses; 64 65 /** 66 * Constructor. Sets local copies of user preferences and initialises grade_tree. 67 * @param int $userid 68 * @param object $gpr grade plugin return tracking object 69 * @param string $context 70 */ 71 public function __construct($userid, $gpr, $context) { 72 global $CFG, $COURSE, $DB, $USER; 73 parent::__construct($COURSE->id, $gpr, $context); 74 75 // Get the user (for full name). 76 $this->user = $DB->get_record('user', array('id' => $userid)); 77 78 // Set onlyactive flag to true if the user's viewing his/her report. 79 $onlyactive = ($this->user->id === $USER->id); 80 81 // Load the user's courses. 82 $this->courses = enrol_get_users_courses($this->user->id, $onlyactive, 'id, shortname, showgrades'); 83 84 $this->showrank = array(); 85 $this->showrank['any'] = false; 86 87 $this->showtotalsifcontainhidden = array(); 88 89 $this->studentcourseids = array(); 90 $this->teachercourses = array(); 91 $roleids = explode(',', get_config('moodle', 'gradebookroles')); 92 93 if ($this->courses) { 94 foreach ($this->courses as $course) { 95 $this->showrank[$course->id] = grade_get_setting($course->id, 'report_overview_showrank', !empty($CFG->grade_report_overview_showrank)); 96 if ($this->showrank[$course->id]) { 97 $this->showrank['any'] = true; 98 } 99 100 $this->showtotalsifcontainhidden[$course->id] = grade_get_setting($course->id, 'report_overview_showtotalsifcontainhidden', $CFG->grade_report_overview_showtotalsifcontainhidden); 101 102 $coursecontext = context_course::instance($course->id); 103 104 foreach ($roleids as $roleid) { 105 if (user_has_role_assignment($userid, $roleid, $coursecontext->id)) { 106 $this->studentcourseids[$course->id] = $course->id; 107 // We only need to check if one of the roleids has been assigned. 108 break; 109 } 110 } 111 112 if (has_capability('moodle/grade:viewall', $coursecontext, $userid)) { 113 $this->teachercourses[$course->id] = $course; 114 } 115 } 116 } 117 118 119 // base url for sorting by first/last name 120 $this->baseurl = $CFG->wwwroot.'/grade/overview/index.php?id='.$userid; 121 $this->pbarurl = $this->baseurl; 122 123 $this->setup_table(); 124 } 125 126 /** 127 * Regrades all courses if needed. 128 * 129 * If $frontend is true, this may show a progress bar and redirect back to the page (possibly 130 * several times if multiple courses need it). Otherwise, it will not return until all the 131 * courses have been updated. 132 * 133 * @param bool $frontend True if we are running front-end code and can safely redirect back 134 */ 135 public function regrade_all_courses_if_needed(bool $frontend = false): void { 136 foreach ($this->courses as $course) { 137 if ($frontend) { 138 grade_regrade_final_grades_if_required($course); 139 } else { 140 grade_regrade_final_grades($course->id); 141 } 142 } 143 } 144 145 /** 146 * Prepares the headers and attributes of the flexitable. 147 */ 148 public function setup_table() { 149 /* 150 * Table has 3 columns 151 *| course | final grade | rank (optional) | 152 */ 153 154 // setting up table headers 155 if ($this->showrank['any']) { 156 $tablecolumns = array('coursename', 'grade', 'rank'); 157 $tableheaders = array(grade_helper::get_lang_string('coursename', 'grades'), 158 grade_helper::get_lang_string('gradenoun'), 159 grade_helper::get_lang_string('rank', 'grades')); 160 } else { 161 $tablecolumns = array('coursename', 'grade'); 162 $tableheaders = array(grade_helper::get_lang_string('coursename', 'grades'), 163 grade_helper::get_lang_string('gradenoun')); 164 } 165 $this->table = new flexible_table('grade-report-overview-'.$this->user->id); 166 167 $this->table->define_columns($tablecolumns); 168 $this->table->define_headers($tableheaders); 169 $this->table->define_baseurl($this->baseurl); 170 171 $this->table->set_attribute('cellspacing', '0'); 172 $this->table->set_attribute('id', 'overview-grade'); 173 $this->table->set_attribute('class', 'boxaligncenter generaltable'); 174 175 $this->table->setup(); 176 } 177 178 /** 179 * Set up the courses grades data for the report. 180 * 181 * @param bool $studentcoursesonly Only show courses that the user is a student of. 182 * @return array of course grades information 183 */ 184 public function setup_courses_data($studentcoursesonly) { 185 global $USER, $DB; 186 187 $coursesdata = array(); 188 $numusers = $this->get_numusers(false); 189 190 foreach ($this->courses as $course) { 191 if (!$course->showgrades) { 192 continue; 193 } 194 195 // If we are only showing student courses and this course isn't part of the group, then move on. 196 if ($studentcoursesonly && !isset($this->studentcourseids[$course->id])) { 197 continue; 198 } 199 200 $coursecontext = context_course::instance($course->id); 201 202 if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { 203 // The course is hidden and the user isn't allowed to see it. 204 continue; 205 } 206 207 if (!has_capability('moodle/user:viewuseractivitiesreport', context_user::instance($this->user->id)) && 208 ((!has_capability('moodle/grade:view', $coursecontext) || $this->user->id != $USER->id) && 209 !has_capability('moodle/grade:viewall', $coursecontext))) { 210 continue; 211 } 212 213 $coursesdata[$course->id]['course'] = $course; 214 $coursesdata[$course->id]['context'] = $coursecontext; 215 216 $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); 217 218 // Get course grade_item. 219 $courseitem = grade_item::fetch_course_item($course->id); 220 221 // Get the stored grade. 222 $coursegrade = new grade_grade(array('itemid' => $courseitem->id, 'userid' => $this->user->id)); 223 $coursegrade->grade_item =& $courseitem; 224 $finalgrade = $coursegrade->finalgrade; 225 226 if (!$canviewhidden and !is_null($finalgrade)) { 227 if ($coursegrade->is_hidden()) { 228 $finalgrade = null; 229 } else { 230 $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($course->id, 231 $courseitem, 232 $finalgrade); 233 234 // We temporarily adjust the view of this grade item - because the min and 235 // max are affected by the hidden values in the aggregation. 236 $finalgrade = $adjustedgrade['grade']; 237 $courseitem->grademax = $adjustedgrade['grademax']; 238 $courseitem->grademin = $adjustedgrade['grademin']; 239 } 240 } else { 241 // We must use the specific max/min because it can be different for 242 // each grade_grade when items are excluded from sum of grades. 243 if (!is_null($finalgrade)) { 244 $courseitem->grademin = $coursegrade->get_grade_min(); 245 $courseitem->grademax = $coursegrade->get_grade_max(); 246 } 247 } 248 249 $coursesdata[$course->id]['finalgrade'] = $finalgrade; 250 $coursesdata[$course->id]['courseitem'] = $courseitem; 251 252 if ($this->showrank['any'] && $this->showrank[$course->id] && !is_null($finalgrade)) { 253 // Find the number of users with a higher grade. 254 // Please note this can not work if hidden grades involved :-( to be fixed in 2.0. 255 $params = array($finalgrade, $courseitem->id); 256 $sql = "SELECT COUNT(DISTINCT(userid)) 257 FROM {grade_grades} 258 WHERE finalgrade IS NOT NULL AND finalgrade > ? 259 AND itemid = ?"; 260 $rank = $DB->count_records_sql($sql, $params) + 1; 261 262 $coursesdata[$course->id]['rank'] = $rank; 263 $coursesdata[$course->id]['numusers'] = $numusers; 264 } 265 } 266 return $coursesdata; 267 } 268 269 /** 270 * Fill the table for displaying. 271 * 272 * @param bool $activitylink If this report link to the activity report or the user report. 273 * @param bool $studentcoursesonly Only show courses that the user is a student of. 274 */ 275 public function fill_table($activitylink = false, $studentcoursesonly = false) { 276 global $CFG, $DB, $OUTPUT, $USER; 277 278 if ($studentcoursesonly && count($this->studentcourseids) == 0) { 279 return false; 280 } 281 282 // Only show user's courses instead of all courses. 283 if ($this->courses) { 284 $coursesdata = $this->setup_courses_data($studentcoursesonly); 285 286 // Check whether current user can view all grades of this user - parent most probably. 287 $viewasuser = $this->course->showgrades && has_any_capability([ 288 'moodle/grade:viewall', 289 'moodle/user:viewuseractivitiesreport', 290 ], context_user::instance($this->user->id)); 291 292 foreach ($coursesdata as $coursedata) { 293 294 $course = $coursedata['course']; 295 $coursecontext = $coursedata['context']; 296 $finalgrade = $coursedata['finalgrade']; 297 $courseitem = $coursedata['courseitem']; 298 299 $coursenamelink = format_string(get_course_display_name_for_list($course), true, ['context' => $coursecontext]); 300 301 // Link to the course grade report pages (performing same capability checks as the pages themselves). 302 if ($activitylink && 303 (has_capability('gradereport/' . $CFG->grade_profilereport .':view', $coursecontext) || $viewasuser)) { 304 305 $coursenamelink = html_writer::link(new moodle_url('/course/user.php', [ 306 'mode' => 'grade', 307 'id' => $course->id, 308 'user' => $this->user->id, 309 ]), $coursenamelink); 310 } else if (!$activitylink && (has_capability('gradereport/user:view', $coursecontext) || $viewasuser)) { 311 $coursenamelink = html_writer::link(new moodle_url('/grade/report/user/index.php', [ 312 'id' => $course->id, 313 'userid' => $this->user->id, 314 'group' => $this->gpr->groupid, 315 ]), $coursenamelink); 316 } 317 318 $data = [$coursenamelink, grade_format_gradevalue($finalgrade, $courseitem, true)]; 319 320 if ($this->showrank['any']) { 321 if ($this->showrank[$course->id] && !is_null($finalgrade)) { 322 $rank = $coursedata['rank']; 323 $numusers = $coursedata['numusers']; 324 $data[] = "$rank/$numusers"; 325 } else { 326 // No grade, no rank. 327 // Or this course wants rank hidden. 328 $data[] = '-'; 329 } 330 } 331 332 $this->table->add_data($data); 333 } 334 335 return true; 336 } else { 337 echo $OUTPUT->notification(get_string('notenrolled', 'grades'), 'notifymessage'); 338 return false; 339 } 340 } 341 342 /** 343 * Prints or returns the HTML from the flexitable. 344 * @param bool $return Whether or not to return the data instead of printing it directly. 345 * @return string 346 */ 347 public function print_table($return=false) { 348 ob_start(); 349 $this->table->print_html(); 350 $html = ob_get_clean(); 351 if ($return) { 352 return $html; 353 } else { 354 echo $html; 355 } 356 } 357 358 /** 359 * Print a table to show courses that the user is able to grade. 360 */ 361 public function print_teacher_table() { 362 $table = new html_table(); 363 $table->head = array(get_string('coursename', 'grades')); 364 $table->data = null; 365 foreach ($this->teachercourses as $courseid => $course) { 366 $coursecontext = context_course::instance($course->id); 367 $coursenamelink = format_string($course->fullname, true, ['context' => $coursecontext]); 368 $url = new moodle_url('/grade/report/index.php', array('id' => $courseid)); 369 $table->data[] = array(html_writer::link($url, $coursenamelink)); 370 } 371 echo html_writer::table($table); 372 } 373 374 /** 375 * Processes the data sent by the form (grades and feedbacks). 376 * @param array $data 377 * @return bool Success or Failure (array of errors). 378 */ 379 function process_data($data) { 380 } 381 function process_action($target, $action) { 382 } 383 384 /** 385 * This report supports being set as the 'grades' report. 386 */ 387 public static function supports_mygrades() { 388 return true; 389 } 390 391 /** 392 * Check if the user can access the report. 393 * 394 * @param stdClass $systemcontext system context 395 * @param stdClass $context course context 396 * @param stdClass $personalcontext personal context 397 * @param stdClass $course course object 398 * @param int $userid userid 399 * @return bool true if the user can access the report 400 * @since Moodle 3.2 401 */ 402 public static function check_access($systemcontext, $context, $personalcontext, $course, $userid) { 403 global $USER; 404 405 $access = false; 406 if (has_capability('moodle/grade:viewall', $systemcontext)) { 407 // Ok - can view all course grades. 408 $access = true; 409 410 } else if (has_capability('moodle/grade:viewall', $context)) { 411 // Ok - can view any grades in context. 412 $access = true; 413 414 } else if ($userid == $USER->id and ((has_capability('moodle/grade:view', $context) and $course->showgrades) 415 || $course->id == SITEID)) { 416 // Ok - can view own course grades. 417 $access = true; 418 419 } else if (has_capability('moodle/grade:viewall', $personalcontext) and $course->showgrades) { 420 // Ok - can view grades of this user - parent most probably. 421 $access = true; 422 } else if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and $course->showgrades) { 423 // Ok - can view grades of this user - parent most probably. 424 $access = true; 425 } 426 return $access; 427 } 428 429 /** 430 * Trigger the grade_report_viewed event 431 * 432 * @param stdClass $context course context 433 * @param int $courseid course id 434 * @param int $userid user id 435 * @since Moodle 3.2 436 */ 437 public static function viewed($context, $courseid, $userid) { 438 $event = \gradereport_overview\event\grade_report_viewed::create( 439 array( 440 'context' => $context, 441 'courseid' => $courseid, 442 'relateduserid' => $userid, 443 ) 444 ); 445 $event->trigger(); 446 } 447 } 448 449 function grade_report_overview_settings_definition(&$mform) { 450 global $CFG; 451 452 //show rank 453 $options = array(-1 => get_string('default', 'grades'), 454 0 => get_string('hide'), 455 1 => get_string('show')); 456 457 if (empty($CFG->grade_report_overview_showrank)) { 458 $options[-1] = get_string('defaultprev', 'grades', $options[0]); 459 } else { 460 $options[-1] = get_string('defaultprev', 'grades', $options[1]); 461 } 462 463 $mform->addElement('select', 'report_overview_showrank', get_string('showrank', 'grades'), $options); 464 $mform->addHelpButton('report_overview_showrank', 'showrank', 'grades'); 465 466 //showtotalsifcontainhidden 467 $options = array(-1 => get_string('default', 'grades'), 468 GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN => get_string('hide'), 469 GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowexhiddenitems', 'grades'), 470 GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN => get_string('hidetotalshowinchiddenitems', 'grades') ); 471 472 if (!array_key_exists($CFG->grade_report_overview_showtotalsifcontainhidden, $options)) { 473 $options[-1] = get_string('defaultprev', 'grades', $options[0]); 474 } else { 475 $options[-1] = get_string('defaultprev', 'grades', $options[$CFG->grade_report_overview_showtotalsifcontainhidden]); 476 } 477 478 $mform->addElement('select', 'report_overview_showtotalsifcontainhidden', get_string('hidetotalifhiddenitems', 'grades'), $options); 479 $mform->addHelpButton('report_overview_showtotalsifcontainhidden', 'hidetotalifhiddenitems', 'grades'); 480 } 481 482 /** 483 * Add nodes to myprofile page. 484 * 485 * @param \core_user\output\myprofile\tree $tree Tree object 486 * @param stdClass $user user object 487 * @param bool $iscurrentuser 488 * @param stdClass $course Course object 489 */ 490 function gradereport_overview_myprofile_navigation(core_user\output\myprofile\tree $tree, $user, $iscurrentuser, $course) { 491 if (empty($course)) { 492 // We want to display these reports under the site context. 493 $course = get_fast_modinfo(SITEID)->get_course(); 494 } 495 $systemcontext = context_system::instance(); 496 $usercontext = context_user::instance($user->id); 497 $coursecontext = context_course::instance($course->id); 498 if (grade_report_overview::check_access($systemcontext, $coursecontext, $usercontext, $course, $user->id)) { 499 $url = new moodle_url('/grade/report/overview/index.php', array('userid' => $user->id, 'id' => $course->id)); 500 $node = new core_user\output\myprofile\node('reports', 'grades', get_string('gradesoverview', 'gradereport_overview'), 501 null, $url); 502 $tree->add_node($node); 503 } 504 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body