. /** * File containing the grade_report class. * @package gradebook */ require_once($CFG->libdir.'/gradelib.php'); /** * An abstract class containing variables and methods used by all or most reports. * @abstract * @package gradebook */ abstract class grade_report { /** * The courseid. * @var int $courseid */ public $courseid; /** * The course. * @var object $course */ public $course; /** Grade plugin return tracking object. * @var object $gpr */ public $gpr; /** * The context. * @var int $context */ public $context; /** * The grade_tree object. * @var object $gtree */ public $gtree; /** * User preferences related to this report. * @var array $prefs */ public $prefs = array(); /** * The roles for this report. * @var string $gradebookroles */ public $gradebookroles; /** * base url for sorting by first/last name. * @var string $baseurl */ public $baseurl; /** * base url for paging. * @var string $pbarurl */ public $pbarurl; /** * Current page (for paging). * @var int $page */ public $page; /** * Array of cached language strings (using get_string() all the time takes a long time!). * @var array $lang_strings */ public $lang_strings = array(); //// GROUP VARIABLES (including SQL) /** * The current group being displayed. * @var int $currentgroup */ public $currentgroup; /** * Current course group mode * @var int $groupmode */ var $groupmode; /** * A HTML select element used to select the current group. * @var string $group_selector */ public $group_selector; /** * An SQL fragment used to add linking information to the group tables. * @var string $groupsql */ protected $groupsql; /** * An SQL constraint to append to the queries used by this object to build the report. * @var string $groupwheresql */ protected $groupwheresql; /** * The ordered params for $groupwheresql * @var array $groupwheresql_params */ protected $groupwheresql_params = array(); /** * Constructor. Sets local copies of user preferences and initialises grade_tree. * @param int $courseid * @param object $gpr grade plugin return tracking object * @param string $context * @param int $page The current page being viewed (when report is paged) */ public function __construct($courseid, $gpr, $context, $page=null) { global $CFG, $COURSE, $DB; if (empty($CFG->gradebookroles)) { print_error('norolesdefined', 'grades'); } $this->courseid = $courseid; if ($this->courseid == $COURSE->id) { $this->course = $COURSE; } else { $this->course = $DB->get_record('course', array('id' => $this->courseid)); } $this->gpr = $gpr; $this->context = $context; $this->page = $page; // roles to be displayed in the gradebook $this->gradebookroles = $CFG->gradebookroles; // Set up link to preferences page $this->preferences_page = $CFG->wwwroot.'/grade/report/grader/preferences.php?id='.$courseid; // init gtree in child class } /** * Given the name of a user preference (without grade_report_ prefix), locally saves then returns * the value of that preference. If the preference has already been fetched before, * the saved value is returned. If the preference is not set at the User level, the $CFG equivalent * is given (site default). * @static (Can be called statically, but then doesn't benefit from caching) * @param string $pref The name of the preference (do not include the grade_report_ prefix) * @param int $objectid An optional itemid or categoryid to check for a more fine-grained preference * @return mixed The value of the preference */ public function get_pref($pref, $objectid=null) { global $CFG; $fullprefname = 'grade_report_' . $pref; $shortprefname = 'grade_' . $pref; $retval = null; if (!isset($this) OR get_class($this) != 'grade_report') { if (!empty($objectid)) { $retval = get_user_preferences($fullprefname . $objectid, grade_report::get_pref($pref)); } elseif (isset($CFG->$fullprefname)) { $retval = get_user_preferences($fullprefname, $CFG->$fullprefname); } elseif (isset($CFG->$shortprefname)) { $retval = get_user_preferences($fullprefname, $CFG->$shortprefname); } else { $retval = null; } } else { if (empty($this->prefs[$pref.$objectid])) { if (!empty($objectid)) { $retval = get_user_preferences($fullprefname . $objectid); if (empty($retval)) { // No item pref found, we are returning the global preference $retval = $this->get_pref($pref); $objectid = null; } } else { $retval = get_user_preferences($fullprefname, $CFG->$fullprefname); } $this->prefs[$pref.$objectid] = $retval; } else { $retval = $this->prefs[$pref.$objectid]; } } return $retval; } /** * Uses set_user_preferences() to update the value of a user preference. If 'default' is given as the value, * the preference will be removed in favour of a higher-level preference. * @static * @param string $pref_name The name of the preference. * @param mixed $pref_value The value of the preference. * @param int $itemid An optional itemid to which the preference will be assigned * @return bool Success or failure. */ public function set_pref($pref, $pref_value='default', $itemid=null) { $fullprefname = 'grade_report_' . $pref; if ($pref_value == 'default') { return unset_user_preference($fullprefname.$itemid); } else { return set_user_preference($fullprefname.$itemid, $pref_value); } } /** * Handles form data sent by this report for this report. Abstract method to implement in all children. * @abstract * @param array $data * @return mixed True or array of errors */ abstract function process_data($data); /** * 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 */ abstract function process_action($target, $action); /** * First checks the cached language strings, then returns match if found, or uses get_string() * to get it from the DB, caches it then returns it. * @param string $strcode * @param string $section Optional language section * @return string */ public function get_lang_string($strcode, $section=null) { if (empty($this->lang_strings[$strcode])) { $this->lang_strings[$strcode] = get_string($strcode, $section); } return $this->lang_strings[$strcode]; } /** * Fetches and returns a count of all the users that will be shown on this page. * @param boolean $groups include groups limit * @return int Count of users */ public function get_numusers($groups=true) { global $CFG, $DB; $groupsql = ""; $groupwheresql = ""; //limit to users with a gradeable role list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0'); //limit to users with an active enrollment list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context); $params = array_merge($gradebookrolesparams, $enrolledparams); if ($groups) { $groupsql = $this->groupsql; $groupwheresql = $this->groupwheresql; $params = array_merge($params, $this->groupwheresql_params); } $countsql = "SELECT COUNT(DISTINCT u.id) FROM {user} u JOIN ($enrolledsql) je ON je.id = u.id JOIN {role_assignments} ra ON u.id = ra.userid $groupsql WHERE ra.roleid $gradebookrolessql AND u.deleted = 0 $groupwheresql AND ra.contextid ".get_related_contexts_string($this->context); return $DB->count_records_sql($countsql, $params); } /** * Sets up this object's group variables, mainly to restrict the selection of users to display. */ protected function setup_groups() { /// find out current groups mode if ($this->groupmode = groups_get_course_groupmode($this->course)) { $this->currentgroup = groups_get_course_group($this->course, true); $this->group_selector = groups_print_course_menu($this->course, $this->pbarurl, true); if ($this->groupmode == SEPARATEGROUPS and !$this->currentgroup and !has_capability('moodle/site:accessallgroups', $this->context)) { $this->currentgroup = -2; // means can not access any groups at all } if ($this->currentgroup) { $this->groupsql = " JOIN {groups_members} gm ON gm.userid = u.id "; $this->groupwheresql = " AND gm.groupid = :gr_grpid "; $this->groupwheresql_params = array('gr_grpid'=>$this->currentgroup); } } } /** * Returns an arrow icon inside an tag, for the purpose of sorting a column. * @param string $direction * @param moodle_url $sort_link * @param string HTML */ protected function get_sort_arrow($direction='move', $sortlink=null) { global $OUTPUT; $matrix = array('up' => 'desc', 'down' => 'asc', 'move' => 'desc'); $strsort = $this->get_lang_string('sort' . $matrix[$direction]); $arrow = print_arrow($direction, $strsort, true); return html_writer::link($sortlink, $arrow, array('title'=>$strsort)); } /** * 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( $this->showtotalsifcontainhidden==GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN ) { return $finalgrade; } //if we've moved on to another user don't return the previous user's affected grades if ($previous_userid!=$this->user->id) { $hiding_affected = null; $previous_userid = $this->user->id; } if( !$hiding_affected ) { $items = grade_item::fetch_all(array('courseid'=>$courseid)); $grades = array(); $sql = "SELECT g.* FROM {grade_grades} g JOIN {grade_items} gi ON gi.id = g.itemid 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 ) { //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]; } } return $finalgrade; } }