//$users = $report_evalcomix->load_users(); //$finalgrades = $report_evalcomix->get_grades(); $numpages = (int) ($numusers / $studentsperpage); if ($numusers % $studentsperpage > 0) { $numpages += 1; } for ($ipage = 0; $ipage < $numpages; ++$ipage) { $report_grader = new grade_report_grader($courseid, null, $context, $ipage, $sortitemid); $report_grader->load_users(); $report_grader->load_final_grades(); foreach ($report_grader->users as $userid => $user) { if ($report_grader->canviewhidden) { $altered = array(); $unknown = array(); } else { $hidingaffected = grade_grade::get_hiding_affected($report_grader->grades[$userid], $report_grader->gtree->get_items()); $altered = $hidingaffected['altered']; $unknown = $hidingaffected['unknown']; unset($hidingaffected); } foreach ($report_grader->gtree->items as $itemid => $unused) { $item =& $report_grader->gtree->items[$itemid]; $grade = $report_grader->grades[$userid][$item->id]; // Get the decimal points preference for this item $decimalpoints = $item->get_decimals(); if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else {
public function fill_table() { global $CFG, $DB; // MDL-11679, only show 'mycourses' instead of all courses if ($courses = get_my_courses($this->user->id, 'c.sortorder ASC', 'id, shortname, showgrades')) { $numusers = $this->get_numusers(false); foreach ($courses as $course) { if (!$course->showgrades) { continue; } $courselink = '<a href="' . $CFG->wwwroot . '/grade/report/user/index.php?id=' . $course->id . '">' . $course->shortname . '</a>'; $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $course->id)); // 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 { // This is a really ugly hack, it will be fixed in 2.0 $items = grade_item::fetch_all(array('courseid' => $course->id)); $grades = array(); $sql = "SELECT g.*\n FROM {grade_grades} g\n JOIN {grade_items} gi ON gi.id = g.itemid\n WHERE g.userid = ? AND gi.courseid = ?"; if ($gradesrecords = $DB->get_records_sql($sql, array($this->user->id, $course->id))) { foreach ($gradesrecords as $grade) { $grades[$grade->itemid] = new grade_grade($grade, false); } unset($gradesrecords); } foreach ($items as $itemid => $unused) { if (!isset($grades[$itemid])) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$itemid]->id; $grades[$itemid] = $grade_grade; } $grades[$itemid]->grade_item =& $items[$itemid]; } $hiding_affected = grade_grade::get_hiding_affected($grades, $items); if (array_key_exists($course_item->id, $hiding_affected['altered'])) { $finalgrade = $hiding_affected['altered'][$course_item->id]; } else { if (!empty($hiding_affected['unknown'][$course_item->id])) { $finalgrade = null; } } unset($hiding_affected); unset($grades); unset($items); } } $data = array($courselink, grade_format_gradevalue($finalgrade, $course_item, true)); if (!$this->showrank) { //nothing to do } else { if (!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 $data[] = '-'; } } $this->table->add_data($data); } return true; } else { notify(get_string('nocourses', 'grades')); return false; } }
/** * Builds and return the HTML rows of the table (grades headed by student). * @return string HTML */ function get_studentshtml() { global $CFG, $USER; $studentshtml = ''; $strfeedback = $this->get_lang_string("feedback"); $strgrade = $this->get_lang_string('grade'); $gradetabindex = 1; $numusers = count($this->users); $showuserimage = $this->get_pref('showuserimage'); $showuseridnumber = $this->get_pref('showuseridnumber'); $fixedstudents = $this->is_fixed_students(); // Preload scale objects for items with a scaleid $scales_list = ''; $tabindices = array(); foreach ($this->gtree->items as $item) { if (!empty($item->scaleid)) { $scales_list .= "{$item->scaleid},"; } $tabindices[$item->id]['grade'] = $gradetabindex; $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers; $gradetabindex += $numusers * 2; } $scales_array = array(); if (!empty($scales_list)) { $scales_list = substr($scales_list, 0, -1); $scales_array = get_records_list('scale', 'id', $scales_list); } $row_classes = array(' even ', ' odd '); $row_classes = array(' even ', ' odd '); foreach ($this->users as $userid => $user) { if ($this->canviewhidden) { $altered = array(); $unknown = array(); } else { $hiding_affected = grade_grade::get_hiding_affected($this->grades[$userid], $this->gtree->items); $altered = $hiding_affected['altered']; $unknown = $hiding_affected['unknown']; unset($hiding_affected); } $columncount = 0; if ($fixedstudents) { $studentshtml .= '<tr class="r' . $this->rowcount++ . $row_classes[$this->rowcount % 2] . '">'; } else { // Student name and link $user_pic = null; if ($showuserimage) { $user_pic = '<div class="userpic">' . print_user_picture($user, $this->courseid, null, 0, true) . '</div>'; } $studentshtml .= '<tr class="r' . $this->rowcount++ . $row_classes[$this->rowcount % 2] . '">' . '<th class="c' . $columncount++ . ' user" scope="row" onclick="set_row(this.parentNode.rowIndex);">' . $user_pic . '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $user->id . '&course=' . $this->course->id . '">' . fullname($user) . '</a></th>'; if ($showuseridnumber) { $studentshtml .= '<th class="c' . $columncount++ . ' useridnumber" onclick="set_row(this.parentNode.rowIndex);">' . $user->idnumber . '</th>'; } } foreach ($this->gtree->items as $itemid => $unused) { $item =& $this->gtree->items[$itemid]; $grade = $this->grades[$userid][$item->id]; // Get the decimal points preference for this item $decimalpoints = $item->get_decimals(); if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade->finalgrade; } } // MDL-11274 // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden' if (!$this->canviewhidden and $grade->is_hidden()) { if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$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 $studentshtml .= '<td class="cell c' . $columncount++ . '"><span class="datesubmitted">' . userdate($grade->get_datesubmitted(), get_string('strftimedatetimeshort')) . '</span></td>'; } else { $studentshtml .= '<td class="cell c' . $columncount++ . '">-</td>'; } continue; } // emulate grade element $eid = $this->gtree->get_grade_eid($grade); $element = array('eid' => $eid, 'object' => $grade, 'type' => 'grade'); $cellclasses = 'grade cell c' . $columncount++; if ($item->is_category_item()) { $cellclasses .= ' cat'; } if ($item->is_course_item()) { $cellclasses .= ' course'; } if ($grade->is_overridden()) { $cellclasses .= ' overridden'; } if ($grade->is_excluded()) { // $cellclasses .= ' excluded'; } $grade_title = '<div class="fullname">' . fullname($user) . '</div>'; $grade_title .= '<div class="itemname">' . $item->get_name(true) . '</div>'; if (!empty($grade->feedback) && !$USER->gradeediting[$this->courseid]) { $grade_title .= '<div class="feedback">' . wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >') . '</div>'; } else { } $studentshtml .= '<td class="' . $cellclasses . '" title="' . s($grade_title) . '">'; if ($grade->is_excluded()) { $studentshtml .= '<span class="excludedfloater">' . get_string('excluded', 'grades') . '</span> '; } // Do not show any icons if no grade (no record in DB to match) if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) { $studentshtml .= $this->get_icons($element); } $hidden = ''; if ($grade->is_hidden()) { $hidden = ' hidden '; } $gradepass = '******'; if ($grade->is_passed($item)) { $gradepass = '******'; } elseif (is_null($grade->is_passed($item))) { $gradepass = ''; } // if in editting mode, we need to print either a text box // or a drop down (for scales) // grades in item of type grade category or course are not directly editable if ($item->needsupdate) { $studentshtml .= '<span class="gradingerror' . $hidden . '">' . get_string('error') . '</span>'; } else { if ($USER->gradeediting[$this->courseid]) { if ($item->scaleid && !empty($scales_array[$item->scaleid])) { $scale = $scales_array[$item->scaleid]; $gradeval = (int) $gradeval; // scales use only integers $scales = explode(",", $scale->scale); // reindex because scale is off 1 // MDL-12104 some previous scales might have taken up part of the array // so this needs to be reset $scaleopt = array(); $i = 0; foreach ($scales as $scaleoption) { $i++; $scaleopt[$i] = $scaleoption; } if ($this->get_pref('quickgrading') and $grade->is_editable()) { $oldval = empty($gradeval) ? -1 : $gradeval; if (empty($item->outcomeid)) { $nogradestr = $this->get_lang_string('nograde'); } else { $nogradestr = $this->get_lang_string('nooutcome', 'grades'); } $studentshtml .= '<input type="hidden" name="oldgrade_' . $userid . '_' . $item->id . '" value="' . $oldval . '"/>'; $studentshtml .= choose_from_menu($scaleopt, 'grade_' . $userid . '_' . $item->id, $gradeval, $nogradestr, '', '-1', true, false, $tabindices[$item->id]['grade']); } elseif (!empty($scale)) { $scales = explode(",", $scale->scale); // invalid grade if gradeval < 1 if ($gradeval < 1) { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">-</span>'; } else { $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . $scales[$gradeval - 1] . '</span>'; } } else { // no such scale, throw error? } } else { if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type if ($this->get_pref('quickgrading') and $grade->is_editable()) { $value = format_float($gradeval, $decimalpoints); $studentshtml .= '<input type="hidden" name="oldgrade_' . $userid . '_' . $item->id . '" value="' . $value . '" />'; $studentshtml .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] . '" type="text" title="' . $strgrade . '" name="grade_' . $userid . '_' . $item->id . '" value="' . $value . '" />'; } else { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . format_float($gradeval, $decimalpoints) . '</span>'; } } } // If quickfeedback is on, print an input element if ($this->get_pref('showquickfeedback') and $grade->is_editable()) { $studentshtml .= '<input type="hidden" name="oldfeedback_' . $userid . '_' . $item->id . '" value="' . s($grade->feedback) . '" />'; $studentshtml .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'] . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_' . $userid . '_' . $item->id . '" value="' . s($grade->feedback) . '" />'; } } else { // Not editing $gradedisplaytype = $item->get_displaytype(); // If feedback present, surround grade with feedback tooltip: Open span here if ($item->needsupdate) { $studentshtml .= '<span class="gradingerror' . $hidden . $gradepass . '">' . get_string('error') . '</span>'; } else { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null) . '</span>'; } } } if (!empty($this->gradeserror[$item->id][$userid])) { $studentshtml .= $this->gradeserror[$item->id][$userid]; } $studentshtml .= '</td>' . "\n"; } $studentshtml .= '</tr>'; } return $studentshtml; }
/** * Optionally blank out course/category totals if they contain any hidden items * @param string $courseid the course id * @param string $course_item an instance of grade_item * @param string $finalgrade the grade for the course_item * @return string The new final grade */ protected function blank_hidden_total($courseid, $course_item, $finalgrade) { global $CFG, $DB; static $hiding_affected = null; //array of items in this course affected by hiding // If we're dealing with multiple users we need to know when we've moved on to a new user. static $previous_userid = null; // If we're dealing with multiple courses we need to know when we've moved on to a new course. static $previous_courseid = null; if (!is_array($this->showtotalsifcontainhidden)) { debugging('showtotalsifcontainhidden should be an array', DEBUG_DEVELOPER); $this->showtotalsifcontainhidden = array($courseid => $this->showtotalsifcontainhidden); } if ($this->showtotalsifcontainhidden[$courseid] == GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN) { return $finalgrade; } // If we've moved on to another course or user, reload the grades. if ($previous_userid != $this->user->id || $previous_courseid != $courseid) { $hiding_affected = null; $previous_userid = $this->user->id; $previous_courseid = $courseid; } if (!$hiding_affected) { $items = grade_item::fetch_all(array('courseid' => $courseid)); $grades = array(); $sql = "SELECT g.*\n FROM {grade_grades} g\n JOIN {grade_items} gi ON gi.id = g.itemid\n WHERE g.userid = {$this->user->id} AND gi.courseid = {$courseid}"; if ($gradesrecords = $DB->get_records_sql($sql)) { foreach ($gradesrecords as $grade) { $grades[$grade->itemid] = new grade_grade($grade, false); } unset($gradesrecords); } foreach ($items as $itemid => $unused) { if (!isset($grades[$itemid])) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$itemid]->id; $grades[$itemid] = $grade_grade; } $grades[$itemid]->grade_item =& $items[$itemid]; } $hiding_affected = grade_grade::get_hiding_affected($grades, $items); } //if the item definitely depends on a hidden item if (array_key_exists($course_item->id, $hiding_affected['altered'])) { if (!$this->showtotalsifcontainhidden[$courseid]) { //hide the grade $finalgrade = null; } else { //use reprocessed marks that exclude hidden items $finalgrade = $hiding_affected['altered'][$course_item->id]; } } else { if (!empty($hiding_affected['unknown'][$course_item->id])) { //not sure whether or not this item depends on a hidden item if (!$this->showtotalsifcontainhidden[$courseid]) { //hide the grade $finalgrade = null; } else { //use reprocessed marks that exclude hidden items $finalgrade = $hiding_affected['unknown'][$course_item->id]; } } } return $finalgrade; }
/** * Builds and returns the rows that will make up the right part of the grader report * @return array Array of html_table_row objects */ public function get_right_rows() { global $CFG, $USER, $OUTPUT, $DB, $PAGE; $rows = array(); $this->rowcount = 0; $numrows = count($this->gtree->get_levels()); $numusers = count($this->users); $gradetabindex = 1; $columnstounset = array(); $strgrade = $this->get_lang_string('grade'); $strfeedback = $this->get_lang_string("feedback"); $arrows = $this->get_sort_arrows(); $jsarguments = array( 'id' => '#fixed_column', 'cfg' => array('ajaxenabled'=>false), 'items' => array(), 'users' => array(), 'feedback' => array() ); $jsscales = array(); foreach ($this->gtree->get_levels() as $key=>$row) { if ($key == 0) { // do not display course grade category // continue; } $headingrow = new html_table_row(); $headingrow->attributes['class'] = 'heading_name_row'; foreach ($row as $columnkey => $element) { $sortlink = clone($this->baseurl); if (isset($element['object']->id)) { $sortlink->param('sortitemid', $element['object']->id); } $eid = $element['eid']; $object = $element['object']; $type = $element['type']; $categorystate = @$element['categorystate']; if (!empty($element['colspan'])) { $colspan = $element['colspan']; } else { $colspan = 1; } if (!empty($element['depth'])) { $catlevel = 'catlevel'.$element['depth']; } else { $catlevel = ''; } // Element is a filler if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') { $fillercell = new html_table_cell(); $fillercell->attributes['class'] = $type . ' ' . $catlevel; $fillercell->colspan = $colspan; $fillercell->text = ' '; $fillercell->header = true; $fillercell->scope = 'col'; $headingrow->cells[] = $fillercell; } // Element is a category else if ($type == 'category') { $categorycell = new html_table_cell(); $categorycell->attributes['class'] = 'category ' . $catlevel; $categorycell->colspan = $colspan; $categorycell->text = shorten_text($element['object']->get_name()); $categorycell->text .= $this->get_collapsing_icon($element); $categorycell->header = true; $categorycell->scope = 'col'; // Print icons if ($USER->gradeediting[$this->courseid]) { $categorycell->text .= $this->get_icons($element); } $headingrow->cells[] = $categorycell; } // Element is a grade_item else { //$itemmodule = $element['object']->itemmodule; //$iteminstance = $element['object']->iteminstance; if ($element['object']->id == $this->sortitemid) { if ($this->sortorder == 'ASC') { $arrow = $this->get_sort_arrow('up', $sortlink); } else { $arrow = $this->get_sort_arrow('down', $sortlink); } } else { $arrow = $this->get_sort_arrow('move', $sortlink); } $headerlink = $this->gtree->get_element_header($element, true, $this->get_pref('showactivityicons'), false); $itemcell = new html_table_cell(); $itemcell->attributes['class'] = $type . ' ' . $catlevel . 'highlightable'; if ($element['object']->is_hidden()) { $itemcell->attributes['class'] .= ' hidden'; } $itemcell->colspan = $colspan; $itemcell->text = shorten_text($headerlink) . $arrow; $itemcell->header = true; $itemcell->scope = 'col'; $headingrow->cells[] = $itemcell; } } $rows[] = $headingrow; } $rows = $this->get_right_icons_row($rows); // Preload scale objects for items with a scaleid $scaleslist = array(); $tabindices = array(); foreach ($this->gtree->get_items() as $itemid=>$item) { $scale = null; if (!empty($item->scaleid)) { $scaleslist[] = $item->scaleid; $jsarguments['items'][$itemid] = array('id'=>$itemid, 'name'=>$item->get_name(true), 'type'=>'scale', 'scale'=>$item->scaleid, 'decimals'=>$item->get_decimals()); } else { $jsarguments['items'][$itemid] = array('id'=>$itemid, 'name'=>$item->get_name(true), 'type'=>'value', 'scale'=>false, 'decimals'=>$item->get_decimals()); } $tabindices[$item->id]['grade'] = $gradetabindex; $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers; $gradetabindex += $numusers * 2; } $scalesarray = array(); if (!empty($scaleslist)) { $scalesarray = $DB->get_records_list('scale', 'id', $scaleslist); } $jsscales = $scalesarray; $rowclasses = array('even', 'odd'); foreach ($this->users as $userid => $user) { if ($this->canviewhidden) { $altered = array(); $unknown = array(); } else { $hidingaffected = grade_grade::get_hiding_affected($this->grades[$userid], $this->gtree->get_items()); $altered = $hidingaffected['altered']; $unknown = $hidingaffected['unknown']; unset($hidingaffected); } $itemrow = new html_table_row(); $itemrow->id = 'user_'.$userid; $itemrow->attributes['class'] = $rowclasses[$this->rowcount % 2]; $jsarguments['users'][$userid] = fullname($user); foreach ($this->gtree->items as $itemid=>$unused) { $item =& $this->gtree->items[$itemid]; $grade = $this->grades[$userid][$item->id]; $itemcell = new html_table_cell(); $itemcell->id = 'u'.$userid.'i'.$itemid; // Get the decimal points preference for this item $decimalpoints = $item->get_decimals(); if (in_array($itemid, $unknown)) { $gradeval = null; } else if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade->finalgrade; } // MDL-11274 // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden' if (!$this->canviewhidden and $grade->is_hidden()) { if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$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 $itemcell->text = html_writer::tag('span', userdate($grade->get_datesubmitted(),get_string('strftimedatetimeshort')), array('class'=>'datesubmitted')); } else { $itemcell->text = '-'; } $itemrow->cells[] = $itemcell; continue; } // emulate grade element $eid = $this->gtree->get_grade_eid($grade); $element = array('eid'=>$eid, 'object'=>$grade, 'type'=>'grade'); $itemcell->attributes['class'] .= ' grade'; if ($item->is_category_item()) { $itemcell->attributes['class'] .= ' cat'; } if ($item->is_course_item()) { $itemcell->attributes['class'] .= ' course'; } if ($grade->is_overridden()) { $itemcell->attributes['class'] .= ' overridden'; } if ($grade->is_excluded()) { // $itemcell->attributes['class'] .= ' excluded'; } if (!empty($grade->feedback)) { //should we be truncating feedback? ie $short_feedback = shorten_text($feedback, $this->feedback_trunc_length); $jsarguments['feedback'][] = array('user'=>$userid, 'item'=>$itemid, 'content'=>wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >')); } if ($grade->is_excluded()) { $itemcell->text .= html_writer::tag('span', get_string('excluded', 'grades'), array('class'=>'excludedfloater')); } // Do not show any icons if no grade (no record in DB to match) if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) { $itemcell->text .= $this->get_icons($element); } $hidden = ''; if ($grade->is_hidden()) { $hidden = ' hidden '; } $gradepass = '******'; if ($grade->is_passed($item)) { $gradepass = '******'; } elseif (is_null($grade->is_passed($item))) { $gradepass = ''; } // if in editing mode, we need to print either a text box // or a drop down (for scales) // grades in item of type grade category or course are not directly editable if ($item->needsupdate) { $itemcell->text .= html_writer::tag('span', get_string('error'), array('class'=>"gradingerror$hidden")); } else if ($USER->gradeediting[$this->courseid]) { if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $scale = $scalesarray[$item->scaleid]; $gradeval = (int)$gradeval; // scales use only integers $scales = explode(",", $scale->scale); // reindex because scale is off 1 // MDL-12104 some previous scales might have taken up part of the array // so this needs to be reset $scaleopt = array(); $i = 0; foreach ($scales as $scaleoption) { $i++; $scaleopt[$i] = $scaleoption; } if ($this->get_pref('quickgrading') and $grade->is_editable()) { $oldval = empty($gradeval) ? -1 : $gradeval; if (empty($item->outcomeid)) { $nogradestr = $this->get_lang_string('nograde'); } else { $nogradestr = $this->get_lang_string('nooutcome', 'grades'); } $itemcell->text .= '<input type="hidden" id="oldgrade_'.$userid.'_'.$item->id.'" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$oldval.'"/>'; $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id'=>'grade_'.$userid.'_'.$item->id); $itemcell->text .= html_writer::select($scaleopt, 'grade_'.$userid.'_'.$item->id, $gradeval, array(-1=>$nogradestr), $attributes);; } elseif(!empty($scale)) { $scales = explode(",", $scale->scale); // invalid grade if gradeval < 1 if ($gradeval < 1) { $itemcell->text .= html_writer::tag('span', '-', array('class'=>"gradevalue$hidden$gradepass")); } else { $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale $itemcell->text .= html_writer::tag('span', $scales[$gradeval-1], array('class'=>"gradevalue$hidden$gradepass")); } } else { // no such scale, throw error? } } else if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type if ($this->get_pref('quickgrading') and $grade->is_editable()) { $value = format_float($gradeval, $decimalpoints); $itemcell->text .= '<input type="hidden" id="oldgrade_'.$userid.'_'.$item->id.'" name="oldgrade_'.$userid.'_'.$item->id.'" value="'.$value.'" />'; $itemcell->text .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] . '" type="text" class="text" title="'. $strgrade .'" name="grade_' .$userid.'_' .$item->id.'" id="grade_'.$userid.'_'.$item->id.'" value="'.$value.'" />'; } else { $itemcell->text .= html_writer::tag('span', format_float($gradeval, $decimalpoints), array('class'=>"gradevalue$hidden$gradepass")); } } // If quickfeedback is on, print an input element if ($this->get_pref('showquickfeedback') and $grade->is_editable()) { $itemcell->text .= '<input type="hidden" id="oldfeedback_'.$userid.'_'.$item->id.'" name="oldfeedback_'.$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />'; $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'].'" id="feedback_'.$userid.'_'.$item->id . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_'.$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />'; } } else { // Not editing $gradedisplaytype = $item->get_displaytype(); if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $itemcell->attributes['class'] .= ' grade_type_scale'; } else if ($item->gradetype != GRADE_TYPE_TEXT) { $itemcell->attributes['class'] .= ' grade_type_text'; } if ($this->get_pref('enableajax')) { $itemcell->attributes['class'] .= ' clickable'; } if ($item->needsupdate) { $itemcell->text .= html_writer::tag('span', get_string('error'), array('class'=>"gradingerror$hidden$gradepass")); } else { $itemcell->text .= html_writer::tag('span', grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null), array('class'=>"gradevalue$hidden$gradepass")); } } if (!empty($this->gradeserror[$item->id][$userid])) { $itemcell->text .= $this->gradeserror[$item->id][$userid]; } $itemrow->cells[] = $itemcell; } $rows[] = $itemrow; } if ($this->get_pref('enableajax')) { $jsarguments['cfg']['ajaxenabled'] = true; $jsarguments['cfg']['scales'] = array(); foreach ($jsscales as $scale) { $jsarguments['cfg']['scales'][$scale->id] = explode(',',$scale->scale); } $jsarguments['cfg']['feedbacktrunclength'] = $this->feedback_trunc_length; //feedbacks are now being stored in $jsarguments['feedback'] in get_right_rows() //$jsarguments['cfg']['feedback'] = $this->feedbacks; } $jsarguments['cfg']['isediting'] = (bool)$USER->gradeediting[$this->courseid]; $jsarguments['cfg']['courseid'] = $this->courseid; $jsarguments['cfg']['studentsperpage'] = $this->get_pref('studentsperpage'); $jsarguments['cfg']['showquickfeedback'] = (bool)$this->get_pref('showquickfeedback'); $module = array( 'name' => 'gradereport_grader', 'fullpath' => '/grade/report/grader/module.js', 'requires' => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io', 'json-parse', 'overlay') ); $PAGE->requires->js_init_call('M.gradereport_grader.init_report', $jsarguments, false, $module); $PAGE->requires->strings_for_js(array('addfeedback','feedback', 'grade'), 'grades'); $PAGE->requires->strings_for_js(array('ajaxchoosescale','ajaxclicktoclose','ajaxerror','ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader'); $rows = $this->get_right_range_row($rows); $rows = $this->get_right_avg_row($rows, true); $rows = $this->get_right_avg_row($rows); return $rows; }
/** * Builds and returns the rows that will make up the right part of the grader report * @param boolean $displayaverages whether to display average rows in the table * @return array Array of html_table_row objects */ public function get_right_rows($displayaverages) { global $CFG, $USER, $OUTPUT, $DB, $PAGE; $rows = array(); $this->rowcount = 0; $numrows = count($this->gtree->get_levels()); $numusers = count($this->users); $gradetabindex = 1; $columnstounset = array(); $strgrade = $this->get_lang_string('grade'); $strfeedback = $this->get_lang_string("feedback"); $arrows = $this->get_sort_arrows(); $jsarguments = array('cfg' => array('ajaxenabled' => false), 'items' => array(), 'users' => array(), 'feedback' => array()); $jsscales = array(); // Get preferences once. $showactivityicons = $this->get_pref('showactivityicons'); $quickgrading = $this->get_pref('quickgrading'); $showquickfeedback = $this->get_pref('showquickfeedback'); $enableajax = $this->get_pref('enableajax'); $showanalysisicon = $this->get_pref('showanalysisicon'); // Get strings which are re-used inside the loop. $strftimedatetimeshort = get_string('strftimedatetimeshort'); $strexcludedgrades = get_string('excluded', 'grades'); $strerror = get_string('error'); foreach ($this->gtree->get_levels() as $key => $row) { $headingrow = new html_table_row(); $headingrow->attributes['class'] = 'heading_name_row'; foreach ($row as $columnkey => $element) { $sortlink = clone $this->baseurl; if (isset($element['object']->id)) { $sortlink->param('sortitemid', $element['object']->id); } $eid = $element['eid']; $object = $element['object']; $type = $element['type']; $categorystate = @$element['categorystate']; if (!empty($element['colspan'])) { $colspan = $element['colspan']; } else { $colspan = 1; } if (!empty($element['depth'])) { $catlevel = 'catlevel' . $element['depth']; } else { $catlevel = ''; } // Element is a filler if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') { $fillercell = new html_table_cell(); $fillercell->attributes['class'] = $type . ' ' . $catlevel; $fillercell->colspan = $colspan; $fillercell->text = ' '; // This is a filler cell; don't use a <th>, it'll confuse screen readers. $fillercell->header = false; $headingrow->cells[] = $fillercell; } else { if ($type == 'category') { // Element is a category $categorycell = new html_table_cell(); $categorycell->attributes['class'] = 'category ' . $catlevel; $categorycell->colspan = $colspan; $categorycell->text = $this->get_course_header($element); $categorycell->header = true; $categorycell->scope = 'col'; // Print icons if ($USER->gradeediting[$this->courseid]) { $categorycell->text .= $this->get_icons($element); } $headingrow->cells[] = $categorycell; } else { // Element is a grade_item if ($element['object']->id == $this->sortitemid) { if ($this->sortorder == 'ASC') { $arrow = $this->get_sort_arrow('up', $sortlink); } else { $arrow = $this->get_sort_arrow('down', $sortlink); } } else { $arrow = $this->get_sort_arrow('move', $sortlink); } $headerlink = $this->gtree->get_element_header($element, true, $showactivityicons, false, false, true); $itemcell = new html_table_cell(); $itemcell->attributes['class'] = $type . ' ' . $catlevel . ' highlightable' . ' i' . $element['object']->id; $itemcell->attributes['data-itemid'] = $element['object']->id; if ($element['object']->is_hidden()) { $itemcell->attributes['class'] .= ' dimmed_text'; } $singleview = ''; if (has_all_capabilities(array('gradereport/singleview:view', 'moodle/grade:viewall', 'moodle/grade:edit'), $this->context)) { $url = new moodle_url('/grade/report/singleview/index.php', array('id' => $this->course->id, 'item' => 'grade', 'itemid' => $element['object']->id)); $singleview = $OUTPUT->action_icon($url, new pix_icon('t/editstring', get_string('singleview', 'grades', $element['object']->get_name()))); } $itemcell->colspan = $colspan; $itemcell->text = shorten_text($headerlink) . $arrow . $singleview; $itemcell->header = true; $itemcell->scope = 'col'; $headingrow->cells[] = $itemcell; } } } $rows[] = $headingrow; } $rows = $this->get_right_icons_row($rows); // Preload scale objects for items with a scaleid and initialize tab indices $scaleslist = array(); $tabindices = array(); foreach ($this->gtree->get_items() as $itemid => $item) { $scale = null; if (!empty($item->scaleid)) { $scaleslist[] = $item->scaleid; $jsarguments['items'][$itemid] = array('id' => $itemid, 'name' => $item->get_name(true), 'type' => 'scale', 'scale' => $item->scaleid, 'decimals' => $item->get_decimals()); } else { $jsarguments['items'][$itemid] = array('id' => $itemid, 'name' => $item->get_name(true), 'type' => 'value', 'scale' => false, 'decimals' => $item->get_decimals()); } $tabindices[$item->id]['grade'] = $gradetabindex; $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers; $gradetabindex += $numusers * 2; } $scalesarray = array(); if (!empty($scaleslist)) { $scalesarray = $DB->get_records_list('scale', 'id', $scaleslist); } $jsscales = $scalesarray; foreach ($this->users as $userid => $user) { if ($this->canviewhidden) { $altered = array(); $unknown = array(); } else { $hidingaffected = grade_grade::get_hiding_affected($this->grades[$userid], $this->gtree->get_items()); $altered = $hidingaffected['altered']; $unknown = $hidingaffected['unknown']; unset($hidingaffected); } $itemrow = new html_table_row(); $itemrow->id = 'user_' . $userid; $fullname = fullname($user); $jsarguments['users'][$userid] = $fullname; foreach ($this->gtree->items as $itemid => $unused) { $item =& $this->gtree->items[$itemid]; $grade = $this->grades[$userid][$item->id]; $itemcell = new html_table_cell(); $itemcell->id = 'u' . $userid . 'i' . $itemid; $itemcell->attributes['data-itemid'] = $itemid; // Get the decimal points preference for this item $decimalpoints = $item->get_decimals(); if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade->finalgrade; } } if (!empty($grade->finalgrade)) { $gradevalforjs = null; if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $gradevalforjs = (int) $gradeval; } else { $gradevalforjs = format_float($gradeval, $decimalpoints); } $jsarguments['grades'][] = array('user' => $userid, 'item' => $itemid, 'grade' => $gradevalforjs); } // MDL-11274 // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden' if (!$this->canviewhidden and $grade->is_hidden()) { if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$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 $itemcell->text = "<span class='datesubmitted'>" . userdate($grade->get_datesubmitted(), $strftimedatetimeshort) . "</span>"; } else { $itemcell->text = '-'; } $itemrow->cells[] = $itemcell; continue; } // emulate grade element $eid = $this->gtree->get_grade_eid($grade); $element = array('eid' => $eid, 'object' => $grade, 'type' => 'grade'); $itemcell->attributes['class'] .= ' grade i' . $itemid; if ($item->is_category_item()) { $itemcell->attributes['class'] .= ' cat'; } if ($item->is_course_item()) { $itemcell->attributes['class'] .= ' course'; } if ($grade->is_overridden()) { $itemcell->attributes['class'] .= ' overridden'; $itemcell->attributes['aria-label'] = get_string('overriddengrade', 'gradereport_grader'); } if (!empty($grade->feedback)) { $feedback = wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br>'); $itemcell->attributes['data-feedback'] = $feedback; $jsarguments['feedback'][] = array('user' => $userid, 'item' => $itemid, 'content' => $feedback); } if ($grade->is_excluded()) { // Adding white spaces before and after to prevent a screenreader from // thinking that the words are attached to the next/previous <span> or text. $itemcell->text .= " <span class='excludedfloater'>" . $strexcludedgrades . "</span> "; } // Do not show any icons if no grade (no record in DB to match) if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) { $itemcell->text .= $this->get_icons($element); } $hidden = ''; if ($grade->is_hidden()) { $hidden = ' dimmed_text '; } $gradepass = '******'; if ($grade->is_passed($item)) { $gradepass = '******'; } else { if (is_null($grade->is_passed($item))) { $gradepass = ''; } } // if in editing mode, we need to print either a text box // or a drop down (for scales) // grades in item of type grade category or course are not directly editable if ($item->needsupdate) { $itemcell->text .= "<span class='gradingerror{$hidden}'>" . $strerror . "</span>"; } else { if ($USER->gradeediting[$this->courseid]) { if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $scale = $scalesarray[$item->scaleid]; $gradeval = (int) $gradeval; // scales use only integers $scales = explode(",", $scale->scale); // reindex because scale is off 1 // MDL-12104 some previous scales might have taken up part of the array // so this needs to be reset $scaleopt = array(); $i = 0; foreach ($scales as $scaleoption) { $i++; $scaleopt[$i] = $scaleoption; } if ($quickgrading and $grade->is_editable()) { $oldval = empty($gradeval) ? -1 : $gradeval; if (empty($item->outcomeid)) { $nogradestr = $this->get_lang_string('nograde'); } else { $nogradestr = $this->get_lang_string('nooutcome', 'grades'); } $attributes = array('tabindex' => $tabindices[$item->id]['grade'], 'id' => 'grade_' . $userid . '_' . $item->id); $gradelabel = $fullname . ' ' . $item->itemname; $itemcell->text .= html_writer::label(get_string('useractivitygrade', 'gradereport_grader', $gradelabel), $attributes['id'], false, array('class' => 'accesshide')); $itemcell->text .= html_writer::select($scaleopt, 'grade[' . $userid . '][' . $item->id . ']', $gradeval, array(-1 => $nogradestr), $attributes); } else { if (!empty($scale)) { $scales = explode(",", $scale->scale); // invalid grade if gradeval < 1 if ($gradeval < 1) { $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>-</span>"; } else { $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>{$scales[$gradeval - 1]}</span>"; } } } } else { if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type if ($quickgrading and $grade->is_editable()) { $value = format_float($gradeval, $decimalpoints); $gradelabel = $fullname . ' ' . $item->itemname; $itemcell->text .= '<label class="accesshide" for="grade_' . $userid . '_' . $item->id . '">' . get_string('useractivitygrade', 'gradereport_grader', $gradelabel) . '</label>'; $itemcell->text .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] . '" type="text" class="text" title="' . $strgrade . '" name="grade[' . $userid . '][' . $item->id . ']" id="grade_' . $userid . '_' . $item->id . '" value="' . $value . '" />'; } else { $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>" . format_float($gradeval, $decimalpoints) . "</span>"; } } } // If quickfeedback is on, print an input element if ($showquickfeedback and $grade->is_editable()) { $feedbacklabel = $fullname . ' ' . $item->itemname; $itemcell->text .= '<label class="accesshide" for="feedback_' . $userid . '_' . $item->id . '">' . get_string('useractivityfeedback', 'gradereport_grader', $feedbacklabel) . '</label>'; $itemcell->text .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'] . '" id="feedback_' . $userid . '_' . $item->id . '" size="6" title="' . $strfeedback . '" type="text" name="feedback[' . $userid . '][' . $item->id . ']" value="' . s($grade->feedback) . '" />'; } } else { // Not editing $gradedisplaytype = $item->get_displaytype(); if ($item->scaleid && !empty($scalesarray[$item->scaleid])) { $itemcell->attributes['class'] .= ' grade_type_scale'; } else { if ($item->gradetype != GRADE_TYPE_TEXT) { $itemcell->attributes['class'] .= ' grade_type_text'; } } if ($enableajax) { $canoverride = true; if ($item->is_category_item() || $item->is_course_item()) { $canoverride = (bool) get_config('moodle', 'grade_overridecat'); } if ($canoverride) { $itemcell->attributes['class'] .= ' clickable'; } } if ($item->needsupdate) { $itemcell->text .= "<span class='gradingerror{$hidden}{$gradepass}'>" . $error . "</span>"; } else { // The max and min for an aggregation may be different to the grade_item. if (!is_null($gradeval)) { $item->grademax = $grade->get_grade_max(); $item->grademin = $grade->get_grade_min(); } $itemcell->text .= "<span class='gradevalue{$hidden}{$gradepass}'>" . grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null) . "</span>"; if ($showanalysisicon) { $itemcell->text .= $this->gtree->get_grade_analysis_icon($grade); } } } } if (!empty($this->gradeserror[$item->id][$userid])) { $itemcell->text .= $this->gradeserror[$item->id][$userid]; } $itemrow->cells[] = $itemcell; } $rows[] = $itemrow; } if ($enableajax) { $jsarguments['cfg']['ajaxenabled'] = true; $jsarguments['cfg']['scales'] = array(); foreach ($jsscales as $scale) { $jsarguments['cfg']['scales'][$scale->id] = explode(',', $scale->scale); } $jsarguments['cfg']['feedbacktrunclength'] = $this->feedback_trunc_length; // Student grades and feedback are already at $jsarguments['feedback'] and $jsarguments['grades'] } $jsarguments['cfg']['isediting'] = (bool) $USER->gradeediting[$this->courseid]; $jsarguments['cfg']['courseid'] = $this->courseid; $jsarguments['cfg']['studentsperpage'] = $this->get_students_per_page(); $jsarguments['cfg']['showquickfeedback'] = (bool) $showquickfeedback; $module = array('name' => 'gradereport_grader', 'fullpath' => '/grade/report/grader/module.js', 'requires' => array('base', 'dom', 'event', 'event-mouseenter', 'event-key', 'io-queue', 'json-parse', 'overlay')); $PAGE->requires->js_init_call('M.gradereport_grader.init_report', $jsarguments, false, $module); $PAGE->requires->strings_for_js(array('addfeedback', 'feedback', 'grade'), 'grades'); $PAGE->requires->strings_for_js(array('ajaxchoosescale', 'ajaxclicktoclose', 'ajaxerror', 'ajaxfailedupdate', 'ajaxfieldchanged'), 'gradereport_grader'); if (!$enableajax && $USER->gradeediting[$this->courseid]) { $PAGE->requires->yui_module('moodle-core-formchangechecker', 'M.core_formchangechecker.init', array(array('formid' => 'gradereport_grader'))); $PAGE->requires->string_for_js('changesmadereallygoaway', 'moodle'); } $rows = $this->get_right_range_row($rows); if ($displayaverages) { $rows = $this->get_right_avg_row($rows, true); $rows = $this->get_right_avg_row($rows); } return $rows; }
/** * Given a userid, and provided the gtree is correctly loaded, returns a complete HTML row for this user. * * @param object $user * @return string */ function get_studentrowhtml($user) { global $CFG, $USER; $showuserimage = $this->get_pref('showuserimage'); $showuseridnumber = $this->get_pref('showuseridnumber'); $fixedstudents = empty($USER->screenreader) && $this->get_pref('fixedstudents'); $studentrowhtml = ''; $row_classes = array(' even ', ' odd '); if ($this->canviewhidden) { $altered = array(); $unknown = array(); } else { $hiding_affected = grade_grade::get_hiding_affected($this->grades[$userid], $this->gtree->items); $altered = $hiding_affected['altered']; $unknown = $hiding_affected['unknown']; unset($hiding_affected); } $columncount = 0; if ($fixedstudents) { $studentrowhtml .= '<tr class="r' . $this->rowcount++ . $row_classes[$this->rowcount % 2] . '">'; } else { // Student name and link $user_pic = null; if ($showuserimage) { $user_pic = '<div class="userpic">' . print_user_picture($user, $this->courseid, true, 0, true) . '</div>'; } $studentrowhtml .= '<tr class="r' . $this->rowcount++ . $row_classes[$this->rowcount % 2] . '">' . '<th class="header c' . $columncount++ . ' user" scope="row" onclick="set_row(this.parentNode.rowIndex);">' . $user_pic . '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $user->id . '&course=' . $this->course->id . '">' . fullname($user) . '</a></th>'; if ($showuseridnumber) { $studentrowhtml .= '<th class="header c' . $columncount++ . ' useridnumber" onclick="set_row(this.parentNode.rowIndex);">' . $user->idnumber . '</th>'; } } $columntabcount = 0; $feedback_tabindex_modifier = 1; // Used to offset the grade value at the beginning of each new column if ($this->get_pref('showquickfeedback')) { $feedback_tabindex_modifier = 2; } foreach ($this->gtree->items as $itemid => $unused) { $nexttabindex = $this->tabindex + $columntabcount * $feedback_tabindex_modifier * $this->numusers; $studentrowhtml .= $this->get_gradecellhtml($user, $itemid, $columncount, $nexttabindex, $altered, $unknown); $columntabcount++; } $studentrowhtml .= '</tr>'; return $studentrowhtml; }
/** * Builds and return the HTML rows of the table (grades headed by student). * @return string HTML */ function get_studentshtml() { global $CFG, $USER; $studentshtml = ''; $strfeedback = $this->get_lang_string("feedback"); $strgrade = $this->get_lang_string('grade'); /* if ($showquickfeedback ) { $gradetabindex += $numusers * 4; } else { $gradetabindex += $numusers * 2; } * */ $gradetabindex = 1; $numusers = count($this->users); $showuserimage = $this->get_pref('showuserimage'); $showuseridnumber = $this->get_pref('showuseridnumber'); $showquickfeedback = $this->get_pref('showquickfeedback'); $accuratetotals = get_user_preferences('grade_report_accuratepointtotals') == null ? 1 : 0; // turn off fixed students column no matter what for this report $fixedstudents = 0; //$this->is_fixed_students(); $quickgrading = $this->get_pref('quickgrading'); // Preload scale objects for items with a scaleid $scales_list = ''; $tabindices = array(); foreach ($this->gtree->items as $item) { if (!empty($item->scaleid)) { $scales_list .= "{$item->scaleid},"; } if ($item->type == 'item') { $tabindices[$item->id]['grade'] = $gradetabindex; if ($showquickfeedback) { $tabindices[$item->id]['feedback'] = $gradetabindex + $numusers; $gradetabindex += $numusers * 2; } else { $gradetabindex += $numusers; } } } $scales_array = array(); if (!empty($scales_list)) { $scales_list = substr($scales_list, 0, -1); $scales_array = get_records_list('scale', 'id', $scales_list); } $row_classes = array(' even ', ' odd '); foreach ($this->users as $userid => $user) { if ($this->canviewhidden) { $altered = array(); $unknown = array(); } else { $hiding_affected = grade_grade::get_hiding_affected($this->grades[$userid], $this->gtree->items); $altered = $hiding_affected['altered']; $unknown = $hiding_affected['unknown']; unset($hiding_affected); } $columncount = 0; if ($fixedstudents) { $studentshtml .= '<tr class="r' . $this->rowcount++ . $row_classes[$this->rowcount % 2] . '">'; } else { // Student name and link $user_pic = null; if ($showuserimage) { $user_pic = '<div class="userpic">' . print_user_picture($user, $this->courseid, null, 0, true) . '</div>'; } //we're either going to add a th or a colspan to keep things aligned // REMOVING $colspan AS ANYTHING $userreportcell = ''; $userreportcellcolspan = ''; if (has_capability('gradereport/' . $CFG->grade_profilereport . ':view', $this->context)) { $a->user = fullname($user); $strgradesforuser = get_string('gradesforuser', 'grades', $a); $userreportcell = '<td class="tduser"><a href="' . $CFG->wwwroot . '/grade/report/' . $CFG->grade_profilereport . '/index.php?id=' . $this->courseid . '&userid=' . $user->id . '" target="_blank">' . '<img class="userreport" src="' . $CFG->pixpath . '/t/grades.gif" alt="' . $strgradesforuser . '" style="align:center" title="' . $strgradesforuser . '" /></a></td>'; // $userreportcell = '<a href="'.$CFG->wwwroot.'/grade/report/'.$CFG->grade_profilereport.'/index.php?id='.$this->courseid.'&userid='.$user->id.'" target="_blank">' // .'<img class="userreport" src="'.$CFG->pixpath.'/t/grades.gif" alt="'.$strgradesforuser.'" style="align:center" title="'.$strgradesforuser.'" /></a>'; } else { $userreportcell = '<td class="userreport"></td>'; } $studentshtml .= '<tr class="r' . $this->rowcount++ . $row_classes[$this->rowcount % 2] . '">' . '<th class="c' . $columncount++ . ' user">' . $user_pic . '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $user->id . '&course=' . $this->course->id . '">' . $user->firstname . '<br /><span class="lastname">' . $user->lastname . "</a></span></th>{$userreportcell}"; // .fullname($user)."</a></th>$userreportcell"; if ($showuseridnumber) { $studentshtml .= '<th class="c' . $columncount++ . ' useridnumber" onclick="set_row(this.parentNode.rowIndex);">' . $user->idnumber . '</th>'; } } // each loop does an item, entire cycle does a user's row foreach ($this->gtree->items as $itemid => $unused) { $item =& $this->gtree->items[$itemid]; $grade = $this->grades[$userid][$item->id]; // Get the decimal points preference for this item $decimalpoints = $item->get_decimals(); if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade->finalgrade; } } // MDL-11274 // Hide grades in the grader report if the current grader doesn't have 'moodle/grade:viewhidden' if (!$this->canviewhidden and $grade->is_hidden()) { if (!empty($CFG->grade_hiddenasdate) and $grade->get_datesubmitted() and !$item->is_category_item() and !$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 $studentshtml .= '<td class="cell c' . $columncount++ . '"><span class="datesubmitted">' . userdate($grade->get_datesubmitted(), get_string('strftimedatetimeshort')) . '</span></td>'; } else { $studentshtml .= '<td class="cell c' . $columncount++ . '">-</td>'; } continue; } // emulate grade element $eid = $this->gtree->get_grade_eid($grade); $element = array('eid' => $eid, 'object' => $grade, 'type' => 'grade'); $cellclasses = 'grade cell c' . $columncount++; if ($item->is_category_item()) { $cellclasses .= ' cat'; } if ($item->is_course_item()) { $cellclasses .= ' course'; } if ($grade->is_overridden()) { $cellclasses .= ' overridden'; } if ($grade->is_excluded()) { $cellclasses .= ' excluded'; } /* ELLIMINATE ALT TEXT WITH DIV SECTIONS $grade_title = '<div class="fullname">'.fullname($user).'</div>'; $grade_title .= '<div class="itemname">'.$item->get_name(true).'</div>'; if (!empty($grade->feedback) && !$USER->gradeediting[$this->courseid]) { $grade_title .= '<div class="feedback">' .wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '<br/ >') . '</div>'; } else { } */ $grade_title = ''; $studentshtml .= '<td class="' . $cellclasses . '" title="' . s($grade_title) . '">'; if ($grade->is_excluded()) { $studentshtml .= '<span class="excludedfloater">' . get_string('excluded', 'grades') . '</span> '; } // Do not show any icons if no grade (no record in DB to match) if (!$item->needsupdate and $USER->gradeediting[$this->courseid]) { $studentshtml .= $this->get_icons($element); } $hidden = ''; if ($grade->is_hidden()) { $hidden = ' hidden '; } $gradepass = '******'; if ($grade->is_passed($item)) { $gradepass = '******'; } elseif (is_null($grade->is_passed($item))) { $gradepass = ''; } // if in editting mode, we need to print either a text box // or a drop down (for scales) // grades in item of type grade category or course are not directly editable if ($item->needsupdate) { $studentshtml .= '<span class="gradingerror' . $hidden . '">' . get_string('error') . '</span>'; } else { if ($USER->gradeediting[$this->courseid]) { if ($item->scaleid && !empty($scales_array[$item->scaleid])) { $scale = $scales_array[$item->scaleid]; $gradeval = (int) $gradeval; // scales use only integers $scales = explode(",", $scale->scale); // reindex because scale is off 1 // MDL-12104 some previous scales might have taken up part of the array // so this needs to be reset $scaleopt = array(); $i = 0; foreach ($scales as $scaleoption) { $i++; $scaleopt[$i] = $scaleoption; } if ($quickgrading and $grade->is_editable()) { $oldval = empty($gradeval) ? -1 : $gradeval; if (empty($item->outcomeid)) { $nogradestr = $this->get_lang_string('nograde'); } else { $nogradestr = $this->get_lang_string('nooutcome', 'grades'); } $studentshtml .= '<input type="hidden" name="oldgrade_' . $userid . '_' . $item->id . '" value="' . $oldval . '"/>'; $studentshtml .= choose_from_menu($scaleopt, 'grade_' . $userid . '_' . $item->id, $gradeval, $nogradestr, '', '-1', true, false, $tabindices[$item->id]['grade']); } elseif (!empty($scale)) { $scales = explode(",", $scale->scale); // invalid grade if gradeval < 1 if ($gradeval < 1) { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">-</span>'; } else { $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . $scales[$gradeval - 1] . '</span>'; } } else { // no such scale, throw error? } } else { if ($item->gradetype != GRADE_TYPE_TEXT) { // Value type // We always want to display the correct (first) displaytype when editing // regardless of grade_report_gradeeditalways $gradedisplaytype = (int) substr((string) $item->get_displaytype(), 0, 1); // if we have an accumulated total points that's not accurately reflected in the db, then we want to display the ACCURATE number // we only need to take the extra calculation into account if points display since percent and letter are accurate by their nature // If the settings don't call for ACCURATE point totals ($this->accuratetotals) then there will be no earned_total value if ($gradedisplaytype == GRADE_DISPLAY_TYPE_REAL && isset($this->grades[$userid][$grade->itemid]->cat_item)) { $items = $this->gtree->items; $grade_values = $this->grades[$userid][$grade->itemid]->cat_item; $grade_maxes = $this->grades[$userid][$grade->itemid]->cat_max; $this_cat = $this->gtree->items[$grade->itemid]->get_item_category(); limit_item($this_cat, $items, $grade_values, $grade_maxes); $gradeval = array_sum($grade_values); $item->grademax = array_sum($grade_maxes); } $value = grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null); if (!$grade->is_hidden() && $gradeval != null && $this->accuratetotals) { $this->grades[$userid][$this->gtree->parents[$grade->itemid]->id]->cat_item[$grade->itemid] = $gradeval; $this->grades[$userid][$this->gtree->parents[$grade->itemid]->id]->cat_max[$grade->itemid] = $grade->rawgrademax; } if ($quickgrading and $grade->is_editable()) { if (!$item->is_course_item() and !$item->is_category_item()) { $studentshtml .= '<input type="hidden" name="oldgrade_' . $userid . '_' . $item->id . '" value="' . $value . '" />'; $studentshtml .= '<input size="6" tabindex="' . $tabindices[$item->id]['grade'] . '" type="text" title="' . $strgrade . '" class ="gradevalue' . $hidden . $gradepass . '" rel="' . $item->id . '" name="grade_' . $userid . '_' . $item->id . '" value="' . $value . '" />'; } else { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . $value . '</span>'; } } else { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . format_float($gradeval, $decimalpoints) . '</span>'; } } } // If quickfeedback is on, print an input element if ($showquickfeedback and $grade->is_editable()) { if (!$item->is_course_item() and !$item->is_category_item()) { $studentshtml .= '<input type="hidden" name="oldfeedback_' . $userid . '_' . $item->id . '" value="' . s($grade->feedback) . '" />'; $studentshtml .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback'] . '" size="6" title="' . $strfeedback . '" type="text" name="feedback_' . $userid . '_' . $item->id . '" value="' . s($grade->feedback) . '" />'; } } } else { // Not editing if ($this->accuratetotals) { $gradedisplaytype = (int) substr((string) $item->get_displaytype(), 0, 1); } else { $gradedisplaytype = $item->get_displaytype(); } // if we have an accumulated total points that's not accurately reflected in the db, then we want to display the ACCURATE number // we only need to take the extra calculation into account if points display since percent and letter are accurate by their nature // If the settings don't call for ACCURATE point totals ($this->accuratetotals) then there will be no earned_total value if ($gradedisplaytype == GRADE_DISPLAY_TYPE_REAL && isset($this->grades[$userid][$grade->itemid]->cat_item)) { $items = $this->gtree->items; $grade_values = $this->grades[$userid][$grade->itemid]->cat_item; $grade_maxes = $this->grades[$userid][$grade->itemid]->cat_max; $this_cat = $this->gtree->items[$grade->itemid]->get_item_category(); limit_item($this_cat, $items, $grade_values, $grade_maxes); $gradeval = array_sum($grade_values); $item->grademax = array_sum($grade_maxes); } // is calculating accurate totals store the earned_total for this item to its parent, if there is one if (!$grade->is_hidden() && $gradeval != null && $this->accuratetotals && isset($this->gtree->parents[$grade->itemid]->id)) { $this->grades[$userid][$this->gtree->parents[$grade->itemid]->id]->cat_item[$grade->itemid] = $gradeval; $this->grades[$userid][$this->gtree->parents[$grade->itemid]->id]->cat_max[$grade->itemid] = $grade->rawgrademax; } // If feedback present, surround grade with feedback tooltip: Open span here if ($item->needsupdate) { $studentshtml .= '<span class="gradingerror' . $hidden . $gradepass . '">' . get_string('error') . '</span>'; } else { $studentshtml .= '<span class="gradevalue' . $hidden . $gradepass . '">' . grade_format_gradevalue($gradeval, $item, true, $gradedisplaytype, null) . '</span>'; } } } if (!empty($this->gradeserror[$item->id][$userid])) { $studentshtml .= $this->gradeserror[$item->id][$userid]; } $studentshtml .= '</td>' . "\n"; } $studentshtml .= '</tr>'; } return $studentshtml; }
/** * Optionally blank out course/category totals if they contain any hidden items * @param string $courseid the course id * @param string $course_item an instance of grade_item * @param string $finalgrade the grade for the course_item * @return array[] containing values for 'grade', 'grademax', 'grademin', 'aggregationstatus' and 'aggregationweight' */ protected function blank_hidden_total_and_adjust_bounds($courseid, $course_item, $finalgrade) { global $CFG, $DB; static $hiding_affected = null; //array of items in this course affected by hiding // If we're dealing with multiple users we need to know when we've moved on to a new user. static $previous_userid = null; // If we're dealing with multiple courses we need to know when we've moved on to a new course. static $previous_courseid = null; $coursegradegrade = grade_grade::fetch(array('userid' => $this->user->id, 'itemid' => $course_item->id)); $grademin = $course_item->grademin; $grademax = $course_item->grademax; if ($coursegradegrade) { $grademin = $coursegradegrade->get_grade_min(); $grademax = $coursegradegrade->get_grade_max(); } else { $coursegradegrade = new grade_grade(array('userid' => $this->user->id, 'itemid' => $course_item->id), false); } $hint = $coursegradegrade->get_aggregation_hint(); $aggregationstatus = $hint['status']; $aggregationweight = $hint['weight']; if (!is_array($this->showtotalsifcontainhidden)) { debugging('showtotalsifcontainhidden should be an array', DEBUG_DEVELOPER); $this->showtotalsifcontainhidden = array($courseid => $this->showtotalsifcontainhidden); } if ($this->showtotalsifcontainhidden[$courseid] == GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN) { return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax, 'aggregationstatus' => $aggregationstatus, 'aggregationweight' => $aggregationweight); } // If we've moved on to another course or user, reload the grades. if ($previous_userid != $this->user->id || $previous_courseid != $courseid) { $hiding_affected = null; $previous_userid = $this->user->id; $previous_courseid = $courseid; } if (!$hiding_affected) { $items = grade_item::fetch_all(array('courseid' => $courseid)); $grades = array(); $sql = "SELECT g.*\n FROM {grade_grades} g\n JOIN {grade_items} gi ON gi.id = g.itemid\n WHERE g.userid = {$this->user->id} AND gi.courseid = {$courseid}"; if ($gradesrecords = $DB->get_records_sql($sql)) { foreach ($gradesrecords as $grade) { $grades[$grade->itemid] = new grade_grade($grade, false); } unset($gradesrecords); } foreach ($items as $itemid => $unused) { if (!isset($grades[$itemid])) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$itemid]->id; $grades[$itemid] = $grade_grade; } $grades[$itemid]->grade_item =& $items[$itemid]; } $hiding_affected = grade_grade::get_hiding_affected($grades, $items); } //if the item definitely depends on a hidden item if (array_key_exists($course_item->id, $hiding_affected['altered']) || array_key_exists($course_item->id, $hiding_affected['alteredgrademin']) || array_key_exists($course_item->id, $hiding_affected['alteredgrademax']) || array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus']) || array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { if (!$this->showtotalsifcontainhidden[$courseid] && array_key_exists($course_item->id, $hiding_affected['altered'])) { // Hide the grade, but only when it has changed. $finalgrade = null; } else { //use reprocessed marks that exclude hidden items if (array_key_exists($course_item->id, $hiding_affected['altered'])) { $finalgrade = $hiding_affected['altered'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredgrademin'])) { $grademin = $hiding_affected['alteredgrademin'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredgrademax'])) { $grademax = $hiding_affected['alteredgrademax'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus'])) { $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id]; } if (!$this->showtotalsifcontainhidden[$courseid]) { // If the course total is hidden we must hide the weight otherwise // it can be used to compute the course total. $aggregationstatus = 'unknown'; $aggregationweight = null; } } } else { if (!empty($hiding_affected['unknown'][$course_item->id])) { //not sure whether or not this item depends on a hidden item if (!$this->showtotalsifcontainhidden[$courseid]) { //hide the grade $finalgrade = null; } else { //use reprocessed marks that exclude hidden items $finalgrade = $hiding_affected['unknown'][$course_item->id]; if (array_key_exists($course_item->id, $hiding_affected['alteredgrademin'])) { $grademin = $hiding_affected['alteredgrademin'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredgrademax'])) { $grademax = $hiding_affected['alteredgrademax'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationstatus'])) { $aggregationstatus = $hiding_affected['alteredaggregationstatus'][$course_item->id]; } if (array_key_exists($course_item->id, $hiding_affected['alteredaggregationweight'])) { $aggregationweight = $hiding_affected['alteredaggregationweight'][$course_item->id]; } } } } return array('grade' => $finalgrade, 'grademin' => $grademin, 'grademax' => $grademax, 'aggregationstatus' => $aggregationstatus, 'aggregationweight' => $aggregationweight); }
function fill_table() { global $CFG; $numusers = $this->get_numusers(false); // total course users $items =& $this->gseq->items; $grades = array(); $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->courseid)); // fetch or create all grades foreach ($items as $key => $unused) { if (!($grade_grade = grade_grade::fetch(array('itemid' => $items[$key]->id, 'userid' => $this->user->id)))) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$key]->id; } $grades[$key] = $grade_grade; $grades[$key]->grade_item =& $items[$key]; } if ($canviewhidden) { $altered = array(); $unknown = array(); } else { $hiding_affected = grade_grade::get_hiding_affected($grades, $items); $altered = $hiding_affected['altered']; $unknown = $hiding_affected['unknown']; unset($hiding_affected); } foreach ($items as $itemid => $unused) { $grade_item =& $items[$itemid]; $grade_grade =& $grades[$itemid]; if (!$canviewhidden and $grade_item->is_hidden()) { if ($this->showhiddenitems == 0) { // no hidden items at all continue; } else { if ($this->showhiddenitems == 1 and !$grade_item->is_hiddenuntil()) { // hidden until that are still hidden are visible continue; } } } $class = 'gradeitem'; if ($grade_item->is_course_item()) { $class = 'courseitem'; } else { if ($grade_item->is_category_item()) { $class = 'categoryitem'; } } if (in_array($itemid, $unknown)) { $gradeval = null; } else { if (array_key_exists($itemid, $altered)) { $gradeval = $altered[$itemid]; } else { $gradeval = $grade_grade->finalgrade; } } $data = array(); // all users should know which items are still hidden $hidden = ''; if ($grade_item->is_hidden()) { $hidden = ' hidden '; } $element = $this->gseq->locate_element($this->gseq->get_item_eid($grade_item)); $header = $this->gseq->get_element_header($element, true, true, true); /// prints grade item name $data[] = '<span class="' . $hidden . $class . '">' . $header . '</span>'; /// prints category $cat = $grade_item->get_parent_category(); $data[] = '<span class="' . $hidden . $class . '">' . $cat->get_name() . '</span>'; $hidden = ''; if ($grade_item->is_hidden()) { // can not see grades in hidden items $hidden = ' hidden '; } else { if ($canviewhidden and $grade_grade->is_hidden()) { // if user can see hidden grades, indicate which ones are hidden $hidden = ' hidden '; } } /// prints the grade if ($grade_grade->is_excluded()) { $excluded = get_string('excluded', 'grades') . ' '; } else { $excluded = ''; } if ($grade_item->needsupdate) { $data[] = '<span class="' . $hidden . $class . ' gradingerror">' . get_string('error') . '</span>'; } else { if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$canviewhidden and $grade_grade->is_hidden() and !$grade_item->is_category_item() and !$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 $data[] = '<span class="' . $hidden . $class . ' datesubmitted">' . $excluded . get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))) . '</span>'; } else { $data[] = '<span class="' . $hidden . $class . '">' . $excluded . grade_format_gradevalue($gradeval, $grade_item, true) . '</span>'; } } /// prints percentage if ($grade_item->needsupdate) { $data[] = '<span class="' . $hidden . $class . 'gradingerror">' . get_string('error') . '</span>'; } else { $data[] = '<span class="' . $hidden . $class . '">' . grade_format_gradevalue($gradeval, $grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE) . '</span>'; } /// prints rank if ($this->showrank) { // TODO: this is broken if hidden grades present!! if ($grade_item->needsupdate) { $data[] = '<span class="' . $hidden . $class . 'gradingerror">' . get_string('error') . '</span>'; } else { if (is_null($gradeval)) { // no grade, no rank $data[] = '<span class="' . $hidden . $class . '">-</span>'; } else { /// find the number of users with a higher grade $sql = "SELECT COUNT(DISTINCT(userid))\n FROM {$CFG->prefix}grade_grades\n WHERE finalgrade > {$grade_grade->finalgrade}\n AND itemid = {$grade_item->id}"; $rank = count_records_sql($sql) + 1; $data[] = '<span class="' . $hidden . $class . '">' . "{$rank}/{$numusers}" . '</span>'; } } } /// prints feedback if (empty($grade_grade->feedback) or !$canviewhidden and $grade_grade->is_hidden()) { $data[] = '<div class="' . $hidden . 'feedbacktext"> </div>'; } else { $data[] = '<div class="' . $hidden . 'feedbacktext">' . format_text($grade_grade->feedback, $grade_grade->feedbackformat) . '</div>'; } $this->table->add_data($data); } return true; }
/** * Optionally blank out course/category totals if they contain any hidden items * @param string $courseid the course id * @param string $course_item an instance of grade_item * @param string $finalgrade the grade for the course_item * @return string The new final grade */ function blank_hidden_total($courseid, $course_item, $finalgrade) { global $CFG; static $hiding_affected = null; //array of items in this course affected by hiding if ($this->showtotalsifcontainhidden == GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN) { return $finalgrade; } if (!$hiding_affected) { $items = grade_item::fetch_all(array('courseid' => $courseid)); $grades = array(); $sql = "SELECT g.*\n FROM {$CFG->prefix}grade_grades g\n JOIN {$CFG->prefix}grade_items gi ON gi.id = g.itemid\n WHERE g.userid = {$this->user->id} AND gi.courseid = {$courseid}"; if ($gradesrecords = get_records_sql($sql)) { foreach ($gradesrecords as $grade) { $grades[$grade->itemid] = new grade_grade($grade, false); } unset($gradesrecords); } foreach ($items as $itemid => $unused) { if (!isset($grades[$itemid])) { $grade_grade = new grade_grade(); $grade_grade->userid = $this->user->id; $grade_grade->itemid = $items[$itemid]->id; $grades[$itemid] = $grade_grade; } $grades[$itemid]->grade_item =& $items[$itemid]; } $hiding_affected = grade_grade::get_hiding_affected($grades, $items); } //if the item definitely depends on a hidden item if (array_key_exists($course_item->id, $hiding_affected['altered'])) { if (!$this->showtotalsifcontainhidden) { //hide the grade $finalgrade = null; } else { //use reprocessed marks that exclude hidden items $finalgrade = $hiding_affected['altered'][$course_item->id]; } } else { if (!empty($hiding_affected['unknown'][$course_item->id])) { //not sure whether or not this item depends on a hidden item if (!$this->showtotalsifcontainhidden) { //hide the grade $finalgrade = null; } else { //use reprocessed marks that exclude hidden items $finalgrade = $hiding_affected['unknown'][$course_item->id]; } } } //unset($hiding_affected); //unset($grades); //unset($items); return $finalgrade; }