See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Condition on grades of current user. 19 * 20 * @package availability_grade 21 * @copyright 2014 The Open University 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace availability_grade; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Condition on grades of current user. 31 * 32 * @package availability_grade 33 * @copyright 2014 The Open University 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class condition extends \core_availability\condition { 37 /** @var int Grade item id */ 38 private $gradeitemid; 39 40 /** @var float|null Min grade (must be >= this) or null if none */ 41 private $min; 42 43 /** @var float|null Max grade (must be < this) or null if none */ 44 private $max; 45 46 /** 47 * Constructor. 48 * 49 * @param \stdClass $structure Data structure from JSON decode 50 * @throws \coding_exception If invalid data structure. 51 */ 52 public function __construct($structure) { 53 // Get grade item id. 54 if (isset($structure->id) && is_int($structure->id)) { 55 $this->gradeitemid = $structure->id; 56 } else { 57 throw new \coding_exception('Missing or invalid ->id for grade condition'); 58 } 59 60 // Get min and max. 61 if (!property_exists($structure, 'min')) { 62 $this->min = null; 63 } else if (is_float($structure->min) || is_int($structure->min)) { 64 $this->min = $structure->min; 65 } else { 66 throw new \coding_exception('Missing or invalid ->min for grade condition'); 67 } 68 if (!property_exists($structure, 'max')) { 69 $this->max = null; 70 } else if (is_float($structure->max) || is_int($structure->max)) { 71 $this->max = $structure->max; 72 } else { 73 throw new \coding_exception('Missing or invalid ->max for grade condition'); 74 } 75 } 76 77 public function save() { 78 $result = (object)array('type' => 'grade', 'id' => $this->gradeitemid); 79 if (!is_null($this->min)) { 80 $result->min = $this->min; 81 } 82 if (!is_null($this->max)) { 83 $result->max = $this->max; 84 } 85 return $result; 86 } 87 88 /** 89 * Returns a JSON object which corresponds to a condition of this type. 90 * 91 * Intended for unit testing, as normally the JSON values are constructed 92 * by JavaScript code. 93 * 94 * @param int $gradeitemid Grade item id 95 * @param number|null $min Min grade (or null if no min) 96 * @param number|null $max Max grade (or null if no max) 97 * @return stdClass Object representing condition 98 */ 99 public static function get_json($gradeitemid, $min = null, $max = null) { 100 $result = (object)array('type' => 'grade', 'id' => (int)$gradeitemid); 101 if (!is_null($min)) { 102 $result->min = $min; 103 } 104 if (!is_null($max)) { 105 $result->max = $max; 106 } 107 return $result; 108 } 109 110 public function is_available($not, \core_availability\info $info, $grabthelot, $userid) { 111 $course = $info->get_course(); 112 $score = $this->get_cached_grade_score($this->gradeitemid, $course->id, $grabthelot, $userid); 113 $allow = $score !== false && 114 (is_null($this->min) || $score >= $this->min) && 115 (is_null($this->max) || $score < $this->max); 116 if ($not) { 117 $allow = !$allow; 118 } 119 120 return $allow; 121 } 122 123 public function get_description($full, $not, \core_availability\info $info) { 124 $course = $info->get_course(); 125 // String depends on type of requirement. We are coy about 126 // the actual numbers, in case grades aren't released to 127 // students. 128 if (is_null($this->min) && is_null($this->max)) { 129 $string = 'any'; 130 } else if (is_null($this->max)) { 131 $string = 'min'; 132 } else if (is_null($this->min)) { 133 $string = 'max'; 134 } else { 135 $string = 'range'; 136 } 137 if ($not) { 138 // The specific strings don't make as much sense with 'not'. 139 if ($string === 'any') { 140 $string = 'notany'; 141 } else { 142 $string = 'notgeneral'; 143 } 144 } 145 $name = self::get_cached_grade_name($course->id, $this->gradeitemid); 146 return get_string('requires_' . $string, 'availability_grade', $name); 147 } 148 149 protected function get_debug_string() { 150 $out = '#' . $this->gradeitemid; 151 if (!is_null($this->min)) { 152 $out .= ' >= ' . sprintf('%.5f', $this->min); 153 } 154 if (!is_null($this->max)) { 155 if (!is_null($this->min)) { 156 $out .= ','; 157 } 158 $out .= ' < ' . sprintf('%.5f', $this->max); 159 } 160 return $out; 161 } 162 163 /** 164 * Obtains the name of a grade item, also checking that it exists. Uses a 165 * cache. The name returned is suitable for display. 166 * 167 * @param int $courseid Course id 168 * @param int $gradeitemid Grade item id 169 * @return string Grade name or empty string if no grade with that id 170 */ 171 private static function get_cached_grade_name($courseid, $gradeitemid) { 172 global $DB, $CFG; 173 require_once($CFG->libdir . '/gradelib.php'); 174 175 // Get all grade item names from cache, or using db query. 176 $cache = \cache::make('availability_grade', 'items'); 177 if (($cacheditems = $cache->get($courseid)) === false) { 178 // We cache the whole items table not the name; the format_string 179 // call for the name might depend on current user (e.g. multilang) 180 // and this is a shared cache. 181 $cacheditems = $DB->get_records('grade_items', array('courseid' => $courseid)); 182 $cache->set($courseid, $cacheditems); 183 } 184 185 // Return name from cached item or a lang string. 186 if (!array_key_exists($gradeitemid, $cacheditems)) { 187 return get_string('missing', 'availability_grade'); 188 } 189 $gradeitemobj = $cacheditems[$gradeitemid]; 190 $item = new \grade_item; 191 \grade_object::set_properties($item, $gradeitemobj); 192 return $item->get_name(); 193 } 194 195 /** 196 * Obtains a grade score. Note that this score should not be displayed to 197 * the user, because gradebook rules might prohibit that. It may be a 198 * non-final score subject to adjustment later. 199 * 200 * @param int $gradeitemid Grade item ID we're interested in 201 * @param int $courseid Course id 202 * @param bool $grabthelot If true, grabs all scores for current user on 203 * this course, so that later ones come from cache 204 * @param int $userid Set if requesting grade for a different user (does 205 * not use cache) 206 * @return float Grade score as a percentage in range 0-100 (e.g. 100.0 207 * or 37.21), or false if user does not have a grade yet 208 */ 209 protected static function get_cached_grade_score($gradeitemid, $courseid, 210 $grabthelot=false, $userid=0) { 211 global $USER, $DB; 212 if (!$userid) { 213 $userid = $USER->id; 214 } 215 $cache = \cache::make('availability_grade', 'scores'); 216 if (($cachedgrades = $cache->get($userid)) === false) { 217 $cachedgrades = array(); 218 } 219 if (!array_key_exists($gradeitemid, $cachedgrades)) { 220 if ($grabthelot) { 221 // Get all grades for the current course. 222 $rs = $DB->get_recordset_sql(' 223 SELECT 224 gi.id,gg.finalgrade,gg.rawgrademin,gg.rawgrademax 225 FROM 226 {grade_items} gi 227 LEFT JOIN {grade_grades} gg ON gi.id=gg.itemid AND gg.userid=? 228 WHERE 229 gi.courseid = ?', array($userid, $courseid)); 230 foreach ($rs as $record) { 231 // This function produces division by zero error warnings when rawgrademax and rawgrademin 232 // are equal. Below change does not affect function behavior, just avoids the warning. 233 if (is_null($record->finalgrade) || $record->rawgrademax == $record->rawgrademin) { 234 // No grade = false. 235 $cachedgrades[$record->id] = false; 236 } else { 237 // Otherwise convert grade to percentage. 238 $cachedgrades[$record->id] = 239 (($record->finalgrade - $record->rawgrademin) * 100) / 240 ($record->rawgrademax - $record->rawgrademin); 241 } 242 } 243 $rs->close(); 244 // And if it's still not set, well it doesn't exist (eg 245 // maybe the user set it as a condition, then deleted the 246 // grade item) so we call it false. 247 if (!array_key_exists($gradeitemid, $cachedgrades)) { 248 $cachedgrades[$gradeitemid] = false; 249 } 250 } else { 251 // Just get current grade. 252 $record = $DB->get_record('grade_grades', array( 253 'userid' => $userid, 'itemid' => $gradeitemid)); 254 // This function produces division by zero error warnings when rawgrademax and rawgrademin 255 // are equal. Below change does not affect function behavior, just avoids the warning. 256 if ($record && !is_null($record->finalgrade) && $record->rawgrademax != $record->rawgrademin) { 257 $score = (($record->finalgrade - $record->rawgrademin) * 100) / 258 ($record->rawgrademax - $record->rawgrademin); 259 } else { 260 // Treat the case where row exists but is null, same as 261 // case where row doesn't exist. 262 $score = false; 263 } 264 $cachedgrades[$gradeitemid] = $score; 265 } 266 $cache->set($userid, $cachedgrades); 267 } 268 return $cachedgrades[$gradeitemid]; 269 } 270 271 public function update_after_restore($restoreid, $courseid, \base_logger $logger, $name) { 272 global $DB; 273 $rec = \restore_dbops::get_backup_ids_record($restoreid, 'grade_item', $this->gradeitemid); 274 if (!$rec || !$rec->newitemid) { 275 // If we are on the same course (e.g. duplicate) then we can just 276 // use the existing one. 277 if ($DB->record_exists('grade_items', 278 array('id' => $this->gradeitemid, 'courseid' => $courseid))) { 279 return false; 280 } 281 // Otherwise it's a warning. 282 $this->gradeitemid = 0; 283 $logger->process('Restored item (' . $name . 284 ') has availability condition on grade that was not restored', 285 \backup::LOG_WARNING); 286 } else { 287 $this->gradeitemid = (int)$rec->newitemid; 288 } 289 return true; 290 } 291 292 public function update_dependency_id($table, $oldid, $newid) { 293 if ($table === 'grade_items' && (int)$this->gradeitemid === (int)$oldid) { 294 $this->gradeitemid = $newid; 295 return true; 296 } else { 297 return false; 298 } 299 } 300 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body