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