MDL-29226 RTL fixes - additions and amendments
[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;
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 across 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     //Trims 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 = null;
797             if (empty($scale)) { //if the item is using a scale that's been removed
798                 $scale_items = array();
799             } else {
800                 $scale_items = explode(',', $scale->scale);
801             }
802             $grademax = end($scale_items) . ' (' . count($scale_items) . ')';
803         } elseif ($item->is_external_item()) {
804             $grademax = format_float($item->grademax, $item->get_decimals());
805         } else {
806             $grademax = '<input type="text" size="6" id="grademax'.$item->id.'" name="grademax_'.$item->id.'" value="'.format_float($item->grademax, $item->get_decimals()).'" />';
807         }
809         $itemcell = clone($this->itemcell);
810         $itemcell->text = $grademax;
811         return $itemcell;
812     }
814     public function is_hidden($mode='simple') {
815         global $CFG;
816         if ($mode == 'simple') {
817             return strstr($CFG->grade_item_advanced, 'grademax');
818         } elseif ($mode == 'advanced') {
819             return false;
820         }
821     }
824 class grade_edit_tree_column_aggregateonlygraded extends grade_edit_tree_column_category {
826     public function __construct($params) {
827         parent::__construct('aggregateonlygraded');
828     }
830     public function get_header_cell() {
831         global $OUTPUT;
832         $headercell = clone($this->headercell);
833         $headercell->style .= 'width: 40px;';
834         $headercell->text = get_string('aggregateonlygraded', 'grades')
835                 . $OUTPUT->help_icon('aggregateonlygraded', 'grades');
836         return $headercell;
837     }
839     public function get_category_cell($category, $levelclass, $params) {
840         $onlygradedcheck = ($category->aggregateonlygraded == 1) ? 'checked="checked"' : '';
841         $hidden = '<input type="hidden" name="aggregateonlygraded_'.$category->id.'" value="0" />';
842         $aggregateonlygraded ='<input type="checkbox" id="aggregateonlygraded_'.$category->id.'" name="aggregateonlygraded_'.$category->id.'" value="1" '.$onlygradedcheck . ' />';
844         if ($this->forced) {
845             $aggregateonlygraded = ($category->aggregateonlygraded) ? get_string('yes') : get_string('no');
846         }
848         $categorycell = clone($this->categorycell);
849         $categorycell->attributes['class'] .= ' ' . $levelclass;
850         $categorycell->text = $hidden.$aggregateonlygraded;
851         return $categorycell;
852     }
854     public function get_item_cell($item, $params) {
855         $itemcell = clone($this->itemcell);
856         $itemcell->text = ' - ';
857         return $itemcell;
858     }
861 class grade_edit_tree_column_aggregatesubcats extends grade_edit_tree_column_category {
863     public function __construct($params) {
864         parent::__construct('aggregatesubcats');
865     }
867     public function get_header_cell() {
868         global $OUTPUT;
869         $headercell = clone($this->headercell);
870         $headercell->style .= 'width: 40px;';
871         $headercell->text = get_string('aggregatesubcats', 'grades')
872               .$OUTPUT->help_icon('aggregatesubcats', 'grades');
873         return $headercell;
874     }
876     public function get_category_cell($category, $levelclass, $params) {
877         $subcatscheck = ($category->aggregatesubcats == 1) ? 'checked="checked"' : '';
878         $hidden = '<input type="hidden" name="aggregatesubcats_'.$category->id.'" value="0" />';
879         $aggregatesubcats = '<input type="checkbox" id="aggregatesubcats_'.$category->id.'" name="aggregatesubcats_'.$category->id.'" value="1" ' . $subcatscheck.' />';
881         if ($this->forced) {
882             $aggregatesubcats = ($category->aggregatesubcats) ? get_string('yes') : get_string('no');
883         }
885         $categorycell = clone($this->categorycell);
886         $categorycell->attributes['class'] .= ' ' . $levelclass;
887         $categorycell->text = $hidden.$aggregatesubcats;
888         return $categorycell;
890     }
892     public function get_item_cell($item, $params) {
893         $itemcell = clone($this->itemcell);
894         $itemcell->text = ' - ';
895         return $itemcell;
896     }
899 class grade_edit_tree_column_aggregateoutcomes extends grade_edit_tree_column_category {
901     public function __construct($params) {
902         parent::__construct('aggregateoutcomes');
903     }
905     public function get_header_cell() {
906         global $OUTPUT;
907         $headercell = clone($this->headercell);
908         $headercell->style .= 'width: 40px;';
909         $headercell->text = get_string('aggregateoutcomes', 'grades')
910               .$OUTPUT->help_icon('aggregateoutcomes', 'grades');
911         return $headercell;
912     }
914     public function get_category_cell($category, $levelclass, $params) {
915         $outcomescheck = ($category->aggregateoutcomes == 1) ? 'checked="checked"' : '';
916         $hidden = '<input type="hidden" name="aggregateoutcomes_'.$category->id.'" value="0" />';
917         $aggregateoutcomes = '<input type="checkbox" id="aggregateoutcomes_'.$category->id.'" name="aggregateoutcomes_'.$category->id.'" value="1" ' . $outcomescheck.' />';
919         if ($this->forced) {
920             $aggregateoutcomes = ($category->aggregateoutcomes) ? get_string('yes') : get_string('no');
921         }
923         $categorycell = clone($this->categorycell);
924         $categorycell->attributes['class'] .= ' ' . $levelclass;
925         $categorycell->text = $hidden.$aggregateoutcomes;
926         return $categorycell;
927     }
929     public function get_item_cell($item, $params) {
930         $itemcell = clone($this->itemcell);
931         $itemcell->text = ' - ';
932         return $itemcell;
933     }
935     public function is_hidden($mode='simple') {
936         global $CFG;
937         if ($CFG->enableoutcomes) {
938             return parent::is_hidden($mode);
939         } else {
940             return true;
941         }
942     }
945 class grade_edit_tree_column_droplow extends grade_edit_tree_column_category {
947     public function __construct($params) {
948         parent::__construct('droplow');
949     }
951     public function get_header_cell() {
952         global $OUTPUT;
953         $headercell = clone($this->headercell);
954         $headercell->text = get_string('droplow', 'grades').$OUTPUT->help_icon('droplow', 'grades');
955         return $headercell;
956     }
958     public function get_category_cell($category, $levelclass, $params) {
959         $droplow = '<input type="text" size="3" id="droplow_'.$category->id.'" name="droplow_'.$category->id.'" value="'.$category->droplow.'" />';
961         if ($this->forced) {
962             $droplow = $category->droplow;
963         }
965         $categorycell = clone($this->categorycell);
966         $categorycell->attributes['class']  .= ' ' . $levelclass;
967         $categorycell->text = $droplow;
968         return $categorycell;
969     }
971     public function get_item_cell($item, $params) {
972         $itemcell = clone($this->itemcell);
973         $itemcell->text = ' - ';
974         return $itemcell;
975     }
978 class grade_edit_tree_column_keephigh extends grade_edit_tree_column_category {
980     public function __construct($params) {
981         parent::__construct('keephigh');
982     }
984     public function get_header_cell() {
985         global $OUTPUT;
986         $headercell = clone($this->headercell);
987         $headercell->text = get_string('keephigh', 'grades').$OUTPUT->help_icon('keephigh', 'grades');
988         return $headercell;
989     }
991     public function get_category_cell($category, $levelclass, $params) {
992         $keephigh = '<input type="text" size="3" id="keephigh_'.$category->id.'" name="keephigh_'.$category->id.'" value="'.$category->keephigh.'" />';
994         if ($this->forced) {
995             $keephigh = $category->keephigh;
996         }
998         $categorycell = clone($this->categorycell);
999         $categorycell->attributes['class'] .= ' ' . $levelclass;
1000         $categorycell->text = $keephigh;
1001         return $categorycell;
1002     }
1004     public function get_item_cell($item, $params) {
1005         $itemcell = clone($this->itemcell);
1006         $itemcell->text = ' - ';
1007         return $itemcell;
1008     }
1011 class grade_edit_tree_column_multfactor extends grade_edit_tree_column {
1013     public function __construct($params) {
1014         parent::__construct();
1015     }
1017     public function get_header_cell() {
1018         global $OUTPUT;
1019         $headercell = clone($this->headercell);
1020         $headercell->text = get_string('multfactor', 'grades').$OUTPUT->help_icon('multfactor', 'grades');
1021         return $headercell;
1022     }
1024     public function get_category_cell($category, $levelclass, $params) {
1025         $categorycell = clone($this->categorycell);
1026         $categorycell->attributes['class'] .= ' ' . $levelclass;
1027         $categorycell->text = ' - ';
1028         return $categorycell;
1029     }
1031     public function get_item_cell($item, $params) {
1032         global $OUTPUT;
1034         $itemcell = clone($this->itemcell);
1035         if (!$item->is_raw_used()) {
1036             $itemcell->text = '&nbsp;';
1037             return $itemcell;
1038         }
1040         $multfactor = '<input type="text" size="4" id="multfactor'.$item->id.'" name="multfactor_'.$item->id.'" value="'.grade_edit_tree::format_number($item->multfactor).'" />';
1042         $itemcell->text = $multfactor;
1043         return $itemcell;
1044     }
1046     public function is_hidden($mode='simple') {
1047         global $CFG;
1048         if ($mode == 'simple') {
1049             return strstr($CFG->grade_item_advanced, 'multfactor');
1050         } elseif ($mode == 'advanced') {
1051             return false;
1052         }
1053     }
1056 class grade_edit_tree_column_plusfactor extends grade_edit_tree_column {
1058     public function get_header_cell() {
1059         global $OUTPUT;
1060         $headercell = clone($this->headercell);
1061         $headercell->text = get_string('plusfactor', 'grades').$OUTPUT->help_icon('plusfactor', 'grades');
1062         return $headercell;
1063     }
1065     public function get_category_cell($category, $levelclass, $params) {
1066         $categorycell = clone($this->categorycell);
1067         $categorycell->attributes['class'] .= ' ' . $levelclass;
1068         $categorycell->text = ' - ';
1069         return $categorycell;
1071     }
1073     public function get_item_cell($item, $params) {
1074         global $OUTPUT;
1076         $itemcell = clone($this->itemcell);
1077         if (!$item->is_raw_used()) {
1078             $itemcell->text = '&nbsp;';
1079             return $itemcell;
1080         }
1082         $plusfactor = '<input type="text" size="4" id="plusfactor_'.$item->id.'" name="plusfactor_'.$item->id.'" value="'.grade_edit_tree::format_number($item->plusfactor).'" />';
1084         $itemcell->text = $plusfactor;
1085         return $itemcell;
1087     }
1089     public function is_hidden($mode='simple') {
1090         global $CFG;
1091         if ($mode == 'simple') {
1092             return strstr($CFG->grade_item_advanced, 'plusfactor');
1093         } elseif ($mode == 'advanced') {
1094             return false;
1095         }
1096     }
1099 class grade_edit_tree_column_actions extends grade_edit_tree_column {
1101     public function __construct($params) {
1102         parent::__construct();
1103     }
1105     public function get_header_cell() {
1106         $headercell = clone($this->headercell);
1107         $headercell->attributes['class'] .= ' actions';
1108         $headercell->text = get_string('actions');
1109         return $headercell;
1110     }
1112     public function get_category_cell($category, $levelclass, $params) {
1114         if (empty($params['actions'])) {
1115             throw new Exception('Array key (actions) missing from 3rd param of grade_edit_tree_column_actions::get_category_actions($category, $levelclass, $params)');
1116         }
1118         $categorycell = clone($this->categorycell);
1119         $categorycell->attributes['class'] .= ' ' . $levelclass;
1120         $categorycell->text = $params['actions'];
1121         return $categorycell;
1122     }
1124     public function get_item_cell($item, $params) {
1125         if (empty($params['actions'])) {
1126             throw new Exception('Array key (actions) missing from 2nd param of grade_edit_tree_column_actions::get_item_cell($item, $params)');
1127         }
1128         $itemcell = clone($this->itemcell);
1129         $itemcell->attributes['class'] .= ' actions';
1130         $itemcell->text = $params['actions'];
1131         return $itemcell;
1132     }
1134     public function is_hidden($mode='simple') {
1135         return false;
1136     }
1139 class grade_edit_tree_column_select extends grade_edit_tree_column {
1141     public function get_header_cell() {
1142         $headercell = clone($this->headercell);
1143         $headercell->attributes['class'] .= ' selection';
1144         $headercell->text = get_string('select');
1145         return $headercell;
1146     }
1148     public function get_category_cell($category, $levelclass, $params) {
1149         global $OUTPUT;
1150         if (empty($params['eid'])) {
1151             throw new Exception('Array key (eid) missing from 3rd param of grade_edit_tree_column_select::get_category_cell($category, $levelclass, $params)');
1152         }
1153         $selectall  = new action_link(new moodle_url('#'), get_string('all'), new component_action('click', 'togglecheckboxes', array('eid' => $params['eid'], 'check' => true)));
1154         $selectnone = new action_link(new moodle_url('#'), get_string('none'), new component_action('click', 'togglecheckboxes', array('eid' => $params['eid'], 'check' => false)));
1156         $categorycell = clone($this->categorycell);
1157         $categorycell->attributes['class'] .= ' last ' . $levelclass;
1158         $categorycell->style .= 'text-align: center;';
1159         $categorycell->text = $OUTPUT->render($selectall) . '<br />' . $OUTPUT->render($selectnone);
1160         return $categorycell;
1161     }
1163     public function get_item_cell($item, $params) {
1164         if (empty($params['itemtype']) || empty($params['eid'])) {
1165             error('Array key (itemtype or eid) missing from 2nd param of grade_edit_tree_column_select::get_item_cell($item, $params)');
1166         }
1167         $itemselect = '';
1169         if ($params['itemtype'] != 'course' && $params['itemtype'] != 'category') {
1170             $itemselect = '<input class="itemselect" type="checkbox" name="select_'.$params['eid'].'" onchange="toggleCategorySelector();"/>'; // TODO: convert to YUI handler
1171         }
1172         //html_writer::table() will wrap the item cell contents in a <TD> so don't do it here
1173         return $itemselect;
1174     }
1176     public function is_hidden($mode='simple') {
1177         return false;
1178     }