Index: admin/settings/grades.php
=========================================================
--- admin/settings/grades.php	(revision 1.14.2.20)
+++ admin/settings/grades.php	Thu Apr 16 09:31:42 CEST 2009
@@ -54,8 +54,14 @@
                                                          '3' => '3',
                                                          '4' => '4',
                                                          '5' => '5')));
+        $temp->add(new admin_setting_configselect('grade_navmethod', get_string('navmethod', 'grades'), null, 0,
+                                                  array(GRADE_NAVMETHOD_DROPDOWN => get_string('dropdown', 'grades'),
+                                                        GRADE_NAVMETHOD_TABS => get_string('tabs', 'grades'),
+                                                        GRADE_NAVMETHOD_COMBO => get_string('combo', 'grades'))));
 
         $temp->add(new admin_setting_special_gradeexport());
+
+        $temp->add(new admin_setting_special_gradelimiting());
     }
     $ADMIN->add('grades', $temp);
 
@@ -76,9 +82,17 @@
                          GRADE_AGGREGATE_MAX             =>get_string('aggregatemax', 'grades'),
                          GRADE_AGGREGATE_MODE            =>get_string('aggregatemode', 'grades'),
                          GRADE_AGGREGATE_SUM             =>get_string('aggregatesum', 'grades'));
+
+        $defaultvisible = array(GRADE_AGGREGATE_MEAN, GRADE_AGGREGATE_WEIGHTED_MEAN, GRADE_AGGREGATE_WEIGHTED_MEAN2,
+                                GRADE_AGGREGATE_EXTRACREDIT_MEAN, GRADE_AGGREGATE_MEDIAN, GRADE_AGGREGATE_MIN,
+                                GRADE_AGGREGATE_MAX, GRADE_AGGREGATE_MODE, GRADE_AGGREGATE_SUM);
+
         $defaults = array('value'=>GRADE_AGGREGATE_WEIGHTED_MEAN2, 'forced'=>false, 'adv'=>false);
         $temp->add(new admin_setting_gradecat_combo('grade_aggregation', get_string('aggregation', 'grades'), get_string('aggregationhelp', 'grades'), $defaults, $options));
 
+        $temp->add(new admin_setting_configmultiselect('grade_aggregations_visible', get_string('aggregationsvisible', 'grades'),
+                                                       get_string('aggregationsvisiblehelp', 'grades'), $defaultvisible, $options));
+
         $options = array(0 => get_string('no'), 1 => get_string('yes'));
 
         $defaults = array('value'=>1, 'forced'=>false, 'adv'=>true);
Index: grade/edit/letter/edit.php
=========================================================
--- grade/edit/letter/edit.php	(revision 1.3.2.2)
+++ grade/edit/letter/edit.php	Thu Apr 16 09:31:42 CEST 2009
@@ -133,12 +133,7 @@
     admin_externalpage_print_header();
 
 } else {
-    $navigation = grade_build_nav(__FILE__, $pagename, $COURSE->id);
-    /// Print header
-    print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($COURSE));
-
-    $currenttab = 'lettersedit';
-    require('tabs.php');
+    print_grade_page_head($COURSE->id, 'letter', 'edit', get_string('editgradeletters', 'grades'));
 }
 
 $mform->display();
Index: grade/edit/letter/index.php
=========================================================
--- grade/edit/letter/index.php	(revision 1.4)
+++ grade/edit/letter/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -45,15 +45,7 @@
 $strgrades = get_string('grades');
 $pagename  = get_string('letters', 'grades');
 
-$navigation = grade_build_nav(__FILE__, $pagename, $courseid);
-
-/// Print header
-print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
-/// Print the plugin selector at the top
-print_grade_plugin_selector($courseid, 'edit', 'letter');
-
-$currenttab = 'lettersview';
-require('tabs.php');
+print_grade_page_head($courseid, 'letter', 'view', get_string('gradeletters', 'grades'));
 
 $letters = grade_get_letters($context);
 
Index: grade/edit/outcome/course.php
=========================================================
--- grade/edit/outcome/course.php	(revision 1.6.2.1)
+++ grade/edit/outcome/course.php	Thu Apr 16 09:31:42 CEST 2009
@@ -132,26 +132,8 @@
     redirect('course.php?id='.$courseid); // we must redirect to get fresh data
 }
 
-$strgrades = get_string('grades');
-$pagename  = get_string('outcomescourse', 'grades');
-
-$navlinks = array(array('name'=>$strgrades,
-                        'link'=>$CFG->wwwroot.'/grade/index.php?id='.$courseid,
-                        'type'=>'misc'),
-                  array('name'=>$pagename,
-                        'link'=>'',
-                        'type'=>'misc')
-                 );
-$navigation = build_navigation($navlinks);
-$navigation = grade_build_nav(__FILE__, $pagename, $courseid);
 /// Print header
-print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
-
-/// Print the plugin selector at the top
-print_grade_plugin_selector($courseid, 'edit', 'outcome');
-
-$currenttab = 'courseoutcomes';
-require('tabs.php');
+print_grade_page_head($COURSE->id, 'outcome', 'course');
 
 check_theme_arrows();
 require('course_form.html');
Index: grade/edit/outcome/edit.php
=========================================================
--- grade/edit/outcome/edit.php	(revision 1.4.2.2)
+++ grade/edit/outcome/edit.php	Thu Apr 16 09:31:42 CEST 2009
@@ -32,9 +32,12 @@
 $id       = optional_param('id', 0, PARAM_INT);
 
 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
+$heading = null;
 
 // a bit complex access control :-O
 if ($id) {
+    $heading = get_string('editoutcome', 'grades');
+
     /// editing existing outcome
     if (!$outcome_rec = get_record('grade_outcomes', 'id', $id)) {
         error('Incorrect outcome id');
@@ -61,6 +64,7 @@
     }
 
 } else if ($courseid){
+    $heading = get_string('addoutcome', 'grades');
     /// adding new outcome from course
     if (!$course = get_record('course', 'id', $courseid)) {
         print_error('nocourseid');
@@ -119,14 +123,8 @@
     redirect($returnurl);
 }
 
-$strgrades       = get_string('grades');
-$strgraderreport = get_string('graderreport', 'grades');
-$stroutcomeedit  = get_string('outcome', 'grades');
-
 if ($courseid) {
-    $navigation = grade_build_nav(__FILE__, $stroutcomeedit, array('courseid' => $courseid));
-    print_header_simple($strgrades.': '.$strgraderreport, ': '.$stroutcomeedit, $navigation, '', '', true, '', navmenu($course));
-
+    print_grade_page_head($courseid, 'outcome', 'edit', $heading);
 } else {
     require_once $CFG->libdir.'/adminlib.php';
     admin_externalpage_setup('outcomes');
Index: grade/edit/outcome/import.php
=========================================================
--- grade/edit/outcome/import.php	(revision 1.1.2.6)
+++ grade/edit/outcome/import.php	Thu Apr 16 09:31:42 CEST 2009
@@ -79,16 +79,11 @@
 $caneditsystemscales = has_capability('moodle/course:managescales', $systemcontext);
 
 if ($courseid) {
-    /// Print header
-    print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
-    /// Print the plugin selector at the top
-    print_grade_plugin_selector($courseid, 'edit', 'outcome');
+
+    print_grade_page_head($courseid, 'outcome', 'import', get_string('importoutcomes', 'grades'));
 
     $caneditcoursescales = has_capability('moodle/course:managescales', $context);
 
-    $currenttab = 'outcomes';
-    require('tabs.php');
-
 } else {
     admin_externalpage_print_header();
     $caneditcoursescales = $caneditsystemscales;
@@ -208,7 +203,7 @@
             print_box(get_string('importskippedoutcome', 'grades', $csv_data[$imported_headers['outcome_shortname']]));
             continue;
         }
-
+        break;
         // new outcome will be added, search for compatible existing scale...
         $scale = get_records_select('scale', 'name ="'. $csv_data[$imported_headers['scale_name']] .'" and scale ="'. $csv_data[$imported_headers['scale_items']] .'" and (courseid = '. $courseid .' or courseid = 0)');
 
Index: grade/edit/outcome/index.php
=========================================================
--- grade/edit/outcome/index.php	(revision 1.10.2.6)
+++ grade/edit/outcome/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -125,16 +125,9 @@
 $caneditsystemscales = has_capability('moodle/course:managescales', $systemcontext);
 
 if ($courseid) {
-    /// Print header
-    print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
-    /// Print the plugin selector at the top
-    print_grade_plugin_selector($courseid, 'edit', 'outcome');
 
     $caneditcoursescales = has_capability('moodle/course:managescales', $context);
 
-    $currenttab = 'outcomes';
-    require('tabs.php');
-
 } else {
     admin_externalpage_print_header();
     $caneditcoursescales = $caneditsystemscales;
@@ -142,10 +135,10 @@
 
 
 $outcomes_tables = array();
+$heading = get_string('outcomes', 'grades');
 
 if ($courseid and $outcomes = grade_outcome::fetch_all_local($courseid)) {
-
-    $return = print_heading($strcustomoutcomes, '', 2, 'main', true);
+    $return = print_heading($strcustomoutcomes, '', 3, 'main', true);
     $data = array();
     foreach($outcomes as $outcome) {
         $line = array();
@@ -253,6 +246,11 @@
     $outcomes_tables[] = $return;
 }
 
+if ($courseid) {
+    /// Print header
+    print_grade_page_head($courseid, 'outcome', 'edit', $heading);
+}
+
 foreach($outcomes_tables as $table) {
     print($table);
 }
Index: grade/edit/scale/edit.php
=========================================================
--- grade/edit/scale/edit.php	(revision 1.6.2.1)
+++ grade/edit/scale/edit.php	Thu Apr 16 09:31:42 CEST 2009
@@ -32,9 +32,12 @@
 $id       = optional_param('id', 0, PARAM_INT);
 
 $systemcontext = get_context_instance(CONTEXT_SYSTEM);
+$heading = '';
 
 // a bit complex access control :-O
 if ($id) {
+    $heading = get_string('editscale', 'grades');
+
     /// editing existing scale
     if (!$scale_rec = get_record('scale', 'id', $id)) {
         error('Incorrect scale id');
@@ -61,6 +64,7 @@
     }
 
 } else if ($courseid){
+    $heading = get_string('addscale', 'grades');
     /// adding new scale from course
     if (!$course = get_record('course', 'id', $courseid)) {
         print_error('nocourseid');
@@ -112,21 +116,15 @@
         }
         $scale->update();
     }
-
     redirect($returnurl);
 }
 
-$strgrades       = get_string('grades');
-$strgraderreport = get_string('graderreport', 'grades');
-$strscaleedit    = get_string('scale');
-
 if ($courseid) {
-    $navigation = grade_build_nav(__FILE__, $strscaleedit, $courseid);
-    print_header_simple($strgrades.': '.$strgraderreport, ': '.$strscaleedit, $navigation, '', '', true, '', navmenu($course));
+    print_grade_page_head($course->id, 'scale', 'edit', $heading);
 
 } else {
     require_once $CFG->libdir.'/adminlib.php';
-    admin_externalpage_setup('outcomes');
+    admin_externalpage_setup('scales');
     admin_externalpage_print_header();
 }
 
Index: grade/edit/scale/edit_form.php
=========================================================
--- grade/edit/scale/edit_form.php	(revision 1.10.2.2)
+++ grade/edit/scale/edit_form.php	Thu Apr 16 09:31:42 CEST 2009
@@ -143,7 +143,7 @@
 
             $options = explode(',', $data['scale']);
             if (count($options) < 2) {
-                $errors['scale'] = get_string('error');
+                $errors['scale'] = get_string('badlyformattedscale', 'grades');
             }
         }
 
Index: grade/edit/scale/index.php
=========================================================
--- grade/edit/scale/index.php	(revision 1.6)
+++ grade/edit/scale/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -47,12 +47,6 @@
 /// return tracking object
 $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'scale', 'courseid'=>$courseid));
 
-
-$strgrades = get_string('grades');
-$pagename  = get_string('scales');
-
-$navigation = grade_build_nav(__FILE__, $pagename, array('courseid' => $courseid));
-
 $strscale          = get_string('scale');
 $strstandardscale  = get_string('scalesstandard');
 $strcustomscales   = get_string('scalescustom');
@@ -88,18 +82,17 @@
         break;
 }
 
-if ($courseid) {
-    /// Print header
-    print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
-    /// Print the plugin selector at the top
-    print_grade_plugin_selector($courseid, 'edit', 'scale');
-
-} else {
+if (!$courseid) {
     admin_externalpage_print_header();
 }
 
+$table = null;
+$table2 = null;
+$heading = '';
+
 if ($courseid and $scales = grade_scale::fetch_all_local($courseid)) {
-    print_heading($strcustomscales);
+    $heading = $strcustomscales;
+
     $data = array();
     foreach($scales as $scale) {
         $line = array();
@@ -123,11 +116,11 @@
     $table->align = array('left', 'center', 'center');
     $table->width = '90%';
     $table->data  = $data;
-    print_table($table);
 }
 
 if ($scales = grade_scale::fetch_all_global()) {
-    print_heading($strstandardscale);
+    $heading = $strstandardscale;
+
     $data = array();
     foreach($scales as $scale) {
         $line = array();
@@ -148,14 +141,22 @@
         $line[] = $buttons;
         $data[] = $line;
     }
-    $table->head  = array($strscale, $strused, $stredit);
-    $table->size  = array('70%', '20%', '10%');
-    $table->align = array('left', 'center', 'center');
-    $table->width = '90%';
-    $table->data  = $data;
-    print_table($table);
+    $table2->head  = array($strscale, $strused, $stredit);
+    $table2->size  = array('70%', '20%', '10%');
+    $table2->align = array('left', 'center', 'center');
+    $table2->width = '90%';
+    $table2->data  = $data;
 }
 
+
+if ($courseid) {
+    print_grade_page_head($courseid, 'scale', null, get_string('coursescales', 'grades'));
+}
+
+print_heading($strcustomscales, '', 3, 'main');
+print_table($table);
+print_heading($strstandardscale, '', 3, 'main');
+print_table($table2);
 echo '<div class="buttons">';
 print_single_button('edit.php', array('courseid'=>$courseid), $srtcreatenewscale);
 echo '</div>';
Index: grade/edit/settings/index.php
=========================================================
--- grade/edit/settings/index.php	(revision 1.3.2.1)
+++ grade/edit/settings/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -74,10 +74,11 @@
     redirect($returnurl);
 }
 
-/// Print header
-print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
-/// Print the plugin selector at the top
-print_grade_plugin_selector($courseid, 'edit', 'settings');
+print_grade_page_head($courseid, 'settings', 'coursesettings', get_string('coursesettings', 'grades'));
+
+print_box_start('generalbox boxaligncenter boxwidthnormal centerpara');
+echo get_string('coursesettingsexplanation', 'grades');
+print_box_end();
 
 $mform->display();
 
Index: grade/edit/tree/category.php
=========================================================
--- grade/edit/tree/category.php	(revision 1.4.2.6)
+++ grade/edit/tree/category.php	Thu Apr 16 09:31:42 CEST 2009
@@ -29,7 +29,7 @@
 require_once 'category_form.php';
 
 $courseid = required_param('courseid', PARAM_INT);
-$id       = optional_param('id', 0, PARAM_INT);
+$id       = optional_param('id', 0, PARAM_INT); // grade_category->id
 
 if (!$course = get_record('course', 'id', $courseid)) {
     print_error('nocourseid');
@@ -45,6 +45,7 @@
 
 
 $mform = new edit_category_form(null, array('gpr'=>$gpr));
+$heading = get_string('categoryedit', 'grades');
 
 if ($id) {
     if (!$grade_category = grade_category::fetch(array('id'=>$id, 'courseid'=>$course->id))) {
@@ -54,19 +55,20 @@
     $category = $grade_category->get_record_data();
     // Get Category preferences
     $category->pref_aggregationview = grade_report::get_pref('aggregationview', $id);
-    // Load agg coef
-    $grade_item = $grade_category->load_grade_item();
-    $category->aggregationcoef = format_float($grade_item->aggregationcoef, 4);
     // set parent
     $category->parentcategory = $grade_category->parent;
+    $grade_item = $grade_category->load_grade_item();
+    foreach ($grade_item as $key => $value) {
+        $category->{"grade_item_$key"} = $value;
+    }
 
 } else {
+    $heading = get_string('newcategory', 'grades');
     $grade_category = new grade_category(array('courseid'=>$courseid), false);
     $grade_category->apply_default_settings();
     $grade_category->apply_forced_settings();
 
     $category = $grade_category->get_record_data();
-    $category->aggregationcoef = format_float(0, 4);
 }
 
 $mform->set_data($category);
@@ -88,6 +90,7 @@
     }
     grade_category::set_properties($grade_category, $data);
 
+    /// CATEGORY
     if (empty($grade_category->id)) {
         $grade_category->insert();
 
@@ -95,6 +98,79 @@
         $grade_category->update();
     }
 
+    /// GRADE ITEM
+    // grade item data saved with prefix "grade_item_"
+    $itemdata = new stdClass();
+    foreach ($data as $k => $v) {
+        if (preg_match('/grade_item_(.*)/', $k, $matches)) {
+            $itemdata->{$matches[1]} = $v;
+        }
+    }
+
+    if (!isset($itemdata->aggregationcoef)) {
+        $itemdata->aggregationcoef = 0;
+    }
+
+    $hidden      = empty($itemdata->hidden) ? 0: $itemdata->hidden;
+    $hiddenuntil = empty($itemdata->hiddenuntil) ? 0: $itemdata->hiddenuntil;
+    unset($itemdata->hidden);
+    unset($itemdata->hiddenuntil);
+
+    $locked   = empty($itemdata->locked) ? 0: $itemdata->locked;
+    $locktime = empty($itemdata->locktime) ? 0: $itemdata->locktime;
+    unset($itemdata->locked);
+    unset($itemdata->locktime);
+
+    $convert = array('grademax', 'grademin', 'gradepass', 'multfactor', 'plusfactor', 'aggregationcoef');
+    foreach ($convert as $param) {
+        if (array_key_exists($param, $itemdata)) {
+            $itemdata->$param = unformat_float($itemdata->$param);
+        }
+    }
+
+    // When creating a new category, a number of grade item fields are filled out automatically, and are required.
+    // If the user leaves these fields empty during creation of a category, we let the default values take effect
+    // Otherwise, we let the user-entered grade item values take effect
+    $grade_item = $grade_category->load_grade_item();
+    $grade_item_copy = fullclone($grade_item);
+    grade_item::set_properties($grade_item, $itemdata);
+
+    if (empty($grade_item->id)) {
+        $grade_item->id = $grade_item_copy->id;
+    }
+    if (empty($grade_item->grademax)) {
+        $grade_item->grademax = $grade_item_copy->grademax;
+    }
+    if (empty($grade_item->grademin)) {
+        $grade_item->grademin = $grade_item_copy->grademin;
+    }
+    if (empty($grade_item->gradepass)) {
+        $grade_item->gradepass = $grade_item_copy->gradepass;
+    }
+    if (empty($grade_item->aggregationcoef)) {
+        $grade_item->aggregationcoef = $grade_item_copy->aggregationcoef;
+    }
+
+    $grade_item->outcomeid = null;
+
+    // update hiding flag
+    if ($hiddenuntil) {
+        $grade_item->set_hidden($hiddenuntil, false);
+    } else {
+        $grade_item->set_hidden($hidden, false);
+    }
+
+    $grade_item->set_locktime($locktime); // locktime first - it might be removed when unlocking
+    $grade_item->set_locked($locked, false, true);
+
+
+    // Handle null decimals value
+    if (!array_key_exists('decimals', $itemdata) or $itemdata->decimals < 0) {
+        $grade_item->decimals = null;
+    }
+
+    $grade_item->update(); // We don't need to insert it, it's already created when the category is created
+
     // Handle user preferences
     if (isset($data->pref_aggregationview)) {
         if (!grade_report::set_pref('aggregationview', $data->pref_aggregationview, $grade_category->id)) {
@@ -119,14 +195,7 @@
 }
 
 
-$strgrades         = get_string('grades');
-$strgraderreport   = get_string('graderreport', 'grades');
-$strcategoriesedit = get_string('categoryedit', 'grades');
-$strcategory       = get_string('category', 'grades');
-
-$navigation = grade_build_nav(__FILE__, $strcategory, array('courseid' => $courseid));
-
-print_header_simple($strgrades . ': ' . $strgraderreport, ': ' . $strcategoriesedit, $navigation, '', '', true, '', navmenu($course));
+print_grade_page_head($courseid, 'edittree', null, $heading);
 
 $mform->display();
 
Index: grade/edit/tree/category_form.php
=========================================================
--- grade/edit/tree/category_form.php	(revision 1.9.2.15)
+++ grade/edit/tree/category_form.php	Thu Apr 16 09:31:42 CEST 2009
@@ -26,28 +26,33 @@
 require_once $CFG->libdir.'/formslib.php';
 
 class edit_category_form extends moodleform {
+    var $allaggoptions;
+    var $aggregation_options = array();
 
     function definition() {
         global $CFG, $COURSE;
         $mform =& $this->_form;
 
-        $options = array(GRADE_AGGREGATE_MEAN            =>get_string('aggregatemean', 'grades'),
+        $this->aggregation_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'));
+                                           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'));
 
+        $this->allaggoptions = array_keys($this->aggregation_options);
+
         // visible elements
         $mform->addElement('header', 'headercategory', get_string('gradecategory', 'grades'));
         $mform->addElement('text', 'fullname', get_string('categoryname', 'grades'));
         $mform->addRule('fullname', null, 'required', null, 'client');
 
-        $mform->addElement('select', 'aggregation', get_string('aggregation', 'grades'), $options);
+        $mform->addElement('select', 'aggregation', get_string('aggregation', 'grades'), $this->aggregation_options);
         $mform->setHelpButton('aggregation', array('aggregation', get_string('aggregation', 'grades'), 'grade'));
+
         if ((int)$CFG->grade_aggregation_flag & 2) {
             $mform->setAdvanced('aggregation');
         }
@@ -55,6 +60,7 @@
         $mform->addElement('checkbox', 'aggregateonlygraded', get_string('aggregateonlygraded', 'grades'));
         $mform->setHelpButton('aggregateonlygraded', array('aggregateonlygraded', get_string('aggregateonlygraded', 'grades'),'grade'), true);
         $mform->disabledIf('aggregateonlygraded', 'aggregation', 'eq', GRADE_AGGREGATE_SUM);
+
         if ((int)$CFG->grade_aggregateonlygraded_flag & 2) {
             $mform->setAdvanced('aggregateonlygraded');
         }
@@ -72,11 +78,13 @@
 
         $mform->addElement('advcheckbox', 'aggregatesubcats', get_string('aggregatesubcats', 'grades'));
         $mform->setHelpButton('aggregatesubcats', array('aggregatesubcats', get_string('aggregatesubcats', 'grades'), 'grade'), true);
+
         if ((int)$CFG->grade_aggregatesubcats_flag & 2) {
             $mform->setAdvanced('aggregatesubcats');
         }
 
         $options = array(0 => get_string('none'));
+
         for ($i=1; $i<=20; $i++) {
             $options[$i] = $i;
         }
@@ -97,40 +105,134 @@
         $mform->disabledIf('keephigh', 'droplow', 'noteq', 0);
         $mform->disabledIf('droplow', 'keephigh', 'noteq', 0);
 
+        // Grade item settings
+        $mform->addElement('header', 'general', get_string('gradeitem', 'grades'));
+
+        $mform->addElement('text', 'grade_item_itemname', get_string('itemname', 'grades'));
+        $mform->addElement('text', 'grade_item_iteminfo', get_string('iteminfo', 'grades'));
+        $mform->setHelpButton('grade_item_iteminfo', array('iteminfo', get_string('iteminfo', 'grades'), 'grade'), true);
+
+        $mform->addElement('text', 'grade_item_idnumber', get_string('idnumbermod'));
+        $mform->setHelpButton('grade_item_idnumber', array('idnumber', get_string('idnumber', 'grades'), 'grade'), true);
+
+        $options = array(GRADE_TYPE_NONE=>get_string('typenone', 'grades'),
+                         GRADE_TYPE_VALUE=>get_string('typevalue', 'grades'),
+                         GRADE_TYPE_SCALE=>get_string('typescale', 'grades'),
+                         GRADE_TYPE_TEXT=>get_string('typetext', 'grades'));
+
+        $mform->addElement('select', 'grade_item_gradetype', get_string('gradetype', 'grades'), $options);
+        $mform->setHelpButton('grade_item_gradetype', array('gradetype', get_string('gradetype', 'grades'), 'grade'), true);
+        $mform->setDefault('grade_item_gradetype', GRADE_TYPE_VALUE);
+
+        //$mform->addElement('text', 'calculation', get_string('calculation', 'grades'));
+        //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_TEXT);
+        //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_NONE);
+
+        $options = array(0=>get_string('usenoscale', 'grades'));
+        if ($scales = get_records('scale')) {
+            foreach ($scales as $scale) {
+                $options[$scale->id] = format_string($scale->name);
+            }
+        }
+        $mform->addElement('select', 'grade_item_scaleid', get_string('scale'), $options);
+        $mform->setHelpButton('grade_item_scaleid', array('scaleid', get_string('scaleid', 'grades'), 'grade'), true);
+        $mform->disabledIf('grade_item_scaleid', 'grade_item_gradetype', 'noteq', GRADE_TYPE_SCALE);
+
+        $mform->addElement('text', 'grade_item_grademax', get_string('grademax', 'grades'));
+        $mform->setHelpButton('grade_item_grademax', array('grademax', get_string('grademax', 'grades'), 'grade'), true);
+        $mform->disabledIf('grade_item_grademax', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE);
+
+        $mform->addElement('text', 'grade_item_grademin', get_string('grademin', 'grades'));
+        $mform->setHelpButton('grade_item_grademin', array('grademin', get_string('grademin', 'grades'), 'grade'), true);
+        $mform->disabledIf('grade_item_grademin', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE);
+
+        $mform->addElement('text', 'grade_item_gradepass', get_string('gradepass', 'grades'));
+        $mform->setHelpButton('grade_item_gradepass', array('gradepass', get_string('gradepass', 'grades'), 'grade'), true);
+        $mform->disabledIf('grade_item_gradepass', 'grade_item_gradetype', 'eq', GRADE_TYPE_NONE);
+        $mform->disabledIf('grade_item_gradepass', 'grade_item_gradetype', 'eq', GRADE_TYPE_TEXT);
+
+        $mform->addElement('text', 'grade_item_multfactor', get_string('multfactor', 'grades'));
+        $mform->setHelpButton('grade_item_multfactor', array('multfactor', get_string('multfactor', 'grades'), 'grade'), true);
+        $mform->setAdvanced('grade_item_multfactor');
+        $mform->disabledIf('grade_item_multfactor', 'grade_item_gradetype', 'eq', GRADE_TYPE_NONE);
+        $mform->disabledIf('grade_item_multfactor', 'grade_item_gradetype', 'eq', GRADE_TYPE_TEXT);
+
+        $mform->addElement('text', 'grade_item_plusfactor', get_string('plusfactor', 'grades'));
+        $mform->setHelpButton('grade_item_plusfactor', array('plusfactor', get_string('plusfactor', 'grades'), 'grade'), true);
+        $mform->setAdvanced('grade_item_plusfactor');
+        $mform->disabledIf('grade_item_plusfactor', 'grade_item_gradetype', 'eq', GRADE_TYPE_NONE);
+        $mform->disabledIf('grade_item_plusfactor', 'grade_item_gradetype', 'eq', GRADE_TYPE_TEXT);
+
+        /// grade display prefs
+        $default_gradedisplaytype = grade_get_setting($COURSE->id, 'displaytype', $CFG->grade_displaytype);
+        $options = array(GRADE_DISPLAY_TYPE_DEFAULT            => get_string('default', 'grades'),
+                         GRADE_DISPLAY_TYPE_REAL               => get_string('real', 'grades'),
+                         GRADE_DISPLAY_TYPE_PERCENTAGE         => get_string('percentage', 'grades'),
+                         GRADE_DISPLAY_TYPE_LETTER             => get_string('letter', 'grades'),
+                         GRADE_DISPLAY_TYPE_REAL_PERCENTAGE    => get_string('realpercentage', 'grades'),
+                         GRADE_DISPLAY_TYPE_REAL_LETTER        => get_string('realletter', 'grades'),
+                         GRADE_DISPLAY_TYPE_LETTER_REAL        => get_string('letterreal', 'grades'),
+                         GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE  => get_string('letterpercentage', 'grades'),
+                         GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER  => get_string('percentageletter', 'grades'),
+                         GRADE_DISPLAY_TYPE_PERCENTAGE_REAL    => get_string('percentagereal', 'grades')
+                         );
+
+        asort($options);
+
+        foreach ($options as $key=>$option) {
+            if ($key == $default_gradedisplaytype) {
+                $options[GRADE_DISPLAY_TYPE_DEFAULT] = get_string('defaultprev', 'grades', $option);
+                break;
+            }
+        }
+        $mform->addElement('select', 'grade_item_display', get_string('gradedisplaytype', 'grades'), $options);
+        $mform->setHelpButton('grade_item_display', array('gradedisplaytype', get_string('gradedisplaytype', 'grades'), 'grade'), true);
+
+        $default_gradedecimals = grade_get_setting($COURSE->id, 'decimalpoints', $CFG->grade_decimalpoints);
+        $options = array(-1=>get_string('defaultprev', 'grades', $default_gradedecimals), 0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5);
+        $mform->addElement('select', 'grade_item_decimals', get_string('decimalpoints', 'grades'), $options);
+        $mform->setHelpButton('grade_item_decimals', array('decimalpoints', get_string('decimalpoints', 'grades'), 'grade'), true);
+        $mform->setDefault('grade_item_decimals', -1);
+        $mform->disabledIf('grade_item_decimals', 'grade_item_display', 'eq', GRADE_DISPLAY_TYPE_LETTER);
+
+        if ($default_gradedisplaytype == GRADE_DISPLAY_TYPE_LETTER) {
+            $mform->disabledIf('grade_item_decimals', 'grade_item_display', "eq", GRADE_DISPLAY_TYPE_DEFAULT);
+        }
+
+        /// hiding
+        // advcheckbox is not compatible with disabledIf!
+        $mform->addElement('checkbox', 'grade_item_hidden', get_string('hidden', 'grades'));
+        $mform->setHelpButton('grade_item_hidden', array('hidden', get_string('hidden', 'grades'), 'grade'));
+        $mform->addElement('date_time_selector', 'grade_item_hiddenuntil', get_string('hiddenuntil', 'grades'), array('optional'=>true));
+        $mform->setHelpButton('grade_item_hiddenuntil', array('hiddenuntil', get_string('hiddenuntil', 'grades'), 'grade'));
+        $mform->disabledIf('grade_item_hidden', 'grade_item_hiddenuntil[off]', 'notchecked');
+
+        /// locking
+        $mform->addElement('checkbox', 'grade_item_locked', get_string('locked', 'grades'));
+        $mform->setHelpButton('grade_item_locked', array('locked', get_string('locked', 'grades'), 'grade'));
+
+        $mform->addElement('date_time_selector', 'grade_item_locktime', get_string('locktime', 'grades'), array('optional'=>true));
+        $mform->setHelpButton('grade_item_locktime', array('lockedafter', get_string('locktime', 'grades'), 'grade'));
+        $mform->disabledIf('grade_item_locktime', 'grade_item_gradetype', 'eq', GRADE_TYPE_NONE);
+
 /// parent category related settings
         $mform->addElement('header', 'headerparent', get_string('parentcategory', 'grades'));
 
         $options = array();
         $default = '';
-        $coefstring = '';
         $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id));
+
         foreach ($categories as $cat) {
             $cat->apply_forced_settings();
             $options[$cat->id] = $cat->get_name();
             if ($cat->is_course_category()) {
                 $default = $cat->id;
             }
-            if ($cat->is_aggregationcoef_used()) {
-                if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
-                    $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefweight') ? 'aggregationcoefweight' : 'aggregationcoef';
-
-                } else if ($cat->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
-                    $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefextra') ? 'aggregationcoefextra' : 'aggregationcoef';
-
-                } else {
-                    $coefstring = 'aggregationcoef';
-                }
-            } else {
-                $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $cat->id);
-            }
-        }
-        if (count($categories) > 1) {
-            $mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options);
         }
 
-        if ($coefstring !== '') {
-            $mform->addElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
-            $mform->setHelpButton('aggregationcoef', array('aggregationcoefweight', get_string('aggregationcoef', 'grades'), 'grade'), true);
+        if (count($categories) > 1) {
+            $mform->addElement('select', 'parentcategory', get_string('parentcategory', 'grades'), $options);
+            $mform->addElement('static', 'currentparentaggregation', get_string('currentparentaggregation', 'grades'));
         }
 
 /// user preferences
@@ -170,6 +272,7 @@
         $mform =& $this->_form;
 
         $somecat = new grade_category();
+
         foreach ($somecat->forceable as $property) {
             if ((int)$CFG->{"grade_{$property}_flag"} & 1) {
                 if ($mform->elementExists($property)) {
@@ -194,18 +297,24 @@
             }
         }
 
+        $current_aggregation = null;
+
         if ($id = $mform->getElementValue('id')) {
             $grade_category = grade_category::fetch(array('id'=>$id));
             $grade_item = $grade_category->load_grade_item();
 
+            $current_aggregation = $grade_category->aggregation;
 
             // remove agg coef if not used
             if ($grade_category->is_course_category()) {
                 if ($mform->elementExists('parentcategory')) {
                     $mform->removeElement('parentcategory');
                 }
-                if ($mform->elementExists('aggregationcoef')) {
-                    $mform->removeElement('aggregationcoef');
+                if ($mform->elementExists('grade_item_aggregationcoef')) {
+                    $mform->removeElement('grade_item_aggregationcoef');
+                }
+                if ($mform->elementExists('currentparentaggregation')) {
+                    $mform->removeElement('currentparentaggregation');
                 }
 
             } else {
@@ -213,28 +322,9 @@
                 if ($mform->elementExists('parentcategory')) {
                     $mform->hardFreeze('parentcategory');
                 }
+                $parent_cat = $grade_category->get_parent_category();
+                $mform->setDefault('currentparentaggregation', $this->aggregation_options[$parent_cat->aggregation]);
 
-                $parent_category = $grade_category->get_parent_category();
-                $parent_category->apply_forced_settings();
-                if (!$parent_category->is_aggregationcoef_used()) {
-                    if ($mform->elementExists('aggregationcoef')) {
-                        $mform->removeElement('aggregationcoef');
-                    }
-                } else {
-                    //fix label if needed
-                    $agg_el =& $mform->getElement('aggregationcoef');
-                    $aggcoef = '';
-                    if ($parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
-                        $aggcoef = 'aggregationcoefweight';
-                    } else if ($parent_category->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
-                        $aggcoef = 'aggregationcoefextra';
-                    }
-                    if ($aggcoef !== '') {
-                        $agg_el->setLabel(get_string($aggcoef, 'grades'));
-                        $mform->setHelpButton('aggregationcoef', array('aggregationcoef', get_string('aggregationcoef', 'grades'), 'grade'), true);
-                    }
-                }
-                
             }
 
             if ($grade_item->is_calculated()) {
@@ -258,7 +348,7 @@
                     $mform->removeElement('aggregatesubcats');
                 }
             }
-            
+
             // If it is a course category, remove the "required" rule from the "fullname" element
             if ($grade_category->is_course_category()) {
                 unset($mform->_rules['fullname']);
@@ -268,16 +358,127 @@
 
             // If it is a course category and its fullname is ?, show an empty field
             if ($grade_category->is_course_category() && $mform->getElementValue('fullname') == '?') {
-                $mform->setDefault('fullname', ''); 
+                $mform->setDefault('fullname', '');
+            }
+            // remove unwanted aggregation options
+            if ($mform->elementExists('aggregation')) {
+                $agg_el =& $mform->getElement('aggregation');
+                $visible = explode(',', $CFG->grade_aggregations_visible);
+                if (!is_null($current_aggregation)) {
+                    // current type is always visible
+                    $visible[] = $current_aggregation;
+                }
+                foreach ($this->allaggoptions as $type) {
+                    if (!in_array($type, $visible) && $grade_category->aggregation != $type) {
+                        $agg_el->removeOption($type);
+                    }
+                }
-            } 
+            }
         }
 
+
         // no parent header for course category
         if (!$mform->elementExists('aggregationcoef') and !$mform->elementExists('parentcategory')) {
             $mform->removeElement('headerparent');
         }
 
+/// GRADE ITEM
+        if ($id = $mform->getElementValue('id')) {
+            $grade_category = grade_category::fetch(array('id'=>$id));
+            $grade_item = $grade_category->load_grade_item();
+
+            $mform->setDefault('grade_item_hidden', (int) $grade_item->hidden);
+
+            if (!$grade_item->is_raw_used()) {
+                $mform->removeElement('grade_item_plusfactor');
+                $mform->removeElement('grade_item_multfactor');
+            }
+
+            if ($grade_item->is_outcome_item()) {
+                // we have to prevent incompatible modifications of outcomes if outcomes disabled
+                $mform->removeElement('grade_item_grademax');
+                $mform->removeElement('grade_item_grademin');
+                $mform->removeElement('grade_item_gradetype');
+                $mform->removeElement('grade_item_display');
+                $mform->removeElement('grade_item_decimals');
+                $mform->hardFreeze('grade_item_scaleid');
+
+            } else {
+                if ($grade_item->is_external_item()) {
+                    // following items are set up from modules and should not be overrided by user
+                    $mform->hardFreeze('grade_item_itemname,grade_item_idnumber,grade_item_gradetype,grade_item_grademax,grade_item_grademin,grade_item_scaleid');
+                    //$mform->removeElement('calculation');
+                }
+            }
+
+            //remove the aggregation coef element if not needed
+            if ($grade_item->is_course_item()) {
+                if ($mform->elementExists('grade_item_parentcategory')) {
+                    $mform->removeElement('grade_item_parentcategory');
+                }
+                if ($mform->elementExists('grade_item_aggregationcoef')) {
+                    $mform->removeElement('grade_item_aggregationcoef');
+                }
+
+            } else {
+                // if we wanted to change parent of existing item - we would have to verify there are no circular references in parents!!!
+                if ($mform->elementExists('grade_item_parentcategory')) {
+                    $mform->hardFreeze('grade_item_parentcategory');
+                }
+
+                if ($grade_item->is_category_item()) {
+                    $category = $grade_item->get_item_category();
+                    $parent_category = $category->get_parent_category();
+                } else {
+                    $parent_category = $grade_item->get_parent_category();
+                }
+
+                $parent_category->apply_forced_settings();
+
+                if (!$parent_category->is_aggregationcoef_used()) {
+                    if ($mform->elementExists('grade_item_aggregationcoef')) {
+                        $mform->removeElement('grade_item_aggregationcoef');
+                    }
+                } else {
+
+                    $coefstring = $grade_item->get_coefstring();
+
+                    if ($coefstring == 'aggregationcoefextrasum') {
+                        // advcheckbox is not compatible with disabledIf!
+                        $element =& $mform->createElement('checkbox', 'grade_item_aggregationcoef', get_string($coefstring, 'grades'));
+                    } else {
+                        $element =& $mform->createElement('text', 'grade_item_aggregationcoef', get_string($coefstring, 'grades'));
+                    }
+                    $mform->insertElementBefore($element, 'parentcategory');
+                    $mform->setDefault('grade_item_aggregationcoef', (int) $grade_item->aggregationcoef); // must be cast to int, otherwise "0" counts as true :S
+                    $mform->setHelpButton('grade_item_aggregationcoef', array($coefstring, get_string($coefstring, 'grades'), 'grade'), true);
+                    $mform->disabledIf('grade_item_aggregationcoef', 'grade_item_parentcategory', 'eq', $parent_category->id);
+                }
+            }
+
+            if ($category = $grade_item->get_item_category()) {
+                if ($category->aggregation == GRADE_AGGREGATE_SUM) {
+                    if ($mform->elementExists('grade_item_gradetype')) {
+                        $mform->hardFreeze('grade_item_gradetype');
+                    }
+                    if ($mform->elementExists('grade_item_grademin')) {
+                        $mform->hardFreeze('grade_item_grademin');
+                    }
+                    if ($mform->elementExists('grade_item_grademax')) {
+                        $mform->hardFreeze('grade_item_grademax');
+                    }
+                    if ($mform->elementExists('grade_item_scaleid')) {
+                        $mform->removeElement('grade_item_scaleid');
+                    }
+                }
+            }
+
+        } else {
+            // all new items are manual, children of course category
+            $mform->removeElement('grade_item_plusfactor');
+            $mform->removeElement('grade_item_multfactor');
+        }
-    } 
+    }
 }
 
 ?>
