<?php // $Id: lib.php,v 1.65.2.5 2007/06/26 11:50:19 tjhunt Exp $

if (!defined('MOODLE_INTERNAL')) {
            die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
}

require_once($CFG->dirroot.'/course/lib.php');

define('UNCATEGORISED', 'uncategorised');

global $GRADEPREFS, $GRADEPREFSDEFAULTS;    // This variables are going to be global... :-/

$GRADEPREFS = array('use_advanced',                // Only add new preferences to the end of this array!
                    'use_weighted_for_letter',     // as the order counts and will affect backward compatibility
                    'display_weighted',
                    'display_points',
                    'display_percent',
                    'display_letters',
                    'reprint_headers',
                    'show_hidden',
                    );


$GRADEPREFSDEFAULTS = array('use_advanced'                => 0,
                            'use_weighted_for_letter'     => 0,
                            'display_weighted'            => 0,
                            'display_points'              => 2,
                            'display_percent'             => 1,
                            'display_letters'             => 0,
                            'reprint_headers'             => 0,
                            'show_hidden'                 => 1
                            );


//******************************************************************
// SQL FUNCTIONS 
//******************************************************************

function grade_get_category_weight($course, $category) {
    global $CFG;
    $sql = "SELECT id, weight, drop_x_lowest, bonus_points, hidden, c.id AS cat_id
        FROM {$CFG->prefix}grade_category c  
        WHERE c.courseid=$course  
            AND c.name='$category'";
    $temp = get_record_sql($sql);
    return $temp;
}

function grade_get_grade_items($course) {
    global $CFG;
     $sql = "SELECT i.id, c.name as cname, i.modid, mm.name as modname, i.cminstance, c.hidden, cm.visible, i.sort_order, cm.id as cmid
            FROM    {$CFG->prefix}grade_item i, 
                    {$CFG->prefix}grade_category c, 
                    {$CFG->prefix}course_modules cm, 
                    {$CFG->prefix}modules mm
            WHERE c.id=i.category 
                AND i.courseid=c.courseid 
                AND c.courseid=$course 
                AND cm.course=c.courseid 
                AND cm.module=mm.id
                AND i.modid=mm.id
                AND cm.instance=i.cminstance";
    $temp = get_records_sql($sql);
    return $temp;
}

function grade_get_module_link($course, $cminstance, $modid) {
    global $CFG;
    $sql = "SELECT cm.id, 1 FROM {$CFG->prefix}course_modules cm, {$CFG->prefix}modules mm, {$CFG->prefix}grade_item i 
            WHERE i.modid='".$modid."' 
                AND i.modid=mm.id 
                AND cm.instance = i.cminstance 
                AND i.cminstance=".$cminstance."
                AND i.courseid=cm.course AND cm.module=mm.id AND i.courseid=$course";
    $temp = get_record_sql($sql);
    return $temp;
}

function grade_get_grade_letter($course, $grade) {
    global $CFG;
    $sql = "SELECT id, letter FROM {$CFG->prefix}grade_letter WHERE courseid=$course AND grade_high >= $grade AND grade_low <= $grade";
    $temp = get_record_sql($sql);
    return $temp;
}

function grade_get_exceptions($course) {
    global $CFG;
    $sql = "SELECT e.id, e.userid, gi.cminstance, gi.modid, c.name as catname, mm.name as modname
            FROM {$CFG->prefix}grade_exceptions e, 
                {$CFG->prefix}grade_item gi, 
                {$CFG->prefix}grade_category c,
                {$CFG->prefix}course_modules cm, 
                {$CFG->prefix}modules mm
            WHERE e.courseid=$course 
                AND gi.id = e.grade_itemid 
                AND c.id = gi.category
                AND cm.course=c.courseid 
                AND cm.module=mm.id
                AND gi.modid=mm.id";
    
    $temp = get_records_sql($sql);
    return $temp;
}

function grade_get_exceptions_user($course, $userid) {
    global $CFG;
    $sql = "SELECT e.id, e.userid, gi.cminstance, gi.modid, c.name as catname, mm.name as modname
            FROM {$CFG->prefix}grade_exceptions e, 
                {$CFG->prefix}grade_item gi, 
                {$CFG->prefix}grade_category c,
                {$CFG->prefix}course_modules cm, 
                {$CFG->prefix}modules mm
            WHERE e.courseid=$course
            AND e.userid=$userid 
                AND gi.id = e.grade_itemid 
                AND c.id = gi.category
                AND cm.course=c.courseid 
                AND cm.module=mm.id
                AND gi.modid=mm.id";
    
    $temp = get_records_sql($sql);
    return $temp;
}
function grade_letters_set($course) {
    global $CFG;
    $sql = "SELECT * FROM {$CFG->prefix}grade_letter WHERE courseid=$course";
    $letters_set = get_records_sql($sql);
    if ($letters_set) {
        return true;
    }
    else {
        return false;
    }
}

function grade_get_users_by_group($course, $group) {
    global $CFG;
    $sql = "SELECT userid FROM {$CFG->prefix}groups_members WHERE courseid=$course AND groupid = $group";
    $members = get_records_sql($sql);
    if ($members) {
        foreach($members as $member) {
            // FIX ME: there is no $userid defined!!! - from where is this function used anyway??
            $group->$userid = true;
        }
        return $group;
    }
    else {
        return NULL;
    }
}

//******************************************************************
// END SQL FUNCTIONS 
//******************************************************************

