MDL-65948 core_grade: Use new core/checkbox-toggleall
[moodle.git] / grade / edit / tree / lib.php
CommitLineData
e060e33d 1<?php
e060e33d 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/>.
dc482cfa 16
a153c9f2
AD
17/**
18 * A library of classes used by the grade edit pages
19 *
20 * @package core_grades
21 * @copyright 2009 Nicolas Connault
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
dc482cfa 25class grade_edit_tree {
26 public $columns = array();
27
28 /**
39873128 29 * @var grade_tree $gtree @see grade/lib.php
dc482cfa 30 */
31 public $gtree;
32
33 /**
34 * @var grade_plugin_return @see grade/lib.php
35 */
36 public $gpr;
37
38 /**
39 * @var string $moving The eid of the category or item being moved
40 */
41 public $moving;
42
43 public $deepest_level;
44
653a8648 45 public $uses_weight = false;
71297a3f 46
54a007e8 47 public $table;
71297a3f 48
54a007e8 49 public $categories = array();
0e999796
AD
50
51 /**
52 * Show calculator icons next to manual grade items
53 * @var bool $show_calculations
54 */
55 private $show_calculations;
56
dc482cfa 57 /**
58 * Constructor
59 */
60 public function __construct($gtree, $moving=false, $gpr) {
54a007e8 61 global $USER, $OUTPUT, $COURSE;
62
0e999796
AD
63 $systemdefault = get_config('moodle', 'grade_report_showcalculations');
64 $this->show_calculations = get_user_preferences('grade_report_showcalculations', $systemdefault);
65
dc482cfa 66 $this->gtree = $gtree;
67 $this->moving = $moving;
68 $this->gpr = $gpr;
69 $this->deepest_level = $this->get_deepest_level($this->gtree->top_element);
70
bfc8c1d4
MG
71 $this->columns = array(grade_edit_tree_column::factory('name', array('deepest_level' => $this->deepest_level)));
72
653a8648 73 if ($this->uses_weight) {
b6a1e366 74 $this->columns[] = grade_edit_tree_column::factory('weight', array('adv' => 'weight'));
653a8648 75 }
653a8648 76
77 $this->columns[] = grade_edit_tree_column::factory('range'); // This is not a setting... How do we deal with it?
653a8648 78 $this->columns[] = grade_edit_tree_column::factory('actions');
bfc8c1d4
MG
79
80 if ($this->deepest_level > 1) {
81 $this->columns[] = grade_edit_tree_column::factory('select');
82 }
54a007e8 83
54a007e8 84 $this->table = new html_table();
85 $this->table->id = "grade_edit_tree_table";
d75a2367
MG
86 $this->table->attributes['class'] = 'generaltable simple setup-grades';
87 if ($this->moving) {
88 $this->table->attributes['class'] .= ' moving';
89 }
54a007e8 90
91 foreach ($this->columns as $column) {
b79bf7a2 92 if (!($this->moving && $column->hide_when_moving)) {
54a007e8 93 $this->table->head[] = $column->get_header_cell();
94 }
95 }
96
97 $rowcount = 0;
98 $this->table->data = $this->build_html_tree($this->gtree->top_element, true, array(), 0, $rowcount);
dc482cfa 99 }
100
101 /**
102 * Recursive function for building the table holding the grade categories and items,
103 * with CSS indentation and styles.
104 *
1c1f64a2 105 * @param array $element The current tree element being rendered
106 * @param boolean $totals Whether or not to print category grade items (category totals)
107 * @param array $parents An array of parent categories for the current element (used for indentation and row classes)
dc482cfa 108 *
109 * @return string HTML
110 */
54a007e8 111 public function build_html_tree($element, $totals, $parents, $level, &$row_count) {
079b2e52 112 global $CFG, $COURSE, $PAGE, $OUTPUT;
71297a3f 113
dc482cfa 114 $object = $element['object'];
115 $eid = $element['eid'];
18761623 116 $object->name = $this->gtree->get_element_header($element, true, true, true, true, true);
dc482cfa 117 $object->stripped_name = $this->gtree->get_element_header($element, false, false, false);
dc482cfa 118 $is_category_item = false;
119 if ($element['type'] == 'categoryitem' || $element['type'] == 'courseitem') {
120 $is_category_item = true;
121 }
122
54a007e8 123 $rowclasses = array();
dc482cfa 124 foreach ($parents as $parent_eid) {
54a007e8 125 $rowclasses[] = $parent_eid;
dc482cfa 126 }
127
2e3ec2af 128 $moveaction = '';
079b2e52 129 $actionsmenu = new action_menu();
079b2e52
MG
130 $actionsmenu->set_menu_trigger(get_string('edit'));
131 $actionsmenu->set_owner_selector('grade-item-' . $eid);
132 $actionsmenu->set_alignment(action_menu::TL, action_menu::BL);
133
134 if (!$is_category_item && ($icon = $this->gtree->get_edit_icon($element, $this->gpr, true))) {
135 $actionsmenu->add($icon);
dc482cfa 136 }
5c3b5143
MM
137 // MDL-49281 if grade_item already has calculation, it should be editable even if global setting is off.
138 $type = $element['type'];
139 $iscalculated = ($type == 'item' or $type == 'courseitem' or $type == 'categoryitem') && $object->is_calculated();
140 $icon = $this->gtree->get_calculation_icon($element, $this->gpr, true);
141 if ($iscalculated || ($this->show_calculations && $icon)) {
079b2e52 142 $actionsmenu->add($icon);
0e999796 143 }
dc482cfa 144
145 if ($element['type'] == 'item' or ($element['type'] == 'category' and $element['depth'] > 1)) {
146 if ($this->element_deletable($element)) {
8ae8bf8a 147 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'delete', 'eid' => $eid, 'sesskey' => sesskey()));
079b2e52
MG
148 $icon = new action_menu_link_secondary($aurl, new pix_icon('t/delete', get_string('delete')), get_string('delete'));
149 $actionsmenu->add($icon);
dc482cfa 150 }
1c1f64a2 151
8ae8bf8a 152 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'moveselect', 'eid' => $eid, 'sesskey' => sesskey()));
2e3ec2af 153 $moveaction .= $OUTPUT->action_icon($aurl, new pix_icon('t/move', get_string('move')));
dc482cfa 154 }
155
079b2e52
MG
156 if ($icon = $this->gtree->get_hiding_icon($element, $this->gpr, true)) {
157 $actionsmenu->add($icon);
158 }
159
160 if ($icon = $this->gtree->get_reset_icon($element, $this->gpr, true)) {
161 $actionsmenu->add($icon);
162 }
2e3ec2af 163
079b2e52 164 $actions = $OUTPUT->render($actionsmenu);
dc482cfa 165
54a007e8 166 $returnrows = array();
dc482cfa 167 $root = false;
168
dc482cfa 169 $id = required_param('id', PARAM_INT);
170
171 /// prepare move target if needed
172 $last = '';
173
174 /// print the list items now
175 if ($this->moving == $eid) {
dc482cfa 176 // do not diplay children
54a007e8 177 $cell = new html_table_cell();
178 $cell->colspan = 12;
d75a2367
MG
179 $cell->attributes['class'] = $element['type'] . ' moving column-name level' .
180 ($level + 1) . ' level' . ($level % 2 ? 'even' : 'odd');
54a007e8 181 $cell->text = $object->name.' ('.get_string('move').')';
8cea545e 182 return array(new html_table_row(array($cell)));
dc482cfa 183 }
184
185 if ($element['type'] == 'category') {
186 $level++;
54a007e8 187 $this->categories[$object->id] = $object->stripped_name;
dc482cfa 188 $category = grade_category::fetch(array('id' => $object->id));
189 $item = $category->get_grade_item();
190
191 // Add aggregation coef input if not a course item and if parent category has correct aggregation type
82c662d3 192 $dimmed = ($item->is_hidden()) ? 'dimmed_text' : '';
dc482cfa 193
194 // Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan)
195 $aggregation_position = grade_get_setting($COURSE->id, 'aggregationposition', $CFG->grade_aggregationposition);
196 $category_total_data = null; // Used if aggregationposition is set to "last", so we can print it last
197
54a007e8 198 $html_children = array();
dc482cfa 199
200 $row_count = 0;
201
202 foreach($element['children'] as $child_el) {
54a007e8 203 $moveto = null;
dc482cfa 204
205 if (empty($child_el['object']->itemtype)) {
206 $child_el['object']->itemtype = false;
207 }
208
209 if (($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') && !$totals) {
210 continue;
211 }
212
54a007e8 213 $child_eid = $child_el['eid'];
dc482cfa 214 $first = '';
215
216 if ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
1c1f64a2 217 $first = array('first' => 1);
dc482cfa 218 $child_eid = $eid;
219 }
220
221 if ($this->moving && $this->moving != $child_eid) {
222
223 $strmove = get_string('move');
224 $strmovehere = get_string('movehere');
2e3ec2af 225 $actions = $moveaction = ''; // no action icons when moving
dc482cfa 226
8ae8bf8a 227 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'move', 'eid' => $this->moving, 'moveafter' => $child_eid, 'sesskey' => sesskey()));
1c1f64a2 228 if ($first) {
8ae8bf8a 229 $aurl->params($first);
1c1f64a2 230 }
71297a3f 231
54a007e8 232 $cell = new html_table_cell();
233 $cell->colspan = 12;
d75a2367 234 $cell->attributes['class'] = 'movehere level' . ($level + 1) . ' level' . ($level % 2 ? 'even' : 'odd');
6ef4878b 235
84d32de3
AD
236 $icon = new pix_icon('movehere', $strmovehere, null, array('class'=>'movetarget'));
237 $cell->text = $OUTPUT->action_icon($aurl, $icon);
1c1f64a2 238
8cea545e 239 $moveto = new html_table_row(array($cell));
dc482cfa 240 }
241
242 $newparents = $parents;
243 $newparents[] = $eid;
244
245 $row_count++;
246 $child_row_count = 0;
247
248 // If moving, do not print course and category totals, but still print the moveto target box
249 if ($this->moving && ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category')) {
54a007e8 250 $html_children[] = $moveto;
dc482cfa 251 } elseif ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
252 // We don't build the item yet because we first need to know the deepest level of categories (for category/name colspans)
54a007e8 253 $category_total_item = $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count);
dc482cfa 254 if (!$aggregation_position) {
54a007e8 255 $html_children = array_merge($html_children, $category_total_item);
dc482cfa 256 }
257 } else {
54a007e8 258 $html_children = array_merge($html_children, $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count));
259 if (!empty($moveto)) {
260 $html_children[] = $moveto;
261 }
dc482cfa 262
263 if ($this->moving) {
264 $row_count++;
265 }
266 }
267
268 $row_count += $child_row_count;
269
270 // If the child is a category, increment row_count by one more (for the extra coloured row)
271 if ($child_el['type'] == 'category') {
272 $row_count++;
273 }
274 }
275
276 // Print category total at the end if aggregation position is "last" (1)
277 if (!empty($category_total_item) && $aggregation_position) {
54a007e8 278 $html_children = array_merge($html_children, $category_total_item);
dc482cfa 279 }
280
54a007e8 281 // Determine if we are at the root
dc482cfa 282 if (isset($element['object']->grade_item) && $element['object']->grade_item->is_course_item()) {
dc482cfa 283 $root = true;
284 }
285
d75a2367 286 $levelclass = "level$level level" . ($level % 2 ? 'odd' : 'even');
2be6ca78 287
b189277f 288 $courseclass = '';
289 if ($level == 1) {
290 $courseclass = 'coursecategory';
291 }
71297a3f 292
54a007e8 293 $row = new html_table_row();
079b2e52 294 $row->id = 'grade-item-' . $eid;
16be8974 295 $row->attributes['class'] = $courseclass . ' category ' . $dimmed;
b5233f9b
AN
296 $row->attributes['data-category'] = $eid;
297 $row->attributes['data-itemid'] = $category->get_grade_item()->id;
54a007e8 298 foreach ($rowclasses as $class) {
16be8974 299 $row->attributes['class'] .= ' ' . $class;
54a007e8 300 }
dc482cfa 301
54a007e8 302 $headercell = new html_table_cell();
303 $headercell->header = true;
304 $headercell->scope = 'row';
16be8974 305 $headercell->attributes['title'] = $object->stripped_name;
d75a2367 306 $headercell->attributes['class'] = 'cell column-rowspan rowspan ' . $levelclass;
bd26d932 307 $headercell->rowspan = $row_count + 1;
54a007e8 308 $row->cells[] = $headercell;
dc482cfa 309
310 foreach ($this->columns as $column) {
b79bf7a2 311 if (!($this->moving && $column->hide_when_moving)) {
b5233f9b
AN
312 $row->cells[] = $column->get_category_cell($category, $levelclass, [
313 'id' => $id,
314 'name' => $object->name,
315 'level' => $level,
316 'actions' => $actions,
317 'moveaction' => $moveaction,
318 'eid' => $eid,
319 ]);
dc482cfa 320 }
321 }
71297a3f 322
54a007e8 323 $returnrows[] = $row;
dc482cfa 324
54a007e8 325 $returnrows = array_merge($returnrows, $html_children);
dc482cfa 326
6ef4878b 327 // Print a coloured row to show the end of the category across the table
54a007e8 328 $endcell = new html_table_cell();
329 $endcell->colspan = (19 - $level);
d75a2367 330 $endcell->attributes['class'] = 'emptyrow colspan ' . $levelclass;
54a007e8 331
0e35ba6f 332 $returnrows[] = new html_table_row(array($endcell));
dc482cfa 333
334 } else { // Dealing with a grade item
335
336 $item = grade_item::fetch(array('id' => $object->id));
337 $element['type'] = 'item';
338 $element['object'] = $item;
2be6ca78 339
b189277f 340 $categoryitemclass = '';
341 if ($item->itemtype == 'category') {
342 $categoryitemclass = 'categoryitem';
343 }
d75a2367
MG
344 if ($item->itemtype == 'course') {
345 $categoryitemclass = 'courseitem';
346 }
dc482cfa 347
54a007e8 348 $dimmed = ($item->is_hidden()) ? "dimmed_text" : "";
349 $gradeitemrow = new html_table_row();
079b2e52 350 $gradeitemrow->id = 'grade-item-' . $eid;
16be8974 351 $gradeitemrow->attributes['class'] = $categoryitemclass . ' item ' . $dimmed;
b5233f9b 352 $gradeitemrow->attributes['data-itemid'] = $object->id;
54a007e8 353 foreach ($rowclasses as $class) {
16be8974 354 $gradeitemrow->attributes['class'] .= ' ' . $class;
54a007e8 355 }
dc482cfa 356
357 foreach ($this->columns as $column) {
b79bf7a2 358 if (!($this->moving && $column->hide_when_moving)) {
2e3ec2af
MG
359 $gradeitemrow->cells[] = $column->get_item_cell($item, array('id' => $id, 'name' => $object->name,
360 'level' => $level, 'actions' => $actions, 'element' => $element, 'eid' => $eid,
361 'moveaction' => $moveaction, 'itemtype' => $object->itemtype));
dc482cfa 362 }
363 }
364
54a007e8 365 $returnrows[] = $gradeitemrow;
dc482cfa 366 }
367
54a007e8 368 return $returnrows;
dc482cfa 369
370 }
371
372 /**
373 * Given a grade_item object, returns a labelled input if an aggregation coefficient (weight or extra credit) applies to it.
374 * @param grade_item $item
375 * @return string HTML
376 */
bfc8c1d4 377 static function get_weight_input($item) {
1c1f64a2 378 global $OUTPUT;
379
dc482cfa 380 if (!is_object($item) || get_class($item) !== 'grade_item') {
381 throw new Exception('grade_edit_tree::get_weight_input($item) was given a variable that is not of the required type (grade_item object)');
382 return false;
383 }
384
385 if ($item->is_course_item()) {
386 return '';
387 }
388
389 $parent_category = $item->get_parent_category();
dc482cfa 390 $parent_category->apply_forced_settings();
653a8648 391 $aggcoef = $item->get_coefstring();
dc482cfa 392
b0c91d72
FM
393 $itemname = $item->itemname;
394 if ($item->is_category_item()) {
395 // Remember, the parent category of a category item is the category itself.
396 $itemname = $parent_category->get_name();
397 }
bfc8c1d4
MG
398 $str = '';
399
400 if ($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoef' || $aggcoef == 'aggregationcoefextraweight') {
b5233f9b 401
d68a9ece
FM
402 return $OUTPUT->render_from_template('core_grades/weight_field', [
403 'id' => $item->id,
404 'itemname' => $itemname,
405 'value' => self::format_number($item->aggregationcoef)
b5233f9b
AN
406 ]);
407
d68a9ece
FM
408 } else if ($aggcoef == 'aggregationcoefextraweightsum') {
409
410 $tpldata = [
411 'id' => $item->id,
412 'itemname' => $itemname,
e584e6ae 413 'value' => self::format_number($item->aggregationcoef2 * 100.0),
d68a9ece
FM
414 'checked' => $item->weightoverride,
415 'disabled' => !$item->weightoverride
416 ];
417 $str .= $OUTPUT->render_from_template('core_grades/weight_override_field', $tpldata);
200e5173 418
dc482cfa 419 }
bfc8c1d4
MG
420
421 return $str;
cd3a391c 422 }
dc482cfa 423
53914e44
MN
424 // Trims trailing zeros.
425 // Used on the 'Gradebook setup' page for grade items settings like aggregation co-efficient.
426 // Grader report has its own decimal place settings so they are handled elsewhere.
e2bb3c92 427 static function format_number($number) {
4592df46 428 $formatted = rtrim(format_float($number, 4),'0');
dacd5daa 429 if (substr($formatted, -1)==get_string('decsep', 'langconfig')) { //if last char is the decimal point
4592df46
AD
430 $formatted .= '0';
431 }
432 return $formatted;
3cf33f51
AD
433 }
434
dc482cfa 435 /**
436 * Given an element of the grade tree, returns whether it is deletable or not (only manual grade items are deletable)
437 *
438 * @param array $element
439 * @return bool
440 */
441 function element_deletable($element) {
442 global $COURSE;
443
444 if ($element['type'] != 'item') {
445 return true;
446 }
447
448 $grade_item = $element['object'];
449
450 if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) {
451 return true;
452 }
453
454 $modinfo = get_fast_modinfo($COURSE);
455 if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) {
456 // module does not exist
457 return true;
458 }
459
460 return false;
461 }
462
463 /**
464 * Given the grade tree and an array of element ids (e.g. c15, i42), and expecting the 'moveafter' URL param,
465 * moves the selected items to the requested location. Then redirects the user to the given $returnurl
466 *
467 * @param object $gtree The grade tree (a recursive representation of the grade categories and grade items)
468 * @param array $eids
469 * @param string $returnurl
470 */
471 function move_elements($eids, $returnurl) {
472 $moveafter = required_param('moveafter', PARAM_INT);
473
474 if (!is_array($eids)) {
475 $eids = array($eids);
476 }
477
ddb9c95f 478 if(!$after_el = $this->gtree->locate_element("cg$moveafter")) {
dc482cfa 479 print_error('invalidelementid', '', $returnurl);
480 }
481
482 $after = $after_el['object'];
483 $parent = $after;
484 $sortorder = $after->get_sortorder();
485
486 foreach ($eids as $eid) {
487 if (!$element = $this->gtree->locate_element($eid)) {
488 print_error('invalidelementid', '', $returnurl);
489 }
490 $object = $element['object'];
491
492 $object->set_parent($parent->id);
493 $object->move_after_sortorder($sortorder);
4d2f7923 494 $sortorder++;
dc482cfa 495 }
496
497 redirect($returnurl, '', 0);
498 }
499
500 /**
501 * Recurses through the entire grade tree to find and return the maximum depth of the tree.
502 * This should be run only once from the root element (course category), and is used for the
503 * indentation of the Name column's cells (colspan)
504 *
505 * @param array $element An array of values representing a grade tree's element (all grade items in this case)
506 * @param int $level The level of the current recursion
507 * @param int $deepest_level A value passed to each subsequent level of recursion and incremented if $level > $deepest_level
508 * @return int Deepest level
509 */
510 function get_deepest_level($element, $level=0, $deepest_level=1) {
511 $object = $element['object'];
512
513 $level++;
653a8648 514 $coefstring = $element['object']->get_coefstring();
dc482cfa 515 if ($element['type'] == 'category') {
bfc8c1d4
MG
516 if ($coefstring == 'aggregationcoefweight' || $coefstring == 'aggregationcoefextraweightsum' ||
517 $coefstring == 'aggregationcoefextraweight') {
518 $this->uses_weight = true;
677bc073 519 }
653a8648 520
dc482cfa 521 foreach($element['children'] as $child_el) {
522 if ($level > $deepest_level) {
523 $deepest_level = $level;
524 }
525 $deepest_level = $this->get_deepest_level($child_el, $level, $deepest_level);
526 }
825a3fa9
JP
527
528 $category = grade_category::fetch(array('id' => $object->id));
529 $item = $category->get_grade_item();
530 if ($item->gradetype == GRADE_TYPE_NONE) {
531 // Add 1 more level for grade category that has no total.
532 $deepest_level++;
533 }
dc482cfa 534 }
535
536 return $deepest_level;
537 }
538}
539
b79bf7a2
MG
540/**
541 * Class grade_edit_tree_column
542 *
543 * @package core_grades
544 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
545 */
dc482cfa 546abstract class grade_edit_tree_column {
547 public $forced;
548 public $hidden;
549 public $forced_hidden;
550 public $advanced_hidden;
551 public $hide_when_moving = true;
54a007e8 552 /**
553 * html_table_cell object used as a template for header cells in all categories.
554 * It must be cloned before being used.
555 * @var html_table_cell $headercell
556 */
557 public $headercell;
558 /**
559 * html_table_cell object used as a template for category cells in all categories.
560 * It must be cloned before being used.
561 * @var html_table_cell $categorycell
562 */
563 public $categorycell;
564 /**
565 * html_table_cell object used as a template for item cells in all categories.
566 * It must be cloned before being used.
567 * @var html_table_cell $itemcell
568 */
569 public $itemcell;
dc482cfa 570
571 public static function factory($name, $params=array()) {
572 $class_name = "grade_edit_tree_column_$name";
573 if (class_exists($class_name)) {
574 return new $class_name($params);
575 }
576 }
577
578 public abstract function get_header_cell();
579
d75a2367
MG
580 public function get_category_cell($category, $levelclass, $params) {
581 $cell = clone($this->categorycell);
582 $cell->attributes['class'] .= ' ' . $levelclass;
583 $cell->attributes['text'] = '';
584 return $cell;
585 }
dc482cfa 586
d75a2367
MG
587 public function get_item_cell($item, $params) {
588 $cell = clone($this->itemcell);
589 $cell->attributes['text'] = '';
590 if (isset($params['level'])) {
591 $level = $params['level'] + (($item->itemtype == 'category' || $item->itemtype == 'course') ? 0 : 1);
592 $cell->attributes['class'] .= ' level' . $level;
593 $cell->attributes['class'] .= ' level' . ($level % 2 ? 'odd' : 'even');
594 }
595 return $cell;
596 }
dc482cfa 597
54a007e8 598 public function __construct() {
599 $this->headercell = new html_table_cell();
600 $this->headercell->header = true;
16be8974 601 $this->headercell->attributes['class'] = 'header';
54a007e8 602
603 $this->categorycell = new html_table_cell();
16be8974 604 $this->categorycell->attributes['class'] = 'cell';
54a007e8 605
606 $this->itemcell = new html_table_cell();
16be8974 607 $this->itemcell->attributes['class'] = 'cell';
d75a2367
MG
608
609 if (preg_match('/^grade_edit_tree_column_(\w*)$/', get_class($this), $matches)) {
610 $this->headercell->attributes['class'] .= ' column-' . $matches[1];
611 $this->categorycell->attributes['class'] .= ' column-' . $matches[1];
612 $this->itemcell->attributes['class'] .= ' column-' . $matches[1];
613 }
54a007e8 614 }
dc482cfa 615}
616
b79bf7a2
MG
617/**
618 * Class grade_edit_tree_column_name
619 *
620 * @package core_grades
621 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
622 */
dc482cfa 623class grade_edit_tree_column_name extends grade_edit_tree_column {
624 public $forced = false;
625 public $hidden = false;
626 public $forced_hidden = false;
627 public $advanced_hidden = false;
628 public $deepest_level = 1;
629 public $hide_when_moving = false;
630
631 public function __construct($params) {
632 if (empty($params['deepest_level'])) {
633 throw new Exception('Tried to instantiate a grade_edit_tree_column_name object without the "deepest_level" param!');
634 }
635
636 $this->deepest_level = $params['deepest_level'];
54a007e8 637 parent::__construct();
dc482cfa 638 }
639
640 public function get_header_cell() {
54a007e8 641 $headercell = clone($this->headercell);
54a007e8 642 $headercell->colspan = $this->deepest_level + 1;
643 $headercell->text = get_string('name');
644 return $headercell;
dc482cfa 645 }
646
647 public function get_category_cell($category, $levelclass, $params) {
54a007e8 648 global $OUTPUT;
dc482cfa 649 if (empty($params['name']) || empty($params['level'])) {
650 throw new Exception('Array key (name or level) missing from 3rd param of grade_edit_tree_column_name::get_category_cell($category, $levelclass, $params)');
651 }
2e3ec2af 652 $moveaction = isset($params['moveaction']) ? $params['moveaction'] : '';
d75a2367 653 $categorycell = parent::get_category_cell($category, $levelclass, $params);
54a007e8 654 $categorycell->colspan = ($this->deepest_level +1) - $params['level'];
2e3ec2af 655 $categorycell->text = $OUTPUT->heading($moveaction . $params['name'], 4);
54a007e8 656 return $categorycell;
dc482cfa 657 }
658
659 public function get_item_cell($item, $params) {
660 global $CFG;
661
662 if (empty($params['element']) || empty($params['name']) || empty($params['level'])) {
663 throw new Exception('Array key (name, level or element) missing from 2nd param of grade_edit_tree_column_name::get_item_cell($item, $params)');
664 }
665
666 $name = $params['name'];
2e3ec2af 667 $moveaction = isset($params['moveaction']) ? $params['moveaction'] : '';
dc482cfa 668
d75a2367 669 $itemcell = parent::get_item_cell($item, $params);
54a007e8 670 $itemcell->colspan = ($this->deepest_level + 1) - $params['level'];
2e3ec2af 671 $itemcell->text = $moveaction . $name;
54a007e8 672 return $itemcell;
dc482cfa 673 }
dc482cfa 674}
675
b79bf7a2
MG
676/**
677 * Class grade_edit_tree_column_weight
678 *
679 * @package core_grades
680 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
681 */
653a8648 682class grade_edit_tree_column_weight extends grade_edit_tree_column {
dc482cfa 683
684 public function get_header_cell() {
c3b834b4 685 global $OUTPUT;
54a007e8 686 $headercell = clone($this->headercell);
bfe969e8 687 $headercell->text = get_string('weights', 'grades').$OUTPUT->help_icon('aggregationcoefweight', 'grades');
54a007e8 688 return $headercell;
dc482cfa 689 }
690
691 public function get_category_cell($category, $levelclass, $params) {
692
693 $item = $category->get_grade_item();
d75a2367 694 $categorycell = parent::get_category_cell($category, $levelclass, $params);
bfc8c1d4 695 $categorycell->text = grade_edit_tree::get_weight_input($item);
54a007e8 696 return $categorycell;
dc482cfa 697 }
698
699 public function get_item_cell($item, $params) {
8b5c4545 700 global $CFG;
dc482cfa 701 if (empty($params['element'])) {
702 throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)');
703 }
d75a2367 704 $itemcell = parent::get_item_cell($item, $params);
54a007e8 705 $itemcell->text = '&nbsp;';
6aeebc25 706 $object = $params['element']['object'];
dc482cfa 707
6aeebc25
FM
708 if (!in_array($object->itemtype, array('courseitem', 'categoryitem', 'category'))
709 && !in_array($object->gradetype, array(GRADE_TYPE_NONE, GRADE_TYPE_TEXT))
8b5c4545
FM
710 && (!$object->is_outcome_item() || $object->load_parent_category()->aggregateoutcomes)
711 && ($object->gradetype != GRADE_TYPE_SCALE || !empty($CFG->grade_includescalesinaggregation))) {
bfc8c1d4 712 $itemcell->text = grade_edit_tree::get_weight_input($item);
dc482cfa 713 }
714
54a007e8 715 return $itemcell;
dc482cfa 716 }
dc482cfa 717}
718
b79bf7a2
MG
719/**
720 * Class grade_edit_tree_column_range
721 *
722 * @package core_grades
723 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
724 */
dc482cfa 725class grade_edit_tree_column_range extends grade_edit_tree_column {
726
727 public function get_header_cell() {
54a007e8 728 $headercell = clone($this->headercell);
729 $headercell->text = get_string('maxgrade', 'grades');
730 return $headercell;
dc482cfa 731 }
732
733 public function get_category_cell($category, $levelclass, $params) {
d75a2367 734 $categorycell = parent::get_category_cell($category, $levelclass, $params);
54a007e8 735 $categorycell->text = ' - ';
736 return $categorycell;
dc482cfa 737 }
738
739 public function get_item_cell($item, $params) {
1c1f64a2 740 global $DB, $OUTPUT;
46409b20 741
b5e00814 742 // If the parent aggregation is Natural, we should show the number, even for scales, as that value is used...
e3a65c9e 743 // ...in the computation. For text grades, the grademax is not used, so we can still show the no value string.
3f874a9d 744 $parentcat = $item->get_parent_category();
e3a65c9e
EM
745 if ($item->gradetype == GRADE_TYPE_TEXT) {
746 $grademax = ' - ';
bfc8c1d4 747 } else if ($item->gradetype == GRADE_TYPE_SCALE) {
3998cc5c 748 $scale = $DB->get_record('scale', array('id' => $item->scaleid));
e2a84477
AD
749 $scale_items = null;
750 if (empty($scale)) { //if the item is using a scale that's been removed
751 $scale_items = array();
752 } else {
753 $scale_items = explode(',', $scale->scale);
754 }
3f874a9d 755 if ($parentcat->aggregation == GRADE_AGGREGATE_SUM) {
290ddb73
MG
756 $grademax = end($scale_items) . ' (' .
757 format_float($item->grademax, $item->get_decimals()) . ')';
758 } else {
759 $grademax = end($scale_items) . ' (' . count($scale_items) . ')';
760 }
dc482cfa 761 } else {
bfc8c1d4 762 $grademax = format_float($item->grademax, $item->get_decimals());
dc482cfa 763 }
764
3f874a9d
EM
765 $isextracredit = false;
766 if ($item->aggregationcoef > 0) {
767 // For category grade items, we need the grandparent category.
768 // The parent is just category the grade item represents.
769 if ($item->is_category_item()) {
770 $grandparentcat = $parentcat->get_parent_category();
771 if ($grandparentcat->is_extracredit_used()) {
772 $isextracredit = true;
773 }
774 } else if ($parentcat->is_extracredit_used()) {
775 $isextracredit = true;
776 }
777 }
778 if ($isextracredit) {
3712ee3e
ZD
779 $grademax .= ' ' . html_writer::tag('abbr', get_string('aggregationcoefextrasumabbr', 'grades'),
780 array('title' => get_string('aggregationcoefextrasum', 'grades')));
781 }
782
d75a2367 783 $itemcell = parent::get_item_cell($item, $params);
54a007e8 784 $itemcell->text = $grademax;
785 return $itemcell;
dc482cfa 786 }
dc482cfa 787}
788
b79bf7a2
MG
789/**
790 * Class grade_edit_tree_column_actions
791 *
792 * @package core_grades
793 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
794 */
dc482cfa 795class grade_edit_tree_column_actions extends grade_edit_tree_column {
796
797 public function __construct($params) {
71297a3f 798 parent::__construct();
dc482cfa 799 }
800
801 public function get_header_cell() {
54a007e8 802 $headercell = clone($this->headercell);
54a007e8 803 $headercell->text = get_string('actions');
804 return $headercell;
dc482cfa 805 }
806
807 public function get_category_cell($category, $levelclass, $params) {
808
809 if (empty($params['actions'])) {
810 throw new Exception('Array key (actions) missing from 3rd param of grade_edit_tree_column_actions::get_category_actions($category, $levelclass, $params)');
811 }
812
d75a2367 813 $categorycell = parent::get_category_cell($category, $levelclass, $params);
54a007e8 814 $categorycell->text = $params['actions'];
815 return $categorycell;
dc482cfa 816 }
817
818 public function get_item_cell($item, $params) {
819 if (empty($params['actions'])) {
820 throw new Exception('Array key (actions) missing from 2nd param of grade_edit_tree_column_actions::get_item_cell($item, $params)');
821 }
d75a2367 822 $itemcell = parent::get_item_cell($item, $params);
54a007e8 823 $itemcell->text = $params['actions'];
824 return $itemcell;
dc482cfa 825 }
dc482cfa 826}
827
b79bf7a2
MG
828/**
829 * Class grade_edit_tree_column_select
830 *
831 * @package core_grades
832 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
833 */
dc482cfa 834class grade_edit_tree_column_select extends grade_edit_tree_column {
835
836 public function get_header_cell() {
54a007e8 837 $headercell = clone($this->headercell);
54a007e8 838 $headercell->text = get_string('select');
839 return $headercell;
dc482cfa 840 }
841
842 public function get_category_cell($category, $levelclass, $params) {
2f848616
JP
843 global $OUTPUT;
844
dc482cfa 845 if (empty($params['eid'])) {
846 throw new Exception('Array key (eid) missing from 3rd param of grade_edit_tree_column_select::get_category_cell($category, $levelclass, $params)');
847 }
2f848616
JP
848
849 $togglegroup = $this->get_checkbox_togglegroup($category);
850 $mastercheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [
851 'id' => $togglegroup,
852 'name' => $togglegroup,
853 'value' => 1,
854 'label' => get_string('all'),
855 'selectall' => get_string('all'),
856 'deselectall' => get_string('none'),
b5233f9b 857 ]);
dc482cfa 858
d75a2367 859 $categorycell = parent::get_category_cell($category, $levelclass, $params);
2f848616 860 $categorycell->text = $OUTPUT->render($mastercheckbox);
54a007e8 861 return $categorycell;
dc482cfa 862 }
863
864 public function get_item_cell($item, $params) {
54a007e8 865 if (empty($params['itemtype']) || empty($params['eid'])) {
2149326b 866 print_error('missingitemtypeoreid', 'core_grades');
54a007e8 867 }
d75a2367 868 $itemcell = parent::get_item_cell($item, $params);
54a007e8 869
870 if ($params['itemtype'] != 'course' && $params['itemtype'] != 'category') {
2f848616
JP
871 global $OUTPUT;
872
873 // Fetch the grade item's category.
874 $category = grade_category::fetch(['id' => $item->categoryid]);
875 $togglegroup = $this->get_checkbox_togglegroup($category);
876
877 $checkboxid = 'select_' . $params['eid'];
878 $checkbox = new \core\output\checkbox_toggleall($togglegroup, false, [
879 'id' => $checkboxid,
880 'name' => $checkboxid,
881 'label' => get_string('select', 'grades', $item->itemname),
882 'labelclasses' => 'accesshide',
883 'classes' => 'itemselect ignoredirty',
884 ]);
885 $itemcell->text = $OUTPUT->render($checkbox);
dc482cfa 886 }
6723bc66 887 return $itemcell;
dc482cfa 888 }
2f848616
JP
889
890 /**
891 * Generates a toggle group name for a bulk-action checkbox based on the given grade category.
892 *
893 * @param grade_category $category The grade category.
894 * @return string
895 */
896 protected function get_checkbox_togglegroup(grade_category $category): string {
897 $levels = [];
898 $categories = explode('/', $category->path);
899 foreach ($categories as $categoryid) {
900 $level = 'category' . $categoryid;
901 if (!in_array($level, $levels)) {
902 $levels[] = 'category' . $categoryid;
903 }
904 }
905 $togglegroup = implode(' ', $levels);
906
907 return $togglegroup;
908 }
dc482cfa 909}
6c3ef410 910