Index: grade/edit/tree/functions.js
=========================================================
--- grade/edit/tree/functions.js	Thu Apr 16 09:31:42 CEST 2009
+++ grade/edit/tree/functions.js	Thu Apr 16 09:31:42 CEST 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/index.php
=========================================================
--- grade/edit/tree/index.php	(revision 1.12.2.6)
+++ grade/edit/tree/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -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,52 @@
 $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) && confirm_sesskey()) {
+    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();
+    grade_regrade_final_grades($courseid);
+}
+
 //first make sure we have proper final grades - we need it for locking changes
 grade_regrade_final_grades($courseid);
 
@@ -59,7 +110,7 @@
 
 } else {
     if (!$element = $gtree->locate_element($eid)) {
-        error('Incorrect element id!', $returnurl);
+        print_error('invalidelementid', '', $returnurl);
     }
     $object = $element['object'];
 }
@@ -74,10 +125,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 ($eid && confirm_sesskey()) {
+            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 +142,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 +164,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 +188,8 @@
 
     case 'moveselect':
         if ($eid and confirm_sesskey()) {
-            $moving = $eid;
+            $grade_edit_tree->moving = $eid;
+            $moving=true;
         }
         break;
 
@@ -135,143 +197,181 @@
         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;
+}
 
-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();
+$CFG->stylesheets[] = $CFG->wwwroot.'/grade/edit/tree/tree.css';
 
-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');
-    echo "<br /><br />";
-    print_single_button('index.php', array('id'=>$course->id, 'action'=>'synclegacy'), get_string('synclegacygrades', 'grades'), 'get');
-    helpbutton('synclegacygrades', get_string('synclegacygrades', 'grades'), 'grade');
 }
-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) && confirm_sesskey()) {
+        $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) && confirm_sesskey()) {
+            $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>';
-    }
+            $grade_category = grade_category::fetch(array('id'=>$a->id, 'courseid'=>$courseid));
+            $grade_category->$param = $value;
+
+            $grade_category->update();
+            grade_regrade_final_grades($courseid);
 
-    $actions .= $gtree->get_hiding_icon($element, $gpr);
-    $actions .= $gtree->get_locking_icon($element, $gpr);
+        // Grade item text inputs
+        } elseif (preg_match('/(grademax|aggregationcoef|multfactor|plusfactor)_([0-9]*)/', $key, $matches) && confirm_sesskey()) {
+            $defaults = array('grademax' => 100, 'aggregationcoef' => 1, 'multfactor' => 1, 'plusfactor' => 0);
 
-/// 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 (is_string($_POST[$matches[0]]) && strlen($_POST[$matches[0]]) < 1) {
+                $_POST[$matches[0]] = null;
-    }
+            }
+            $value = optional_param($matches[0], $defaults[$matches[1]], PARAM_NUMBER);
 
-/// print the list items now
-    if ($moving == $eid) {
-        // do not diplay children
-        echo '<li class="'.$element['type'].' moving">'.$header.'('.get_string('move').')</li>';
+            $param = $matches[1];
+            $a->id = $matches[2];
+            $grade_item = grade_item::fetch(array('id'=>$a->id, 'courseid'=>$courseid));
+            $grade_item->$param = $value;
 
-    } else if ($element['type'] != 'category') {
-        if ($catcourseitem and $switch) {
-            if ($switchedlast) {
-                echo '<li class="'.$element['type'].'">'.$header.$actions.'</li>';
+            $grade_item->update();
+            grade_regrade_final_grades($courseid);
+
+        // Grade item checkbox inputs
+        } elseif (preg_match('/extracredit_original_([0-9]*)/', $key, $matches) && confirm_sesskey()) { // 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;
+            if ($original_value == 1 && is_null($extracredit)) {
+                $newvalue = 0;
+            } elseif ($original_value == 0 && $extracredit == 1) {
+                $newvalue = 1;
             } else {
-                echo $moveto;
+                continue;
             }
+
+            $grade_item = grade_item::fetch(array('id'=>$a->id, 'courseid'=>$courseid));
+            $grade_item->aggregationcoef = $newvalue;
+
+            $grade_item->update();
+            grade_regrade_final_grades($courseid);
+
+        // Grade category checkbox inputs
+        } elseif (preg_match('/aggregate(onlygraded|subcats|outcomes)_original_([0-9]*)/', $key, $matches) && confirm_sesskey()) {
+            $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.'</li>'.$moveto;
+                continue;
-        }
+            }
 
-    } 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;
+            $grade_category = grade_category::fetch(array('id'=>$a->id, 'courseid'=>$courseid));
+            $grade_category->{'aggregate'.$matches[1]} = $newvalue;
+
+            $grade_category->update();
+            grade_regrade_final_grades($courseid);
-            }
+        }
-            print_grade_tree($gtree, $child_el, $moving, $gpr, $switch);
-        }
+    }
-        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
-        }
-    }
+}
+
+// Print Table of categories and items
+print_box_start('gradetreebox generalbox');
+
+echo '<form id="gradetreeform" method="post" action="'.$returnurl.'">';
+echo '<div>';
+echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
+
+// 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);
+
+echo '<div id="gradetreesubmit">';
+if (!$moving) {
+    echo '<input class="advanced" type="submit" value="'.get_string('savechanges').'" />';
 }
 
-function element_deletable($element) {
-    global $COURSE;
+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>';
+}
 
-    if ($element['type'] != 'item') {
-        return true;
-    }
+echo '</div>';
+
+echo '</div></form>';
 
-    $grade_item = $element['object'];
+print_box_end();
 
-    if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) {
-        return true;
-    }
+// Print action buttons
+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');
 
-    $modinfo = get_fast_modinfo($COURSE);
-    if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) {
-        // module does not exist
-        return true;
+    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'=>'autosort'), get_string('autosort', 'grades'), 'get');
+    echo "<br /><br />";
+    print_single_button('index.php', array('id'=>$course->id, 'action'=>'synclegacy'), get_string('synclegacygrades', 'grades'), 'get');
+    helpbutton('synclegacygrades', get_string('synclegacygrades', 'grades'), 'grade');
+}
+
+echo '</div>';
+
+print_footer($course);
+
+// Restore original show/hide preference if moving
+if ($moving) {
+    $USER->gradeediting[$course->id] = $original_gradeediting;
 }
+die;
 
 ?>
Index: grade/edit/tree/item.php
=========================================================
--- grade/edit/tree/item.php	(revision 1.14.2.4)
+++ grade/edit/tree/item.php	Thu Apr 16 09:31:42 CEST 2009
@@ -49,6 +49,8 @@
     redirect($returnurl);
 }
 
+$heading = get_string('itemsedit', 'grades');
+
 if ($grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$courseid))) {
     // redirect if outcomeid present
     if (!empty($grade_item->outcomeid) && !empty($CFG->enableoutcomes)) {
@@ -69,6 +71,7 @@
         $item->parentcategory = $parent_category->id;
     }
 } else {
+    $heading = get_string('newitem', 'grades');
     $grade_item = new grade_item(array('courseid'=>$courseid, 'itemtype'=>'manual'), false);
     $item = $grade_item->get_record_data();
     $parent_category = grade_category::fetch_course_category($courseid);
@@ -159,15 +162,7 @@
     redirect($returnurl);
 }
 
-$strgrades       = get_string('grades');
-$strgraderreport = get_string('graderreport', 'grades');
-$stritemsedit    = get_string('itemsedit', 'grades');
-$stritem         = get_string('item', 'grades');
-
-$navigation = grade_build_nav(__FILE__, $stritem, array('courseid' => $courseid));
-
-
-print_header_simple($strgrades . ': ' . $strgraderreport, ': ' . $stritemsedit, $navigation, '', '', true, '', navmenu($course));
+print_grade_page_head($courseid, 'edittree', null, $heading);
 
 $mform->display();
 
Index: grade/edit/tree/item_form.php
=========================================================
--- grade/edit/tree/item_form.php	(revision 1.20.2.19)
+++ grade/edit/tree/item_form.php	Thu Apr 16 09:31:42 CEST 2009
@@ -149,44 +149,20 @@
         $default = '';
         $coefstring = '';
         $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id));
+
         foreach ($categories as $cat) {
             $cat->apply_forced_settings();
             $options[$cat->id] = $cat->get_name();
+
             if ($cat->is_course_category()) {
                 $default = $cat->id;
             }
-            if ($cat->is_aggregationcoef_used()) {
-                if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
-                    $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefweight') ? 'aggregationcoefweight' : 'aggregationcoef';
-
-                } else if ($cat->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
-                    $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefextra') ? 'aggregationcoefextra' : 'aggregationcoef';
-
-                } else if ($cat->aggregation == GRADE_AGGREGATE_SUM) {
-                    $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefextrasum') ? 'aggregationcoefextrasum' : 'aggregationcoef';
-
-                } else {
-                    $coefstring = 'aggregationcoef';
-                }
-            } else {
-                $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $cat->id);
-            }
         }
 
         if (count($categories) > 1) {
             $mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options);
         }
 
-        if ($coefstring !== '') {
-            if ($coefstring == 'aggregationcoefextrasum') {
-                // advcheckbox is not compatible with disabledIf!
-                $mform->addElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
-            } else {
-                $mform->addElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
-            }
-            $mform->setHelpButton('aggregationcoef', array($coefstring, get_string($coefstring, 'grades'), 'grade'), true);
-        }
-
 /// hidden params
         $mform->addElement('hidden', 'id', 0);
         $mform->setType('id', PARAM_INT);
@@ -276,23 +252,26 @@
                         $mform->removeElement('aggregationcoef');
                     }
                 } else {
-                    //fix label if needed
-                    $agg_el =& $mform->getElement('aggregationcoef');
-                    $aggcoef = '';
-                    if ($parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
-                        $aggcoef = 'aggregationcoefweight';
-
-                    } else if ($parent_category->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
-                        $aggcoef = 'aggregationcoefextra';
-
-                    } else if ($parent_category->aggregation == GRADE_AGGREGATE_SUM) {
-                        $aggcoef = 'aggregationcoefextrasum';
+                    if ($grade_item->is_category_item()) {
+                        $parent_category = $parent_category->get_parent_category();
+                        $coefstring = $parent_category->get_grade_item()->get_coefstring();
+                    } else {
+                        $parent_category->apply_forced_settings();
+                        $coefstring = $grade_item->get_coefstring();
                     }
 
-                    if ($aggcoef !== '') {
-                        $agg_el->setLabel(get_string($aggcoef, 'grades'));
-                        $mform->setHelpButton('aggregationcoef', array($aggcoef, get_string($aggcoef, 'grades'), 'grade'), true);
+                    if ($coefstring !== '') {
+                        if ($coefstring == 'aggregationcoefextrasum') {
+                            // advcheckbox is not compatible with disabledIf!
+                            $element =& $mform->createElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
+                        } else {
+                            $element =& $mform->createElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
-                    }
+                        }
+                        $mform->insertElementBefore($element, 'parentcategory');
+                        $mform->setHelpButton('aggregationcoef', array($coefstring, get_string($coefstring, 'grades'), 'grade'), true);
+                    }
+
+                    $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $parent_category->id);
                 }
             }
 