function grade_get_formatted_grades() {
    global $CFG;
    global $COURSE;
    global $preferences;
    $course = $COURSE;
    $i = 2; // index to differentiate activities with the same name MDL-6876
    $grades = grade_get_grades();
    if (isset($grades)) {
        // iterate through all students
        foreach($grades as $cur_category=>$grade_category) {
            // $cur_category holds the value of the current category name
            // $grade_category holds an array of all the mod types that are in this category
            if (isset($grade_category)) {
                foreach($grade_category as $cur_mod=>$mod_category)  {
                    // $cur_mod is the id of the current moodle module type
                    // $mod_category holds the grades for $cur_mod (current mod type)
                    $module_info = get_record('modules', 'id', $cur_mod);
                    $cur_modname = $module_info->name;
                    if (isset($mod_category)) {
                        foreach($mod_category as $cur_cminstance=>$students_grade) {
                            // $cur_cminstance is the course module instance for the cur_mod
                            $instance = get_record($cur_modname, 'id',$cur_cminstance, 'course',$course->id);
                            // it's necessary to check if the name is blank because some mods don't clean up the grades when the instance is deleted
                            // this is a bug. as it is plausible that some item could get created and have old data from a previous mod laying around
                            if ($instance->name != '') {                
                                // duplicate grade item name, the user should know better than to name to things by the same name, but to make sure grades don't disappear lets modify the name slightly
                                if (isset($all_categories["$cur_category"]["$instance->name"])) {
                                    $instance->name= $instance->name.' #'.$i++;
                                }

                                if (isset($students_grade->grades) && $students_grade->grades != '') {
                                    foreach($students_grade->grades as $student=>$grade) {
                                        // add an entry for any student that has a grade
                                        $grades_by_student["$student"]["$cur_category"]["$instance->name"]['grade'] = $grade;
                                        $grades_by_student["$student"]["$cur_category"]["$instance->name"]['sort_order'] = $students_grade->sort_order;
                                        
                                        if (!isset($grades_by_student["$student"]["$cur_category"]['stats'])) {
                                            $grades_by_student["$student"]["$cur_category"]['stats'] = array();
                                        }
                                        
                                        if (!isset($grades_by_student["$student"]["$cur_category"]['stats']['points'])) {
                                            $grades_by_student["$student"]["$cur_category"]['stats']['points'] = $grade;
                                        }
                                        else {
                                            $grades_by_student["$student"]["$cur_category"]['stats']['points'] = $grades_by_student["$student"]["$cur_category"]['stats']['points'] + $grade;
                                        }
        
                                        // This next block just creates a comma seperated list of all grades for the category
                                        if (isset($grades_by_student["$student"]["$cur_category"]['stats']['allgrades'])) {
                                            $grades_by_student["$student"]["$cur_category"]['stats']['allgrades'] .=  ','.$grade;
                                        }
                                        else {
                                            $grades_by_student["$student"]["$cur_category"]['stats']['allgrades'] = $grade;
                                        }
                                    }
                                }

                            
                        
                                // set up a list of all categories and assignments (adjusting things for extra credit where necessary)
                                $all_categories["$cur_category"]["$instance->name"]['hidden'] = $students_grade->hidden;
                                $all_categories["$cur_category"]["$instance->name"]['sort_order'] = $students_grade->sort_order;
                                
                                $all_categories["$cur_category"]["$instance->name"]['extra_credit'] = $students_grade->extra_credit;
                                
                                if ($all_categories["$cur_category"]["$instance->name"]['extra_credit'] != 1) {
                                    $all_categories["$cur_category"]["$instance->name"]['maxgrade'] = $students_grade->maxgrade;
                                }
                                else {
                                    $all_categories["$cur_category"]["$instance->name"]['maxgrade'] = 0; 
                                }                                
                                $all_categories["$cur_category"]["$instance->name"]['scale_grade'] = $students_grade->scale_grade;
                                if ($students_grade->scale_grade != 0) {
                                    $all_categories["$cur_category"]["$instance->name"]['scaled_max'] = round($all_categories["$cur_category"]["$instance->name"]['maxgrade']/$students_grade->scale_grade);
                                }
                                else {
                                    // avoids divide by zero... scale_grade shouldn't be set to 0 anyway
                                    $all_categories["$cur_category"]["$instance->name"]['scaled_max'] = $all_categories["$cur_category"]["$instance->name"]['maxgrade'];
                                    $all_categories["$cur_category"]["$instance->name"]['scale_grade'] = 1.0;
                                }
                                if (! isset($all_categories["$cur_category"]['stats']) ) {
                                    $all_categories["$cur_category"]['stats'] = array();
                                }
                                $all_categories["$cur_category"]["$instance->name"]['grade_against'] = $all_categories["$cur_category"]["$instance->name"]['scaled_max'];
                                if (!isset($all_categories["$cur_category"]['stats']['weight'])) {
                                    $weight = grade_get_category_weight($course->id, $cur_category);        
                                    $all_categories["$cur_category"]['stats']['weight'] = $weight->weight;
                                }
        
                                $all_categories["$cur_category"]["$instance->name"]['cminstance'] = $cur_cminstance;
                                $all_categories["$cur_category"]["$instance->name"]['modid'] = $cur_mod;
                                $modname = get_record('modules','id',$cur_mod);
                                $all_categories["$cur_category"]["$instance->name"]['modname'] = $modname->name;
        
                                // get bonus points and drop the x lowest                            
                                $drop = get_record('grade_category', 'courseid', $course->id, 'name', $cur_category);
                                $all_categories["$cur_category"]['stats']['drop'] = $drop->drop_x_lowest;
                                $all_categories["$cur_category"]['stats']['bonus_points'] = $drop->bonus_points;
                            }
                        }
                    }
                } 
            }
        }
        if (!$students = grade_get_course_students($course->id)) {
            return false;  
        }

                              
        if (isset($students) && $students) {
            foreach ($students as $userid => $student) {  
                $grades_by_student["$userid"]['student_data']['firstname'] = $student->firstname;
                $grades_by_student["$userid"]['student_data']['lastname'] = $student->lastname;
                $grades_by_student["$userid"]['student_data']['email'] = $student->email;
                if (isset($student->location)) {
                    $grades_by_student["$userid"]['student_data']['location'] = $student->location;
                }
                $grades_by_student["$userid"]['student_data']['department'] = $student->department;
                $grades_by_student["$userid"]['student_data']['idnumber'] = $student->idnumber;
            }
        }
        
        // unset any item that has a "" for a name at this point this inludes instructors who have grades or any student formerly enrolled.
        if (isset($grades_by_student)) {
            foreach ($grades_by_student as $student => $assignments) {
                if (!isset($grades_by_student["$student"]['student_data']['firstname']) && !isset($grades_by_student["$student"]['student_data']['lastname'])) {
                    unset($grades_by_student["$student"]);
                }
            }
        }
        
        
        // set the totalpoints for each category taking into account drop_x_lowest
        // also set the number of grade items for the category to make calculating grades for students who have not taken anything easier        
        foreach($all_categories as $category => $assignments) {
            $dropcount = 0;    
            $all_categories["$category"]['stats']['totalpoints'] = 0;
            $all_categories["$category"]['stats']['grade_items'] = 0;
            if (isset($assignments)) {
                foreach($assignments as $assignment=>$grade) {
                    if ($assignment != 'stats') {
                        if ($dropcount < $all_categories["$category"]['stats']['drop']) {
                            // skip a grade in the total
                            $dropcount++;
                        }
                        else {
                            // make sure the current assignment is not extra credit and then add it to the totalpoints
                            if ($all_categories["$category"][$assignment]['extra_credit'] != 1) {
                                $all_categories["$category"]['stats']['totalpoints'] = $all_categories["$category"]['stats']['totalpoints'] + $assignments["$assignment"]['grade_against'];
                                $all_categories["$category"]['stats']['grade_items'] = $all_categories["$category"]['stats']['grade_items'] + 1;
                            }
                        }
                    }
                }
            }
        }
    

        // if the user has selected a group to view by get the group members
        if ($currentgroup = get_current_group($course->id)) {
            $groupmembers = get_group_users($currentgroup);
        }

        // this next block catches any students who do not have a grade for any item in a particular category
        foreach($all_categories as $category => $main_category) {
            // make sure each student has an entry for each category
            if (isset($grades_by_student)) {
                foreach($grades_by_student as $student=>$categories) {
                    if ( (isset($groupmembers) && isset($groupmembers[$student])) || !isset($groupmembers)) {
                
                        $grades_by_student["$student"]["$category"]['stats']['totalpoints'] = $main_category['stats']['totalpoints'];
                        $grades_by_student["$student"]["$category"]['stats']['weight'] = $main_category['stats']['weight'];
                        $grades_by_student["$student"]["$category"]['stats']['grade_items'] = $main_category['stats']['grade_items'];

                        foreach($main_category as $assignment => $items) {
                            if ($assignment != 'stats') {
                                if(!isset($grades_by_student["$student"]["$category"]["$assignment"]['grade'])) {
                                    if (isset($grades_by_student["$student"]["$category"]['stats']['allgrades'])) {
                                        $grades_by_student["$student"]["$category"]['stats']['allgrades'] .=  ',0';
                                    } else {
                                        $grades_by_student["$student"]["$category"]['stats']['allgrades'] = '0';
                                    }
                                }
                            }            
                        }
            
                        if (!isset($grades_by_student["$student"]["$category"]['stats']['points'])) {
                           $grades_by_student["$student"]["$category"]['stats']['points'] = '-';
               
                        }
        
                        else {
                            // points are set... see if the current category is using drop the x lowest and do so
                            // also drop exceptions first, so then this grades(s) won't be recoqnized as the x lowest
                // Get exception scores and assign them in the array
                            if ($main_category['stats']['drop'] != 0) {
                                $exceptions = grade_get_exceptions_user($course->id, $student);
                                if (isset($exceptions) && $exceptions) {
                                    foreach($exceptions as $exception) {
                                        if (isset($grades_by_student["$exception->userid"])) {
                                            if ($grades_by_student["$exception->userid"]["$exception->catname"]) {
                                                $assgn = get_record($exception->modname, 'id', $exception->cminstance, 'course', $course->id);
                                                $grade = $grades_by_student["$exception->userid"]["$exception->catname"]["$assgn->name"]['grade'];
                        if (isset($grade)) {
                            if (!isset($grades_by_student["$exception->userid"]["$exception->catname"]['stats']['exceptions'])) {
                                                             $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['exceptions'] = $grade;
                                                    }
                                                    elseif (isset($grades_by_student["$exception->userid"]["$exception->catname"]['stats']['exceptions'])) {
                                                             $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['exceptions'] .= ','. $grade;
                            }
                        }
                                            }
                                        }
                                    }
                }
                                if (isset($grades_by_student["$student"]["$category"]['stats']['exceptions'])) {
                                    $grades_by_student["$student"]["$category"]['stats']['allgrades'] = grade_drop_exceptions($grades_by_student["$student"]["$category"]['stats']['allgrades'], $grades_by_student["$student"]["$category"]['stats']['exceptions']);
                                }
                
                                $grades_by_student["$student"]["$category"]['stats']['allgrades'] = grade_drop_lowest($grades_by_student["$student"]["$category"]['stats']['allgrades'], $main_category['stats']['drop'], $main_category['stats']['grade_items']);
                                if ($grades_by_student["$student"]["$category"]['stats']['points'] != '-') {
                                    $cat_points = 0;
                                    $count_grades = explode(',',$grades_by_student["$student"]["$category"]['stats']['allgrades']);
                                    foreach($count_grades as $grade) {
                                        $cat_points = $cat_points + $grade;
                                    }
                                    $grades_by_student["$student"]["$category"]['stats']['points'] = $cat_points;
                                }
                            }
                        }
       
                        // add any bonus points for the category
                        if ($all_categories["$category"]['stats']['bonus_points'] != 0) {
                            $grades_by_student["$student"]["$category"]['stats']['points'] = $grades_by_student["$student"]["$category"]['stats']['points'] + $all_categories["$category"]['stats']['bonus_points'];
                        }
        
                        foreach($main_category as $assignment => $items) {
                            if ($assignment != 'stats') {
                                if(!isset($grades_by_student["$student"]["$category"]["$assignment"]['maxgrade'])) {
                                    $grades_by_student["$student"]["$category"]["$assignment"]['maxgrade'] = $all_categories["$category"]["$assignment"]['grade_against'];
                                }
                                if(!isset($grades_by_student["$student"]["$category"]["$assignment"]['grade'])) {
                                    $grades_by_student["$student"]["$category"]["$assignment"]['grade'] = '-';
                                    $grades_by_student["$student"]["$category"]["$assignment"]['sort_order'] = $all_categories["$category"]["$assignment"]['sort_order'];
                                }
                            }            
                        }
                    } // end groupmember if
                    else {
                        // unset grade since they are not in the selected group.
                        unset($grades_by_student["$student"]);
                    }
                }
            }
        }
        // set the total coursepoints
        $all_categories['stats']['weight'] = 0;
        $all_categories['stats']['totalpoints'] = 0;
        foreach($all_categories as $category => $info) {
            if ($category != 'stats') {
                $all_categories['stats']['weight'] = $all_categories['stats']['weight'] + $all_categories["$category"]['stats']['weight'];
                $all_categories['stats']['totalpoints'] = $all_categories['stats']['totalpoints'] + $all_categories["$category"]['stats']['totalpoints'];
            }
        }
        
        // set each individuals total points by category so we can then exclude some grades if set to use exceptions
        if (isset($grades_by_student)) {
            foreach($grades_by_student as $student => $categories) {
                foreach($all_categories as $category => $assignments) {
                    if ($category != 'stats') {
                        $grades_by_student["$student"]["$category"]['stats']['totalpoints'] = $all_categories["$category"]['stats']['totalpoints'];
                    }
                }
                $grades_by_student["$student"]['student_data']['totalpoints'] = $all_categories['stats']['totalpoints'];
            }
        }
        
        // take into account any excluded grade_items
        $strexcluded = get_string('excluded', 'grades');
        $exceptions = grade_get_exceptions($course->id);
        if (isset($exceptions) && $exceptions) {
            foreach($exceptions as $exception) {
                if (isset($grades_by_student["$exception->userid"])) {
                    if ($grades_by_student["$exception->userid"]["$exception->catname"]) {
                        $assgn = get_record($exception->modname, 'id', $exception->cminstance, 'course', $course->id);
                        $grades_by_student["$exception->userid"]['student_data']['totalpoints'] = $grades_by_student["$exception->userid"]['student_data']['totalpoints'] - $all_categories["$exception->catname"]["$assgn->name"]['maxgrade'];
            //total point should not be smaller than grade against
            if ($grades_by_student["$exception->userid"]["$exception->catname"]['stats']['totalpoints'] - $all_categories["$exception->catname"]["$assgn->name"]['grade_against'] != 0 ) {
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['totalpoints'] = $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['totalpoints'] - $all_categories["$exception->catname"]["$assgn->name"]['grade_against'];
            }
                        $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['grade_items'] = $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['grade_items'] - 1;
                        if ($grades_by_student["$exception->userid"]["$exception->catname"]['stats']['grade_items'] < 0) {
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['grade_items'] = 0;
            }
                        if ($all_categories["$exception->catname"]['stats']['drop'] == 0) {
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['points'] = $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['points'] - $grades_by_student["$exception->userid"]["$exception->catname"]["$assgn->name"]['grade'];
                        }
                        $grades_by_student["$exception->userid"]["$exception->catname"]["$assgn->name"]['maxgrade'] = $strexcluded;
                        $grades_by_student["$exception->userid"]["$exception->catname"]["$assgn->name"]['grade'] = $strexcluded;
                        // see if they are excluded entirely from a category
                        if ($grades_by_student["$exception->userid"]["$exception->catname"]['stats']['totalpoints'] == 0) {
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['totalpoints'] = $strexcluded;
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['percent'] = $strexcluded;
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['points'] = $strexcluded;
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['weight'] = $strexcluded;
                            $grades_by_student["$exception->userid"]["$exception->catname"]['stats']['weighted'] = $strexcluded;
                        }
                    }
                }
                else {
                    // the user had exceptions, but was unenrolled from the course... we could delete it here, but we will leave it because the user may be re-added to the course
                }
            }
        }
                        
        if (isset($grades_by_student)) {
            foreach($grades_by_student as $student => $categories) {
                $grades_by_student["$student"]['student_data']['points'] = '-';
                $grades_by_student["$student"]['student_data']['totalpoints'] = 0;
                $grades_by_student["$student"]['student_data']['weight'] = 0;
                $grades_by_student["$student"]['student_data']['weighted'] = 0;
                foreach($categories as $category => $assignments) {
                    if ($category != 'student_data') {
                        // set the student's total points earned 
                        if ($grades_by_student["$student"]["$category"]['stats']['points'] != $strexcluded) {
                            if ($grades_by_student["$student"]["$category"]['stats']['points'] != '-') {
                                $grades_by_student["$student"]['student_data']['points'] = $grades_by_student["$student"]['student_data']['points'] + $grades_by_student["$student"]["$category"]['stats']['points'];
                            }
                        $grades_by_student["$student"]['student_data']['totalpoints'] = $grades_by_student["$student"]['student_data']['totalpoints'] + $grades_by_student["$student"]["$category"]['stats']['totalpoints'];
                        }
                
                        // set percents and weights for each assignment
                        foreach($assignments as $assignment => $info) {
                            if ($assignment != 'stats') {
                                if ($grades_by_student["$student"]["$category"]["$assignment"]['grade'] != $strexcluded) {    
                                    if ($grades_by_student["$student"]["$category"]["$assignment"]['maxgrade'] != 0) {
                                        $grades_by_student["$student"]["$category"]["$assignment"]['percent'] = round($grades_by_student["$student"]["$category"]["$assignment"]['grade']/$grades_by_student["$student"]["$category"]["$assignment"]['maxgrade']*100,2);
                                    }
                                    else {
                                        $grades_by_student["$student"]["$category"]["$assignment"]['percent'] = 0;
                                    }
                                    if ($grades_by_student["$student"]["$category"]['stats']['totalpoints'] != 0) {
                                        $grades_by_student["$student"]["$category"]["$assignment"]['weight'] = round($all_categories["$category"]['stats']['weight']*($grades_by_student["$student"]["$category"]["$assignment"]['maxgrade']/$grades_by_student["$student"]["$category"]['stats']['totalpoints']),2);
                                    }
                                    else {
                                        $grades_by_student["$student"]["$category"]["$assignment"]['weight'] = 0.00;
                                    }
                                    if ($grades_by_student["$student"]["$category"]["$assignment"]['weight'] != 0) {
                                        $grades_by_student["$student"]["$category"]["$assignment"]['weighted'] = round($grades_by_student["$student"]["$category"]["$assignment"]['percent']*$grades_by_student["$student"]["$category"]["$assignment"]['weight']/100,2);
                                    }
                                    else {
                                        // should only be here if this is extra credit
                                        $grades_by_student["$student"]["$category"]["$assignment"]['weighted'] = 0.00;
                                    }
                                }
                                else {
                                    $grades_by_student["$student"]["$category"]["$assignment"]['percent'] = $strexcluded;
                                    $grades_by_student["$student"]["$category"]["$assignment"]['weight'] = $strexcluded;
                                    $grades_by_student["$student"]["$category"]["$assignment"]['weighted'] = $strexcluded;
                                }
                            }
                        }
                        // set the percent and weight per category
                        if ($grades_by_student["$student"]["$category"]['stats']['totalpoints'] != 0 ) {
                            $grades_by_student["$student"]["$category"]['stats']['percent']  = round($grades_by_student["$student"]["$category"]['stats']['points']/$grades_by_student["$student"]["$category"]['stats']['totalpoints']*100,2);
                            $grades_by_student["$student"]["$category"]['stats']['weighted'] = round($grades_by_student["$student"]["$category"]['stats']['points']/$grades_by_student["$student"]["$category"]['stats']['totalpoints']*$grades_by_student["$student"]["$category"]['stats']['weight'],2);
                        }
                        else {
                            if ($grades_by_student["$student"]["$category"]['stats']['totalpoints'] != $strexcluded) {
                                $grades_by_student["$student"]["$category"]['stats']['percent'] = 0.00;
                                $grades_by_student["$student"]["$category"]['stats']['weighted'] = 0.00;
                            }
                        }
                                
                        // set students overall weight (this is what percent they will be graded against)
                        if ($grades_by_student["$student"]["$category"]['stats']['weight'] != $strexcluded) {
                            $grades_by_student["$student"]['student_data']['weight'] = $grades_by_student["$student"]['student_data']['weight'] + $grades_by_student["$student"]["$category"]['stats']['weight'];
                        }
                        
                        // set the students total categories towards point total we have to defer the percent calculation until we know what total weight they should be graded against since they may
                        // be excluded from a whole category.
                        if ($all_categories["$category"]['stats']['totalpoints'] != 0)  {
                            $grades_by_student["$student"]['student_data']['weighted'] = $grades_by_student["$student"]['student_data']['weighted'] + $grades_by_student["$student"]["$category"]['stats']['weighted'];
                        }
                    }
                    
    
                }
                
                // set the percent and weight overall
                if ($grades_by_student["$student"]['student_data']['totalpoints'] != 0 && $grades_by_student["$student"]['student_data']['totalpoints'] != $strexcluded) {
                    $grades_by_student["$student"]['student_data']['percent'] = round($grades_by_student["$student"]['student_data']['points']/$grades_by_student["$student"]['student_data']['totalpoints']*100,2);
                    if ($grades_by_student["$student"]['student_data']['weight'] != 0) {
                        $grades_by_student["$student"]['student_data']['weighted'] = round($grades_by_student["$student"]['student_data']['weighted']/$grades_by_student["$student"]['student_data']['weight']*100,2);
                    }
                    else {
                        $grades_by_student["$student"]['student_data']['weighted'] = 0.00;
                    }
                }
                else if ($grades_by_student["$student"]['student_data']['totalpoints'] == 0) {
                    $grades_by_student["$student"]['student_data']['percent'] = 0.00;
                }
                
            }
        }
        
        if (isset($grades_by_student)) {
            $sort = optional_param('sort','default');
            
            switch ($sort) {
                case 'highgrade_category':
                    uasort($grades_by_student, 'grade_sort_by_highgrade_category');
                    break;
                case 'highgrade_category_asc':
                    uasort($grades_by_student, 'grade_sort_by_highgrade_category_asc');
                    break;
                case 'highgrade':
                {
                    if ($preferences->use_weighted_for_letter == 1) {
                        uasort($grades_by_student, 'grade_sort_by_weighted');
                    }
                    else {
                        uasort($grades_by_student, 'grade_sort_by_percent');
                    }
                    break;
                }
                case 'points':
                    uasort($grades_by_student, 'grade_sort_by_points');
                    break;
                case 'points_asc':
                    uasort($grades_by_student, 'grade_sort_by_points_asc');
                    break;
                case 'weighted':
                    uasort($grades_by_student, 'grade_sort_by_weighted');
                    break;
                case 'weighted_asc':
                    uasort($grades_by_student, 'grade_sort_by_weighted_asc');
                    break;
                case 'percent':
                    uasort($grades_by_student, 'grade_sort_by_percent');
                    break;
                case 'percent_asc':
                    uasort($grades_by_student, 'grade_sort_by_percent_asc');
                    break;
                case 'highgrade_asc':
                {
                    if ($preferences->use_weighted_for_letter == 1) {
                        uasort($grades_by_student, 'grade_sort_by_weighted_asc');
                    }
                    else {
                        uasort($grades_by_student, 'grade_sort_by_percent_asc');
                    }
                    break;
                }
                case 'firstname':
                    uasort($grades_by_student, 'grade_sort_by_firstname');
                    break;
                default:
                    uasort($grades_by_student, 'grade_sort_by_lastname');
            }
        }
        else {
            $grades_by_student = 0;
        }        
        $retval = array($grades_by_student, $all_categories);
    }
    else {
        $retval = array(0,0);
        // echo "<center><font color=red>Could not find any graded items for this course.</font></center>";
    }
    return $retval;
}

function grade_drop_exceptions($grades, $grades_exceptions) {
    $grade_array = explode(',',$grades);
    $grade_exception_array = explode(',',$grades_exceptions);
    $ret_grades = Array(); 
    foreach ($grade_array as $key => $val) {
        $posb = array_search($val,$grade_exception_array);
        if (is_integer($posb)) {
            unset($grade_exception_array[$posb]);
        } else {
        
            $ret_grades[] = $val;
        }
    }
    $grades = implode(',', $ret_grades);
    return $grades;
}

function grade_drop_lowest($grades, $drop, $total) {
    // drops the lowest $drop numbers from the comma seperated $grades making sure that if $grades has 
    // fewer items than $total that we don't drop too many
    $grade_array = explode(',',$grades);
    if (count($grade_array) == 1) {
    $grades = implode('', $grade_array);
    }
    else if ($drop > 0 AND (count($grade_array) > $drop)) {
        rsort($grade_array);

        for($i=0; $i < (count($grade_array) - $drop); $i++) {
            $ret_grades["$i"] = $grade_array["$i"];
        }
        if (isset($ret_grades)) {
            $grades = implode(',',$ret_grades);
        }
    }
    else {
        $grades = 0;
    }
    return $grades;    
}

function grade_get_grades() {
    global $CFG;
    global $course;
    $mods = grade_get_grade_items($course->id);
    $preferences = grade_get_preferences($course->id);
    
    if ($mods) {
        foreach ($mods as $mod)    {
            // hidden is a gradebook setting for an assignment and visible is a course_module setting 
            if (($mod->hidden != 1 && $mod->visible==1) or (has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_MODULE, $mod->cmid)) && $preferences->show_hidden==1)) {
                $libfile = "$CFG->dirroot/mod/$mod->modname/lib.php";
                if (file_exists($libfile)) {
                    require_once($libfile);
                    $gradefunction = $mod->modname."_grades";
                    if ($grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"] = $gradefunction($mod->cminstance)) {
                        // added grades for particular mod
                        // now get the grade_item modifiers ie. scale_grade and extra credit
                        $scale_grade = get_record('grade_item', 'courseid', $course->id, 'cminstance', $mod->cminstance, 'modid', $mod->modid);

                        if (isset($scale_grade)) {
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->scale_grade = $scale_grade->scale_grade;
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->extra_credit = $scale_grade->extra_credit;
                        }
                        else {
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->scale_grade = 1.00;
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->extra_credit = 0;
                        }
                        
                        if ($mod->hidden != 1 && $mod->visible==1) {
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->hidden = 0;
                        }
                        else {
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->hidden = 1;
                        }
                        
                        $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->sort_order = $scale_grade->sort_order;
                        
                        // I don't think this should be necessary but it appears that the forum doesn't follow the grades API properly it returns blank or NULL when it 
                        // should return a value for maxgrade according to the moodle API... so if it doesn't want to give us a grade let's not use it.
                        // this happens when grading is set to a non-numeric for a forum ie. uses "seperate and connected ways of knowing"
                        if ($grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->maxgrade == '')
                        {
                            $grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]->maxgrade = 100;
                            //unset($grades["$mod->cname"]["$mod->modid"]["$mod->cminstance"]);
                        }
                    }
                    else {
                        // delete this item from the grade_item table since it was deleted through the mod interface
                        delete_records('grade_item', 'modid', $mods->modid, 'courseid', $course->id);
                        delete_records('grade_exceptions', 'grade_itemid', $mod->id, 'courseid', $course->id);
                    }                
                }
                else {
                    //echo "<center><font color=red>Could not find lib file for $mod->modid</font></center>";
                }
            }
        }
    }
    else {
        // Do something here for no grades
        //echo "<center><font color=red>No grades returned. It appears that there are no items with grades for this course.</font></center>";
    }
    if (isset($grades)) {
        return $grades;
    }
    else {
        return NULL;
    }    
}

