From 4ba9941c4bf33d16dd8dba699dac8ccddc477e28 Mon Sep 17 00:00:00 2001 From: nicolasconnault Date: Thu, 12 Jul 2007 16:36:38 +0000 Subject: [PATCH] MDL-10386 Refactored the grader report file (index.php), and extracted the grade_report_grader class out of it. The index file now only calls upon the class' main methods. Certain elements and code blocks in the class can be abstracted for other reports (such as the paging, sorting arrows and several general purpose variables). --- grade/report.php | 3 +- grade/report/grader/grader_report.php | 1136 +++++++++++++++++++++++++ grade/report/grader/index.php | 753 +--------------- lib/gradelib.php | 176 ---- lib/weblib.php | 12 +- 5 files changed, 1173 insertions(+), 907 deletions(-) create mode 100644 grade/report/grader/grader_report.php diff --git a/grade/report.php b/grade/report.php index 8a63da1b4c..c82967e8fa 100644 --- a/grade/report.php +++ b/grade/report.php @@ -29,7 +29,6 @@ $courseid = required_param('id'); // course id $report = optional_param('report', 'user', PARAM_FILE); // course id $edit = optional_param('edit', -1, PARAM_BOOL); // sticky editting mode - $feedback = optional_param('feedback', -1, PARAM_BOOL); // sticky feedback mode /// Make sure they can even access this course @@ -99,7 +98,7 @@ $USER->gradeediting = 0; } - // params for the turn editting on and feedback buttons + // params for the turn editting on $options['id'] = $courseid; $options['report'] = $report; diff --git a/grade/report/grader/grader_report.php b/grade/report/grader/grader_report.php new file mode 100644 index 0000000000..2a5f0aa84e --- /dev/null +++ b/grade/report/grader/grader_report.php @@ -0,0 +1,1136 @@ +libdir.'/tablelib.php'); +require_once($CFG->libdir.'/gradelib.php'); +require_once($CFG->dirroot.'/grade/report/lib.php'); + +/** + * Class providing an API for the grader report building and displaying. + * @package gradebook + */ +class grade_report_grader { + /** + * The courseid. + * @var int $courseid + */ + var $courseid; + + /** + * The context. + * @var int $context + */ + var $context; + + /** + * The grade_tree object. + * @var object $gtree + */ + var $gtree; + + /** + * The final grades. + * @var array $finalgrades + */ + var $finalgrades; + + /** + * The grade items. + * @var array $items + */ + var $items; + + /** + * Array of errors for bulk grades updating. + * @var array $gradeserror + */ + var $gradeserror = array(); + +//// USER PREFERENCES + + /** + * Number of users on a page. + * @var int $perpage + */ + var $studentsperpage; + + /** + * Number of digits after the decimal point. + * @var int $decimalspoints + */ + var $decimalspoints; + + /** + * Whether or not to display the grandtotals row. + * @var bool $showgrandtotals + */ + var $showgrandtotals; + + /** + * Whether or not to display group selector, total row and other group-related elements. + * @var bool $showgroups + */ + var $showgroups; + + /** + * The position of the Aggregation column in relation to the raw grade items. + * @var int $aggregation_position + */ + var $aggregation_position; + + /** + * Whether or not to display a row of scales/ranges for each grade_item. + * @var bool $showscales + */ + var $showscales; + + /** + * Whether or not to use quickgrading. + * @var bool $quickgrading + */ + var $quickgrading; + + /** + * Whether or not to use quickfeedback. + * @var bool $quickfeedback + */ + var $quickfeedback; + +//// SQL-RELATED + + /** + * The roles for this report. + * @var string $gradebookroles + */ + var $gradebookroles; + + /** + * base url for sorting by first/last name. + * @var string $baseurl + */ + var $baseurl; + + /** + * base url for paging. + * @var string $pbarurl + */ + var $pbarurl; + + /** + * Current page (for paging). + * @var int $page + */ + var $page; + + /** + * The id of the grade_item by which this report will be sorted. + * @var int $sortitemid + */ + var $sortitemid; + + /** + * Sortorder used in the SQL selections. + * @var int $sortorder + */ + var $sortorder; + + /** + * An SQL fragment affecting the search for users. + * @var string $userselect + */ + var $userselect; + +//// GROUP VARIABLES (including SQL) + + /** + * The current group being displayed. + * @var int $currentgroup + */ + var $currentgroup; + + /** + * A HTML select element used to select the current group. + * @var string $group_selector + */ + var $group_selector; + + /** + * An SQL fragment used to add linking information to the group tables. + * @var string $groupsql + */ + var $groupsql; + + /** + * An SQL constraint to append to the queries used by this object to build the report. + * @var string $groupwheresql + */ + var $groupwheresql; + + + + /** + * Constructor. Sets local copies of user preferences and initialises grade_tree. + * @param int $courseid + */ + function grade_report_grader($courseid, $context, $page=null, $sortitemid=null) { + global $CFG; + + $this->courseid = $courseid; + $this->context = $context; + $this->page = $page; + $this->sortitemid = $sortitemid; + + // roles to be displayed in the gradebook + $this->gradebookroles = $CFG->gradebookroles; + + // User preferences + $this->studentsperpage = get_user_preferences('grade_report_studentsperpage', + $CFG->grade_report_studentsperpage); + $this->decimalpoints = get_user_preferences('grade_report_decimalpoints', + $CFG->grade_report_decimalpoints); + $this->showgrandtotals = get_user_preferences('grade_report_showgrandtotals', + $CFG->grade_report_showgrandtotals); + $this->showgroups = get_user_preferences('grade_report_showgroups', + $CFG->grade_report_showgroups); + $this->aggregation_position = get_user_preferences('grade_report_aggregationposition', + $CFG->grade_report_aggregationposition); + $this->showscales = get_user_preferences('grade_report_showscales', + $CFG->grade_report_showscales); + $this->quickgrading = get_user_preferences('grade_report_quickgrading', + $CFG->grade_report_quickgrading); + $this->quickfeedback = get_user_preferences('grade_report_quickfeedback', + $CFG->grade_report_quickfeedback); + + // Grab the grade_tree for this course + $this->gtree = new grade_tree($this->courseid, true, false, $this->aggregation_position); + + // base url for sorting by first/last name + $this->baseurl = 'report.php?id='.$this->courseid.'&perpage='.$this->studentsperpage.'&report=grader&page='.$this->page; + // + $this->pbarurl = 'report.php?id='.$this->courseid.'&perpage='.$this->studentsperpage.'&report=grader&'; + + if ($this->showgroups) { + $this->setup_groups(); + } + + $this->setup_sortitemid(); + } + + /** + * Uses set_user_preferences() to update the value of a user preference. + * Also updates the object's corresponding variable. + * @param string $pref_name The name of the preference. + * @param mixed $pref_value The value of the preference. + * @return bool Success or failure. + * TODO print visual feedback + */ + function set_user_pref($pref_name, $pref_value) { + if ($result = set_user_preferences(array($pref_name => $pref_value))) { + $this->$pref_name = $pref_value; + } + return $result; + } + + /** + * Processes the data sent by the form (grades and feedbacks). + * @var array $data + * @return bool Success or Failure (array of errors). + */ + function process_data($data) { + // always initialize all arrays + $queue = array(); + + foreach ($data as $varname => $postedvalue) { + // this is a bit tricky - we have to first load all grades into memory, + // check if changed and only then start updating the final grades because + // columns might depend one on another - the result would be overriden calculated and category grades + + $needsupdate = false; + $note = false; // TODO implement note?? + + // skip, not a grade nor feedback + $data_type = ''; + if (strstr($varname, 'grade')) { + $data_type = 'grade'; + } elseif (strstr($varname, 'feedback')) { + $data_type = 'feedback'; + } else { + continue; + } + + $gradeinfo = explode("_", $varname); + + $userid = clean_param($gradeinfo[1], PARAM_INT); + $itemid = clean_param($gradeinfo[2], PARAM_INT); + + if (!$grade_item = grade_item::fetch(array('id'=>$itemid, 'courseid'=>$this->courseid))) { // we must verify course id here! + error('Incorrect grade item id'); + } + + // Pre-process grade + if ($data_type == 'grade') { + + if ($grade_item->gradetype == GRADE_TYPE_SCALE) { + if ($postedvalue == -1) { // -1 means no grade + $finalgrade = null; + } else { + $finalgrade = (float)$postedvalue; + } + } else { + if ($postedvalue == '') { // empty string means no grade + $finalgrade = null; + } else { + $finalgrade = format_grade($postedvalue); + } + } + + if (!is_null($finalgrade) and ($finalgrade < $grade_item->grademin or $finalgrade > $grade_item->grademax)) { + $this->gradeserror[$grade_item->id][$userid] = 'outofrange'; //TODO: localize + // another possiblity is to use bounded number instead + continue; + } + } + + // Get the grade object to compare old value with new value + if ($grade = grade_grades::fetch(array('userid'=>$userid, 'itemid'=>$grade_item->id))) { + if ($data_type == 'feedback') { + $finalgrade = false; + $text = $grade->load_text(); + if ($text != s($postedvalue)) { + $feedback = s($postedvalue); + $feedbackformat = GRADER_REPORT_FEEDBACK_FORMAT_TEXT; + $needsupdate = true; + } + } elseif ($data_type == 'grade') { + $feedback = false; + $feedbackformat = false; + if (!is_null($grade->finalgrade)) { + $grade->finalgrade = (float)$grade->finalgrade; + } + if ($grade->finalgrade === $finalgrade) { + $needsupdate = true; + } + } + + } + + // we must not update all grades, only changed ones - we do not want to mark everything as overriden + if ($needsupdate) { + $gradedata = new object(); + $gradedata->grade_item = $grade_item; + $gradedata->userid = $userid; + $gradedata->note = $note; + $gradedata->finalgrade = $finalgrade; + $gradedata->feedback = $feedback; + $gradedata->feedbackformat = $feedbackformat; + + $queue[] = $gradedata; + } + } + + // now we update the new final grade for each changed grade + foreach ($queue as $gradedata) { + $gradedata->grade_item->update_final_grade($gradedata->userid, $gradedata->finalgrade, 'gradebook', + $gradedata->note, $gradedata->feedback, $gradedata->feedbackformat); + } + + return true; + } + + /** + * Sets up this object's group variables, mainly to restrict the selection of users to display. + */ + function setup_groups() { + global $CFG; + + /// find out current groups mode + $course = get_record('course', 'id', $this->courseid); + $groupmode = $course->groupmode; + ob_start(); + $this->currentgroup = setup_and_print_groups($course, $groupmode, $this->baseurl); + $this->group_selector = ob_get_clean(); + + // update paging after group + $this->baseurl .= 'group='.$this->currentgroup.'&'; + $this->pbarurl .= 'group='.$this->currentgroup.'&'; + + if ($this->currentgroup) { + $this->groupsql = " LEFT JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id "; + $this->groupwheresql = " AND gm.groupid = $this->currentgroup "; + } + } + + /** + * Setting the sort order, this depends on last state + * all this should be in the new table class that we might need to use + * for displaying grades. + */ + function setup_sortitemid() { + if ($this->sortitemid) { + if (!isset($SESSION->gradeuserreport->sort)) { + $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; + } else { + // this is the first sort, i.e. by last name + if (!isset($SESSION->gradeuserreport->sortitemid)) { + $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; + } else if ($SESSION->gradeuserreport->sortitemid == $this->sortitemid) { + // same as last sort + if ($SESSION->gradeuserreport->sort == 'ASC') { + $this->sortorder = $SESSION->gradeuserreport->sort = 'DESC'; + } else { + $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; + } + } else { + $this->sortorder = $SESSION->gradeuserreport->sort = 'ASC'; + } + } + $SESSION->gradeuserreport->sortitemid = $this->sortitemid; + } else { + // not requesting sort, use last setting (for paging) + + if (isset($SESSION->gradeuserreport->sortitemid)) { + $this->sortitemid = $SESSION->gradeuserreport->sortitemid; + } + if (isset($SESSION->gradeuserreport->sort)) { + $this->sortorder = $SESSION->gradeuserreport->sort; + } else { + $this->sortorder = 'ASC'; + } + } + } + + /** + * Processes a single action against a category, grade_item or grade. + * @param string $target Sortorder + * @param string $action Which action to take (edit, delete etc...) + * @return + * TODO Update this, it's quite old and needs a major makeover + */ + function process_action($target, $action) { + $element = $this->gtree->locate_element($target); + + switch ($action) { + case 'edit': + break; + case 'delete': + if ($confirm == 1) { // Perform the deletion + //TODO: add proper delete support for grade items and categories + //$element['object']->delete(); + // Print result message + + } else { // Print confirmation dialog + $eid = $element['eid']; + $strdeletecheckfull = get_string('deletecheck', '', $element['object']->get_name()); + $linkyes = "category.php?target=$eid&action=delete&confirm=1$this->gtree->commonvars"; + $linkno = "category.php?$this->gtree->commonvars"; + notice_yesno($strdeletecheckfull, $linkyes, $linkno); + } + break; + + case 'hide': + // TODO Implement calendar for selection of a date to hide element until + $element['object']->set_hidden(1); + $this->gtree = new grade_tree($this->courseid); + break; + case 'show': + $element['object']->set_hidden(0); + $this->gtree = new grade_tree($this->courseid); + break; + case 'lock': + // TODO Implement calendar for selection of a date to lock element after + if (!$element['object']->set_locked(1)) { + debugging("Could not update the element's locked state!"); + } + $this->gtree = new grade_tree($this->courseid); + break; + case 'unlock': + if (!$element['object']->set_locked(0)) { + debugging("Could not update the element's locked state!"); + } + $this->gtree = new grade_tree($this->courseid); + break; + default: + break; + } + + } + + /** + * pulls out the userids of the users to be display, and sort them + * the right outer join is needed because potentially, it is possible not + * to have the corresponding entry in grade_grades table for some users + * this is check for user roles because there could be some users with grades + * but not supposed to be displayed + */ + function load_users() { + global $CFG; + + if (is_numeric($this->sortitemid)) { + $sql = "SELECT u.id, u.firstname, u.lastname + FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN + {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $this->sortitemid) + LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid + $this->groupsql + WHERE ra.roleid in ($this->gradebookroles) + $this->groupwheresql + AND ra.contextid ".get_related_contexts_string($this->context)." + ORDER BY g.finalgrade $this->sortorder"; + $this->users = get_records_sql($sql, $this->studentsperpage * $this->page, $this->studentsperpage); + } else { + // default sort + // get users sorted by lastname + $this->users = get_role_users(@implode(',', $CFG->gradebookroles), $this->context, false, + 'u.id, u.firstname, u.lastname', 'u.'.$this->sortitemid .' '. $this->sortorder, + false, $this->page * $this->studentsperpage, $this->studentsperpage, $this->currentgroup); + // need to cut users down by groups + + } + + if (empty($this->users)) { + $this->userselect = ''; + $this->users = array(); + } else { + $this->userselect = 'AND g.userid in ('.implode(',', array_keys($this->users)).')'; + } + + return $this->users; + } + + /** + * Fetches and returns a count of all the users that will be shows on this page. + * @return int Count of users + */ + function get_numusers() { + global $CFG; + $countsql = "SELECT COUNT(DISTINCT u.id) + FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN + {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $this->sortitemid) + LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid + $this->groupsql + WHERE ra.roleid in ($this->gradebookroles) + $this->groupwheresql + AND ra.contextid ".get_related_contexts_string($this->context); + return count_records_sql($countsql); + } + + /** + * we supply the userids in this query, and get all the grades + * pulls out all the grades, this does not need to worry about paging + */ + function load_final_grades() { + global $CFG; + + $sql = "SELECT g.id, g.itemid, g.userid, g.finalgrade, g.hidden, g.locked, g.locktime, g.overridden, gt.feedback + FROM {$CFG->prefix}grade_items gi, + {$CFG->prefix}grade_grades g + LEFT JOIN {$CFG->prefix}grade_grades_text gt ON g.id = gt.gradeid + WHERE g.itemid = gi.id + AND gi.courseid = $this->courseid $this->userselect"; + + if ($grades = get_records_sql($sql)) { + foreach ($grades as $grade) { + $this->finalgrades[$grade->userid][$grade->itemid] = $grade; + } + } + } + + /** + * Builds and returns a div with on/off toggles. + * @return string HTML code + */ + function get_toggles_html() { + global $USER; + $html = '
'; + if ($USER->gradeediting) { + $html .= $this->print_toggle('eyecons', true); + $html .= $this->print_toggle('locks', true); + $html .= $this->print_toggle('calculations', true); + } + + $html .= $this->print_toggle('grandtotals', true); + $html .= $this->print_toggle('groups', true); + $html .= $this->print_toggle('scales', true); + $html .= '
'; + return $html; + } + + /** + * Shortcut function for printing the grader report toggles. + * @param string $type The type of toggle + * @param bool $return Whether to return the HTML string rather than printing it + * @return void + */ + function print_toggle($type, $return=false) { + global $CFG; + + $icons = array('eyecons' => 'hide', + 'calculations' => 'calc', + 'locks' => 'lock', + 'grandtotals' => 'sigma'); + + $pref_name = 'grade_report_show' . $type; + $show_pref = get_user_preferences($pref_name, $CFG->$pref_name); + + $strshow = get_string('show' . $type, 'grades'); + $strhide = get_string('hide' . $type, 'grades'); + + $show_hide = 'show'; + $toggle_action = 1; + + if ($show_pref) { + $show_hide = 'hide'; + $toggle_action = 0; + } + + if (array_key_exists($type, $icons)) { + $image_name = $icons[$type]; + } else { + $image_name = $type; + } + + $string = ${'str' . $show_hide}; + + $img = ''
+                      .$string.''. "\n"; + + $retval = '
' . $img . '" + . $string . '
'; + + if ($return) { + return $retval; + } else { + echo $retval; + } + } + + /** + * Builds and returns the HTML code for the headers. + * @return string $headerhtml + */ + function get_headerhtml() { + global $CFG, $USER; + + $strsortasc = get_string('sortasc', 'grades'); + $strsortdesc = get_string('sortdesc', 'grades'); + if ($this->sortitemid === 'lastname') { + if ($this->sortorder == 'ASC') { + $lastarrow = print_arrow('up', $strsortasc, true); + } else { + $lastarrow = print_arrow('down', $strsortdesc, true); + } + } else { + $lastarrow = ''; + } + + if ($this->sortitemid === 'firstname') { + if ($this->sortorder == 'ASC') { + $firstarrow = print_arrow('up', $strsortasc, true); + } else { + $firstarrow = print_arrow('down', $strsortdesc, true); + } + } else { + $firstarrow = ''; + } + // Prepare Table Headers + $headerhtml = ''; + + $numrows = count($this->gtree->levels); + + foreach ($this->gtree->levels as $key=>$row) { + if ($key == 0) { + // do not diplay course grade category + // continue; + } + + $headerhtml .= ''; + + if ($key == $numrows - 1) { + $headerhtml .= 'Firstname ' //TODO: localize + . $firstarrow. '/ Lastname '. $lastarrow .''; + } else { + $headerhtml .= ' '; + } + + foreach ($row as $element) { + $eid = $element['eid']; + $object = $element['object']; + $type = $element['type']; + + if (!empty($element['colspan'])) { + $colspan = 'colspan="'.$element['colspan'].'"'; + } else { + $colspan = ''; + } + + if (!empty($element['depth'])) { + $catlevel = ' catlevel'.$element['depth']; + } else { + $catlevel = ''; + } + + + if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') { + $headerhtml .= ' '; + } else if ($type == 'category') { + $headerhtml .= ''.$element['object']->get_name(); + + // Print icons + if ($USER->gradeediting) { + $headerhtml .= $this->get_icons($element); + } + + $headerhtml .= ''; + } else { + if ($element['object']->id == $this->sortitemid) { + if ($this->sortorder == 'ASC') { + $arrow = print_arrow('up', $strsortasc, true); + } else { + $arrow = print_arrow('down', $strsortdesc, true); + } + } else { + $arrow = ''; + } + + $dimmed = ''; + if ($element['object']->is_hidden()) { + $dimmed = ' dimmed_text '; + } + + if ($object->itemtype == 'mod') { + $icon = ''
+                              .get_string('modulename', $object->itemmodule).''; + } else if ($object->itemtype == 'manual') { + //TODO: add manual grading icon + $icon = ''.get_string('manualgrade', 'grades')
+                              .''; + } + + + $headerhtml .= ''. $element['object']->get_name() + . '' . $arrow; + + $headerhtml .= $this->get_icons($element) . ''; + + $this->items[$element['object']->sortorder] =& $element['object']; + } + + } + + $headerhtml .= ''; + } + return $headerhtml; + } + + /** + * Builds and return the HTML rows of the table (grades headed by student). + * @return string HTML + */ + function get_studentshtml() { + global $CFG, $USER; + $studentshtml = ''; + $strfeedback = get_string("feedback"); + + foreach ($this->users as $userid => $user) { + // Student name and link + $studentshtml .= '' . fullname($user) . ''; + foreach ($this->items as $item) { + + if (isset($this->finalgrades[$userid][$item->id])) { + $gradeval = $this->finalgrades[$userid][$item->id]->finalgrade; + $grade = new grade_grades($this->finalgrades[$userid][$item->id], false); + $grade->feedback = $this->finalgrades[$userid][$item->id]->feedback; + + } else { + $gradeval = null; + $grade = new grade_grades(array('userid' => $userid, 'itemid' => $item->id), false); + $grade->feedback = ''; + } + + if ($grade->is_overridden()) { + $studentshtml .= ''; + } else { + $studentshtml .= ''; + } + + // emulate grade element + $grade->courseid = $this->courseid; + $grade->grade_item = $item; // this may speedup is_hidden() and other grade_grades methods + $element = array ('eid'=>'g'.$grade->id, 'object'=>$grade, 'type'=>'grade'); + + // Do not show any icons if no grade (no record in DB to match) + if (!empty($grade->id)) { + $studentshtml .= $this->get_icons($element); + } + + // 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 ($USER->gradeediting) { + // We need to retrieve each grade_grade object from DB in order to + // know if they are hidden/locked + + if ($item->scaleid) { + if ($scale = get_record('scale', 'id', $item->scaleid)) { + $scales = explode(",", $scale->scale); + // reindex because scale is off 1 + $i = 0; + foreach ($scales as $scaleoption) { + $i++; + $scaleopt[$i] = $scaleoption; + } + + if ($this->quickgrading) { + $studentshtml .= choose_from_menu($scaleopt, 'grade_'.$userid.'_'.$item->id, + $gradeval, get_string('nograde'), '', -1, true); + } elseif ($scale = get_record('scale', 'id', $item->scaleid)) { + $scales = explode(",", $scale->scale); + + // invalid grade if gradeval < 1 + if ((int) $gradeval < 1) { + $studentshtml .= '-'; + } else { + $studentshtml .= $scales[$gradeval-1]; + } + } else { + // no such scale, throw error? + } + } + } else { + if ($this->quickgrading) { + $studentshtml .= ''; + } else { + $studentshtml .= get_grade_clean($gradeval); + } + } + + + // If quickfeedback is on, print an input element + if ($this->quickfeedback) { + if ($this->quickgrading) { + $studentshtml .= '
'; + } + $studentshtml .= ''; + } + + $studentshtml .= '
' . $this->get_icons($element, array('edit')) . '
'; + } else { + // If feedback present, surround grade with feedback tooltip + if (!empty($grade->feedback)) { + $studentshtml .= ''; + } + + // finalgrades[$userid][$itemid] could be null because of the outer join + // in this case it's different than a 0 + if ($item->scaleid) { + if ($scale = get_record('scale', 'id', $item->scaleid)) { + $scales = explode(",", $scale->scale); + + // invalid grade if gradeval < 1 + if ((int) $gradeval < 1) { + $studentshtml .= '-'; + } else { + $studentshtml .= $scales[$gradeval-1]; + } + } else { + // no such scale, throw error? + } + } else { + if (is_null($gradeval)) { + $studentshtml .= '-'; + } else { + $studentshtml .= get_grade_clean($gradeval); + } + } + if (!empty($grade->feedback)) { + $studentshtml .= ''; + } + } + + if (!empty($this->gradeserror[$item->id][$userid])) { + $studentshtml .= $this->gradeserror[$item->id][$userid]; + } + + $studentshtml .= '' . "\n"; + } + $studentshtml .= ''; + } + return $studentshtml; + } + + /** + * Builds and return the HTML rows of the table (grades headed by student). + * @return string HTML + */ + function get_groupsumhtml() { + global $CFG; + + $groupsumhtml = ''; + + if ($this->currentgroup && $this->showgroups) { + + /** SQL for finding group sum */ + $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum + FROM {$CFG->prefix}grade_items gi LEFT JOIN + {$CFG->prefix}grade_grades g ON gi.id = g.itemid RIGHT OUTER JOIN + {$CFG->prefix}user u ON u.id = g.userid LEFT JOIN + {$CFG->prefix}role_assignments ra ON u.id = ra.userid + $this->groupsql + WHERE gi.courseid = $this->courseid + $this->groupwheresql + AND ra.roleid in ($this->gradebookroles) + AND ra.contextid ".get_related_contexts_string($this->context)." + GROUP BY g.itemid"; + + $groupsum = array(); + $sums = get_records_sql($SQL); + foreach ($sums as $itemid => $csum) { + $groupsum[$itemid] = $csum; + } + + $groupsumhtml = 'Group total'; + foreach ($this->items as $item) { + if (!isset($groupsum[$item->id])) { + $groupsumhtml .= '-'; + } else { + $sum = $groupsum[$item->id]; + $groupsumhtml .= ''.get_grade_clean($sum->sum).''; + } + } + $groupsumhtml .= ''; + } + return $groupsumhtml; + } + + function get_gradesumhtml() { + global $CFG; + + $gradesumhtml = ''; + if ($this->showgrandtotals) { + + /** SQL for finding the SUM grades of all visible users ($CFG->gradebookroles) */ + + $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum + FROM {$CFG->prefix}grade_items gi LEFT JOIN + {$CFG->prefix}grade_grades g ON gi.id = g.itemid RIGHT OUTER JOIN + {$CFG->prefix}user u ON u.id = g.userid LEFT JOIN + {$CFG->prefix}role_assignments ra ON u.id = ra.userid + WHERE gi.courseid = $this->courseid + AND ra.roleid in ($this->gradebookroles) + AND ra.contextid ".get_related_contexts_string($this->context)." + GROUP BY g.itemid"; + + $classsum = array(); + $sums = get_records_sql($SQL); + foreach ($sums as $itemid => $csum) { + $classsum[$itemid] = $csum; + } + + $gradesumhtml = 'Total'; + foreach ($this->items as $item) { + if (!isset($classsum[$item->id])) { + $gradesumhtml .= '-'; + } else { + $sum = $classsum[$item->id]; + $gradesumhtml .= ''.get_grade_clean($sum->sum).''; + } + } + $gradesumhtml .= ''; + } + return $gradesumhtml; + } + + function get_scalehtml() { + $scalehtml = ''; + if ($this->showscales) { + $scalehtml = ''.get_string('range','grades').''; + foreach ($this->items as $item) { + $scalehtml .= ''. get_grade_clean($item->grademin).'-'. get_grade_clean($item->grademax).''; + } + $scalehtml .= ''; + } + return $scalehtml; + } + + /** + * Given a grade_category, grade_item or grade_grade, this function + * figures out the state of the object and builds then returns a div + * with the icons needed for the grader report. + * + * @param object $object + * @param array $icons An array of icon names that this function is explicitly requested to print, regardless of settings + * @param bool $limit If true, use the $icons array as the only icons that will be printed. If false, use it to exclude these icons. + * @return string HTML + */ + function get_icons($element, $icons=null, $limit=true) { + global $CFG; + global $USER; + + // Load language strings + $stredit = get_string("edit"); + $streditcalculation= get_string("editcalculation", 'grades'); + $strfeedback = get_string("feedback"); + $strmove = get_string("move"); + $strmoveup = get_string("moveup"); + $strmovedown = get_string("movedown"); + $strmovehere = get_string("movehere"); + $strcancel = get_string("cancel"); + $stredit = get_string("edit"); + $strdelete = get_string("delete"); + $strhide = get_string("hide"); + $strshow = get_string("show"); + $strlock = get_string("lock", 'grades'); + $strswitch_minus = get_string("contract", 'grades'); + $strswitch_plus = get_string("expand", 'grades'); + $strunlock = get_string("unlock", 'grades'); + + // Prepare container div + $html = '
'; + + // Prepare reference variables + $eid = $element['eid']; + $object = $element['object']; + $type = $element['type']; + + // Add mock attributes in case the object is not of the right type + if ($type != 'grade') { + $object->feedback = ''; + } + + // Load user preferences + $aggregationview = get_user_preferences('grade_report_aggregationview', $CFG->grade_report_aggregationview); + $showeyecons = get_user_preferences('grade_report_showeyecons', $CFG->grade_report_showeyecons); + $showlocks = get_user_preferences('grade_report_showlocks', $CFG->grade_report_showlocks); + $showcalculations = get_user_preferences('grade_report_showcalculations', $CFG->grade_report_showcalculations); + + // Prepare image strings + $edit_category_icon = '' + . ''
+                            . $stredit.''. "\n"; + + $edit_item_icon = '' + . ''
+                        . $stredit.''. "\n"; + $overlib = ''; + if (!empty($object->feedback)) { + $overlib = 'onmouseover="return overlib(\''.$object->feedback.'\', CAPTION, \'' + . $strfeedback.'\');" onmouseout="return nd();"'; + } + + $edit_grade_icon = '' + . '' . $stredit.''. "\n"; + + + $edit_calculation_icon = '' + . ''
+                               . $streditcalculation.''. "\n"; + + // Prepare Hide/Show icon state + $hide_show = 'hide'; + if ($object->is_hidden()) { + $hide_show = 'show'; + } + + $show_hide_icon = 'gtree->commonvars . "\">\n" + . ''
+                        . ${'str' . $hide_show}.''. "\n"; + + // Prepare lock/unlock string + $lock_unlock = 'lock'; + if ($object->is_locked()) { + $lock_unlock = 'unlock'; + } + + // Print lock/unlock icon + + $lock_unlock_icon = 'gtree->commonvars . "\">\n" + . ''
+                          . ${'str' . $lock_unlock}.''. "\n"; + + // Prepare expand/contract string + $expand_contract = 'switch_minus'; // Default: expanded + $state = get_user_preferences('grade_category_' . $object->id, GRADE_CATEGORY_EXPANDED); + if ($state == GRADE_CATEGORY_CONTRACTED) { + $expand_contract = 'switch_plus'; + } + + $contract_expand_icon = 'gtree->commonvars . "\">\n" + . ''
+                              . ${'str' . $expand_contract}.''. "\n"; + + // If an array of icon names is given, return only these in the order they are given + if (!empty($icons) && is_array($icons)) { + $new_html = ''; + + foreach ($icons as $icon_name) { + if ($icon_name == 'edit') { + $icon_name .= "_$type"; + } + if ($limit) { + $new_html .= ${$icon_name . '_icon'}; + } else { + ${'show_' . $icon_name} = false; + } + } + if ($limit) { + return $new_html; + } else { + $html .= $new_html; + } + } + + // Icons shown when edit mode is on + if ($USER->gradeediting) { + // Edit icon (except for grade_grades) + if ($type == 'category') { + $html .= $edit_category_icon; + + } else if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') { + $html .= $edit_item_icon; + } + + // Calculation icon for items and categories + if ($showcalculations && $type != 'grade') { + $html .= $edit_calculation_icon; + } + + if ($showeyecons) { + $html .= $show_hide_icon; + } + + if ($showlocks) { + $html .= $lock_unlock_icon; + } + + // If object is a category, display expand/contract icon + if (get_class($object) == 'grade_category' && $aggregationview == GRADER_REPORT_AGGREGATION_VIEW_COMPACT) { + $html .= $contract_expand_icon; + } + } else { // Editing mode is off + } + + return $html . '
'; + } +} +?> diff --git a/grade/report/grader/index.php b/grade/report/grader/index.php index 295405d11d..cc773afd50 100644 --- a/grade/report/grader/index.php +++ b/grade/report/grader/index.php @@ -2,431 +2,53 @@ /// This creates and handles the whole grader report interface, sans header and footer -require_once($CFG->libdir.'/tablelib.php'); -require_once($CFG->libdir.'/gradelib.php'); -require_once($CFG->dirroot.'/grade/report/lib.php'); +require_once($CFG->dirroot.'/grade/report/grader/grader_report.php'); $gradeserror = array(); -/** -* Shortcut function for printing the grader report toggles. -* @param string $type The type of toggle -* @param string $baseurl The base of the URL the toggles will link to -* @param bool $return Whether to return the HTML string rather than printing it -* @return void -*/ -function grader_report_print_toggle($type, $baseurl, $return=false) { - global $CFG; - - $icons = array('eyecons' => 'hide', - 'calculations' => 'calc', - 'locks' => 'lock', - 'grandtotals' => 'sigma'); - - $pref_name = 'grade_report_show' . $type; - $show_pref = get_user_preferences($pref_name, $CFG->$pref_name); - - $strshow = get_string('show' . $type, 'grades'); - $strhide = get_string('hide' . $type, 'grades'); - - $show_hide = 'show'; - $toggle_action = 1; - - if ($show_pref) { - $show_hide = 'hide'; - $toggle_action = 0; - } - - if (array_key_exists($type, $icons)) { - $image_name = $icons[$type]; - } else { - $image_name = $type; - } - - $string = ${'str' . $show_hide}; - - $img = ''
-                  .$string.''. "\n"; - - $retval = '
' . $img . '" - . $string . '
'; - - if ($return) { - return $retval; - } else { - echo $retval; - } -} - - -/// processing posted grades & feedback here - -if ($data = data_submitted() and confirm_sesskey()) { - - // always initialize all arrays - $queue = array(); - - foreach ($data as $varname => $postedvalue) { - // this is a bit tricky - we have to first load all grades into memory, - // check if changed and only then start updating the final grades because - // columns might depend one on another - the result would be overriden calculated and category grades - - $needsupdate = false; - $note = false; // TODO implement note?? - - // skip, not a grade nor feedback - $data_type = ''; - if (strstr($varname, 'grade')) { - $data_type = 'grade'; - } elseif (strstr($varname, 'feedback')) { - $data_type = 'feedback'; - } else { - continue; - } - - $gradeinfo = explode("_", $varname); - - $userid = clean_param($gradeinfo[1], PARAM_INT); - $itemid = clean_param($gradeinfo[2], PARAM_INT); - - if (!$grade_item = grade_item::fetch(array('id'=>$itemid, 'courseid'=>$course->id))) { // we must verify course id here! - error('Incorrect grade item id'); - } - - // Pre-process grade - if ($data_type == 'grade') { - - if ($grade_item->gradetype == GRADE_TYPE_SCALE) { - if ($postedvalue == -1) { // -1 means no grade - $finalgrade = null; - } else { - $finalgrade = (float)$postedvalue; - } - } else { - if ($postedvalue == '') { // empty string means no grade - $finalgrade = null; - } else { - $finalgrade = format_grade($postedvalue); - } - } - - if (!is_null($finalgrade) and ($finalgrade < $grade_item->grademin or $finalgrade > $grade_item->grademax)) { - $gradeserror[$grade_item->id][$userid] = 'outofrange'; //TODO: localize - // another possiblity is to use bounded number instead - continue; - } - } - - // Get the grade object to compare old value with new value - if ($grade = grade_grades::fetch(array('userid'=>$userid, 'itemid'=>$grade_item->id))) { - if ($data_type == 'feedback') { - $finalgrade = false; - $text = $grade->load_text(); - if ($text != s($postedvalue)) { - $feedback = s($postedvalue); - $feedbackformat = GRADER_REPORT_FEEDBACK_FORMAT_TEXT; - $needsupdate = true; - } - } elseif ($data_type == 'grade') { - $feedback = false; - $feedbackformat = false; - if (!is_null($grade->finalgrade)) { - $grade->finalgrade = (float)$grade->finalgrade; - } - if ($grade->finalgrade === $finalgrade) { - $needsupdate = true; - } - } - - } - - // we must not update all grades, only changed ones - we do not want to mark everything as overriden - if ($needsupdate) { - $gradedata = new object(); - $gradedata->grade_item = $grade_item; - $gradedata->userid = $userid; - $gradedata->note = $note; - $gradedata->finalgrade = $finalgrade; - $gradedata->feedback = $feedback; - $gradedata->feedbackformat = $feedbackformat; - - $queue[] = $gradedata; - } - } - - // now we update the new final grade for each changed grade - foreach ($queue as $gradedata) { - $gradedata->grade_item->update_final_grade($gradedata->userid, $gradedata->finalgrade, 'gradebook', - $gradedata->note, $gradedata->feedback, $gradedata->feedbackformat); - } -} - -// get the params -$courseid = required_param('id', PARAM_INT); -$context = get_context_instance(CONTEXT_COURSE, $courseid); +// get the params ($report, $courseid and $context are already set in grade/report.php) $page = optional_param('page', 0, PARAM_INT); $sortitemid = optional_param('sortitemid', 0, PARAM_ALPHANUM); // sort by which grade item -$report = optional_param('report', 0, PARAM_ALPHANUM); $action = optional_param('action', 0, PARAM_ALPHA); $move = optional_param('move', 0, PARAM_INT); $type = optional_param('type', 0, PARAM_ALPHA); $target = optional_param('target', 0, PARAM_ALPHANUM); $toggle = optional_param('toggle', NULL, PARAM_INT); $toggle_type = optional_param('toggle_type', 0, PARAM_ALPHANUM); - +$db->debug=true; // Handle toggle change request -// TODO print visual feedback if (!is_null($toggle) && !empty($toggle_type)) { set_user_preferences(array('grade_report_show' . $toggle_type => $toggle)); } -// Get the user preferences -$perpage = get_user_preferences('grade_report_studentsperpage', $CFG->grade_report_studentsperpage); // number of users on a page -$decimals = get_user_preferences('grade_report_decimalpoints', $CFG->grade_report_decimalpoints); // decimals in grades -$showgrandtotals = get_user_preferences('grade_report_showgrandtotals', $CFG->grade_report_showgrandtotals); -$showgroups = get_user_preferences('grade_report_showgroups', $CFG->grade_report_showgroups); -$aggregation_position = get_user_preferences('grade_report_aggregationposition', $CFG->grade_report_aggregationposition); -$showscales = get_user_preferences('grade_report_showscales', $CFG->grade_report_showscales); -$quickgrading = get_user_preferences('grade_report_quickgrading', $CFG->grade_report_quickgrading); -$quickfeedback = get_user_preferences('grade_report_quickfeedback', $CFG->grade_report_quickfeedback); +// Initialise the grader report object +$report = new grade_report_grader($courseid, $context, $page, $sortitemid); -// Override perpage if set in URL -if ($perpageurl = optional_param('perpage', 0, PARAM_INT)) { - $perpage = $perpageurl; -} - -// Prepare language strings -$strsortasc = get_string('sortasc', 'grades'); -$strsortdesc = get_string('sortdesc', 'grades'); -$strfeedback = get_string("feedback"); - -// base url for sorting by first/last name -$baseurl = 'report.php?id='.$courseid.'&perpage='.$perpage.'&report=grader&page='.$page; -// base url for paging -$pbarurl = 'report.php?id='.$courseid.'&perpage='.$perpage.'&report=grader&'; - -/// setting up groups -$groupsql = ''; -$groupwheresql = ''; -$group_selector = null; -$currentgroup = null; - -if ($showgroups) { - /// find out current groups mode - $course = get_record('course', 'id', $courseid); - $groupmode = $course->groupmode; - ob_start(); - $currentgroup = setup_and_print_groups($course, $groupmode, $baseurl); - $group_selector = ob_get_clean(); - - // update paging after group - $baseurl .= 'group='.$currentgroup.'&'; - $pbarurl .= 'group='.$currentgroup.'&'; - - if ($currentgroup) { - $groupsql = " LEFT JOIN {$CFG->prefix}groups_members gm ON gm.userid = u.id "; - $groupwheresql = " AND gm.groupid = $currentgroup "; - } +/// processing posted grades & feedback here +if ($data = data_submitted() and confirm_sesskey()) { + $report->process_data($data); } -// Grab the grade_tree for this course -$gtree = new grade_tree($courseid, true, false, $aggregation_position); - -// setting the sort order, this depends on last state -// all this should be in the new table class that we might need to use -// for displaying grades - -// already in not requesting sort, i.e. normal paging - -if ($sortitemid) { - if (!isset($SESSION->gradeuserreport->sort)) { - $sortorder = $SESSION->gradeuserreport->sort = 'ASC'; - } else { - // this is the first sort, i.e. by last name - if (!isset($SESSION->gradeuserreport->sortitemid)) { - $sortorder = $SESSION->gradeuserreport->sort = 'ASC'; - } else if ($SESSION->gradeuserreport->sortitemid == $sortitemid) { - // same as last sort - if ($SESSION->gradeuserreport->sort == 'ASC') { - $sortorder = $SESSION->gradeuserreport->sort = 'DESC'; - } else { - $sortorder = $SESSION->gradeuserreport->sort = 'ASC'; - } - } else { - $sortorder = $SESSION->gradeuserreport->sort = 'ASC'; - } - } - $SESSION->gradeuserreport->sortitemid = $sortitemid; -} else { - // not requesting sort, use last setting (for paging) - - if (isset($SESSION->gradeuserreport->sortitemid)) { - $sortitemid = $SESSION->gradeuserreport->sortitemid; - } - if (isset($SESSION->gradeuserreport->sort)) { - $sortorder = $SESSION->gradeuserreport->sort; - } else { - $sortorder = 'ASC'; - } +// Override perpage if set in URL +if ($perpageurl = optional_param('perpage', 0, PARAM_INT)) { + $report->studentsperpage = $perpageurl; } -/// end of setting sort order code - // Perform actions on categories, items and grades if (!empty($target) && !empty($action) && confirm_sesskey()) { - - $element = $gtree->locate_element($target); - - switch ($action) { - case 'edit': - break; - case 'delete': - if ($confirm == 1) { // Perform the deletion - //TODO: add proper delete support for grade items and categories - //$element['object']->delete(); - // Print result message - - } else { // Print confirmation dialog - $eid = $element['eid']; - $strdeletecheckfull = get_string('deletecheck', '', $element['object']->get_name()); - $linkyes = "category.php?target=$eid&action=delete&confirm=1$gtree->commonvars"; - $linkno = "category.php?$gtree->commonvars"; - notice_yesno($strdeletecheckfull, $linkyes, $linkno); - } - break; - - case 'hide': - // TODO Implement calendar for selection of a date to hide element until - $element['object']->set_hidden(1); - $gtree = new grade_tree($courseid); - break; - case 'show': - $element['object']->set_hidden(0); - $gtree = new grade_tree($courseid); - break; - case 'lock': - // TODO Implement calendar for selection of a date to lock element after - if (!$element['object']->set_locked(1)) { - debugging("Could not update the element's locked state!"); - } - $gtree = new grade_tree($courseid); - break; - case 'unlock': - if (!$element['object']->set_locked(0)) { - debugging("Could not update the element's locked state!"); - } - $gtree = new grade_tree($courseid); - break; - default: - break; - } + $report->process_action($target, $action); } // first make sure we have all final grades // TODO: check that no grade_item has needsupdate set grade_regrade_final_grades($courseid); -// roles to be displaye in the gradebook -$gradebookroles = $CFG->gradebookroles; - -/* -* pulls out the userids of the users to be display, and sort them -* the right outer join is needed because potentially, it is possible not -* to have the corresponding entry in grade_grades table for some users -* this is check for user roles because there could be some users with grades -* but not supposed to be displayed -*/ -if (is_numeric($sortitemid)) { - $sql = "SELECT u.id, u.firstname, u.lastname - FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN - {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $sortitemid) - LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid - $groupsql - WHERE ra.roleid in ($gradebookroles) - $groupwheresql - AND ra.contextid ".get_related_contexts_string($context)." - ORDER BY g.finalgrade $sortorder"; - $users = get_records_sql($sql, $perpage * $page, $perpage); -} else { - // default sort - // get users sorted by lastname - $users = get_role_users(@implode(',', $CFG->gradebookroles), $context, false, 'u.id, u.firstname, u.lastname', 'u.'.$sortitemid .' '. $sortorder, false, $page * $perpage, $perpage, $currentgroup); - // need to cut users down by groups - -} - -/// count total records for paging - -$countsql = "SELECT COUNT(DISTINCT u.id) - FROM {$CFG->prefix}grade_grades g RIGHT OUTER JOIN - {$CFG->prefix}user u ON (u.id = g.userid AND g.itemid = $sortitemid) - LEFT JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid - $groupsql - WHERE ra.roleid in ($gradebookroles) - $groupwheresql - AND ra.contextid ".get_related_contexts_string($context); -$numusers = count_records_sql($countsql); - -// print_object($users); // debug - -if (empty($users)) { - $userselect = ''; - $users = array(); -} else { - $userselect = 'AND g.userid in ('.implode(',', array_keys($users)).')'; -} - - -// phase 2 sql, we supply the userids in this query, and get all the grades -// pulls out all the grades, this does not need to worry about paging -$sql = "SELECT g.id, g.itemid, g.userid, g.finalgrade, g.hidden, g.locked, g.locktime, g.overridden, gt.feedback - FROM {$CFG->prefix}grade_items gi, - {$CFG->prefix}grade_grades g - LEFT JOIN {$CFG->prefix}grade_grades_text gt ON g.id = gt.gradeid - WHERE g.itemid = gi.id - AND gi.courseid = $courseid $userselect"; - -///print_object($grades); //debug - -$finalgrades = array(); -// needs to be formatted into an array for easy retrival - -if ($grades = get_records_sql($sql)) { - foreach ($grades as $grade) { - $finalgrades[$grade->userid][$grade->itemid] = $grade; - } -} - -/// With the users in an sorted array and grades fetched, we can not print the main html table +$report->load_users(); +$numusers = $report->get_numusers(); +$report->load_final_grades(); -// 1. Fetch all top-level categories for this course, with all children preloaded, sorted by sortorder - - // Fetch array of students enroled in this course -if (!$context = get_context_instance(CONTEXT_COURSE, $gtree->courseid)) { +if (!$context = get_context_instance(CONTEXT_COURSE, $report->gtree->courseid)) { return false; } -//$users = get_role_users(@implode(',', $CFG->gradebookroles), $context); - -if ($sortitemid === 'lastname') { - if ($sortorder == 'ASC') { - $lastarrow = print_arrow('up', $strsortasc, true); - } else { - $lastarrow = print_arrow('down', $strsortdesc, true); - } -} else { - $lastarrow = ''; -} - -if ($sortitemid === 'firstname') { - if ($sortorder == 'ASC') { - $firstarrow = print_arrow('up', $strsortasc, true); - } else { - $firstarrow = print_arrow('down', $strsortdesc, true); - } -} else { - $firstarrow = ''; -} - -/********* BEGIN OUTPUT *********/ print_heading('Grader Report'); @@ -434,333 +56,18 @@ print_heading('Grader Report'); $currenttab = 'graderreport'; include('tabs.php'); -// Group selection drop-down -echo $group_selector; - -// Show/hide toggles -echo '
'; -if ($USER->gradeediting) { - grader_report_print_toggle('eyecons', $baseurl); - grader_report_print_toggle('locks', $baseurl); - grader_report_print_toggle('calculations', $baseurl); -} - -grader_report_print_toggle('grandtotals', $baseurl); -grader_report_print_toggle('groups', $baseurl); -grader_report_print_toggle('scales', $baseurl); -echo '
'; - -// Paging bar -print_paging_bar($numusers, $page, $perpage, $pbarurl); - -$items = array(); - -// Prepare Table Headers -$headerhtml = ''; - -$numrows = count($gtree->levels); - -foreach ($gtree->levels as $key=>$row) { - if ($key == 0) { - // do not diplay course grade category - // continue; - } - - $headerhtml .= ''; - - if ($key == $numrows - 1) { - $headerhtml .= 'Firstname ' //TODO: localize - . $firstarrow. '/ Lastname '. $lastarrow .''; - } else { - $headerhtml .= ' '; - } - - foreach ($row as $element) { - $eid = $element['eid']; - $object = $element['object']; - $type = $element['type']; - - if (!empty($element['colspan'])) { - $colspan = 'colspan="'.$element['colspan'].'"'; - } else { - $colspan = ''; - } - - if (!empty($element['depth'])) { - $catlevel = ' catlevel'.$element['depth']; - } else { - $catlevel = ''; - } - - - if ($type == 'filler' or $type == 'fillerfirst' or $type == 'fillerlast') { - $headerhtml .= ' '; - } else if ($type == 'category') { - $headerhtml .= ''.$element['object']->get_name(); - - // Print icons - if ($USER->gradeediting) { - $headerhtml .= grade_get_icons($element, $gtree); - } - - $headerhtml .= ''; - } else { - if ($element['object']->id == $sortitemid) { - if ($sortorder == 'ASC') { - $arrow = print_arrow('up', $strsortasc, true); - } else { - $arrow = print_arrow('down', $strsortdesc, true); - } - } else { - $arrow = ''; - } - - $dimmed = ''; - if ($element['object']->is_hidden()) { - $dimmed = ' dimmed_text '; - } - - if ($object->itemtype == 'mod') { - $icon = ''
-                      .get_string('modulename', $object->itemmodule).''; - } else if ($object->itemtype == 'manual') { - //TODO: add manual grading icon - $icon = ''.get_string('manualgrade', 'grades')
-                      .''; - } - - - $headerhtml .= ''. $element['object']->get_name() - . '' . $arrow; - - $headerhtml .= grade_get_icons($element, $gtree) . ''; - - $items[$element['object']->sortorder] =& $element['object']; - } - - } - - $headerhtml .= ''; -} - -// Prepare Table Rows -$studentshtml = ''; - -foreach ($users as $userid => $user) { - // Student name and link - $studentshtml .= '' . fullname($user) . ''; - foreach ($items as $item) { - - if (isset($finalgrades[$userid][$item->id])) { - $gradeval = $finalgrades[$userid][$item->id]->finalgrade; - $grade = new grade_grades($finalgrades[$userid][$item->id], false); - $grade->feedback = $finalgrades[$userid][$item->id]->feedback; - - } else { - $gradeval = null; - $grade = new grade_grades(array('userid' => $userid, 'itemid' => $item->id), false); - $grade->feedback = ''; - } - - if ($grade->is_overridden()) { - $studentshtml .= ''; - } else { - $studentshtml .= ''; - } - - // Do not show any icons if no grade (no record in DB to match) - if (!empty($grade->id)) { - // emulate grade element - $grade->courseid = $course->id; - $grade->grade_item = $item; // this may speedup is_hidden() and other grade_grades methods - $element = array ('eid'=>'g'.$grade->id, 'object'=>$grade, 'type'=>'grade'); - $studentshtml .= grade_get_icons($element, $gtree); - } - - - // 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 ($USER->gradeediting) { - // We need to retrieve each grade_grade object from DB in order to - // know if they are hidden/locked - - if ($item->scaleid) { - if ($scale = get_record('scale', 'id', $item->scaleid)) { - $scales = explode(",", $scale->scale); - // reindex because scale is off 1 - $i = 0; - foreach ($scales as $scaleoption) { - $i++; - $scaleopt[$i] = $scaleoption; - } - - if ($quickgrading) { - $studentshtml .= choose_from_menu($scaleopt, 'grade_'.$userid.'_'.$item->id, - $gradeval, get_string('nograde'), '', -1, true); - } elseif ($scale = get_record('scale', 'id', $item->scaleid)) { - $scales = explode(",", $scale->scale); - - // invalid grade if gradeval < 1 - if ((int) $gradeval < 1) { - $studentshtml .= '-'; - } else { - $studentshtml .= $scales[$gradeval-1]; - } - } else { - // no such scale, throw error? - } - } - } else { - if ($quickgrading) { - $studentshtml .= ''; - } else { - $studentshtml .= get_grade_clean($gradeval); - } - } - - - // If quickfeedback is on, print an input element - if ($quickfeedback) { - $studentshtml .= ''; - } - - $studentshtml .= '
' . grade_get_icons($element, $gtree, array('edit')) . '
'; - } else { - // If feedback present, surround grade with feedback tooltip - if (!empty($grade->feedback)) { - $studentshtml .= ''; - } - - // finalgrades[$userid][$itemid] could be null because of the outer join - // in this case it's different than a 0 - if ($item->scaleid) { - if ($scale = get_record('scale', 'id', $item->scaleid)) { - $scales = explode(",", $scale->scale); - - // invalid grade if gradeval < 1 - if ((int) $gradeval < 1) { - $studentshtml .= '-'; - } else { - $studentshtml .= $scales[$gradeval-1]; - } - } else { - // no such scale, throw error? - } - } else { - if (is_null($gradeval)) { - $studentshtml .= '-'; - } else { - $studentshtml .= get_grade_clean($gradeval); - } - } - if (!empty($grade->feedback)) { - $studentshtml .= ''; - } - } - - if (!empty($gradeserror[$item->id][$userid])) { - $studentshtml .= $gradeserror[$item->id][$userid]; - } - - $studentshtml .= '' . "\n"; - } - $studentshtml .= ''; -} - -// if user preference to display group sum -$groupsumhtml = ''; - -if ($currentgroup && $showgroups) { - -/** SQL for finding group sum */ - $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum - FROM {$CFG->prefix}grade_items gi LEFT JOIN - {$CFG->prefix}grade_grades g ON gi.id = g.itemid RIGHT OUTER JOIN - {$CFG->prefix}user u ON u.id = g.userid LEFT JOIN - {$CFG->prefix}role_assignments ra ON u.id = ra.userid - $groupsql - WHERE gi.courseid = $courseid - $groupwheresql - AND ra.roleid in ($gradebookroles) - AND ra.contextid ".get_related_contexts_string($context)." - GROUP BY g.itemid"; - - $groupsum = array(); - $sums = get_records_sql($SQL); - foreach ($sums as $itemid => $csum) { - $groupsum[$itemid] = $csum; - } - - $groupsumhtml = 'Group total'; - foreach ($items as $item) { - if (!isset($groupsum[$item->id])) { - $groupsumhtml .= '-'; - } else { - $sum = $groupsum[$item->id]; - $groupsumhtml .= ''.get_grade_clean($sum->sum).''; - } - } - $groupsumhtml .= ''; -} - -// Grand totals -$gradesumhtml = ''; -if ($showgrandtotals) { - -/** SQL for finding the SUM grades of all visible users ($CFG->gradebookroles) */ - - $SQL = "SELECT g.itemid, SUM(g.finalgrade) as sum - FROM {$CFG->prefix}grade_items gi LEFT JOIN - {$CFG->prefix}grade_grades g ON gi.id = g.itemid RIGHT OUTER JOIN - {$CFG->prefix}user u ON u.id = g.userid LEFT JOIN - {$CFG->prefix}role_assignments ra ON u.id = ra.userid - WHERE gi.courseid = $courseid - AND ra.roleid in ($gradebookroles) - AND ra.contextid ".get_related_contexts_string($context)." - GROUP BY g.itemid"; - - $classsum = array(); - $sums = get_records_sql($SQL); - foreach ($sums as $itemid => $csum) { - $classsum[$itemid] = $csum; - } - - $gradesumhtml = 'Total'; - foreach ($items as $item) { - if (!isset($classsum[$item->id])) { - $gradesumhtml .= '-'; - } else { - $sum = $classsum[$item->id]; - $gradesumhtml .= ''.get_grade_clean($sum->sum).''; - } - } - $gradesumhtml .= ''; -} - -// finding the ranges of each gradeitem -$scalehtml = ''; -if ($showscales) { - $scalehtml = ''.get_string('range','grades').''; - foreach ($items as $item) { - $scalehtml .= ''. get_grade_clean($item->grademin).'-'. get_grade_clean($item->grademax).''; - } - $scalehtml .= ''; -} - -echo "
"; - -$reporthtml = "$headerhtml"; -$reporthtml .= $scalehtml; -$reporthtml .= $studentshtml; -$reporthtml .= $groupsumhtml; -$reporthtml .= $gradesumhtml; +echo $report->group_selector; +echo $report->get_toggles_html(); +print_paging_bar($numusers, $report->page, $report->studentsperpage, $report->pbarurl); +echo '
'; + +$reporthtml = '
'; +$reporthtml .= $report->get_headerhtml(); +$reporthtml .= $report->get_scalehtml(); +$reporthtml .= $report->get_studentshtml(); +$reporthtml .= $report->get_groupsumhtml(); +$reporthtml .= $report->get_gradesumhtml(); $reporthtml .= "
"; - // print submit button if ($USER->gradeediting) { echo '
'; @@ -773,13 +80,13 @@ if ($USER->gradeediting) { echo $reporthtml; // print submit button -if ($USER->gradeediting && ($quickfeedback || $quickgrading)) { +if ($USER->gradeediting && ($report->quickfeedback || $report->quickgrading)) { echo '
'; echo '
'; } // prints paging bar at bottom for large pages -if ($perpage >= 20) { - print_paging_bar($numusers, $page, $perpage, $pbarurl); +if ($report->studentsperpage >= 20) { + print_paging_bar($numusers, $report->page, $report->studentsperpage, $report->pbarurl); } ?> diff --git a/lib/gradelib.php b/lib/gradelib.php index 7a2b18a0be..da703aa053 100644 --- a/lib/gradelib.php +++ b/lib/gradelib.php @@ -64,16 +64,6 @@ define('GRADE_HISTORY_INSERT', 1); define('GRADE_HISTORY_UPDATE', 2); define('GRADE_HISTORY_DELETE', 3); -// Set up constants for report preferences -define('GRADER_REPORT_AGGREGATION_POSITION_LEFT', 0); -define('GRADER_REPORT_AGGREGATION_POSITION_RIGHT', 1); -define('GRADER_REPORT_AGGREGATION_VIEW_FULL', 0); -define('GRADER_REPORT_AGGREGATION_VIEW_COMPACT', 1); -define('GRADER_REPORT_GRADE_DISPLAY_TYPE_RAW', 0); -define('GRADER_REPORT_GRADE_DISPLAY_TYPE_PERCENTAGE', 1); -define('GRADER_REPORT_FEEDBACK_FORMAT_TEXT', 0); -define('GRADER_REPORT_FEEDBACK_FORMAT_HTML', 1); - require_once($CFG->libdir . '/grade/grade_category.php'); require_once($CFG->libdir . '/grade/grade_item.php'); @@ -704,170 +694,4 @@ function grade_oldgradebook_upgrade($courseid) { } } -/** - * Given a grade_category, grade_item or grade_grade, this function - * figures out the state of the object and builds then returns a div - * with the icons needed for the grader report. - * - * @param object $object - * @param object $tree (A complete grade_tree object) - * @param array $icons An array of icon names that this function is explicitly requested to print, regardless of settings - * @param bool $limit If true, use the $icons array as the only icons that will be printed. If false, use it to exclude these icons. - * @return string HTML - */ -function grade_get_icons($element, $tree, $icons=null, $limit=true) { - global $CFG; - global $USER; - - - // Load language strings - $stredit = get_string("edit"); - $streditcalculation= get_string("editcalculation", 'grades'); - $strfeedback = get_string("feedback"); - $strmove = get_string("move"); - $strmoveup = get_string("moveup"); - $strmovedown = get_string("movedown"); - $strmovehere = get_string("movehere"); - $strcancel = get_string("cancel"); - $stredit = get_string("edit"); - $strdelete = get_string("delete"); - $strhide = get_string("hide"); - $strshow = get_string("show"); - $strlock = get_string("lock", 'grades'); - $strswitch_minus = get_string("contract", 'grades'); - $strswitch_plus = get_string("expand", 'grades'); - $strunlock = get_string("unlock", 'grades'); - - // Prepare container div - $html = '
'; - - // Prepare reference variables - $eid = $element['eid']; - $object = $element['object']; - $type = $element['type']; - - // Add mock attributes in case the object is not of the right type - if ($type != 'grade') { - $object->feedback = ''; - } - - // Load user preferences - $aggregationview = get_user_preferences('grade_report_aggregationview', $CFG->grade_report_aggregationview); - $showeyecons = get_user_preferences('grade_report_showeyecons', $CFG->grade_report_showeyecons); - $showlocks = get_user_preferences('grade_report_showlocks', $CFG->grade_report_showlocks); - $showcalculations = get_user_preferences('grade_report_showcalculations', $CFG->grade_report_showcalculations); - - // Prepare image strings - $edit_category_icon = '' - . ''
-                        . $stredit.''. "\n"; - - $edit_item_icon = '' - . ''
-                    . $stredit.''. "\n"; - $overlib = ''; - if (!empty($object->feedback)) { - $overlib = 'onmouseover="return overlib(\''.$object->feedback.'\', CAPTION, \'' - . $strfeedback.'\');" onmouseout="return nd();"'; - } - - $edit_grade_icon = '' - . '' . $stredit.''. "\n"; - - - $edit_calculation_icon = '' - . ''
-                           . $streditcalculation.''. "\n"; - - // Prepare Hide/Show icon state - $hide_show = 'hide'; - if ($object->is_hidden()) { - $hide_show = 'show'; - } - - $show_hide_icon = 'commonvars\">\n" - . ''
-                    . ${'str' . $hide_show}.''. "\n"; - - // Prepare lock/unlock string - $lock_unlock = 'lock'; - if ($object->is_locked()) { - $lock_unlock = 'unlock'; - } - - // Print lock/unlock icon - - $lock_unlock_icon = 'commonvars\">\n" - . ''
-                      . ${'str' . $lock_unlock}.''. "\n"; - - // Prepare expand/contract string - $expand_contract = 'switch_minus'; // Default: expanded - $state = get_user_preferences('grade_category_' . $object->id, GRADE_CATEGORY_EXPANDED); - if ($state == GRADE_CATEGORY_CONTRACTED) { - $expand_contract = 'switch_plus'; - } - - $contract_expand_icon = 'commonvars\">\n" - . ''
-                          . ${'str' . $expand_contract}.''. "\n"; - - // If an array of icon names is given, return only these in the order they are given - if (!empty($icons) && is_array($icons)) { - $new_html = ''; - - foreach ($icons as $icon_name) { - if ($icon_name == 'edit') { - $icon_name .= "_$type"; - } - if ($limit) { - $new_html .= ${$icon_name . '_icon'}; - } else { - ${'show_' . $icon_name} = false; - } - } - if ($limit) { - return $new_html; - } else { - $html .= $new_html; - } - } - - // Icons shown when edit mode is on - if ($USER->gradeediting) { - // Edit icon (except for grade_grades) - if ($type == 'category') { - $html .= $edit_category_icon; - - } else if ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') { - $html .= $edit_item_icon; - } - - // Calculation icon for items and categories - if ($showcalculations && $type != 'grade') { - $html .= $edit_calculation_icon; - } - - if ($showeyecons) { - $html .= $show_hide_icon; - } - - if ($showlocks) { - $html .= $lock_unlock_icon; - } - - // If object is a category, display expand/contract icon - if (get_class($object) == 'grade_category' && $aggregationview == GRADER_REPORT_AGGREGATION_VIEW_COMPACT) { - $html .= $contract_expand_icon; - } - } else { // Editing mode is off - } - - return $html . '
'; -} - ?> diff --git a/lib/weblib.php b/lib/weblib.php index a21a35ee86..b4f1af7c13 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -3748,7 +3748,7 @@ has_capability('moodle/course:viewhiddenuserfields', $context)) { if (has_capability('moodle/notes:manage', $context) || has_capability('moodle/notes:view', $context)) { $output .= ''.get_string('notes','notes').'
'; } - + if (has_capability('moodle/site:viewreports', $context)) { $timemidnight = usergetmidnight(time()); $output .= ''. $string->activity .'
'; @@ -6291,7 +6291,7 @@ function print_location_comment($file, $line, $return = false) } -/** +/** * Returns an image of an up or down arrow, used for column sorting. To avoid unnecessary DB accesses, please * provide this function with the language strings for sortasc and sortdesc. * If no sort string is associated with the direction, an arrow with no alt text will be printed/returned. @@ -6299,17 +6299,17 @@ function print_location_comment($file, $line, $return = false) * @param string $strsort The language string used for the alt attribute of this image * @param bool $return Whether to print directly or return the html string * @return string HTML for the image - * + * * TODO See if this isn't already defined somewhere. If not, move this to weblib */ function print_arrow($direction='up', $strsort=null, $return=false) { global $CFG; - + if (!in_array($direction, array('up', 'down', 'right', 'left'))) { return null; } - - $return = null; + + $return = null; switch ($direction) { case 'up': -- 2.39.5