Index: grade/edit/tree/lib.php
=========================================================
--- grade/edit/tree/lib.php	Thu Apr 16 09:33:38 CEST 2009
+++ grade/edit/tree/lib.php	Thu Apr 16 09:33:38 CEST 2009
@@ -0,0 +1,965 @@
+<?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 {
+    var $columns = array();
+
+    /**
+     * @var object $gtree          @see grade/lib.php
+     */
+    var $gtree;
+
+    /**
+     * @var grade_plugin_return @see grade/lib.php
+     */
+    var $gpr;
+
+    /**
+     * @var string              $moving The eid of the category or item being moved
+     */
+    var $moving;
+
+    var $deepest_level;
+
+    var $uses_extra_credit = false;
+
+    var $uses_weight = false;
+
+    /**
+     * Constructor
+     */
+    function grade_edit_tree($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)));
+
+        if ($this->uses_weight) {
+            $this->columns[] = grade_edit_tree_column::factory('weight', array('adv' => 'aggregationcoef'));
+        }
+        if ($this->uses_extra_credit) {
+            $this->columns[] = grade_edit_tree_column::factory('extracredit', array('adv' => 'aggregationcoef'));
+        }
+
+        $this->columns[] = grade_edit_tree_column::factory('range'); // This is not a setting... How do we deal with it?
+        $this->columns[] = grade_edit_tree_column::factory('aggregateonlygraded', array('flag' => true));
+        $this->columns[] = grade_edit_tree_column::factory('aggregatesubcats', array('flag' => true));
+        $this->columns[] = grade_edit_tree_column::factory('aggregateoutcomes', array('flag' => true));
+        $this->columns[] = grade_edit_tree_column::factory('droplow', array('flag' => true));
+        $this->columns[] = grade_edit_tree_column::factory('keephigh', array('flag' => true));
+        $this->columns[] = grade_edit_tree_column::factory('multfactor', array('adv' => true));
+        $this->columns[] = grade_edit_tree_column::factory('plusfactor', array('adv' => true));
+        $this->columns[] = grade_edit_tree_column::factory('actions');
+        $this->columns[] = 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
+     */
+    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
+     * @param string type "extra" or "weight": the type of the column hosting the weight input
+     * @return string HTML
+     */
+    function get_weight_input($item, $type) {
+        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();
+        $parent_category->apply_forced_settings();
+        $aggcoef = $item->get_coefstring();
+
+        if ((($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoef') && $type == 'weight') ||
+            ($aggcoef == 'aggregationcoefextra' && $type == 'extra')) {
+            return '<input type="text" size="6" id="aggregationcoef_'.$item->id.'" name="aggregationcoef_'.$item->id.'"
+                value="'.format_float($item->aggregationcoef).'" />';
+        } elseif ($aggcoef == 'aggregationcoefextrasum' && $type == 'extra') {
+            $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++;
+        $coefstring = $element['object']->get_coefstring();
+        if ($element['type'] == 'category') {
+            if ($coefstring == 'aggregationcoefweight') {
+                $this->uses_weight = true;
+            } elseif ($coefstring ==  'aggregationcoefextra' || $coefstring == 'aggregationcoefextrasum') {
+                $this->uses_extra_credit = true;
+            }
+
+            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;
+    }
+}
+
+class grade_edit_tree_column {
+    var $forced;
+    var $hidden;
+    var $forced_hidden;
+    var $advanced_hidden;
+    var $hide_when_moving = true;
+
+    function factory($name, $params=array()) {
+        $class_name = "grade_edit_tree_column_$name";
+        if (class_exists($class_name)) {
+            return new $class_name($params);
+        }
+    }
+
+    function get_header_cell() {}
+
+    function get_category_cell($category, $levelclass, $params) {}
+
+    function get_item_cell($item, $params) {}
+
+    function is_hidden($mode='simple') {}
+}
+
+class grade_edit_tree_column_category extends grade_edit_tree_column {
+
+    var $forced;
+    var $advanced;
+
+    function __construct($name) {
+        global $CFG;
+        $this->forced = (int)$CFG->{"grade_$name"."_flag"} & 1;
+        $this->advanced = (int)$CFG->{"grade_$name"."_flag"} & 2;
+    }
+
+    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 {
+    var $forced = false;
+    var $hidden = false;
+    var $forced_hidden = false;
+    var $advanced_hidden = false;
+    var $deepest_level = 1;
+    var $hide_when_moving = false;
+
+    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'];
+    }
+
+    function get_header_cell() {
+        return '<th class="header name" colspan="'.($this->deepest_level + 1).'" scope="col">'.get_string('name').'</th>';
+    }
+
+    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";
+    }
+
+    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>';
+    }
+
+    function is_hidden($mode='simple') {
+        return false;
+    }
+}
+
+class grade_edit_tree_column_aggregation extends grade_edit_tree_column_category {
+
+    function __construct($params) {
+        parent::__construct('aggregation');
+    }
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('aggregation', 'grades').helpbutton('aggregation', 'aggregation', 'grade', true, false, '', true).'</th>';
+    }
+
+    function get_category_cell($category, $levelclass, $params) {
+        global $CFG;
+        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'));
+
+        $visible = explode(',', $CFG->grade_aggregations_visible);
+        foreach ($options as $constant => $string) {
+            if (!in_array($constant, $visible) && $constant != $category->aggregation) {
+                unset($options[$constant]);
+            }
+        }
+
+        $script = "window.location='index.php?id={$params['id']}&amp;category={$category->id}&amp;aggregationtype='+this.value+'&amp;sesskey=" . sesskey()."';";
+        $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>';
+
+    }
+
+    function get_item_cell($item, $params) {
+          return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_extracredit extends grade_edit_tree_column {
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('extracredit', 'grades').helpbutton('aggregationcoefextra', 'aggregationcoefextra', 'grade', true, false, '', true).'</th>';
+    }
+
+    function get_category_cell($category, $levelclass, $params) {
+
+        $item = $category->get_grade_item();
+        $aggcoef_input = grade_edit_tree::get_weight_input($item, 'extra');
+        return '<td class="cell '.$levelclass.'">' . $aggcoef_input . '</td>';
+    }
+
+    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)');
+        }
+
+        $html = '<td class="cell">';
+
+        if (!in_array($params['element']['object']->itemtype, array('courseitem', 'categoryitem', 'category'))) {
+            $html .= grade_edit_tree::get_weight_input($item, 'extra');
+        }
+
+        return $html.'</td>';
+    }
+
+    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_weight extends grade_edit_tree_column {
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('weightuc', 'grades').helpbutton('aggregationcoefweight', 'aggregationcoefweight', 'grade', true, false, '', true).'</th>';
+    }
+
+    function get_category_cell($category, $levelclass, $params) {
+
+        $item = $category->get_grade_item();
+        $aggcoef_input = grade_edit_tree::get_weight_input($item, 'weight');
+        return '<td class="cell '.$levelclass.'">' . $aggcoef_input . '</td>';
+    }
+
+    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)');
+        }
+
+        $html = '<td class="cell">';
+
+        if (!in_array($params['element']['object']->itemtype, array('courseitem', 'categoryitem', 'category'))) {
+            $html .= grade_edit_tree::get_weight_input($item, 'weight');
+        }
+
+        return $html.'</td>';
+    }
+
+    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 {
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('maxgrade', 'grades').'</th>';
+    }
+
+    function get_category_cell($category, $levelclass, $params) {
+        return '<td class="cell range '.$levelclass.'"> - </td>';
+    }
+
+    function get_item_cell($item, $params) {
+
+        // If the parent aggregation is Sum of Grades, this cannot be changed
+        $parent_cat = $item->get_parent_category();
+        if ($parent_cat->aggregation == GRADE_AGGREGATE_SUM) {
+            $grademax = format_float($item->grademax, $item->get_decimals());
+        } elseif ($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>';
+    }
+
+    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 {
+
+    function __construct($params) {
+        parent::__construct('aggregateonlygraded');
+    }
+
+    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>';
+    }
+
+    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 . $hidden.'</td>';
+    }
+
+    function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_aggregatesubcats extends grade_edit_tree_column_category {
+
+    function __construct($params) {
+        parent::__construct('aggregatesubcats');
+    }
+
+    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>';
+    }
+
+    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>';
+
+    }
+
+    function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_aggregateoutcomes extends grade_edit_tree_column_category {
+
+    function __construct($params) {
+        parent::__construct('aggregateoutcomes');
+    }
+
+    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>';
+    }
+
+    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>';
+    }
+
+    function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+
+    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 {
+
+    function __construct($params) {
+        parent::__construct('droplow');
+    }
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('droplow', 'grades').helpbutton('droplow', 'droplow', 'grade', true, false, '', true).'</th>';
+    }
+
+    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>';
+    }
+
+    function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_keephigh extends grade_edit_tree_column_category {
+
+    function __construct($params) {
+        parent::__construct('keephigh');
+    }
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('keephigh', 'grades').helpbutton('keephigh', 'keephigh', 'grade', true, false, '', true).'</th>';
+    }
+
+    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>';
+    }
+
+    function get_item_cell($item, $params) {
+        return '<td class="cell"> - </td>';
+    }
+}
+
+class grade_edit_tree_column_multfactor extends grade_edit_tree_column {
+
+    function __construct($params) {
+
+    }
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('multfactor', 'grades').helpbutton('multfactor', 'multfactor', 'grade', true, false, '', true).'</th>';
+    }
+
+    function get_category_cell($category, $levelclass, $params) {
+
+        return '<td class="cell '.$levelclass.'"> - </td>';
+    }
+
+    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>';
+    }
+
+    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 {
+
+    function get_header_cell() {
+        return '<th class="header" scope="col">'.get_string('plusfactor', 'grades').helpbutton('plusfactor', 'plusfactor', 'grade', true, false, '', true).'</th>';
+    }
+
+    function get_category_cell($category, $levelclass, $params) {
+        return '<td class="cell '.$levelclass.'"> - </td>';
+
+    }
+
+    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>';
+
+    }
+
+    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 {
+
+    function __construct($params) {
+
+    }
+
+    function get_header_cell() {
+        return '<th class="header actions" scope="col">'.get_string('actions').'</th>';
+    }
+
+    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>';
+    }
+
+    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>';
+    }
+
+    function is_hidden($mode='simple') {
+        return false;
+    }
+}
+
+class grade_edit_tree_column_select extends grade_edit_tree_column {
+
+    function get_header_cell() {
+        return '<th class="header selection" scope="col">'.get_string('select').'</th>';
+    }
+
+    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>';
+    }
+
+    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>';
+    }
+
+    function is_hidden($mode='simple') {
+        return false;
+    }
+}
+?>
Index: grade/edit/tree/outcomeitem.php
=========================================================
--- grade/edit/tree/outcomeitem.php	(revision 1.15.2.7)
+++ grade/edit/tree/outcomeitem.php	Thu Apr 16 09:31:42 CEST 2009
@@ -50,6 +50,8 @@
     redirect($returnurl);
 }
 
+$heading = get_string('outcomeitemsedit', 'grades');
+
 if ($grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$courseid))) {
     // redirect if outcomeid present
     if (empty($grade_item->outcomeid)) {
@@ -69,6 +71,7 @@
     }
 
 } else {
+    $heading = get_string('newoutcomeitem', 'grades');
     $grade_item = new grade_item(array('courseid'=>$courseid, 'itemtype'=>'manual'), false);
     $item = $grade_item->get_record_data();
     $item->cmid = 0;
@@ -205,14 +208,7 @@
     redirect($returnurl);
 }
 
-$strgrades       = get_string('grades');
-$strgraderreport = get_string('graderreport', 'grades');
-$stroutcomesedit = get_string('outcomeitemsedit', 'grades');
-$stroutcome      = get_string('outcomeitem', 'grades');
-
-$navigation = grade_build_nav(__FILE__, $stroutcome, array('courseid' => $courseid));
-
-print_header_simple($strgrades . ': ' . $strgraderreport, ': ' . $stroutcomesedit, $navigation, '', '', true, '', navmenu($course));
+print_grade_page_head($courseid, 'edittree', null, $heading);
 
 if (!grade_outcome::fetch_all_available($COURSE->id)) {
     notice_yesno(get_string('nooutcomes', 'grades'), $CFG->wwwroot.'/grade/edit/outcome/course.php?id='.$courseid, $returnurl);
Index: grade/edit/tree/tree.css
=========================================================
--- grade/edit/tree/tree.css	Thu Apr 16 09:31:42 CEST 2009
+++ grade/edit/tree/tree.css	Thu Apr 16 09:31:42 CEST 2009
@@ -0,0 +1,130 @@
+.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: #f3dfd0 !important;
+    width: 10px;
+}
+.level2 {
+    background-color: #d0dbf3 !important;
+    width: 10px;
+}
+.level3 {
+    background-color: #d0f3d6 !important;
+    width: 10px;
+}
+.level4 {
+    background-color: #f0f0aa !important;
+    width: 10px;
+}
+.level5 {
+    background-color: #ebdef6 !important;
+    width: 10px;
+}
+
+.gradetreebox table.generaltable {
+    border:  1px solid #AAAAAA;
+    width: 100%;
+}
Index: grade/export/keymanager.php
=========================================================
--- grade/export/keymanager.php	(revision 1.2)
+++ grade/export/keymanager.php	Thu Apr 16 09:31:42 CEST 2009
@@ -37,11 +37,7 @@
 
 require_capability('moodle/grade:export', $context);
 
-$strgrades = get_string('grades', 'grades');
-$navigation = grade_build_nav(__FILE__, null, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, '', '');
+print_grade_page_head($course->id, 'export', 'keymanager', get_string('keymanager', 'grades'));
 
 $stredit         = get_string('edit');
 $strdelete       = get_string('delete');
Index: grade/export/ods/index.php
=========================================================
--- grade/export/ods/index.php	(revision 1.27.2.1)
+++ grade/export/ods/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -39,13 +39,7 @@
 require_capability('moodle/grade:export', $context);
 require_capability('gradeexport/ods:view', $context);
 
-
-$strgrades = get_string('grades', 'grades');
-$actionstr = get_string('modulename', 'gradeexport_ods');
-$navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, 'export', 'ods');
+print_grade_page_head($COURSE->id, 'export', 'ods', get_string('exportto', 'grades') . ' ' . get_string('modulename', 'gradeexport_ods'));
 
 if (!empty($CFG->gradepublishing)) {
     $CFG->gradepublishing = has_capability('gradeexport/ods:publish', $context);
Index: grade/export/txt/index.php
=========================================================
--- grade/export/txt/index.php	(revision 1.31.2.1)
+++ grade/export/txt/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -39,13 +39,7 @@
 require_capability('moodle/grade:export', $context);
 require_capability('gradeexport/txt:view', $context);
 
-
-$strgrades = get_string('grades', 'grades');
-$actionstr = get_string('modulename', 'gradeexport_txt');
-$navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, 'export', 'txt');
+print_grade_page_head($COURSE->id, 'export', 'txt', get_string('exportto', 'grades') . ' ' . get_string('modulename', 'gradeexport_txt'));
 
 if (!empty($CFG->gradepublishing)) {
     $CFG->gradepublishing = has_capability('gradeexport/txt:publish', $context);
Index: grade/export/xls/index.php
=========================================================
--- grade/export/xls/index.php	(revision 1.26.2.1)
+++ grade/export/xls/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -39,13 +39,7 @@
 require_capability('moodle/grade:export', $context);
 require_capability('gradeexport/xls:view', $context);
 
-
-$strgrades = get_string('grades', 'grades');
-$actionstr = get_string('modulename', 'gradeexport_xls');
-$navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, 'export', 'xls');
+print_grade_page_head($COURSE->id, 'export', 'xls', get_string('exportto', 'grades') . ' ' . get_string('modulename', 'gradeexport_xls'));
 
 if (!empty($CFG->gradepublishing)) {
     $CFG->gradepublishing = has_capability('gradeexport/xls:publish', $context);
Index: grade/export/xml/grade_export_xml.php
=========================================================
--- grade/export/xml/grade_export_xml.php	(revision 1.22.2.1)
+++ grade/export/xml/grade_export_xml.php	Thu Apr 16 09:31:42 CEST 2009
@@ -29,7 +29,7 @@
 
     var $plugin = 'xml';
     var $updatedgradesonly = false; // default to export ALL grades
-    
+
     /**
      * To be implemented by child classes
      * @param boolean $feedback
@@ -75,12 +75,12 @@
                 $grade_item = $this->grade_items[$itemid];
                 $grade->grade_item =& $grade_item;
                 $gradestr = $this->format_grade($grade); // no formating for now
-                
+
                 // MDL-11669, skip exported grades or bad grades (if setting says so)
                 if ($export_tracking) {
                     $status = $geub->track($grade);
                     if ($this->updatedgradesonly && ($status == 'nochange' || $status == 'unknown')) {
-                        continue; 
+                        continue;
                     }
                 }
 
Index: grade/export/xml/index.php
=========================================================
--- grade/export/xml/index.php	(revision 1.35.2.2)
+++ grade/export/xml/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -39,13 +39,7 @@
 require_capability('moodle/grade:export', $context);
 require_capability('gradeexport/xml:view', $context);
 
-
-$strgrades = get_string('grades', 'grades');
-$actionstr = get_string('modulename', 'gradeexport_xml');
-$navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, 'export', 'xml');
+print_grade_page_head($COURSE->id, 'export', 'xml', get_string('exportto', 'grades') . ' ' . get_string('modulename', 'gradeexport_xml'));
 
 if (!empty($CFG->gradepublishing)) {
     $CFG->gradepublishing = has_capability('gradeexport/xml:publish', $context);
Index: grade/import/csv/index.php
=========================================================
--- grade/import/csv/index.php	(revision 1.31.2.7)
+++ grade/import/csv/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -61,12 +61,8 @@
     $csv_encode = '/\&\#44/';
 }
 
-$strgrades = get_string('grades', 'grades');
 $actionstr = get_string('csv', 'grades');
-$navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, 'import', 'csv');
+print_grade_page_head($course->id, 'import', 'csv');
 
 // set up import form
 $mform = new grade_import_form(null, array('includeseparator'=>!isset($CFG->CSV_DELIMITER), 'verbosescales'=>true));
Index: grade/import/keymanager.php
=========================================================
--- grade/import/keymanager.php	(revision 1.1)
+++ grade/import/keymanager.php	Thu Apr 16 09:31:42 CEST 2009
@@ -37,11 +37,7 @@
 
 require_capability('moodle/grade:import', $context);
 
-$strgrades = get_string('grades', 'grades');
-$navigation = grade_build_nav(__FILE__, null, array('courseid' => $course->id));
-
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, '', '');
+print_grade_page_head($course->id, 'import', 'keymanager', get_string('keymanager', 'grades'));
 
 $stredit         = get_string('edit');
 $strdelete       = get_string('delete');
Index: grade/import/xml/import.php
=========================================================
--- grade/import/xml/import.php	(revision 1.3)
+++ grade/import/xml/import.php	Thu Apr 16 09:31:42 CEST 2009
@@ -71,12 +71,7 @@
         }
 
     } else {
-        $strgrades = get_string('grades', 'grades');
-        $actionstr = get_string('xml', 'grades');
-        $navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
-
-        print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-        print_grade_plugin_selector($id, 'import', 'xml');
+        print_grade_page_head($course->id, 'import', 'xml', get_string('importxml', 'grades'));
 
         grade_import_commit($id, $importcode, $feedback, true);
 
Index: grade/import/xml/index.php
=========================================================
--- grade/import/xml/index.php	(revision 1.21)
+++ grade/import/xml/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -41,7 +41,6 @@
 // print header
 $strgrades = get_string('grades', 'grades');
 $actionstr = get_string('modulename', 'gradeimport_xml');
-$navigation = grade_build_nav(__FILE__, $actionstr, array('courseid' => $course->id));
 
 if (!empty($CFG->gradepublishing)) {
     $CFG->gradepublishing = has_capability('gradeimport/xml:publish', $context);
@@ -60,8 +59,8 @@
     }
 
     if ($text = $mform->get_file_content('userfile')) {
-        print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-        print_grade_plugin_selector($id, 'import', 'xml');
+
+        print_grade_page_head($COURSE->id, 'import', 'xml', get_string('importxml', 'grades'));
 
         $error = '';
         $importcode = import_xml_grades($text, $course, $error);
@@ -84,8 +83,7 @@
             $data->key = create_user_key('grade/import', $USER->id, $course->id, $data->iprestriction, $data->validuntil);
         }
 
-        print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-        print_grade_plugin_selector($id, 'import', 'xml');
+        print_grade_page_head($COURSE->id, 'import', 'xml', get_string('importxml', 'grades'));
 
         echo '<div class="gradeexportlink">';
         $link = $CFG->wwwroot.'/grade/import/xml/fetch.php?id='.$id.'&amp;feedback='.(int)($data->feedback).'&amp;url='.urlencode($data->url).'&amp;key='.$data->key;
@@ -96,8 +94,7 @@
     }
 }
 
-print_header($course->shortname.': '.get_string('grades'), $course->fullname, $navigation);
-print_grade_plugin_selector($id, 'import', 'xml');
+print_grade_page_head($COURSE->id, 'import', 'xml', get_string('importxml', 'grades'));
 
 $mform->display();
 
Index: grade/lib.php
=========================================================
--- grade/lib.php	(revision 1.120.2.22)
+++ grade/lib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -301,38 +301,276 @@
  * @param boolean $return return as string
  * @return nothing or string if $return true
  */
-function print_grade_plugin_selector($courseid, $active_type, $active_plugin, $return=false) {
+function print_grade_plugin_selector($plugin_info, $return=false) {
+    global $CFG;
+
+    $menu = array();
+    $count = 0;
+    $active = '';
+
+    foreach ($plugin_info as $plugin_type => $plugins) {
+        if ($plugin_type == 'strings') {
+            continue;
+        }
+
+        $first_plugin = reset($plugins);
+
+        if (is_array($first_plugin)) {
+            $menu[$first_plugin['link'].'&amp;'] = '**header**'.$plugin_info['strings'][$plugin_type];
+        } else {
+            $menu[$plugins['link']] = '**header**'.$plugins['string'];
+        }
+
+        if (empty($plugins['id'])) {
+            foreach ($plugins as $plugin) {
+                $menu[$plugin['link']] = $plugin['string'];
+                $count++;
+            }
+        }
+    }
+
+/// finally print/return the popup form
+    if ($count > 1) {
+        $select = popup_form('', $menu, 'choosepluginreport', '', get_string('chooseaction', 'grades'), '', '', true, 'self');
+        $select = preg_replace('/\>\*\*header\*\*/', ' class="optionheader">', $select);
+        if ($return) {
+            return $select;
+        } else {
+            echo $select;
+        }
+    } else {
+        // only one option - no plugin selector needed
+        return '';
+    }
+}
+
+/**
+ * Print grading plugin selection tab-based navigation.
+ *
+ * @param int $courseid id of course
+ * @param string $active_type type of plugin on current page - import, export, report or edit
+ * @param string $active_plugin active plugin type - grader, user, cvs, ...
+ * @param boolean $return return as string
+ * @return nothing or string if $return true
+ */
+function grade_print_tabs($active_type, $active_plugin, $plugin_info, $return=false) {
+    global $CFG, $COURSE;
+
+    if (!isset($currenttab)) {
+        $currenttab = '';
+    }
+
+    $tabs = array();
+    $top_row  = array();
+    $bottom_row = array();
+    $inactive = array($active_plugin);
+    $activated = array();
+
+    $count = 0;
+    $active = '';
+
+    foreach ($plugin_info as $plugin_type => $plugins) {
+        if ($plugin_type == 'strings') {
+            continue;
+        }
+
+        // If $plugins is actually the definition of a child-less parent link:
+        if (!empty($plugins['id'])) {
+            $string = $plugins['string'];
+            if (!empty($plugin_info[$active_type]['parent'])) {
+                $string = $plugin_info[$active_type]['parent']['string'];
+            }
+
+            $top_row[] = new tabobject($plugin_type, $plugins['link'], $string);
+            continue;
+        }
+
+        $first_plugin = reset($plugins);
+        $url = $first_plugin['link'];
+
+        if ($plugin_type == 'report') {
+            $url = $CFG->wwwroot.'/grade/report/index.php?id='.$COURSE->id;
+        }
+
+        $top_row[] = new tabobject($plugin_type, $url, $plugin_info['strings'][$plugin_type]);
+
+        if ($active_type == $plugin_type) {
+            foreach ($plugins as $plugin) {
+                $bottom_row[] = new tabobject($plugin['id'], $plugin['link'], $plugin['string']);
+                if ($plugin['id'] == $active_plugin) {
+                    $inactive = array($plugin['id']);
+                }
+            }
+        }
+    }
+
+    $tabs[] = $top_row;
+    $tabs[] = $bottom_row;
+
+    if ($return) {
+        return print_tabs($tabs, $active_type, $inactive, $activated, true);
+    } else {
+        print_tabs($tabs, $active_type, $inactive, $activated);
+    }
+}
+
+function grade_get_plugin_info($courseid, $active_type, $active_plugin) {
     global $CFG;
 
     $context = get_context_instance(CONTEXT_COURSE, $courseid);
 
-    $menu = array();
+    $plugin_info = array();
     $count = 0;
     $active = '';
+    $url_prefix = $CFG->wwwroot . '/grade/';
+
+    // Language strings
+    $plugin_info['strings'] = array(
+        'report' => get_string('view'),
+        'edittree' => get_string('edittree', 'grades'),
+        'scale' => get_string('scales'),
+        'outcome' => get_string('outcomes', 'grades'),
+        'letter' => get_string('letters', 'grades'),
+        'export' => get_string('export', 'grades'),
+        'import' => get_string('import'),
+        'preferences' => get_string('mypreferences', 'grades'),
+        'settings' => get_string('settings'));
+
+    // Settings tab first
+    if (has_capability('moodle/course:update', $context)) {
+        $url = $url_prefix.'edit/settings/index.php?id='.$courseid;
+
+        if ($active_type == 'settings' and $active_plugin == 'course') {
+            $active = $url;
+        }
+
+        $plugin_info['settings'] = array('id' => 'coursesettings', 'link' => $url, 'string' => get_string('settings'));
+        $count++;
+    }
+
 
 /// report plugins with its special structure
     if ($reports = get_list_of_plugins('grade/report', 'CVS')) {         // Get all installed reports
         foreach ($reports as $key => $plugin) {                      // Remove ones we can't see
+            // Outcomes are a special core plugin depending on $CFG->enableoutcomes
+            if ($plugin == 'outcomes' && empty($CFG->enableoutcomes)) {
+                unset($reports[$key]);
+            }
             if (!has_capability('gradereport/'.$plugin.':view', $context)) {
                 unset($reports[$key]);
             }
         }
     }
+
     $reportnames = array();
+
     if (!empty($reports)) {
         foreach ($reports as $plugin) {
-            $url = 'report/'.$plugin.'/index.php?id='.$courseid;
+            $url = $url_prefix.'report/'.$plugin.'/index.php?id='.$courseid;
             if ($active_type == 'report' and $active_plugin == $plugin ) {
                 $active = $url;
             }
-            $reportnames[$url] = get_string('modulename', 'gradereport_'.$plugin);
+            $reportnames[$plugin] = array('id' => $plugin, 'link' => $url, 'string' => get_string('modulename', 'gradereport_'.$plugin));
+
+            // Add link to preferences tab if such a page exists
+            if (file_exists($CFG->dirroot . '/grade/report/'.$plugin.'/preferences.php')) {
+                $pref_url = $url_prefix.'report/'.$plugin.'/preferences.php?id='.$courseid;
+                $plugin_info['preferences'][$plugin] = array('id' => $plugin, 'link' => $pref_url, 'string' => get_string('modulename', 'gradereport_'.$plugin));
+            }
+
             $count++;
         }
         asort($reportnames);
     }
     if (!empty($reportnames)) {
-        $menu['reportgroup']='--'.get_string('view');
-        $menu = $menu+$reportnames;
+        $plugin_info['report']=$reportnames;
+    }
+
+/// editing scripts - not real plugins
+    if (has_capability('moodle/grade:manage', $context)
+      or has_capability('moodle/grade:manageletters', $context)
+      or has_capability('moodle/course:managescales', $context)
+      or has_capability('moodle/course:update', $context)) {
+
+        if (has_capability('moodle/grade:manage', $context)) {
+            $url = $url_prefix.'edit/tree/index.php?sesskey='.sesskey().'&amp;showadvanced=0&amp;id='.$courseid;
+            $url_adv = $url_prefix.'edit/tree/index.php?sesskey='.sesskey().'&amp;showadvanced=1&amp;id='.$courseid;
+
+            if ($active_type == 'edittree' and $active_plugin == 'simpleview') {
+                $active = $url;
+            } elseif ($active_type == 'edittree' and $active_plugin == 'fullview') {
+                $active = $url_adv;
+            }
+
+            $plugin_info['edittree'] = array();
+            $plugin_info['edittree']['simpleview'] = array('id' => 'simpleview', 'link' => $url, 'string' => get_string('simpleview', 'grades'));
+            $plugin_info['edittree']['fullview'] = array('id' => 'fullview', 'link' => $url_adv, 'string' => get_string('fullview', 'grades'));
+            $count++;
+        }
+
+        if (has_capability('moodle/course:managescales', $context)) {
+            $url = $url_prefix.'edit/scale/index.php?id='.$courseid;
+
+            if ($active_type == 'scale' and is_null($active_plugin)) {
+                $active = $url;
+            }
+
+            if ($active_type == 'scale' and $active_plugin == 'edit') {
+                $edit_url = $url_prefix.'edit/scale/edit.php?courseid='.$courseid.'&amp;id='.optional_param('id', 0, PARAM_INT);
+                $active = $edit_url;
+                $plugin_info['scale'] = array('id' => 'edit', 'link' => $edit_url, 'string' => get_string('edit'),
+                    'parent' => array('id' => 'scale', 'link' => $url, 'string' => get_string('scales')));
+            } else {
+                $plugin_info['scale'] = array('id' => 'scale', 'link' => $url, 'string' => get_string('scales'));
+            }
+
+            $count++;
+        }
+
+        if (!empty($CFG->enableoutcomes) && (has_capability('moodle/grade:manage', $context) or
+                                             has_capability('moodle/course:update', $context))) {
+
+            $url_course = $url_prefix.'edit/outcome/course.php?id='.$courseid;
+            $url_edit = $url_prefix.'edit/outcome/index.php?id='.$courseid;
+
+            $plugin_info['outcome'] = array();
+
+            if (has_capability('moodle/course:update', $context)) {  // Default to course assignment
+                $plugin_info['outcome']['course'] = array('id' => 'course', 'link' => $url_course, 'string' => get_string('outcomescourse', 'grades'));
+                $plugin_info['outcome']['edit'] = array('id' => 'edit', 'link' => $url_edit, 'string' => get_string('editoutcomes', 'grades'));
+            } else {
+                $plugin_info['outcome'] = array('id' => 'edit', 'link' => $url_course, 'string' => get_string('outcomescourse', 'grades'));
+            }
+
+            if ($active_type == 'outcome' and is_null($active_plugin)) {
+                $active = $url_edit;
+            } elseif ($active_type == 'outcome' and $active_plugin == 'course' ) {
+                $active = $url_course;
+            } elseif ($active_type == 'outcome' and $active_plugin == 'edit' ) {
+                $active = $url_edit;
+            } elseif ($active_type == 'outcome' and $active_plugin == 'import') {
+                $plugin_info['outcome']['import'] = array('id' => 'import', 'link' => null, 'string' => get_string('importoutcomes', 'grades'));
+            }
+
+            $count++;
+        }
+
+        if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:manageletters', $context)) {
+            $course_context = get_context_instance(CONTEXT_COURSE, $courseid);
+            $url = $url_prefix.'edit/letter/index.php?id='.$courseid;
+            $url_edit = $url_prefix.'edit/letter/edit.php?id='.$course_context->id;
+
+            if ($active_type == 'letter' and $active_plugin == 'view' ) {
+                $active = $url;
+            } elseif ($active_type == 'letter' and $active_plugin == 'edit' ) {
+                $active = $url_edit;
+            }
+
+            $plugin_info['letter'] = array();
+            $plugin_info['letter']['view'] = array('id' => 'view', 'link' => $url, 'string' => get_string('view'));
+            $plugin_info['letter']['edit'] = array('id' => 'edit', 'link' => $url_edit, 'string' => get_string('edit'));
+            $count++;
+        }
     }
 
 /// standard import plugins
@@ -346,18 +584,17 @@
     $importnames = array();
     if (!empty($imports)) {
         foreach ($imports as $plugin) {
-            $url = 'import/'.$plugin.'/index.php?id='.$courseid;
+            $url = $url_prefix.'import/'.$plugin.'/index.php?id='.$courseid;
             if ($active_type == 'import' and $active_plugin == $plugin ) {
                 $active = $url;
             }
-            $importnames[$url] = get_string('modulename', 'gradeimport_'.$plugin);
+            $importnames[$plugin] = array('id' => $plugin, 'link' => $url, 'string' => get_string('modulename', 'gradeimport_'.$plugin));
             $count++;
         }
         asort($importnames);
     }
     if (!empty($importnames)) {
-        $menu['importgroup']='--'.get_string('importfrom', 'grades');
-        $menu = $menu+$importnames;
+        $plugin_info['import']=$importnames;
     }
 
 /// standard export plugins
@@ -371,86 +608,157 @@
     $exportnames = array();
     if (!empty($exports)) {
         foreach ($exports as $plugin) {
-            $url = 'export/'.$plugin.'/index.php?id='.$courseid;
+            $url = $url_prefix.'export/'.$plugin.'/index.php?id='.$courseid;
             if ($active_type == 'export' and $active_plugin == $plugin ) {
                 $active = $url;
             }
-            $exportnames[$url] = get_string('modulename', 'gradeexport_'.$plugin);
+            $exportnames[$plugin] = array('id' => $plugin, 'link' => $url, 'string' => get_string('modulename', 'gradeexport_'.$plugin));
             $count++;
         }
         asort($exportnames);
     }
+
     if (!empty($exportnames)) {
-        $menu['exportgroup']='--'.get_string('exportto', 'grades');
-        $menu = $menu+$exportnames;
+        $plugin_info['export']=$exportnames;
     }
 
-/// editing scripts - not real plugins
-    if (has_capability('moodle/grade:manage', $context)
-      or has_capability('moodle/grade:manageletters', $context)
-      or has_capability('moodle/course:managescales', $context)
-      or has_capability('moodle/course:update', $context)) {
-        $menu['edit']='--'.get_string('edit');
+    // Key managers
+    if ($CFG->gradepublishing) {
+        $keymanager_url = $url_prefix.'export/keymanager.php?id='.$courseid;
+        $plugin_info['export']['keymanager'] = array('id' => 'keymanager', 'link' => $keymanager_url, 'string' => get_string('keymanager', 'grades'));
+        if ($active_type == 'export' and $active_plugin == 'keymanager' ) {
+            $active = $keymanager_url;
+        }
+        $count++;
 
-        if (has_capability('moodle/grade:manage', $context)) {
-            $url = 'edit/tree/index.php?id='.$courseid;
-            if ($active_type == 'edit' and $active_plugin == 'tree' ) {
-                $active = $url;
+        $keymanager_url = $url_prefix.'import/keymanager.php?id='.$courseid;
+        $plugin_info['import']['keymanager'] = array('id' => 'keymanager', 'link' => $keymanager_url, 'string' => get_string('keymanager', 'grades'));
+        if ($active_type == 'import' and $active_plugin == 'keymanager' ) {
+            $active = $keymanager_url;
-            }
+        }
-            $menu[$url] = get_string('edittree', 'grades');
-            $count++;
-        }
+        $count++;
+    }
 
-        if (has_capability('moodle/course:managescales', $context)) {
-            $url = 'edit/scale/index.php?id='.$courseid;
-            if ($active_type == 'edit' and $active_plugin == 'scale' ) {
-                $active = $url;
+
+    foreach ($plugin_info as $plugin_type => $plugins) {
+        if (!empty($plugins['id']) && $active_plugin == $plugins['id']) {
+            $plugin_info['strings']['active_plugin_str'] = $plugins['string'];
+            break;
+        }
+        foreach ($plugins as $plugin) {
+            if ($active_plugin == $plugin['id']) {
+                $plugin_info['strings']['active_plugin_str'] = $plugin['string'];
+            }
-            }
+        }
-            $menu[$url] = get_string('scales');
-            $count++;
-        }
+    }
 
-        if (!empty($CFG->enableoutcomes) && (has_capability('moodle/grade:manage', $context) or
-                                             has_capability('moodle/course:update', $context))) {
-            if (has_capability('moodle/course:update', $context)) {  // Default to course assignment
-                $url = 'edit/outcome/course.php?id='.$courseid;
-            } else {
-                $url = 'edit/outcome/index.php?id='.$courseid;
+    // Put settings last
+    if (!empty($plugin_info['settings'])) {
+        $settings = $plugin_info['settings'];
+        unset($plugin_info['settings']);
+        $plugin_info['settings'] = $settings;
-            }
+    }
-            if ($active_type == 'edit' and $active_plugin == 'outcome' ) {
-                $active = $url;
+
+    // Put preferences last
+    if (!empty($plugin_info['preferences'])) {
+        $prefs = $plugin_info['preferences'];
+        unset($plugin_info['preferences']);
+        $plugin_info['preferences'] = $prefs;
-            }
+    }
-            $menu[$url] = get_string('outcomes', 'grades');
-            $count++;
+
+    return $plugin_info;
+}
+
+/**
+ * Prints the page headers, breadcrumb trail, page heading, (optional) dropdown navigation menu and
+ * (optional) navigation tabs for any gradebook page. All gradebook pages MUST use these functions
+ * in favour of the usual print_header(), print_header_simple(), print_heading() etc.
+ * !IMPORTANT! Use of tabs.php file in gradebook pages is forbidden unless tabs are switched off at
+ * the site level for the gradebook ($CFG->grade_navmethod = GRADE_NAVMETHOD_DROPDOWN).
+ *
+ * @param int $courseid
+ * @param string $active_type The type of the current page (report, settings, import, export, scales, outcomes, letters)
+ * @param string $active_plugin The plugin of the current page (grader, fullview etc...)
+ * @param string $heading The heading of the page. Tries to guess if none is given
+ * @param boolean $return Whether to return (true) or echo (false) the HTML generated by this function
+ * @param string $bodytags Additional attributes that will be added to the <body> tag
+ * @param string $buttons Additional buttons to display on the page
+ *
+ * @return string HTML code or nothing if $return == false
+ */
+function print_grade_page_head($courseid, $active_type, $active_plugin=null, $heading = false, $return=false, $bodytags='', $buttons=false, $extracss=array()) {
+    global $CFG, $COURSE;
+    $strgrades = get_string('grades');
+    $plugin_info = grade_get_plugin_info($courseid, $active_type, $active_plugin);
+
+    // Determine the string of the active plugin
+    $stractive_plugin = ($active_plugin) ? $plugin_info['strings']['active_plugin_str'] : $heading;
+    $stractive_type = $plugin_info['strings'][$active_type];
+
+    $navlinks = array();
+    $first_link = '';
+
+    if ($active_type == 'settings' && $active_plugin != 'coursesettings') {
+        $first_link = $plugin_info['report'][$active_plugin]['link'];
+    } elseif ($active_type != 'report') {
+        $first_link = $CFG->wwwroot.'/grade/index.php?id='.$COURSE->id;
-        }
+    }
 
-        if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:manageletters', $context)) {
-            $url = 'edit/letter/index.php?id='.$courseid;
-            if ($active_type == 'edit' and $active_plugin == 'letter' ) {
-                $active = $url;
+    if ($active_type == 'preferences') {
+        $CFG->stylesheets[] = $CFG->wwwroot . '/grade/report/styles.css';
-            }
+    }
-            $menu[$url] = get_string('letters', 'grades');
-            $count++;
+
+    foreach ($extracss as $css_url) {
+        $CFG->stylesheets[] = $css_url;
-        }
+    }
 
-        if (has_capability('moodle/grade:manage', $context)) {
-            $url = 'edit/settings/index.php?id='.$courseid;
-            if ($active_type == 'edit' and $active_plugin == 'settings' ) {
-                $active = $url;
+    $navlinks[] = array('name' => $strgrades,
+                        'link' => $first_link,
+                        'type' => 'misc');
+
+    $active_type_link = '';
+
+    if (!empty($plugin_info[$active_type]['link']) && $plugin_info[$active_type]['link'] != qualified_me()) {
+        $active_type_link = $plugin_info[$active_type]['link'];
-            }
+    }
-            $menu[$url] = get_string('coursesettings', 'grades');
-            $count++;
+
+    if (!empty($plugin_info[$active_type]['parent']['link'])) {
+        $active_type_link = $plugin_info[$active_type]['parent']['link'];
+        $navlinks[] = array('name' => $stractive_type, 'link' => $active_type_link, 'type' => 'misc');
+    }
+
+    if (empty($plugin_info[$active_type]['id'])) {
+        $navlinks[] = array('name' => $stractive_type, 'link' => $active_type_link, 'type' => 'misc');
+    }
+
+    $navlinks[] = array('name' => $stractive_plugin, 'link' => null, 'type' => 'misc');
+
+    $navigation = build_navigation($navlinks);
+
+    $title = ': ' . $stractive_plugin;
+    if (empty($plugin_info[$active_type]['id']) || !empty($plugin_info[$active_type]['parent'])) {
+        $title = ': ' . $stractive_type . ': ' . $stractive_plugin;
+    }
+
+    $returnval = print_header_simple($strgrades . ': ' . $stractive_type, $title, $navigation, '',
+            $bodytags, true, $buttons, navmenu($COURSE), false, '', $return);
+
+    // Guess heading if not given explicitly
+    if (!$heading) {
+        $heading = $stractive_plugin;
+    }
+
+    if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_DROPDOWN) {
+        $returnval .= print_grade_plugin_selector($plugin_info, $return);
-        }
+    }
+    $returnval .= print_heading($heading);
 
+    if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_TABS) {
+        $returnval .= grade_print_tabs($active_type, $active_plugin, $plugin_info, $return);
     }
 