function grade_set_uncategorized() {
    // this function checks to see if any mods have not been assigned a category and sets them to uncategorized.
    global $CFG;
    global $course;
    $uncat = UNCATEGORISED;

    $uncat_id = get_record('grade_category', 'courseid', $course->id, 'name', $uncat);
    
    if (!$uncat_id) {
        // insert the uncategorized category 
        $temp->name=$uncat;
        $temp->courseid=$course->id;
        $temp->drop_x_lowest = 0;
        $temp->bonus_points = 0;
        $temp->hidden = 0;
        $temp->weight = 100.00;
        
        insert_record('grade_category', $temp);
        $uncat_id = get_record('grade_category', 'courseid', $course->id, 'name', $uncat);
        if (!$uncat_id) {
            error(get_string('errornocategorizedid','grades'));
            exit(0);
        }
    }
    

    /// Collect modules data
    get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
    $itemcount = 0;
    
    // this will let us establish the order for gradebook item display
    $sort = 0;
    
    /// Search through all the modules, pulling out grade data
    $sections = get_all_sections($course->id); // Sort everything the same as the course
    for ($i=0; $i<=$course->numsections; $i++) {
        if (isset($sections["$i"])) {   // should always be true
            $section = $sections["$i"];
            if ($section->sequence) {
                $sectionmods = explode(",", $section->sequence);
                foreach ($sectionmods as $sectionmod) {
                    if (isset($mods["$sectionmod"])) {
                        $mod = $mods["$sectionmod"];
                        $instance = get_record("$mod->modname", "id", "$mod->instance");
                        $libfile = "$CFG->dirroot/mod/$mod->modname/lib.php";
                        if (file_exists($libfile)) {
                            require_once($libfile);
                            $gradefunction = $mod->modname."_grades";
                            if (function_exists($gradefunction)) {   // Skip modules without grade function
                                if ($modgrades = $gradefunction($mod->instance)) {
                                    $itemcount++;
                                    //modgrades contains student information with associated grade
                                    //echo "<b>modname: $mod->modname id: $mod->id course: $mod->course</b><br/>";
                                    // get instance name from db.
                                    $instance = get_record($mod->modname, 'id', $mod->instance);
                                    // see if the item is already in the category table and if it is call category select with the id so it is selected
                                    get_record('modules', 'name', $mod->modname);
                                    $item = get_record('grade_item', 'courseid', $course->id, 'modid', $mod->module, 'cminstance', $mod->instance);
                                    if (!$item) {
                                        // set the item to uncategorized in grade_item
                                        $item->courseid = $course->id;
                                        $item->category = $uncat_id->id;
                                        $item->modid = $mod->module;
                                        $item->cminstance = $mod->instance;
                                        $item->id = insert_record('grade_item', $item);
                                    }
                                    else if ($item->category == 0) {
                                        // this catches any errors where they may have some wierd category set
                                        set_field('grade_item', 'category', $uncat_id->id, 'id', $item->id);
                                    }
                                    set_field('grade_item', 'sort_order', $sort, 'id', $item->id);
                                    $sort++;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

// sorting functions for grades
function grade_sort_by_lastname($x,$y)
{
    //$grades_by_student["$student->userid"]['student_data']['firstname'] = $student->firstname;
    //$grades_by_student["$student->userid"]['student_data']['lastname'] = $student->lastname;
    if (strnatcasecmp($x['student_data']['lastname'],$y['student_data']['lastname']) == 0) {
        return strnatcasecmp($x['student_data']['firstname'],$y['student_data']['firstname']);
    }
    else {
        return strnatcasecmp($x['student_data']['lastname'],$y['student_data']['lastname']);
    }
}

function grade_sort_by_firstname($x,$y)
{
    //$grades_by_student["$student->userid"]['student_data']['firstname'] = $student->firstname;
    //$grades_by_student["$student->userid"]['student_data']['lastname'] = $student->lastname;
    if (strnatcasecmp($x['student_data']['firstname'],$y['student_data']['firstname']) == 0) {
        return strnatcasecmp($x['student_data']['lastname'],$y['student_data']['lastname']);
    }
    else {
        return strnatcasecmp($x['student_data']['firstname'],$y['student_data']['firstname']);
    }
}

function grade_sort_by_points($x,$y) {
    if ($x['student_data']['points'] == $y['student_data']['points']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        if ($x['student_data']['points'] > $y['student_data']['points'])
            return -1;
        else 
            return 1;
    }
}

function grade_sort_by_points_asc($x,$y) {
    if ($x['student_data']['points'] == $y['student_data']['points']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        if ($x['student_data']['points'] < $y['student_data']['points'])
            return -1;
        else 
            return 1;
    }
}

function grade_sort_by_weighted($x,$y) {
    if ($x['student_data']['weighted'] == $y['student_data']['weighted']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        if ($x['student_data']['weighted'] > $y['student_data']['weighted'])
            return -1;
        else 
            return 1;
    }
}

function grade_sort_by_percent($x,$y) {
    if ($x['student_data']['percent'] == $y['student_data']['percent']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        if ($x['student_data']['percent'] > $y['student_data']['percent'])
            return -1;
        else 
            return 1;
    }
}

function grade_sort_by_percent_asc($x,$y) {
    if ($x['student_data']['percent'] == $y['student_data']['percent']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        if ($x['student_data']['percent'] < $y['student_data']['percent'])
            return -1;
        else 
            return 1;
    }
}

function grade_sort_by_weighted_asc($x,$y) {
    if ($x['student_data']['weighted'] == $y['student_data']['weighted']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        if ($x['student_data']['weighted'] < $y['student_data']['weighted'])
            return -1;
        else 
            return 1;
    }
}
    
function grade_sort_by_highgrade_category($x,$y) {
    global $cview;
    
    if(!$cview) {
        $cview = optional_param('cview');
    }
    
    if ($x["$cview"]['stats']['points'] == $y["$cview"]['stats']['points']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        return ($y["$cview"]['stats']['points'] - $x["$cview"]['stats']['points']);
    }
}

function grade_sort_by_highgrade_category_asc($x,$y) {
    global $cview;
    
    if(!$cview)
        $cview = optional_param('cview');
    
    if ($x["$cview"]['stats']['points'] == $y["$cview"]['stats']['points']) {
        return grade_sort_by_lastname($x,$y);
    }
    else {
        return ($x["$cview"]['stats']['points'] - $y["$cview"]['stats']['points']);
    }
}


function grade_set_preference($courseid, $name, $value) {
    global $GRADEPREFS;

    if (false !== ($key = array_search($name, $GRADEPREFS))) {
        if ($record = get_record('grade_preferences', 'courseid', $courseid, 'preference', $key)) {
            $record->value = $value;
            update_record('grade_preferences', $record);
        } else {   // Make a new one
            $record->preference = $key;
            $record->courseid = $courseid;
            $record->value = $value;
            insert_record('grade_preferences', $record);
        }
    }
}

function grade_get_preference($courseid, $name) {
    global $GRADEPREFS, $GRADEPREFSDEFAULTS;

    if (false !== ($key = array_search($name, $GRADEPREFS))) {
        if (!($record = get_record('grade_preferences', 'courseid', $courseid, 'preference', $key))) {
            // Make a new one
            $record->preference = $key;
            $record->courseid = $courseid;
            $record->value = $GRADEPREFSDEFAULTS[$name];
            insert_record('grade_preferences', $record);
        }
        return $record->value;
    }
    return NULL;
}

function grade_get_preferences($courseid) {
    global $CFG;
    global $GRADEPREFS, $GRADEPREFSDEFAULTS;

    $preferences = NULL;

    // Get the preferences for the course.
    if ($rawprefs = get_records('grade_preferences', 'courseid', $courseid)) {
        foreach ($rawprefs as $pref) {
            if (isset($GRADEPREFS[$pref->preference])) {  // Valid pref
                $name = $GRADEPREFS[$pref->preference];
                $preferences->$name = $pref->value;
            }
        }
    }

    // Check for any missing ones and create them from defaults
    // We don't save them in the database so we save space
    foreach ($GRADEPREFS as $number => $name) {
        if (!isset($preferences->$name)) {
            $preferences->$name = $GRADEPREFSDEFAULTS[$name];
        }
    }

    // Construct some other ones about which fields are shown

    $isteacher = has_capability('moodle/course:managegrades', get_context_instance(CONTEXT_COURSE, $courseid));

    $preferences->show_weighted = (($preferences->display_weighted > 0  && $isteacher) || 
                                   ($preferences->display_weighted > 1 && !$isteacher));
    
    $preferences->show_points   = (($preferences->display_points > 0  && $isteacher) || 
                                   ($preferences->display_points > 1 && !$isteacher));
    
    $preferences->show_percent  = (($preferences->display_percent > 0  && $isteacher) || 
                                   ($preferences->display_percent > 1 && !$isteacher));

    $preferences->show_letters  = (($preferences->display_letters > 0  && $isteacher) || 
                                   ($preferences->display_letters > 1 && !$isteacher));

    return $preferences;
}


function grade_set_preferences($course, $newprefs) {
    
    if (!isset($newprefs->use_advanced) or ($newprefs->use_advanced == 1)) {
        foreach ($newprefs as $name => $value) {        /// Just save them all
            grade_set_preference($course->id, $name, $value);
        }
        return true;
    }

/// We don't need advanced features, and we need to unset all extra features
/// So they don't affect grades    (This approach should be revisited because it resets everything!!)
    
    grade_set_preference($course->id, 'use_advanced', 0);
    grade_set_preference($course->id, 'use_weighted_for_letter', 0);
    grade_set_preference($course->id, 'display_weighted', 0);
    grade_set_preference($course->id, 'display_points', 2);
    grade_set_preference($course->id, 'display_percent', 0);
    grade_set_preference($course->id, 'display_letters', 0);
    
/// Lose all exceptions
    delete_records('grade_exceptions', 'courseid', $course->id);
        
    if (!$uncat = get_record('grade_category', 'courseid', $course->id, 'name', UNCATEGORISED)) {
        /// Make a category for uncategorised stuff
        $uncat->name=UNCATEGORISED;
        $uncat->courseid=$course->id;
        if (!$uncat->id = insert_record('grade_category', $uncat)) {
            error(get_string('errornocategorizedid','grades'));
        }
    }
    
    set_field('grade_item', 'category', $uncat->id, 'courseid', $course->id);
    set_field('grade_item', 'scale_grade', 1.00, 'courseid', $course->id);
    set_field('grade_item', 'extra_credit', 0, 'courseid', $course->id);

    set_field('grade_category', 'weight', 100.0, 'courseid', $course->id, 'id', $uncat->id);
    set_field('grade_category', 'bonus_points', '0', 'courseid', $course->id);
}


function grade_preferences_menu($action, $course) {

    if (!has_capability('moodle/course:managegrades', get_context_instance(CONTEXT_COURSE, $course->id))) {
        return;
    }

    // remap some actions to simplify later code        
    switch ($action) {
        case 'prefs':
        case 'set_grade_preferences':
            $curraction = 'prefs';
            break;
        case 'cats':
        case 'vcats':
            $curraction = '';
            break;
        case 'insert_category':
        case 'assign_categories':
        case 'delete_category':
            $curraction = 'cats';
            break;
        case 'set_grade_weights':
        case 'weights':
            $curraction = 'weights';
            break;
        case 'letters':
        case 'set_letter_grades':
            $curraction = 'letters';
            break;
        case 'view_student_grades':
        case 'view_student_category_grades':
        case 'grades':
            $curraction = 'grades';
            break;
        case 'excepts':
            $curraction = 'excepts';
            break;

        default:
            $curraction = 'grades';
    }

    $tabs = $row = array();
    $row[] = new tabobject('grades', 'index.php?id='.$course->id,
                           get_string('viewgrades', 'grades'));
    $row[] = new tabobject('prefs', 'index.php?id='.$course->id.'&amp;action=prefs',
                           get_string('setpreferences', 'grades'));
    // only show the extra options if advanced is turned on, they don't do anything otherwise
    if (grade_get_preference($course->id, 'use_advanced') == 1) {
        $row[] = new tabobject('cats', 'index.php?id='.$course->id.'&amp;action=cats',
                               get_string('setcategories', 'grades'));
        $row[] = new tabobject('weights', 'index.php?id='.$course->id.'&amp;action=weights',
                               get_string('setweights', 'grades'));
        $row[] = new tabobject('letters', 'index.php?id='.$course->id.'&amp;action=letters',
                               get_string('setgradeletters', 'grades'));
        $row[] = new tabobject('excepts', 'exceptions.php?id='.$course->id.'&amp;action=excepts',
                               get_string('gradeexceptions', 'grades'));
    }
    $tabs[] = $row;

    print_tabs($tabs, $curraction);
}


function grade_nav($course, $action='grades') {
    global $CFG;
    global $USER;
    global $cview;

    $strgrades = get_string('grades', 'grades');
    $gradenav = "<a href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a>";

    if (has_capability('moodle/course:managegrades', get_context_instance(CONTEXT_COURSE, $course->id))) {
        switch ($action) {
            case 'prefs':
            case 'set_grade_preferences':
                $strcurpage = get_string('setpreferences','grades');
                break;
            case 'cats':
            case 'delete_category':
            case 'cats':
            case 'insert_category':
            case 'assign_categories':
                $strcurpage = get_string('setcategories','grades');
                break;
            case 'weights':
            case 'set_grade_weights':
                $strcurpage = get_string('setweights','grades');
                break;
            case 'set_letter_grades':
            case 'letters':
                $strcurpage = get_string('setgradeletters','grades');
                break;
            case 'excepts':
                $strcurpage = get_string('gradeexceptions', 'grades');
                break;
            default:
                unset($strcurpage);
                break;
        }
    
        if ($action=='grades') {
            $gradenav .= " -> $strgrades";
        } else {
            $gradenav .= " -> <a href=\"index.php?id=$course->id&amp;action=grades\">$strgrades</a>";
        }
        
        // if we are on a grades sub-page provide a link back (including grade preferences and grade items
        
        if (isset($strcurpage)) {
            $gradenav .= " -> $strcurpage";
        } else if($action =='vcats') {
            // show sub category
            if (isset($cview)) {
                $gradenav .= " -> $cview";
            }
        }

    } else {
        $gradenav .= " -> $strgrades";
    }
    
    return $gradenav;    
}

function grade_download($download, $id) {
    global $CFG;

    require_login();

    if (! $course = get_record("course", "id", $id)) {
        error("Course ID was incorrect");
    }

    require_capability('moodle/course:viewcoursegrades', get_context_instance(CONTEXT_COURSE, $id));

    $strgrades = get_string("grades");
    $strgrade = get_string("grade");
    $strmax = get_string("maximumshort");
    $stractivityreport = get_string("activityreport");

/// Check to see if groups are being used in this course
    $currentgroup = get_current_group($course->id);

    if ($currentgroup) {
        $students = get_group_students($currentgroup, "u.lastname ASC");
    } else {
        $students = grade_get_course_students($course->id);
    }

    if (!empty($students)) {
        foreach ($students as $student) {
            $grades[$student->id] = array();    // Collect all grades in this array
            $gradeshtml[$student->id] = array(); // Collect all grades html formatted in this array
            $totals[$student->id] = array();    // Collect all totals in this array
        }
    }
    $columns = array();     // Accumulate column names in this array.
    $columnhtml = array();  // Accumulate column html in this array.


/// Collect modules data
    get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);

/// Search through all the modules, pulling out grade data
    $sections = get_all_sections($course->id); // Sort everything the same as the course
    for ($i=0; $i<=$course->numsections; $i++) {
        if (isset($sections[$i])) {   // should always be true
            $section = $sections[$i];
            if ($section->sequence) {
                $sectionmods = explode(",", $section->sequence);
                foreach ($sectionmods as $sectionmod) {
                    $mod = $mods[$sectionmod];
                    $instance = get_record("$mod->modname", "id", "$mod->instance");
                    $libfile = "$CFG->dirroot/mod/$mod->modname/lib.php";    
                    
                    if (file_exists($libfile)) {
                        require_once($libfile);
                        $gradefunction = $mod->modname."_grades";
                        if (function_exists($gradefunction)) {   // Skip modules without grade function
                            if ($modgrades = $gradefunction($mod->instance)) {
                                if (!empty($modgrades->maxgrade)) {
                                    if ($mod->visible) {
                                        $maxgrade = "$strmax: $modgrades->maxgrade";
                                    } else {
                                        $maxgrade = "$strmax: $modgrades->maxgrade";
                                    }
                                } else {
                                    $maxgrade = "";
                                }

                                $columns[] = "$mod->modfullname: ".format_string($instance->name,true)." - $maxgrade";

                                if (!empty($students)) {
                                    foreach ($students as $student) {
                                        if (!empty($modgrades->grades[$student->id])) {
                                            $grades[$student->id][] = $currentstudentgrade = $modgrades->grades[$student->id];
                                        } else {
                                            $grades[$student->id][] = $currentstudentgrade = "";
                                            $gradeshtml[$student->id][] = "";
                                        }
                                        if (!empty($modgrades->maxgrade)) {
                                            $totals[$student->id] = (float)($totals[$student->id]) + (float)($currentstudentgrade);
                                        } else {
                                            $totals[$student->id] = (float)($totals[$student->id]) + 0;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    } // a new Moodle nesting record? ;-)

/// OK, we have all the data, now present it to the user
/// OK, we have all the data, now present it to the user
    if ($download == "ods" and confirm_sesskey()) {
        require_once("../lib/odslib.class.php");

    /// Calculate file name
        $downloadfilename = clean_filename("$course->shortname $strgrades.ods");
    /// Creating a workbook
        $workbook = new MoodleODSWorkbook("-");
    /// Sending HTTP headers
        $workbook->send($downloadfilename);
    /// Adding the worksheet
        $myxls =& $workbook->add_worksheet($strgrades);
    
    /// Print names of all the fields
        $myxls->write_string(0,0,get_string("firstname"));
        $myxls->write_string(0,1,get_string("lastname"));
        $myxls->write_string(0,2,get_string("idnumber"));
        $myxls->write_string(0,3,get_string("institution"));
        $myxls->write_string(0,4,get_string("department"));
        $myxls->write_string(0,5,get_string("email"));
        $pos=6;
        foreach ($columns as $column) {
            $myxls->write_string(0,$pos++,strip_tags($column));
        }
        $myxls->write_string(0,$pos,get_string("total"));
    
    /// Print all the lines of data.
        $i = 0;
        if (!empty($grades)) {
            foreach ($grades as $studentid => $studentgrades) {
                $i++;
                $student = $students[$studentid];
                if (empty($totals[$student->id])) {
                    $totals[$student->id] = '';
                }
        
                $myxls->write_string($i,0,$student->firstname);
                $myxls->write_string($i,1,$student->lastname);
                $myxls->write_string($i,2,$student->idnumber);
                $myxls->write_string($i,3,$student->institution);
                $myxls->write_string($i,4,$student->department);
                $myxls->write_string($i,5,$student->email);
                $j=6;
                foreach ($studentgrades as $grade) {
                    if (is_numeric($grade)) {
                        $myxls->write_number($i,$j++,strip_tags($grade));
                    }
                    else {
                        $myxls->write_string($i,$j++,strip_tags($grade));
                    }
                }
                $myxls->write_number($i,$j,$totals[$student->id]);
            }
        }

    /// Close the workbook
        $workbook->close();
    
        exit;

    } else if ($download == "xls" and confirm_sesskey()) {
        require_once("../lib/excellib.class.php");

    /// Calculate file name
        $downloadfilename = clean_filename("$course->shortname $strgrades.xls");
    /// Creating a workbook
        $workbook = new MoodleExcelWorkbook("-");
    /// Sending HTTP headers
        $workbook->send($downloadfilename);
    /// Adding the worksheet
        $myxls =& $workbook->add_worksheet($strgrades);
    
    /// Print names of all the fields
        $myxls->write_string(0,0,get_string("firstname"));
        $myxls->write_string(0,1,get_string("lastname"));
        $myxls->write_string(0,2,get_string("idnumber"));
        $myxls->write_string(0,3,get_string("institution"));
        $myxls->write_string(0,4,get_string("department"));
        $myxls->write_string(0,5,get_string("email"));
        $pos=6;
        foreach ($columns as $column) {
            $myxls->write_string(0,$pos++,strip_tags($column));
        }
        $myxls->write_string(0,$pos,get_string("total"));
    
    /// Print all the lines of data.
        $i = 0;
        if (!empty($grades)) {
            foreach ($grades as $studentid => $studentgrades) {
                $i++;
                $student = $students[$studentid];
                if (empty($totals[$student->id])) {
                    $totals[$student->id] = '';
                }
        
                $myxls->write_string($i,0,$student->firstname);
                $myxls->write_string($i,1,$student->lastname);
                $myxls->write_string($i,2,$student->idnumber);
                $myxls->write_string($i,3,$student->institution);
                $myxls->write_string($i,4,$student->department);
                $myxls->write_string($i,5,$student->email);
                $j=6;
                foreach ($studentgrades as $grade) {
                    if (is_numeric($grade)) {
                        $myxls->write_number($i,$j++,strip_tags($grade));
                    }
                    else {
                        $myxls->write_string($i,$j++,strip_tags($grade));
                    }
                }
                $myxls->write_number($i,$j,$totals[$student->id]);
            }
        }

    /// Close the workbook
        $workbook->close();
    
        exit;

    } else if ($download == "txt" and confirm_sesskey()) {

/// Print header to force download

        header("Content-Type: application/download\n"); 
        $downloadfilename = clean_filename("$course->shortname $strgrades");
        header("Content-Disposition: attachment; filename=\"$downloadfilename.txt\"");

/// Print names of all the fields

        echo get_string("firstname")."\t".
             get_string("lastname")."\t".
             get_string("idnumber")."\t".
             get_string("institution")."\t".
             get_string("department")."\t".
             get_string("email");
        foreach ($columns as $column) {
            $column = strip_tags($column);
            echo "\t$column";
        }
        echo "\t".get_string("total")."\n";
    
/// Print all the lines of data.
        foreach ($grades as $studentid => $studentgrades) {
            $student = $students[$studentid];
            if (empty($totals[$student->id])) {
                $totals[$student->id] = '';
            }
            echo "$student->firstname\t$student->lastname\t$student->idnumber\t$student->institution\t$student->department\t$student->email";
            foreach ($studentgrades as $grade) {
                $grade = strip_tags($grade);
                echo "\t$grade";
            }
            echo "\t".$totals[$student->id];
            echo "\n";
        }
    
        exit;
    
    }else if ($download == '' and confirm_sesskey()) {
        error("No file type specified");
        exit;
    }
}

function grade_get_stats($category='all') {
    list($grades_by_student, $all_categories) = grade_get_formatted_grades();

    if ($grades_by_student != 0 && $all_categories != 0) {
        switch($category) {
            case 'all':
            {
                //populate the sum of student points, # items and totalpoints for each category
                foreach($grades_by_student as $student=>$categories) {
                    foreach($categories as $cur_category=>$assignments) {
                        if($category != 'student_data') {
                            if (isset($assignments['stats'])) {
                                if (isset($stats[$cur_category]['sum'])) {
                                    $stats[$cur_category]['sum'] = $stats[$cur_category]['sum'] + $assignments['stats']['points'];
                                }
                                else {
                                    $stats[$cur_category]['sum'] = $assignments['stats']['points'];
                                }
                                $stats[$cur_category]['items'] = $assignments['stats']['grade_items'];
                                $stats[$cur_category]['totalpoints'] = $assignments['stats']['totalpoints'];
                                $stats[$cur_category]['weight'] = $all_categories[$cur_category]['stats']['weight'];
                            }
                        }
                    }
                }
                // populate the overall sum,items and totalpoints 
                foreach($stats as $cur_category=>$info) {
                    if($cur_category != 'all' && $cur_category != 'student_data') {
                        
                        if ($stats[$cur_category]['totalpoints'] == get_string('excluded', 'grades')) {
                            $stats[$cur_category]['totalpoints'] = 1;
                            $stats['all']['sum'] = $stats['all']['sum'] + $stats[$cur_category]['sum'];
                            $stats['all']['items']  = $stats['all']['items'] + $stats[$cur_category]['items'];
                            $stats['all']['totalpoints'] = $stats['all']['totalpoints'] + $stats[$cur_category]['totalpoints'];
                            $stats['all']['weighted_sum'] = $stats['all']['weighted_sum'] + ($stats[$cur_category]['sum']/($stats[$cur_category]['totalpoints']))*$stats[$cur_category]['weight'];
                        }
                        
                        else if (isset($stats['all'])) {
                            $stats['all']['sum'] = $stats['all']['sum'] + $stats[$cur_category]['sum'];
                            $stats['all']['items']  = $stats['all']['items'] + $stats[$cur_category]['items'];
                            $stats['all']['totalpoints'] = $stats['all']['totalpoints'] + $stats[$cur_category]['totalpoints'];
                            $stats['all']['weighted_sum'] = $stats['all']['weighted_sum'] + ($stats[$cur_category]['sum']/($stats[$cur_category]['totalpoints']))*$stats[$cur_category]['weight'];
                        }
                        else {
                            $stats['all']['sum'] = $stats[$cur_category]['sum'];
                            $stats['all']['items']  = $stats[$cur_category]['items'];
                            $stats['all']['totalpoints'] = $stats[$cur_category]['totalpoints'];
                            $stats['all']['weighted_sum'] = ($stats[$cur_category]['sum']/($stats[$cur_category]['totalpoints']))*$stats[$cur_category]['weight'];

                        }
                    } 
                }
                $stats['all']['students'] = count($grades_by_student);
                $stats['all']['average'] = $stats['all']['sum'] / $stats['all']['students'];
                $stats['all']['average_weighted'] = $stats['all']['weighted_sum']/$stats['all']['students'];
                
                // calculate the average squared deviation and populate a list of all scores while we're at it
                $stats['all']['avgsqddev'] = 0;
                $stats['all']['avgsqddev_weighted'] = 0;
                foreach($grades_by_student as $student=>$categories) {
                    foreach($categories as $cur_category=>$assignments) {
                        if ($cur_category != 'student_data') {
                            $stats['all']['avgsqddev'] = $stats['all']['avgsqddev'] + pow(($grades_by_student[$student]['student_data']['points']-$stats['all']['average']),2);
                            $stats['all']['avgsqddev_weighted'] = $stats['all']['avgsqddev_weighted'] + pow(($grades_by_student[$student]['student_data']['weighted']-$stats['all']['average_weighted']),2);
                        }
                    }
                    if (isset($stats['all']['all_scores'])) {
                        $stats['all']['all_scores'] .= ','.$grades_by_student[$student]['student_data']['points'];
                        $stats['all']['all_scores_weighted'] .= ','.$grades_by_student[$student]['student_data']['weighted'];
                    }
                    else {
                        $stats['all']['all_scores'] = $grades_by_student[$student]['student_data']['points'];
                        $stats['all']['all_scores_weighted'] = $grades_by_student[$student]['student_data']['weighted'];
                    }
                }
                $stats['all']['avgsqddev']=$stats['all']['avgsqddev']/$stats['all']['students'];
                $stats['all']['avgsqddev_weighted']=$stats['all']['avgsqddev_weighted']/$stats['all']['students'];
                $stats['all']['stddev'] = sqrt($stats['all']['avgsqddev']);
                $stats['all']['stddev_weighted'] = sqrt($stats['all']['avgsqddev_weighted']);
                $stats['all']['mode'] = grade_mode($stats['all']['all_scores']);
                $stats['all']['mode_weighted'] = grade_mode($stats['all']['all_scores_weighted']);
                
                // make sure the mode is not set to every score
                if(count($stats['all']['mode']) == count($grades_by_student)) {
                    $stats['all']['mode'] = get_string('nomode','grade');
                }
                if(count($stats['all']['mode_weighted']) == count($grades_by_student)) {
                    $stats['all']['mode_weighted'] = get_string('nomode','grade');
                }
                break;
            }
            default:
            {
                // get the stats for category
                //populate the sum of student points, # items and totalpoints for each category
                foreach($grades_by_student as $student=>$categories) {
                        if(isset($grades_by_student[$student][$category]['stats'])) {
                            if (isset($stats[$category]['sum'])) {
                                $stats[$category]['sum'] = $stats[$category]['sum'] + $grades_by_student[$student][$category]['stats']['points'];
                            }
                            else {
                                $stats[$category]['sum'] = $grades_by_student[$student][$category]['stats']['points'];
                            }
                            $stats[$category]['items'] = $grades_by_student[$student][$category]['stats']['grade_items'];
                            $stats[$category]['totalpoints'] = $grades_by_student[$student][$category]['stats']['totalpoints'];
                        }
                }
                $stats[$category]['students'] = count($grades_by_student);
                $stats[$category]['average'] = $stats[$category]['sum']/$stats[$category]['students'];
                
                // calculate the average squared deviation and populate a list of all scores too
                $stats[$category]['avgsqddev'] = 0;
                foreach($grades_by_student as $student=>$categories) {
                    foreach($categories as $cur_category=>$assignment) {
                        if ($cur_category != 'student_data') {
                            if ($grades_by_student[$student][$category]['stats']['points'] == '-' || $grades_by_student[$student][$category]['stats']['points'] == get_string('grades','excluded')) {
                                // count grade as a zero
                                $stats[$category]['avgsqddev'] = $stats[$category]['avgsqddev'] + pow(($stats[$category]['average']),2);
                            }
                            else {
                                $stats[$category]['avgsqddev'] = $stats[$category]['avgsqddev'] + pow(($grades_by_student[$student][$category]['stats']['points']-$stats[$category]['average']),2);
                            }
                        }
                    }

                    if (isset($stats[$category]['all_scores'])) {
                        $stats[$category]['all_scores'] .= ','.$grades_by_student[$student][$category]['stats']['points'];
                    }
                    else {
                        $stats[$category]['all_scores'] = $grades_by_student[$student][$category]['stats']['points'];
                    }
                }
                $stats[$category]['avgsqddev'] = $stats[$category]['avgsqddev']/$stats[$category]['students'];
                $stats[$category]['stddev'] = sqrt($stats[$category]['avgsqddev']);
                $stats[$category]['mode'] = grade_mode($stats[$category]['all_scores']);
                break;
            }
        } // end switch
        // do a little cleanup 
        $stats[$category]['stddev'] = sprintf("%0.2f", $stats[$category]['stddev']);
        $stats[$category]['average'] = sprintf("%0.2f", $stats[$category]['average']);
        $stats[$category]['max'] = max(explode(',',$stats[$category]['all_scores'])); 
        $stats[$category]['min'] = min(explode(',',$stats[$category]['all_scores']));
        $stats[$category]['median'] = explode(',',$stats[$category]['all_scores']);
        
        if (isset($stats[$category]['stddev_weighted'])) {
            $stats[$category]['stddev_weighted'] = sprintf("%0.2f", $stats[$category]['stddev_weighted']);
        }
        if (isset($stats[$category]['average_weighted'])) {
            $stats[$category]['average_weighted'] = sprintf("%0.2f", $stats[$category]['average_weighted']);
        }
        if (isset($stats[$category]['max_weighted'])) {
            $stats[$category]['max_weighted'] = max(explode(',',$stats[$category]['all_scores_weighted'])); 
        }
        if (isset($stats[$category]['min_weighted'])) {
            $stats[$category]['min_weighted'] = min(explode(',',$stats[$category]['all_scores_weighted']));
        }
        
        if (isset($stats[$category]['all_scores_weighted'])) {
            $stats[$category]['median_weighted'] = explode(',',$stats[$category]['all_scores_weighted']);
        }
        else {
        
        }

        
        sort($stats[$category]['median']);

        if (count($stats[$category]['median'])/2 == floor(count($stats[$category]['median'])/2) ) {
            // even number of scores
            $temp = $stats[$category]['median'][count($stats[$category]['median'])/2-1] + $stats[$category]['median'][count($stats[$category]['median'])/2];
            $temp = $temp/2;
        }
        else {
            // odd number of scores
            $temp = $stats[$category]['median'][floor(count($stats[$category]['median'])/2)];
        }        
        unset($stats[$category]['median']);
        $stats[$category]['median'] = $temp;
        
        if (isset($stats[$category]['median_weighted'])) {
            if (count($stats[$category]['median_weighted'])/2 == floor(count($stats[$category]['median_weighted'])/2)) {
                // even number of scores
                $temp = $stats[$category]['median_weighted'][count($stats[$category]['median_weighted'])/2-1] + $stats[$category]['median_weighted'][count($stats[$category]['median_weighted'])/2+1];
                $temp = $temp/2;
            }
            else {
                // odd number of scores
                $temp = $stats[$category]['median_weighted'][floor(count($stats[$category]['median_weighted'])/2)];
            }         
            unset($stats[$category]['median_weighted']);
            $stats[$category]['median_weighted'] = $temp;
        }
    }
    return $stats;
}

// returns a comma seperated list of the most common values in $items, $items is expected to be a comma sperated list of numbers
function grade_mode($items) {
    $all_scores = explode(',',$items);
    foreach($all_scores as $value) {
        if (isset($frequency[$value])) {
            $frequency[$value]++;
        }
        else {
            $frequency[$value] = 1;
        }
    }
    $max = max($frequency);
    foreach($frequency as $key=>$value) {
        if ($value == $max) {
            if (isset($retval)) {
                $retval .= ', '.$key;
            }
            else {
                $retval = $key;
            }
        }
    }
    return $retval;
}


function grade_stats() {
    global $CFG;
    global $course;
    global $USER;
    global $preferences;

    if (!isset($category)) {
        $category = clean_param($_REQUEST['category'], PARAM_CLEAN);
    }
    
    $stats = grade_get_stats($category);

    // output our data    
    print_header();
    echo  '<table align="center"><tr><th colspan="3" scope="col">'.$category.' '.get_string('stats','grades').'</th></tr>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<tr><th scope="col">&nbsp;</th><th scope="col">'.get_string('points','grades').'<th scope="col">'.get_string('weight','grades').'</th></tr>';            
    }

    echo  '<tr><td align="right">'.get_string('max','grades').':</td><td align="right">'.$stats[$category]['max'].'</td>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<td align="right">'.$stats[$category]['max_weighted'].'</td>';            
    }
    echo  '</tr>';
    
    echo  '<tr><td align="right">'.get_string('min','grades').':</td><td align="right">'.$stats[$category]['min'].'</td>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<td align="right">'.$stats[$category]['min_weighted'].'</td>';            
    }
    echo  '</tr>';
    
    echo  '<tr><td align="right">'.get_string('average','grades').':</td><td align="right">'.$stats[$category]['average'].'</td>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<td align="right">'.$stats[$category]['average_weighted'].'</td>';            
    }
    echo  '</tr>';
    
    echo  '<tr><td align="right">'.get_string('median','grades').':</td><td align="right">'.$stats[$category]['median'].'</td>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<td align="right">'.$stats[$category]['median_weighted'].'</td>';            
    }
    echo  '</tr>';
    
    echo  '<tr><td align="right">'.get_string('mode','grades').':</td><td align="right">'.$stats[$category]['mode'].'</td>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<td align="right">'.$stats[$category]['mode_weighted'].'</td>';            
    }
    echo  '</tr>';
    
    echo  '<tr><td align="right">'.get_string('standarddeviation','grades').':</td><td align="right">'.$stats[$category]['stddev'].'</td>';
    if ($preferences->show_weighted == 1 && $preferences->use_weighted_for_letter == 1 && $category== 'all') {
        echo  '<td align="right">'.$stats[$category]['stddev_weighted'].'</td>';            
    }
    echo  '</tr>';
    echo  '</table>';
    print_footer();
}

function grade_view_category_grades($view_by_student) {
    global $CFG;
    global $course;
    global $USER;
    global $preferences;

    $context = get_context_instance(CONTEXT_COURSE, $course->id);
    
    // if can't see course grades, print single grade view
    if (!has_capability('moodle/course:viewcoursegrades', $context)) {
        $view_by_student = $USER->id;
    }

    if ($preferences->use_advanced == 0) {
        $cview = UNCATEGORISED;
    }
    else {
        $cview=clean_param($_REQUEST['cview'], PARAM_CLEAN);
    }
    
    if ($cview) {
        list($grades_by_student, $all_categories) = grade_get_formatted_grades();

        if ($grades_by_student != 0 && $all_categories != 0) {
            // output a form for the user to download the grades.
            grade_download_form();

            if ($view_by_student != -1) {
                // unset all grades except for this student
                foreach ($grades_by_student as $student=>$junk) {
                    if($student != $view_by_student) {
                        unset($grades_by_student[$student]);
                    }
                }
            }

            $grade_columns = $preferences->show_weighted + $preferences->show_points + $preferences->show_percent;

            $first = 0;
            //$maxpoints = 0;
            $maxpercent = 0;
            $reprint = 0;
            if (has_capability('moodle/course:viewcoursegrades', $context)) {
                $student_heading_link = get_string('student','grades');
                //only set sorting links if more than one student displayed.
                if ($view_by_student == -1) {
                    $student_heading_link .='<br /><a href="?id='.$course->id.'&amp;action=vcats&amp;cview='.$cview.'&amp;sort=lastname">'.get_string('sortbylastname','grades').'</a>';
                    $student_heading_link .= '<br /><a href="?id='.$course->id.'&amp;action=vcats&amp;cview='.$cview.'&amp;sort=firstname">'.get_string('sortbyfirstname','grades').'</a>';
                }
                else {
                    $student_heading_link .= '<br /><a href="?id='.$course->id.'&amp;action=vcats&amp;cview='.$cview.'">'.get_string('showallstudents','grades').'</a>';
                }
            }
            echo '<table align="center" class="grades">';
            if (has_capability('moodle/course:viewcoursegrades', $context)) {
                $header = '<tr class="header"><th rowspan="2" scope="col">'.$student_heading_link.'</th>';
            }
            else {
                $header = '<tr class="header">';
            }
            $header1 = '<tr class="header">';
            
            // to keep track of what we've output
            $colcount = 0;
            $oddrow = true;
            $reprint = 0;
            
            // this next section is to display the items in the course order
            foreach($grades_by_student as $student => $categories) {
                if (isset($item_order)) {
                    // we already have the sort order let's jump out
                    break;
                }
                $item_order = array();
                foreach($categories as $category => $items) {
                    if ($category == $cview) {
                        foreach ($items as $assignment=>$points) {
                            if ($assignment != 'stats') {
                                $temp = $points['sort_order'];
                                $item_order[$temp] = $assignment;
                            }
                        }
                    }
                }
            }
            /// Make sure $item_order is initialised (bug 3424)
            if (empty($item_order)) $item_order = array();
            
            ksort($item_order);
            
            foreach($grades_by_student as $student => $categories) {
                
                if ($preferences->reprint_headers != 0 && $reprint >= $preferences->reprint_headers) {
                    echo  $header.$header1.'</tr>';
                    $reprint=0;
                }
                
                // alternate row classes
                $row = ($oddrow) ? '<tr class="r0">' : '<tr class="r1">';
                $oddrow = !$oddrow;

                // reset the col classes
                $oddcol = true;

                    
                // set the links to student information based on multiview or individual... if individual go to student info... if many go to individual grades view.
                if (has_capability('moodle/course:viewcoursegrades', $context)) {
                    if ($view_by_student != -1) {
                        $student_link = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$student.'&amp;course='.$course->id.'">';
                    }
                    else {
                        $student_link = '<a href="?id='.$course->id.'&amp;action=vcats&amp;user='.$student.'&amp;cview='.$cview.'">';
                    }
                    $student_link .= $grades_by_student[$student]['student_data']['lastname'].', '.$grades_by_student[$student]['student_data']['firstname'].'</a>';
                    $row .= '<th class="fullname" scope="row">'.$student_link.'</th>';
                }
                
                foreach($categories as $category => $items) {
                    if ($category == $cview) {
                        // make sure that the grades come out in the same order
                        foreach($item_order as $order=>$assignment) {
                            
                            $class = $all_categories[$category][$assignment]['modname'];

                            if ($assignment != 'stats') {
                                    
                                if ($first == 0) {
                                    $colcount++;
                                    $link_id = grade_get_module_link($course->id, $all_categories[$category][$assignment]['cminstance'], $all_categories[$category][$assignment]['modid']);

                                    $link = $CFG->wwwroot.'/mod/'.$all_categories[$category][$assignment]['modname'].'/view.php?id='.$link_id->id;
                                    $all_categories[$category][$assignment]['link'] = $link;
                                    if ($all_categories[$category][$assignment]['hidden'] == 0) {
                                        $header .= '<th class="'.$class.'" colspan="'.$grade_columns.'" scope="col"><a href="'.$link.'">'.format_string($assignment,true).'</a>';
                                    }
                                    else {
                                        $header .= '<th class="'.$class.'" colspan="'.$grade_columns.'" scope="col"><a class="dimmed" href="'.$link.'">'.format_string($assignment,true).'</a>';
                                    }
                                    if ($all_categories[$category][$assignment]['extra_credit'] == 1) {
                                        $header .= '<span class="extracredit">('.get_string('extracredit','grades').')</span>'; 
                                    }
                                    $header .='</th>';
                                    if ($preferences->show_points) {
                                        $header1 .= '<th class="'.$class.'" scope="col">'. $all_categories[$category][$assignment]['maxgrade'];
                                        if ($all_categories[$category][$assignment]['grade_against'] != $all_categories[$category][$assignment]['maxgrade']) {
                                            $header1 .= '('. $all_categories[$category][$assignment]['grade_against'].')';
                                        }
                                        $header1 .= '</th>';
                                    }
                                                                        
                                    if($preferences->show_percent)    {
                                        if ($all_categories[$category][$assignment]['grade_against'] != $all_categories[$category][$assignment]['maxgrade']) {
                                            $header1 .= '<th class="'.$class.'" scope="col">'.get_string('scaledpct','grades').'</th>';
                                        }
                                        else {
                                            $header1 .= '<th class="'.$class.'" scope="col">'.get_string('rawpct','grades').'</th>';
                                        }
                                    }
                                    if ($preferences->show_weighted) {
                                        if ($all_categories[$category]['stats']['totalpoints'] != 0) {
                                            $cur_weighted_max = sprintf("%0.2f", $all_categories[$category][$assignment]['grade_against']/$all_categories[$category]['stats']['totalpoints']*$all_categories[$category]['stats']['weight']);
                                        }
                                        else {
                                            $cur_weighted_max = 0;
                                        }
                                        $header1 .= '<th scope="col">'.$cur_weighted_max.get_string('pctoftotalgrade','grades').'</th>';
                                    }
                                }

                                // display points 
                                if ($preferences->show_points) { 
                                    $class .= ($oddcol) ? ' c0 points' : ' c1 points';
                                    $oddcol = !$oddcol;
                                    $row .= '<td class="'.$class.'"><a href="'.$all_categories[$category][$assignment]['link'].'">' . $items[$assignment]['grade'] . '</a></td>';
                                }

                                if ($preferences->show_percent) {
                                    $class .= ($oddcol) ? ' c0 percent' : ' c1 percent';
                                    $oddcol = !$oddcol;
                                    $row .= '<td class="'.$class.'">'. $items[$assignment]['percent'].'%</td>';
                                }
                                
                                if ($preferences->show_weighted) {
                                    $class .= ($oddcol) ? ' c0 weighted' : ' c1 weighted';
                                    $oddcol = !$oddcol;
                                    $row .= '<td class="'.$class.'">'.$items[$assignment]['weighted'].'%</td>';
                                }
                            }
                        }
                    } else {
                        $class = '';
                    }
                }
                
                if ($first == 0) {
                    if (has_capability('moodle/course:viewcoursegrades', $context) && $view_by_student == -1) {
                        $total_sort_link = '<a href="?id='.$course->id.'&amp;action=vcats&amp;cview='.$cview.'&amp;sort=highgrade_category"><img src="'.$CFG->wwwroot.'/pix/t/down.gif" alt="'.get_string('highgradedescending','grades').'" /></a>';
                        $total_sort_link .= '<a href="?id='.$course->id.'&amp;action=vcats&amp;cview='.$cview.'&amp;sort=highgrade_category_asc"><img src="'.$CFG->wwwroot.'/pix/t/up.gif" alt="'.get_string('highgradeascending','grades').'" /></a>';
                    }
                    else {
                        $total_sort_link = '';
                    }
                    
                    $stats_link = '<a href="javascript:void(0)" onclick="window.open(\'?id='.$course->id.'&amp;action=stats&amp;category='.$cview.'\',\''.get_string('statslink','grades').'\',\'height=200,width=300,scrollbars=no\')">'.get_string('statslink','grades').'</a>';
                    if ($all_categories[$cview]['stats']['drop'] != 0) {
                        $header .= '<th class="'.$class.'" colspan="'.$grade_columns.'" scope="col">'.get_string('total','grades').'&nbsp; (Lowest '. $all_categories[$cview]['stats']['drop']. ' Dropped)'.$total_sort_link.' '.$stats_link.'</th>';
                    }
                    else {
                        $header .= '<th class="'.$class.'" colspan="'.$grade_columns.'" scope="col">'.get_string('total','grades').'&nbsp;'.$total_sort_link.' '.$stats_link.'</th>';
                    }
                    
                    if ($preferences->show_points) {
                        $header1 .= '<th class="'.$class.'" scope="col">'.$all_categories[$cview]['stats']['totalpoints'];
                        if ($all_categories[$cview]['stats']['bonus_points'] != 0) {
                            $header1 .='(+'.$all_categories[$cview]['stats']['bonus_points'].')';
                        }
                        $header1 .='</th>';
                    }
                    if ($preferences->show_percent) {
                        $header1 .= '<th class="'.$class.'" scope="col">'.get_string('percent','grades').'</th>';
                    }
                    
                    
                    if ($preferences->show_weighted) {
                        $header1 .= '<th class="'.$class.'" scope="col">'.$all_categories[$cview]['stats']['weight'].get_string('pctoftotalgrade','grades').'</th>';
                    }
                    
                    if (has_capability('moodle/course:viewcoursegrades', $context)) {
                        $header .= '<th rowspan="2" scope="col">'.$student_heading_link.'</th></tr>';
                    }
                    else {
                        $header .= '</tr>';
                    }
                    
                    //adjust colcount to reflect the actual number of columns output
                    $colcount++; // total column
                    $colcount = $colcount*$grade_columns + 2;
                    echo  '<tr class="title"><th colspan="'.$colcount.'" scope="col">';
                    if ($preferences->use_advanced != 0) {
                        echo  $cview.' '.get_string('grades','grades');
                    }
                    else {
                        echo  get_string('grades','grades');
                    }

                    if (has_capability('moodle/course:viewcoursegrades', $context)) {
                        helpbutton('teacher', get_string('gradehelp','grades'), 'grade');
                    }
                    else {
                        helpbutton('student', get_string('gradehelp','grades'), 'grade');
                    }
                    echo  '</th></tr>';
                    echo  $header;
                    echo  $header1.'</tr>';
                    $first = 1;
                }

                // total points for category
                if ($preferences->show_points) {
                    $class .= ($oddcol) ? ' c0 points' : ' c1 points';
                    $oddcol = !$oddcol;
                    $row .= '<td class="'.$class.'">'.$grades_by_student[$student][$cview]['stats']['points'].'</td>';
                }
                
                // total percent for category
                if ($preferences->show_percent) {
                    $class .= ($oddcol) ? ' c0 percent' : ' c1 percent';
                    $oddcol = !$oddcol;
                    $row .= '<td class="'.$class.'">'.$grades_by_student[$student][$cview]['stats']['percent'].'%</td>';
                }
                

                // total weighted for category
                if ($preferences->show_weighted) {
                    $class .= ($oddcol) ? ' c0 weighted' : ' c1 weighted';
                    $oddcol = !$oddcol;
                    $row .= '<td class="'.$class.'">'.$grades_by_student[$student][$cview]['stats']['weighted'].'%</td>';
                }

                if (has_capability('moodle/course:viewcoursegrades', $context)) {
                    $row .= '<td class="fullname">'.$student_link.'</td>';
                }
                $row .= '</tr>';
                echo  $row;
                $reprint++;
            }
            echo  '</table>';
        }
        else { // no grades returned
            error(get_string('nogradesreturned','grades'), $CFG->wwwroot.'/course/view.php?id='.$course->id);
        }
    }
    else {
        error(get_string('nocategoryview','grades'), $CFG->wwwroot.'/course/view.php?id='.$course->id);
    }
}

function grade_view_all_grades($view_by_student) { // if mode=='grade' then we are in user view
// displays all grades for the course
    global $CFG;
    global $course;
    global $USER;
    global $preferences;
    
    if (!$context = get_context_instance(CONTEXT_COURSE, $course->id)) {
        return false;
    }

    // if can't see course grades, print single grade view
    if (!has_capability('moodle/course:viewcoursegrades', $context)) {
        $view_by_student = $USER->id;
    }
    
    list($grades_by_student, $all_categories) = grade_get_formatted_grades();

    if ($grades_by_student != 0 && $all_categories != 0) {
      
        // output a form for the user to download the grades.  
        grade_download_form();
        
        if ($view_by_student != -1) {
            // unset all grades except for this student
            foreach ($grades_by_student as $student=>$junk) {
                if($student != $view_by_student) {
                    unset($grades_by_student[$student]);
                }
            }
        }
        
        $grade_columns = $preferences->show_weighted + $preferences->show_points + $preferences->show_percent;
        
        $first = 0;
        $total_course_points = 0;
        $maxpercent = 0;
        $reprint=0;
        
        echo  '<table align="center" class="grades">';
        if (has_capability('moodle/course:viewcoursegrades', $context)) {
            $student_heading_link = get_string('student','grades');
            if ($view_by_student == -1) {
                $student_heading_link .='<a href="?id='.$course->id.'&amp;action=grades&amp;sort=lastname"><br /><font size="-2">'.get_string('sortbylastname','grades').'</font></a>';
                $student_heading_link .= '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=firstname"><br /><font size="-2">'.get_string('sortbyfirstname','grades').'</font></a>';
            }
            else {
                $student_heading_link .= '<br /><a href="?id='.$course->id.'&amp;&amp;action=grades"><font size="-2">'.get_string('showallstudents','grades').'</font></a>';
            }
            $header = '<tr><th rowspan="2" scope="col">'.$student_heading_link.'</th>';
        }
        else {
            $header = '</tr>';
        }
        $header1 = '<tr>';
        
        $rowcount = 0;
        $oddrow = true;
        $colcount = 0;
  
        foreach($grades_by_student as $student => $categories) {
 
            $totalpoints = 0;
            $totalgrade = 0;
            $total_bonus_points = 0;
            if ($preferences->reprint_headers != 0 && $reprint >= $preferences->reprint_headers) {
                echo  $header.$header1;
                $reprint=0;
            }
            
            // alternate row classes
            $row = ($oddrow) ? '<tr class="r0">' : '<tr class="r1">';
            $oddrow = !$oddrow;
            
            // set the links to student information based on multiview or individual... if individual go to student info... if many go to individual grades view.
            if (has_capability('moodle/course:viewcoursegrades', $context)) {
                if ($view_by_student != -1) {
                    $studentviewlink = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$student.'&amp;course='.$course->id.'">'.$grades_by_student[$student]['student_data']['lastname'].', '.$grades_by_student[$student]['student_data']['firstname'].'</a>';
                }
                else {
                    $studentviewlink = '<a href="?id='.$course->id.'&amp;action=view_student_grades&amp;user='.$student.'">'.$grades_by_student[$student]['student_data']['lastname'].', '.$grades_by_student[$student]['student_data']['firstname'].'</a>';
                }
                $row .= '<th scope="row">'. $studentviewlink .'</th>';
            }
            ksort($categories);

            foreach($categories as $category => $items) {
                if ($category != 'student_data') {
                   
                    if ($first == 0) {
                        $colcount++;
                        if ($category == UNCATEGORISED) {
                            $categoryname = get_string(UNCATEGORISED, 'grades');
                        } else {
                            $categoryname = $category;
                        }
                        // only print the category headers if something is displayed for them
                        if ($preferences->show_weighted || $preferences->show_percent || $preferences->show_points) {
                            $stats_link = '<a href="javascript:void(0)" onclick="window.open(\'?id='.$course->id.'&amp;action=stats&amp;category='.$category.'\',\''.get_string('statslink','grades').'\',\'height=200,width=300,scrollbars=no\')"><font size="-2">'.get_string('statslink','grades').'</font></a>';
                            $header .= '<th colspan="'.$grade_columns.'" scope="col"><a href="?id='.$course->id.'&amp;action=vcats&amp;cview='.$category;
                            if ($view_by_student != -1) {
                                $header .= '&amp;user='.$view_by_student;
                            }
                            $header .='">'. $categoryname .' '.$stats_link.'</a>';
                        }
                        if ($preferences->display_weighted != 0) {
                            $header .= '('. $all_categories[$category]['stats']['weight'] . '%)';
                        }
                        $header .= '</th>';
                        if ($preferences->show_points) {
                            $header1 .= '<th scope="col">'.get_string('points','grades').'('.$all_categories[$category]['stats']['totalpoints'].')';
                            if ($all_categories[$category]['stats']['bonus_points'] != 0) {
                                $header1 .='(+'.$all_categories[$category]['stats']['bonus_points'].')';
                            }
                            $header1 .='</th>';
                        }
                        if ($preferences->show_percent) {
                            $header1 .= '<th scope="col">'.get_string('percent','grades').'</th>';
                        }
                        if ($preferences->show_weighted) {
                            $header1 .= '<th scope="col">'.get_string('weightedpctcontribution','grades').'</th>';
                        }
                        $maxpercent = $all_categories["$category"]['stats']['weight'] + $maxpercent;
                        //$total_course_points = $all_categories[$category]['stats']['totalpoints']+ $total_course_points;
                        //$total_course_points = $all_categories[$category]['stats']['totalpoints']+ $total_course_points;
                    }
                    
                    if ($preferences->show_points) {
                        $row .= '<td align="right">' . $items['stats']['points'] . '</td>';
                    }
                    if ($preferences->show_percent) {
                        $row .= '<td align="right">'. $items['stats']['percent'].'%</td>';
                    }

                    if ($preferences->show_weighted) {
                        $row .= '<td align="right">'. $items['stats']['weighted'] . '%</td>';
                    }
                    $total_bonus_points = $all_categories[$category]['stats']['bonus_points'];
                } 
            }
            if ($first == 0) {
                if ($preferences->show_letters) {
                    $total_columns = $grade_columns + 1;
                }
                else {
                    $total_columns = $grade_columns;
                }
                
                if (has_capability('moodle/course:viewcoursegrades', $context) && $view_by_student == -1) {
                    $grade_sort_link = '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=highgrade"><img src="'.$CFG->wwwroot.'/pix/t/down.gif" alt="'.get_string('highgradedescending','grades').'" /></a>';
                    $grade_sort_link .= '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=highgrade_asc"><img src="'.$CFG->wwwroot.'/pix/t/up.gif" alt="'.get_string('highgradeascending','grades').'" /></a>';
                    $points_sort_link = '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=points"><img src="'.$CFG->wwwroot.'/pix/t/down.gif" alt="'.get_string('pointsdescending','grades').'" /></a>';
                    $points_sort_link .= '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=points_asc"><img src="'.$CFG->wwwroot.'/pix/t/up.gif" alt="'.get_string('pointsascending','grades').'" /></a>';
                    $weighted_sort_link = '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=weighted"><img src="'.$CFG->wwwroot.'/pix/t/down.gif" alt="'.get_string('weighteddescending','grades').'" /></a>';
                    $weighted_sort_link .= '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=weighted_asc"><img src="'.$CFG->wwwroot.'/pix/t/up.gif" alt="'.get_string('weightedascending','grades').'" /></a>';
                    $percent_sort_link = '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=percent"><img src="'.$CFG->wwwroot.'/pix/t/down.gif" alt="'.get_string('percentdescending','grades').'" /></a>';
                    $percent_sort_link .= '<a href="?id='.$course->id.'&amp;action=grades&amp;sort=percent_asc"><img src="'.$CFG->wwwroot.'/pix/t/up.gif" alt="'.get_string('percentascending','grades').'" /></a>';
                }
                $stats_link = '<a href="javascript:void(0)" onclick="window.open(\'?id='.$course->id.'&amp;action=stats&amp;category=all\',\''.get_string('statslink','grades').'\',\'height=200,width=300,scrollbars=no\')"><font size="-2">'.get_string('statslink','grades').'</font></a>';
                $header .= '<th colspan="'.$total_columns.'" scope="col">'.get_string('total','grades').'&nbsp;'.$stats_link.'</th>';
                if (has_capability('moodle/course:viewcoursegrades', $context) && $view_by_student == -1) {
                    if ($preferences->show_points) {
                        $header1 .= '<th scope="col">'.get_string('points','grades').'('.$all_categories['stats']['totalpoints'].')';
                        if ($category != 'student_data' && $all_categories[$category]['stats']['bonus_points'] != 0) {

                            $header1 .='(+'.$total_bonus_points.')';
                        }
                        $header1 .= '<br />'.$points_sort_link.' '.'</th>';
                    }
                    if ($preferences->show_percent) {
                        $header1 .= '<th scope="col">'.get_string('percentshort','grades').'<br />'.$percent_sort_link.' '.'</th>';
                    }
                    if ($preferences->show_weighted) {
                        $header1 .= '<th scope="col">'.get_string('weightedpct','grades').'('.$all_categories['stats']['weight'].')'.'<br />'.$weighted_sort_link.' '.'</th>';
                    }
                    if ($preferences->show_letters) {
                        $header1 .= '<th scope="col">'.get_string('lettergrade','grades').'<br />'.$grade_sort_link.' '.'</th>';
                    }
                    $header1 .= '</tr>';
                }
                else {
                    if ($preferences->show_points) {
                        $header1 .= '<th scope="col">'.get_string('points','grades').'('.$all_categories['stats']['totalpoints'].')';
                        if ($category != 'student_data' && $all_categories[$category]['stats']['bonus_points'] != 0) {
                            $header1 .='(+'.$total_bonus_points.')';
                        }
                        $header1 .= '</th>';
                    }
                    if ($preferences->show_percent) {
                        $header1 .= '<th scope="col">'.get_string('percentshort','grades').'</th>';
                    }
                    if ($preferences->show_weighted) {
                        $header1 .= '<th scope="col">'.get_string('weightedpct','grades').'('.$all_categories['stats']['weight'].')</th>';
                    }
                    if ($preferences->show_letters) {
                        $header1 .= '<th scope="col">'.get_string('lettergrade','grades').'</th>';
                    }
                    $header1 .= '</tr>';
                }
                if (has_capability('moodle/course:viewcoursegrades', $context)) {
                    $header .= '<th rowspan="2" scope="col">'.$student_heading_link.'</th></tr>';
                }
                // adjust colcount to reflect actual number of columns output
                $colcount = $colcount * $grade_columns + $total_columns + 2;
  
                echo  '<tr><th colspan="'.$colcount.'" scope="col"><font size="+1">'.get_string('allgrades','grades').'</font>';
                if (has_capability('moodle/course:viewcoursegrades', $context)) {
                    helpbutton('teacher', get_string('gradehelp','grades'), 'grade');
                }
                else {
                    helpbutton('student', get_string('gradehelp','grades'), 'grade');
                }
                echo  '</th></tr>';
                
                                
                echo  $header;
                echo  $header1;
                $first = 1;
            }
            if ($preferences->show_points) {
                $row .= '<td align="right">'.$grades_by_student[$student]['student_data']['points'].'</td>';
            }
            if ($preferences->show_percent) {
                $row .= '<td align="right">'.$grades_by_student[$student]['student_data']['percent'].'%</td>';
            }
            if ($preferences->show_weighted) {
                $row .= '<td align=right>'.$grades_by_student[$student]['student_data']['weighted'].'%</td>';
            }
            if ($preferences->show_letters) {
                if ($preferences->use_weighted_for_letter == 1) {
                    $grade = $grades_by_student[$student]['student_data']['weighted'];
                }
                else {
                    $grade = $grades_by_student[$student]['student_data']['percent'];
                }
                $letter_grade = grade_get_grade_letter($course->id, $grade);
                if ($letter_grade) {
                    $row .= '<td align="right">'.$letter_grade->letter.'</td>';
                }
                else {
                    // there wasn't an appropriate entry to use in the gradebook.
                    if (grade_letters_set($course->id)) {
                        $row .= '<td align="right">'.get_string('nolettergrade','grades').' '.$grade.'</td>';
                    }
                    else {
                        $row .= '<td align="right">'.get_string('nogradeletters','grades').'</td>';
                    }
                }
            }
            if (has_capability('moodle/course:viewcoursegrades', $context)) {
                $row .= '<td>'. $studentviewlink .'</td></tr>';
            }
            else {
                $row .= '</tr>';
            }

            echo $row;           
            $reprint++;
        }
        echo  '</table>';
    }
    else { // no grades returned
        error(get_string('nogradesreturned','grades'));
    }
}


function grade_set_grade_weights() {
// set the grade weights as submitted from the form generated by display_grade_weights    
    global $CFG;
    global $course;
    global $USER;
    
    if (!empty($USER->id)) {
        if (!confirm_sesskey()) {
            error(get_string('confirmsesskeybad', 'error'));
        }
    }
    
    // get list of all categories
    $categories = get_records('grade_category', 'courseid', $course->id);
    if ($categories) {
        foreach ($categories as $category) {
            $form_catname = preg_replace('/[.| ]/', '_', $category->name);

            $submitted_category = optional_param($form_catname);    
            if (is_numeric($submitted_category)) {
                // see if there is a weight if so see if it needs to be updated
                $weight = grade_get_category_weight($course->id, $category->name);
                if ($weight) {
                    if ($weight->weight != $submitted_category)
                    {                        
                        set_field('grade_category', 'weight', $submitted_category, 'id', $weight->id);
                    }
                    
                    $cur_drop = optional_param("drop_x_lowest$form_catname");
                    $cur_bonus_points = optional_param("bonus_points$form_catname");
                    $cur_hidden = optional_param("hidden$form_catname");
                    if ($cur_hidden) {
                        $cur_hidden = true;
                    }
                    else {
                        $cur_hidden = false;
                    }
                    
                    if ($weight->drop_x_lowest != $cur_drop) {
                        set_field('grade_category', 'drop_x_lowest', $cur_drop, 'id', $weight->cat_id);
                    }
                    if ($weight->bonus_points != $cur_bonus_points) {
                        set_field('grade_category', 'bonus_points', $cur_bonus_points, 'id', $weight->cat_id);
                    }
                    if ($cur_hidden) {
                        set_field('grade_category', 'hidden', 1, 'id', $weight->cat_id);
                    }
                    else {
                        set_field('grade_category', 'hidden', 0, 'id', $weight->cat_id);
                    }
                }
                else {
                    // insert the new record... we shouldn't reach this point anymore
                    //$new_weight->course = $course->id;
                    //$new_weight->category = $category->id;
                    //$new_weight->weight = $submitted_category;
                    //insert_record('grade_weight', $new_weight);
                }
            }
            else {
                echo  '<center><font color="red">'.get_string('nonumericweight','grades').
                        format_string($category->name) .': "'.$submitted_category.'"</font></center><br />';
            }
        }
    }
}

function grade_display_grade_weights() {
// get all categories with weights
// then set and display that entry.
    global $CFG;
    global $course;
    global $USER;
    
    $categories = get_records('grade_category', 'courseid', $course->id);
    if ($categories) {
        echo  '<form id="grade_weights" action="./index.php" method="post">';
        echo  '<table border="0" cellspacing="2" cellpadding="5" align="center" class="generalbox">';
        echo  '<tr><th colspan="5" class="header" scope="col">'.get_string('setweights','grades');
        helpbutton('weight', get_string('gradeweighthelp','grades'), 'grade');
        echo  '</th></tr>';
        echo  '<tr><td align="center" class="generaltableheader">'.get_string('category','grades').'</td>';
        echo  '<td align="center" class="generaltableheader">'.get_string('weight','grades').'</td>';
        echo  '<td align="center" class="generaltableheader">'.get_string('dropxlowest','grades').'</td>';
        echo  '<td align="center" class="generaltableheader">'.get_string('bonuspoints','grades').'</td>';
        echo  '<td align="center" class="generaltableheader">'.get_string('hidecategory','grades').'</td>';
        echo  '</tr>';
        echo  '<input type="hidden" name="id" value="'.$course->id.'" />';
        echo  '<input type="hidden" name="action" value="set_grade_weights" />';
        echo  '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';

        $sum = 0;
        
        foreach($categories as $category) {
            $val = $category->weight;
            $sum = $sum + $val;
                        
            // make names form safe
            $form_catname = str_replace(' ', '_', $category->name);
            if ($category->name == UNCATEGORISED) {
                $category->name = get_string(UNCATEGORISED, 'grades');
            }
            echo  '<tr><td align="center" class="generalboxcontent">'. format_string($category->name) .'</td>';
            echo  '<td align="center" class="generalboxcontent"><input type="text" size="5" name="'.$form_catname.'" value="'.$val.'" /></td>';
            echo  '<td align="center" class="generalboxcontent"><input type="text" size="5" name="drop_x_lowest'.$form_catname.'" value="'.$category->drop_x_lowest.'" /></td>';
            echo  '<td align="center" class="generalboxcontent"><input type="text" size="5" name="bonus_points'.$form_catname.'" value="'.$category->bonus_points.'" /></td>';
            echo  '<td align="center" class="generalboxcontent"><input type="checkbox" name="hidden'.$form_catname.'" ';
            if ($category->hidden == 1) {
                echo  ' checked="checked"';
            }
            echo  ' /></td></tr>';
        }
        echo  '<tr><td colspan="5" align="center" class="generalboxcontent">';
        echo  '<input type="submit" value="'.get_string('savechanges','grades').'" />';
        echo  '</td></tr>';
        if ($sum != 100) {
            echo  '<tr><td colspan="5" align="center" class="generalboxcontent"><font color="red">'.get_string('totalweightnot100','grades').'</font></td></tr>';
        }
        else {
            echo  '<tr><td colspan="5" align="center" class="generalboxcontent"><font color="green">'.get_string('totalweight100','grades').'</font></td></tr>';
        }
    }
    else {
        /// maybe this should just do the default population of the categories instead?
        echo  '<font color="red">'.get_string('setcategorieserror','grades').'</font>';
    }
    echo  '</table>';
    echo  '</form>';
    echo  '<center>'.get_string('dropxlowestwarning','grades').'</center><br />';
}

function grade_set_categories() {
    global $CFG;
    global $course;
    global $USER;
    

    /// Collect modules data
    get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
    
    echo  '<table border="0" cellspacing="2" cellpadding="5" align="center" class="generalbox">';
    echo  '<tr><th colspan="5" class="header" scope="col">'.get_string('setcategories','grades');
    helpbutton('category', get_string('gradecategoryhelp','grades'), 'grade');
    echo  '</th></tr>';
    echo  '<tr><td align="center" class="generaltableheader">'.get_string('gradeitem','grades').'</td>';
    echo  '<td align="center" class="generaltableheader">'.get_string('category','grades').'</td>';
    echo  '<td align="center" class="generaltableheader">'.get_string('maxgrade','grades').'</td>';
    echo  '<td align="center" class="generaltableheader">'.get_string('curveto','grades').'</td>';
    echo  '<td align="center" class="generaltableheader">'.get_string('extracredit','grades').'</td></tr>';
    echo  '<form id="set_categories" method="post" action="./index.php" >';
    echo  '<input type="hidden" name="action" value="assign_categories" />';
    echo  '<input type="hidden" name="id" value="'.$course->id.'" />';
    echo  '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
    
    $itemcount = 0;
    
    /// Search through all the modules, pulling out grade data
    $sections = get_all_sections($course->id); // Sort everything the same as the course
    for ($i=0; $i<=$course->numsections; $i++) {
        if (isset($sections[$i])) {   // should always be true
            $section = $sections[$i];
            if ($section->sequence) {
                $sectionmods = explode(",", $section->sequence);
                foreach ($sectionmods as $sectionmod) {
                    if (empty($mods[$sectionmod])) {
                        continue;
                    }
                    $mod = $mods[$sectionmod];
                    $instance = get_record("$mod->modname", "id", "$mod->instance");
                    $libfile = "$CFG->dirroot/mod/$mod->modname/lib.php";
                    if (file_exists($libfile)) {
                        require_once($libfile);
                        $gradefunction = $mod->modname."_grades";
                        if (function_exists($gradefunction)) {   // Skip modules without grade function
                            if ($modgrades = $gradefunction($mod->instance)) {
                                
                                if ($modgrades->maxgrade != '')
                                // this block traps out broken modules that don't return a maxgrade according to the moodle API
                                {
                                    $itemcount++;
                                    //modgrades contains student information with associated grade
                                    //echo  "<b>modname: $mod->modname id: $mod->id course: $mod->course</b><br />";
                                    echo  '<input type="hidden" name="modname'.$itemcount.'" value="'.$mod->modname.'" />';
                                    echo  '<input type="hidden" name="mod'.$itemcount.'" value="'.$mod->instance.'" />';
                                    echo  '<input type="hidden" name="course'.$itemcount.'" value="'.$mod->course.'" />';
                                    echo  '<tr><td align="center" class="generalboxcontent">';
                                    // get instance name from db.
                                    $instance = get_record($mod->modname, 'id', $mod->instance);
                                    echo  format_string($instance->name)."</td>";
                                    // see if the item is already in the category table and if it is call category select with the id so it is selected
                                    echo  '<td align="center" class="generalboxcontent"><select name="category'.$itemcount.'">';
                                    $item_cat_id = get_record('grade_item', 'modid', $mod->module, 'courseid', $course->id, 'cminstance', $mod->instance);
                                    //print_object($item_cat_id);
                                    if (isset($item_cat_id)) {
                                        grade_category_select($item_cat_id->category);
                                    }
                                    else {
                                        grade_category_select(-1);
                                    }
                                    echo  '</select></td><td align="center" class="generalboxcontent">'.$modgrades->maxgrade.'<input type="hidden" name="maxgrade'.$itemcount.'" value="'.$modgrades->maxgrade.'" /></td>';
                                        
                                    if (isset($item_cat_id)) {
                                        // the value held in scale_grade is a scaling percent. The next line just formats it so it is easier for the user (they just enter the point value they want to be 100%)
                                        if ($item_cat_id->scale_grade == '' || $item_cat_id->scale_grade == 0)
                                            $scale_to = $modgrades->maxgrade;
                                        else
                                            $scale_to = round($modgrades->maxgrade/$item_cat_id->scale_grade);
                                        echo  '<td align="center" class="generalboxcontent"><input type="text" size="5" name="scale_grade'.$itemcount.'" value="'.$scale_to.'" /></td>';
                                    }
                                    else {
                                        echo  '<td align="center" class="generalboxcontent"><input type="text" size="5" name="scale_grade'.$itemcount.'" value="'.$modgrades->maxgrade.'" /></td>';
                                    }
                                    
                                    echo  '<td align="center" class="generalboxcontent"><input type="checkbox" name="extra_credit'.$itemcount.'" ';
                                    if ($item_cat_id->extra_credit == 1) {
                                        echo  ' checked="checked"';
                                    }
                                    echo  ' /></td></tr>';
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    echo  '<input type="hidden" name="totalitems" value="'.$itemcount.'" />';
    echo  '<tr><td colspan="5" align="center" class="generalboxcontent"><input type="submit" value="'.get_string('savechanges','grades').'" /></td></tr>';
    echo  '</form>';
    echo  '<tr><td colspan="5" align="center" class="generalboxcontent">';
    grade_add_category_form();
    echo  '</td></tr><tr><td colspan="5" align="center" class="generalboxcontent">';
    grade_delete_category_form();
    echo  '</td></tr></table>';
    echo '<center>'.get_string('extracreditwarning','grades').'</center>';
}

function grade_delete_category() {
    global $CFG;
    global $course;
    global $USER;
    
    if (!empty($USER->id)) {
        if (!confirm_sesskey()) {
            error(get_string('confirmsesskeybad', 'error'));
        }
    }
    
    $cat_id = optional_param('category_id');
    if ($cat_id != 'blank') {
        // delete the record
        delete_records('grade_category', 'id', $cat_id, 'courseid', $course->id);
        // set grade_item category field=0 where it was the deleted category (set uncategorized will clean this up)
        set_field('grade_item', 'category', 0, 'category', $cat_id);
    }
}

function grade_assign_categories() {
    global $CFG;
    global $course;
    global $USER;
    $num_categories = optional_param('totalitems');

    if (!empty($USER->id)) {
        if (!confirm_sesskey()) {
            error(get_string('confirmsesskeybad', 'error'));
        }
    }

    for ($i = 1; $i <= $num_categories; $i++) {

        // these next sets of lines are a bit obtuse, but it lets there be a dynamic number of grade items
        // in the grade category form (maybe there's a better way?)
        $cur_cat_id = '$_REQUEST[\'category'.$i.'\'];';
        eval( "\$cur_cat_id = $cur_cat_id;" ); 
        $cur_modname = '$_REQUEST[\'modname'.$i.'\'];';
        eval( "\$cur_modname = $cur_modname;" ); 
        $cur_mod = '$_REQUEST[\'mod'.$i.'\'];';
        eval( "\$cur_mod = $cur_mod;" );
        $cur_maxgrade = '$_REQUEST[\'maxgrade'.$i.'\'];';
        eval( "\$cur_maxgrade = $cur_maxgrade;" );
        $cur_scale_grade = '$_REQUEST[\'scale_grade'.$i.'\'];';
        eval( "\$cur_scale_grade = $cur_scale_grade;" );
        $cur_extra_credit = '$_REQUEST[\'extra_credit'.$i.'\'];';
        $temp = 'extra_credit'.$i;
        $junk = get_record('modules','name',$cur_modname);
        $cur_modid = $junk->id;
        if (isset($_REQUEST[$temp])) {
            eval( "\$cur_extra_credit = $cur_extra_credit;" );
        }
        else {
            $cur_extra_credit = false;
        }
        if ($cur_extra_credit) {
        $cur_extra_credit = 1;
        } else {
            $cur_extra_credit = 0;
        }
        if ($cur_scale_grade == 0 || $cur_scale_grade == '') {
            $cur_scale_grade = 1.0;
        }
        
        $db_cat = get_record('grade_item', 'modid', $cur_modid, 'cminstance', $cur_mod, 'courseid', $course->id);
        if ( $db_cat ) {
            if ($db_cat->category != $cur_cat_id) {
                // item doesn't match in the db so update it to point to the new category
                set_field('grade_item', 'category', $cur_cat_id, 'id', $db_cat->id);
            }

            if ($db_cat->scale_grade != $cur_maxgrade/$cur_scale_grade) {
                // scale_grade doesn't match
                set_field('grade_item', 'scale_grade', ($cur_maxgrade/$cur_scale_grade), 'id', $db_cat->id);
            }

            set_field('grade_item', 'extra_credit', $cur_extra_credit, 'id', $db_cat->id);
        }
        else {
            // add a new record
            $item->courseid = $course->id;
            $item->category = $cur_cat_id;
            $item->modid = $cur_modid;
            $item->cminstance = $cur_mod;
            $item->scale_grade = $cur_scale_grade;
            $item->extra_credit = $cur_extra_credit;
            insert_record('grade_item', $item);
        }
    }
}

function grade_add_category_form() {
    /// outputs a form to add a category
    /// just a simple text box with submit
    global $course;
    global $USER;
    echo  '<form id="new_category">';
    echo  get_string('addcategory','grades').':<input type="text" name="name" size="20" />';
    echo  '<input type="hidden" name="id" value="'.$course->id.'" />';
    echo  '<input type="hidden" name="action" value="insert_category" />';
    echo  '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
    echo  '<input type="submit" value="'.get_string('addcategory','grades').'" />';
    echo  '</form>';
}

function grade_delete_category_form() {
    // outputs a form to delete a category
    global $course;
    global $USER;
    echo  '<form id="delete_category">';
    echo  get_string('deletecategory','grades').': <select name="category_id">';
    grade_category_select();
    echo  '</select><input type="hidden" name="id" value="'.$course->id.'" />';
    echo  '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
    echo  '<input type="hidden" name="action" value="delete_category" />';
    echo  '<input type="submit" value="'.get_string('deletecategory','grades').'" /></form>';
}

function grade_insert_category() {
    global $CFG;
    global $course;
    global $USER;
    
    $category->name=optional_param('name');
    $category->courseid=$course->id;
    
    if (!empty($USER->id)) {
        if (!confirm_sesskey()) {
            error(get_string('confirmsesskeybad', 'error'));
        }
    }
    
    // make sure the record isn't already there and insert if okay
    if (record_exists('grade_category', 'name', $category->name, 'courseid', $category->courseid)) {
            // category already exists
    }
    elseif ($category->name != ''){
        if (!insert_record('grade_category', $category) ) {
            echo  '<font color="red">'.get_string('addcategoryerror','grades').'</font>';
        }
    }
}

function grade_category_select($id_selected = 0) {
    /// prints out a select box containing categories.
    global $CFG;
    global $course;

    
    echo  '<option value="blank">'.get_string('choosecategory','grades').'</option>';
    
    $categories = get_records('grade_category', 'courseid', $course->id, 'name');
    
    if (!isset($categories)) {
        error(get_string("nocategories"));
    }
    else {
        foreach($categories as $category) {
            if ($category->name == UNCATEGORISED) {
                $category->name = get_string('uncategorised', 'grades');
            }
            if ($category->id == $id_selected) {
                echo  '<option value="'.$category->id.'" selected="selected">'. format_string($category->name) .'</option>';
            }
            else {
                echo  '<option value="'.$category->id.'">'. format_string($category->name) .'</option>';
            }
        }
    }
}

function grade_display_grade_preferences($course, $preferences) {
    global $CFG;
    global $USER;

    if ($preferences->use_advanced == 0) {
        $useadvanced = 1;
        $buttonlabel = get_string('useadvanced', 'grades');
    } else {
        $useadvanced = 0;
        $buttonlabel = get_String('hideadvanced', 'grades');
    }

    $buttonoptions = array('action'  => 'set_grade_preferences',
                           'id'      => $course->id,
                           'sesskey' => sesskey(),
                           'use_advanced' => $useadvanced);
                           

    print_heading_with_help(get_string('setpreferences','grades'), 'preferences', 'grade');
    
    echo '<center>';
    print_single_button('index.php', $buttonoptions, $buttonlabel, 'post');
    echo '<br /></center>';

    echo  '<form id="set_grade_preferences" method="post" action="./index.php">';
    echo  '<input type="hidden" name="action" value="set_grade_preferences" />';
    echo  '<input type="hidden" name="id" value="'.$course->id.'" />';
    echo  '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
    echo  '<table border="0" cellspacing="2" cellpadding="5" align="center" class="gradeprefs generalbox">';
    
    $optionsyesno = NULL;
    $optionsyesno[0] = get_string('no');
    $optionsyesno[1] = get_string('yes');

    
    if ($preferences->use_advanced) {
        $options = NULL;
        $options[0] = get_string('no');
        $options[1] = get_string('toonly', 'moodle', $course->teachers);
        $options[2] = get_string('toeveryone', 'moodle');

        // display grade weights
        echo  '<tr><td class="c0">'.get_string('displayweighted','grades').':</td>';
        echo  '<td class="c1">';
        choose_from_menu($options, 'display_weighted', $preferences->display_weighted, '');
        echo  '</td></tr>';
        
        // display points    
        echo  '<tr><td class="c0">'.get_string('displaypoints','grades').':</td>';
        echo  '<td class="c1">';
        choose_from_menu($options, 'display_points', $preferences->display_points, '');
        echo  '</td></tr>';

        // display percent
        echo  '<tr><td class="c0">'.get_string('displaypercent','grades').':</td>';
        echo  '<td class="c1">';
        choose_from_menu($options, 'display_percent', $preferences->display_percent, '');
        echo  '</td></tr>';
        
        // display letter grade
        echo  '<tr><td class="c0">'.get_string('displaylettergrade','grades').':</td>';
        echo  '<td class="c1">';
        choose_from_menu($options, 'display_letters', $preferences->display_letters, '');
        echo  '</td></tr>';

        // letter grade uses weighted percent
        $options = NULL;
        $options[0] = get_string('usepercent','grades');
        $options[1] = get_string('useweighted','grades');

        echo  '<tr><td class="c0">'.get_string('lettergrade','grades').':</td>';
        echo  '<td class="c1">';
        choose_from_menu($options, 'use_weighted_for_letter', $preferences->use_weighted_for_letter, '');
        echo  '</td></tr>';
    }

    $headerlist[0] = get_string('none');
    for ($i=1; $i<=100; $i++) {
        $headerlist[$i] = $i;
    }
    
    // reprint headers every n lines default n=0
    echo '<tr><td class="c0">'.get_string('reprintheaders','grades').':</td>';
    echo '<td class="c1">';
    choose_from_menu($headerlist, 'reprint_headers', $preferences->reprint_headers, '');
    echo '</td></tr>';
    
    // show hidden grade items to teacher
    echo  '<tr><td class="c0">'.get_string('showhiddenitems','grades').'</td>';
    echo  '<td class="c1">';
    choose_from_menu($optionsyesno, 'show_hidden', $preferences->show_hidden, '');
    echo  '</td></tr>';
    
    echo  '<tr><td colspan="3" align="center"><input type="submit" value="'.get_string('savepreferences','grades').'" /></td></tr></table></form>';
}



function grade_display_letter_grades() {
    global $CFG;
    global $course;
    global $USER;
    
    $db_letters = get_records('grade_letter', 'courseid', $course->id, 'grade_high DESC');
    
    if ($db_letters) {
        $using_defaults = false;
        foreach ($db_letters as $letter) {
            $letters[$letter->id]->letter = $letter->letter;
            $letters[$letter->id]->grade_low = $letter->grade_low;
            $letters[$letter->id]->grade_high = $letter->grade_high;
            $letters[$letter->id]->courseid = $course->id;
        }
    }
    else {
        $using_defaults = true;
        // default A
        $letters[0]->letter='A';
        $letters[0]->grade_low=93.00;
        $letters[0]->grade_high=100.00;
        $letters[0]->courseid = $course->id;
        // default A-
        $letters[1]->letter='A-';
        $letters[1]->grade_low=90.00;
        $letters[1]->grade_high=92.99;
        $letters[1]->courseid = $course->id;
        // default B+
        $letters[2]->letter='B+';
        $letters[2]->grade_low=87.00;
        $letters[2]->grade_high=89.99;
        $letters[2]->courseid = $course->id;
        // default B
        $letters[3]->letter='B';
        $letters[3]->grade_low=83.00;
        $letters[3]->grade_high=86.99;
        $letters[3]->courseid = $course->id;
        // default B-
        $letters[4]->letter='B-';
        $letters[4]->grade_low=80.00;
        $letters[4]->grade_high=82.99;
        $letters[4]->courseid = $course->id;
        // default C+
        $letters[5]->letter='C+';
        $letters[5]->grade_low=77.00;
        $letters[5]->grade_high=79.99;
        $letters[5]->courseid = $course->id;
        // default C
        $letters[6]->letter='C';
        $letters[6]->grade_low=73.00;
        $letters[6]->grade_high=76.99;
        $letters[6]->courseid = $course->id;
        // default C-
        $letters[7]->letter='C-';
        $letters[7]->grade_low=70.00;
        $letters[7]->grade_high=72.99;
        $letters[7]->courseid = $course->id;
        // default D+
        $letters[8]->letter='D+';
        $letters[8]->grade_low=67.00;
        $letters[8]->grade_high=69.99;
        $letters[8]->courseid = $course->id;
        // default D
        $letters[9]->letter='D';
        $letters[9]->grade_low=60.00;
        $letters[9]->grade_high=66.99;
        $letters[9]->courseid = $course->id;
        // default F
        $letters[10]->letter='F';
        $letters[10]->grade_low=0.00;
        $letters[10]->grade_high=59.99;
        $letters[10]->courseid = $course->id;
    }

    echo '<form id="grade_letter"><input type="hidden" name="id" value="'.$course->id.'" />';
    echo '<table border="0" cellspacing="2" cellpadding="5" align="center" class="generalbox"><tr>';
    echo '<th colspan="3" class="header" scope="col">'.get_string('setgradeletters','grades');
    helpbutton('letter', get_string('gradeletterhelp','grades'), 'grade');
    echo '</th></tr>';
    echo '<tr><td align="center" class="generaltableheader">'.get_string('gradeletter','grades').'</td>';
    echo '<td align="center" class="generaltableheader">'.get_string('lowgradeletter','grades').'</td>';
    echo '<td align="center" class="generaltableheader">'.get_string('highgradeletter','grades').'</td></tr>';
    echo '<input type="hidden" name="action" value="set_letter_grades" />';
    echo '<input type="hidden" name="sesskey" value="'.$USER->sesskey.'" />';
    $i=0;
    foreach ($letters as $id=>$items) {
        if ($id !='' && !$using_defaults) {
            // send the record id so if the user deletes the values we can delete the row.
            echo '<input type="hidden" name="id'.$i.'" value="'.$id.'" />';
        }
        echo '<tr><td align="center" class="generalboxcontent"><input size="8" type="text" name="letter'.$i.'" value="'.$items->letter.'" /></td>'."\n";
        echo '<td align="center" class="generalboxcontent"><input size="8" type="text" name="grade_low'.$i.'" value="'.$items->grade_low.'" /></td>'."\n";
        echo '<td align="center" class="generalboxcontent"><input size="8" type="text" name="grade_high'.$i.'" value="'.$items->grade_high.'" /></td></tr>'."\n";
        $i++;
    }
    echo '<tr><td align="center" class="generalboxcontent"><input size="8" type="text" name="letter'.$i.'" value="" /></td><td align="center" class="generalboxcontent"><input size="8" type="text" name="grade_low'.$i.'" value="" /></td><td align="center" class="generalboxcontent"><input type="text" size="8" name="grade_high'.$i.'" value="" /></td></tr>';
    echo '<tr><td colspan="3" align="center" class="generalboxcontent"><input type="submit" value="'.get_string('savechanges','grades').'" /></td></tr>';
    echo '<input type="hidden" name="totalitems" value="'.$i.'" />';
    echo '<tr><td colspan="3" align="center" class="generalboxcontent">'.get_string('gradeletternote','grades').'</td></tr></table>';
    echo '</form>';
}

function grade_set_letter_grades() {
    global $CFG;
    global $course;
    global $USER;
    
    if (!empty($USER->id)) {
        if (!confirm_sesskey()) {
            error(get_string('confirmsesskeybad', 'error'));
        }
    }
    
    $totalitems= clean_param($_REQUEST['totalitems'], PARAM_CLEAN);
    
    for($i=0; $i<=$totalitems; $i++) {
        if (isset($_REQUEST["id$i"])) {
            // item submitted was already in database
            $letterid = $_REQUEST["id$i"];
            $updateletters[$letterid]->letter = clean_param($_REQUEST["letter$i"], PARAM_CLEAN);
            // grade_low && grade_high don't need cleaning as they are possibly floats (no appropriate clean method) so we check is_numeric
            $updateletters[$letterid]->grade_low = $_REQUEST["grade_low$i"];
            $updateletters[$letterid]->grade_high = $_REQUEST["grade_high$i"];
            $updateletters[$letterid]->id = $letterid;
        }
        else {
            // its a new item
            $newletter->letter = clean_param($_REQUEST["letter$i"], PARAM_CLEAN);
            $newletter->grade_low = $_REQUEST["grade_low$i"];
            $newletter->grade_high = $_REQUEST["grade_high$i"];
            $newletter->courseid = $course->id;
            if (is_numeric($newletter->grade_high) && is_numeric($newletter->grade_low)) {
                insert_record('grade_letter', $newletter);
            }
            else {
                if ($i < $totalitems) {
                    if ($newletter->grade_high != '' or $newletter->grade_low != '') {
                        echo '<center>'.get_string('lettergradenonnumber','grades').' '.$newletter->letter.' item number: '.$i.'<br /></center>';
                    }
                }
            }
        }
    }

    if (isset($updateletters)) {
        foreach($updateletters as $id=>$items) {
            // see if any of the values are blank... if so delete them
            if ($items->letter == '' || $items->grade_low == '' || $items->grade_high == '') {
                delete_records('grade_letter', 'id', $id);
            }
            else {
                if (is_numeric($items->grade_high) && is_numeric($items->grade_low)) {
                    update_record('grade_letter', $items);
                }
                else {
                    echo '<center><font color="red">'.get_string('errorgradevaluenonnumeric','grades').$letter.'</font></center>';
                }
            }
        }
    }
}

function grade_download_form($type='both') {
    global $course,$USER, $action, $cview;
    if ($type != 'both' and $type != 'ods' and $type != 'excel' and $type != 'text') {
        $type = 'both';
    }
    
    if (has_capability('moodle/course:viewcoursegrades', get_context_instance(CONTEXT_COURSE, $course->id))) {
        echo '<table align="center"><tr>';
        $options['id'] = $course->id;
        $options['sesskey'] = $USER->sesskey;
        
        if ($type == 'both' || $type == 'ods') {
            $options['action'] = 'ods';
            echo '<td align="center">';
            print_single_button("index.php", $options, get_string("downloadods"));
            echo '</td>';
        }
        if ($type == 'both' || $type == 'excel') {
            $options['action'] = 'excel';
            echo '<td align="center">';
            print_single_button("index.php", $options, get_string("downloadexcel"));
            echo '</td>';
        }
        if ($type == 'both' || $type == 'text') {
            $options['action'] = 'text';
            echo '<td align="center">';
            print_single_button("index.php", $options, get_string("downloadtext"));
            echo '</td>';
        }
        echo '<td>';
        
        $url = 'index.php?id='.$course->id;
        if (!empty($action)) {
            $url .= '&amp;action='.$action;
            if ($action == 'vcats') {
               $url .= '&amp;cview='.$cview;
            }
        }
        echo '</td>';

        echo '</tr></table>';
    }
}





/** 
 * Simply prints all grade of one student from all modules from a given course
 * used in the grade book for student view, and grade button under user profile
 * @param int $userid;
 * @param int $courseid;
 */
function print_student_grade($user, $course) {
    
    global $CFG;
  
    if (!empty($user)) {
        $grades[$user->id] = array();    // Collect all grades in this array
        $gradeshtml[$user->id] = array(); // Collect all grades html formatted in this array
        $totals[$user->id] = array();    // Collect all totals in this array
    }
  
    $strmax = get_string("maximumshort");
  /// Collect modules data
    get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);

/// Search through all the modules, pulling out grade data
    $sections = get_all_sections($course->id); // Sort everything the same as the course
    
    // prints table

    // flag for detecting whether to print table header or not
    $nograde = 0;
    
    for ($i=0; $i<=$course->numsections; $i++) {
        if (isset($sections[$i])) {   // should always be true
            $section = $sections[$i];
            if ($section->sequence) {
                $sectionmods = explode(",", $section->sequence);
                foreach ($sectionmods as $sectionmod) {
                 
                    $mod = $mods[$sectionmod];
                    if (empty($mod->modname)) {
                        continue;  // Just in case, see MDL-7150
                    }
                    $instance = get_record($mod->modname, 'id', $mod->instance);
                    $libfile = "$CFG->dirroot/mod/$mod->modname/lib.php";    
                    
                    if (file_exists($libfile)) {
                        require_once($libfile);
                        $gradefunction = $mod->modname.'_grades';
                        if (function_exists($gradefunction)) {   // Skip modules without grade function
                            if ($modgrades = $gradefunction($mod->instance)) {
                                if (!empty($modgrades->maxgrade)) {
                                    if ($mod->visible) {
                                        $maxgrade = $modgrades->maxgrade;
                                    } else {
                                        $maxgrade = $modgrades->maxgrade;
                                    }
                                } else {
                                    $maxgrade = '';
                                }
                                
                                if ($maxgrade) { 
                                    if (!$nograde) {
                                         echo ('<table align="center" class="grades"><tr><th scope="col">'.get_string('activity').'</th><th scope="col">'.get_string('yourgrade','grades').'</th><th scope="col">'.get_string('maxgrade','grades').'</th></tr>');
                                    }
                                    $nograde++;                               
                                  
                                    $link_id = grade_get_module_link($course->id, $mod->instance, $mod->module);
                                    $link = $CFG->wwwroot.'/mod/'.$mod->modname.'/view.php?id='.$link_id->id;

                                    echo '<tr>';
                                    if (!empty($modgrades->grades[$user->id])) {
                                        $currentgrade = $modgrades->grades[$user->id];
                                        echo "<td><a href='$link'>$mod->modfullname: ".format_string($instance->name,true)."</a></td><td>$currentgrade</td><td>$maxgrade</td>";            
                                    } else {
                                        echo "<td><a href='$link'>$mod->modfullname: ".format_string($instance->name,true)."</a></td><td>".get_string('nograde')."</td><td>$maxgrade</td>";                                        
                                    }
                                    echo '</tr>';                       
                                }
                            }
                        }
                    }
                }
            }
        }
    } // a new Moodle nesting record? ;-)
    
    if ($nograde) {
        echo '</table>';
    }
}

function grade_get_course_students($courseid) {
    global $CFG;
    // The list of roles to display is stored in CFG->gradebookroles
    if (!$context = get_context_instance(CONTEXT_COURSE, $courseid)) {
        return false;  
    } 
        
    $configvar = get_config('', 'gradebookroles');
    if (empty($configvar)) {
        notify ('no roles defined in admin->appearance->graderoles');
        return false; // no roles to displayreturn false;  
    }
         
    if ($rolestoget = explode(',', $configvar)) {
        foreach ($rolestoget as $crole) {
            if ($tempstudents = get_role_users($crole, $context, true)) {
                foreach ($tempstudents as $tempuserid=>$tempstudent) {
                    $students[$tempuserid] = $tempstudent;  
                }            
            }
        }
    } else {
        notify ('no roles defined in admin->appearance->graderoles');
        return false; // no roles to displayreturn false;  
    }
    return isset($students)?$students:'';
}
?>
