Index: grade/edit/tree/functions.js
=========================================================
--- grade/edit/tree/functions.js	Tue Feb 03 11:17:31 CET 2009
+++ grade/edit/tree/functions.js	Tue Feb 03 11:17:31 CET 2009
@@ -0,0 +1,28 @@
+/**
+ * Toggles the selection checkboxes of all grade items children of the given eid (a category id)
+ */
+function togglecheckboxes(eid, value) {
+    var rows = YAHOO.util.Dom.getElementsByClassName(eid);
+
+    for (var i = 0; i < rows.length; i++) {
+        var element = new YAHOO.util.Element(rows[i]);
+        var checkboxes = element.getElementsByClassName('itemselect');
+        if (checkboxes[0]) {
+            checkboxes[0].checked=value;
+        }
+    }
+
+}
+
+function toggle_advanced_columns() {
+    var advEls = YAHOO.util.Dom.getElementsByClassName("advanced");
+    var shownAdvEls = YAHOO.util.Dom.getElementsByClassName("advancedshown");
+
+    for (var i = 0; i < advEls.length; i++) {
+        YAHOO.util.Dom.replaceClass(advEls[i], "advanced", "advancedshown");
+    }
+
+    for (var i = 0; i < shownAdvEls.length; i++) {
+        YAHOO.util.Dom.replaceClass(shownAdvEls[i], "advancedshown", "advanced");
+    }
+}
Index: grade/edit/tree/lib.php
=========================================================
--- grade/edit/tree/lib.php	Tue Feb 03 11:17:31 CET 2009
+++ grade/edit/tree/lib.php	Tue Feb 03 11:17:31 CET 2009
@@ -0,0 +1,914 @@
+<?php  // $Id: lib.php,v 1.3 2008/11/05 01:11:35 moodler Exp $
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 1999 onwards  Martin Dougiamas  http://moodle.com       //
+//                                                                       //
+// This program is free software; you can redistribute it and/or modify  //
+// it under the terms of the GNU General Public License as published by  //
+// the Free Software Foundation; either version 2 of the License, or     //
+// (at your option) any later version.                                   //
+//                                                                       //
+// This program is distributed in the hope that it will be useful,       //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of        //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
+// GNU General Public License for more details:                          //
+//                                                                       //
+//          http://www.gnu.org/copyleft/gpl.html                         //
+//                                                                       //
+///////////////////////////////////////////////////////////////////////////
+
+class grade_edit_tree {
+    public $columns = array();
+
+    /**
+     * @var object $gtree          @see grade/lib.php
+     */
+    public $gtree;
+
+    /**
+     * @var grade_plugin_return @see grade/lib.php
+     */
+    public $gpr;
+
+    /**
+     * @var string              $moving The eid of the category or item being moved
+     */
+    public $moving;
+
+    public $deepest_level;
+
+    /**
+     * Constructor
+     */
+    public function __construct($gtree, $moving=false, $gpr) {
+        $this->gtree = $gtree;
+        $this->moving = $moving;
+        $this->gpr = $gpr;
+        $this->deepest_level = $this->get_deepest_level($this->gtree->top_element);
+
+        $this->columns = array(grade_edit_tree_column::factory('name', array('deepest_level' => $this->deepest_level)),
+                               grade_edit_tree_column::factory('aggregation', array('flag' => true)),
+                               grade_edit_tree_column::factory('weightorextracredit', array('adv' => 'aggregationcoef')),
+                               grade_edit_tree_column::factory('range'), // This is not a setting... How do we deal with it?
+                               grade_edit_tree_column::factory('aggregateonlygraded', array('flag' => true)),
+                               grade_edit_tree_column::factory('aggregatesubcats', array('flag' => true)),
+                               grade_edit_tree_column::factory('aggregateoutcomes', array('flag' => true)),
+                               grade_edit_tree_column::factory('droplow', array('flag' => true)),
+                               grade_edit_tree_column::factory('keephigh', array('flag' => true)),
+                               grade_edit_tree_column::factory('multfactor', array('adv' => true)),
+                               grade_edit_tree_column::factory('plusfactor', array('adv' => true)),
+                               grade_edit_tree_column::factory('actions'),
+                               grade_edit_tree_column::factory('select')
+                               );
+    }
+
+    /**
+     * Recursive function for building the table holding the grade categories and items,
+     * with CSS indentation and styles.
+     *
+     * @param array               $element The current tree element being rendered
+     * @param boolean             $totals Whether or not to print category grade items (category totals)
+     * @param array               $parents An array of parent categories for the current element (used for indentation and row classes)
+     *
+     * @return string HTML
+     */
+    public function build_html_tree($element, $totals, $parents=array(), &$categories=array(), $level=0, &$row_count=0) {
+        global $CFG, $COURSE, $USER;
+
+        $object = $element['object'];
+        $eid    = $element['eid'];
+        $object->name = $this->gtree->get_element_header($element, true, true, false);
+        $object->stripped_name = $this->gtree->get_element_header($element, false, false, false);
+
+        $is_category_item = false;
+        if ($element['type'] == 'categoryitem' || $element['type'] == 'courseitem') {
+            $is_category_item = true;
+        }
+
+        $rowclasses = '';
+        foreach ($parents as $parent_eid) {
+            $rowclasses .= " $parent_eid ";
+        }
+
+        $actions = '';
+
+        if (!$is_category_item) {
+            $actions .= $this->gtree->get_edit_icon($element, $this->gpr);
+        }
+
+        $actions .= $this->gtree->get_calculation_icon($element, $this->gpr);
+
+        if ($element['type'] == 'item' or ($element['type'] == 'category' and $element['depth'] > 1)) {
+            if ($this->element_deletable($element)) {
+                $actions .= '<a href="index.php?id='.$COURSE->id.'&amp;action=delete&amp;eid='
+                         . $eid.'&amp;sesskey='.sesskey().'"><img src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt="'
+                         . get_string('delete').'" title="'.get_string('delete').'"/></a>';
+            }
+            $actions .= '<a href="index.php?id='.$COURSE->id.'&amp;action=moveselect&amp;eid='
+                     . $eid.'&amp;sesskey='.sesskey().'"><img src="'.$CFG->pixpath.'/t/move.gif" class="iconsmall" alt="'
+                     . get_string('move').'" title="'.get_string('move').'"/></a>';
+        }
+
+        $actions .= $this->gtree->get_hiding_icon($element, $this->gpr);
+        $actions .= $this->gtree->get_locking_icon($element, $this->gpr);
+
+        $mode = ($USER->gradeediting[$COURSE->id]) ? 'advanced' : 'simple';
+
+        $html = '';
+        $root = false;
+
+
+        $id = required_param('id', PARAM_INT);
+
+        /// prepare move target if needed
+        $last = '';
+
+        /// print the list items now
+        if ($this->moving == $eid) {
+
+            // do not diplay children
+            return '<tr><td colspan="12" class="'.$element['type'].' moving">'.$object->name.' ('.get_string('move').')</td></tr>';
+
+        }
+
+        if ($element['type'] == 'category') {
+            $level++;
+            $categories[$object->id] = $object->stripped_name;
+            $category = grade_category::fetch(array('id' => $object->id));
+            $item = $category->get_grade_item();
+
+            // Add aggregation coef input if not a course item and if parent category has correct aggregation type
+            $dimmed = ($item->hidden) ? " dimmed " : "";
+
+            // Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan)
+            $aggregation_position = grade_get_setting($COURSE->id, 'aggregationposition', $CFG->grade_aggregationposition);
+            $category_total_data = null; // Used if aggregationposition is set to "last", so we can print it last
+
+            $html_children = '';
+
+            $row_count = 0;
+
+            foreach($element['children'] as $child_el) {
+                $moveto = '';
+
+                if (empty($child_el['object']->itemtype)) {
+                    $child_el['object']->itemtype = false;
+                }
+
+                if (($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') && !$totals) {
+                    continue;
+                }
+
+                $child_eid    = $child_el['eid'];
+                $first = '';
+
+                if ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
+                    $first = '&amp;first=1';
+                    $child_eid = $eid;
+                }
+
+                if ($this->moving && $this->moving != $child_eid) {
+
+                    $strmove     = get_string('move');
+                    $strmovehere = get_string('movehere');
+                    $actions = ''; // no action icons when moving
+
+                    $moveto = '<tr><td colspan="12"><a href="index.php?id='.$COURSE->id.'&amp;action=move&amp;eid='.$this->moving.'&amp;moveafter='
+                            . $child_eid.'&amp;sesskey='.sesskey().$first.'"><img class="movetarget" src="'.$CFG->wwwroot.'/pix/movehere.gif" alt="'
+                            . $strmovehere.'" title="'.$strmovehere.'" /></a></td></tr>';
+                }
+
+                $newparents = $parents;
+                $newparents[] = $eid;
+
+                $row_count++;
+                $child_row_count = 0;
+
+                // If moving, do not print course and category totals, but still print the moveto target box
+                if ($this->moving && ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category')) {
+                    $html_children .= $moveto;
+                } elseif ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') {
+                    // We don't build the item yet because we first need to know the deepest level of categories (for category/name colspans)
+                    $category_total_item = $this->build_html_tree($child_el, $totals, $newparents, $categories, $level, $child_row_count);
+                    if (!$aggregation_position) {
+                        $html_children .= $category_total_item;
+                    }
+                } else {
+                    $html_children .= $this->build_html_tree($child_el, $totals, $newparents, $categories, $level, $child_row_count) . $moveto;
+
+                    if ($this->moving) {
+                        $row_count++;
+                    }
+                }
+
+                $row_count += $child_row_count;
+
+                // If the child is a category, increment row_count by one more (for the extra coloured row)
+                if ($child_el['type'] == 'category') {
+                    $row_count++;
+                }
+            }
+
+            // Print category total at the end if aggregation position is "last" (1)
+            if (!empty($category_total_item) && $aggregation_position) {
+                $html_children .= $category_total_item;
+            }
+
+            // now build the header
+            if (isset($element['object']->grade_item) && $element['object']->grade_item->is_course_item()) {
+                // Reduce width if advanced elements are not shown
+                $width_style = '';
+
+                if ($mode == 'simple') {
+                    $width_style = ' style="width:auto;" ';
+                }
+
+                $html .= '<table cellpadding="5" class="generaltable" '.$width_style.'>
+                            <tr>';
+
+                foreach ($this->columns as $column) {
+                    if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
+                        $html .= $column->get_header_cell();
+                    }
+                }
+
+                $html .= '</tr>';
+                $root = true;
+            }
+
+            $row_count_offset = 0;
+
+            if (empty($category_total_item) && !$this->moving) {
+                $row_count_offset = -1;
+            }
+
+            $levelclass = " level$level ";
+
+            $html .= '
+                    <tr class="category '.$dimmed.$rowclasses.'">
+                      <th scope="row" title="'.$object->stripped_name.'" class="cell rowspan '.$levelclass.'" rowspan="'.($row_count+1+$row_count_offset).'"></th>';
+
+            foreach ($this->columns as $column) {
+                if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
+                    $html .= $column->get_category_cell($category, $levelclass, array('id' => $id, 'name' => $object->name, 'level' => $level, 'actions' => $actions, 'eid' => $eid));
+                }
+            }
+
+            $html .= '</tr>';
+
+            $html .= $html_children;
+
+            // Print a coloured row to show the end of the category accross the table
+            $html .= '<tr><td colspan="'.(19 - $level).'" class="colspan '.$levelclass.'"></td></tr>';
+
+        } else { // Dealing with a grade item
+
+            $item = grade_item::fetch(array('id' => $object->id));
+            $element['type'] = 'item';
+            $element['object'] = $item;
+
+            // Determine aggregation coef element
+
+            $dimmed = ($item->hidden) ? " dimmed_text " : "";
+            $html .= '<tr class="item'.$dimmed.$rowclasses.'">';
+
+            foreach ($this->columns as $column) {
+                if (!($this->moving && $column->hide_when_moving) && !$column->is_hidden($mode)) {
+                    $html .= $column->get_item_cell($item, array('id' => $id, 'name' => $object->name, 'level' => $level, 'actions' => $actions,
+                                                                 'element' => $element, 'eid' => $eid, 'itemtype' => $object->itemtype));
+                }
+            }
+
+            $html .= '</tr>';
+        }
+
+
+        if ($root) {
+            $html .= "</table>\n";
+        }
+
+        return $html;
+
+    }
+
+    /**
+     * Given a grade_item object, returns a labelled input if an aggregation coefficient (weight or extra credit) applies to it.
+     * @param grade_item $item
+     * @return string HTML
+     */
+    function get_weight_input($item) {
+        if (!is_object($item) || get_class($item) !== 'grade_item') {
+            throw new Exception('grade_edit_tree::get_weight_input($item) was given a variable that is not of the required type (grade_item object)');
+            return false;
+        }
+
+        if ($item->is_course_item()) {
+            return '';
+        }
+
+        $parent_category = $item->get_parent_category();
+
+        if ($item->is_category_item()) {
+            $parent_category = $parent_category->get_parent_category();
+        }
+
+        $parent_category->apply_forced_settings();
+
+        if ($parent_category->is_aggregationcoef_used()) {
+            $aggcoef = '';
+
+            if ($parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
+                $aggcoef = 'aggregationcoefweight';
+            } elseif ($parent_category->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
+                $aggcoef = 'aggregationcoefextra';
+            } elseif ($parent_category->aggregation == GRADE_AGGREGATE_SUM) {
+                $aggcoef = 'aggregationcoefextrasum';
+            }
+
+            if ($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoefextra') {
+                return '<input type="text" size="6" id="aggregationcoef_'.$item->id.'" name="aggregationcoef_'.$item->id.'"
+                    value="'.format_float($item->aggregationcoef).'" />';
+            } elseif ($aggcoef == 'aggregationcoefextrasum' ) {
+                $checked = ($item->aggregationcoef > 0) ? 'checked="checked"' : '';
+                $extracredit = ($item->aggregationcoef > 0) ? 1 : 0;
+
+                return '<input type="checkbox" id="extracredit_'.$item->id.'" name="extracredit_'.$item->id.'" ' . "$checked />\n"
+                               . '<input type="hidden" name="extracredit_original_'.$item->id.'" value="'.$extracredit.'" />';
+            } else {
+                return '';
+            }
+        }
+    }
+
+    /**
+     * Given an element of the grade tree, returns whether it is deletable or not (only manual grade items are deletable)
+     *
+     * @param array $element
+     * @return bool
+     */
+    function element_deletable($element) {
+        global $COURSE;
+
+        if ($element['type'] != 'item') {
+            return true;
+        }
+
+        $grade_item = $element['object'];
+
+        if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) {
+            return true;
+        }
+
+        $modinfo = get_fast_modinfo($COURSE);
+        if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) {
+            // module does not exist
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Given the grade tree and an array of element ids (e.g. c15, i42), and expecting the 'moveafter' URL param,
+     * moves the selected items to the requested location. Then redirects the user to the given $returnurl
+     *
+     * @param object $gtree The grade tree (a recursive representation of the grade categories and grade items)
+     * @param array $eids
+     * @param string $returnurl
+     */
+    function move_elements($eids, $returnurl) {
+        $moveafter = required_param('moveafter', PARAM_INT);
+
+        if (!is_array($eids)) {
+            $eids = array($eids);
+        }
+
+        if(!$after_el = $this->gtree->locate_element("c$moveafter")) {
+            print_error('invalidelementid', '', $returnurl);
+        }
+
+        $after = $after_el['object'];
+        $parent = $after;
+        $sortorder = $after->get_sortorder();
+
+        foreach ($eids as $eid) {
+            if (!$element = $this->gtree->locate_element($eid)) {
+                print_error('invalidelementid', '', $returnurl);
+            }
+            $object = $element['object'];
+
+            $object->set_parent($parent->id);
+            $object->move_after_sortorder($sortorder);
+        }
+
+        redirect($returnurl, '', 0);
+    }
+
+    /**
+     * Recurses through the entire grade tree to find and return the maximum depth of the tree.
+     * This should be run only once from the root element (course category), and is used for the
+     * indentation of the Name column's cells (colspan)
+     *
+     * @param array $element An array of values representing a grade tree's element (all grade items in this case)
+     * @param int $level The level of the current recursion
+     * @param int $deepest_level A value passed to each subsequent level of recursion and incremented if $level > $deepest_level
+     * @return int Deepest level
+     */
+    function get_deepest_level($element, $level=0, $deepest_level=1) {
+        $object = $element['object'];
+
+        $level++;
+
+        if ($element['type'] == 'category') {
+            foreach($element['children'] as $child_el) {
+                if ($level > $deepest_level) {
+                    $deepest_level = $level;
+                }
+                $deepest_level = $this->get_deepest_level($child_el, $level, $deepest_level);
+            }
+        }
+
+        return $deepest_level;
+    }
+}
+
+abstract class grade_edit_tree_column {
+    public $forced;
+    public $hidden;
+    public $forced_hidden;
+    public $advanced_hidden;
+    public $hide_when_moving = true;
+
+    public static function factory($name, $params=array()) {
+        $class_name = "grade_edit_tree_column_$name";
+        if (class_exists($class_name)) {
+            return new $class_name($params);
+        }
+    }
+
+    public abstract function get_header_cell();
+
+    public abstract function get_category_cell($category, $levelclass, $params);
+
+    public abstract function get_item_cell($item, $params);
+
+    public abstract function is_hidden($mode='simple');
+}
+
+abstract class grade_edit_tree_column_category extends grade_edit_tree_column {
+
+    public $forced;
+    public $advanced;
+
+    public function __construct($name) {
+        global $CFG;
+        $this->forced = (int)$CFG->{"grade_$name"."_flag"} & 1;
+        $this->advanced = (int)$CFG->{"grade_$name"."_flag"} & 2;
+    }
+
+    public function is_hidden($mode='simple') {
+        global $CFG;
+        if ($mode == 'simple') {
+            return $this->advanced;
+        } elseif ($mode == 'advanced') {
+            if ($this->forced && $CFG->grade_hideforcedsettings) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+}
+
+class grade_edit_tree_column_name extends grade_edit_tree_column {
+    public $forced = false;
+    public $hidden = false;
+    public $forced_hidden = false;
+    public $advanced_hidden = false;
+    public $deepest_level = 1;
+    public $hide_when_moving = false;
+
+    public function __construct($params) {
+        if (empty($params['deepest_level'])) {
+            throw new Exception('Tried to instantiate a grade_edit_tree_column_name object without the "deepest_level" param!');
+        }
+
+        $this->deepest_level = $params['deepest_level'];
+    }
+
+    public function get_header_cell() {
+        return '<th class="header name" colspan="'.($this->deepest_level + 1).'" scope="col">'.get_string('name').'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        if (empty($params['name']) || empty($params['level'])) {
+            throw new Exception('Array key (name or level) missing from 3rd param of grade_edit_tree_column_name::get_category_cell($category, $levelclass, $params)');
+        }
+
+        return '<td class="cell name '.$levelclass.'" colspan="'.(($this->deepest_level +1) - $params['level']).'"><h4>' . $params['name'] . "</h4></td>\n";
+    }
+
+    public function get_item_cell($item, $params) {
+        global $CFG;
+
+        if (empty($params['element']) || empty($params['name']) || empty($params['level'])) {
+            throw new Exception('Array key (name, level or element) missing from 2nd param of grade_edit_tree_column_name::get_item_cell($item, $params)');
+        }
+
+        $name = $params['name'];
+
+        return '<td class="cell name" colspan="'.(($this->deepest_level + 1) - $params['level']).'">' . $name . '</td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        return false;
+    }
+}
+
+class grade_edit_tree_column_aggregation extends grade_edit_tree_column_category {
+
+    public function __construct($params) {
+        parent::__construct('aggregation');
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('aggregation', 'grades').helpbutton('aggregation', 'aggregation', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        if (empty($params['id'])) {
+            throw new Exception('Array key (id) missing from 3rd param of grade_edit_tree_column_aggregation::get_category_cell($category, $levelclass, $params)');
+        }
+
+        $options = array(GRADE_AGGREGATE_MEAN             => get_string('aggregatemean', 'grades'),
+                         GRADE_AGGREGATE_WEIGHTED_MEAN    => get_string('aggregateweightedmean', 'grades'),
+                         GRADE_AGGREGATE_WEIGHTED_MEAN2   => get_string('aggregateweightedmean2', 'grades'),
+                         GRADE_AGGREGATE_EXTRACREDIT_MEAN => get_string('aggregateextracreditmean', 'grades'),
+                         GRADE_AGGREGATE_MEDIAN           => get_string('aggregatemedian', 'grades'),
+                         GRADE_AGGREGATE_MIN              => get_string('aggregatemin', 'grades'),
+                         GRADE_AGGREGATE_MAX              => get_string('aggregatemax', 'grades'),
+                         GRADE_AGGREGATE_MODE             => get_string('aggregatemode', 'grades'),
+                         GRADE_AGGREGATE_SUM              => get_string('aggregatesum', 'grades'));
+
+        $script = "window.location='index.php?id={$params['id']}&amp;category={$category->id}&amp;aggregationtype='+this.value";
+        $aggregation = choose_from_menu($options, 'aggregation_'.$category->id, $category->aggregation, get_string('choose'), $script, 0, true);
+
+        if ($this->forced) {
+            $aggregation = $options[$category->aggregation];
+        }
+
+        return '<td class="cell '.$levelclass.'">' . $aggregation . '</td>';
+
+    }
+
+    public function get_item_cell($item, $params) {
+          return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_weightorextracredit extends grade_edit_tree_column {
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('weightorextracredit', 'grades').helpbutton('aggregationcoefweight', 'aggregationcoefweight', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+
+        $item = $category->get_grade_item();
+        $aggcoef_input = grade_edit_tree::get_weight_input($item);
+        return '<td class="cell '.$levelclass.'">' . $aggcoef_input . '</td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        if (empty($params['element'])) {
+            throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)');
+        }
+
+        $aggcoef_input = grade_edit_tree::get_weight_input($item);
+        $html = '<td class="cell">';
+
+        if ($params['element']['type'] == 'categoryitem' || $params['element']['type'] == 'courseitem') {
+            $html .= $aggcoef_input;
+        }
+
+        return $html.'</td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        global $CFG;
+        if ($mode == 'simple') {
+            return strstr($CFG->grade_item_advanced, 'aggregationcoef');
+        } elseif ($mode == 'advanced') {
+            return false;
+        }
+    }
+}
+
+class grade_edit_tree_column_range extends grade_edit_tree_column {
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('maxgrade', 'grades').'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        return '<td class="cell range '.$levelclass.'"> - </td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        if ($item->gradetype == GRADE_TYPE_SCALE) {
+            $scale = get_record('scale', 'id', $item->scaleid);
+            $scale_items = explode(',', $scale->scale);
+            $grademax = end($scale_items) . ' (' . count($scale_items) . ')';
+        } elseif ($item->is_external_item()) {
+            $grademax = format_float($item->grademax, $item->get_decimals());
+        } else {
+            $grademax = '<input type="text" size="3" id="grademax'.$item->id.'" name="grademax'.$item->id.'" value="'.format_float($item->grademax, $item->get_decimals()).'" />';
+        }
+
+        return '<td class="cell">'.$grademax.'</td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        global $CFG;
+        if ($mode == 'simple') {
+            return strstr($CFG->grade_item_advanced, 'grademax');
+        } elseif ($mode == 'advanced') {
+            return false;
+        }
+    }
+}
+
+class grade_edit_tree_column_aggregateonlygraded extends grade_edit_tree_column_category {
+
+    public function __construct($params) {
+        parent::__construct('aggregateonlygraded');
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" style="width: 40px" scope="col">'.get_string('aggregateonlygraded', 'grades')
+              .helpbutton('aggregateonlygraded', 'aggregateonlygraded', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        $onlygradedcheck = ($category->aggregateonlygraded == 1) ? 'checked="checked"' : '';
+        $aggregateonlygraded ='<input type="checkbox" id="aggregateonlygraded_'.$category->id.'" name="aggregateonlygraded_'.$category->id.'" '.$onlygradedcheck . ' />';
+        $hidden = '<input type="hidden" name="aggregateonlygraded_original_'.$category->id.'" value="'.$category->aggregateonlygraded.'" />';
+
+        if ($this->forced) {
+            $aggregateonlygraded = ($category->aggregateonlygraded) ? get_string('yes') : get_string('no');
+        }
+
+        return '<td class="cell '.$levelclass.'">' . $aggregateonlygraded . '</td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_aggregatesubcats extends grade_edit_tree_column_category {
+
+    public function __construct($params) {
+        parent::__construct('aggregatesubcats');
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" style="width: 40px" scope="col">'.get_string('aggregatesubcats', 'grades')
+              .helpbutton('aggregatesubcats', 'aggregatesubcats', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        $subcatscheck = ($category->aggregatesubcats == 1) ? 'checked="checked"' : '';
+        $aggregatesubcats = '<input type="checkbox" id="aggregatesubcats_'.$category->id.'" name="aggregatesubcats_'.$category->id.'" ' . $subcatscheck.' />';
+        $hidden = '<input type="hidden" name="aggregatesubcats_original_'.$category->id.'" value="'.$category->aggregatesubcats.'" />';
+
+        if ($this->forced) {
+            $aggregatesubcats = ($category->aggregatesubcats) ? get_string('yes') : get_string('no');
+        }
+
+        return '<td class="cell '.$levelclass.'">' . $aggregatesubcats . $hidden.'</td>';
+
+    }
+
+    public function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_aggregateoutcomes extends grade_edit_tree_column_category {
+
+    public function __construct($params) {
+        parent::__construct('aggregateoutcomes');
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" style="width: 40px" scope="col">'.get_string('aggregateoutcomes', 'grades')
+              .helpbutton('aggregateoutcomes', 'aggregateoutcomes', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+
+        $outcomescheck = ($category->aggregateoutcomes == 1) ? 'checked="checked"' : '';
+        $aggregateoutcomes = '<input type="checkbox" id="aggregateoutcomes_'.$category->id.'" name="aggregateoutcomes_'.$category->id.'" ' . $outcomescheck.' />';
+        $hidden = '<input type="hidden" name="aggregateoutcomes_original_'.$category->id.'" value="'.$category->aggregateoutcomes.'" />';
+
+        if ($this->forced) {
+            $aggregateoutcomes = ($category->aggregateoutcomes) ? get_string('yes') : get_string('no');
+        }
+
+        return '<td class="cell '.$levelclass.'">' . $aggregateoutcomes . $hidden.'</td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        global $CFG;
+        if ($CFG->enableoutcomes) {
+            return parent::is_hidden($mode);
+        } else {
+            return true;
+        }
+    }
+}
+
+class grade_edit_tree_column_droplow extends grade_edit_tree_column_category {
+
+    public function __construct($params) {
+        parent::__construct('droplow');
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('droplow', 'grades').helpbutton('droplow', 'droplow', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        $droplow = '<input type="text" size="3" id="droplow_'.$category->id.'" name="droplow_'.$category->id.'" value="'.$category->droplow.'" />';
+
+        if ($this->forced) {
+            $droplow = $category->droplow;
+        }
+
+        return '<td class="cell '.$levelclass.'">' . $droplow . '</td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_keephigh extends grade_edit_tree_column_category {
+
+    public function __construct($params) {
+        parent::__construct('keephigh');
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('keephigh', 'grades').helpbutton('keephigh', 'keephigh', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        $keephigh = '<input type="text" size="3" id="keephigh_'.$category->id.'" name="keephigh_'.$category->id.'" value="'.$category->keephigh.'" />';
+
+        if ($this->forced) {
+            $keephigh = $category->keephigh;
+        }
+
+        return '<td class="cell '.$levelclass.'">' . $keephigh . '</td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_multfactor extends grade_edit_tree_column {
+
+    public function __construct($params) {
+
+    }
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('multfactor', 'grades').helpbutton('multfactor', 'multfactor', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+
+        return '<td class="cell '.$levelclass.'"> - </td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        $multfactor = '<input type="text" size="3" id="multfactor'.$item->id.'" name="multfactor'.$item->id.'" value="'.format_float($item->multfactor).'" />';
+        return '<td class="cell">'.$multfactor.'</td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        global $CFG;
+        if ($mode == 'simple') {
+            return strstr($CFG->grade_item_advanced, 'multfactor');
+        } elseif ($mode == 'advanced') {
+            return false;
+        }
+    }
+}
+
+class grade_edit_tree_column_plusfactor extends grade_edit_tree_column {
+
+    public function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('plusfactor', 'grades').helpbutton('plusfactor', 'plusfactor', 'grade', true, false, '', true).'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+        return '<td class="cell '.$levelclass.'"> - </td>';
+
+    }
+
+    public function get_item_cell($item, $params) {
+        $plusfactor = '<input type="text" size="3" id="plusfactor_'.$item->id.'" name="plusfactor_'.$item->id.'" value="'.format_float($item->plusfactor).'" />';
+        return '<td class="cell">'.$plusfactor.'</td>';
+
+    }
+
+    public function is_hidden($mode='simple') {
+        global $CFG;
+        if ($mode == 'simple') {
+            return strstr($CFG->grade_item_advanced, 'plusfactor');
+        } elseif ($mode == 'advanced') {
+            return false;
+        }
+    }
+}
+
+class grade_edit_tree_column_actions extends grade_edit_tree_column {
+
+    public function __construct($params) {
+
+    }
+
+    public function get_header_cell() {
+        return '<th class="header actions" scope="col">'.get_string('actions').'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+
+        if (empty($params['actions'])) {
+            throw new Exception('Array key (actions) missing from 3rd param of grade_edit_tree_column_actions::get_category_actions($category, $levelclass, $params)');
+        }
+
+        return '<td class="cell actions '.$levelclass.'">' . $params['actions'] . '</td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        if (empty($params['actions'])) {
+            throw new Exception('Array key (actions) missing from 2nd param of grade_edit_tree_column_actions::get_item_cell($item, $params)');
+        }
+        return '<td class="cell actions">' . $params['actions'] . '</td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        return false;
+    }
+}
+
+class grade_edit_tree_column_select extends grade_edit_tree_column {
+
+    public function get_header_cell() {
+        return '<th class="header selection" scope="col">'.get_string('select').'</th>';
+    }
+
+    public function get_category_cell($category, $levelclass, $params) {
+
+        if (empty($params['eid'])) {
+            throw new Exception('Array key (eid) missing from 3rd param of grade_edit_tree_column_select::get_category_cell($category, $levelclass, $params)');
+        }
+
+        return '<td class="cell last  '.$levelclass.'" style="text-align: center">
+                    <span class="actionlink" onclick="togglecheckboxes(\''.$params['eid'].'\', true);">'.get_string('all').'</span><br />
+                    <span class="actionlink" onclick="togglecheckboxes(\''.$params['eid'].'\', false);">'.get_string('none').'</span>
+                </td>';
+    }
+
+    public function get_item_cell($item, $params) {
+        if (empty($params['itemtype']) || empty($params['eid'])) {
+            throw new Exception('Array key (itemtype or eid) missing from 2nd param of grade_edit_tree_column_select::get_item_cell($item, $params)');
+        }
+        $itemselect = '';
+
+        if ($params['itemtype'] != 'course' && $params['itemtype'] != 'category') {
+            $itemselect = '<input class="itemselect" type="checkbox" name="select_'.$params['eid'].'" />';
+        }
+        return '<td class="cell last selection">' . $itemselect . '</td>';
+    }
+
+    public function is_hidden($mode='simple') {
+        return false;
+    }
+}
+?>
Index: grade/edit/tree/tree.css
=========================================================
--- grade/edit/tree/tree.css	Fri Feb 06 21:56:07 CET 2009
+++ grade/edit/tree/tree.css	Fri Feb 06 21:56:07 CET 2009
@@ -0,0 +1,133 @@
+.gradetreebox {
+    font-size: 0.8em;
+}
+
+.gradetreebox input, .gradetreebox select {
+    font-size: 85%;
+}
+.gradetreebox tr.category td {
+    background-color: #DDDDDD;
+}
+.gradetreebox tr.category th {
+    background-color: #DDDDDD;
+}
+.gradetreebox tr.category td.name {
+    border-left: 0px;
+}
+
+.gradetreebox .category td.name h4 {
+    display: inline;
+}
+
+.gradetreebox td.name, .gradetreebox td.range, .gradetreebox td.actions   {
+    white-space: nowrap;
+}
+
+.gradetreebox th.actions {
+    width: 80px;
+}
+
+.child {
+    background-image: url(img/ln.gif);
+}
+
+.hidden {
+    display: none;
+}
+.shown {
+}
+
+img.iconsmall {
+    margin-left: 4px;
+}
+
+img.icon {
+    margin-right: 5px;
+}
+
+.grade-edit-tree .gradetreebox  {
+    margin-left: auto;
+    margin-right: auto;
+    margin-top: 10px;
+    padding-bottom: 15px;
+    width: auto;
+}
+
+.gradetreebox table {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.buttons {
+    margin: 20px;
+    text-align: center;
+    width: 100%;
+}
+
+.buttons .singlebutton {
+    display: inline;
+    padding: 5px;
+}
+
+#gradetreesubmit {
+    width: 100%;
+    text-align: center;
+    margin-top: 10px;
+
+}
+
+.gradetreebox span.actionlink {
+ color: blue;
+}
+
+.gradetreebox span.actionlink:hover {
+ text-decoration: underline;
+ cursor: pointer;
+}
+
+.gradetreebox td.colspan {
+    border-left: 1px solid #AAAAAA;
+    border-bottom: 1px solid #AAAAAA;
+    border-top: none;
+    background-color: #DDDDDD;
+}
+
+.gradetreebox tr.category th.rowspan {
+    border-color: #AAAAAA;
+    border-bottom: 0px;
+    border-top: 0px;
+    border-right: 0px !important;
+}
+
+.gradetreebox tr.category th.rowspan:hover {
+    background-color: #EEEEEE !important;
+}
+
+.level1 {
+    background-color: #d0dbf3 !important;
+    width: 10px;
+}
+.level2 {
+    background-color: #f3dfd0 !important;
+    width: 10px;
+}
+.level3 {
+    background-color: #d0f3d6 !important;
+    width: 10px;
+}
+.level4 {
+    background-color: #eae8be !important;
+    width: 10px;
+}
+.level5 {
+    background-color: #ebdef6 !important;
+    width: 10px;
+}
+
+.gradetreebox table.generaltable {
+    border:  1px solid #AAAAAA;
+    width: 100%;
+}
+
+
+
Index: lang/en_utf8/grades.php
=========================================================
--- lang/en_utf8/grades.php	(revision 1.111.2.60)
+++ lang/en_utf8/grades.php	Sat Feb 07 09:10:06 CET 2009
@@ -183,6 +192,7 @@
 $string['forstudents'] = 'For students';
 $string['full'] = 'Full';
 $string['fullmode'] = 'Full view';
+$string['fullview'] = 'Full view';
 $string['generalsettings'] = 'General settings';
 $string['grade'] = 'Grade';
 $string['gradebook'] = 'Gradebook';
@@ -473,6 +484,7 @@
 $string['showuseridnumber'] = 'Show user idnumber';
 $string['showuserimage'] = 'Show user profile images';
 $string['showverbose'] = 'Show $a->category$a->itemmodule $a->itemname';
+$string['simpleview'] = 'Simple view';
 $string['sitewide'] = 'Site-wide';
 $string['sort'] = 'sort';
 $string['sortasc'] = 'Sort in ascending order';
@@ -487,6 +499,8 @@
 $string['subcategory'] = 'Normal category';
 $string['submissions'] = 'Submissions';
 $string['submittedon'] = 'Submitted: $a';
+$string['switchtofullview'] = 'Switch to full view';
+$string['switchtosimpleview'] = 'Switch to simple view';
 $string['synclegacygrades'] = 'Synchronise legacy grades';
 $string['topcategory'] = 'Super category';
 $string['total'] = 'Total';
Index: grade/edit/tree/index.php
=========================================================
--- grade/edit/tree/index.php	(revision 1.12.2.5)
+++ grade/edit/tree/index.php	Tue Feb 03 11:17:31 CET 2009
@@ -1,4 +1,4 @@
-<?php  // $Id: index.php,v 1.12.2.5 2008/02/17 19:11:29 skodak Exp $
+<?php  // $Id: index.php,v 1.4 2008/11/20 15:38:38 nicolasconnault Exp $
 
 ///////////////////////////////////////////////////////////////////////////
 //                                                                       //
@@ -26,14 +26,19 @@
 require_once '../../../config.php';
 require_once $CFG->dirroot.'/grade/lib.php';
 require_once $CFG->dirroot.'/grade/report/lib.php'; // for preferences
+require_once $CFG->dirroot.'/grade/edit/tree/lib.php';
+
+require_js(array('yui_yahoo', 'yui_dom', 'yui_event', 'yui_json', 'yui_connection', 'yui_dragdrop', 'yui_treeview', 'yui_element',
+            $CFG->wwwroot.'/grade/edit/tree/functions.js'));
 
-$courseid = required_param('id', PARAM_INT);
-$action   = optional_param('action', 0, PARAM_ALPHA);
-$eid      = optional_param('eid', 0, PARAM_ALPHANUM);
+$courseid        = required_param('id', PARAM_INT);
+$action          = optional_param('action', 0, PARAM_ALPHA);
+$eid             = optional_param('eid', 0, PARAM_ALPHANUM);
-
+$category        = optional_param('category', null, PARAM_INT);
+$aggregationtype = optional_param('aggregationtype', null, PARAM_INT);
+$showadvanced    = optional_param('showadvanced', -1, PARAM_BOOL); // sticky editting mode
 
 /// Make sure they can even access this course
-
 if (!$course = get_record('course', 'id', $courseid)) {
     print_error('nocourseid');
 }
@@ -46,6 +51,51 @@
 $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'tree', 'courseid'=>$courseid));
 $returnurl = $gpr->get_return_url(null);
 
+/// Build editing on/off buttons
+if (!isset($USER->gradeediting)) {
+    $USER->gradeediting = array();
+}
+
+$current_view = '';
+
+if (has_capability('moodle/grade:manage', $context)) {
+    if (!isset($USER->gradeediting[$course->id])) {
+        $USER->gradeediting[$course->id] = 0;
+    }
+
+    if (($showadvanced == 1) and confirm_sesskey()) {
+        $USER->gradeediting[$course->id] = 1;
+    } else if (($showadvanced == 0) and confirm_sesskey()) {
+        $USER->gradeediting[$course->id] = 0;
+    }
+
+    // page params for the turn editting on
+    $options = $gpr->get_options();
+    $options['sesskey'] = sesskey();
+
+    if ($USER->gradeediting[$course->id]) {
+        $options['showadvanced'] = 0;
+        $current_view = 'fullview';
+    } else {
+        $options['showadvanced'] = 1;
+        $current_view = 'simpleview';
+    }
+
+} else {
+    $USER->gradeediting[$course->id] = 0;
+    $buttons = '';
+}
+
+// Change category aggregation if requested
+if (!is_null($category) && !is_null($aggregationtype)) {
+    if (!$grade_category = grade_category::fetch(array('id'=>$category, 'courseid'=>$courseid))) {
+        error('Incorrect category id!');
+    }
+    $data->aggregation = $aggregationtype;
+    grade_category::set_properties($grade_category, $data);
+    $grade_category->update();
+}
+
 //first make sure we have proper final grades - we need it for locking changes
 grade_regrade_final_grades($courseid);
 
@@ -59,7 +109,7 @@
 
 } else {
     if (!$element = $gtree->locate_element($eid)) {
-        error('Incorrect element id!', $returnurl);
+        print_error('invalidelementid', '', $returnurl);
     }
     $object = $element['object'];
 }
@@ -74,10 +124,12 @@
 $navigation = grade_build_nav(__FILE__, $strcategoriesanditems, array('courseid' => $courseid));
 $moving = false;
 
+$grade_edit_tree = new grade_edit_tree($gtree, $moving, $gpr);
+
 switch ($action) {
     case 'delete':
         if ($eid) {
-            if (!element_deletable($element)) {
+            if (!$grade_edit_tree->element_deletable($element)) {
                 // no deleting of external activities - they would be recreated anyway!
                 // exception is activity without grading or misconfigured activities
                 break;
@@ -89,7 +141,7 @@
                 redirect($returnurl);
 
             } else {
-                print_header_simple($strgrades . ': ' . $strgraderreport, ': ' . $strcategoriesedit, $navigation, '', '', true, '', navmenu($course));
+                print_header_simple($strgrades . ': ' . $strgraderreport, ': ' . $strcategoriesedit, $navigation, '', '', true, null, navmenu($course));
                 $strdeletecheckfull = get_string('deletecheck', '', $object->get_name());
                 $optionsyes = array('eid'=>$eid, 'confirm'=>1, 'sesskey'=>sesskey(), 'id'=>$course->id, 'action'=>'delete');
                 $optionsno  = array('id'=>$course->id);
@@ -111,14 +163,22 @@
     case 'move':
         if ($eid and confirm_sesskey()) {
             $moveafter = required_param('moveafter', PARAM_ALPHANUM);
+            $first = optional_param('first', false,  PARAM_BOOL); // If First is set to 1, it means the target is the first child of the category $moveafter
+
             if(!$after_el = $gtree->locate_element($moveafter)) {
-                error('Incorect element id in moveafter', $returnurl);
+                print_error('invalidelementid', '', $returnurl);
             }
+
             $after = $after_el['object'];
-            $parent = $after->get_parent_category();
             $sortorder = $after->get_sortorder();
 
+            if (!$first) {
+                $parent = $after->get_parent_category();
-            $object->set_parent($parent->id);
+                $object->set_parent($parent->id);
+            } else {
+                $object->set_parent($after->id);
+            }
+
             $object->move_after_sortorder($sortorder);
 
             redirect($returnurl);
@@ -127,7 +187,7 @@
 
     case 'moveselect':
         if ($eid and confirm_sesskey()) {
-            $moving = $eid;
+            $grade_edit_tree->moving = $eid;
         }
         break;
 
@@ -135,141 +195,166 @@
         break;
 }
 
-print_header_simple($strgrades . ': ' . $strgraderreport, ': ' . $strcategoriesedit, $navigation, '', '', true, '', navmenu($course));
-
-/// Print the plugin selector at the top
-print_grade_plugin_selector($courseid, 'edit', 'tree');
-
-print_heading(get_string('categoriesedit', 'grades'));
+// Hide advanced columns if moving
+if ($grade_edit_tree->moving) {
+    $original_gradeediting = $USER->gradeediting[$course->id];
+    $USER->gradeediting[$course->id] = 0;
+}
 
+$CFG->stylesheets[] = $CFG->wwwroot.'/grade/edit/tree/tree.css';
 
-print_box_start('gradetreebox generalbox');
-echo '<ul id="grade_tree">';
-print_grade_tree($gtree, $gtree->top_element, $moving, $gpr, $switch);
-echo '</ul>';
-print_box_end();
-
-echo '<div class="buttons">';
-if ($moving) {
-    print_single_button('index.php', array('id'=>$course->id), get_string('cancel'), 'get');
-} else {
-    print_single_button('category.php', array('courseid'=>$course->id), get_string('addcategory', 'grades'), 'get');
-    print_single_button('item.php', array('courseid'=>$course->id), get_string('additem', 'grades'), 'get');
-    if (!empty($CFG->enableoutcomes)) {
-        print_single_button('outcomeitem.php', array('courseid'=>$course->id), get_string('addoutcomeitem', 'grades'), 'get');
+$current_view_str = '';
+if ($current_view != '') {
+    if ($current_view == 'simpleview') {
+        $current_view_str = get_string('simpleview', 'grades');
+    } elseif ($current_view == 'fullview') {
+        $current_view_str = get_string('fullview', 'grades');
     }
-    //print_single_button('index.php', array('id'=>$course->id, 'action'=>'autosort'), get_string('autosort', 'grades'), 'get');
-    print_single_button('index.php', array('id'=>$course->id, 'action'=>'synclegacy'), get_string('synclegacygrades', 'grades'), 'get');
 }
-echo '</div>';
-print_footer($course);
-die;
 
-/**
- * TODO document
- */
-function print_grade_tree(&$gtree, $element, $moving, &$gpr, $switch, $switchedlast=false) {
-    global $CFG, $COURSE;
+print_grade_page_head($courseid, 'edittree', $current_view, get_string('categoriesedit', 'grades') . ': ' . $current_view_str);
 
-/// fetch needed strings
-    $strmove     = get_string('move');
-    $strmovehere = get_string('movehere');
-    $strdelete   = get_string('delete');
+$form_key = optional_param('sesskey', null, PARAM_ALPHANUM);
 
-    $object = $element['object'];
-    $eid    = $element['eid'];
+if ($form_key && $data = data_submitted()) {
+    // Perform bulk actions first
+    if (!empty($data->bulkmove)) {
+        $elements = array();
 
-    $header = $gtree->get_element_header($element, true, true, true);
+        foreach ($data as $key => $value) {
+            if (preg_match('/select_(i[0-9]*)/', $key, $matches)) {
+                $elements[] = $matches[1];
+            }
+        }
 
-    if ($object->is_hidden()) {
-        $header = '<span class="dimmed_text">'.$header.'</span>';
+        $grade_edit_tree->move_elements($elements, $returnurl);
     }
 
-/// prepare actions
-    $actions = $gtree->get_edit_icon($element, $gpr);
-    $actions .= $gtree->get_calculation_icon($element, $gpr);
+    // Category and item field updates
+    foreach ($data as $key => $value) {
+        // Grade category text inputs
+        if (preg_match('/(aggregation|droplow|keephigh)_([0-9]*)/', $key, $matches)) {
+            $value = required_param($matches[0], PARAM_INT);
+            $param = $matches[1];
+            $a->id = $matches[2];
 
-    if ($element['type'] == 'item' or ($element['type'] == 'category' and $element['depth'] > 1)) {
-        if (element_deletable($element)) {
-            $actions .= '<a href="index.php?id='.$COURSE->id.'&amp;action=delete&amp;eid='
-                     . $eid.'&amp;sesskey='.sesskey().'"><img src="'.$CFG->pixpath.'/t/delete.gif" class="iconsmall" alt="'
-                     . $strdelete.'" title="'.$strdelete.'"/></a>';
-        }
-        $actions .= '<a href="index.php?id='.$COURSE->id.'&amp;action=moveselect&amp;eid='
-                 . $eid.'&amp;sesskey='.sesskey().'"><img src="'.$CFG->pixpath.'/t/move.gif" class="iconsmall" alt="'
-                 . $strmove.'" title="'.$strmove.'"/></a>';
+            if (!update_record('grade_categories', (object) array('id' => $matches[2], $param => $value))) {
+                print_error('errorupdatinggradecategoryaggregation', 'grades', $a);
-    }
+            }
 
-    $actions .= $gtree->get_hiding_icon($element, $gpr);
-    $actions .= $gtree->get_locking_icon($element, $gpr);
+        // Grade item text inputs
+        } elseif (preg_match('/(aggregationcoef|multfactor|plusfactor)_([0-9]*)/', $key, $matches)) {
+            $value = required_param($matches[0], PARAM_NUMBER);
+            $param = $matches[1];
+            $a->id = $matches[2];
 
-/// prepare move target if needed
-    $last = '';
-    $catcourseitem = ($element['type'] == 'courseitem' or $element['type'] == 'categoryitem');
-    $moveto = '';
-    if ($moving) {
-        $actions = ''; // no action icons when moving
-        $moveto = '<li><a href="index.php?id='.$COURSE->id.'&amp;action=move&amp;eid='.$moving.'&amp;moveafter='
-                . $eid.'&amp;sesskey='.sesskey().'"><img class="movetarget" src="'.$CFG->wwwroot.'/pix/movehere.gif" alt="'
-                . $strmovehere.'" title="'.$strmovehere.'" /></a></li>';
+            if (!update_record('grade_items', (object) array('id' => $matches[2], $param => $value))) {
+                print_error('errorupdatinggradeitemaggregationcoef', 'grades', $a);
-    }
+            }
 
-/// print the list items now
-    if ($moving == $eid) {
-        // do not diplay children
-        echo '<li class="'.$element['type'].' moving">'.$header.'('.get_string('move').')</li>';
+        // Grade item checkbox inputs
+        } elseif (preg_match('/extracredit_original_([0-9]*)/', $key, $matches)) { // Sum extra credit checkbox
+            $extracredit = optional_param("extracredit_{$matches[1]}", null, PARAM_BOOL);
+            $original_value = required_param($matches[0], PARAM_BOOL);
+            $a->id = $matches[1];
+            $newvalue = null;
 
-    } else if ($element['type'] != 'category') {
-        if ($catcourseitem and $switch) {
-            if ($switchedlast) {
-                echo '<li class="'.$element['type'].'">'.$header.$actions.'</li>';
+            if ($original_value == 1 && is_null($extracredit)) {
+                $newvalue = 0;
+            } elseif ($original_value == 0 && $extracredit == 1) {
+                $newvalue = 1;
             } else {
-                echo $moveto;
+                continue;
             }
-        } else {
-            echo '<li class="'.$element['type'].'">'.$header.$actions.'</li>'.$moveto;
+
+            if (!update_record('grade_items', array('id' => $matches[1], 'aggregationcoef' => $newvalue))) {
+                print_error('errorupdatinggradeitemaggregationcoef', 'grades', $a);
-        }
+            }
 
+        // Grade category checkbox inputs
+        } elseif (preg_match('/aggregate(onlygraded|subcats|outcomes)_original_([0-9]*)/', $key, $matches)) {
+            $setting = optional_param('aggregate'.$matches[1].'_'.$matches[2], null, PARAM_BOOL);
+            $original_value = required_param($matches[0], PARAM_BOOL);
+            $a->id = $matches[2];
+
+            $newvalue = null;
+            if ($original_value == 1 && is_null($setting)) {
+                $newvalue = 0;
+            } elseif ($original_value == 0 && $setting == 1) {
+                $newvalue = 1;
-    } else {
+            } else {
-        echo '<li class="'.$element['type'].'">'.$header.$actions;
-        echo '<ul class="catlevel'.$element['depth'].'">';
-        $last = null;
-        foreach($element['children'] as $child_el) {
-            if ($switch and empty($last)) {
-                $last = $child_el;
+                continue;
             }
-            print_grade_tree($gtree, $child_el, $moving, $gpr, $switch);
+
+            if (!update_record('grade_categories', array('id' => $matches[2], 'aggregate'.$matches[1] => $newvalue))) {
+                print_error('errorupdatinggradecategoryaggregate'.$matches[1], 'grades', $a);
-        }
+            }
-        if ($last) {
-            print_grade_tree($gtree, $last, $moving, $gpr, $switch, true);
         }
-        echo '</ul></li>';
-        if ($element['depth'] > 1) {
-            echo $moveto; // can not move after the top category
-        }
     }
 }
 
-function element_deletable($element) {
-    global $COURSE;
+// Print Table of categories and items
+print_box_start('gradetreebox generalbox');
 
-    if ($element['type'] != 'item') {
-        return true;
-    }
+echo '<form id="gradetreeform" method="post" action="'.$returnurl.'">';
+echo '<div>';
+echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
 
-    $grade_item = $element['object'];
+// Build up an array of categories for move drop-down (by reference)
+$categories = array();
+echo $grade_edit_tree->build_html_tree($gtree->top_element, true, array(), $categories);
-    
+
-    if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) {
-        return true;
-    }
+echo '<div id="gradetreesubmit">';
+if (!$moving) {
+    echo '<input class="advanced" type="submit" value="'.get_string('savechanges').'" />';
+}
+
+if (!$moving) {
+    echo '<br /><br />';
+    echo '<input type="hidden" name="bulkmove" value="0" id="bulkmoveinput" />';
+    echo get_string('moveselectedto', 'grades') . ' : ';
+    echo choose_from_menu($categories, 'moveafter', '', 'choose',
+            'document.getElementById(\'bulkmoveinput\').value=1;document.getElementById(\'gradetreeform\').submit()', 0, true);
+    echo '<div id="noscriptgradetreeform" style="display: inline;">
+            <input type="submit" value="'.get_string('go').'" />
+          </div>
+          <script type="text/javascript">
+            //<![CDATA[
+                document.getElementById("noscriptgradetreeform").style.display= "none";
+            //]]>
+          </script>';
+}
+
+echo '</div>';
+
+echo '</div></form>';
+
+print_box_end();
+
+// Print action buttons
+echo '<div class="buttons">';
 
-    $modinfo = get_fast_modinfo($COURSE);
-    if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) {
-        // module does not exist
-        return true;
+if ($moving) {
+    print_single_button('index.php', array('id'=>$course->id), get_string('cancel'), 'get');
+} else {
+    print_single_button('category.php', array('courseid'=>$course->id), get_string('addcategory', 'grades'), 'get');
+    print_single_button('item.php', array('courseid'=>$course->id), get_string('additem', 'grades'), 'get');
+
+    if (!empty($CFG->enableoutcomes)) {
+        print_single_button('outcomeitem.php', array('courseid'=>$course->id), get_string('addoutcomeitem', 'grades'), 'get');
     }
 
-    return false;
+    print_single_button('index.php', array('id'=>$course->id, 'action'=>'synclegacy'), get_string('synclegacygrades', 'grades'), 'get');
+}
+
+echo '</div>';
+
+print_footer($course);
+
+// Restore original show/hide preference if moving
+if ($moving) {
+    $USER->gradeediting[$course->id] = $original_gradeediting;
 }
+die;
 
 ?>