-/// finally print/return the popup form
-    if ($count > 1) {
-        return popup_form($CFG->wwwroot.'/grade/', $menu, 'choosepluginreport', '',
-                get_string('chooseaction', 'grades'), '', '', $return, 'self');
-    } else {
-        // only one option - no plugin selector needed
-        return '';
+    if ($return) {
+        return $returnval;
     }
 }
 
@@ -783,38 +1091,42 @@
             case 'courseitem':
             case 'categoryitem':
                 if ($element['object']->is_calculated()) {
-                    return '<img src="'.$CFG->pixpath.'/i/calc.gif" class="icon itemicon" alt="'.get_string('calculation', 'grades').'"/>';
+                    $strcalc = get_string('calculatedgrade', 'grades');
+                    return '<img src="'.$CFG->pixpath.'/i/calc.gif" class="icon itemicon" title="'.$strcalc.'" alt="'.$strcalc.'"/>';
 
                 } else if (($element['object']->is_course_item() or $element['object']->is_category_item())
                   and ($element['object']->gradetype == GRADE_TYPE_SCALE or $element['object']->gradetype == GRADE_TYPE_VALUE)) {
                     if ($category = $element['object']->get_item_category()) {
                         switch ($category->aggregation) {
-                            case GRADE_AGGREGATE_MEAN:
+                            case GRADE_AGGREGATE_MEDIAN:
                             case GRADE_AGGREGATE_MEDIAN:
+                                return '<img src="'.$CFG->pixpath.'/i/agg_sum.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>';
                             case GRADE_AGGREGATE_WEIGHTED_MEAN:
                             case GRADE_AGGREGATE_WEIGHTED_MEAN2:
                             case GRADE_AGGREGATE_EXTRACREDIT_MEAN:
                                 return '<img src="'.$CFG->pixpath.'/i/agg_mean.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>';
                             case GRADE_AGGREGATE_SUM:
-                                return '<img src="'.$CFG->pixpath.'/i/agg_sum.gif" class="icon itemicon" alt="'.get_string('aggregation', 'grades').'"/>';
                         }
                     }
 
                 } else if ($element['object']->itemtype == 'mod') {
-                    return '<img src="'.$CFG->modpixpath.'/'.$element['object']->itemmodule.'/icon.gif" class="icon itemicon" alt="'
-                           .get_string('modulename', $element['object']->itemmodule).'"/>';
+                    $strmodname = get_string('modulename', $element['object']->itemmodule);
+                    return '<img src="'.$CFG->modpixpath.'/'.$element['object']->itemmodule.'/icon.gif" class="icon itemicon" title="' .$strmodname.'" alt="' .$strmodname.'"/>';
 
                 } else if ($element['object']->itemtype == 'manual') {
                     if ($element['object']->is_outcome_item()) {
-                        return '<img src="'.$CFG->pixpath.'/i/outcomes.gif" class="icon itemicon" alt="'.get_string('outcome', 'grades').'"/>';
+                        $stroutcome = get_string('outcome', 'grades');
+                        return '<img src="'.$CFG->pixpath.'/i/outcomes.gif" class="icon itemicon" title="'.$stroutcome.'" alt="'.$stroutcome.'"/>';
                     } else {
-                        return '<img src="'.$CFG->pixpath.'/t/manual_item.gif" class="icon itemicon" alt="'.get_string('manualitem', 'grades').'"/>';
+                        $strmanual = get_string('manualitem', 'grades');
+                        return '<img src="'.$CFG->pixpath.'/t/manual_item.gif" class="icon itemicon" title="'.$strmanual.'" alt="'.$strmanual.'"/>';
                     }
                 }
                 break;
 
             case 'category':
-                return '<img src="'.$CFG->pixpath.'/f/folder.gif" class="icon itemicon" alt="'.get_string('category', 'grades').'"/>';
+                $strcat = get_string('category', 'grades');
+                return '<img src="'.$CFG->pixpath.'/f/folder.gif" class="icon itemicon" title="'.$strcat.'" alt="'.$strcat.'" />';
         }
 
         if ($spacerifnone) {
@@ -841,8 +1153,7 @@
             $header .= $this->get_element_icon($element, $spacerifnone);
         }
 
