2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * A library of classes used by the grade edit pages
20 * @package core_grades
21 * @copyright 2009 Nicolas Connault
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 class grade_edit_tree {
26 public $columns = array();
29 * @var object $gtree @see grade/lib.php
34 * @var grade_plugin_return @see grade/lib.php
39 * @var string $moving The eid of the category or item being moved
43 public $deepest_level;
45 public $uses_extra_credit = false;
47 public $uses_weight = false;
51 public $categories = array();
55 public function __construct($gtree, $moving=false, $gpr) {
56 global $USER, $OUTPUT, $COURSE;
58 $this->gtree = $gtree;
59 $this->moving = $moving;
61 $this->deepest_level = $this->get_deepest_level($this->gtree->top_element);
63 $this->columns = array(grade_edit_tree_column::factory('name', array('deepest_level' => $this->deepest_level)),
64 grade_edit_tree_column::factory('aggregation', array('flag' => true)));
66 if ($this->uses_weight) {
67 $this->columns[] = grade_edit_tree_column::factory('weight', array('adv' => 'aggregationcoef'));
69 if ($this->uses_extra_credit) {
70 $this->columns[] = grade_edit_tree_column::factory('extracredit', array('adv' => 'aggregationcoef'));
73 $this->columns[] = grade_edit_tree_column::factory('range'); // This is not a setting... How do we deal with it?
74 $this->columns[] = grade_edit_tree_column::factory('aggregateonlygraded', array('flag' => true));
75 $this->columns[] = grade_edit_tree_column::factory('aggregatesubcats', array('flag' => true));
76 $this->columns[] = grade_edit_tree_column::factory('aggregateoutcomes', array('flag' => true));
77 $this->columns[] = grade_edit_tree_column::factory('droplow', array('flag' => true));
78 $this->columns[] = grade_edit_tree_column::factory('keephigh', array('flag' => true));
79 $this->columns[] = grade_edit_tree_column::factory('multfactor', array('adv' => true));
80 $this->columns[] = grade_edit_tree_column::factory('plusfactor', array('adv' => true));
81 $this->columns[] = grade_edit_tree_column::factory('actions');
82 $this->columns[] = grade_edit_tree_column::factory('select');
84 $mode = ($USER->gradeediting[$COURSE->id]) ? 'advanced' : 'simple';
87 if ($mode == 'simple') {
88 $widthstyle = ' style="width:auto;" ';
91 $this->table = new html_table();
92 $this->table->id = "grade_edit_tree_table";
93 $this->table->cellpadding = 5;
94 $this->table->attributes['class'] = 'generaltable ' . $mode;
95 $this->table->style = $widthstyle;
97 foreach ($this->columns as $column) {
98 if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
99 $this->table->head[] = $column->get_header_cell();
104 $this->table->data = $this->build_html_tree($this->gtree->top_element, true, array(), 0, $rowcount);
108 * Recursive function for building the table holding the grade categories and items,
109 * with CSS indentation and styles.
111 * @param array $element The current tree element being rendered
112 * @param boolean $totals Whether or not to print category grade items (category totals)
113 * @param array $parents An array of parent categories for the current element (used for indentation and row classes)
115 * @return string HTML
117 public function build_html_tree($element, $totals, $parents, $level, &$row_count) {
118 global $CFG, $COURSE, $USER, $OUTPUT;
120 $object = $element['object'];
121 $eid = $element['eid'];
122 $object->name = $this->gtree->get_element_header($element, true, true, false);
123 $object->stripped_name = $this->gtree->get_element_header($element, false, false, false);
125 $is_category_item = false;
126 if ($element['type'] == 'categoryitem' || $element['type'] == 'courseitem') {
127 $is_category_item = true;
130 $rowclasses = array();
131 foreach ($parents as $parent_eid) {
132 $rowclasses[] = $parent_eid;
137 if (!$is_category_item) {
138 $actions .= $this->gtree->get_edit_icon($element, $this->gpr);
141 $actions .= $this->gtree->get_calculation_icon($element, $this->gpr);
143 if ($element['type'] == 'item' or ($element['type'] == 'category' and $element['depth'] > 1)) {
144 if ($this->element_deletable($element)) {
145 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'delete', 'eid' => $eid, 'sesskey' => sesskey()));
146 $actions .= $OUTPUT->action_icon($aurl, new pix_icon('t/delete', get_string('delete')));
149 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'moveselect', 'eid' => $eid, 'sesskey' => sesskey()));
150 $actions .= $OUTPUT->action_icon($aurl, new pix_icon('t/move', get_string('move')));
153 $actions .= $this->gtree->get_hiding_icon($element, $this->gpr);
154 $actions .= $this->gtree->get_locking_icon($element, $this->gpr);
156 $mode = ($USER->gradeediting[$COURSE->id]) ? 'advanced' : 'simple';
158 $returnrows = array();
161 $id = required_param('id', PARAM_INT);
163 /// prepare move target if needed
166 /// print the list items now
167 if ($this->moving == $eid) {
168 // do not diplay children
169 $cell = new html_table_cell();
171 $cell->attributes['class'] = $element['type'] . ' moving';
172 $cell->text = $object->name.' ('.get_string('move').')';
173 return array(new html_table_row(array($cell)));
176 if ($element['type'] == 'category') {
178 $this->categories[$object->id] = $object->stripped_name;
179 $category = grade_category::fetch(array('id' => $object->id));
180 $item = $category->get_grade_item();
182 // Add aggregation coef input if not a course item and if parent category has correct aggregation type
183 $dimmed = ($item->is_hidden()) ? 'dimmed' : '';
185 // Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan)
186 $aggregation_position = grade_get_setting($COURSE->id, 'aggregationposition', $CFG->grade_aggregationposition);
187 $category_total_data = null; // Used if aggregationposition is set to "last", so we can print it last
189 $html_children = array();
193 foreach($element['children'] as $child_el) {
196 if (empty($child_el['object']->itemtype)) {
197 $child_el['object']->itemtype = false;
200 if (($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') && !$totals) {
204 $child_eid = $child_el['eid'];
207 if ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
208 $first = array('first' => 1);
212 if ($this->moving && $this->moving != $child_eid) {
214 $strmove = get_string('move');
215 $strmovehere = get_string('movehere');
216 $actions = ''; // no action icons when moving
218 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'move', 'eid' => $this->moving, 'moveafter' => $child_eid, 'sesskey' => sesskey()));
220 $aurl->params($first);
223 $cell = new html_table_cell();
226 $icon = new pix_icon('movehere', $strmovehere, null, array('class'=>'movetarget'));
227 $cell->text = $OUTPUT->action_icon($aurl, $icon);
229 $moveto = new html_table_row(array($cell));
232 $newparents = $parents;
233 $newparents[] = $eid;
236 $child_row_count = 0;
238 // If moving, do not print course and category totals, but still print the moveto target box
239 if ($this->moving && ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category')) {
240 $html_children[] = $moveto;
241 } elseif ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
242 // We don't build the item yet because we first need to know the deepest level of categories (for category/name colspans)
243 $category_total_item = $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count);
244 if (!$aggregation_position) {
245 $html_children = array_merge($html_children, $category_total_item);
248 $html_children = array_merge($html_children, $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count));
249 if (!empty($moveto)) {
250 $html_children[] = $moveto;
258 $row_count += $child_row_count;
260 // If the child is a category, increment row_count by one more (for the extra coloured row)
261 if ($child_el['type'] == 'category') {
266 // Print category total at the end if aggregation position is "last" (1)
267 if (!empty($category_total_item) && $aggregation_position) {
268 $html_children = array_merge($html_children, $category_total_item);
271 // Determine if we are at the root
272 if (isset($element['object']->grade_item) && $element['object']->grade_item->is_course_item()) {
276 $row_count_offset = 0;
278 if (empty($category_total_item) && !$this->moving) {
279 $row_count_offset = -1;
282 $levelclass = "level$level";
286 $courseclass = 'coursecategory';
289 $row = new html_table_row();
290 $row->attributes['class'] = $courseclass . ' category ' . $dimmed;
291 foreach ($rowclasses as $class) {
292 $row->attributes['class'] .= ' ' . $class;
295 $headercell = new html_table_cell();
296 $headercell->header = true;
297 $headercell->scope = 'row';
298 $headercell->attributes['title'] = $object->stripped_name;
299 $headercell->attributes['class'] = 'cell rowspan ' . $levelclass;
300 $headercell->rowspan = $row_count+1+$row_count_offset;
301 $row->cells[] = $headercell;
303 foreach ($this->columns as $column) {
304 if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
305 $row->cells[] = $column->get_category_cell($category, $levelclass, array('id' => $id, 'name' => $object->name, 'level' => $level, 'actions' => $actions, 'eid' => $eid));
309 $returnrows[] = $row;
311 $returnrows = array_merge($returnrows, $html_children);
313 // Print a coloured row to show the end of the category across the table
314 $endcell = new html_table_cell();
315 $endcell->colspan = (19 - $level);
316 $endcell->attributes['class'] = 'colspan ' . $levelclass;
318 $returnrows[] = new html_table_row(array($endcell));;
320 } else { // Dealing with a grade item
322 $item = grade_item::fetch(array('id' => $object->id));
323 $element['type'] = 'item';
324 $element['object'] = $item;
326 $categoryitemclass = '';
327 if ($item->itemtype == 'category') {
328 $categoryitemclass = 'categoryitem';
331 $dimmed = ($item->is_hidden()) ? "dimmed_text" : "";
332 $gradeitemrow = new html_table_row();
333 $gradeitemrow->attributes['class'] = $categoryitemclass . ' item ' . $dimmed;
334 foreach ($rowclasses as $class) {
335 $gradeitemrow->attributes['class'] .= ' ' . $class;
338 foreach ($this->columns as $column) {
339 if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
340 $gradeitemrow->cells[] = $column->get_item_cell($item, array('id' => $id, 'name' => $object->name, 'level' => $level, 'actions' => $actions,
341 'element' => $element, 'eid' => $eid, 'itemtype' => $object->itemtype));
345 $returnrows[] = $gradeitemrow;
353 * Given a grade_item object, returns a labelled input if an aggregation coefficient (weight or extra credit) applies to it.
354 * @param grade_item $item
355 * @param string type "extra" or "weight": the type of the column hosting the weight input
356 * @return string HTML
358 static function get_weight_input($item, $type) {
361 if (!is_object($item) || get_class($item) !== 'grade_item') {
362 throw new Exception('grade_edit_tree::get_weight_input($item) was given a variable that is not of the required type (grade_item object)');
366 if ($item->is_course_item()) {
370 $parent_category = $item->get_parent_category();
371 $parent_category->apply_forced_settings();
372 $aggcoef = $item->get_coefstring();
374 if ((($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoef') && $type == 'weight') ||
375 ($aggcoef == 'aggregationcoefextraweight' && $type == 'extra')) {
376 return '<input type="text" size="6" id="aggregationcoef_'.$item->id.'" name="aggregationcoef_'.$item->id.'"
377 value="'.grade_edit_tree::format_number($item->aggregationcoef).'" />';
378 } elseif ($aggcoef == 'aggregationcoefextrasum' && $type == 'extra') {
379 $checked = ($item->aggregationcoef > 0) ? 'checked="checked"' : '';
380 return '<input type="hidden" name="extracredit_'.$item->id.'" value="0" />
381 <input type="checkbox" id="extracredit_'.$item->id.'" name="extracredit_'.$item->id.'" value="1" '."$checked />\n";
387 //Trims trailing zeros
388 //Used on the 'categories and items' page for grade items settings like aggregation co-efficient
389 //Grader report has its own decimal place settings so they are handled elsewhere
390 static function format_number($number) {
391 $formatted = rtrim(format_float($number, 4),'0');
392 if (substr($formatted, -1)==get_string('decsep', 'langconfig')) { //if last char is the decimal point
399 * Given an element of the grade tree, returns whether it is deletable or not (only manual grade items are deletable)
401 * @param array $element
404 function element_deletable($element) {
407 if ($element['type'] != 'item') {
411 $grade_item = $element['object'];
413 if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) {
417 $modinfo = get_fast_modinfo($COURSE);
418 if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) {
419 // module does not exist
427 * Given the grade tree and an array of element ids (e.g. c15, i42), and expecting the 'moveafter' URL param,
428 * moves the selected items to the requested location. Then redirects the user to the given $returnurl
430 * @param object $gtree The grade tree (a recursive representation of the grade categories and grade items)
432 * @param string $returnurl
434 function move_elements($eids, $returnurl) {
435 $moveafter = required_param('moveafter', PARAM_INT);
437 if (!is_array($eids)) {
438 $eids = array($eids);
441 if(!$after_el = $this->gtree->locate_element("c$moveafter")) {
442 print_error('invalidelementid', '', $returnurl);
445 $after = $after_el['object'];
447 $sortorder = $after->get_sortorder();
449 foreach ($eids as $eid) {
450 if (!$element = $this->gtree->locate_element($eid)) {
451 print_error('invalidelementid', '', $returnurl);
453 $object = $element['object'];
455 $object->set_parent($parent->id);
456 $object->move_after_sortorder($sortorder);
460 redirect($returnurl, '', 0);
464 * Recurses through the entire grade tree to find and return the maximum depth of the tree.
465 * This should be run only once from the root element (course category), and is used for the
466 * indentation of the Name column's cells (colspan)
468 * @param array $element An array of values representing a grade tree's element (all grade items in this case)
469 * @param int $level The level of the current recursion
470 * @param int $deepest_level A value passed to each subsequent level of recursion and incremented if $level > $deepest_level
471 * @return int Deepest level
473 function get_deepest_level($element, $level=0, $deepest_level=1) {
474 $object = $element['object'];
477 $coefstring = $element['object']->get_coefstring();
478 if ($element['type'] == 'category') {
479 if ($coefstring == 'aggregationcoefweight') {
480 $this->uses_weight = true;
481 } elseif ($coefstring == 'aggregationcoefextraweight' || $coefstring == 'aggregationcoefextrasum') {
482 $this->uses_extra_credit = true;
485 foreach($element['children'] as $child_el) {
486 if ($level > $deepest_level) {
487 $deepest_level = $level;
489 $deepest_level = $this->get_deepest_level($child_el, $level, $deepest_level);
493 return $deepest_level;
497 abstract class grade_edit_tree_column {
500 public $forced_hidden;
501 public $advanced_hidden;
502 public $hide_when_moving = true;
504 * html_table_cell object used as a template for header cells in all categories.
505 * It must be cloned before being used.
506 * @var html_table_cell $headercell
510 * html_table_cell object used as a template for category cells in all categories.
511 * It must be cloned before being used.
512 * @var html_table_cell $categorycell
514 public $categorycell;
516 * html_table_cell object used as a template for item cells in all categories.
517 * It must be cloned before being used.
518 * @var html_table_cell $itemcell
522 public static function factory($name, $params=array()) {
523 $class_name = "grade_edit_tree_column_$name";
524 if (class_exists($class_name)) {
525 return new $class_name($params);
529 public abstract function get_header_cell();
531 public abstract function get_category_cell($category, $levelclass, $params);
533 public abstract function get_item_cell($item, $params);
535 public abstract function is_hidden($mode='simple');
537 public function __construct() {
538 $this->headercell = new html_table_cell();
539 $this->headercell->header = true;
540 $this->headercell->style = 'whitespace: normal;';
541 $this->headercell->attributes['class'] = 'header';
543 $this->categorycell = new html_table_cell();
544 $this->categorycell->attributes['class'] = 'cell';
546 $this->itemcell = new html_table_cell();
547 $this->itemcell->attributes['class'] = 'cell';
551 abstract class grade_edit_tree_column_category extends grade_edit_tree_column {
556 public function __construct($name) {
558 $this->forced = (int)$CFG->{"grade_$name"."_flag"} & 1;
559 $this->advanced = (int)$CFG->{"grade_$name"."_flag"} & 2;
560 parent::__construct();
563 public function is_hidden($mode='simple') {
565 if ($mode == 'simple') {
566 return $this->advanced;
567 } elseif ($mode == 'advanced') {
568 if ($this->forced && $CFG->grade_hideforcedsettings) {
577 class grade_edit_tree_column_name extends grade_edit_tree_column {
578 public $forced = false;
579 public $hidden = false;
580 public $forced_hidden = false;
581 public $advanced_hidden = false;
582 public $deepest_level = 1;
583 public $hide_when_moving = false;
585 public function __construct($params) {
586 if (empty($params['deepest_level'])) {
587 throw new Exception('Tried to instantiate a grade_edit_tree_column_name object without the "deepest_level" param!');
590 $this->deepest_level = $params['deepest_level'];
591 parent::__construct();
594 public function get_header_cell() {
595 $headercell = clone($this->headercell);
596 $headercell->attributes['class'] .= ' name';
597 $headercell->colspan = $this->deepest_level + 1;
598 $headercell->text = get_string('name');
602 public function get_category_cell($category, $levelclass, $params) {
604 if (empty($params['name']) || empty($params['level'])) {
605 throw new Exception('Array key (name or level) missing from 3rd param of grade_edit_tree_column_name::get_category_cell($category, $levelclass, $params)');
607 $categorycell = clone($this->categorycell);
608 $categorycell->attributes['class'] .= ' name ' . $levelclass;
609 $categorycell->colspan = ($this->deepest_level +1) - $params['level'];
610 $categorycell->text = $OUTPUT->heading($params['name'], 4);
611 return $categorycell;
614 public function get_item_cell($item, $params) {
617 if (empty($params['element']) || empty($params['name']) || empty($params['level'])) {
618 throw new Exception('Array key (name, level or element) missing from 2nd param of grade_edit_tree_column_name::get_item_cell($item, $params)');
621 $name = $params['name'];
623 $itemcell = clone($this->itemcell);
624 $itemcell->attributes['class'] .= ' name';
625 $itemcell->colspan = ($this->deepest_level + 1) - $params['level'];
626 $itemcell->text = $name;
630 public function is_hidden($mode='simple') {
635 class grade_edit_tree_column_aggregation extends grade_edit_tree_column_category {
637 public function __construct($params) {
638 parent::__construct('aggregation');
641 public function get_header_cell() {
643 $headercell = clone($this->headercell);
644 $headercell->text = get_string('aggregation', 'grades').$OUTPUT->help_icon('aggregation', 'grades');
648 public function get_category_cell($category, $levelclass, $params) {
649 global $CFG, $OUTPUT;
650 if (empty($params['id'])) {
651 throw new Exception('Array key (id) missing from 3rd param of grade_edit_tree_column_aggregation::get_category_cell($category, $levelclass, $params)');
654 $options = array(GRADE_AGGREGATE_MEAN => get_string('aggregatemean', 'grades'),
655 GRADE_AGGREGATE_WEIGHTED_MEAN => get_string('aggregateweightedmean', 'grades'),
656 GRADE_AGGREGATE_WEIGHTED_MEAN2 => get_string('aggregateweightedmean2', 'grades'),
657 GRADE_AGGREGATE_EXTRACREDIT_MEAN => get_string('aggregateextracreditmean', 'grades'),
658 GRADE_AGGREGATE_MEDIAN => get_string('aggregatemedian', 'grades'),
659 GRADE_AGGREGATE_MIN => get_string('aggregatemin', 'grades'),
660 GRADE_AGGREGATE_MAX => get_string('aggregatemax', 'grades'),
661 GRADE_AGGREGATE_MODE => get_string('aggregatemode', 'grades'),
662 GRADE_AGGREGATE_SUM => get_string('aggregatesum', 'grades'));
664 $visible = explode(',', $CFG->grade_aggregations_visible);
665 foreach ($options as $constant => $string) {
666 if (!in_array($constant, $visible) && $constant != $category->aggregation) {
667 unset($options[$constant]);
672 $aggregation = $options[$category->aggregation];
674 $attributes = array();
675 $attributes['id'] = 'aggregation_'.$category->id;
676 $aggregation = html_writer::label(get_string('aggregation', 'grades'), 'aggregation_'.$category->id, false, array('class' => 'accesshide'));
677 $aggregation .= html_writer::select($options, 'aggregation_'.$category->id, $category->aggregation, null, $attributes);
678 $action = new component_action('change', 'update_category_aggregation', array('courseid' => $params['id'], 'category' => $category->id, 'sesskey' => sesskey()));
679 $OUTPUT->add_action_handler($action, 'aggregation_'.$category->id);
682 $categorycell = clone($this->categorycell);
683 $categorycell->attributes['class'] .= ' ' . $levelclass;
684 $categorycell->text = $aggregation;
685 return $categorycell;
689 public function get_item_cell($item, $params) {
690 $itemcell = clone($this->itemcell);
691 $itemcell->text = ' - ';
696 class grade_edit_tree_column_extracredit extends grade_edit_tree_column {
698 public function get_header_cell() {
700 $headercell = clone($this->headercell);
701 $headercell->text = get_string('aggregationcoefextra', 'grades').$OUTPUT->help_icon('aggregationcoefextra', 'grades');
705 public function get_category_cell($category, $levelclass, $params) {
706 $item = $category->get_grade_item();
707 $categorycell = clone($this->categorycell);
708 $categorycell->attributes['class'] .= ' ' . $levelclass;
709 $categorycell->text = grade_edit_tree::get_weight_input($item, 'extra');
710 return $categorycell;
713 public function get_item_cell($item, $params) {
714 if (empty($params['element'])) {
715 throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)');
718 $itemcell = clone($this->itemcell);
719 $itemcell->text = ' ';
721 if (!in_array($params['element']['object']->itemtype, array('courseitem', 'categoryitem', 'category'))) {
722 $itemcell->text = grade_edit_tree::get_weight_input($item, 'extra');
728 public function is_hidden($mode='simple') {
730 if ($mode == 'simple') {
731 return strstr($CFG->grade_item_advanced, 'aggregationcoef');
732 } elseif ($mode == 'advanced') {
738 class grade_edit_tree_column_weight extends grade_edit_tree_column {
740 public function get_header_cell() {
742 $headercell = clone($this->headercell);
743 $headercell->text = get_string('weightuc', 'grades').$OUTPUT->help_icon('aggregationcoefweight', 'grades');
747 public function get_category_cell($category, $levelclass, $params) {
749 $item = $category->get_grade_item();
750 $categorycell = clone($this->categorycell);
751 $categorycell->attributes['class'] .= ' ' . $levelclass;
752 $categorycell->text = grade_edit_tree::get_weight_input($item, 'weight');
753 return $categorycell;
756 public function get_item_cell($item, $params) {
757 if (empty($params['element'])) {
758 throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)');
760 $itemcell = clone($this->itemcell);
761 $itemcell->text = ' ';
763 if (!in_array($params['element']['object']->itemtype, array('courseitem', 'categoryitem', 'category'))) {
764 $itemcell->text = grade_edit_tree::get_weight_input($item, 'weight');
770 public function is_hidden($mode='simple') {
772 if ($mode == 'simple') {
773 return strstr($CFG->grade_item_advanced, 'aggregationcoef');
774 } elseif ($mode == 'advanced') {
780 class grade_edit_tree_column_range extends grade_edit_tree_column {
782 public function get_header_cell() {
783 $headercell = clone($this->headercell);
784 $headercell->text = get_string('maxgrade', 'grades');
788 public function get_category_cell($category, $levelclass, $params) {
789 $categorycell = clone($this->categorycell);
790 $categorycell->attributes['class'] .= ' range ' . $levelclass;
791 $categorycell->text = ' - ';
792 return $categorycell;
795 public function get_item_cell($item, $params) {
798 // If the parent aggregation is Sum of Grades, this cannot be changed
799 $parent_cat = $item->get_parent_category();
800 if ($parent_cat->aggregation == GRADE_AGGREGATE_SUM) {
801 $grademax = format_float($item->grademax, $item->get_decimals());
802 } elseif ($item->gradetype == GRADE_TYPE_SCALE) {
803 $scale = $DB->get_record('scale', array('id' => $item->scaleid));
805 if (empty($scale)) { //if the item is using a scale that's been removed
806 $scale_items = array();
808 $scale_items = explode(',', $scale->scale);
810 $grademax = end($scale_items) . ' (' . count($scale_items) . ')';
811 } elseif ($item->is_external_item()) {
812 $grademax = format_float($item->grademax, $item->get_decimals());
814 $grademax = '<input type="text" size="6" id="grademax'.$item->id.'" name="grademax_'.$item->id.'" value="'.format_float($item->grademax, $item->get_decimals()).'" />';
817 $itemcell = clone($this->itemcell);
818 $itemcell->text = $grademax;
822 public function is_hidden($mode='simple') {
824 if ($mode == 'simple') {
825 return strstr($CFG->grade_item_advanced, 'grademax');
826 } elseif ($mode == 'advanced') {
832 class grade_edit_tree_column_aggregateonlygraded extends grade_edit_tree_column_category {
834 public function __construct($params) {
835 parent::__construct('aggregateonlygraded');
838 public function get_header_cell() {
840 $headercell = clone($this->headercell);
841 $headercell->style .= 'width: 40px;';
842 $headercell->text = get_string('aggregateonlygraded', 'grades')
843 . $OUTPUT->help_icon('aggregateonlygraded', 'grades');
847 public function get_category_cell($category, $levelclass, $params) {
848 $onlygradedcheck = ($category->aggregateonlygraded == 1) ? 'checked="checked"' : '';
849 $hidden = '<input type="hidden" name="aggregateonlygraded_'.$category->id.'" value="0" />';
850 $aggregateonlygraded ='<input type="checkbox" id="aggregateonlygraded_'.$category->id.'" name="aggregateonlygraded_'.$category->id.'" value="1" '.$onlygradedcheck . ' />';
853 $aggregateonlygraded = ($category->aggregateonlygraded) ? get_string('yes') : get_string('no');
856 $categorycell = clone($this->categorycell);
857 $categorycell->attributes['class'] .= ' ' . $levelclass;
858 $categorycell->text = $hidden.$aggregateonlygraded;
859 return $categorycell;
862 public function get_item_cell($item, $params) {
863 $itemcell = clone($this->itemcell);
864 $itemcell->text = ' - ';
869 class grade_edit_tree_column_aggregatesubcats extends grade_edit_tree_column_category {
871 public function __construct($params) {
872 parent::__construct('aggregatesubcats');
875 public function get_header_cell() {
877 $headercell = clone($this->headercell);
878 $headercell->style .= 'width: 40px;';
879 $headercell->text = get_string('aggregatesubcats', 'grades')
880 .$OUTPUT->help_icon('aggregatesubcats', 'grades');
884 public function get_category_cell($category, $levelclass, $params) {
885 $subcatscheck = ($category->aggregatesubcats == 1) ? 'checked="checked"' : '';
886 $hidden = '<input type="hidden" name="aggregatesubcats_'.$category->id.'" value="0" />';
887 $aggregatesubcats = '<input type="checkbox" id="aggregatesubcats_'.$category->id.'" name="aggregatesubcats_'.$category->id.'" value="1" ' . $subcatscheck.' />';
890 $aggregatesubcats = ($category->aggregatesubcats) ? get_string('yes') : get_string('no');
893 $categorycell = clone($this->categorycell);
894 $categorycell->attributes['class'] .= ' ' . $levelclass;
895 $categorycell->text = $hidden.$aggregatesubcats;
896 return $categorycell;
900 public function get_item_cell($item, $params) {
901 $itemcell = clone($this->itemcell);
902 $itemcell->text = ' - ';
907 class grade_edit_tree_column_aggregateoutcomes extends grade_edit_tree_column_category {
909 public function __construct($params) {
910 parent::__construct('aggregateoutcomes');
913 public function get_header_cell() {
915 $headercell = clone($this->headercell);
916 $headercell->style .= 'width: 40px;';
917 $headercell->text = get_string('aggregateoutcomes', 'grades')
918 .$OUTPUT->help_icon('aggregateoutcomes', 'grades');
922 public function get_category_cell($category, $levelclass, $params) {
923 $outcomescheck = ($category->aggregateoutcomes == 1) ? 'checked="checked"' : '';
924 $hidden = '<input type="hidden" name="aggregateoutcomes_'.$category->id.'" value="0" />';
925 $aggregateoutcomes = '<input type="checkbox" id="aggregateoutcomes_'.$category->id.'" name="aggregateoutcomes_'.$category->id.'" value="1" ' . $outcomescheck.' />';
928 $aggregateoutcomes = ($category->aggregateoutcomes) ? get_string('yes') : get_string('no');
931 $categorycell = clone($this->categorycell);
932 $categorycell->attributes['class'] .= ' ' . $levelclass;
933 $categorycell->text = $hidden.$aggregateoutcomes;
934 return $categorycell;
937 public function get_item_cell($item, $params) {
938 $itemcell = clone($this->itemcell);
939 $itemcell->text = ' - ';
943 public function is_hidden($mode='simple') {
945 if ($CFG->enableoutcomes) {
946 return parent::is_hidden($mode);
953 class grade_edit_tree_column_droplow extends grade_edit_tree_column_category {
955 public function __construct($params) {
956 parent::__construct('droplow');
959 public function get_header_cell() {
961 $headercell = clone($this->headercell);
962 $headercell->text = get_string('droplow', 'grades').$OUTPUT->help_icon('droplow', 'grades');
966 public function get_category_cell($category, $levelclass, $params) {
967 $droplow = '<input type="text" size="3" id="droplow_'.$category->id.'" name="droplow_'.$category->id.'" value="'.$category->droplow.'" />';
970 $droplow = $category->droplow;
973 $categorycell = clone($this->categorycell);
974 $categorycell->attributes['class'] .= ' ' . $levelclass;
975 $categorycell->text = $droplow;
976 return $categorycell;
979 public function get_item_cell($item, $params) {
980 $itemcell = clone($this->itemcell);
981 $itemcell->text = ' - ';
986 class grade_edit_tree_column_keephigh extends grade_edit_tree_column_category {
988 public function __construct($params) {
989 parent::__construct('keephigh');
992 public function get_header_cell() {
994 $headercell = clone($this->headercell);
995 $headercell->text = get_string('keephigh', 'grades').$OUTPUT->help_icon('keephigh', 'grades');
999 public function get_category_cell($category, $levelclass, $params) {
1000 $keephigh = '<input type="text" size="3" id="keephigh_'.$category->id.'" name="keephigh_'.$category->id.'" value="'.$category->keephigh.'" />';
1002 if ($this->forced) {
1003 $keephigh = $category->keephigh;
1006 $categorycell = clone($this->categorycell);
1007 $categorycell->attributes['class'] .= ' ' . $levelclass;
1008 $categorycell->text = $keephigh;
1009 return $categorycell;
1012 public function get_item_cell($item, $params) {
1013 $itemcell = clone($this->itemcell);
1014 $itemcell->text = ' - ';
1019 class grade_edit_tree_column_multfactor extends grade_edit_tree_column {
1021 public function __construct($params) {
1022 parent::__construct();
1025 public function get_header_cell() {
1027 $headercell = clone($this->headercell);
1028 $headercell->text = get_string('multfactor', 'grades').$OUTPUT->help_icon('multfactor', 'grades');
1032 public function get_category_cell($category, $levelclass, $params) {
1033 $categorycell = clone($this->categorycell);
1034 $categorycell->attributes['class'] .= ' ' . $levelclass;
1035 $categorycell->text = ' - ';
1036 return $categorycell;
1039 public function get_item_cell($item, $params) {
1042 $itemcell = clone($this->itemcell);
1043 if (!$item->is_raw_used()) {
1044 $itemcell->text = ' ';
1048 $multfactor = '<input type="text" size="4" id="multfactor'.$item->id.'" name="multfactor_'.$item->id.'" value="'.grade_edit_tree::format_number($item->multfactor).'" />';
1050 $itemcell->text = $multfactor;
1054 public function is_hidden($mode='simple') {
1056 if ($mode == 'simple') {
1057 return strstr($CFG->grade_item_advanced, 'multfactor');
1058 } elseif ($mode == 'advanced') {
1064 class grade_edit_tree_column_plusfactor extends grade_edit_tree_column {
1066 public function get_header_cell() {
1068 $headercell = clone($this->headercell);
1069 $headercell->text = get_string('plusfactor', 'grades').$OUTPUT->help_icon('plusfactor', 'grades');
1073 public function get_category_cell($category, $levelclass, $params) {
1074 $categorycell = clone($this->categorycell);
1075 $categorycell->attributes['class'] .= ' ' . $levelclass;
1076 $categorycell->text = ' - ';
1077 return $categorycell;
1081 public function get_item_cell($item, $params) {
1084 $itemcell = clone($this->itemcell);
1085 if (!$item->is_raw_used()) {
1086 $itemcell->text = ' ';
1090 $plusfactor = '<input type="text" size="4" id="plusfactor_'.$item->id.'" name="plusfactor_'.$item->id.'" value="'.grade_edit_tree::format_number($item->plusfactor).'" />';
1092 $itemcell->text = $plusfactor;
1097 public function is_hidden($mode='simple') {
1099 if ($mode == 'simple') {
1100 return strstr($CFG->grade_item_advanced, 'plusfactor');
1101 } elseif ($mode == 'advanced') {
1107 class grade_edit_tree_column_actions extends grade_edit_tree_column {
1109 public function __construct($params) {
1110 parent::__construct();
1113 public function get_header_cell() {
1114 $headercell = clone($this->headercell);
1115 $headercell->attributes['class'] .= ' actions';
1116 $headercell->text = get_string('actions');
1120 public function get_category_cell($category, $levelclass, $params) {
1122 if (empty($params['actions'])) {
1123 throw new Exception('Array key (actions) missing from 3rd param of grade_edit_tree_column_actions::get_category_actions($category, $levelclass, $params)');
1126 $categorycell = clone($this->categorycell);
1127 $categorycell->attributes['class'] .= ' ' . $levelclass;
1128 $categorycell->text = $params['actions'];
1129 return $categorycell;
1132 public function get_item_cell($item, $params) {
1133 if (empty($params['actions'])) {
1134 throw new Exception('Array key (actions) missing from 2nd param of grade_edit_tree_column_actions::get_item_cell($item, $params)');
1136 $itemcell = clone($this->itemcell);
1137 $itemcell->attributes['class'] .= ' actions';
1138 $itemcell->text = $params['actions'];
1142 public function is_hidden($mode='simple') {
1147 class grade_edit_tree_column_select extends grade_edit_tree_column {
1149 public function get_header_cell() {
1150 $headercell = clone($this->headercell);
1151 $headercell->attributes['class'] .= ' selection';
1152 $headercell->text = get_string('select');
1156 public function get_category_cell($category, $levelclass, $params) {
1158 if (empty($params['eid'])) {
1159 throw new Exception('Array key (eid) missing from 3rd param of grade_edit_tree_column_select::get_category_cell($category, $levelclass, $params)');
1161 $selectall = new action_link(new moodle_url('#'), get_string('all'), new component_action('click', 'togglecheckboxes', array('eid' => $params['eid'], 'check' => true)));
1162 $selectnone = new action_link(new moodle_url('#'), get_string('none'), new component_action('click', 'togglecheckboxes', array('eid' => $params['eid'], 'check' => false)));
1164 $categorycell = clone($this->categorycell);
1165 $categorycell->attributes['class'] .= ' last ' . $levelclass;
1166 $categorycell->style .= 'text-align: center;';
1167 $categorycell->text = $OUTPUT->render($selectall) . '<br />' . $OUTPUT->render($selectnone);
1168 return $categorycell;
1171 public function get_item_cell($item, $params) {
1172 if (empty($params['itemtype']) || empty($params['eid'])) {
1173 error('Array key (itemtype or eid) missing from 2nd param of grade_edit_tree_column_select::get_item_cell($item, $params)');
1177 if ($params['itemtype'] != 'course' && $params['itemtype'] != 'category') {
1178 $itemselect = '<input class="itemselect" type="checkbox" name="select_'.$params['eid'].'" onchange="toggleCategorySelector();"/>'; // TODO: convert to YUI handler
1180 //html_writer::table() will wrap the item cell contents in a <TD> so don't do it here
1184 public function is_hidden($mode='simple') {