### Eclipse Workspace Patch 1.0
#P moodle
Index: lib/db/upgrade.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/db/upgrade.php,v
retrieving revision 1.103
diff -u -r1.103 upgrade.php
--- lib/db/upgrade.php	3 Aug 2007 19:25:22 -0000	1.103
+++ lib/db/upgrade.php	3 Aug 2007 22:27:22 -0000
@@ -1073,6 +1073,7 @@
         $table->addFieldInfo('aggregation', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
         $table->addFieldInfo('keephigh', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
         $table->addFieldInfo('droplow', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
+        $table->addFieldInfo('aggregateoutcomes', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
         $table->addFieldInfo('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
         $table->addFieldInfo('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, null);
 
@@ -1232,6 +1233,7 @@
         $table->addFieldInfo('aggregation', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
         $table->addFieldInfo('keephigh', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
         $table->addFieldInfo('droplow', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, null, '0');
+        $table->addFieldInfo('aggregateoutcomes', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0');
 
     /// Adding keys to table grade_categories_history
         $table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
@@ -1598,6 +1600,28 @@
 
     }
 
+    if ($result && $oldversion < 2007080300) {
+
+    /// Define field aggregateoutcomes to be added to grade_categories
+        $table = new XMLDBTable('grade_categories');
+        $field = new XMLDBField('aggregateoutcomes');
+        if (!field_exists($table, $field)) {
+            $field->setAttributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'droplow');
+
+        /// Launch add field aggregateoutcomes
+            $result = $result && add_field($table, $field);
+        }
+
+    /// Define field aggregateoutcomes to be added to grade_categories
+        $table = new XMLDBTable('grade_categories_history');
+        $field = new XMLDBField('aggregateoutcomes');
+        if (!field_exists($table, $field)) {
+            $field->setAttributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, null, '0', 'droplow');
+
+        /// Launch add field aggregateoutcomes
+            $result = $result && add_field($table, $field);
+        }
+    }
 
 /*
     /// drop old gradebook tables
Index: grade/edit/tree/outcomeitem_form.php
===================================================================
RCS file: /cvsroot/moodle/moodle/grade/edit/tree/outcomeitem_form.php,v
retrieving revision 1.4
diff -u -r1.4 outcomeitem_form.php
--- grade/edit/tree/outcomeitem_form.php	2 Aug 2007 10:38:22 -0000	1.4
+++ grade/edit/tree/outcomeitem_form.php	3 Aug 2007 22:27:17 -0000
@@ -26,6 +26,7 @@
             }
         }
         $mform->addElement('select', 'outcomeid', get_string('outcome', 'grades'), $options);
+        $mform->addRule('outcomeid', get_string('required'), 'required');
 
         $options = array(0=>get_string('none'));
         if ($coursemods = get_course_mods($COURSE->id)) {
Index: grade/edit/tree/category_form.php
===================================================================
RCS file: /cvsroot/moodle/moodle/grade/edit/tree/category_form.php,v
retrieving revision 1.1
diff -u -r1.1 category_form.php
--- grade/edit/tree/category_form.php	29 Jul 2007 23:02:08 -0000	1.1
+++ grade/edit/tree/category_form.php	3 Aug 2007 22:27:17 -0000
@@ -29,6 +29,10 @@
         $mform->addElement('select', 'aggregation', get_string('aggregation', 'grades'), $options);
         $mform->setDefault('gradetype', GRADE_AGGREGATE_MEAN_ALL);
 
+        if (!empty($CFG->enableoutcomes)) {
+            $mform->addElement('advcheckbox', 'aggregateoutcomes', get_string('aggregateoutcomes', 'grades'));
+        }
+
         $options = array();
         $options[0] = get_string('none');
         for ($i=1; $i<=20; $i++) {
Index: version.php
===================================================================
RCS file: /cvsroot/moodle/moodle/version.php,v
retrieving revision 1.510
diff -u -r1.510 version.php
--- version.php	2 Aug 2007 14:25:58 -0000	1.510
+++ version.php	3 Aug 2007 22:27:04 -0000
@@ -6,7 +6,7 @@
 // This is compared against the values stored in the database to determine
 // whether upgrades should be performed (see lib/db/*.php)
 
-    $version = 2007080202;  // YYYYMMDD = date
+    $version = 2007080300;  // YYYYMMDD = date
                             //       XY = increments within a single day
 
     $release = '1.9 dev';   // Human-friendly version name
Index: course/moodleform_mod.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/moodleform_mod.php,v
retrieving revision 1.13
diff -u -r1.13 moodleform_mod.php
--- course/moodleform_mod.php	2 Aug 2007 10:38:20 -0000	1.13
+++ course/moodleform_mod.php	3 Aug 2007 22:27:17 -0000
@@ -29,10 +29,17 @@
      */
     var $_cm;
 
+    /**
+     * List of not yet used outcomes in activity
+     * @var array
+     */
+    var $_candidate_outcomes;
+
     function moodleform_mod($instance, $section, $cm) {
         $this->_instance = $instance;
         $this->_section = $section;
         $this->_cm = $cm;
+        $this->_candidate_outcomes = array();
         parent::moodleform('modedit.php');
     }
 
@@ -45,7 +52,7 @@
     }
 
     function definition_after_data() {
-        global $COURSE;
+        global $COURSE, $CFG;
         $mform =& $this->_form;
 
         if ($id = $mform->getElementValue('update')) {
@@ -59,11 +66,54 @@
                         $elname = 'outcome_'.$item->outcomeid;
                         if ($mform->elementExists($elname)) {
                             $mform->hardFreeze($elname); // prevent removing of existing outcomes
+                            unset($this->_candidate_outcomes[$item->outcomeid]);
                         }
                     }
                 }
             }
+
         }
+
+        if (!empty($CFG->enableoutcomes)) {
+            if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
+                $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id));
+                $options = array();
+
+                if (!$id) {
+                    $options[-1] = get_string('outcomecategorynew', 'grades');
+                }
+                foreach ($categories as $category) {
+                    $options[$category->id] = $category->get_name();
+                }
+                $cat_el =& $mform->getElement('outcomecat');
+                $cat_el->load($options);
+
+                if ($id) {
+                    $modulename = $mform->getElementValue('modulename');
+                    $instance   = $mform->getElementValue('instance');
+                    if ($activity_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$modulename,
+                                                       'iteminstance'=>$instance, 'itemnumber'=>0, 'courseid'=>$COURSE->id))) {
+                        $mform->setDefault('outcomecat', $activity_item->categoryid);
+                    } else {
+                        $course_category = grade_category::fetch_course_category($COURSE->id);
+                        $mform->setDefault('outcomecat', $course_category->id);
+                    }
+                } else {
+                    $mform->setDefault('outcomecat', -1);
+                    // TODO: it might be better to use course category here by default
+/*                    $course_category = grade_category::fetch_course_category($COURSE->id);
+                    $mform->setDefault('outcomecat', $course_category->id);*/
+                    
+                }
+
+                if (empty($this->_candidate_outcomes)) {
+                    // no more outcomes can be addded
+                    $mform->removeElement('outcomecat');
+                }
+            } else {
+                $mform->removeElement('outcomecat');
+            }
+        }            
     }
 
     // form verification