-        $title = $element['object']->get_name();
-        $header .= $title;
+        $header .= $element['object']->get_name();
 
         if ($element['type'] != 'item' and $element['type'] != 'categoryitem' and $element['type'] != 'courseitem') {
             return $header;
@@ -855,6 +1166,8 @@
         if ($withlink and $itemtype=='mod' and $iteminstance and $itemmodule) {
             if ($cm = get_coursemodule_from_instance($itemmodule, $iteminstance, $this->courseid)) {
 
+                $a->name = get_string('modulename', $element['object']->itemmodule);
+                $title = get_string('linktoactivity', 'grades', $a);
                 $dir = $CFG->dirroot.'/mod/'.$itemmodule;
 
                 if (file_exists($dir.'/grade.php')) {
@@ -946,7 +1259,6 @@
         }
 
         $object = $element['object'];
-        $overlib = '';
 
         switch ($element['type']) {
             case 'item':
@@ -977,9 +1289,6 @@
                 $url = $gpr->add_url_params($url);
                 if (!empty($object->feedback)) {
                     $feedback = addslashes_js(trim(format_string($object->feedback, $object->feedbackformat)));
-                    $function = "return overlib('$feedback', BORDER, 0, FGCLASS, 'feedback', "
-                              ."CAPTIONFONTCLASS, 'caption', CAPTION, '$strfeedback');";
-                    $overlib = 'onmouseover="'.s($function).'" onmouseout="return nd();"';
                 }
                 break;
 
@@ -988,7 +1297,7 @@
         }
 
         if ($url) {
-            return '<a href="'.$url.'"><img '.$overlib.' src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$stredit.'" title="'.$stredit.'"/></a>';
+            return '<a href="'.$url.'"><img src="'.$CFG->pixpath.'/t/edit.gif" class="iconsmall" alt="'.$stredit.'" title="'.$stredit.'"/></a>';
 
         } else {
             return '';
Index: grade/report/grader/functions.js
=========================================================
--- grade/report/grader/functions.js	(revision 1.1.2.2)
+++ grade/report/grader/functions.js	Thu Apr 16 09:31:42 CEST 2009
@@ -2,7 +2,7 @@
 function set_row(idx) {
     var table = document.getElementById('user-grades');
     var rowsize = table.rows[idx].cells.length;
-    for (var i = 1; i < rowsize; i++) {
+    for (var i = 0; i < rowsize; i++) {
         if (table.rows[idx].cells[i]) {
             if (table.rows[idx].cells[i].className.search(/hmarked/) != -1) {
                 table.rows[idx].cells[i].className = table.rows[idx].cells[i].className.replace(' hmarked', '');
Index: grade/report/grader/index.php
=========================================================
--- grade/report/grader/index.php	(revision 1.65.2.9)
+++ grade/report/grader/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -28,6 +28,9 @@
 require_once $CFG->dirroot.'/grade/lib.php';
 require_once $CFG->dirroot.'/grade/report/grader/lib.php';
 
+require_js(array('yui_yahoo', 'yui_dom', 'yui_event', 'yui_container', 'yui_connection', 'yui_dragdrop', 'yui_element'));
+
+
 $courseid      = required_param('id', PARAM_INT);        // course id
 $page          = optional_param('page', 0, PARAM_INT);   // active page
 $perpageurl    = optional_param('perpage', 0, PARAM_INT);
@@ -60,13 +63,6 @@
 }
 $USER->grade_last_report[$course->id] = 'grader';
 
-/// Build navigation
-
-$strgrades  = get_string('grades');
-$reportname = get_string('modulename', 'gradereport_grader');
-
-$navigation = grade_build_nav(__FILE__, $reportname, $courseid);
-
 /// Build editing on/off buttons
 
 if (!isset($USER->gradeediting)) {
@@ -121,7 +117,6 @@
 // Initialise the grader report object
 $report = new grade_report_grader($courseid, $gpr, $context, $page, $sortitemid);
 
-
 /// processing posted grades & feedback here
 if ($data = data_submitted() and confirm_sesskey() and has_capability('moodle/grade:edit', $context)) {
     $warnings = $report->process_data($data);
@@ -141,19 +136,12 @@
 $report->load_final_grades();
 
 /// Print header
-print_header_simple($strgrades.': '.$reportname, ': '.$strgrades, $navigation,
-                        '', '', true, $buttons, navmenu($course));
-
-/// Print the plugin selector at the top
-print_grade_plugin_selector($courseid, 'report', 'grader');
-
-// Add tabs
-$currenttab = 'graderreport';
-require('tabs.php');
+$reportname = get_string('modulename', 'gradereport_grader');
+print_grade_page_head($COURSE->id, 'report', 'grader', $reportname, false, null, $buttons, array($CFG->wwwroot . '/lib/yui/container/assets/skins/sam/container.css'));
 
 echo $report->group_selector;
 echo '<div class="clearer"></div>';
-echo $report->get_toggles_html();
+// echo $report->get_toggles_html();
 
 //show warnings if any
 foreach($warnings as $warning) {
@@ -167,15 +155,15 @@
 }
 
 $reporthtml = '<script src="functions.js" type="text/javascript"></script>';
-
-$reporthtml .= '<table id="user-grades" class="gradestable flexible boxaligncenter generaltable">';
+$reporthtml .= '<div class="gradeparent">';
+$reporthtml .= $report->get_studentnameshtml();
 $reporthtml .= $report->get_headerhtml();
 $reporthtml .= $report->get_iconshtml();
-$reporthtml .= $report->get_rangehtml();
 $reporthtml .= $report->get_studentshtml();
+$reporthtml .= $report->get_rangehtml();
 $reporthtml .= $report->get_avghtml(true);
 $reporthtml .= $report->get_avghtml();
-$reporthtml .= "</table>";
+$reporthtml .= "</tbody></table></div></div>";
 
 // print submit button
 if ($USER->gradeediting[$course->id]) {
@@ -199,6 +187,26 @@
     print_paging_bar($numusers, $report->page, $studentsperpage, $report->pbarurl);
 }
 
+// Print YUI tooltip code
+?>
+<script type="text/javascript">
+
+YAHOO.namespace("graderreport");
+
+function init() {
+    // Get all <td> with class grade
+    var cells = YAHOO.util.Dom.getElementsByClassName('grade', 'td');
+    YAHOO.graderreport.tooltips = new Array(cells.length);
+
+    for (var i = 0; i < cells.length; i++) {
+        YAHOO.graderreport.tooltips[i] = new YAHOO.widget.Tooltip("tt"+i, { context: cells[i], autodismissdelay: 10000000 });
+    }
+};
+YAHOO.util.Event.onDOMReady(init);
+
+</script>
+<?php
+
 print_footer($course);
 
 ?>
Index: grade/report/grader/lib.php
=========================================================
--- grade/report/grader/lib.php	(revision 1.98.2.49)
+++ grade/report/grader/lib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -85,6 +85,8 @@
      * */
     var $canviewhidden;
 
+    var $preferences_page=false;
+
     /**
      * Constructor. Sets local copies of user preferences and initialises grade_tree.
      * @param int $courseid
@@ -201,10 +203,13 @@
                 // Warn if the grade is out of bounds.
                 if (is_null($finalgrade)) {
                     // ok
-                } else if ($finalgrade < $grade_item->grademin) {
+                } else {
+                    $bounded = $grade_item->bounded_grade($finalgrade);
+                    if ($bounded > $finalgrade) {
-                    $errorstr = 'lessthanmin';
+                        $errorstr = 'lessthanmin';
-                } else if ($finalgrade > $grade_item->grademax) {
+                    } else if ($bounded < $finalgrade) {
-                    $errorstr = 'morethanmax';
+                        $errorstr = 'morethanmax';
+                    }
                 }
                 if ($errorstr) {
                     $user = get_record('user', 'id', $userid, '', '', '', '', 'id, firstname, lastname');
@@ -489,31 +494,38 @@
     function get_headerhtml() {
         global $CFG, $USER;
 
+        $this->rowcount = 0;
+        $fixedstudents = empty($USER->screenreader) && $CFG->grade_report_fixedstudents;
+
+        if (!$fixedstudents) {
-        $strsortasc   = $this->get_lang_string('sortasc', 'grades');
-        $strsortdesc  = $this->get_lang_string('sortdesc', 'grades');
-        $strfirstname = $this->get_lang_string('firstname');
-        $strlastname  = $this->get_lang_string('lastname');
-        $showuseridnumber = $this->get_pref('showuseridnumber');
+            $strsortasc   = $this->get_lang_string('sortasc', 'grades');
+            $strsortdesc  = $this->get_lang_string('sortdesc', 'grades');
+            $strfirstname = $this->get_lang_string('firstname');
+            $strlastname  = $this->get_lang_string('lastname');
+            $showuseridnumber = $this->get_pref('showuseridnumber');
 
-        if ($this->sortitemid === 'lastname') {
-            if ($this->sortorder == 'ASC') {
-                $lastarrow = print_arrow('up', $strsortasc, true);
-            } else {
-                $lastarrow = print_arrow('down', $strsortdesc, true);
-            }
-        } else {
-            $lastarrow = '';
-        }
+            if ($this->sortitemid === 'lastname') {
+                if ($this->sortorder == 'ASC') {
+                    $lastarrow = print_arrow('up', $strsortasc, true);
+                } else {
+                    $lastarrow = print_arrow('down', $strsortdesc, true);
+                }
+            } else {
+                $lastarrow = '';
+            }
 
-        if ($this->sortitemid === 'firstname') {
-            if ($this->sortorder == 'ASC') {
-                $firstarrow = print_arrow('up', $strsortasc, true);
-            } else {
-                $firstarrow = print_arrow('down', $strsortdesc, true);
-            }
-        } else {
-            $firstarrow = '';
-        }
+            if ($this->sortitemid === 'firstname') {
+                if ($this->sortorder == 'ASC') {
+                    $firstarrow = print_arrow('up', $strsortasc, true);
+                } else {
+                    $firstarrow = print_arrow('down', $strsortdesc, true);
+                }
+            } else {
+                $firstarrow = '';
+            }
+
+        }
+
         // Prepare Table Headers
         $headerhtml = '';
 
@@ -521,7 +533,6 @@
 
         $columns_to_unset = array();
 
-
         foreach ($this->gtree->levels as $key=>$row) {
             $columncount = 0;
             if ($key == 0) {
@@ -529,38 +540,42 @@
                 // continue;
             }
 
+            if ($fixedstudents) {
+                $headerhtml .= '<tr class="heading_name_row">';
+            } else {
-            $headerhtml .= '<tr class="heading r'.$this->rowcount++.'">';
+                $headerhtml .= '<tr class="heading r'.$this->rowcount++.'">';
-
-            if ($key == $numrows - 1) {
+                if ($key == $numrows - 1) {
-                $headerhtml .= '<th class="header c'.$columncount++.'" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=firstname">'
+                    $headerhtml .= '<th class=" c'.$columncount++.'" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=firstname">'
-                            . $strfirstname . '</a> '
-                            . $firstarrow. '/ <a href="'.$this->baseurl.'&amp;sortitemid=lastname">' . $strlastname . '</a>'. $lastarrow .'</th>';
-                if ($showuseridnumber) {
-                    if ('idnumber' == $this->sortitemid) {
-                        if ($this->sortorder == 'ASC') {
-                            $idnumberarrow = print_arrow('up', $strsortasc, true);
-                        } else {
-                            $idnumberarrow = print_arrow('down', $strsortdesc, true);
-                        }
-                    } else {
-                        $idnumberarrow = '';
-                    }
+                                . $strfirstname . '</a> '
+                                . $firstarrow. '/ <a href="'.$this->baseurl.'&amp;sortitemid=lastname">' . $strlastname . '</a>'. $lastarrow .'</th>';
+                    if ($showuseridnumber) {
+                        if ('idnumber' == $this->sortitemid) {
+                            if ($this->sortorder == 'ASC') {
+                                $idnumberarrow = print_arrow('up', $strsortasc, true);
+                            } else {
+                                $idnumberarrow = print_arrow('down', $strsortdesc, true);
+                            }
+                        } else {
+                            $idnumberarrow = '';
+                        }
-                    $headerhtml .= '<th class="header c'.$columncount++.' useridnumber" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=idnumber">'
+                        $headerhtml .= '<th class=" c'.$columncount++.' useridnumber" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=idnumber">'
-                            . get_string('idnumber') . '</a> ' . $idnumberarrow . '</th>';
-                }
-             } else {
-                $colspan='';
-                if ($showuseridnumber) {
-                    $colspan = 'colspan="2" ';
-                }
+                                . get_string('idnumber') . '</a> ' . $idnumberarrow . '</th>';
+                    }
+                 } else {
+                    $colspan='';
+                    if ($showuseridnumber) {
+                        $colspan = 'colspan="2" ';
+                    }
 
-                $headerhtml .= '<td '.$colspan.'class="cell c'.$columncount++.' topleft">&nbsp;</td>';
+                    $headerhtml .= '<td '.$colspan.'class="cell c'.$columncount++.' topleft">&nbsp;</td>';
 
-                if ($showuseridnumber) {
-                    $columncount++;
+                    if ($showuseridnumber) {
+                        $columncount++;
+                    }
                 }
             }
 
+
             foreach ($row as $columnkey => $element) {
                 $sort_link = '';
                 if (isset($element['object']->id)) {
@@ -594,7 +609,7 @@
                 }
 // Element is a category
                 else if ($type == 'category') {
-                    $headerhtml .= '<th class="header '. $columnclass.' category'.$catlevel.'" '.$colspan.' scope="col">'
+                    $headerhtml .= '<th class=" '. $columnclass.' category'.$catlevel.'" '.$colspan.' scope="col">'
                                 . shorten_text($element['object']->get_name());
                     $headerhtml .= $this->get_collapsing_icon($element);
 
@@ -626,7 +641,7 @@
                     }
 
                     $headerlink = $this->gtree->get_element_header($element, true, $this->get_pref('showactivityicons'), false);
-                    $headerhtml .= '<th class="header '.$columnclass.' '.$type.$catlevel.$hidden.'" scope="col" onclick="set_col(this.cellIndex)">'
+                    $headerhtml .= '<th class=" '.$columnclass.' '.$type.$catlevel.$hidden.'" scope="col" onclick="set_col(this.cellIndex)">'
                                 . shorten_text($headerlink) . $arrow;
                     $headerhtml .= '</th>';
                 }
@@ -649,9 +664,10 @@
         $strfeedback  = $this->get_lang_string("feedback");
         $strgrade     = $this->get_lang_string('grade');
         $gradetabindex = 1;
+        $numusers      = count($this->users);
         $showuserimage = $this->get_pref('showuserimage');
         $showuseridnumber = $this->get_pref('showuseridnumber');
-        $numusers      = count($this->users);
+        $fixedstudents = empty($USER->screenreader) && $CFG->grade_report_fixedstudents;
 
         // Preload scale objects for items with a scaleid
         $scales_list = '';
@@ -690,20 +706,25 @@
             }
 
             $columncount = 0;
+            if ($fixedstudents) {
+                $studentshtml .= '<tr class="r'.$this->rowcount++ . $row_classes[$this->rowcount % 2] . '">';
+            } else {
-            // Student name and link
-            $user_pic = null;
-            if ($showuserimage) {
+                // Student name and link
+                $user_pic = null;
+                if ($showuserimage) {
-                $user_pic = '<div class="userpic">' . print_user_picture($user, $this->courseid, NULL, 0, true) . '</div>';
+                    $user_pic = '<div class="userpic">' . print_user_picture($user, $this->courseid, null, 0, true) . '</div>';
-            }
+                }
 
-            $studentshtml .= '<tr class="r'.$this->rowcount++ . $row_classes[$this->rowcount % 2] . '">'
+                $studentshtml .= '<tr class="r'.$this->rowcount++ . $row_classes[$this->rowcount % 2] . '">'
-                          .'<th class="header c'.$columncount++.' user" scope="row" onclick="set_row(this.parentNode.rowIndex);">'.$user_pic
+                              .'<th class="c'.$columncount++.' user" scope="row" onclick="set_row(this.parentNode.rowIndex);">'.$user_pic
-                          .'<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->course->id.'">'
-                          .fullname($user).'</a></th>';
+                              .'<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->course->id.'">'
+                              .fullname($user).'</a></th>';
 
-            if ($showuseridnumber) {
+                if ($showuseridnumber) {
-                $studentshtml .= '<th class="header c'.$columncount++.' useridnumber" onclick="set_row(this.parentNode.rowIndex);">'.
-                        $user->idnumber.'</th>';
+                    $studentshtml .= '<th class="c'.$columncount++.' useridnumber" onclick="set_row(this.parentNode.rowIndex);">'.
+                            $user->idnumber.'</a></th>';
+                }
+
             }
 
             foreach ($this->gtree->items as $itemid=>$unused) {
@@ -737,7 +758,7 @@
                 $eid = $this->gtree->get_grade_eid($grade);
                 $element = array('eid'=>$eid, 'object'=>$grade, 'type'=>'grade');
 
-                $cellclasses = 'cell c'.$columncount++;
+                $cellclasses = 'grade cell c'.$columncount++;
                 if ($item->is_category_item()) {
                     $cellclasses .= ' cat';
                 }
@@ -749,13 +770,23 @@
                 }
 
                 if ($grade->is_excluded()) {
-                    $cellclasses .= ' excluded';
+                    // $cellclasses .= ' excluded';
                 }
 
-                $studentshtml .= '<td class="'.$cellclasses.'">';
+                $grade_title = '&lt;div class=&quot;fullname&quot;&gt;'.fullname($user).'&lt;/div&gt;';
+                $grade_title .= '&lt;div class=&quot;itemname&quot;&gt;'.$item->get_name(true).'&lt;/div&gt;';
+
+                if (!empty($grade->feedback) && !$USER->gradeediting[$this->courseid]) {
+                    $grade_title .= '&lt;div class=&quot;feedback&quot;&gt;'
+                                 .wordwrap(trim(format_string($grade->feedback, $grade->feedbackformat)), 34, '&lt;br/ &gt;') . '&lt;/div&gt;';
+                } else {
+
+                }
+
+                $studentshtml .= '<td class="'.$cellclasses.'" title="'.$grade_title.'">';
 
                 if ($grade->is_excluded()) {
-                    $studentshtml .= get_string('excluded', 'grades') . ' ';
+                    $studentshtml .= '<span class="excludedfloater">'.get_string('excluded', 'grades') . '</span> ';
                 }
 
                 // Do not show any icons if no grade (no record in DB to match)
@@ -817,7 +848,7 @@
                             if ($gradeval < 1) {
                                 $studentshtml .= '<span class="gradevalue'.$hidden.$gradepass.'">-</span>';
                             } else {
-                                $gradeval = (int)bounded_number($grade->grade_item->grademin, $gradeval, $grade->grade_item->grademax); //just in case somebody changes scale
+                                $gradeval = $grade->grade_item->bounded_grade($gradeval); //just in case somebody changes scale
                                 $studentshtml .= '<span class="gradevalue'.$hidden.$gradepass.'">'.$scales[$gradeval-1].'</span>';
                             }
                         } else {
@@ -839,9 +870,7 @@
 
                     // If quickfeedback is on, print an input element
                     if ($this->get_pref('showquickfeedback') and $grade->is_editable()) {
-                        if ($this->get_pref('quickgrading')) {
-                            $studentshtml .= '<br />';
-                        }
+
                         $studentshtml .= '<input type="hidden" name="oldfeedback_'
                                       .$userid.'_'.$item->id.'" value="' . s($grade->feedback) . '" />';
                         $studentshtml .= '<input class="quickfeedback" tabindex="' . $tabindices[$item->id]['feedback']
@@ -853,13 +882,6 @@
                     $gradedisplaytype = $item->get_displaytype();
 
                     // If feedback present, surround grade with feedback tooltip: Open span here
-                    if (!empty($grade->feedback)) {
-                        $overlib = '';
-                        $feedback = addslashes_js(trim(format_string($grade->feedback, $grade->feedbackformat)));
-                        $overlib = "return overlib('$feedback', BORDER, 0, FGCLASS, 'feedback', "
-                                  ."CAPTIONFONTCLASS, 'caption', CAPTION, '$strfeedback');";
-                        $studentshtml .= '<span onmouseover="'.s($overlib).'" onmouseout="return nd();">';
-                    }
 
                     if ($item->needsupdate) {
                         $studentshtml .= '<span class="gradingerror'.$hidden.$gradepass.'">'.get_string('error').'</span>';
@@ -885,6 +907,138 @@
         return $studentshtml;
     }
 
+    function get_studentnameshtml() {
+        global $CFG, $USER;
+        $studentshtml = '';
+
+        $showuserimage = $this->get_pref('showuserimage');
+        $showuseridnumber = $this->get_pref('showuseridnumber');
+        $fixedstudents = empty($USER->screenreader) && $CFG->grade_report_fixedstudents;
+
+        $strsortasc   = $this->get_lang_string('sortasc', 'grades');
+        $strsortdesc  = $this->get_lang_string('sortdesc', 'grades');
+        $strfirstname = $this->get_lang_string('firstname');
+        $strlastname  = $this->get_lang_string('lastname');
+
+        if ($this->sortitemid === 'lastname') {
+            if ($this->sortorder == 'ASC') {
+                $lastarrow = print_arrow('up', $strsortasc, true);
+            } else {
+                $lastarrow = print_arrow('down', $strsortdesc, true);
+            }
+        } else {
+            $lastarrow = '';
+        }
+
+        if ($this->sortitemid === 'firstname') {
+            if ($this->sortorder == 'ASC') {
+                $firstarrow = print_arrow('up', $strsortasc, true);
+            } else {
+                $firstarrow = print_arrow('down', $strsortdesc, true);
+            }
+        } else {
+            $firstarrow = '';
+        }
+
+        if ($fixedstudents) {
+            $studentshtml .= '<div class="left_scroller">
+                <table id="fixed_column" class="fixed_grades_column">
+                    <tbody class="leftbody">';
+
+            $colspan = '';
+            if ($showuseridnumber) {
+                $colspan = 'colspan="2"';
+            }
+
+            $levels = count($this->gtree->levels) - 1;
+
+
+            for ($i = 0; $i < $levels; $i++) {
+                $studentshtml .= '
+                        <tr class="heading name_row">
+                            <td '.$colspan.' class="fixedcolumn cell c0 topleft"> </td>
+                        </tr>
+                        ';
+            }
+
+            $studentshtml .= '<tr class="heading"><th class="header c0" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=firstname">'
+                        . $strfirstname . '</a> '
+                        . $firstarrow. '/ <a href="'.$this->baseurl.'&amp;sortitemid=lastname">' . $strlastname . '</a>'. $lastarrow .'</th>';
+
+            if ($showuseridnumber) {
+                if ('idnumber' == $this->sortitemid) {
+                    if ($this->sortorder == 'ASC') {
+                        $idnumberarrow = print_arrow('up', $strsortasc, true);
+                    } else {
+                        $idnumberarrow = print_arrow('down', $strsortdesc, true);
+                    }
+                } else {
+                    $idnumberarrow = '';
+                }
+                $studentshtml .= '<th class="header c0 useridnumber" scope="col"><a href="'.$this->baseurl.'&amp;sortitemid=idnumber">'
+                        . get_string('idnumber') . '</a> ' . $idnumberarrow . '</th>';
+            }
+
+            $studentshtml .= '</tr>';
+
+            if ($USER->gradeediting[$this->courseid]) {
+                $studentshtml .= '<tr class="controls"><th class="header c0 controls" scope="row" '.$colspan.'>'.$this->get_lang_string('controls','grades').'</th></tr>';
+            }
+
+            $row_classes = array(' even ', ' odd ');
+
+            foreach ($this->users as $userid => $user) {
+
+                $user_pic = null;
+                if ($showuserimage) {
+                    $user_pic = '<div class="userpic">' . print_user_picture($user, $this->courseid, NULL, 0, true) . "</div>\n";
+                }
+
+                $studentshtml .= '<tr class="r'.$this->rowcount++ . $row_classes[$this->rowcount % 2] . '">'
+                              .'<th class="c0 user" scope="row" onclick="set_row(this.parentNode.rowIndex);">'.$user_pic
+                              .'<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->course->id.'">'
+                              .fullname($user)."</a></th>\n";
+
+                if ($showuseridnumber) {
+                    $studentshtml .= '<th class="header c0 useridnumber" onclick="set_row(this.parentNode.rowIndex);">'. $user->idnumber."</th>\n";
+                }
+                $studentshtml .= "</tr>\n";
+            }
+
+            if ($this->get_pref('showranges')) {
+                $studentshtml .= '<tr class="range r'.$this->rowcount++.'">' . '<th class="header c0 range " '.$colspan.' scope="row">'.$this->get_lang_string('range','grades').'</th></tr>';
+            }
+
+            // Averages heading
+
+            $straverage_group = get_string('groupavg', 'grades');
+            $showaverages_group = $this->currentgroup && $this->get_pref('showgroups');
+            $straverage = get_string('overallaverage', 'grades');
+            $showaverages = $this->get_pref('showaverages');
+
+            if ($showaverages_group) {
+                $studentshtml .= '<tr class="groupavg r'.$this->rowcount++.'"><th class="header c0" '.$colspan.'scope="row">'.$straverage_group.'</th></tr>';
+            }
+
+            if ($showaverages) {
+                $studentshtml .= '<tr class="avg r'.$this->rowcount++.'"><th class="header c0" '.$colspan.'scope="row">'.$straverage.'</th></tr>';
+            }
+
+            $studentshtml .= '</tbody>
+                </table>
+            </div>
+            <div class="right_scroller">
+                <table id="user-grades" class="">
+                    <tbody class="righttest">';
+
+        } else {
+            $studentshtml .= '<table id="user-grades" class="gradestable flexible boxaligncenter generaltable">
+                                <tbody>';
+        }
+
+        return $studentshtml;
+    }
+
     /**
      * Builds and return the HTML row of column totals.
      * @param  bool $grouponly Whether to return only group averages or all averages.
@@ -903,7 +1057,6 @@
         $averagesdecimalpoints = $this->get_pref('averagesdecimalpoints');
         $meanselection         = $this->get_pref('meanselection');
         $shownumberofgrades    = $this->get_pref('shownumberofgrades');
-        $showuseridnumber      = $this->get_pref('showuseridnumber');
 
         $avghtml = '';
         $avgcssclass = 'avg';
@@ -950,16 +1103,8 @@
             }
 
             $columncount=0;
-            $colspan='';
-            if ($showuseridnumber) {
-                $colspan = 'colspan="2" ';
-            }
 
-            $avghtml = '<tr class="' . $avgcssclass . ' r'.$this->rowcount++.'"><th class="header c0" '.$colspan.'scope="row">'.$straverage.'</th>';
-
-            if ($showuseridnumber) {
-                $columncount++;
-            }
+            $avghtml = '<tr class="' . $avgcssclass . ' r'.$this->rowcount++.'">';
 
             // MDL-10875 Empty grades must be evaluated as grademin, NOT always 0
             // This query returns a count of ungraded grades (NULL finalgrade OR no matching record in grade_grades table)
@@ -978,6 +1123,15 @@
 
             $ungraded_counts = get_records_sql($SQL);
 
+            $fixedstudents = empty($USER->screenreader) && $CFG->grade_report_fixedstudents;
+            if (!$fixedstudents) {
+                $colspan='';
+                if ($this->get_pref('showuseridnumber')) {
+                    $colspan = 'colspan="2" ';
+                }
+                $avghtml .= '<th class="header c0 range "'.$colspan.' scope="row">'.$straverage.'</th>';
+            }
+
             foreach ($this->gtree->items as $itemid=>$unused) {
                 $item =& $this->gtree->items[$itemid];
 
@@ -1049,25 +1203,23 @@
      * @return string HTML
      */
     function get_rangehtml() {
-        global $USER;
-        $showuseridnumber      = $this->get_pref('showuseridnumber');
+        global $USER, $CFG;
 
-        $scalehtml = '';
+        $rangehtml = '';
         if ($this->get_pref('showranges')) {
             $rangesdisplaytype   = $this->get_pref('rangesdisplaytype');
             $rangesdecimalpoints = $this->get_pref('rangesdecimalpoints');
 
             $columncount=0;
+            $rangehtml = '<tr class="range r'.$this->rowcount++.' heading">';
+
+            $fixedstudents = empty($USER->screenreader) && $CFG->grade_report_fixedstudents;
+            if (!$fixedstudents) {
-            $colspan='';
+                $colspan='';
-            if ($showuseridnumber) {
+                if ($this->get_pref('showuseridnumber')) {
-                $colspan = 'colspan="2" ';
-            }
+                    $colspan = 'colspan="2" ';
+                }
-
-            $scalehtml = '<tr class="r'.$this->rowcount++.'">'
-                       . '<th class="header c0 range" '.$colspan.'scope="row">'.$this->get_lang_string('range','grades').'</th>';
-
-            if ($showuseridnumber) {
-                $columncount++;
+                $rangehtml .= '<th class="header c0 range "'.$colspan.' scope="row">'.$this->get_lang_string('range','grades').'</th>';
             }
 
             foreach ($this->gtree->items as $itemid=>$unused) {
@@ -1081,12 +1233,12 @@
 
                 $formatted_range = $item->get_formatted_range($rangesdisplaytype, $rangesdecimalpoints);
 
-                $scalehtml .= '<th class="header c'.$columncount++.' range"><span class="rangevalues'.$hidden.'">'. $formatted_range .'</span></th>';
+                $rangehtml .= '<th class="header c'.$columncount++.' range"><span class="rangevalues'.$hidden.'">'. $formatted_range .'</span></th>';
 
             }
-            $scalehtml .= '</tr>';
+            $rangehtml .= '</tr>';
         }
-        return $scalehtml;
+        return $rangehtml;
     }
 
     /**
@@ -1099,15 +1251,9 @@
         $iconshtml = '';
         if ($USER->gradeediting[$this->courseid]) {
 
-            $colspan='';
-            if ($this->get_pref('showuseridnumber')) {
-                $colspan = 'colspan="2" ';
-            }
+            $iconshtml = '<tr class="controls">';
 
-            $iconshtml = '<tr class="r'.$this->rowcount++.'">'
-                       . '<th class="header c0 range" scope="row" '.$colspan.'>'.$this->get_lang_string('controls','grades').'</th>';
-
-            $columncount = 1;
+            $columncount = 0;
             foreach ($this->gtree->items as $itemid=>$unused) {
                 // emulate grade element
                 $item =& $this->gtree->items[$itemid];
@@ -1115,7 +1261,7 @@
                 $eid = $this->gtree->get_item_eid($item);
                 $element = $this->gtree->locate_element($eid);
 
-                $iconshtml .= '<td class="cell c'.$columncount++.' icons">' . $this->get_icons($element) . '</td>';
+                $iconshtml .= '<td class="controls cell c'.$columncount++.' icons">' . $this->get_icons($element) . '</td>';
             }
             $iconshtml .= '</tr>';
         }
@@ -1138,7 +1284,12 @@
         }
 
         // Init all icons
+        $edit_icon = '';
+
+        if ($element['type'] != 'categoryitem' && $element['type'] != 'courseitem') {
-        $edit_icon             = $this->gtree->get_edit_icon($element, $this->gpr);
+            $edit_icon             = $this->gtree->get_edit_icon($element, $this->gpr);
+        }
+
         $edit_calculation_icon = '';
         $show_hide_icon        = '';
         $lock_unlock_icon      = '';
@@ -1182,7 +1333,7 @@
 
             if (in_array($element['object']->id, $this->collapsed['aggregatesonly'])) {
                 $expand_contract = 'switch_plus';
-            } elseif (in_array($element['object']->id, $this->collapsed['gradesonly'])) {
+            } elseif (!empty($this->collapsed['gradesonly']) && in_array($element['object']->id, $this->collapsed['gradesonly'])) {
                 $expand_contract = 'switch_whole';
             }
             $url = $this->gpr->get_return_url(null, array('target'=>$element['eid'], 'action'=>$expand_contract, 'sesskey'=>sesskey()));
Index: grade/report/grader/preferences.php
=========================================================
--- grade/report/grader/preferences.php	(revision 1.25.2.2)
+++ grade/report/grader/preferences.php	Thu Apr 16 09:31:42 CEST 2009
@@ -44,12 +44,8 @@
 require('preferences_form.php');
 $mform = new grader_report_preferences_form('preferences.php', compact('course'));
 
-if ($mform->is_cancelled()){
-    redirect($CFG->wwwroot . '/grade/report/grader/index.php?id='.$courseid);
-}
-
 // If data submitted, then process and store.
-if ($data = $mform->get_data()) {
+if (!$mform->is_cancelled() && $data = $mform->get_data()) {
     foreach ($data as $preference => $value) {
         if (substr($preference, 0, 6) !== 'grade_') {
             continue;
@@ -66,21 +62,11 @@
     exit;
 }
 
-$strgrades = get_string('grades');
-$strgraderreport = get_string('modulename', 'gradereport_grader');
-$strgradepreferences = get_string('gradepreferences', 'grades');
-
-$navigation = grade_build_nav(__FILE__, $strgradepreferences, $courseid);
-
-print_header_simple($strgrades.': '.$strgraderreport . ': ' . $strgradepreferences,': '.$strgradepreferences, $navigation,
-                    '', '', true, '', navmenu($course));
-
-/// Print the plugin selector at the top
-print_grade_plugin_selector($course->id, 'report', 'grader');
+if ($mform->is_cancelled()){
+    redirect($CFG->wwwroot . '/grade/report/grader/index.php?id='.$courseid);
+}
 
-// Add tabs
-$currenttab = 'preferences';
-include('tabs.php');
+print_grade_page_head($courseid, 'preferences', 'grader', get_string('preferences', 'gradereport_grader'));
 
 // If USER has admin capability, print a link to the site config page for this report
 if (has_capability('moodle/site:config', $systemcontext)) {
Index: grade/report/grader/settings.php
=========================================================
--- grade/report/grader/settings.php	(revision 1.34.2.6)
+++ grade/report/grader/settings.php	Thu Apr 16 09:31:42 CEST 2009
@@ -39,6 +39,9 @@
 $settings->add(new admin_setting_configcheckbox('grade_report_showquickfeedback', get_string('quickfeedback', 'grades'),
                                             get_string('configshowquickfeedback', 'grades'), 0));
 
+$settings->add(new admin_setting_configcheckbox('grade_report_fixedstudents', get_string('fixedstudents', 'grades'),
+                                            get_string('configfixedstudents', 'grades'), 0));
+
 $settings->add(new admin_setting_configselect('grade_report_aggregationview', get_string('aggregationview', 'grades'),
                                           get_string('configaggregationview', 'grades'), GRADE_REPORT_AGGREGATION_VIEW_FULL,
                                           array(GRADE_REPORT_AGGREGATION_VIEW_FULL => get_string('full', 'grades'),
Index: grade/report/grader/styles.php
=========================================================
--- grade/report/grader/styles.php	(revision 1.1.2.14)
+++ grade/report/grader/styles.php	Thu Apr 16 09:31:42 CEST 2009
@@ -1,237 +1,549 @@
 .flexible th {
-    white-space:normal;
+white-space:normal;
 }
 
 .gradestable th.user img {
-    width: 20px;
-    height: 20px;
-}
-
-.gradestable th.user, .gradestable th.range {
-    white-space: nowrap;
+width:20px;
+height:20px;
 }
 
-.grade-report-grader table#user-grades .catlevel1 {
-  background-color: #ffffff;
-}
 .grade-report-grader table#user-grades .catlevel2 {
-  background-color: #eeeeee;
-}
-.grade-report-grader table#user-grades .catlevel3 {
-  background-color: #dddddd;
+background-color:#f9f9f9;
 }
 
 .grade-report-grader table#user-grades td.overridden {
-  background-color: #EFD9B3;
+background-color:#ddd;
 }
 
 .grade-report-grader table#user-grades tr.avg td.cell {
-    background-color: #efefff;
+background-color:#efefff;
+font-weight:700;
+color:#00008B;
 }
 
 .grade-report-grader table#user-grades tr.odd td.cell {
-    background-color: #efefef;
+background-color:#efefef;
+white-space:nowrap;
 }
 
 .grade-report-grader table#user-grades tr.even td.overridden {
-  background-color: #F3E4C0; 
+background-color:#F3E4C0;
 }
 
 .grade-report-grader table#user-grades tr.odd td.overridden {
-  background-color: #EFD9A4;
+background-color:#EFD9A4;
 }
 
 .grade-report-grader table#user-grades tr.even td.excluded {
-  background-color: #EABFFF; 
+background-color:#EABFFF;
 }
 
 .grade-report-grader table#user-grades tr.odd td.excluded {
-  background-color: #E5AFFF;
+background-color:#E5AFFF;
 }
 
-.grade-report-grader table#user-grades tr.odd th.header { 
+.grade-report-grader table#user-grades tr.odd th.header {
-    background-color: #efefef;
-    background-image: none;
-}
-
-.grade-report-grader table#user-grades td.vmarked, .grade-report-grader table#user-grades tr.odd td.vmarked {
-    background-color: #ffcc33;
-}
-
-.grade-report-grader table#user-grades td.hmarked, .grade-report-grader table#user-grades tr.odd td.hmarked {
-    background-color: #ffff99;
-}
-
-.grade-report-grader table#user-grades td.hmarked.vmarked, .grade-report-grader table#user-grades tr.odd td.hmarked.vmarked{
-    background-color: #ffcc99;
-} 
-
-.grade-report-grader table#user-grades tr.groupavg td.cell {
-    background-color: #efffef;
+background-color:#efefef;
+background-image:none;
 }
 
 .grade-report-grader table#user-grades tr.groupavg td.cell {
-  font-weight: bold;
-  color: #006400;
+background-color:#efffef;
+font-weight:700;
+color:#006400;
 }
 
-.grade-report-grader table#user-grades tr.avg td.cell {
-  font-weight: bold;
-  color: #00008B;
-}
-
-.grade-report-grader table#user-grades td.cat,
-.grade-report-grader table#user-grades td.course {
-  font-weight: bold;
-}
-
-.grade-report-grader table#user-grades {
-    font-size: 80%;
+.grade-report-grader table#user-grades td.cat,.grade-report-grader table#user-grades td.course {
+font-weight:700;
 }
 
 .grade-report-grader table#user-grades {
-  border-width:1px;
-  border-style:solid;
-  margin-top: 20px;
+font-size:10px;
+width:auto;
+background-color:transparent;
+border-style:solid;
+border-width:1px;
+margin:20px 0 0;
 }
 
 .grade-report-grader #overDiv table {
-  margin: 0;  
+margin:0;
 }
 
 .grade-report-grader #overDiv table td.feedback {
-  border: 0px;
+border:0;
 }
+
 .grade-report-grader #overDiv .feedback {
-  background-color: #AABBFF;
-  color: #000000;
-  font-family: Verdana;
-  font-size: 70%;
-  font-weight: normal;  
+font-size:70%;
+background-color:#ABF;
+color:#000;
+font-family:Verdana;
+font-weight:400;
 }
 
 .grade-report-grader #overDiv .caption {
-  background-color: #5566CC;
-  color: #CCCCFF;
-  font-family: Arial;
-  font-size: 70%;
-  font-weight: bold;
+font-size:70%;
+background-color:#56C;
+color:#CCF;
+font-family:Arial;
+font-weight:700;
 }
 
-.grade-report-grader div.submit {
-  margin-top: 20px;
-  text-align: center;
+.grade-report-grader #overDiv .intersection {
+font-size:70%;
+background-color:#ABF;
+color:#000;
+font-family:Verdana;
+font-weight:400;
 }
 
-.grade-report-grader table#user-grades td {
-  border-width:1px;
-  border-style:solid;
+.grade-report-grader #overDiv .intersectioncaption {
+background-color:#56C;
+color:#CCF;
+font-family:Arial;
+font-weight:700;
 }
 
-.grade-report-grader table#user-grades tr.heading {
-  border-width:0px 0px 0px 0px;
-  border-style:solid;
+.grade-report-grader div.submit {
+margin-top:20px;
+text-align:center;
 }
 
-.grade-report-grader table#user-grades .heading td {
-  border-width:0px 0px 0px 0px;
-  border-style:solid;
+.grade-report-grader table#user-grades td {
+text-align:right;
+border-style:solid;
+border-width:0 1px 1px 0;
 }
 
 .grade-report-grader table#user-grades th.category {
-  border-width:1px 1px 0px 1px;
-  border-style:solid;
-  vertical-align: top;
+vertical-align:top;
+border-style:solid;
+border-width:1px 1px 0;
 }
 
 .grade-report-grader table#user-grades th.user {
-  border-width:0px 0px 1px 0px;
-  border-style:solid;
+text-align:left;
+border-style:solid;
+border-width:0 0 1px;
 }
 
 .grade-report-grader table#user-grades th.useridnumber {
-  border-width:0px 0px 1px 1px;
-  border-style:solid;
+border-style:solid;
+border-width:0 0 1px 1px;
 }
 
+.grade-report-grader table#user-grades th.categoryitem,.grade-report-grader table#user-grades th.courseitem,.grade-report-grader table#user-grades td.topleft {
+vertical-align:top;
+border-style:solid;
+border-width:0 1px;
+}
+
+.grade-report-grader table#user-grades th.category,
+.grade-report-grader table#user-grades th.item,
 .grade-report-grader table#user-grades th.categoryitem,
-.grade-report-grader table#user-grades th.courseitem,
-.grade-report-grader table#user-grades td.topleft {
-  border-width:0px 1px 0px 1px;
-  border-style:solid;
-  vertical-align: top;
+.grade-report-grader table#user-grades th.courseitem {
+height: 25px;
+}
+
+.grade-report-grader td,.grade-report-grader th {
+border-color:#CECECE;
 }
 
 .grade-report-grader table#participants th {
-  vertical-align: top; 
+vertical-align:top;
+width:auto;
 }
 
 .grade-report-grader table#user-grades td.fillerfirst {
-  border-width:0px 0px 0px 1px;
-  border-style:solid;
+border-style:solid;
+border-width:0 0 0 1px;
 }
 
 .grade-report-grader table#user-grades td.fillerlast {
-  border-width:0px 1px 0px 0px;
-  border-style:solid;
+border-style:solid;
+border-width:0 1px 0 0;
 }
 
-.grade-report-grader table#user-grades th.item {
-  border-width:1px 1px 1px 1px;
-  border-style:solid;
-  vertical-align: top;
+.grade-report-grader table#user-grades th.item ,
+.grade-report-grader table#user-grades th.categoryitem ,
+.grade-report-grader table#user-grades th.courseitem {
+border-bottom-color:#000;
+vertical-align:top;
+border-style:solid;
+border-width:1px;
 }
 
 .grade-report-grader div.gradertoggle {
-  display: inline;
-  margin-left: 20px;
+display:inline;
+margin-left:20px;
+}
+
+.grade-report-grader table#user-grades th.range {
+text-align:right;
+border-style:solid;
+border-width:1px;
+}
+
+.grade-report-grader table#user-grades .userpic {
+display:inline;
+margin-right:10px;
+}
+
+.grade-report-grader table#user-grades .quickfeedback {
+border:#000 1px dashed;
+}
+
+.grade-report-grader #siteconfiglink {
+text-align:right;
+}
+
+.grade-report-grader table#user-grades .hidden,.grade-report-grader table#user-grades .hidden a {
+color:#aaa;
+}
+
+.grade-report-grader table#user-grades .datesubmitted {
+font-size:.7em;
+}
+
+.grade-report-grader table#user-grades td.cell {
+padding-left:5px;
+padding-right:5px;
+vertical-align:middle;
+}
+
+.grade-report-grader table {
+border-collapse:collapse;
+background-color:#fff;
+font-size:10px;
+border-color:#cecece;
+}
+
+.grade-report-grader th {
+padding:2px 10px 0;
+}
+
+.grade-report-grader span.inclusion-links {
+margin:0 5px 0 10px;
+}
+
+.grade-report-grader table#user-grades .item {
+background-color:#e9e9e9;
+}
+
+.grade-report-grader table tr.odd th.header {
+background-color:#efefef;
+background-image:none;
+border-width:0 0 1px;
+}
+
+.grade-report-grader table tr.heading th.header {
+border-top:1px solid #cecece;
+}
+
+.grade-report-grader table#user-grades tr.heading th.categoryitem,.grade-report-grader table#user-grades tr.heading th.courseitem {
+border-width:0 0 0 1px;
+}
+
+.grade-report-grader table#user-grades th.category.header.catlevel1 {
+vertical-align:top;
+border-style:solid;
+border-width:1px 1px 0 0;
+}
+
+.grade-report-grader div.left_scroller th.user a {
+vertical-align:middle;
+margin:0;
+padding:0;
+}
+
+.grade-report-grader table#user-grades th.categoryitem,.grade-report-grader table#user-grades th.courseitem,.grade-report-grader table td.topleft {
+vertical-align:top;
+border-color:#cecece #cecece #000;
+border-style:solid;
+border-width:0 1px 1px;
+}
+
+.grade-report-grader table td.topleft {
+border-bottom:none;
+}
+
+.grade-report-grader table#user-grades td.topleft {
+background-color:#fff;
+}
+
+.grade-report-grader div.userpic {
+margin-right:10px;
+float:left;
+}
+
+.grade-report-grader div.userpic img {
+border:3px double #cecece;
+vertical-align:middle;
+width:2.7em;
+height:2.7em;
 }
 
-.grade-report-grader table#user-grades {
-  margin-left:auto;
-  margin-right:auto;
+.grade-report-grader a.quickedit {
+line-height:1em;
+display:block;
+float:right;
+clear:none;
+font-size:9px;
+background-color:transparent;
+margin:.1em 0 0;
+}
+
+.grade-report-grader a.quickedit2 {
+display:block;
+float:right;
+clear:none;
+background-color:transparent;
+margin:1.3em 0 0;
+}
+
+.grade-report-grader table#quick_edit {
+border:1px solid #cecece;
+margin:0 auto;
+}
+
+.grade-report-grader table#quick_edit td {
+vertical-align:middle;
+border:1px solid #cecece;
+text-align:left;
+margin:0;
+padding:5px;
+}
+
+.grade-report-grader table#quick_edit td img {
+border:3px double #cecece;
+vertical-align:middle;
+padding:0;
+}
+
+.grade-report-grader td input {
+border:1px solid #666;
+margin-left:10px;
+margin-right:10px;
+}
+
+.grade-report-grader table#quick_edit td.fullname {
+border-left:none;
+padding-left:5px;
+}
+
+.grade-report-grader table#quick_edit td.picture {
+border-right:none;
+}
+
+.grade-report-grader table#quick_edit td.finalgrade input {
+width:5em;
+}
+
+.grade-report-grader h1 {
+text-align:center;
+clear:both;
+}
+
+.grade-report-grader form {
+text-align:center;
+}
+
+.grade-report-grader input.center {
+margin:10px auto 0;
+}
+
+.grade-report-grader .lefttbody {
+width:auto;
+vertical-align:middle;
+}
+
+.grade-report-grader table#user-grades th.fixedcolumn {
+border:1px solid #cecece;
+vertical-align:middle;
+}
+
+.grade-report-grader table#fixed_column th {
+border:1px solid #cecece;
+vertical-align:middle;
+border-right-color:#000;
+}
+
+.grade-report-grader table#fixed_column {
+padding-top:20px;
+border-top:1px solid #cecece;
+background-color:#fff;
+}
+
+.grade-report-grader .left_scroller {
+float:left;
+clear:none;
+padding-top:20px;
+}
+
+.grade-report-grader .right_scroller {
+width:auto;
+clear:none;
+overflow-x:auto;
+}
+
+.grade-report-grader table tr.avg,.grade-report-grader table tr.groupavg td,.grade-report-grader table tr.avg td,.grade-report-grader table tr.groupavg th,.grade-report-grader table tr.avg th,.grade-report-grader table tr.controls_row,.grade-report-grader table tr.controls_row th,.grade-report-grader table tr.range_row,.grade-report-grader table tr.range_row th,div.right_scroller tr {
+height:2em;
+}
+
+.grade-report-grader table#user-grades tr.groupavg td.cell,.grade-report-grader tr.groupavg th.header {
+background-color:#efffef;
+}
+
+.grade-report-grader form td.excluded {
+color:red;
+}
+
+.grade-report-grader .excludedfloater {
+font-weight:700;
+color:red;
+font-size:9px;
+float:left;
+}
+
+.grade-report-grader span.gradepass {
+color:#298721;
+}
+
+.grade-report-grader span.gradefail {
+color:#890d0d;
+}
+
+.gradeweight {
+color:#461d7c;
+font-weight:700;
+}
+
+.grade-report-grader td select {
+font-size:100%;
+padding:0;
+}
+
+.grade-report-grader .righttest td select {
+font-size:86%;
+padding:0;
+}
+
+.grade-report-grader tr.avg,tr.controls,td.controls,th.controls,.grade-report-grader tr.groupavg,tr.range,th.range,td.range,tr.heading th.range {
+height:2em!important;
+white-space:nowrap;
+}
+
+.heading_name_row th {
+white-space:nowrap;
+width:2000px;
+}
+
+.ie .right_scroller {
+padding-bottom:15px;
+overflow-y:hidden;
+}
+
+.ie table#fixed_column th {
+height:40.5px;
+}
+
+.ie table#fixed_column tr.avg th {
+height:21px;
+}
+
+.ie div.left_scroller td {
+height:42px;
+}
+
+.ie6 div.left_scroller {
+margin-top:40px;
+}
+
+.ie6 div.right_scroller {
+margin-top:40px;
+width:auto;
+position:absolute;
+}
+
+.ie6 .excludedfloater {
+font-size:7px;
+}
+
+.grade_icons img.ajax {
+float:right;
+}
+
+.gradestable th.user,.gradestable th.range,.grade-report-grader .flexible th,.grade-report-grader .flexible td,.grade-report-grader .flexible th a,.grade-report-grader .flexible td a,.grade-report-grader .gradestable th.range,.grade-report-grader td {
+white-space:nowrap;
+}
+
+.grade-report-grader table#user-grades .catlevel1,.grade-report-grader .r1,.grade-report-grader table tr.even td.cell,.grade-report-grader table tr.even th {
+background-color:#fff;
+}
+
+.grade-report-grader table#user-grades .catlevel3,.grade-report-grader table tr.odd td.cell {
+background-color:#efefef;
 }
 
-.grade-report-grader table#user-grades  th.user {
-  text-align:left;
+.grade-report-grader table#fixed_column tr.odd th ,
+.grade-report-grader table#user-grades tr.odd th {
+background-color:#efefef;
 }
 
-.grade-report-grader table#user-grades  td.useridnumber {
-  text-align:left;
+.grade-report-grader table#user-grades td.vmarked,.grade-report-grader table#user-grades tr.odd td.vmarked,.grade-report-grader table#user-grades td.vmarked,.grade-report-grader table#user-grades tr.odd td.vmarked,.grade-report-grader table#user-grades tr.even td.vmarked {
+background-color:#fc3;
 }
 
-.grade-report-grader table#user-grades  td {
-  text-align:right;
+.grade-report-grader table#user-grades td.hmarked,.grade-report-grader table#user-grades tr.odd td.hmarked,.grade-report-grader table#user-grades td.hmarked,.grade-report-grader table#user-grades tr.odd td.hmarked,.grade-report-grader table#user-grades tr.even td.hmarked {
+background-color:#ff9;
 }
 
-.grade-report-grader table#user-grades th.range {
-  border-width:1px 1px 1px 1px;
-  border-style:solid;
+.grade-report-grader table#user-grades td.hmarked.vmarked,.grade-report-grader table#user-grades tr.odd td.hmarked.vmarked,.grade-report-grader table#user-grades td.hmarked.vmarked,.grade-report-grader table#user-grades tr.even td.hmarked.vmarked,.grade-report-grader table#user-grades tr.odd td.hmarked.vmarked {
+background-color:#fc9;
+}
+
+.grade-report-grader table#user-grades tr.heading,.grade-report-grader table#user-grades .heading td {
+border-style:solid;
+border-width:0;
 }
 
-.grade-report-grader table#user-grades .userpic {
-  display: inline;
-  margin-right: 10px;
+.grade-report-grader table#user-grades td.useridnumber,.grade-report-grader table#user-grades th,.grade-report-grader div.gradeparent,.ie6 form,.grade-report-grader table#user-grades td.ajax {
+text-align:left;
 }
 
-.grade-report-grader table#user-grades .quickfeedback {
-  border: #000000 1px dashed;
+.grade-report-grader table tr.avg td.cell,.grade-report-grader table#user-grades td.controls,.grade-report-grader table tr.avg,.grade-report-grader table tr.avg td,.grade-report-grader table tr.avg th {
+background-color:#f3ead8;
 }
 
-.grade-report-grader #siteconfiglink {
-  text-align: right;
+.grade-report-grader div.left_scroller tr,.grade-report-grader div.right_scroller tr,.grade-report-grader div.left_scroller td,.grade-report-grader div.right_scroller td,.grade-report-grader div.left_scroller th,.grade-report-grader div.right_scroller th {
+height:4em;
+font-size:10px;
 }
 
-.grade-report-grader table#user-grades .hidden,
-.grade-report-grader table#user-grades .hidden a {
-  color:#aaaaaa;
+.grade-report-grader table th.user,.grade-report-grader table td.useridnumber {
+text-align:left;
+vertical-align:middle;
 }
 
-.grade-report-grader table#user-grades .datesubmitted {
+.grade-report-grader .yui-overlay {
+    background-color: #FFEE69;
+    border-color: #D4C237 #A6982B #A6982B;
+    border-style: solid;
+    border-width: 1px;
+    left: 0;
+    padding: 2px 5px;
-  font-size: 0.7em;
+    font-size: 0.7em;
 }
 
-.grade-report-grader table#user-grades td.cell {
-  padding-left: 5px;
-  padding-right: 5px;
+.grade-report-grader .yui-overlay .fullname {
+    color: #5F3E00;
+    font-weight: bold;
+}
+.grade-report-grader .yui-overlay .itemname {
+    color: #194F3E;
+    font-weight: bold;
+}
+.grade-report-grader .yui-overlay .feedback {
+    color: #5F595E;
 }
Index: grade/report/lib.php
=========================================================
--- grade/report/lib.php	(revision 1.30.2.10)
+++ grade/report/lib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -126,7 +126,6 @@
      */
     var $groupwheresql;
 
-
     /**
      * Constructor. Sets local copies of user preferences and initialises grade_tree.
      * @param int $courseid
@@ -156,6 +155,9 @@
         // roles to be displayed in the gradebook
         $this->gradebookroles = $CFG->gradebookroles;
 
+        // Set up link to preferences page
+        $this->preferences_page = $CFG->wwwroot.'/grade/report/grader/preferences.php?id='.$courseid;
+
         // init gtree in child class
     }
 
@@ -182,7 +184,7 @@
             } elseif (isset($CFG->$fullprefname)) {
                 $retval = get_user_preferences($fullprefname, $CFG->$fullprefname);
             } elseif (isset($CFG->$shortprefname)) {
-                $retval = get_user_preferences($fullprefname, $CFG->$shortprefname); 
+                $retval = get_user_preferences($fullprefname, $CFG->$shortprefname);
             } else {
                 $retval = null;
             }
@@ -308,10 +310,11 @@
      * @param string HTML
      */
     function get_sort_arrow($direction='move', $sort_link=null) {
-        $matrix = array('up' => 'asc', 'down' => 'desc', 'move' => 'desc');
+        $matrix = array('up' => 'desc', 'down' => 'asc', 'move' => 'desc');
         $strsort = $this->get_lang_string('sort' . $matrix[$direction]);
+
         $arrow = print_arrow($direction, $strsort, true);
-        $html = '<a href="'.$sort_link .'">' . $arrow . '</a>';
+        $html = '<a href="'.$sort_link .'" alt="'.$strsort.'" title="'.$strsort.'">' . $arrow . '</a>';
         return $html;
     }
 }
Index: grade/report/outcomes/index.php
=========================================================
--- grade/report/outcomes/index.php	(revision 1.11.2.3)
+++ grade/report/outcomes/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -38,16 +38,6 @@
 
 require_capability('gradereport/outcomes:view', $context);
 
-// Build navigation
-$strgrades = get_string('grades');
-$stroutcomes = get_string('outcomes', 'grades');
-
-$navigation = grade_build_nav(__FILE__, $stroutcomes, $course->id);
-
-/// Print header
-print_header_simple($strgrades.':'.$stroutcomes, ':'.$strgrades, $navigation, '', '', true);
-print_grade_plugin_selector($courseid, 'report', 'outcomes');
-
 //first make sure we have proper final grades
 grade_regrade_final_grades($courseid);
 
@@ -165,7 +155,9 @@
 
 
 $html .= '</table>';
-print_heading($stroutcomes);
+
+print_grade_page_head($courseid, 'report', 'outcomes');
+
 
 echo $html;
 print_footer($course);
Index: grade/report/overview/index.php
=========================================================
--- grade/report/overview/index.php	(revision 1.2.2.3)
+++ grade/report/overview/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -67,18 +67,6 @@
 }
 $USER->grade_last_report[$course->id] = 'overview';
 
-/// Build navigation
-$strgrades  = get_string('grades');
-$reportname = get_string('modulename', 'gradereport_overview');
-
-$navigation = grade_build_nav(__FILE__, $reportname, $course->id);
-
-/// Print header
-print_header_simple($strgrades.': '.$reportname, ': '.$strgrades, $navigation,
-                    '', '', true, '', navmenu($course));
-
-/// Print the plugin selector at the top
-print_grade_plugin_selector($course->id, 'report', 'overview');
 
 if ($access) {
 
@@ -87,9 +75,7 @@
 
     // Create a report instance
     $report = new grade_report_overview($userid, $gpr, $context);
-
-    // print the page
-    print_heading(get_string('modulename', 'gradereport_overview'). ' - '.fullname($report->user));
+    print_grade_page_head($courseid, 'report', 'overview', get_string('modulename', 'gradereport_overview'). ' - '.fullname($report->user));
 
     if ($report->fill_table()) {
         echo $report->print_table(true);
@@ -97,6 +83,15 @@
 
 } else {
     // no access to grades!
+    /// Print header
+    /// Build navigation
+    $strgrades  = get_string('grades');
+    $reportname = get_string('modulename', 'gradereport_overview');
+
+    $navigation = grade_build_nav(__FILE__, $reportname, $course->id);
+    print_header_simple($strgrades.': '.$reportname, ': '.$strgrades, $navigation,
+                        '', '', true, '', navmenu($course));
+
     echo "Can not view grades."; //TODO: localize
 }
 print_footer($course);
Index: grade/report/styles.css
=========================================================
--- grade/report/styles.css	Thu Apr 16 09:31:42 CEST 2009
+++ grade/report/styles.css	Thu Apr 16 09:31:42 CEST 2009
@@ -0,0 +1,4 @@
+#content #siteconfiglink {
+ text-align: right;
+}
+
Index: grade/report/user/index.php
=========================================================
--- grade/report/user/index.php	(revision 1.26.2.9)
+++ grade/report/user/index.php	Thu Apr 16 09:31:42 CEST 2009
@@ -71,19 +71,6 @@
 }
 $USER->grade_last_report[$course->id] = 'user';
 
-/// Build navigation
-$strgrades  = get_string('grades');
-$reportname = get_string('modulename', 'gradereport_user');
-
-$navigation = grade_build_nav(__FILE__, $reportname, $courseid);
-
-/// Print header
-print_header_simple($strgrades.': '.$reportname, ': '.$strgrades, $navigation,
-                    '', '', true, '', navmenu($course));
-
-/// Print the plugin selector at the top
-print_grade_plugin_selector($courseid, 'report', 'user');
-
 
 if ($access) {
 
@@ -92,41 +79,47 @@
 
     if (has_capability('moodle/grade:viewall', $context)) { //Teachers will see all student reports
         /// Print graded user selector at the top
-        echo '<div id="graded_users_selector">';
-        print_graded_users_selector($course, 'report/user/index.php?id=' . $course->id, $userid);
-        echo '</div>';
-        echo "<p style = 'page-break-after: always;'></p>";
+        $user_selector = '<div id="graded_users_selector">';
+        $user_selector .= print_graded_users_selector($course, 'report/user/index.php?id=' . $course->id, $userid, true);
+        $user_selector .= '</div>';
+        $user_selector .= "<p style = 'page-break-after: always;'></p>";
 
         if ($userid === 0) {
             $gui = new graded_users_iterator($course);
             $gui->init();
+            // Add tabs
+            print_grade_page_head($courseid, 'report', 'user');
+
+            echo $user_selector.'<br />';
             while ($userdata = $gui->next_user()) {
                 $user = $userdata->user;
                 $report = new grade_report_user($courseid, $gpr, $context, $user->id);
                 print_heading(get_string('modulename', 'gradereport_user'). ' - '.fullname($report->user));
+
                 if ($report->fill_table()) {
-                    echo $report->print_table(true);
+                    echo '<br />'.$report->print_table(true);
                 }
                 echo "<p style = 'page-break-after: always;'></p>";
             }
             $gui->close();
         } elseif ($userid) { // Only show one user's report
             $report = new grade_report_user($courseid, $gpr, $context, $userid);
-            print_heading(get_string('modulename', 'gradereport_user'). ' - '.fullname($report->user));
+            print_grade_page_head($courseid, 'report', 'user', get_string('modulename', 'gradereport_user'). ' - '.fullname($report->user));
+            echo $user_selector;
             if ($report->fill_table()) {
-                echo $report->print_table(true);
+                echo '<br />'.$report->print_table(true);
-            } 
+            }
         }
-    } else { //Students will see just their own report 
+    } else { //Students will see just their own report
 
         // Create a report instance
         $report = new grade_report_user($courseid, $gpr, $context, $userid);
 
         // print the page
-        print_heading(get_string('modulename', 'gradereport_user'). ' - '.fullname($report->user));
+        print_grade_page_head($courseid, 'report', 'user', get_string('modulename', 'gradereport_user'). ' - '.fullname($report->user));
 
         if ($report->fill_table()) {
-            echo $report->print_table(true);
+            echo '<br />'.$report->print_table(true);
         }
     }
 
Index: grade/report/user/lib.php
=========================================================
--- grade/report/user/lib.php	(revision 1.18.2.17)
+++ grade/report/user/lib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -49,6 +49,8 @@
      */
     var $table;
 
+    var $gtree;
+
     /**
      * Flat structure similar to grade tree
      */
@@ -65,6 +67,22 @@
     var $showpercentage;
 
     /**
+     * Show range
+     */
+    var $showrange;
+
+    var $tableheaders;
+    var $tablecolumns;
+
+    var $maxdepth;
+    var $evenodd;
+
+    var $tabledata;
+    var $canviewhidden;
+
+    var $switch;
+
+    /**
      * Show hidden items even when user does not have required cap
      */
     var $showhiddenitems;
@@ -84,10 +102,24 @@
         $this->showpercentage  = grade_get_setting($this->courseid, 'report_user_showpercentage', $CFG->grade_report_user_showpercentage);
         $this->showhiddenitems = grade_get_setting($this->courseid, 'report_user_showhiddenitems', $CFG->grade_report_user_showhiddenitems);
 
-        $switch = grade_get_setting($this->courseid, 'aggregationposition', $CFG->grade_aggregationposition);
+        $this->showrange = true;
 
-        // Grab the grade_seq for this course
-        $this->gseq = new grade_seq($this->courseid, $switch);
+        $this->switch = grade_get_setting($this->courseid, 'aggregationposition', $CFG->grade_aggregationposition);
+
+        // Grab the grade_tree for this course
+        $this->gtree = new grade_tree($this->courseid, false, $this->switch, false, true);
+
+        // Determine the number of rows and indentation
+        $this->maxdepth = 1;
+        $this->inject_rowspans($this->gtree->top_element);
+        $this->maxdepth++; // Need to account for the lead column that spans all children
+        for ($i = 1; $i <= $this->maxdepth; $i++) {
+            $this->evenodd[$i] = 0;
+        }
+
+        $this->tabledata = array();
+
+        $this->canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->courseid));
 
         // get the user (for full name)
         $this->user = get_record('user', 'id', $userid);
@@ -100,6 +132,22 @@
         $this->setup_table();
     }
 
+    function inject_rowspans(&$element) {
+        if ($element['depth'] > $this->maxdepth) {
+            $this->maxdepth = $element['depth'];
+        }
+        if (empty($element['children'])) {
+            return 1;
+        }
+        $count = 1;
+        foreach ($element['children'] as $key=>$child) {
+            $count += $this->inject_rowspans($element['children'][$key]);
+        }
+        $element['rowspan'] = $count;
+        return $count;
+    }
+
+
     /**
      * Prepares the headers and attributes of the flexitable.
      */
@@ -111,196 +159,208 @@
          */
 
         // setting up table headers
-        $tablecolumns = array('itemname', 'category', 'grade');
-        $tableheaders = array($this->get_lang_string('gradeitem', 'grades'),
-                              $this->get_lang_string('category'),
+        $this->tablecolumns = array('itemname', 'grade');
+        $this->tableheaders = array($this->get_lang_string('gradeitem', 'grades'),
                               $this->get_lang_string('grade'));
 
+        if ($this->showrange) {
+            $this->tablecolumns[] = 'range';
+            $this->tableheaders[] = $this->get_lang_string('range', 'grades');
+        }
+
         if ($this->showpercentage) {
-            $tablecolumns[] = 'percentage';
-            $tableheaders[] = $this->get_lang_string('percentage', 'grades');
+            $this->tablecolumns[] = 'percentage';
+            $this->tableheaders[] = $this->get_lang_string('percentage', 'grades');
         }
 
         if ($this->showrank) {
             // TODO: this is broken if hidden grades present!!
-            $tablecolumns[] = 'rank';
-            $tableheaders[] = $this->get_lang_string('rank', 'grades');
+            $this->tablecolumns[] = 'rank';
+            $this->tableheaders[] = $this->get_lang_string('rank', 'grades');
         }
 
-        $tablecolumns[] = 'feedback';
-        $tableheaders[] = $this->get_lang_string('feedback', 'grades');
+        $this->tablecolumns[] = 'feedback';
+        $this->tableheaders[] = $this->get_lang_string('feedback', 'grades');
 
-        $this->table = new flexible_table('grade-report-user-'.$this->courseid);
-
-        $this->table->define_columns($tablecolumns);
-        $this->table->define_headers($tableheaders);
-        $this->table->define_baseurl($this->baseurl);
-
-        $this->table->set_attribute('cellspacing', '0');
-        $this->table->set_attribute('id', 'user-grade');
-        $this->table->set_attribute('class', 'boxaligncenter generaltable');
-
-        // not sure tables should be sortable or not, because if we allow it then sorted results distort grade category structure and sortorder
-        $this->table->set_control_variables(array(
-                TABLE_VAR_SORT    => 'ssort',
-                TABLE_VAR_HIDE    => 'shide',
-                TABLE_VAR_SHOW    => 'sshow',
-                TABLE_VAR_IFIRST  => 'sifirst',
-                TABLE_VAR_ILAST   => 'silast',
-                TABLE_VAR_PAGE    => 'spage'
-                ));
-
-        $this->table->setup();
     }
 
     function fill_table() {
-        global $CFG;
-        $numusers = $this->get_numusers(false); // total course users
-        $items =& $this->gseq->items;
-        $grades = array();
-
-        $canviewhidden = has_capability('moodle/grade:viewhidden', get_context_instance(CONTEXT_COURSE, $this->courseid));
-
-        // fetch or create all grades
-        foreach ($items as $key=>$unused) {
-            if (!$grade_grade = grade_grade::fetch(array('itemid'=>$items[$key]->id, 'userid'=>$this->user->id))) {
-                $grade_grade = new grade_grade();
-                $grade_grade->userid = $this->user->id;
-                $grade_grade->itemid = $items[$key]->id;
-            }
-            $grades[$key] = $grade_grade;
-            $grades[$key]->grade_item =& $items[$key];
+        //print "<pre>";
+        //print_r($this->gtree->top_element);
+        $this->fill_table_recursive($this->gtree->top_element);
+        //print_r($this->tabledata);
+        //print "</pre>";
+        return true;
-        }
+    }
 
-        if ($canviewhidden) {
-            $altered = array();
-            $unknown = array();
-        } else {
-            $hiding_affected = grade_grade::get_hiding_affected($grades, $items);
-            $altered = $hiding_affected['altered'];
-            $unknown = $hiding_affected['unknown'];
-            unset($hiding_affected);
-        }
+    function fill_table_recursive(&$element) {
+        global $CFG;
 
-        foreach ($items as $itemid=>$unused) {
-            $grade_item  =& $items[$itemid];
-            $grade_grade =& $grades[$itemid];
+        $type = $element['type'];
+        $depth = $element['depth'];
+        $grade_object = $element['object'];
+        $eid = $grade_object->id;
+        $fullname = $this->gtree->get_element_header($element, true, true, true);
+        $data = array();
+        $hidden = '';
+        $excluded = '';
+        $class = '';
 
-            if (!$canviewhidden and $grade_item->is_hidden()) {
-                if ($this->showhiddenitems == 0) {
-                    // no hidden items at all
-                    continue;
-                } else if ($this->showhiddenitems == 1 and !$grade_item->is_hiddenuntil()) {
-                    // hidden until that are still hidden are visible
-                    continue;
-                }
+        // If this is a hidden grade item, hide it completely from the user. showhiddenitems: 0 = hide all, 1 = show only hidden until, 2 = show all
+        if ($grade_object->is_hidden() && !$this->canviewhidden && (
+                $this->showhiddenitems == 0 ||
+                ($this->showhiddenitems == 1 && !$grade_object->is_hiddenuntil()))) {
+            return false;
-            }
+        }
 
-            $class = 'gradeitem';
-            if ($grade_item->is_course_item()) {
-                $class = 'courseitem';
-            } else if ($grade_item->is_category_item()) {
-                $class = 'categoryitem';
+        if ($type == 'category') {
+            $this->evenodd[$depth] = (($this->evenodd[$depth] + 1) % 2);
-            }
+        }
+        $alter = ($this->evenodd[$depth] == 0) ? 'even' : 'odd';
 
-            if (in_array($itemid, $unknown)) {
-                $gradeval = null;
-            } else if (array_key_exists($itemid, $altered)) {
-                $gradeval = $altered[$itemid];
-            } else {
-                $gradeval = $grade_grade->finalgrade;
+        /// Process those items that have scores associated
+        if ($type == 'item' or $type == 'categoryitem' or $type == 'courseitem') {
+            if (! $grade_grade = grade_grade::fetch(array('itemid'=>$grade_object->id,'userid'=>$this->user->id))) {
+                $grade_grade = new grade_grade();
+                $grade_grade->userid = $this->user->id;
+                $grade_grade->itemid = $grade_object->id;
             }
 
-            $data = array();
+            $grade_grade->load_grade_item();
 
-            // all users should know which items are still hidden
-            $hidden = '';
-            if ($grade_item->is_hidden()) {
-                $hidden = ' hidden ';
+            /// Hidden Items
+            if ($grade_grade->grade_item->is_hidden()) {
+                $hidden = ' hidden';
             }
 
-            $element = $this->gseq->locate_element($this->gseq->get_item_eid($grade_item));
-            $header = $this->gseq->get_element_header($element, true, true, true);
-
-            /// prints grade item name
-            $data[] = '<span class="'.$hidden.$class.'">'.$header.'</span>';
+            // If this is a hidden grade item, hide it completely from the user. showhiddenitems: 0 = hide all, 1 = show only hidden until, 2 = show all
+            if ($grade_grade->is_hidden() && !$this->canviewhidden && (
+                    $this->showhiddenitems == 0 ||
+                    ($this->showhiddenitems == 1 && !$grade_grade->is_hiddenuntil()))) {
+                // return false;
+            } else {
 
-            /// prints category
-            $cat = $grade_item->get_parent_category();
-            $data[] = '<span class="'.$hidden.$class.'">'.$cat->get_name().'</span>';
-
-            $hidden = '';
-            if ($grade_item->is_hidden()) {
-                // can not see grades in hidden items
-                $hidden = ' hidden ';
-            } else if ($canviewhidden and $grade_grade->is_hidden()) {
-                // if user can see hidden grades, indicate which ones are hidden
-                $hidden = ' hidden ';
+                /// Excluded Item
+                if ($grade_grade->is_excluded()) {
+                    $fullname .= ' ['.get_string('excluded', 'grades').']';
+                    $excluded = ' excluded';
-            }
+                }
 
-            /// prints the grade
-            if ($grade_grade->is_excluded()) {
-                $excluded = get_string('excluded', 'grades').' ';
+                /// Other class information
+                $class = "$hidden $excluded";
+                if ($this->switch) { // alter style based on whether aggregation is first or last
+                   $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggt b2b" : " item b1b";
-            } else {
+                } else {
-                $excluded = '';
+                   $class .= ($type == 'categoryitem' or $type == 'courseitem') ? " ".$alter."d$depth baggb" : " item b1b";
-            }
+                }
 
-            if ($grade_item->needsupdate) {
-                $data[] = '<span class="'.$hidden.$class.' gradingerror">'.get_string('error').'</span>';
+                /// Name
+                $data['itemname']['content'] = $fullname;
+                $data['itemname']['class'] = $class;
+                $data['itemname']['colspan'] = ($this->maxdepth - $depth);
 
-            } else if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$canviewhidden and $grade_grade->is_hidden()
-                   and !$grade_item->is_category_item() and !$grade_item->is_course_item()) {
+                /// Actual Grade
+                $gradeval = $grade_grade->finalgrade;
+                if ($grade_grade->grade_item->needsupdate) {
+                    $data['grade']['class'] = $class.' gradingerror';
+                    $data['grade']['content'] = get_string('error');
+                } else if (!empty($CFG->grade_hiddenasdate) and $grade_grade->get_datesubmitted() and !$this->canviewhidden and $grade_grade->is_hidden()
+                       and !$grade_grade->grade_item->is_category_item() and !$grade_grade->grade_item->is_course_item()) {
-                // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records
+                    // the problem here is that we do not have the time when grade value was modified, 'timemodified' is general modification date for grade_grades records
-                $data[] = '<span class="'.$hidden.$class.' datesubmitted">'.$excluded.get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort'))).'</span>';
+                    $class .= ' datesubmitted';
+                    $data['grade']['class'] = $class;
+                    $data['grade']['content'] = get_string('submittedon', 'grades', userdate($grade_grade->get_datesubmitted(), get_string('strftimedatetimeshort')));
 
+                } elseif ($grade_grade->is_hidden()) {
+                    $data['grade']['class'] = $class.' hidden';
+                    $data['grade']['content'] = '-';
-            } else {
+                } else {
-                $data[] = '<span class="'.$hidden.$class.'">'.$excluded.grade_format_gradevalue($gradeval, $grade_item, true).'</span>';
+                    $data['grade']['class'] = $class;
+                    $data['grade']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true);
-            }
+                }
 
-            /// prints percentage
+                /// Percentage
-            if ($this->showpercentage) {
+                if ($this->showpercentage) {
-                if ($grade_item->needsupdate) {
-                    $data[] = '<span class="'.$hidden.$class.'gradingerror">'.get_string('error').'</span>';
-
+                    if ($grade_grade->grade_item->needsupdate) {
+                        $data['percentage']['class'] = $class.' gradingerror';
+                        $data['percentage']['content'] = get_string('error');
+                    } elseif ($grade_grade->is_hidden()) {
+                        $data['percentage']['class'] = $class.' hidden';
+                        $data['percentage']['content'] = '-';
-                } else {
+                    } else {
-                    $data[] = '<span class="'.$hidden.$class.'">'.grade_format_gradevalue($gradeval, $grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE).'</span>';
+                        $data['percentage']['class'] = $class;
+                        $data['percentage']['content'] = grade_format_gradevalue($gradeval, $grade_grade->grade_item, true, GRADE_DISPLAY_TYPE_PERCENTAGE);
-                }
-            }
+                    }
+                }
 
-            /// prints rank
+                /// Rank
-            if ($this->showrank) {
-                // TODO: this is broken if hidden grades present!!
+                if ($this->showrank) {
+                    // TODO: this is broken if hidden grades present!!
-                if ($grade_item->needsupdate) {
-                    $data[] = '<span class="'.$hidden.$class.'gradingerror">'.get_string('error').'</span>';
-
+                    if ($grade_grade->grade_item->needsupdate) {
+                        $data['rank']['class'] = $class.' gradingerror';
+                        $data['rank']['content'] = get_string('error');
+                    } elseif ($grade_grade->is_hidden()) {
+                        $data['rank']['class'] = $class.' hidden';
+                        $data['rank']['content'] = '-';
-                } else if (is_null($gradeval)) {
-                    // no grade, no rank
+                    } else if (is_null($gradeval)) {
+                        // no grade, no rank
-                    $data[] = '<span class="'.$hidden.$class.'">-</span>';;
+                        $data['rank']['class'] = $class;
+                        $data['rank']['content'] = '-';
 
-                } else {
-                    /// find the number of users with a higher grade
-                    $sql = "SELECT COUNT(DISTINCT(userid))
-                              FROM {$CFG->prefix}grade_grades
+                    } else {
+                        /// find the number of users with a higher grade
+                        $sql = "SELECT COUNT(DISTINCT(userid))
+                                  FROM {$CFG->prefix}grade_grades
-                             WHERE finalgrade > {$grade_grade->finalgrade}
-                                   AND itemid = {$grade_item->id}";
+                                 WHERE finalgrade > {$gradeval}
+                                       AND itemid = {$grade_grade->grade_item->id}";
-                    $rank = count_records_sql($sql) + 1;
+                        $rank = count_records_sql($sql) + 1;
 
-                    $data[] = '<span class="'.$hidden.$class.'">'."$rank/$numusers".'</span>';
+                        $data['rank']['class'] = $class;
+                        $data['rank']['content'] = "$rank/".$this->get_numusers(false); // total course users
-                }
-            }
+                    }
+                }
 
-            /// prints feedback
-            if (empty($grade_grade->feedback) or (!$canviewhidden and $grade_grade->is_hidden())) {
-                $data[] = '<div class="'.$hidden.'feedbacktext">&nbsp;</div>';
+                /// Feedback
+                if (empty($grade_grade->feedback) or (!$this->canviewhidden and $grade_grade->is_hidden())) {
+                    $data['feedback']['class'] = $class.' feedbacktext';
+                    $data['feedback']['content'] = '&nbsp;';
 
-            } else {
+                } else {
-                $data[] = '<div class="'.$hidden.'feedbacktext">'.format_text($grade_grade->feedback, $grade_grade->feedbackformat).'</div>';
+                    $data['feedback']['class'] = $class.' feedbacktext';
+                    $data['feedback']['content'] = format_text($grade_grade->feedback, $grade_grade->feedbackformat);
-            }
+                }
 
-            $this->table->add_data($data);
+                /// Range
+                if ($this->showrange) {
+                    $data['range']['class'] = $class;
+                    $data['range']['content'] = $grade_grade->grade_item->get_formatted_range();
+                }
+            }
         }
 
-        return true;
+        /// Category
+        if ($type == 'category') {
+            $data['leader']['class'] = $class.' '.$alter."d$depth b1t b2b b1l";
+            $data['leader']['rowspan'] = $element['rowspan'];
+
+            if ($this->switch) { // alter style based on whether aggregation is first or last
+               $data['itemname']['class'] = $class.' '.$alter."d$depth b1b b1t";
+            } else {
+               $data['itemname']['class'] = $class.' '.$alter."d$depth b2t";
+            }
+            $data['itemname']['colspan'] = ($this->maxdepth - $depth + count($this->tablecolumns) - 1);
+            $data['itemname']['content'] = $fullname;
+        }
+
+        /// Add this row to the overall system
+        $this->tabledata[] = $data;
+
+        /// Recursively iterate through all child elements
+        if (isset($element['children'])) {
+            foreach ($element['children'] as $key=>$child) {
+                $this->fill_table_recursive($element['children'][$key]);
+            }
+        }
     }
 
     /**
@@ -309,9 +369,46 @@
      * @return string
      */
     function print_table($return=false) {
-        ob_start();
-        $this->table->print_html();
-        $html = ob_get_clean();
+         $maxspan = $this->maxdepth;
+
+        /// Build table structure
+        $html = "
+            <table cellspacing='0' cellpadding='0' class='boxaligncenter generaltable' id='user-grade'>
+            <thead>
+                <tr>
+                    <th class=\"header\" colspan='$maxspan'>".$this->tableheaders[0]."</th>\n";
+
+        for ($i = 1; $i < count($this->tableheaders); $i++) {
+            $html .= "<th class=\"header\">".$this->tableheaders[$i]."</th>\n";
+        }
+
+        $html .= "
+                </tr>
+            </thead>
+            <tbody>\n";
+
+        /// Print out the table data
+        for ($i = 0; $i < count($this->tabledata); $i++) {
+            $html .= "<tr>\n";
+            if (isset($this->tabledata[$i]['leader'])) {
+                $rowspan = $this->tabledata[$i]['leader']['rowspan'];
+                $class = $this->tabledata[$i]['leader']['class'];
+                $html .= "<td class='$class' rowspan='$rowspan'></td>\n";
+            }
+            for ($j = 0; $j < count($this->tablecolumns); $j++) {
+                $name = $this->tablecolumns[$j];
+                $class = (isset($this->tabledata[$i][$name]['class'])) ? $this->tabledata[$i][$name]['class'] : '';
+                $colspan = (isset($this->tabledata[$i][$name]['colspan'])) ? "colspan='".$this->tabledata[$i][$name]['colspan']."'" : '';
+                $content = (isset($this->tabledata[$i][$name]['content'])) ? $this->tabledata[$i][$name]['content'] : null;
+                if (isset($content)) {
+                    $html .= "<td class='$class' $colspan>$content</td>\n";
+                }
+            }
+            $html .= "</tr>\n";
+        }
+
+        $html .= "</tbody></table>";
+
         if ($return) {
             return $html;
         } else {
@@ -345,9 +442,9 @@
     $mform->setHelpButton('report_user_showrank', array('showrank', get_string('showrank', 'grades'), 'grade'));
 
     if (empty($CFG->grade_report_user_showpercentage)) {
+        $options[-1] = get_string('defaultprev', 'grades', $options[0]);
+    } else {
         $options[-1] = get_string('defaultprev', 'grades', $options[1]);
-    } else {
-        $options[-1] = get_string('defaultprev', 'grades', $options[0]);
     }
 
     $mform->addElement('select', 'report_user_showpercentage', get_string('showpercentage', 'grades'), $options);
Index: grade/report/user/styles.php
=========================================================
--- grade/report/user/styles.php	(revision 1.1.2.4)
+++ grade/report/user/styles.php	Thu Apr 16 09:31:42 CEST 2009
@@ -1,4 +1,3 @@
-
 .grade-report-user table#user-grade .datesubmitted {
   font-size: 0.7em;
 }
@@ -23,3 +22,100 @@
 .grade-report-user table#user-grade .hidden a {
   color:#aaaaaa;
 }
+
+table#user-grade {
+   border: 1px solid black;
+   margin: auto;
+   padding: 0.25em;
+   font-size: 0.8em;
+}
+table#user-grade td {
+   margin: 1px;
+   padding: 0.25em;
+}
+table#user-grade thead {
+   border-bottom: 3px double black;
+}
+table#user-grade thead th {
+   padding: 0.25em 0.75em 0.25em 0.75em;
+}
+
+table#user-grade td.oddd1 {
+   background-color: #f3dfd0;
+}
+table#user-grade td.oddd2 {
+   background-color: #d0dbf3;
+}
+table#user-grade td.oddd3 {
+   background-color: #d0f3d6;
+}
+table#user-grade td.oddd4 {
+   background-color: #f0f0aa;
+}
+
+table#user-grade td.evend2 {
+   background-color: #b0bbd3;
+}
+table#user-grade td.evend3 {
+   background-color: #b0dfb6;
+}
+table#user-grade td.evend4 {
+   background-color: #cac8be;
+}
+
+table#user-grade td.b1t {
+   border-top: 1px solid black;
+}
+table#user-grade td.b1r {
+   border-right: 1px solid black;
+}
+table#user-grade td.b1b {
+   border-bottom: 1px solid black;
+}
+table#user-grade td.b1l {
+   border-left: 1px solid black;
+}
+
+table#user-grade td.b2t {
+   border-top: 2px solid black;
+}
+table#user-grade td.b2r {
+   border-right: 2px solid black;
+}
+table#user-grade td.b2b {
+   border-bottom: 2px solid black;
+}
+table#user-grade td.b2l {
+   border-left: 2px solid black;
+}
+
+table#user-grade td.baggt,
+table#user-grade td.baggb {
+   font-style: italic;
+   font-weight: bold;
+}
+table#user-grade td.baggt {
+   border-top: 3px double black;
+}
+table#user-grade td.baggb {
+   border-bottom: 3px double black;
+}
+
+table#user-grade td.item {
+   border-left: 1px solid gray;
+   border-right: 1px solid gray;
+}
+table#user-grade td.excluded {
+   /*text-decoration: line-through;*/
+   background-color: #666;
+}
+table#user-grade td.hidden {
+   color: #aaa;
+}
+
+table#user-grade td {
+   min-width: 0.5em;
+   vertical-align: top;
+}
+
+
Index: lang/en_utf8/gradereport_grader.php
=========================================================
--- lang/en_utf8/gradereport_grader.php	(revision 1.2)
+++ lang/en_utf8/gradereport_grader.php	Thu Apr 16 09:31:42 CEST 2009
@@ -1,7 +1,8 @@
-<?PHP // $Id: gradereport_grader.php,v 1.2 2007/07/23 08:13:59 moodler Exp $ 
+<?PHP // $Id: gradereport_grader.php,v 1.2 2007/07/23 08:13:59 moodler Exp $
 
 $string['modulename'] = 'Grader report';
 $string['grader:manage'] = 'Manage the grader report';
 $string['grader:view'] = 'View the grader report';
