See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 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 * The user screen. 19 * 20 * @package gradereport_singleview 21 * @copyright 2014 Moodle Pty Ltd (http://moodle.com) 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace gradereport_singleview\local\screen; 26 27 use grade_seq; 28 use gradereport_singleview; 29 use moodle_url; 30 use pix_icon; 31 use html_writer; 32 use gradereport_singleview\local\ui\range; 33 use gradereport_singleview\local\ui\bulk_insert; 34 use grade_item; 35 use grade_grade; 36 use stdClass; 37 38 defined('MOODLE_INTERNAL') || die; 39 40 /** 41 * The user screen. 42 * 43 * @package gradereport_singleview 44 * @copyright 2014 Moodle Pty Ltd (http://moodle.com) 45 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 46 */ 47 class user extends tablelike implements selectable_items { 48 49 /** @var array $categories A cache for grade_item categories */ 50 private $categories = []; 51 52 /** @var int $requirespaging Do we have more items than the paging limit? */ 53 private $requirespaging = true; 54 55 /** 56 * Get the label for the select box that chooses items for this page. 57 * @return string 58 */ 59 public function select_label(): string { 60 return get_string('selectgrade', 'gradereport_singleview'); 61 } 62 63 /** 64 * Get the description for the screen. 65 * 66 * @return string 67 */ 68 public function description(): string { 69 return get_string('gradeitems', 'grades'); 70 } 71 72 /** 73 * Convert the list of items to a list of options. 74 * 75 * @return array 76 */ 77 public function options(): array { 78 $result = []; 79 foreach ($this->items as $itemid => $item) { 80 $result[$itemid] = $item->get_name(); 81 } 82 return $result; 83 } 84 85 /** 86 * Get the type of items on this screen. 87 * 88 * @return string 89 */ 90 public function item_type(): string { 91 return 'grade'; 92 } 93 94 /** 95 * Init the screen 96 * 97 * @param bool $selfitemisempty Have we selected an item yet? 98 */ 99 public function init($selfitemisempty = false) { 100 101 if (!$selfitemisempty) { 102 $validusers = \grade_report::get_gradable_users($this->courseid, $this->groupid); 103 if (!isset($validusers[$this->itemid])) { 104 // If the passed user id is not valid, show the first user from the list instead. 105 $this->item = reset($validusers); 106 $this->itemid = $this->item->id; 107 } else { 108 $this->item = $validusers[$this->itemid]; 109 } 110 } 111 112 $seq = new grade_seq($this->courseid, true); 113 114 $this->items = []; 115 foreach ($seq->items as $itemid => $item) { 116 if (grade::filter($item)) { 117 $this->items[$itemid] = $item; 118 } 119 } 120 121 $this->requirespaging = count($this->items) > $this->perpage; 122 123 $this->setup_structure(); 124 125 $this->definition = [ 126 'finalgrade', 'feedback', 'override', 'exclude' 127 ]; 128 $this->set_headers($this->original_headers()); 129 } 130 131 /** 132 * Get the list of headers for the table. 133 * 134 * @return array List of headers 135 */ 136 public function original_headers(): array { 137 return [ 138 get_string('assessmentname', 'gradereport_singleview'), 139 '', // For filter icon. 140 get_string('gradecategory', 'grades'), 141 get_string('grade', 'grades'), 142 get_string('range', 'grades'), 143 get_string('feedback', 'grades'), 144 get_string('override', 'gradereport_singleview'), 145 get_string('exclude', 'gradereport_singleview'), 146 ]; 147 } 148 149 /** 150 * Format each row of the table. 151 * 152 * @param grade_item $item 153 * @return array 154 */ 155 public function format_line($item): array { 156 global $OUTPUT; 157 158 $grade = $this->fetch_grade_or_default($item, $this->item->id); 159 $lockicon = ''; 160 161 $lockeditem = $lockeditemgrade = 0; 162 if (!empty($grade->locked)) { 163 $lockeditem = 1; 164 } 165 if (!empty($grade->grade_item->locked)) { 166 $lockeditemgrade = 1; 167 } 168 // Check both grade and grade item. 169 if ($lockeditem || $lockeditemgrade) { 170 $lockicon = $OUTPUT->pix_icon('t/locked', 'grade is locked', 'moodle', ['class' => 'ml-3']); 171 } 172 173 // Create a fake gradetreeitem so we can call get_element_header(). 174 // The type logic below is from grade_category->_get_children_recursion(). 175 $gradetreeitem = []; 176 177 $type = in_array($item->itemtype, ['course', 'category']) ? "{$item->itemtype}item" : 'item'; 178 $gradetreeitem['type'] = $type; 179 $gradetreeitem['object'] = $item; 180 $gradetreeitem['userid'] = $this->item->id; 181 182 $itemname = $this->structure->get_element_header($gradetreeitem, true, false, false, false, true); 183 $grade->label = $item->get_name(); 184 185 $formatteddefinition = $this->format_definition($grade); 186 187 $itemicon = html_writer::div($this->format_icon($item), 'mr-1'); 188 $itemtype = \html_writer::span($this->structure->get_element_type_string($gradetreeitem), 189 'd-block text-uppercase small dimmed_text'); 190 // If a behat test site is running avoid outputting the information about the type of the grade item. 191 // This additional information currently causes issues in behat particularly with the existing xpath used to 192 // interact with table elements. 193 if (!defined('BEHAT_SITE_RUNNING')) { 194 $itemcontent = html_writer::div($itemtype . $itemname); 195 } else { 196 $itemcontent = html_writer::div($itemname); 197 } 198 199 $line = [ 200 html_writer::div($itemicon . $itemcontent . $lockicon, "{$type} d-flex align-items-center"), 201 $this->get_item_action_menu($item), 202 $this->category($item), 203 $formatteddefinition['finalgrade'], 204 new range($item), 205 $formatteddefinition['feedback'], 206 $formatteddefinition['override'], 207 $formatteddefinition['exclude'], 208 ]; 209 $lineclasses = [ 210 'gradeitem', 211 'action', 212 'category', 213 'grade', 214 'range', 215 ]; 216 217 $outputline = []; 218 $i = 0; 219 foreach ($line as $key => $value) { 220 $cell = new \html_table_cell($value); 221 if ($isheader = $i == 0) { 222 $cell->header = $isheader; 223 $cell->scope = "row"; 224 } 225 if (array_key_exists($key, $lineclasses)) { 226 $cell->attributes['class'] = $lineclasses[$key]; 227 } 228 $outputline[] = $cell; 229 $i++; 230 } 231 232 return $outputline; 233 } 234 235 /** 236 * Helper to get the icon for an item. 237 * 238 * @param grade_item $item 239 * @return string 240 */ 241 private function format_icon($item): string { 242 $element = ['type' => 'item', 'object' => $item]; 243 return $this->structure->get_element_icon($element); 244 } 245 246 /** 247 * Return the action menu HTML for the grade item. 248 * 249 * @param grade_item $item 250 * @return mixed 251 */ 252 private function get_item_action_menu(grade_item $item) { 253 global $OUTPUT; 254 255 $menuitems = []; 256 $url = new moodle_url($this->format_link('grade', $item->id)); 257 $title = get_string('showallgrades', 'core_grades'); 258 $menuitems[] = new \action_menu_link_secondary($url, null, $title); 259 $menu = new \action_menu($menuitems); 260 $icon = $OUTPUT->pix_icon('i/moremenu', get_string('actions')); 261 $extraclasses = 'btn btn-link btn-icon icon-size-3 d-flex align-items-center justify-content-center'; 262 $menu->set_menu_trigger($icon, $extraclasses); 263 $menu->set_menu_left(); 264 $menu->set_boundary('window'); 265 266 return $OUTPUT->render($menu); 267 } 268 269 /** 270 * Helper to get the category for an item. 271 * 272 * @param grade_item $item 273 * @return string 274 */ 275 private function category(grade_item $item): string { 276 global $DB; 277 278 if (empty($item->categoryid)) { 279 280 if ($item->itemtype == 'course') { 281 return $this->course->fullname; 282 } 283 284 $params = ['id' => $item->iteminstance]; 285 $elem = $DB->get_record('grade_categories', $params); 286 287 return $elem->fullname; 288 } 289 290 if (!isset($this->categories[$item->categoryid])) { 291 $category = $item->get_parent_category(); 292 293 $this->categories[$category->id] = $category; 294 } 295 296 return $this->categories[$item->categoryid]->get_name(); 297 } 298 299 /** 300 * Get the heading for the page. 301 * 302 * @return string 303 */ 304 public function heading(): string { 305 global $PAGE; 306 $headinglangstring = $PAGE->user_is_editing() ? 'gradeuseredit' : 'gradeuser'; 307 return get_string($headinglangstring, 'gradereport_singleview', fullname($this->item)); 308 } 309 310 /** 311 * Get the summary for this table. 312 * 313 * @return string 314 */ 315 public function summary(): string { 316 return get_string('summaryuser', 'gradereport_singleview'); 317 } 318 319 /** 320 * Default pager 321 * 322 * @return string 323 */ 324 public function pager(): string { 325 global $OUTPUT; 326 327 if (!$this->supports_paging()) { 328 return ''; 329 } 330 331 return $OUTPUT->paging_bar( 332 count($this->items), $this->page, $this->perpage, 333 new moodle_url('/grade/report/singleview/index.php', [ 334 'perpage' => $this->perpage, 335 'id' => $this->courseid, 336 'group' => $this->groupid, 337 'itemid' => $this->itemid, 338 'item' => 'user' 339 ]) 340 ); 341 } 342 343 /** 344 * Does this page require paging? 345 * 346 * @return bool 347 */ 348 public function supports_paging(): bool { 349 return $this->requirespaging; 350 } 351 352 353 /** 354 * Process the data from the form. 355 * 356 * @param array $data 357 * @return stdClass of warnings 358 */ 359 public function process($data): stdClass { 360 $bulk = new bulk_insert($this->item); 361 // Bulk insert messages the data to be passed in 362 // ie: for all grades of empty grades apply the specified value. 363 if ($bulk->is_applied($data)) { 364 $filter = $bulk->get_type($data); 365 $insertvalue = $bulk->get_insert_value($data); 366 367 $userid = $this->item->id; 368 foreach ($this->items as $gradeitemid => $gradeitem) { 369 $null = $gradeitem->gradetype == GRADE_TYPE_SCALE ? -1 : ''; 370 $field = "finalgrade_{$gradeitem->id}_{$this->itemid}"; 371 if (isset($data->$field)) { 372 continue; 373 } 374 375 $oldfinalgradefield = "oldfinalgrade_{$gradeitem->id}_{$this->itemid}"; 376 // Bulk grade changes for all grades need to be processed and shouldn't be skipped if they had a previous grade. 377 if ($gradeitem->is_course_item() || ($filter != 'all' && !empty($data->$oldfinalgradefield))) { 378 if ($gradeitem->is_course_item()) { 379 // The course total should not be overridden. 380 unset($data->$field); 381 unset($data->oldfinalgradefield); 382 $oldoverride = "oldoverride_{$gradeitem->id}_{$this->itemid}"; 383 unset($data->$oldoverride); 384 $oldfeedback = "oldfeedback_{$gradeitem->id}_{$this->itemid}"; 385 unset($data->$oldfeedback); 386 } 387 continue; 388 } 389 $grade = grade_grade::fetch([ 390 'itemid' => $gradeitemid, 391 'userid' => $userid 392 ]); 393 394 $data->$field = empty($grade) ? $null : $grade->finalgrade; 395 $data->{"old$field"} = $data->$field; 396 } 397 398 foreach ($data as $varname => $value) { 399 if (preg_match('/^oldoverride_(\d+)_(\d+)/', $varname, $matches)) { 400 // If we've selected overriding all grades. 401 if ($filter == 'all') { 402 $override = "override_{$matches[1]}_{$matches[2]}"; 403 $data->$override = '1'; 404 } 405 } 406 if (!preg_match('/^finalgrade_(\d+)_(\d+)/', $varname, $matches)) { 407 continue; 408 } 409 410 $gradeitem = grade_item::fetch([ 411 'courseid' => $this->courseid, 412 'id' => $matches[1], 413 ]); 414 415 $isscale = ($gradeitem->gradetype == GRADE_TYPE_SCALE); 416 417 $empties = (trim($value ?? '') === '' || ($isscale && $value == -1)); 418 419 if ($filter == 'all' || $empties) { 420 $data->$varname = ($isscale && empty($insertvalue)) ? -1 : $insertvalue; 421 } 422 } 423 } 424 return parent::process($data); 425 } 426 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body