@@ -119,8 +169,10 @@
         if (!empty($CFG->enableoutcomes)) {
             if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
                 $mform->addElement('header', 'modoutcomes', get_string('outcomes', 'grades'));
+                $mform->addElement('select', 'outcomecat', get_string('outcomecategory', 'grades'));
                 foreach($outcomes as $outcome) {
                     $mform->addElement('advcheckbox', 'outcome_'.$outcome->id, $outcome->get_name());
+                    $this->_candidate_outcomes[$outcome->id] = $outcome->id;
                 }
             }
         }
Index: course/modedit.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/modedit.php,v
retrieving revision 1.29
diff -u -r1.29 modedit.php
--- course/modedit.php	3 Aug 2007 14:12:38 -0000	1.29
+++ course/modedit.php	3 Aug 2007 22:27:17 -0000
@@ -295,18 +295,35 @@
         }
 
         //sync idnumber with grade_item
-        if ($grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename,
+        if ($activity_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename,
                      'iteminstance'=>$fromform->instance, 'itemnumber'=>0, 'courseid'=>$COURSE->id))) {
-            if ($grade_item->idnumber != $fromform->cmidnumber) {
-                $grade_item->idnumber = $fromform->cmidnumber;
-                $grade_item->update();
+            if ($activity_item->idnumber != $fromform->cmidnumber) {
+                $activity_item->idnumber = $fromform->cmidnumber;
+                $activity_item->update();
+            }
+        }
+
+
+        $category = false; // means course category
+        if (isset($fromform->outcomecat)) {
+            if ($fromform->outcomecat == -1) { // new category - adding activity only
+                //create new category - do not insert yet
+                $category = new grade_category();
+                $category->fullname = stripslashes($fromform->name);
+                $category->courseid = $COURSE->id;
+
+            } else if ($fromform->outcomecat == 0) { // keep the same cat as activity item or course cat if no grading
+                if ($activity_item) {
+                    $category = $activity_item->get_parent_category();
+                }
+
+            } else { // put into specified category
+                $category = grade_category::fetch(array('id'=>$fromform->outcomecat));
             }
         }
 
         // add outcomes if requested
         if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) {
-            $grade_items = array();
-
             foreach($outcomes as $outcome) {
                 $elname = 'outcome_'.$outcome->id;
 
@@ -347,49 +364,25 @@
 
                     $grade_item->insert();
 
-                    // TODO comment on these next 4 lines
-                    if ($item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$grade_item->itemmodule,
-                                 'iteminstance'=>$grade_item->iteminstance, 'itemnumber'=>0, 'courseid'=>$COURSE->id))) {
-                        $grade_item->set_parent($item->categoryid);
-                        $grade_item->move_after_sortorder($item->sortorder);
-                    }
-                    $grade_items[] = $grade_item;
-                }
-            }
+                    // Put it into requested category
+                    if ($category) {
 
-            // Create a grade_category to represent this module, if outcomes have been attached
-            if (!empty($grade_items)) {
-                // Add the module's normal grade_item as a child of this category
-                $item_params = array('itemtype'=>'mod',
-                                     'itemmodule'=>$fromform->modulename,
-                                     'iteminstance'=>$fromform->instance,
-                                     'itemnumber'=>0,
-                                     'courseid'=>$COURSE->id);
-                $item = grade_item::fetch($item_params);
-
-                // Only create the category if it will contain at least 2 items
-                if ($item OR count($grade_items) > 1) { // If we are here it means there is at least 1 outcome
-                    $cat_params = array('courseid'=>$COURSE->id, 'fullname'=>$fromform->name);
-                    $grade_category = grade_category::fetch($cat_params);
-
-                    if (!$grade_category) {
-                        $grade_category = new grade_category($cat_params);
-                        $grade_category->courseid = $COURSE->id;
-                        $grade_category->fullname = $fromform->name;
-                        $grade_category->insert();
-                    }
-
-                    $sortorder = $grade_category->sortorder;
+                        if (empty($category->id)) {
+                            // now is the time to insert it
+                            $category->insert();
+                        }
 
-                    if ($item) {
-                        $item->set_parent($grade_category->id);
-                        $sortorder = $item->sortorder;
+                        if ($add and $activity_item and $activity_item->categoryid != $category->id) {
+                            // move activity to category when inserting only
+                            $activity_item->set_parent($category->id);
+                        }
+                        
+                        $grade_item->set_parent($category->id);
                     }
 
-                    // Add the outcomes as children of this category
-                    foreach ($grade_items as $gi) {
-                        $gi->set_parent($grade_category->id);
-                        $gi->move_after_sortorder($sortorder);
+                    // ...just after the activity grade item if exists
+                    if ($activity_item) {
+                        $grade_item->move_after_sortorder($activity_item->sortorder);
                     }
                 }
             }
Index: lang/en_utf8/grades.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lang/en_utf8/grades.php,v
retrieving revision 1.66
diff -u -r1.66 grades.php
--- lang/en_utf8/grades.php	3 Aug 2007 08:41:20 -0000	1.66
+++ lang/en_utf8/grades.php	3 Aug 2007 22:27:17 -0000
@@ -21,6 +21,7 @@
 $string['aggregatemaxgraded'] = 'Highest grade of non-empty grades';
 $string['aggregatemodeall'] = 'Mode of all grades';
 $string['aggregatemodegraded'] = 'Mode of non-empty grades';
+$string['aggregateoutcomes'] = 'Aggregate also outcomes';
 $string['aggregateweightedmeanall'] = 'Weighted mean of all grades';
 $string['aggregateweightedmeangraded'] = 'Weighted mean of non-empty grades';
 $string['aggregation'] = 'Aggregation';
@@ -234,6 +235,8 @@
 $string['onascaleof'] = ' on a scale of $a->grademin to $a->grademax';
 $string['operations'] = 'Operations';
 $string['outcome'] = 'Outcome';
+$string['outcomecategory'] = 'Create outcomes in category';
+$string['outcomecategorynew'] = 'New category';
 $string['outcomecreate'] = 'Add a new outcome';
 $string['outcomeitem'] = 'Outcome item';
 $string['outcomeitemsedit'] = 'Edit outcome item';
Index: backup/restorelib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/backup/restorelib.php,v
retrieving revision 1.259
diff -u -r1.259 restorelib.php
--- backup/restorelib.php	2 Aug 2007 08:28:29 -0000	1.259
+++ backup/restorelib.php	3 Aug 2007 22:27:16 -0000
@@ -1283,6 +1283,7 @@
                             $dbrec->aggregation = backup_todb($info['GRADE_CATEGORY']['#']['AGGREGATION']['0']['#']);
                             $dbrec->keephigh = backup_todb($info['GRADE_CATEGORY']['#']['KEEPHIGH']['0']['#']);
                             $dbrec->droplow = backup_todb($info['GRADE_CATEGORY']['#']['DROPLOW']['0']['#']);
+                            $dbrec->aggregateoutcomes = backup_todb($info['GRADE_CATEGORY']['#']['AGGREGATEOUTCOMES']['0']['#']);
                             $dbrec->hidden = backup_todb($info['GRADE_CATEGORY']['#']['HIDDEN']['0']['#']);
 
                             //Structure is equal to db, insert record
Index: backup/backuplib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/backup/backuplib.php,v
retrieving revision 1.158
diff -u -r1.158 backuplib.php
--- backup/backuplib.php	3 Aug 2007 08:55:27 -0000	1.158
+++ backup/backuplib.php	3 Aug 2007 22:27:08 -0000
@@ -1386,6 +1386,7 @@
                 fwrite ($bf,full_tag("AGGREGATION",5,false,$grade_category->aggregation));
                 fwrite ($bf,full_tag("KEEPHIGH",5,false,$grade_category->keephigh));
                 fwrite ($bf,full_tag("DROPLOW",5,false,$grade_category->droplow));
+                fwrite ($bf,full_tag("AGGREGATEOUTCOMES",5,false,$grade_category->aggregateoutcomes));
 
                 //End grade_category
                 fwrite ($bf,end_tag("GRADE_CATEGORY",4,true));
Index: lib/grade/grade_category.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/grade/grade_category.php,v
retrieving revision 1.83
diff -u -r1.83 grade_category.php
--- lib/grade/grade_category.php	3 Aug 2007 19:25:24 -0000	1.83
+++ lib/grade/grade_category.php	3 Aug 2007 22:27:22 -0000
@@ -94,6 +94,12 @@
     var $droplow = 0;
 
     /**
+     * Aggregate outcomes together with normal items
+     * @$aggregateoutcomes
+     */
+    var $aggregateoutcomes = 0;
+
+    /**
      * Array of grade_items or grade_categories nested exactly 1 level below this category
      * @var array $children
      */
@@ -308,11 +314,12 @@
 
         $db_item = grade_category::fetch(array('id'=>$this->id));
 
-        $aggregationdiff = $db_item->aggregation != $this->aggregation;
-        $keephighdiff    = $db_item->keephigh    != $this->keephigh;
-        $droplowdiff     = $db_item->droplow     != $this->droplow;
+        $aggregationdiff = $db_item->aggregation       != $this->aggregation;
+        $keephighdiff    = $db_item->keephigh          != $this->keephigh;
+        $droplowdiff     = $db_item->droplow           != $this->droplow;
+        $aggoutcomesdiff = $db_item->aggregateoutcomes != $this->aggregateoutcomes;
 
-        return ($aggregationdiff || $keephighdiff || $droplowdiff);
+        return ($aggregationdiff || $keephighdiff || $droplowdiff || $aggoutcomesdiff);
     }
 
     /**
@@ -668,10 +675,15 @@
         // recursively resort children
         if (!empty($category_array['children'])) {
             $result['children'] = array();
+            //process the category item first
+            $cat_item_id = null;
             foreach($category_array['children'] as $oldorder=>$child_array) {
                 if ($child_array['type'] == 'courseitem' or $child_array['type'] == 'categoryitem') {
                     $result['children'][$sortorder] = grade_category::_fetch_course_tree_recursion($child_array, $sortorder);
-                } else {
+                }
+            }            
+            foreach($category_array['children'] as $oldorder=>$child_array) {
+                if ($child_array['type'] != 'courseitem' and $child_array['type'] != 'categoryitem') {
                     $result['children'][++$sortorder] = grade_category::_fetch_course_tree_recursion($child_array, $sortorder);
                 }
             }
Index: lib/grade/grade_item.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/grade/grade_item.php,v
retrieving revision 1.97
diff -u -r1.97 grade_item.php
--- lib/grade/grade_item.php	3 Aug 2007 19:25:24 -0000	1.97
+++ lib/grade/grade_item.php	3 Aug 2007 22:27:22 -0000
@@ -232,6 +232,11 @@
         // Retrieve scale and infer grademax/min from it if needed
         $this->load_scale();
 
+        // make sure there is not 0 in outcomeid
+        if (empty($this->outcomeid)) {
+            $this->outcomeid = null;
+        }
+
         if ($this->qualifies_for_regrading()) {
             $this->force_regrading();
         }
@@ -349,6 +354,11 @@
             }
         }
 
+        // make sure there is not 0 in outcomeid
+        if (empty($this->outcomeid)) {
+            $this->outcomeid = null;
+        }
+
         if (parent::insert($source)) {
             // force regrading of items if needed
             $this->force_regrading();
@@ -1127,10 +1137,17 @@
                 return array();
             }
 
+            if (!empty($CFG->enableoutcomes) or $grade_category->aggregateoutcomes) {
+                $outcomes_sql = "";
+            } else {
+                $outcomes_sql = "AND gi.outcomeid IS NULL";
+            }
+
             $sql = "SELECT gi.id
                       FROM {$CFG->prefix}grade_items gi
                      WHERE gi.categoryid = {$grade_category->id}
-                           AND (gi.gradetype = ".GRADE_TYPE_VALUE." OR gi.gradetype = ".GRADE_TYPE_SCALE.")  
+                           AND (gi.gradetype = ".GRADE_TYPE_VALUE." OR gi.gradetype = ".GRADE_TYPE_SCALE.")
+                           $outcomes_sql
 
                     UNION
 
@@ -1138,7 +1155,8 @@
                       FROM {$CFG->prefix}grade_items gi, {$CFG->prefix}grade_categories gc
                      WHERE (gi.itemtype = 'category' OR gi.itemtype = 'course') AND gi.iteminstance=gc.id
                            AND gc.parent = {$grade_category->id}
-                           AND (gi.gradetype = ".GRADE_TYPE_VALUE." OR gi.gradetype = ".GRADE_TYPE_SCALE.")";  
+                           AND (gi.gradetype = ".GRADE_TYPE_VALUE." OR gi.gradetype = ".GRADE_TYPE_SCALE.")
+                           $outcomes_sql";
 
             if ($children = get_records_sql($sql)) {
                 return array_keys($children);