+$string['preferences'] = 'Grader report preferences';
 
 ?>
Index: lang/en_utf8/grades.php
=========================================================
--- lang/en_utf8/grades.php	(revision 1.111.2.60)
+++ lang/en_utf8/grades.php	Thu Apr 16 09:31:42 CEST 2009
@@ -11,6 +11,8 @@
 $string['addidnumbers'] = 'Add id numbers';
 $string['additem'] = 'Add grade item';
 $string['addoutcomeitem'] = 'Add outcome item';
+$string['addoutcome'] = 'Add an outcome';
+$string['addscale'] = 'Add a scale';
 $string['aggregateextracreditmean'] = 'Mean of grades (with extra credits)';
 $string['aggregatemean'] = 'Mean of grades';
 $string['aggregatemedian'] = 'Median of grades';
@@ -37,6 +39,8 @@
 $string['aggregationcoefweighthelp'] = 'Weight applied to all grades in this grade item during aggregation with other grade items.';
 $string['aggregationhelp'] = 'Strategy used to aggregate grades across all students in a course.';
 $string['aggregationposition'] = 'Aggregation position';
+$string['aggregationsvisible'] = 'Available aggregation types';
+$string['aggregationsvisiblehelp'] = 'Select all aggregation types that should be available. Hold down the Ctrl key to select multiple items.';
 $string['aggregationview'] = 'Aggregation view';
 $string['allgrades'] = 'All grades by category';
 $string['allstudents'] = 'All students';
