/** * Fill the table with data. * * @param $element - An array containing the table data for the current row. */ private function fill_table_recursive(&$element) { global $DB, $CFG; $type = $element['type']; $depth = $element['depth']; $grade_object = $element['object']; $eid = $grade_object->id; $element['userid'] = $this->user->id; $fullname = $this->gtree->get_element_header($element, true, true, true, true, true); $data = array(); $gradeitemdata = array(); $hidden = ''; $excluded = ''; $itemlevel = $type == 'categoryitem' || $type == 'category' || $type == 'courseitem' ? $depth : $depth + 1; $class = 'level' . $itemlevel . ' level' . ($itemlevel % 2 ? 'odd' : 'even'); $classfeedback = ''; // If this is a hidden grade category, hide it completely from the user if ($type == 'category' && $grade_object->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_object->is_hiddenuntil())) { return false; } if ($type == 'category') { $this->evenodd[$depth] = ($this->evenodd[$depth] + 1) % 2; } $alter = $this->evenodd[$depth] == 0 ? 'even' : 'odd'; /// Process those items that have scores associated if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') { $header_row = "row_{$eid}_{$this->user->id}"; $header_cat = "cat_{$grade_object->categoryid}_{$this->user->id}"; if (!($grade_grade = grade_grade::fetch(array('itemid' => $grade_object->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $grade_object->id; } $grade_grade->load_grade_item(); /// Hidden Items if ($grade_grade->grade_item->is_hidden()) { $hidden = ' dimmed_text'; } $hide = false; // If this is a hidden grade item, hide it completely from the user. if ($grade_grade->is_hidden() && !$this->canviewhidden && ($this->showhiddenitems == GRADE_REPORT_USER_HIDE_HIDDEN || $this->showhiddenitems == GRADE_REPORT_USER_HIDE_UNTIL && !$grade_grade->is_hiddenuntil())) { $hide = true; } else { if (!empty($grade_object->itemmodule) && !empty($grade_object->iteminstance)) { // The grade object can be marked visible but still be hidden if // the student cannot see the activity due to conditional access // and it's set to be hidden entirely. $instances = $this->modinfo->get_instances_of($grade_object->itemmodule); if (!empty($instances[$grade_object->iteminstance])) { $cm = $instances[$grade_object->iteminstance]; $gradeitemdata['cmid'] = $cm->id; if (!$cm->uservisible) { // If there is 'availableinfo' text then it is only greyed // out and not entirely hidden. if (!$cm->availableinfo) { $hide = true; } } } } } // Actual Grade - We need to calculate this whether the row is hidden or not. $gradeval = $grade_grade->finalgrade; $hint = $grade_grade->get_aggregation_hint(); if (!$this->canviewhidden) { /// Virtual Grade (may be calculated excluding hidden items etc). $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($this->courseid, $grade_grade->grade_item, $gradeval); $gradeval = $adjustedgrade['grade']; // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $grade_grade->grade_item->grademax = $adjustedgrade['grademax']; $grade_grade->grade_item->grademin = $adjustedgrade['grademin']; $hint['status'] = $adjustedgrade['aggregationstatus']; $hint['weight'] = $adjustedgrade['aggregationweight']; } else { // The max and min for an aggregation may be different to the grade_item. if (!is_null($gradeval)) { $grade_grade->grade_item->grademax = $grade_grade->get_grade_max(); $grade_grade->grade_item->grademin = $grade_grade->get_grade_min(); } } if (!$hide) { /// Excluded Item /** if ($grade_grade->is_excluded()) { $fullname .= ' ['.get_string('excluded', 'grades').']'; $excluded = ' excluded'; } **/ /// Other class information $class .= $hidden . $excluded; if ($this->switch) { // alter style based on whether aggregation is first or last $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggt b2b" : " item b1b"; } else { $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " " . $alter . "d{$depth} baggb" : " item b1b"; } if ($type == 'categoryitem' or $type == 'courseitem') { $header_cat = "cat_{$grade_object->iteminstance}_{$this->user->id}"; } /// Name $data['itemname']['content'] = $fullname; $data['itemname']['class'] = $class; $data['itemname']['colspan'] = $this->maxdepth - $depth; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = $header_row; // Basic grade item information. $gradeitemdata['id'] = $grade_object->id; $gradeitemdata['itemtype'] = $grade_object->itemtype; $gradeitemdata['itemmodule'] = $grade_object->itemmodule; $gradeitemdata['iteminstance'] = $grade_object->iteminstance; $gradeitemdata['itemnumber'] = $grade_object->itemnumber; $gradeitemdata['categoryid'] = $grade_object->categoryid; $gradeitemdata['outcomeid'] = $grade_object->outcomeid; $gradeitemdata['scaleid'] = $grade_object->outcomeid; if ($this->showfeedback) { // Copy $class before appending itemcenter as feedback should not be centered $classfeedback = $class; } $class .= " itemcenter "; if ($this->showweight) { $data['weight']['class'] = $class; $data['weight']['content'] = '-'; $data['weight']['headers'] = "{$header_cat} {$header_row} weight"; // has a weight assigned, might be extra credit // This obliterates the weight because it provides a more informative description. if (is_numeric($hint['weight'])) { $data['weight']['content'] = format_float($hint['weight'] * 100.0, 2) . ' %'; $gradeitemdata['weightraw'] = $hint['weight']; $gradeitemdata['weightformatted'] = $data['weight']['content']; } if ($hint['status'] != 'used' && $hint['status'] != 'unknown') { $data['weight']['content'] .= '<br>' . get_string('aggregationhint' . $hint['status'], 'grades'); $gradeitemdata['status'] = $hint['status']; } } if ($this->showgrade) { $gradeitemdata['graderaw'] = null; $gradeitemdata['gradehiddenbydate'] = false; $gradeitemdata['gradeneedsupdate'] = $grade_grade->grade_item->needsupdate; $gradeitemdata['gradeishidden'] = $grade_grade->is_hidden(); $gradeitemdata['gradedatesubmitted'] = $grade_grade->get_datesubmitted(); $gradeitemdata['gradedategraded'] = $grade_grade->get_dategraded(); if ($grade_grade->grade_item->needsupdate) { $data['grade']['class'] = $class . ' gradingerror'; $data['grade']['content'] = get_string('error'); } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden() and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) { // 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 $class .= ' datesubmitted'; $data['grade']['class'] = $class; $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))); $gradeitemdata['gradehiddenbydate'] = true; } else { if ($grade_grade->is_hidden()) { $data['grade']['class'] = $class . ' dimmed_text'; $data['grade']['content'] = '-'; if ($this->canviewhidden) { $gradeitemdata['graderaw'] = $gradeval; $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); } } else { $data['grade']['class'] = $class; $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true); $gradeitemdata['graderaw'] = $gradeval; } } } $data['grade']['headers'] = "{$header_cat} {$header_row} grade"; $gradeitemdata['gradeformatted'] = $data['grade']['content']; } // Range if ($this->showrange) { $data['range']['class'] = $class; $data['range']['content'] = $grade_grade->grade_item->get_formatted_range(GRADE_DISPLAY_TYPE_REAL, $this->rangedecimals); $data['range']['headers'] = "{$header_cat} {$header_row} range"; $gradeitemdata['rangeformatted'] = $data['range']['content']; $gradeitemdata['grademin'] = $grade_grade->grade_item->grademin; $gradeitemdata['grademax'] = $grade_grade->grade_item->grademax; } // Percentage if ($this->showpercentage) { if ($grade_grade->grade_item->needsupdate) { $data['percentage']['class'] = $class . ' gradingerror'; $data['percentage']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['percentage']['class'] = $class . ' dimmed_text'; $data['percentage']['content'] = '-'; if ($this->canviewhidden) { $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } else { $data['percentage']['class'] = $class; $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE); } } $data['percentage']['headers'] = "{$header_cat} {$header_row} percentage"; $gradeitemdata['percentageformatted'] = $data['percentage']['content']; } // Lettergrade if ($this->showlettergrade) { if ($grade_grade->grade_item->needsupdate) { $data['lettergrade']['class'] = $class . ' gradingerror'; $data['lettergrade']['content'] = get_string('error'); } else { if ($grade_grade->is_hidden()) { $data['lettergrade']['class'] = $class . ' dimmed_text'; if (!$this->canviewhidden) { $data['lettergrade']['content'] = '-'; } else { $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } else { $data['lettergrade']['class'] = $class; $data['lettergrade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_LETTER); } } $data['lettergrade']['headers'] = "{$header_cat} {$header_row} lettergrade"; $gradeitemdata['lettergradeformatted'] = $data['lettergrade']['content']; } // Rank if ($this->showrank) { $gradeitemdata['rank'] = 0; if ($grade_grade->grade_item->needsupdate) { $data['rank']['class'] = $class . ' gradingerror'; $data['rank']['content'] = get_string('error'); } elseif ($grade_grade->is_hidden()) { $data['rank']['class'] = $class . ' dimmed_text'; $data['rank']['content'] = '-'; } else { if (is_null($gradeval)) { // no grade, no rank $data['rank']['class'] = $class; $data['rank']['content'] = '-'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade > ?\n AND itemid = ?\n AND hidden = 0"; $rank = $DB->count_records_sql($sql, array($grade_grade->finalgrade, $grade_grade->grade_item->id)) + 1; $data['rank']['class'] = $class; $numusers = $this->get_numusers(false); $data['rank']['content'] = "{$rank}/{$numusers}"; // Total course users. $gradeitemdata['rank'] = $rank; $gradeitemdata['numusers'] = $numusers; } } $data['rank']['headers'] = "{$header_cat} {$header_row} rank"; } // Average if ($this->showaverage) { $gradeitemdata['averageformatted'] = ''; $data['average']['class'] = $class; if (!empty($this->gtree->items[$eid]->avg)) { $data['average']['content'] = $this->gtree->items[$eid]->avg; $gradeitemdata['averageformatted'] = $this->gtree->items[$eid]->avg; } else { $data['average']['content'] = '-'; } $data['average']['headers'] = "{$header_cat} {$header_row} average"; } // Feedback if ($this->showfeedback) { $gradeitemdata['feedback'] = ''; $gradeitemdata['feedbackformat'] = $grade_grade->feedbackformat; if ($grade_grade->overridden > 0 and ($type == 'categoryitem' or $type == 'courseitem')) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = get_string('overridden', 'grades') . ': ' . format_text($grade_grade->feedback, $grade_grade->feedbackformat); $gradeitemdata['feedback'] = $grade_grade->feedback; } else { if (empty($grade_grade->feedback) or !$this->canviewhidden and $grade_grade->is_hidden()) { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = ' '; } else { $data['feedback']['class'] = $classfeedback . ' feedbacktext'; $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat); $gradeitemdata['feedback'] = $grade_grade->feedback; } } $data['feedback']['headers'] = "{$header_cat} {$header_row} feedback"; } // Contribution to the course total column. if ($this->showcontributiontocoursetotal) { $data['contributiontocoursetotal']['class'] = $class; $data['contributiontocoursetotal']['content'] = '-'; $data['contributiontocoursetotal']['headers'] = "{$header_cat} {$header_row} contributiontocoursetotal"; } $this->gradeitemsdata[] = $gradeitemdata; } // We collect the aggregation hints whether they are hidden or not. if ($this->showcontributiontocoursetotal) { $hint['grademax'] = $grade_grade->grade_item->grademax; $hint['grademin'] = $grade_grade->grade_item->grademin; $hint['grade'] = $gradeval; $parent = $grade_object->load_parent_category(); if ($grade_object->is_category_item()) { $parent = $parent->load_parent_category(); } $hint['parent'] = $parent->load_grade_item()->id; $this->aggregationhints[$grade_grade->itemid] = $hint; } } /// Category if ($type == 'category') { $data['leader']['class'] = $class . ' ' . $alter . "d{$depth} b1t b2b b1l"; $data['leader']['rowspan'] = $element['rowspan']; if ($this->switch) { // alter style based on whether aggregation is first or last $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b1b b1t"; } else { $data['itemname']['class'] = $class . ' ' . $alter . "d{$depth} b2t"; } $data['itemname']['colspan'] = $this->maxdepth - $depth + count($this->tablecolumns) - 1; $data['itemname']['content'] = $fullname; $data['itemname']['celltype'] = 'th'; $data['itemname']['id'] = "cat_{$grade_object->id}_{$this->user->id}"; } /// Add this row to the overall system foreach ($data as $key => $celldata) { $data[$key]['class'] .= ' column-' . $key; } $this->tabledata[] = $data; /// Recursively iterate through all child elements if (isset($element['children'])) { foreach ($element['children'] as $key => $child) { $this->fill_table_recursive($element['children'][$key]); } } // Check we are showing this column, and we are looking at the root of the table. // This should be the very last thing this fill_table_recursive function does. if ($this->showcontributiontocoursetotal && ($type == 'category' && $depth == 1)) { // We should have collected all the hints by now - walk the tree again and build the contributions column. $this->fill_contributions_column($element); } }
/** * Set up the courses grades data for the report. * * @param bool $studentcoursesonly Only show courses that the user is a student of. * @return array of course grades information */ public function setup_courses_data($studentcoursesonly) { global $USER, $DB; $coursesdata = array(); $numusers = $this->get_numusers(false); foreach ($this->courses as $course) { if (!$course->showgrades) { continue; } // If we are only showing student courses and this course isn't part of the group, then move on. if ($studentcoursesonly && !isset($this->studentcourseids[$course->id])) { continue; } $coursecontext = context_course::instance($course->id); if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { // The course is hidden and the user isn't allowed to see it. continue; } if (!has_capability('moodle/user:viewuseractivitiesreport', context_user::instance($this->user->id)) && ((!has_capability('moodle/grade:view', $coursecontext) || $this->user->id != $USER->id) && !has_capability('moodle/grade:viewall', $coursecontext))) { continue; } $coursesdata[$course->id]['course'] = $course; $coursesdata[$course->id]['context'] = $coursecontext; $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Get course grade_item. $courseitem = grade_item::fetch_course_item($course->id); // Get the stored grade. $coursegrade = new grade_grade(array('itemid' => $courseitem->id, 'userid' => $this->user->id)); $coursegrade->grade_item =& $courseitem; $finalgrade = $coursegrade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($coursegrade->is_hidden()) { $finalgrade = null; } else { $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($course->id, $courseitem, $finalgrade); // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $finalgrade = $adjustedgrade['grade']; $courseitem->grademax = $adjustedgrade['grademax']; $courseitem->grademin = $adjustedgrade['grademin']; } } else { // We must use the specific max/min because it can be different for // each grade_grade when items are excluded from sum of grades. if (!is_null($finalgrade)) { $courseitem->grademin = $coursegrade->get_grade_min(); $courseitem->grademax = $coursegrade->get_grade_max(); } } $coursesdata[$course->id]['finalgrade'] = $finalgrade; $coursesdata[$course->id]['courseitem'] = $courseitem; if ($this->showrank['any'] && $this->showrank[$course->id] && !is_null($finalgrade)) { // Find the number of users with a higher grade. // Please note this can not work if hidden grades involved :-( to be fixed in 2.0. $params = array($finalgrade, $courseitem->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $coursesdata[$course->id]['rank'] = $rank; $coursesdata[$course->id]['numusers'] = $numusers; } } return $coursesdata; }
/** * Generates and saves final grades in associated category grade item. * These immediate children must already have their own final grades. * The category's aggregation method is used to generate final grades. * * Please note that category grade is either calculated or aggregated, not both at the same time. * * This method must be used ONLY from grade_item::regrade_final_grades(), * because the calculation must be done in correct order! * * Steps to follow: * 1. Get final grades from immediate children * 3. Aggregate these grades * 4. Save them in final grades of associated category grade item * * @param int $userid The user ID if final grade generation should be limited to a single user * @return bool */ public function generate_grades($userid = null) { global $CFG, $DB; $this->load_grade_item(); if ($this->grade_item->is_locked()) { return true; // no need to recalculate locked items } // find grade items of immediate children (category or grade items) and force site settings $depends_on = $this->grade_item->depends_on(); if (empty($depends_on)) { $items = false; } else { list($usql, $params) = $DB->get_in_or_equal($depends_on); $sql = "SELECT *\n FROM {grade_items}\n WHERE id {$usql}"; $items = $DB->get_records_sql($sql, $params); foreach ($items as $id => $item) { $items[$id] = new grade_item($item); } } $grade_inst = new grade_grade(); $fields = 'g.' . implode(',g.', $grade_inst->required_fields); // where to look for final grades - include grade of this item too, we will store the results there $gis = array_merge($depends_on, array($this->grade_item->id)); list($usql, $params) = $DB->get_in_or_equal($gis); if ($userid) { $usersql = "AND g.userid=?"; $params[] = $userid; } else { $usersql = ""; } $sql = "SELECT {$fields}\n FROM {grade_grades} g, {grade_items} gi\n WHERE gi.id = g.itemid AND gi.id {$usql} {$usersql}\n ORDER BY g.userid"; // group the results by userid and aggregate the grades for this user $rs = $DB->get_recordset_sql($sql, $params); if ($rs->valid()) { $prevuser = 0; $grade_values = array(); $excluded = array(); $oldgrade = null; $grademaxoverrides = array(); $grademinoverrides = array(); foreach ($rs as $used) { $grade = new grade_grade($used); if (isset($items[$grade->itemid])) { // Prevent grade item to be fetched from DB. $grade->grade_item =& $items[$grade->itemid]; } if ($grade->userid != $prevuser) { $this->aggregate_grades($prevuser, $items, $grade_values, $oldgrade, $excluded, $grademinoverrides, $grademaxoverrides); $prevuser = $grade->userid; $grade_values = array(); $excluded = array(); $oldgrade = null; $grademaxoverrides = array(); $grademinoverrides = array(); } $grade_values[$grade->itemid] = $grade->finalgrade; $grademaxoverrides[$grade->itemid] = $grade->get_grade_max(); $grademinoverrides[$grade->itemid] = $grade->get_grade_min(); if ($grade->excluded) { $excluded[] = $grade->itemid; } if ($this->grade_item->id == $grade->itemid) { $oldgrade = $grade; } } $this->aggregate_grades($prevuser, $items, $grade_values, $oldgrade, $excluded, $grademinoverrides, $grademaxoverrides); //the last one } $rs->close(); return true; }
/** * Fill the table for displaying. * * @param bool $activitylink If this report link to the activity report or the user report. * @param bool $studentcoursesonly Only show courses that the user is a student of. */ public function fill_table($activitylink = false, $studentcoursesonly = false) { global $CFG, $DB, $OUTPUT, $USER; if ($studentcoursesonly && count($this->studentcourseids) == 0) { return false; } // Only show user's courses instead of all courses. if ($this->courses) { $numusers = $this->get_numusers(false); foreach ($this->courses as $course) { if (!$course->showgrades) { continue; } // If we are only showing student courses and this course isn't part of the group, then move on. if ($studentcoursesonly && !isset($this->studentcourseids[$course->id])) { continue; } $coursecontext = context_course::instance($course->id); if (!$course->visible && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) { // The course is hidden and the user isn't allowed to see it continue; } if (!has_capability('moodle/user:viewuseractivitiesreport', context_user::instance($this->user->id)) && ((!has_capability('moodle/grade:view', $coursecontext) || $this->user->id != $USER->id) && !has_capability('moodle/grade:viewall', $coursecontext))) { continue; } $coursename = format_string(get_course_display_name_for_list($course), true, array('context' => $coursecontext)); // Link to the activity report version of the user grade report. if ($activitylink) { $courselink = html_writer::link(new moodle_url('/course/user.php', array('mode' => 'grade', 'id' => $course->id, 'user' => $this->user->id)), $coursename); } else { $courselink = html_writer::link(new moodle_url('/grade/report/user/index.php', array('id' => $course->id, 'userid' => $this->user->id)), $coursename); } $canviewhidden = has_capability('moodle/grade:viewhidden', $coursecontext); // Get course grade_item $course_item = grade_item::fetch_course_item($course->id); // Get the stored grade $course_grade = new grade_grade(array('itemid' => $course_item->id, 'userid' => $this->user->id)); $course_grade->grade_item =& $course_item; $finalgrade = $course_grade->finalgrade; if (!$canviewhidden and !is_null($finalgrade)) { if ($course_grade->is_hidden()) { $finalgrade = null; } else { $adjustedgrade = $this->blank_hidden_total_and_adjust_bounds($course->id, $course_item, $finalgrade); // We temporarily adjust the view of this grade item - because the min and // max are affected by the hidden values in the aggregation. $finalgrade = $adjustedgrade['grade']; $course_item->grademax = $adjustedgrade['grademax']; $course_item->grademin = $adjustedgrade['grademin']; } } else { // We must use the specific max/min because it can be different for // each grade_grade when items are excluded from sum of grades. if (!is_null($finalgrade)) { $course_item->grademin = $course_grade->get_grade_min(); $course_item->grademax = $course_grade->get_grade_max(); } } $data = array($courselink, grade_format_gradevalue($finalgrade, $course_item, true)); if (!$this->showrank['any']) { //nothing to do } else { if ($this->showrank[$course->id] && !is_null($finalgrade)) { /// find the number of users with a higher grade /// please note this can not work if hidden grades involved :-( to be fixed in 2.0 $params = array($finalgrade, $course_item->id); $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {grade_grades}\n WHERE finalgrade IS NOT NULL AND finalgrade > ?\n AND itemid = ?"; $rank = $DB->count_records_sql($sql, $params) + 1; $data[] = "{$rank}/{$numusers}"; } else { // No grade, no rank. // Or this course wants rank hidden. $data[] = '-'; } } $this->table->add_data($data); } return true; } else { echo $OUTPUT->notification(get_string('notenrolled', 'grades'), 'notifymessage'); return false; } }