gradebook MDL-23042 missing help topic being reported due to incorrect component...
[moodle.git] / grade / edit / tree / lib.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 class grade_edit_tree {
19     public $columns = array();
21     /**
22      * @var object $gtree          @see grade/lib.php
23      */
24     public $gtree;
26     /**
27      * @var grade_plugin_return @see grade/lib.php
28      */
29     public $gpr;
31     /**
32      * @var string              $moving The eid of the category or item being moved
33      */
34     public $moving;
36     public $deepest_level;
38     public $uses_extra_credit = false;
40     public $uses_weight = false;
42     public $table;
44     public $categories = array();
45     /**
46      * Constructor
47      */
48     public function __construct($gtree, $moving=false, $gpr) {
49         global $USER, $OUTPUT, $COURSE;
51         $this->gtree = $gtree;
52         $this->moving = $moving;
53         $this->gpr = $gpr;
54         $this->deepest_level = $this->get_deepest_level($this->gtree->top_element);
56         $this->columns = array(grade_edit_tree_column::factory('name', array('deepest_level' => $this->deepest_level)),
57                                grade_edit_tree_column::factory('aggregation', array('flag' => true)));
59         if ($this->uses_weight) {
60             $this->columns[] = grade_edit_tree_column::factory('weight', array('adv' => 'aggregationcoef'));
61         }
62         if ($this->uses_extra_credit) {
63             $this->columns[] = grade_edit_tree_column::factory('extracredit', array('adv' => 'aggregationcoef'));
64         }
66         $this->columns[] = grade_edit_tree_column::factory('range'); // This is not a setting... How do we deal with it?
67         $this->columns[] = grade_edit_tree_column::factory('aggregateonlygraded', array('flag' => true));
68         $this->columns[] = grade_edit_tree_column::factory('aggregatesubcats', array('flag' => true));
69         $this->columns[] = grade_edit_tree_column::factory('aggregateoutcomes', array('flag' => true));
70         $this->columns[] = grade_edit_tree_column::factory('droplow', array('flag' => true));
71         $this->columns[] = grade_edit_tree_column::factory('keephigh', array('flag' => true));
72         $this->columns[] = grade_edit_tree_column::factory('multfactor', array('adv' => true));
73         $this->columns[] = grade_edit_tree_column::factory('plusfactor', array('adv' => true));
74         $this->columns[] = grade_edit_tree_column::factory('actions');
75         $this->columns[] = grade_edit_tree_column::factory('select');
77         $mode = ($USER->gradeediting[$COURSE->id]) ? 'advanced' : 'simple';
79         $widthstyle = '';
80         if ($mode == 'simple') {
81             $widthstyle = ' style="width:auto;" ';
82         }
84         $this->table = new html_table();
85         $this->table->id = "grade_edit_tree_table";
86         $this->table->cellpadding = 5;
87         $this->table->attributes['class'] = 'generaltable ' . $mode;
88         $this->table->style = $widthstyle;
90         foreach ($this->columns as $column) {
91             if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
92                 $this->table->head[] = $column->get_header_cell();
93             }
94         }
96         $rowcount = 0;
97         $this->table->data = $this->build_html_tree($this->gtree->top_element, true, array(), 0, $rowcount);
98     }
100     /**
101      * Recursive function for building the table holding the grade categories and items,
102      * with CSS indentation and styles.
103      *
104      * @param array   $element The current tree element being rendered
105      * @param boolean $totals Whether or not to print category grade items (category totals)
106      * @param array   $parents An array of parent categories for the current element (used for indentation and row classes)
107      *
108      * @return string HTML
109      */
110     public function build_html_tree($element, $totals, $parents, $level, &$row_count) {
111         global $CFG, $COURSE, $USER, $OUTPUT;
113         $object = $element['object'];
114         $eid    = $element['eid'];
115         $object->name = $this->gtree->get_element_header($element, true, true, false);
116         $object->stripped_name = $this->gtree->get_element_header($element, false, false, false);
118         $is_category_item = false;
119         if ($element['type'] == 'categoryitem' || $element['type'] == 'courseitem') {
120             $is_category_item = true;
121         }
123         $rowclasses = array();
124         foreach ($parents as $parent_eid) {
125             $rowclasses[] = $parent_eid;
126         }
128         $actions = '';
130         if (!$is_category_item) {
131             $actions .= $this->gtree->get_edit_icon($element, $this->gpr);
132         }
134         $actions .= $this->gtree->get_calculation_icon($element, $this->gpr);
136         if ($element['type'] == 'item' or ($element['type'] == 'category' and $element['depth'] > 1)) {
137             if ($this->element_deletable($element)) {
138                 $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'delete', 'eid' => $eid, 'sesskey' => sesskey()));
139                 $actions .= $OUTPUT->action_icon($aurl, new pix_icon('t/delete', get_string('delete')));
140             }
142             $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'moveselect', 'eid' => $eid, 'sesskey' => sesskey()));
143             $actions .= $OUTPUT->action_icon($aurl, new pix_icon('t/move', get_string('move')));
144         }
146         $actions .= $this->gtree->get_hiding_icon($element, $this->gpr);
147         $actions .= $this->gtree->get_locking_icon($element, $this->gpr);
149         $mode = ($USER->gradeediting[$COURSE->id]) ? 'advanced' : 'simple';
151         $returnrows = array();
152         $root = false;
154         $id = required_param('id', PARAM_INT);
156         /// prepare move target if needed
157         $last = '';
159         /// print the list items now
160         if ($this->moving == $eid) {
161             // do not diplay children
162             $cell = new html_table_cell();
163             $cell->colspan = 12;
164             $cell->attributes['class'] = $element['type'] . ' moving';
165             $cell->text = $object->name.' ('.get_string('move').')';
166             return array(new html_table_row(array($cell)));
167         }
169         if ($element['type'] == 'category') {
170             $level++;
171             $this->categories[$object->id] = $object->stripped_name;
172             $category = grade_category::fetch(array('id' => $object->id));
173             $item = $category->get_grade_item();
175             // Add aggregation coef input if not a course item and if parent category has correct aggregation type
176             $dimmed = ($item->is_hidden()) ? 'dimmed' : '';
178             // Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan)
179             $aggregation_position = grade_get_setting($COURSE->id, 'aggregationposition', $CFG->grade_aggregationposition);
180             $category_total_data = null; // Used if aggregationposition is set to "last", so we can print it last
182             $html_children = array();
184             $row_count = 0;
186             foreach($element['children'] as $child_el) {
187                 $moveto = null;
189                 if (empty($child_el['object']->itemtype)) {
190                     $child_el['object']->itemtype = false;
191                 }
193                 if (($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') && !$totals) {
194                     continue;
195                 }
197                 $child_eid = $child_el['eid'];
198                 $first = '';
200                 if ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
201                     $first = array('first' => 1);
202                     $child_eid = $eid;
203                 }
205                 if ($this->moving && $this->moving != $child_eid) {
207                     $strmove     = get_string('move');
208                     $strmovehere = get_string('movehere');
209                     $actions = ''; // no action icons when moving
211                     $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'move', 'eid' => $this->moving, 'moveafter' => $child_eid, 'sesskey' => sesskey()));
212                     if ($first) {
213                         $aurl->params($first);
214                     }
216                     $cell = new html_table_cell();
217                     $cell->colspan = 12;
218                     
219                     $icon = new pix_icon('movehere', $strmovehere, null, array('class'=>'movetarget'));
220                     $cell->text = $OUTPUT->action_icon($aurl, $icon);
222                     $moveto = new html_table_row(array($cell));
223                 }
225                 $newparents = $parents;
226                 $newparents[] = $eid;
228                 $row_count++;
229                 $child_row_count = 0;
231                 // If moving, do not print course and category totals, but still print the moveto target box
232                 if ($this->moving && ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category')) {
233                     $html_children[] = $moveto;
234                 } elseif ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
235                     // We don't build the item yet because we first need to know the deepest level of categories (for category/name colspans)
236                     $category_total_item = $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count);
237                     if (!$aggregation_position) {
238                         $html_children = array_merge($html_children, $category_total_item);
239                     }
240                 } else {
241                     $html_children = array_merge($html_children, $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count));
242                     if (!empty($moveto)) {
243                         $html_children[] = $moveto;
244                     }
246                     if ($this->moving) {
247                         $row_count++;
248                     }
249                 }
251                 $row_count += $child_row_count;
253                 // If the child is a category, increment row_count by one more (for the extra coloured row)
254                 if ($child_el['type'] == 'category') {
255                     $row_count++;
256                 }
257             }
259             // Print category total at the end if aggregation position is "last" (1)
260             if (!empty($category_total_item) && $aggregation_position) {
261                 $html_children = array_merge($html_children, $category_total_item);
262             }
264             // Determine if we are at the root
265             if (isset($element['object']->grade_item) && $element['object']->grade_item->is_course_item()) {
266                 $root = true;
267             }
269             $row_count_offset = 0;
271             if (empty($category_total_item) && !$this->moving) {
272                 $row_count_offset = -1;
273             }
275             $levelclass = "level$level";
277             $courseclass = '';
278             if ($level == 1) {
279                 $courseclass = 'coursecategory';
280             }
282             $row = new html_table_row();
283             $row->attributes['class'] = $courseclass . ' category ' . $dimmed;
284             foreach ($rowclasses as $class) {
285                 $row->attributes['class'] .= ' ' . $class;
286             }
288             $headercell = new html_table_cell();
289             $headercell->header = true;
290             $headercell->scope = 'row';
291             $headercell->attributes['title'] = $object->stripped_name;
292             $headercell->attributes['class'] = 'cell rowspan ' . $levelclass;
293             $headercell->rowspan = $row_count+1+$row_count_offset;
294             $row->cells[] = $headercell;
296             foreach ($this->columns as $column) {
297                 if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
298                     $row->cells[] = $column->get_category_cell($category, $levelclass, array('id' => $id, 'name' => $object->name, 'level' => $level, 'actions' => $actions, 'eid' => $eid));
299                 }
300             }
302             $returnrows[] = $row;
304             $returnrows = array_merge($returnrows, $html_children);
306             // Print a coloured row to show the end of the category accross the table
307             $endcell = new html_table_cell();
308             $endcell->colspan = (19 - $level);
309             $endcell->attributes['class'] = 'colspan ' . $levelclass;
311             $returnrows[] = new html_table_row(array($endcell));;
313         } else { // Dealing with a grade item
315             $item = grade_item::fetch(array('id' => $object->id));
316             $element['type'] = 'item';
317             $element['object'] = $item;
319             $categoryitemclass = '';
320             if ($item->itemtype == 'category') {
321                 $categoryitemclass = 'categoryitem';
322             }
324             $dimmed = ($item->is_hidden()) ? "dimmed_text" : "";
325             $gradeitemrow = new html_table_row();
326             $gradeitemrow->attributes['class'] = $categoryitemclass . ' item ' . $dimmed;
327             foreach ($rowclasses as $class) {
328                 $gradeitemrow->attributes['class'] .= ' ' . $class;
329             }
331             foreach ($this->columns as $column) {
332                 if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
333                     $gradeitemrow->cells[] = $column->get_item_cell($item, array('id' => $id, 'name' => $object->name, 'level' => $level, 'actions' => $actions,
334                                                                  'element' => $element, 'eid' => $eid, 'itemtype' => $object->itemtype));
335                 }
336             }
338             $returnrows[] = $gradeitemrow;
339         }
341         return $returnrows;
343     }
345     /**
346      * Given a grade_item object, returns a labelled input if an aggregation coefficient (weight or extra credit) applies to it.
347      * @param grade_item $item
348      * @param string type "extra" or "weight": the type of the column hosting the weight input
349      * @return string HTML
350      */
351     function get_weight_input($item, $type) {
352         global $OUTPUT;
354         if (!is_object($item) || get_class($item) !== 'grade_item') {
355             throw new Exception('grade_edit_tree::get_weight_input($item) was given a variable that is not of the required type (grade_item object)');
356             return false;
357         }
359         if ($item->is_course_item()) {
360             return '';
361         }
363         $parent_category = $item->get_parent_category();
364         $parent_category->apply_forced_settings();
365         $aggcoef = $item->get_coefstring();
367         if ((($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoef') && $type == 'weight') ||
368             ($aggcoef == 'aggregationcoefextraweight' && $type == 'extra')) {
369             return '<input type="text" size="6" id="aggregationcoef_'.$item->id.'" name="aggregationcoef_'.$item->id.'"
370                 value="'.grade_edit_tree::format_number($item->aggregationcoef).'" />';
371         } elseif ($aggcoef == 'aggregationcoefextrasum' && $type == 'extra') {
372             $checked = ($item->aggregationcoef > 0) ? 'checked="checked"' : '';
373             return '<input type="hidden" name="extracredit_'.$item->id.'" value="0" />
374                     <input type="checkbox" id="extracredit_'.$item->id.'" name="extracredit_'.$item->id.'" value="1" '."$checked />\n";
375         } else {
376             return '';
377         }
378     }
380     //Trim's trailing zeros
381     //Used on the 'categories and items' page for grade items settings like aggregation co-efficient
382     //Grader report has its own decimal place settings so they are handled elsewhere
383     function format_number($number) {
384         $formatted = rtrim(format_float($number, 4),'0');
385         if (substr($formatted, -1)=='.') { //if last char is the decimal point
386             $formatted .= '0';
387         }
388         return $formatted;
389     }
391     /**
392      * Given an element of the grade tree, returns whether it is deletable or not (only manual grade items are deletable)
393      *
394      * @param array $element
395      * @return bool
396      */
397     function element_deletable($element) {
398         global $COURSE;
400         if ($element['type'] != 'item') {
401             return true;
402         }
404         $grade_item = $element['object'];
406         if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) {
407             return true;
408         }
410         $modinfo = get_fast_modinfo($COURSE);
411         if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) {
412             // module does not exist
413             return true;
414         }
416         return false;
417     }
419     /**
420      * Given the grade tree and an array of element ids (e.g. c15, i42), and expecting the 'moveafter' URL param,
421      * moves the selected items to the requested location. Then redirects the user to the given $returnurl
422      *
423      * @param object $gtree The grade tree (a recursive representation of the grade categories and grade items)
424      * @param array $eids
425      * @param string $returnurl
426      */
427     function move_elements($eids, $returnurl) {
428         $moveafter = required_param('moveafter', PARAM_INT);
430         if (!is_array($eids)) {
431             $eids = array($eids);
432         }
434         if(!$after_el = $this->gtree->locate_element("c$moveafter")) {
435             print_error('invalidelementid', '', $returnurl);
436         }
438         $after = $after_el['object'];
439         $parent = $after;
440         $sortorder = $after->get_sortorder();
442         foreach ($eids as $eid) {
443             if (!$element = $this->gtree->locate_element($eid)) {
444                 print_error('invalidelementid', '', $returnurl);
445             }
446             $object = $element['object'];
448             $object->set_parent($parent->id);
449             $object->move_after_sortorder($sortorder);
450             $sortorder++;
451         }
453         redirect($returnurl, '', 0);
454     }
456     /**
457      * Recurses through the entire grade tree to find and return the maximum depth of the tree.
458      * This should be run only once from the root element (course category), and is used for the
459      * indentation of the Name column's cells (colspan)
460      *
461      * @param array $element An array of values representing a grade tree's element (all grade items in this case)
462      * @param int $level The level of the current recursion
463      * @param int $deepest_level A value passed to each subsequent level of recursion and incremented if $level > $deepest_level
464      * @return int Deepest level
465      */
466     function get_deepest_level($element, $level=0, $deepest_level=1) {
467         $object = $element['object'];
469         $level++;
470         $coefstring = $element['object']->get_coefstring();
471         if ($element['type'] == 'category') {
472             if ($coefstring == 'aggregationcoefweight') {
473                 $this->uses_weight = true;
474             } elseif ($coefstring ==  'aggregationcoefextraweight' || $coefstring == 'aggregationcoefextrasum') {
475                 $this->uses_extra_credit = true;
476             }
478             foreach($element['children'] as $child_el) {
479                 if ($level > $deepest_level) {
480                     $deepest_level = $level;
481                 }
482                 $deepest_level = $this->get_deepest_level($child_el, $level, $deepest_level);
483             }
484         }
486         return $deepest_level;
487     }
490 abstract class grade_edit_tree_column {
491     public $forced;
492     public $hidden;
493     public $forced_hidden;
494     public $advanced_hidden;
495     public $hide_when_moving = true;
496     /**
497      * html_table_cell object used as a template for header cells in all categories.
498      * It must be cloned before being used.
499      * @var html_table_cell $headercell
500      */
501     public $headercell;
502     /**
503      * html_table_cell object used as a template for category cells in all categories.
504      * It must be cloned before being used.
505      * @var html_table_cell $categorycell
506      */
507     public $categorycell;
508     /**
509      * html_table_cell object used as a template for item cells in all categories.
510      * It must be cloned before being used.
511      * @var html_table_cell $itemcell
512      */
513     public $itemcell;
515     public static function factory($name, $params=array()) {
516         $class_name = "grade_edit_tree_column_$name";
517         if (class_exists($class_name)) {
518             return new $class_name($params);
519         }
520     }
522     public abstract function get_header_cell();
524     public abstract function get_category_cell($category, $levelclass, $params);
526     public abstract function get_item_cell($item, $params);
528     public abstract function is_hidden($mode='simple');
530     public function __construct() {
531         $this->headercell = new html_table_cell();
532         $this->headercell->header = true;
533         $this->headercell->style = 'whitespace: normal;';
534         $this->headercell->attributes['class'] = 'header';
536         $this->categorycell = new html_table_cell();
537         $this->categorycell->attributes['class']  = 'cell';
539         $this->itemcell = new html_table_cell();
540         $this->itemcell->attributes['class'] = 'cell';
541     }
544 abstract class grade_edit_tree_column_category extends grade_edit_tree_column {
546     public $forced;
547     public $advanced;
549     public function __construct($name) {
550         global $CFG;
551         $this->forced = (int)$CFG->{"grade_$name"."_flag"} & 1;
552         $this->advanced = (int)$CFG->{"grade_$name"."_flag"} & 2;
553         parent::__construct();
554     }
556     public function is_hidden($mode='simple') {
557         global $CFG;
558         if ($mode == 'simple') {
559             return $this->advanced;
560         } elseif ($mode == 'advanced') {
561             if ($this->forced && $CFG->grade_hideforcedsettings) {
562                 return true;
563             } else {
564                 return false;
565             }
566         }
567     }
570 class grade_edit_tree_column_name extends grade_edit_tree_column {
571     public $forced = false;
572     public $hidden = false;
573     public $forced_hidden = false;
574     public $advanced_hidden = false;
575     public $deepest_level = 1;
576     public $hide_when_moving = false;
578     public function __construct($params) {
579         if (empty($params['deepest_level'])) {
580             throw new Exception('Tried to instantiate a grade_edit_tree_column_name object without the "deepest_level" param!');
581         }
583         $this->deepest_level = $params['deepest_level'];
584         parent::__construct();
585     }
587     public function get_header_cell() {
588         $headercell = clone($this->headercell);
589         $headercell->attributes['class'] .= ' name';
590         $headercell->colspan = $this->deepest_level + 1;
591         $headercell->text = get_string('name');
592         return $headercell;
593     }
595     public function get_category_cell($category, $levelclass, $params) {
596         global $OUTPUT;
597         if (empty($params['name']) || empty($params['level'])) {
598             throw new Exception('Array key (name or level) missing from 3rd param of grade_edit_tree_column_name::get_category_cell($category, $levelclass, $params)');
599         }
600         $categorycell = clone($this->categorycell);
601         $categorycell->attributes['class'] .= ' name ' . $levelclass;
602         $categorycell->colspan = ($this->deepest_level +1) - $params['level'];
603         $categorycell->text = $OUTPUT->heading($params['name'], 4);
604         return $categorycell;
605     }
607     public function get_item_cell($item, $params) {
608         global $CFG;
610         if (empty($params['element']) || empty($params['name']) || empty($params['level'])) {
611             throw new Exception('Array key (name, level or element) missing from 2nd param of grade_edit_tree_column_name::get_item_cell($item, $params)');
612         }
614         $name = $params['name'];
616         $itemcell = clone($this->itemcell);
617         $itemcell->attributes['class'] .= ' name';
618         $itemcell->colspan = ($this->deepest_level + 1) - $params['level'];
619         $itemcell->text = $name;
620         return $itemcell;
621     }
623     public function is_hidden($mode='simple') {
624         return false;
625     }
628 class grade_edit_tree_column_aggregation extends grade_edit_tree_column_category {
630     public function __construct($params) {
631         parent::__construct('aggregation');
632     }
634     public function get_header_cell() {
635         global $OUTPUT;
636         $headercell = clone($this->headercell);
637         $headercell->text = get_string('aggregation', 'grades').$OUTPUT->help_icon('aggregation', 'grades');
638         return $headercell;
639     }
641     public function get_category_cell($category, $levelclass, $params) {
642         global $CFG, $OUTPUT;
643         if (empty($params['id'])) {
644             throw new Exception('Array key (id) missing from 3rd param of grade_edit_tree_column_aggregation::get_category_cell($category, $levelclass, $params)');
645         }
647         $options = array(GRADE_AGGREGATE_MEAN             => get_string('aggregatemean', 'grades'),
648                          GRADE_AGGREGATE_WEIGHTED_MEAN    => get_string('aggregateweightedmean', 'grades'),
649                          GRADE_AGGREGATE_WEIGHTED_MEAN2   => get_string('aggregateweightedmean2', 'grades'),
650                          GRADE_AGGREGATE_EXTRACREDIT_MEAN => get_string('aggregateextracreditmean', 'grades'),
651                          GRADE_AGGREGATE_MEDIAN           => get_string('aggregatemedian', 'grades'),
652                          GRADE_AGGREGATE_MIN              => get_string('aggregatemin', 'grades'),
653                          GRADE_AGGREGATE_MAX              => get_string('aggregatemax', 'grades'),
654                          GRADE_AGGREGATE_MODE             => get_string('aggregatemode', 'grades'),
655                          GRADE_AGGREGATE_SUM              => get_string('aggregatesum', 'grades'));
657         $visible = explode(',', $CFG->grade_aggregations_visible);
658         foreach ($options as $constant => $string) {
659             if (!in_array($constant, $visible) && $constant != $category->aggregation) {
660                 unset($options[$constant]);
661             }
662         }
664         if ($this->forced) {
665             $aggregation = $options[$category->aggregation];
666         } else {
667             $attributes = array();
668             $attributes['id'] = 'aggregation_'.$category->id;
669             $aggregation = html_writer::select($options, 'aggregation_'.$category->id, $category->aggregation, null, $attributes);
670             $action = new component_action('change', 'update_category_aggregation', array('courseid' => $params['id'], 'category' => $category->id, 'sesskey' => sesskey()));
671             $OUTPUT->add_action_handler($action, 'aggregation_'.$category->id);
672         }
674         $categorycell = clone($this->categorycell);
675         $categorycell->attributes['class'] .= ' ' . $levelclass;
676         $categorycell->text = $aggregation;
677         return $categorycell;
679     }
681     public function get_item_cell($item, $params) {
682         $itemcell = clone($this->itemcell);
683         $itemcell->text = ' - ';
684         return $itemcell;
685     }
688 class grade_edit_tree_column_extracredit extends grade_edit_tree_column {
690     public function get_header_cell() {
691         global $OUTPUT;
692         $headercell = clone($this->headercell);
693         $headercell->text = get_string('aggregationcoefextra', 'grades').$OUTPUT->help_icon('aggregationcoefextra', 'grades');
694         return $headercell;
695     }
697     public function get_category_cell($category, $levelclass, $params) {
698         $item = $category->get_grade_item();
699         $categorycell = clone($this->categorycell);
700         $categorycell->attributes['class'] .= ' ' . $levelclass;
701         $categorycell->text = grade_edit_tree::get_weight_input($item, 'extra');
702         return $categorycell;
703     }
705     public function get_item_cell($item, $params) {
706         if (empty($params['element'])) {
707             throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)');
708         }
710         $itemcell = clone($this->itemcell);
711         $itemcell->text = '&nbsp;';
713         if (!in_array($params['element']['object']->itemtype, array('courseitem', 'categoryitem', 'category'))) {
714             $itemcell->text = grade_edit_tree::get_weight_input($item, 'extra');
715         }
717         return $itemcell;
718     }
720     public function is_hidden($mode='simple') {
721         global $CFG;
722         if ($mode == 'simple') {
723             return strstr($CFG->grade_item_advanced, 'aggregationcoef');
724         } elseif ($mode == 'advanced') {
725             return false;
726         }
727     }
730 class grade_edit_tree_column_weight extends grade_edit_tree_column {
732     public function get_header_cell() {
733         global $OUTPUT;
734         $headercell = clone($this->headercell);
735         $headercell->text = get_string('weightuc', 'grades').$OUTPUT->help_icon('aggregationcoefweight', 'grades');
736         return $headercell;
737     }
739     public function get_category_cell($category, $levelclass, $params) {
741         $item = $category->get_grade_item();
742         $categorycell = clone($this->categorycell);
743         $categorycell->attributes['class']  .= ' ' . $levelclass;
744         $categorycell->text = grade_edit_tree::get_weight_input($item, 'weight');
745         return $categorycell;
746     }
748     public function get_item_cell($item, $params) {
749         if (empty($params['element'])) {
750             throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)');
751         }
752         $itemcell = clone($this->itemcell);
753         $itemcell->text = '&nbsp;';
755         if (!in_array($params['element']['object']->itemtype, array('courseitem', 'categoryitem', 'category'))) {
756             $itemcell->text = grade_edit_tree::get_weight_input($item, 'weight');
757         }
759         return $itemcell;
760     }
762     public function is_hidden($mode='simple') {
763         global $CFG;
764         if ($mode == 'simple') {
765             return strstr($CFG->grade_item_advanced, 'aggregationcoef');
766         } elseif ($mode == 'advanced') {
767             return false;
768         }
769     }
772 class grade_edit_tree_column_range extends grade_edit_tree_column {
774     public function get_header_cell() {
775         $headercell = clone($this->headercell);
776         $headercell->text = get_string('maxgrade', 'grades');
777         return $headercell;
778     }
780     public function get_category_cell($category, $levelclass, $params) {
781         $categorycell = clone($this->categorycell);
782         $categorycell->attributes['class'] .= ' range ' . $levelclass;
783         $categorycell->text = ' - ';
784         return $categorycell;
785     }
787     public function get_item_cell($item, $params) {
788         global $DB, $OUTPUT;
790         // If the parent aggregation is Sum of Grades, this cannot be changed
791         $parent_cat = $item->get_parent_category();
792         if ($parent_cat->aggregation == GRADE_AGGREGATE_SUM) {
793             $grademax = format_float($item->grademax, $item->get_decimals());
794         } elseif ($item->gradetype == GRADE_TYPE_SCALE) {
795             $scale = $DB->get_record('scale', array('id' => $item->scaleid));
796             $scale_items = explode(',', $scale->scale);
797             $grademax = end($scale_items) . ' (' . count($scale_items) . ')';
798         } elseif ($item->is_external_item()) {
799             $grademax = format_float($item->grademax, $item->get_decimals());
800         } else {
801             $grademax = '<input type="text" size="4" id="grademax'.$item->id.'" name="grademax_'.$item->id.'" value="'.format_float($item->grademax, $item->get_decimals()).'" />';
802         }
804         $itemcell = clone($this->itemcell);
805         $itemcell->text = $grademax;
806         return $itemcell;
807     }
809     public function is_hidden($mode='simple') {
810         global $CFG;
811         if ($mode == 'simple') {
812             return strstr($CFG->grade_item_advanced, 'grademax');
813         } elseif ($mode == 'advanced') {
814             return false;
815         }
816     }
819 class grade_edit_tree_column_aggregateonlygraded extends grade_edit_tree_column_category {
821     public function __construct($params) {
822         parent::__construct('aggregateonlygraded');
823     }
825     public function get_header_cell() {
826         global $OUTPUT;
827         $headercell = clone($this->headercell);
828         $headercell->style .= 'width: 40px;';
829         $headercell->text = get_string('aggregateonlygraded', 'grades')
830                 . $OUTPUT->help_icon('aggregateonlygraded', 'grades');
831         return $headercell;
832     }
834     public function get_category_cell($category, $levelclass, $params) {
835         $onlygradedcheck = ($category->aggregateonlygraded == 1) ? 'checked="checked"' : '';
836         $hidden = '<input type="hidden" name="aggregateonlygraded_'.$category->id.'" value="0" />';
837         $aggregateonlygraded ='<input type="checkbox" id="aggregateonlygraded_'.$category->id.'" name="aggregateonlygraded_'.$category->id.'" value="1" '.$onlygradedcheck . ' />';
839         if ($this->forced) {
840             $aggregateonlygraded = ($category->aggregateonlygraded) ? get_string('yes') : get_string('no');
841         }
843         $categorycell = clone($this->categorycell);
844         $categorycell->attributes['class'] .= ' ' . $levelclass;
845         $categorycell->text = $hidden.$aggregateonlygraded;
846         return $categorycell;
847     }
849     public function get_item_cell($item, $params) {
850         $itemcell = clone($this->itemcell);
851         $itemcell->text = ' - ';
852         return $itemcell;
853     }
856 class grade_edit_tree_column_aggregatesubcats extends grade_edit_tree_column_category {
858     public function __construct($params) {
859         parent::__construct('aggregatesubcats');
860     }
862     public function get_header_cell() {
863         global $OUTPUT;
864         $headercell = clone($this->headercell);
865         $headercell->style .= 'width: 40px;';
866         $headercell->text = get_string('aggregatesubcats', 'grades')
867               .$OUTPUT->old_help_icon('aggregatesubcats', 'aggregatesubcats', 'grade');
868         return $headercell;
869     }
871     public function get_category_cell($category, $levelclass, $params) {
872         $subcatscheck = ($category->aggregatesubcats == 1) ? 'checked="checked"' : '';
873         $hidden = '<input type="hidden" name="aggregatesubcats_'.$category->id.'" value="0" />';
874         $aggregatesubcats = '<input type="checkbox" id="aggregatesubcats_'.$category->id.'" name="aggregatesubcats_'.$category->id.'" value="1" ' . $subcatscheck.' />';
876         if ($this->forced) {
877             $aggregatesubcats = ($category->aggregatesubcats) ? get_string('yes') : get_string('no');
878         }
880         $categorycell = clone($this->categorycell);
881         $categorycell->attributes['class'] .= ' ' . $levelclass;
882         $categorycell->text = $hidden.$aggregatesubcats;
883         return $categorycell;
885     }
887     public function get_item_cell($item, $params) {
888         $itemcell = clone($this->itemcell);
889         $itemcell->text = ' - ';
890         return $itemcell;
891     }
894 class grade_edit_tree_column_aggregateoutcomes extends grade_edit_tree_column_category {
896     public function __construct($params) {
897         parent::__construct('aggregateoutcomes');
898     }
900     public function get_header_cell() {
901         global $OUTPUT;
902         $headercell = clone($this->headercell);
903         $headercell->style .= 'width: 40px;';
904         $headercell->text = get_string('aggregateoutcomes', 'grades')
905               .$OUTPUT->help_icon('aggregateoutcomes', 'grades');
906         return $headercell;
907     }
909     public function get_category_cell($category, $levelclass, $params) {
910         $outcomescheck = ($category->aggregateoutcomes == 1) ? 'checked="checked"' : '';
911         $hidden = '<input type="hidden" name="aggregateoutcomes_'.$category->id.'" value="0" />';
912         $aggregateoutcomes = '<input type="checkbox" id="aggregateoutcomes_'.$category->id.'" name="aggregateoutcomes_'.$category->id.'" value="1" ' . $outcomescheck.' />';
914         if ($this->forced) {
915             $aggregateoutcomes = ($category->aggregateoutcomes) ? get_string('yes') : get_string('no');
916         }
918         $categorycell = clone($this->categorycell);
919         $categorycell->attributes['class'] .= ' ' . $levelclass;
920         $categorycell->text = $hidden.$aggregateoutcomes;
921         return $categorycell;
922     }
924     public function get_item_cell($item, $params) {
925         $itemcell = clone($this->itemcell);
926         $itemcell->text = ' - ';
927         return $itemcell;
928     }
930     public function is_hidden($mode='simple') {
931         global $CFG;
932         if ($CFG->enableoutcomes) {
933             return parent::is_hidden($mode);
934         } else {
935             return true;
936         }
937     }
940 class grade_edit_tree_column_droplow extends grade_edit_tree_column_category {
942     public function __construct($params) {
943         parent::__construct('droplow');
944     }
946     public function get_header_cell() {
947         global $OUTPUT;
948         $headercell = clone($this->headercell);
949         $headercell->text = get_string('droplow', 'grades').$OUTPUT->help_icon('droplow', 'grades');
950         return $headercell;
951     }
953     public function get_category_cell($category, $levelclass, $params) {
954         $droplow = '<input type="text" size="3" id="droplow_'.$category->id.'" name="droplow_'.$category->id.'" value="'.$category->droplow.'" />';
956         if ($this->forced) {
957             $droplow = $category->droplow;
958         }
960         $categorycell = clone($this->categorycell);
961         $categorycell->attributes['class']  .= ' ' . $levelclass;
962         $categorycell->text = $droplow;
963         return $categorycell;
964     }
966     public function get_item_cell($item, $params) {
967         $itemcell = clone($this->itemcell);
968         $itemcell->text = ' - ';
969         return $itemcell;
970     }
973 class grade_edit_tree_column_keephigh extends grade_edit_tree_column_category {
975     public function __construct($params) {
976         parent::__construct('keephigh');
977     }
979     public function get_header_cell() {
980         global $OUTPUT;
981         $headercell = clone($this->headercell);
982         $headercell->text = get_string('keephigh', 'grades').$OUTPUT->old_help_icon('keephigh', 'keephigh', 'grade');
983         return $headercell;
984     }
986     public function get_category_cell($category, $levelclass, $params) {
987         $keephigh = '<input type="text" size="3" id="keephigh_'.$category->id.'" name="keephigh_'.$category->id.'" value="'.$category->keephigh.'" />';
989         if ($this->forced) {
990             $keephigh = $category->keephigh;
991         }
993         $categorycell = clone($this->categorycell);
994         $categorycell->attributes['class'] .= ' ' . $levelclass;
995         $categorycell->text = $keephigh;
996         return $categorycell;
997     }
999     public function get_item_cell($item, $params) {
1000         $itemcell = clone($this->itemcell);
1001         $itemcell->text = ' - ';
1002         return $itemcell;
1003     }
1006 class grade_edit_tree_column_multfactor extends grade_edit_tree_column {
1008     public function __construct($params) {
1009         parent::__construct();
1010     }
1012     public function get_header_cell() {
1013         global $OUTPUT;
1014         $headercell = clone($this->headercell);
1015         $headercell->text = get_string('multfactor', 'grades').$OUTPUT->help_icon('multfactor', 'grades');
1016         return $headercell;
1017     }
1019     public function get_category_cell($category, $levelclass, $params) {
1020         $categorycell = clone($this->categorycell);
1021         $categorycell->attributes['class'] .= ' ' . $levelclass;
1022         $categorycell->text = ' - ';
1023         return $categorycell;
1024     }
1026     public function get_item_cell($item, $params) {
1027         global $OUTPUT;
1029         $itemcell = clone($this->itemcell);
1030         if (!$item->is_raw_used()) {
1031             $itemcell->text = '&nbsp;';
1032             return $itemcell;
1033         }
1035         $multfactor = '<input type="text" size="4" id="multfactor'.$item->id.'" name="multfactor_'.$item->id.'" value="'.grade_edit_tree::format_number($item->multfactor).'" />';
1037         $itemcell->text = $multfactor;
1038         return $itemcell;
1039     }
1041     public function is_hidden($mode='simple') {
1042         global $CFG;
1043         if ($mode == 'simple') {
1044             return strstr($CFG->grade_item_advanced, 'multfactor');
1045         } elseif ($mode == 'advanced') {
1046             return false;
1047         }
1048     }
1051 class grade_edit_tree_column_plusfactor extends grade_edit_tree_column {
1053     public function get_header_cell() {
1054         global $OUTPUT;
1055         $headercell = clone($this->headercell);
1056         $headercell->text = get_string('plusfactor', 'grades').$OUTPUT->help_icon('plusfactor', 'grades');
1057         return $headercell;
1058     }
1060     public function get_category_cell($category, $levelclass, $params) {
1061         $categorycell = clone($this->categorycell);
1062         $categorycell->attributes['class'] .= ' ' . $levelclass;
1063         $categorycell->text = ' - ';
1064         return $categorycell;
1066     }
1068     public function get_item_cell($item, $params) {
1069         global $OUTPUT;
1071         $itemcell = clone($this->itemcell);
1072         if (!$item->is_raw_used()) {
1073             $itemcell->text = '&nbsp;';
1074             return $itemcell;
1075         }
1077         $plusfactor = '<input type="text" size="4" id="plusfactor_'.$item->id.'" name="plusfactor_'.$item->id.'" value="'.grade_edit_tree::format_number($item->plusfactor).'" />';
1079         $itemcell->text = $plusfactor;
1080         return $itemcell;
1082     }
1084     public function is_hidden($mode='simple') {
1085         global $CFG;
1086         if ($mode == 'simple') {
1087             return strstr($CFG->grade_item_advanced, 'plusfactor');
1088         } elseif ($mode == 'advanced') {
1089             return false;
1090         }
1091     }
1094 class grade_edit_tree_column_actions extends grade_edit_tree_column {
1096     public function __construct($params) {
1097         parent::__construct();
1098     }
1100     public function get_header_cell() {
1101         $headercell = clone($this->headercell);
1102         $headercell->attributes['class'] .= ' actions';
1103         $headercell->text = get_string('actions');
1104         return $headercell;
1105     }
1107     public function get_category_cell($category, $levelclass, $params) {
1109         if (empty($params['actions'])) {
1110             throw new Exception('Array key (actions) missing from 3rd param of grade_edit_tree_column_actions::get_category_actions($category, $levelclass, $params)');
1111         }
1113         $categorycell = clone($this->categorycell);
1114         $categorycell->attributes['class'] .= ' ' . $levelclass;
1115         $categorycell->text = $params['actions'];
1116         return $categorycell;
1117     }
1119     public function get_item_cell($item, $params) {
1120         if (empty($params['actions'])) {
1121             throw new Exception('Array key (actions) missing from 2nd param of grade_edit_tree_column_actions::get_item_cell($item, $params)');
1122         }
1123         $itemcell = clone($this->itemcell);
1124         $itemcell->attributes['class'] .= ' actions';
1125         $itemcell->text = $params['actions'];
1126         return $itemcell;
1127     }
1129     public function is_hidden($mode='simple') {
1130         return false;
1131     }
1134 class grade_edit_tree_column_select extends grade_edit_tree_column {
1136     public function get_header_cell() {
1137         $headercell = clone($this->headercell);
1138         $headercell->attributes['class'] .= ' selection';
1139         $headercell->text = get_string('select');
1140         return $headercell;
1141     }
1143     public function get_category_cell($category, $levelclass, $params) {
1144         global $OUTPUT;
1145         if (empty($params['eid'])) {
1146             throw new Exception('Array key (eid) missing from 3rd param of grade_edit_tree_column_select::get_category_cell($category, $levelclass, $params)');
1147         }
1148         $selectall  = new action_link(new moodle_url('#'), get_string('all'), new component_action('click', 'togglecheckboxes', array('eid' => $params['eid'], 'check' => true)));
1149         $selectnone = new action_link(new moodle_url('#'), get_string('none'), new component_action('click', 'togglecheckboxes', array('eid' => $params['eid'], 'check' => false)));
1151         $categorycell = clone($this->categorycell);
1152         $categorycell->attributes['class'] .= ' last ' . $levelclass;
1153         $categorycell->style .= 'text-align: center;';
1154         $categorycell->text = $OUTPUT->render($selectall) . '<br />' . $OUTPUT->render($selectnone);
1155         return $categorycell;
1156     }
1158     public function get_item_cell($item, $params) {
1159         if (empty($params['itemtype']) || empty($params['eid'])) {
1160             error('Array key (itemtype or eid) missing from 2nd param of grade_edit_tree_column_select::get_item_cell($item, $params)');
1161         }
1162         $itemselect = '';
1164         if ($params['itemtype'] != 'course' && $params['itemtype'] != 'category') {
1165             $itemselect = '<input class="itemselect" type="checkbox" name="select_'.$params['eid'].'" onchange="toggleCategorySelector();"/>'; // TODO: convert to YUI handler
1166         }
1167         return '<td class="cell last selection">' . $itemselect . '</td>';
1168     }
1170     public function is_hidden($mode='simple') {
1171         return false;
1172     }