@@ -48,9 +52,12 @@
 $string['averagesdisplaytype'] = 'Column averages display type';
 $string['backupwithoutgradebook'] = 'Backup does not contain Gradebook configuration';
 $string['badgrade'] = 'Supplied grade is invalid';
+$string['badlyformattedscale'] = 'Please enter a comma-separated list of values (at least two values required).';
 $string['baduser'] = 'Supplied user is invalid';
 $string['bonuspoints'] = 'Bonus points';
+$string['combo'] = 'Tabs and Dropdown menu';
 $string['bulkcheckboxes'] = 'Bulk checkboxes';
+$string['calculatedgrade'] = 'Calculated grade';
 $string['calculation'] = 'Calculation';
 $string['calculationadd'] = 'Add calculation';
 $string['calculationedit'] = 'Edit calculation';
@@ -63,6 +70,7 @@
 $string['categoryedit'] = 'Edit category';
 $string['categoryname'] = 'Category name';
 $string['categorytotal'] = 'Category total';
+$string['categorytotalfull'] = '$a->category total';
 $string['changereportdefaults'] = 'Change report defaults';
 $string['changedefaults'] = 'Change defaults';
 $string['choosecategory'] = 'Select category';
@@ -77,6 +85,7 @@
 $string['configdecimalpoints'] = 'Specifies the number of decimal points to display for each grade. This setting may be overridden per grading item.';
 $string['configenableajax'] = 'Adds a layer of AJAX functionality to the grader report, simplifying and speeding up common operations. Depends on Javascript being switched on at the user\'s browser level.';
 $string['configenableoutcomes'] = 'Support for Outcomes (also known as Competencies, Goals, Standards or Criteria) means that we can grade things using one or more scales that are tied to outcome statements. Enabling outcomes makes such special grading possible throughout the site.';
+$string['configfixedstudents'] = 'Allows grades to scroll horizontally without losing sight of the students column, by making it static.';
 $string['configgradeboundary'] = 'A percentage boundary over which grades will be assigned a grade letter (if the Letter grade display type is used). ';
 $string['configgradedisplaytype'] = 'Specifies how to display grades in the grader and user reports. Grades may be shown as actual grades, as percentages (in reference to the minimum and maximum grades) or as letters.';
 $string['configgradeexportdisplaytype'] = 'Grades can be shown as real grades, as percentages (in reference to the minimum and maximum grades) or as letters (A, B, C etc..) during export. This can be overridden during export.';
@@ -111,17 +120,21 @@
 $string['configshowuserimage'] = 'Whether to show the user\'s profile image next to the name in the grader report.';
 $string['configstudentsperpage'] = 'The number of students to display per page in the grader report.';
 $string['configstudentsperpagedefault'] = 'The number of students to display per page in the grader report. Leave this field empty to use the site default (currently $a).';
+$string['configunlimitedgrades'] = 'By default grades are limited by the maximum and minimum values of the grade item. Enabling this setting removes this limit, and allows grades of over 100%% to be entered directly in the gradebook. It is recommended that this setting is enabled at an off-peak time, as all grades will be recalculated, which may result in a high server load.';
 $string['contract'] = 'Contract Category';
 $string['controls'] = 'Controls';
 $string['coursegradecategory'] = 'Course grade category';
 $string['coursegradedisplaytype'] = 'Course grade display type';
 $string['coursegradedisplayupdated'] = 'The course grade display type has been updated.';
 $string['coursename'] = 'Course name';
+$string['coursescales'] = 'Course scales';
 $string['coursesettings'] = 'Course settings';
+$string['coursesettingsexplanation'] = 'Course settings determine how the gradebook appears for all participants in the course.';
 $string['coursetotal'] = 'Course total';
 $string['createcategory'] = 'Create category';
 $string['createcategoryerror'] = 'Could not create a new category';
 $string['creatinggradebooksettings'] = 'Creating gradebook settings';
+$string['currentparentaggregation'] = 'Current parent aggregation';
 $string['csv'] = 'CSV';
 $string['curveto'] = 'Curve to';
 $string['decimalpoints'] = 'Overall decimal points';
@@ -133,6 +146,7 @@
 $string['displaypercent'] = 'Display percents';
 $string['displaypoints'] = 'Display points';
 $string['displayweighted'] = 'Display weighted grades';
+$string['dropdown'] = 'Dropdown menu';
 $string['droplow'] = 'Drop the lowest';
 $string['droplowhelp'] = 'If set, this option will drop the X lowest grades, X being the selected value for this option.';
 $string['dropped'] = 'Dropped';
@@ -144,7 +158,10 @@
 $string['editcalculationverbose'] = 'Edit calculation for $a->category$a->itemmodule $a->itemname';
 $string['editfeedback'] = 'Edit feedback';
 $string['editgrade'] = 'Edit grade';
+$string['editgradeletters'] = 'Edit grade letters';
+$string['editoutcome'] = 'Edit outcome';
 $string['editoutcomes'] = 'Edit outcomes';
+$string['editscale'] = 'Edit scale';
 $string['edittree'] = 'Categories and items';
 $string['editverbose'] = 'Edit $a->category$a->itemmodule $a->itemname';
 $string['enableajax'] = 'Enable AJAX';
@@ -177,12 +194,14 @@
 $string['feedbacksaved'] = 'Feedback saved';
 $string['finalgrade'] = 'Final grade';
 $string['finalgradehelp'] = 'The final grade (cached) after all calculations are performed.';
+$string['fixedstudents'] = 'Static students column';
 $string['forceoff'] = 'Force: Off';
 $string['forceon'] = 'Force: On';
 $string['forelementtypes'] = ' for the selected $a';
 $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';
@@ -200,6 +219,7 @@
 $string['gradeexceptionshelp'] = 'Grade exceptions Help';
 $string['gradeexportdisplaytype'] = 'Grade export display type';
 $string['gradeexportdecimalpoints'] = 'Grade export decimal points';
+$string['gradeforstudent'] = '$a->student<br />$a->item$a->feedback';
 $string['gradehelp'] = 'Grade Help';
 $string['gradehistorylifetime'] = 'Grade history lifetime';
 $string['gradeitem'] = 'Grade item';
@@ -286,6 +306,7 @@
 $string['incorrectcourseid'] = 'Course ID was incorrect';
 $string['incorrectminmax'] = 'The minimum must be lower than the maximum';
 $string['inherit'] = 'Inherit';
+$string['intersectioninfo'] = 'Student/Grade info';
 $string['item'] = 'Item';
 $string['iteminfo'] = 'Item info';
 $string['iteminfohelp'] = 'A space for entering information about the item. Text entered does not appear anywhere else.';
@@ -305,6 +326,7 @@
 $string['letters'] = 'Letters';
 $string['linkedactivity'] = 'Linked activity';
 $string['linkedactivityhelp'] = 'Specifies an optional activity to which this outcome item is linked. This is used to measure student performance on criteria not assessed by the activity grade.';
+$string['linktoactivity'] = 'Link to $a->name activity';
 $string['lock'] = 'Lock';
 $string['locked'] = 'Locked';
 $string['locktime'] = 'Lock after';
@@ -326,12 +348,17 @@
 $string['missingscale'] = 'Scale must be selected';
 $string['mode'] = 'Mode';
 $string['morethanmax'] = 'The grade entered for $a->itemname for $a->username is more than the maximum allowed';
+$string['moveselectedto'] = 'Move selected items to:';
 $string['movingelement'] = 'Moving $a';
 $string['multfactor'] = 'Multiplicator';
 $string['multfactorhelp'] = 'Factor by which all grades for this grade item will be multiplied.';
+$string['mypreferences'] = 'My preferences';
 $string['myreportpreferences'] = 'My report preferences';
+$string['navmethod'] = 'Navigation method';
 $string['neverdeletehistory'] = 'Never delete history';
 $string['newcategory'] = 'New category';
+$string['newitem'] = 'New grade item';
+$string['newoutcomeitem'] = 'New outcome item';
 $string['newuserkey'] = 'New user key';
 $string['no'] = 'No';
 $string['nocategories'] = 'Grade categories could not be added or found for this course';
@@ -473,6 +500,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,7 +515,10 @@
 $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['tabs'] = 'Tabs';
 $string['topcategory'] = 'Super category';
 $string['total'] = 'Total';
 $string['totalweight100'] = 'The total weight is equal to 100';
@@ -502,6 +533,7 @@
 $string['uncategorised'] = 'Uncategorised';
 $string['unchangedgrade'] = 'Grade unchanged';
 $string['unenrolledusersinimport'] = 'This import included the following grades for users not currently enrolled in this course: $a';
+$string['unlimitedgrades'] = 'Unlimited grades';
 $string['unlock'] = 'Unlock';
 $string['unlockverbose'] = 'Unlock $a->category$a->itemmodule $a->itemname';
 $string['unused'] = 'Unused';
@@ -521,11 +553,15 @@
 $string['viewbygroup'] = 'Group';
 $string['viewgrades'] = 'View grades';
 $string['weight'] = 'weight';
+$string['weightuc'] = 'Weight';
 $string['weightcourse'] = 'Use weighted grades for course';
 $string['weightedascending'] = 'Sort by weighted percent ascending';
 $string['weighteddescending'] = 'Sort by weighted percent descending';
 $string['weightedpct'] = 'weighted %%';
 $string['weightedpctcontribution'] = 'weighted %% contribution';
+$string['weightorextracredit'] = 'Weight or extra credit';
+$string['weights'] = 'Weights';
+$string['weightsedit'] = 'Edit weights and extra credits';
 $string['writinggradebookinfo'] = 'Writing gradebook settings';
 $string['xml'] = 'XML';
 $string['yes'] = 'Yes';
Index: lib/adminlib.php
=========================================================
--- lib/adminlib.php	(revision 1.153.2.72)
+++ lib/adminlib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -3495,6 +3495,35 @@
     }
 }
 
+class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
+    function admin_setting_special_gradelimiting() {
+        parent::admin_setting_configcheckbox('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
+                                                  get_string('configunlimitedgrades', 'grades'), '0', '1', '0');
+    }
+
+    function regrade_all() {
+        global $CFG;
+        require_once("$CFG->libdir/gradelib.php");
+        grade_force_site_regrading();
+    }
+
+    function write_setting($data) {
+        $previous = $this->get_setting();
+
+        if ($previous === null) {
+            if ($data) {
+                $this->regrade_all();
+            }
+        } else {
+            if ($data != $previous) {
+                $this->regrade_all();
+            }
+        }
+        return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
+    }
+
+}
+
 /**
  * Primary grade export plugin - has state tracking.
  */
Index: lib/grade/constants.php
=========================================================
--- lib/grade/constants.php	(revision 1.7.2.3)
+++ lib/grade/constants.php	Thu Apr 16 09:31:42 CEST 2009
@@ -85,4 +85,8 @@
 
 define('GRADE_REPORT_MEAN_ALL', 0);
 define('GRADE_REPORT_MEAN_GRADED', 1);
+
+define('GRADE_NAVMETHOD_DROPDOWN', 0);
+define('GRADE_NAVMETHOD_TABS', 1);
+define('GRADE_NAVMETHOD_COMBO', 2);
 ?>
Index: lib/grade/grade_category.php
=========================================================
--- lib/grade/grade_category.php	(revision 1.96.2.23)
+++ lib/grade/grade_category.php	Thu Apr 16 09:31:42 CEST 2009
@@ -144,6 +144,11 @@
     var $forceable = array('aggregation', 'keephigh', 'droplow', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats');
 
     /**
+     * String representing the aggregation coefficient. Variable is used as cache.
+     */
+    var $coefstring = null;
+
+    /**
      * Builds this category's path string based on its parents (if any) and its own id number.
      * This is typically done just before inserting this object in the DB for the first time,
      * or when a new parent is added or changed. It is a recursive function: once the calling
@@ -567,11 +572,7 @@
         // recalculate the grade back to requested range
         $finalgrade = grade_grade::standardise_score($agg_grade, 0, 1, $this->grade_item->grademin, $this->grade_item->grademax);
 
-        if (is_null($finalgrade)) {
-            $grade->finalgrade = null;
-        } else {
-            $grade->finalgrade = (float)bounded_number($this->grade_item->grademin, $finalgrade, $this->grade_item->grademax);
-        }
+        $grade->finalgrade = $this->grade_item->bounded_grade($finalgrade);
 
         // update in db if changed
         if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
@@ -769,7 +770,7 @@
         $this->apply_limit_rules($grade_values);
 
         $sum = array_sum($grade_values);
-        $grade->finalgrade = bounded_number($this->grade_item->grademin, $sum, $this->grade_item->grademax);
+        $grade->finalgrade = $this->grade_item->bounded_grade($sum);
 
         // update in db if changed
         if (grade_floats_different($grade->finalgrade, $oldfinalgrade)) {
@@ -811,6 +812,52 @@
     }
 
     /**
+     * Recursive function to find which weight/extra credit field to use in the grade item form. Inherits from a parent category
+     * if that category has aggregatesubcats set to true.
+     * @param string $coefstring
+     * @return string $coefstring
+     */
+    function get_coefstring($first=true) {
+        if (!is_null($this->coefstring)) {
+            return $this->coefstring;
+        }
+
+        $overriding_coefstring = null;
+
+        // Stop recursing upwards if this category aggregates subcats or has no parent
+        if (!$first && !$this->aggregatesubcats) {
+            if ($parent_category = $this->get_parent_category()) {
+                return $parent_category->get_coefstring(false);
+            } else {
+                return null;
+            }
+        } elseif ($first) {
+            if (!$this->aggregatesubcats) {
+                if ($parent_category = $this->get_parent_category()) {
+                    $overriding_coefstring = $parent_category->get_coefstring(false);
+                }
+            }
+        }
+
+        // If an overriding coefstring has trickled down from one of the parent categories, return it. Otherwise, return self.
+        if (!is_null($overriding_coefstring)) {
+            return $overriding_coefstring;
+        }
+
+        // No parent category is overriding this category's aggregation, return its string
+        if ($this->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
+            $this->coefstring = 'aggregationcoefweight';
+        } else if ($this->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
+            $this->coefstring = 'aggregationcoefextra';
+        } else if ($this->aggregation == GRADE_AGGREGATE_SUM) {
+            $this->coefstring = 'aggregationcoefextrasum';
+        } else {
+            $this->coefstring = 'aggregationcoef';
+        }
+        return $this->coefstring;
+    }
+
+    /**
      * Returns tree with all grade_items and categories as elements
      * @static
      * @param int $courseid
Index: lib/grade/grade_grade.php
=========================================================
--- lib/grade/grade_grade.php	(revision 1.14.2.18)
+++ lib/grade/grade_grade.php	Thu Apr 16 09:31:42 CEST 2009
@@ -436,7 +436,7 @@
             return $this->hidden == 1 or ($this->hidden != 0 and $this->hidden > time());
         } else {
             return $this->hidden == 1 or ($this->hidden != 0 and $this->hidden > time()) or $this->grade_item->is_hidden();
-        } 
+        }
     }
 
     /**
@@ -622,7 +622,7 @@
                             foreach ($dependson[$do] as $itemid) {
                                 if (array_key_exists($itemid, $altered)) {
                                     $values[$itemid] = $altered[$itemid];
-                                } else {
+                                } elseif (!empty($values[$itemid])) {
                                     $values[$itemid] = $grade_grades[$itemid]->finalgrade;
                                 }
                             }
@@ -667,9 +667,7 @@
                             // recalculate the rawgrade back to requested range
                             $finalgrade = grade_grade::standardise_score($agg_grade, 0, 1, $grade_items[$do]->grademin, $grade_items[$do]->grademax);
 
-                            if (!is_null($finalgrade)) {
-                                $finalgrade = bounded_number($grade_items[$do]->grademin, $finalgrade, $grade_items[$do]->grademax);
-                            }
+                            $finalgrade = $grade_items[$do]->bounded_grade($finalgrade);
 
                             $altered[$do] = $finalgrade;
                             unset($todo[$key]);
@@ -716,8 +714,8 @@
     }
 
     function insert($source=null) {
-        // TODO: dategraded hack - do not update times, they are used for submission and grading  
-        //$this->timecreated = $this->timemodified = time(); 
+        // TODO: dategraded hack - do not update times, they are used for submission and grading
+        //$this->timecreated = $this->timemodified = time();
         return parent::insert($source);
     }
 
Index: lib/grade/grade_item.php
=========================================================
--- lib/grade/grade_item.php	(revision 1.130.2.30)
+++ lib/grade/grade_item.php	Thu Apr 16 09:31:42 CEST 2009
@@ -735,7 +735,7 @@
             $rawgrade *= $this->multfactor;
             $rawgrade += $this->plusfactor;
 
-            return bounded_number($this->grademin, $rawgrade, $this->grademax);
+            return $this->bounded_grade($rawgrade);
 
         } else if ($this->gradetype == GRADE_TYPE_SCALE) { // Dealing with a scale value
             if (empty($this->scale)) {
@@ -756,7 +756,7 @@
                 $rawgrade = grade_grade::standardise_score($rawgrade, $rawmin, $rawmax, $this->grademin, $this->grademax);
             }
 
-            return (int)bounded_number(0, round($rawgrade+0.00001), $this->grademax);
+            return $this->bounded_grade($rawgrade);
 
 
         } else if ($this->gradetype == GRADE_TYPE_TEXT or $this->gradetype == GRADE_TYPE_NONE) { // no value
@@ -1163,9 +1163,10 @@
     /**
      * Returns the most descriptive field for this object. This is a standard method used
      * when we do not know the exact type of an object.
+     * @param boolean $fulltotal: if the item is a category total, returns $categoryname."total" instead of "Category total" or "Course total"
      * @return string name
      */
-    function get_name() {
+    function get_name($fulltotal=false) {
         if (!empty($this->itemname)) {
             // MDL-10557
             return format_string($this->itemname);
@@ -1174,7 +1175,14 @@
             return get_string('coursetotal', 'grades');
 
         } else if ($this->is_category_item()) {
+            if ($fulltotal) {
+                $category = $this->get_parent_category();
+                $a = new stdClass();
+                $a->category = $category->get_name();
+                return get_string('categorytotalfull', 'grades', $a);
+            } else {
-            return get_string('categorytotal', 'grades');
+                return get_string('categorytotal', 'grades');
+            }
 
         } else {
             return get_string('grade');
@@ -1210,6 +1218,43 @@
     }
 
     /**
+     * Makes sure value is a valid grade value.
+     * @param float $gradevalue
+     * @return mixed float or int fixed grade value
+     */
+    function bounded_grade($gradevalue) {
+        global $CFG;
+
+        if (is_null($gradevalue)) {
+            return null;
+        }
+
+        if ($this->gradetype == GRADE_TYPE_SCALE) {
+            // no >100% grades hack for scale grades!
+            // 1.5 is rounded to 2 ;-)
+            return (int)bounded_number($this->grademin, round($gradevalue+0.00001), $this->grademax);
+        }
+
+        $grademax = $this->grademax;
+
+        // NOTE: if you change this value you must manually reset the needsupdate flag in all grade items
+        $maxcoef = isset($CFG->gradeoverhundredprocentmax) ? $CFG->gradeoverhundredprocentmax : 10; // 1000% max by default
+
+        if (!empty($CFG->unlimitedgrades)) {
+            // NOTE: if you change this value you must manually reset the needsupdate flag in all grade items
+            $grademax = $grademax * $maxcoef;
+        } else if ($this->is_category_item() or $this->is_course_item()) {
+            $category = $this->load_item_category();
+            if ($category->aggregation >= 100) {
+                // grade >100% hack
+                $grademax = $grademax * $maxcoef;
+            }
+        }
+
+        return (float)bounded_number($this->grademin, $gradevalue, $grademax);
+    }
+
+    /**
      * Finds out on which other items does this depend directly when doing calculation or category agregation
      * @param bool $reset_cache
      * @return array of grade_item ids this one depends on
@@ -1388,11 +1433,7 @@
                 $grade->overridden = 0;
             }
 
-            if (is_null($finalgrade)) {
-                $grade->finalgrade = null;
-            } else {
-                $grade->finalgrade = bounded_number($this->grademin, $finalgrade, $this->grademax);
-            }
+            $grade->finalgrade = $this->bounded_grade($finalgrade);
         }
 
         // do we have comment from teacher?
@@ -1735,11 +1776,7 @@
 
         } else {
             // normalize
-            $result = bounded_number($this->grademin, $result, $this->grademax);
-            if ($this->gradetype == GRADE_TYPE_SCALE) {
-                $result = round($result+0.00001); // round scales upwards
-            }
-            $grade->finalgrade = $result;
+            $grade->finalgrade = $this->bounded_grade($result);
         }
 
         // update in db if changed
@@ -1908,5 +1945,21 @@
         return $grademin.'&ndash;'. $grademax;
     }
 
+    /**
+     * Queries parent categories recursively to find the aggregationcoef type that applies to this
+     * grade item.
+     */
+    function get_coefstring() {
+        $parent_category = $this->get_parent_category();
+        if ($this->is_category_item()) {
+            $parent_category = $parent_category->get_parent_category();
+        }
+
+        if ($parent_category->is_aggregationcoef_used()) {
+            return $parent_category->get_coefstring();
+        } else {
+            return false;
+        }
+    }
 }
 ?>
Index: lib/gradelib.php
=========================================================
--- lib/gradelib.php	(revision 1.120.2.27)
+++ lib/gradelib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -706,7 +706,7 @@
             return get_string('error');
         }
 
-        $value = (int)bounded_number($grade_item->grademin, $value, $grade_item->grademax);
+        $value = $grade_item->bounded_grade($value);
         return format_string($scale->scale_items[$value-1]);
 
     } else {
@@ -720,7 +720,7 @@
     if ($min == $max) {
         return '';
     }
-    $value = bounded_number($min, $value, $max);
+    $value = $grade_item->bounded_grade($value);
     $percentage = (($value-$min)*100)/($max-$min);
     return format_float($percentage, $decimals, $localized).' %';
 }
@@ -731,6 +731,10 @@
         return ''; // no letters??
     }
 
+    if (is_null($value)) {
+        return '-';
+    }
+
     $value = grade_grade::standardise_score($value, $grade_item->grademin, $grade_item->grademax, 0, 100);
     $value = bounded_number(0, $value, 100); // just in case
     foreach ($letters as $boundary => $letter) {
@@ -862,6 +866,15 @@
 }
 
 /**
+ * Forces regrading of all site grades - usualy when chanign site setings
+ */
+function grade_force_site_regrading() {
+    global $CFG;
+    $sql = "UPDATE {$CFG->prefix}grade_items SET needsupdate=1";
+    execute_sql($sql);
+}
+
+/**
  * Updates all final grades in course.
  *
  * @param int $courseid
@@ -1069,7 +1082,7 @@
 
     $fullmod = $CFG->dirroot.'/mod/'.$modinstance->modname;
     if (!file_exists($fullmod.'/lib.php')) {
-        debugging('missing lib.php file in module');
+        debugging('missing lib.php file in module ' . $modinstance->modname);
         return false;
     }
     include_once($fullmod.'/lib.php');
Index: lib/moodlelib.php
=========================================================
--- lib/moodlelib.php	(revision 1.960.2.126)
+++ lib/moodlelib.php	Thu Apr 16 09:31:42 CEST 2009
@@ -790,7 +790,7 @@
 
 /**
  * Use this funciton to get a list of users from a config setting of type admin_setting_users_with_capability.
- * @param string $value the value of the config setting. 
+ * @param string $value the value of the config setting.
  * @param string $capability the capability - must match the one passed to the admin_setting_users_with_capability constructor.
  * @return array of user objects.
  */
@@ -6327,6 +6327,39 @@
 }
 
 /**
+ * Returns one or several CSS class names that match the user's browser. These can be put
+ * in the body tag of the page to apply browser-specific rules without relying on CSS hacks
+ */
+function get_browser_version_classes() {
+    $classes = '';
+    if (check_browser_version("MSIE", "0")) {
+        $classes .= 'ie ';
+        if (check_browser_version("MSIE", 8)) {
+            $classes .= 'ie8 ';
+        } elseif (check_browser_version("MSIE", 7)) {
+            $classes .= 'ie7 ';
+        } elseif (check_browser_version("MSIE", 6)) {
+            $classes .= 'ie6 ';
+        }
+    } elseif (check_browser_version("Firefox", 0) || check_browser_version("Gecko", 0) || check_browser_version("Camino", 0)) {
+        $classes .= 'gecko ';
+
+        if (preg_match('/rv\:([1-2])\.([0-9])/', $_SERVER['HTTP_USER_AGENT'], $matches)) {
+            $classes .= "gecko{$matches[1]}{$matches[2]} ";
+        }
+
+    } elseif (check_browser_version("Safari", 0)) {
+        $classes .= 'safari ';
+
+    } elseif (check_browser_version("Opera", 0)) {
+        $classes .= 'opera ';
+
+    }
+
+    return $classes;
+}
+
+/**
  * This function makes the return value of ini_get consistent if you are
  * setting server directives through the .htaccess file in apache.
  * Current behavior for value set from php.ini On = 1, Off = [blank]
Index: lib/pear/HTML/QuickForm/select.php
=========================================================
--- lib/pear/HTML/QuickForm/select.php	(revision 1.1)
+++ lib/pear/HTML/QuickForm/select.php	Thu Apr 16 09:31:42 CEST 2009
@@ -31,7 +31,7 @@
  * @access       public
  */
 class HTML_QuickForm_select extends HTML_QuickForm_element {
-    
+
     // {{{ properties
 
     /**
@@ -42,10 +42,10 @@
      * @access    private
      */
     var $_options = array();
-    
+
     /**
      * Default values of the SELECT
-     * 
+     *
      * @var       string
      * @since     1.0
      * @access    private
@@ -54,10 +54,10 @@
 
     // }}}
     // {{{ constructor
-        
+
     /**
      * Class constructor
-     * 
+     *
      * @param     string    Select name attribute
      * @param     mixed     Label(s) for the select
      * @param     mixed     Data to be used to populate options
@@ -75,13 +75,13 @@
             $this->load($options);
         }
     } //end constructor
-    
+
     // }}}
     // {{{ apiVersion()
 
     /**
-     * Returns the current API version 
-     * 
+     * Returns the current API version
+     *
      * @since     1.0
      * @access    public
      * @return    double
@@ -96,7 +96,7 @@
 
     /**
      * Sets the default values of the select box
-     * 
+     *
      * @param     mixed    $values  Array or comma delimited string of selected values
      * @since     1.0
      * @access    public
@@ -113,13 +113,13 @@
             $this->_values = array($values);
         }
     } //end func setSelected
-    
+
     // }}}
     // {{{ getSelected()
 
     /**
      * Returns an array of the selected values
-     * 
+     *
      * @since     1.0
      * @access    public
      * @return    array of selected values
@@ -134,7 +134,7 @@
 
     /**
      * Sets the input field name
-     * 
+     *
      * @param     string    $name   Input field name attribute
      * @since     1.0
      * @access    public
@@ -144,13 +144,13 @@
     {
         $this->updateAttributes(array('name' => $name));
     } //end func setName
-    
+
     // }}}
     // {{{ getName()
 
     /**
      * Returns the element name
-     * 
+     *
      * @since     1.0
      * @access    public
      * @return    string
@@ -165,7 +165,7 @@
 
     /**
      * Returns the element name (possibly with brackets appended)
-     * 
+     *
      * @since     1.0
      * @access    public
      * @return    string
@@ -200,7 +200,7 @@
 
     /**
      * Returns an array of the selected values
-     * 
+     *
      * @since     1.0
      * @access    public
      * @return    array of selected values
@@ -215,7 +215,7 @@
 
     /**
      * Sets the select field size, only applies to 'multiple' selects
-     * 
+     *
      * @param     int    $size  Size of select  field
      * @since     1.0
      * @access    public
@@ -225,13 +225,13 @@
     {
         $this->updateAttributes(array('size' => $size));
     } //end func setSize
-    
+
     // }}}
     // {{{ getSize()
 
     /**
      * Returns the select field size
-     * 
+     *
      * @since     1.0
      * @access    public
      * @return    int
@@ -246,7 +246,7 @@
 
     /**
      * Sets the select mutiple attribute
-     * 
+     *
      * @param     bool    $multiple  Whether the select supports multi-selections
      * @since     1.2
      * @access    public
@@ -260,13 +260,13 @@
             $this->removeAttribute('multiple');
         }
     } //end func setMultiple
-    
+
     // }}}
     // {{{ getMultiple()
 
     /**
      * Returns the select mutiple attribute
-     * 
+     *
      * @since     1.2
      * @access    public
      * @return    bool    true if multiple select, false otherwise
@@ -284,7 +284,7 @@
      *
      * @param     string    $text       Display text for the OPTION
      * @param     string    $value      Value for the OPTION
-     * @param     mixed     $attributes Either a typical HTML attribute string 
+     * @param     mixed     $attributes Either a typical HTML attribute string
      *                                  or an associative array
      * @since     1.0
      * @access    public
@@ -309,13 +309,13 @@
         }
         $this->_options[] = array('text' => $text, 'attr' => $attributes);
     } // end func addOption
-    
+
     // }}}
     // {{{ loadArray()
 
     /**
      * Loads the options from an associative array
-     * 
+     *
      * @param     array    $arr     Associative array of options
      * @param     mixed    $values  (optional) Array or comma delimited string of selected values
      * @since     1.0
@@ -343,12 +343,12 @@
 
     /**
      * Loads the options from DB_result object
-     * 
+     *
      * If no column names are specified the first two columns of the result are
      * used as the text and value columns respectively
-     * @param     object    $result     DB_result object 
-     * @param     string    $textCol    (optional) Name of column to display as the OPTION text 
-     * @param     string    $valueCol   (optional) Name of column to use as the OPTION value 
+     * @param     object    $result     DB_result object
+     * @param     string    $textCol    (optional) Name of column to display as the OPTION text
+     * @param     string    $valueCol   (optional) Name of column to use as the OPTION value
      * @param     mixed     $values     (optional) Array or comma delimited string of selected values
      * @since     1.0
      * @access    public
@@ -373,17 +373,17 @@
         }
         return true;
     } // end func loadDbResult
-    
+
     // }}}
     // {{{ loadQuery()
 
     /**
      * Queries a database and loads the options from the results
      *
-     * @param     mixed     $conn       Either an existing DB connection or a valid dsn 
+     * @param     mixed     $conn       Either an existing DB connection or a valid dsn
      * @param     string    $sql        SQL query string
-     * @param     string    $textCol    (optional) Name of column to display as the OPTION text 
-     * @param     string    $valueCol   (optional) Name of column to use as the OPTION value 
+     * @param     string    $textCol    (optional) Name of column to display as the OPTION text
+     * @param     string    $valueCol   (optional) Name of column to use as the OPTION value
      * @param     mixed     $values     (optional) Array or comma delimited string of selected values
      * @since     1.1
      * @access    public
@@ -425,7 +425,7 @@
      * first are optional and only mean something depending on the type of the first argument.
      * If the first argument is an array then all arguments are passed in order to loadArray.
      * If the first argument is a db_result then all arguments are passed in order to loadDbResult.
-     * If the first argument is a string or a DB connection then all arguments are 
+     * If the first argument is a string or a DB connection then all arguments are
      * passed in order to loadQuery.
      * @param     mixed     $options     Options source currently supports assoc array or DB_result
      * @param     mixed     $param1     (optional) See function detail
@@ -451,7 +451,7 @@
                 break;
         }
     } // end func load
-    
+
     // }}}
     // {{{ toHtml()
 
@@ -495,13 +495,13 @@
             return $strHtml . $tabs . '</select>';
         }
     } //end func toHtml
-    
+
     // }}}
     // {{{ getFrozenHtml()
 
     /**
      * Returns the value of field without HTML tags
-     * 
+     *
      * @since     1.0
      * @access    public
      * @return    string
@@ -574,7 +574,7 @@
             return $this->_prepareValue($cleanValue, $assoc);
         }
     }
-    
+
     // }}}
     // {{{ onQuickFormEvent()
 
Index: theme/standard/styles_fonts.css
=========================================================
--- theme/standard/styles_fonts.css	(revision 1.140.2.17)
+++ theme/standard/styles_fonts.css	Thu Apr 16 09:31:53 CEST 2009
@@ -296,11 +296,11 @@
     font-style: normal;
 }
 
-.plugincompattable { 
+.plugincompattable {
     text-align: left;
 }
 
-.plugincheckwrapper { 
+.plugincheckwrapper {
     text-align: center;
 }
 
@@ -586,6 +586,9 @@
     font-style: normal;
 }
 
+#choosepluginreport_jump option.optionheader {
+    font-weight: bold;
+}
 /***
  *** Login
  ***/
Index: theme/standard/styles_layout.css
=========================================================
--- theme/standard/styles_layout.css	(revision 1.516.2.76)
+++ theme/standard/styles_layout.css	Thu Apr 16 09:31:53 CEST 2009
@@ -2209,7 +2209,6 @@
   display: inline;
   padding: 5px;
 }
-
 /* outcomes edit */
 
 .grade-edit-outcome .buttons {
@@ -2302,6 +2301,14 @@
     margin-top: 7px;
 }
 
+#choosepluginreport_jump option {
+    margin-left: 20px;
+}
+
+#choosepluginreport_jump option.optionheader {
+    margin-left: 0px;
+}
+
 /***
  *** Login
  ***/
