### Eclipse Workspace Patch 1.0
#P moodle19
Index: lang/en_utf8/error.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lang/en_utf8/error.php,v
retrieving revision 1.26.2.8
diff -u -r1.26.2.8 error.php
--- lang/en_utf8/error.php	30 Apr 2008 14:34:23 -0000	1.26.2.8
+++ lang/en_utf8/error.php	12 May 2008 19:44:20 -0000
@@ -72,6 +72,7 @@
 $string['modulemissingcode'] = 'Module $a is missing the code needed to perform this function';
 $string['modulerequirementsnotmet'] = 'Module \"$a->modulename\" ($a->moduleversion) could not be installed.  It requires a newer version of Moodle (currently you are using $a->currentmoodle, you need $a->requiremoodle).';
 $string['mustbeteacher'] = 'You must be a teacher to look at this page';
+$string['nocategorydelete'] = 'Category \'$a\' can not be deleted!';
 $string['nocontext'] = 'Sorry, but that course is not a valid context';
 $string['noinstances'] = 'There are no instances of $a in this course!';
 $string['nologinas'] = 'You are not allowed to login as that user';
Index: lang/en_utf8/moodle.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lang/en_utf8/moodle.php,v
retrieving revision 1.141.2.35
diff -u -r1.141.2.35 moodle.php
--- lang/en_utf8/moodle.php	20 Apr 2008 01:15:54 -0000	1.141.2.35
+++ lang/en_utf8/moodle.php	12 May 2008 19:44:23 -0000
@@ -188,8 +188,10 @@
 $string['categories'] = 'Course categories';
 $string['category'] = 'Category';
 $string['categoryadded'] = 'The category \'$a\' was added';
+$string['categorycontent'] = 'Subcategories and courses';
 $string['categorydeleted'] = 'The category \'$a\' was deleted';
 $string['categoryduplicate'] = 'A category named \'$a\' already exists!';
+$string['categorymodifiedcancel'] = 'Category was modified! Please cancel and try again.';
 $string['categoryname'] = 'Category name';
 $string['categoryupdated'] = 'The category \'$a\' was updated';
 $string['changedpassword'] = 'Changed password';
@@ -364,7 +366,10 @@
 $string['deleteall'] = 'Delete all';
 $string['deleteallcomments'] = 'Delete all comments';
 $string['deleteallratings'] = 'Delete all ratings';
+$string['deletecategory'] = 'Delete category: $a';
 $string['deletecategorycheck'] = 'Are you absolutely sure you want to completely delete this category <b>\'$a\'</b>?<br />This will move all courses into the parent category if there is one, or into Miscellaneous.';
+$string['deletecategorycheck2'] = 'Are you absolutely sure you want to completely delete this category?<br />You can either delete all subcategories and courses or move them into existing category.<br />';
+$string['deletecategoryconfirm'] = 'Confirm category delete';
 $string['deletecheck'] = 'Delete $a ?';
 $string['deletecheckfiles'] = 'Are you absolutely sure you want to delete these files?';
 $string['deletecheckfull'] = 'Are you absolutely sure you want to completely delete $a ?';
@@ -959,6 +964,7 @@
 $string['mostrecently'] = 'most recently';
 $string['move'] = 'Move';
 $string['movecategoryto'] = 'Move category to:';
+$string['movecategorycontentto'] = 'Move into';
 $string['movecourseto'] = 'Move course to:';
 $string['movedown'] = 'Move down';
 $string['movefilestohere'] = 'Move files to here';
Index: lang/en_utf8/question.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lang/en_utf8/question.php,v
retrieving revision 1.7.2.1
diff -u -r1.7.2.1 question.php
--- lang/en_utf8/question.php	9 May 2008 15:05:39 -0000	1.7.2.1
+++ lang/en_utf8/question.php	12 May 2008 19:44:23 -0000
@@ -26,10 +26,12 @@
 $string['createdmodifiedheader'] = 'Created / Last Saved';
 $string['defaultfor'] = 'Default for $a';
 $string['defaultinfofor'] = 'The default category for questions shared in context \'$a\'.';
+$string['deletecoursecategorywithquestions'] = 'There are questions in the question bank associated with this course category. If you proceed, they will be deleted. You may wish to move them first, using the question bank interface.';
 $string['donothing']= 'Don\'t copy or move files or change links.';
 $string['editingcategory'] = 'Editing a category';
 $string['editingquestion'] = 'Editing a question';
 $string['erroraccessingcontext'] = 'Cannot access context';
+$string['errordeletingquestionsfromcategory'] = 'Error deleting questions from category $a.';
 $string['errorfilecannotbecopied'] = 'Error cannot copy file $a.';
 $string['errorfilecannotbemoved'] = 'Error cannot move file $a.';
 $string['errormovingquestions'] = 'Error while moving questions with ids $a.';
@@ -42,6 +44,7 @@
 $string['getcategoryfromfile'] = 'Get category from file';
 $string['getcontextfromfile'] = 'Get context from file';
 $string['ignorebroken'] = 'Ignore broken links';
+$string['invalidcontextinhasanyquestions'] = 'Invalid context passed to question_context_has_any_questions.';
 $string['linkedfiledoesntexist'] = 'Linked file $a doesn\'t exist';
 $string['makechildof'] = "Make Child of '\$a'";
 $string['maketoplevelitem'] = 'Move to top level';
@@ -49,6 +52,7 @@
 $string['modified'] = 'Last saved';
 $string['move']= 'Move from $a and change links.';
 $string['movecategory']= 'Move Category';
+$string['movedquestionsandcategories'] = 'Moved questions and question categories from $a->oldplace to $a->newplace.';
 $string['movelinksonly']= 'Just change where links point to, do not move or copy files.';
 $string['moveqtoanothercontext']= 'Move question to another context.';
 $string['moveq']= 'Move question(s)';
@@ -72,6 +76,9 @@
 $string['questionbank'] = 'Question bank';
 $string['questioncatsfor'] = 'Question Categories for \'$a\'';
 $string['questiondoesnotexist'] = 'This question does not exist';
+$string['questionsmovedto'] = 'Questions still in use moved to "$a" in the parent course category.';
+$string['questionsrescuedfrom'] = 'Questions saved from context $a.';
+$string['questionsrescuedfrominfo'] = 'These questions (some of which may be hidden) where saved when context $a was deleted because they are still used by some quizzes or other activities.';
 $string['questionuse'] = 'Use question in this activity';
 $string['shareincontext'] = 'Share in context for $a';
 $string['tofilecategory'] = 'Write category to file';
Index: course/editcategory.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/editcategory.php,v
retrieving revision 1.3.2.4
diff -u -r1.3.2.4 editcategory.php
--- course/editcategory.php	24 Apr 2008 08:35:47 -0000	1.3.2.4
+++ course/editcategory.php	12 May 2008 19:44:17 -0000
@@ -72,7 +72,7 @@
         } else {
             $newcategory->context = get_context_instance(CONTEXT_COURSECAT, $newcategory->id);
             mark_context_dirty($newcategory->context->path);
-            redirect('index.php?categoryedit=on', get_string('categoryadded', null, stripslashes($newcategory->name)));
+            redirect('index.php?categoryedit=on');
         }
     } elseif (has_capability('moodle/category:update', $context)) {
         $newcategory->id = $category->id;
Index: course/lib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/lib.php,v
retrieving revision 1.538.2.43
diff -u -r1.538.2.43 lib.php
--- course/lib.php	12 May 2008 10:44:38 -0000	1.538.2.43
+++ course/lib.php	12 May 2008 19:44:20 -0000
@@ -2694,6 +2694,103 @@
     return (record_exists('course_allowed_modules','course',$course->id,'module',$modid));
 }
 
+/**
+ * Recursively delete category including all subcategories and courses.
+ * @param object $ccategory
+ * @return bool status
+ */
+function category_delete_full($category, $showfeedback=true) {
+    global $CFG;
+    require_once($CFG->libdir.'/gradelib.php');
+    require_once($CFG->libdir.'/questionlib.php');
+
+    if ($children = get_records('course_categories', 'parent', $category->id, 'sortorder ASC')) {
+        foreach ($children as $childcat) {
+            if (!category_delete_full($childcat, $showfeedback)) {
+                notify("Error deleting category $childcat->name"); 
+                return false;
+            }
+        }
+    }
+
+    if ($courses = get_records('course', 'category', $category->id, 'sortorder ASC')) {
+        foreach ($courses as $course) {
+            if (!delete_course($course->id, false)) {
+                notify("Error deleting course $course->shortname"); 
+                return false;
+            }
+            notify("Deleted course $course->shortname", 'notifysuccess'); // TODO: localize 
+        }
+    }
+
+    // now delete anything that may depend on course category context
+    grade_course_category_delete($category->id, 0, $showfeedback);
+    if (!question_delete_course_category($category, 0, $showfeedback)) {
+        notify(get_string('errordeletingquestionsfromcategory', 'question', $category), 'notifysuccess'); 
+        return false;
+    }
+
+    // finally delete the category and it's context
+    delete_records('course_categories', 'id', $category->id);
+    delete_context(CONTEXT_COURSECAT, $category->id);
+
+    events_trigger('category_deleted', $category);
+
+    notify("Deleted category $category->name", 'notifysuccess'); // TODO: localize 
+
+    return true;
+}
+
+/**
+ * Delete category, but move contents to another category.
+ * @param object $ccategory
+ * @param int $newparentid category id
+ * @return bool status
+ */
+function category_delete_move($category, $newparentid, $showfeedback=true) {
+    global $CFG;
+    require_once($CFG->libdir.'/gradelib.php');
+    require_once($CFG->libdir.'/questionlib.php');
+
+    if (!$newparentcat = get_record('course_categories', 'id', $newparentid)) {
+        return false;
+    }
+
+    if ($children = get_records('course_categories', 'parent', $category->id, 'sortorder ASC')) {
+        foreach ($children as $childcat) {
+            if (!move_category($childcat, $newparentcat)) {
+                notify("Error moving category $childcat->name"); 
+                return false;
+            }
+        }
+    }
+
+    if ($courses = get_records('course', 'category', $category->id, 'sortorder ASC', 'id')) {
+        if (!move_courses(array_keys($courses), $newparentid)) {
+            notify("Error moving courses"); 
+            return false;
+        }
+        notify("Moved courses from $category->name", 'notifysuccess'); // TODO: localize 
+    }
+
+    // now delete anything that may depend on course category context
+    grade_course_category_delete($category->id, $newparentid, $showfeedback);
+    if (!question_delete_course_category($category, $newparentcat, $showfeedback)) {
+        notify(get_string('errordeletingquestionsfromcategory', 'question', $category), 'notifysuccess'); 
+        return false;
+    }
+
+    // finally delete the category and it's context
+    delete_records('course_categories', 'id', $category->id);
+    delete_context(CONTEXT_COURSECAT, $category->id);
+
+    events_trigger('category_deleted', $category);
+
+    notify("Deleted category $category->name", 'notifysuccess'); // TODO: localize 
+
+    return true;
+}
+
 /***
  *** Efficiently moves many courses around while maintaining
  *** sortorder in order.
Index: course/index.php
===================================================================
RCS file: /cvsroot/moodle/moodle/course/index.php,v
retrieving revision 1.89.2.3
diff -u -r1.89.2.3 index.php
--- course/index.php	9 Jan 2008 10:56:48 -0000	1.89.2.3
+++ course/index.php	12 May 2008 19:44:17 -0000
@@ -9,12 +9,11 @@
     $delete   = optional_param('delete',0,PARAM_INT);
     $hide     = optional_param('hide',0,PARAM_INT);
     $show     = optional_param('show',0,PARAM_INT);
-    $sure     = optional_param('sure','',PARAM_ALPHANUM);
     $move     = optional_param('move',0,PARAM_INT);
     $moveto   = optional_param('moveto',-1,PARAM_INT);
     $moveup   = optional_param('moveup',0,PARAM_INT);
     $movedown = optional_param('movedown',0,PARAM_INT);
-    
+
     $sysctx  = get_context_instance(CONTEXT_SYSTEM);
     $context = $sysctx;
 
@@ -77,7 +76,7 @@
             print_box_end();
         }
 
-        /// I am not sure this context in the next has_capability call is correct. 
+        /// I am not sure this context in the next has_capability call is correct.
         if (isloggedin() and !isguest() and !has_capability('moodle/course:create', $sysctx) and $CFG->enablecourserequests) {  // Print link to request a new course
             print_single_button('request.php', NULL, get_string('courserequest'), 'get');
         }
@@ -95,67 +94,59 @@
 
 /// From now on is all the admin/course creator functions
 
-/// Print headings
+/// Delete a category if necessary
 
-    if (has_capability('moodle/site:config', $sysctx)) {
-        require_once($CFG->libdir.'/adminlib.php');
-        admin_externalpage_setup('coursemgmt');
-        admin_externalpage_print_header();
-    } else {
-        print_header("$site->shortname: $strcategories", $strcourses,
-            build_navigation(array(array('name'=>$strcategories,'link'=>'','type'=>'misc'))), '', '', true, update_categories_button());
-    }
+    if (!empty($delete) and confirm_sesskey()) {
+        require_once('delete_category_form.php');
 
-    print_heading($strcategories);
+        if (!$deletecat = get_record('course_categories', 'id', $delete)) {
+            error('Incorrect category id', 'index.php');
+        }
 
-/// Delete a category if necessary
+        $heading = get_string('deletecategory', '', format_string($deletecat->name));
 
-    if (!empty($delete) and confirm_sesskey()) {
+        $context = get_context_instance(CONTEXT_COURSECAT, $delete);
+        require_capability('moodle/category:delete', $context);
 
-          // context is coursecat, if not present admins should have it set in site level
-         $context = get_context_instance(CONTEXT_COURSECAT, $delete);
-        if ($deletecat = get_record('course_categories', 'id', $delete) and has_capability('moodle/category:delete', $context)) {
-            if (!empty($sure) && $sure == md5($deletecat->timemodified)) {
-                /// Send the children categories to live with their grandparent
-                if ($childcats = get_records('course_categories', 'parent', $deletecat->id)) {
-                    foreach ($childcats as $childcat) {
-                        if (! set_field('course_categories', 'parent', $deletecat->parent, 'id', $childcat->id)) {
-                            error('Could not update a child category!', 'index.php');
-                        }
-                    }
-                }
+        $mform = new delete_category_form(null, $deletecat);
+        $mform->set_data(array('delete'=>$delete));
 
-                ///  If the grandparent is a valid (non-zero) category, then
-                ///  send the children courses to live with their grandparent as well
-                if ($deletecat->parent) {
-                    if ($childcourses = get_records('course', 'category', $deletecat->id)) {
-                        foreach ($childcourses as $childcourse) {
-                            if (! set_field('course', 'category', $deletecat->parent, 'id', $childcourse->id)) {
-                                error('Could not update a child course!', 'index.php');
-                            }
-                        }
-                    }
-                }
+        if ($mform->is_cancelled()) {
+            redirect('index.php');
 
-                /// Finally delete the category itself
-                if (delete_records('course_categories', 'id', $deletecat->id)) {
-                    notify(get_string('categorydeleted', '', format_string($deletecat->name)));
-                    // MLD-9983
-                    events_trigger('category_deleted', $deletecat);
-                }
+        } else if (!$data= $mform->get_data(false)) {
+            require_once($CFG->libdir . '/questionlib.php');
+            print_category_edit_header();
+            print_heading($heading);
+            print_box(get_string('deletecategorycheck2'), 'generalbox boxwidthnormal boxaligncenter');
+            if (question_context_has_any_questions($context)) {
+                print_box(get_string('deletecoursecategorywithquestions', 'question'), 
+                        'generalbox boxwidthnormal boxaligncenter');
             }
-            else {
-                $strdeletecategorycheck = get_string('deletecategorycheck','', format_string($deletecat->name));
-                notice_yesno($strdeletecategorycheck,
-                             "index.php?delete=$delete&amp;sure=".md5($deletecat->timemodified)."&amp;sesskey=$USER->sesskey",
-                             "index.php?sesskey=$USER->sesskey");
+            $mform->display();
+            print_footer();
+            exit();
+        }
 
-                print_footer();  
-                exit();
-            }
+        print_category_edit_header();
+        print_heading($heading);
+
+        if ($data->fulldelete) {
+            category_delete_full($deletecat, true);
+        } else {
+            category_delete_move($deletecat, $data->newparent, true);
         }
+
+        print_continue('index.php');
+
+        print_footer();
+        die;
     }
 
+/// Print headings
+    print_category_edit_header();
+    print_heading($strcategories);
+
 
 /// Create a default category if necessary
     if (!$categories = get_categories()) {    /// No category yet!
@@ -289,7 +280,7 @@
     echo '</table>';
 
     echo '<div class="buttons">';
-    
+
     if (!empty($category->id)) {
         // Print link to create a new course in current category
         if (has_capability('moodle/course:create', $context)) {
@@ -298,7 +289,7 @@
             print_single_button('edit.php', $options, get_string('addnewcourse'), 'get');
         }
     }else{
-        if (has_capability('moodle/course:create', $sysctx)) {  
+        if (has_capability('moodle/course:create', $sysctx)) {
             // print create course link to first category
             $options = array();
             $options = array('category' => get_field('course_categories', 'id', 'parent', '0'));
@@ -428,4 +419,17 @@
         }
     }
 }
+
+function print_category_edit_header() {
+    global $CFG;
+
+    if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
+        require_once($CFG->libdir.'/adminlib.php');
+        admin_externalpage_setup('coursemgmt');
+        admin_externalpage_print_header();
+    } else {
+        print_header("$site->shortname: $strcategories", get_string('courses'),
+            build_navigation(array(array('name'=>get_string('categories'),'link'=>'','type'=>'misc'))), '', '', true, update_categories_button());
+    }
+}
 ?>
Index: lib/questionlib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/questionlib.php,v
retrieving revision 1.119.2.7
diff -u -r1.119.2.7 questionlib.php
--- lib/questionlib.php	12 May 2008 17:29:47 -0000	1.119.2.7
+++ lib/questionlib.php	12 May 2008 19:44:26 -0000
@@ -266,6 +266,29 @@
 }
 
 /**
+ * Determine whether there arey any questions belonging to this context, that is whether any of its
+ * question categories contain any questions. This will return true even if all the questions are
+ * hidden.
+ *
+ * @param mixed $context either a context object, or a context id.
+ * @return boolean whether any of the question categories beloning to this context have
+ *         any questions in them.
+ */
+function question_context_has_any_questions($context) {
+    global $CFG;
+    if (is_object($context)) {
+        $contextid = $context->id;
+    } else if (is_numeric($context)) {
+        $contextid = $context;
+    } else {
+        print_error('invalidcontextinhasanyquestions', 'question');
+    }
+    return record_exists_sql('SELECT * FROM ' . $CFG->prefix . 'question q ' .
+            'JOIN ' . $CFG->prefix . 'question_categories qc ON qc.id = q.category ' .
+            "WHERE qc.contextid = $contextid AND q.parent = 0");
+}
+
+/**
  * Returns list of 'allowed' grades for grade selection
  * formatted suitably for dropdown box function
  * @return object ->gradeoptionsfull full array ->gradeoptions +ve only
@@ -519,6 +542,116 @@
 }
 
 /**
+ * Category is about to be deleted,
+ * 1/ All question categories and their questions are deleted for this course category.
+ * 2/ All questions are moved to new category
+ *
+ * @param object $category course category object
+ * @param object $newcategory empty means everything deleted, otherwise id of category where content moved
+ * @param boolean $feedback to specify if the process must output a summary of its work
+ * @return boolean
+ */
+function question_delete_course_category($category, $newcategory, $feedback=true) {
+    $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
+    if (empty($newcategory)) {
+        $feedbackdata   = array(); // To store feedback to be showed at the end of the process
+        $rescueqcategory = null; // See the code around the call to question_save_from_deletion.
+        $strcatdeleted = get_string('unusedcategorydeleted', 'quiz');
+
+        // Loop over question categories.
+        if ($categories = get_records('question_categories', 'contextid', $context->id, 'parent', 'id, parent, name')) {
+            foreach ($categories as $category) {
+    
+                // Deal with any questions in the category.
+                if ($questions = get_records('question', 'category', $category->id)) {
+
+                    // Try to delete each question.
+                    foreach ($questions as $question) {
+                        delete_question($question->id);
+                    }
+
+                    // Check to see if there were any questions that were kept because they are
+                    // still in use somehow, even though quizzes in courses in this category will
+                    // already have been deteted. This could happen, for example, if questions are
+                    // added to a course, and then that course is moved to another category (MDL-14802).
+                    $questionids = get_records_select_menu('question', 'category = ' . $category->id, '', 'id,1');
+                    if (!empty($questionids)) {
+                        if (!$rescueqcategory = question_save_from_deletion(implode(',', array_keys($questionids)),
+                                get_parent_contextid($context), print_context_name($context), $rescueqcategory)) {
+                            return false;
+                       }
+                       $feedbackdata[] = array($category->name, get_string('questionsmovedto', 'question', $rescueqcategory->name));
+                    }
+                }
+
+                // Now delete the category.
+                if (!delete_records('question_categories', 'id', $category->id)) {
+                    return false;
+                }
+                $feedbackdata[] = array($category->name, $strcatdeleted);
+
+            } // End loop over categories.
+        }
+
+        // Output feedback if requested.
+        if ($feedback) {
+            $table = new stdClass;
+            $table->head = array(get_string('category','quiz'), get_string('action'));
+            $table->data = $feedbackdata;
+            print_table($table);
+        }
+
+    } else {
+        // Move question categories ot the new context.
+        if (!$newcontext = get_context_instance(CONTEXT_COURSECAT, $newcategory->id)) {
+            return false;
+        }
+        if (!set_field('question_categories', 'contextid', $newcontext->id, 'contextid', $context->id)) {
+            return false;
+        }
+        if ($feedback) {
+            $a = new stdClass;
+            $a->oldplace = print_context_name($context);
+            $a->newplace = print_context_name($newcontext);
+            notify(get_string('movedquestionsandcategories', 'question', $a), 'notifysuccess');
+        }
+    }
+
+    return true;
+}
+
+/**
+ * Enter description here...
+ *
+ * @param string $questionids list of questionids
+ * @param object $newcontext the context to create the saved category in.
+ * @param string $oldplace a textual description of the think being deleted, e.g. from get_context_name 
+ * @param object $newcategory
+ * @return mixed false on 
+ */
+function question_save_from_deletion($questionids, $newcontextid, $oldplace, $newcategory = null) {
+    // Make a category in the parent context to move the questions to.
+    if (is_null($newcategory)) {
+        $newcategory = new object();
+        $newcategory->parent = 0;
+        $newcategory->contextid = $newcontextid;
+        $newcategory->name = addslashes(get_string('questionsrescuedfrom', 'question', $oldplace));
+        $newcategory->info = addslashes(get_string('questionsrescuedfrominfo', 'question', $oldplace));
+        $newcategory->sortorder = 999;
+        $newcategory->stamp = make_unique_id_code();
+        if (!$newcategory->id = insert_record('question_categories', $newcategory)) {
+            return false;
+        }
+    }
+
+    // Move any remaining questions to the 'saved' category.
+    if (!question_move_questions_to_category($questionids, $newcategory->id)) {
+        return false;
+    }
+    return $newcategory;
+}
+
+/**
  * All question categories and their questions are deleted for this activity.
  *
  * @param object $cm the course module object representing the activity
Index: lib/gradelib.php
===================================================================
RCS file: /cvsroot/moodle/moodle/lib/gradelib.php,v
retrieving revision 1.120.2.25
diff -u -r1.120.2.25 gradelib.php
--- lib/gradelib.php	16 Mar 2008 23:21:35 -0000	1.120.2.25
+++ lib/gradelib.php	12 May 2008 19:44:24 -0000
@@ -1186,7 +1186,7 @@
 /**
  * Remove all grade related course data - history is kept
  * @param int $courseid
- * @param bool @showfeedback print feedback
+ * @param bool $showfeedback print feedback
  */
 function remove_course_grades($courseid, $showfeedback) {
     $strdeleted = get_string('deleted');
@@ -1223,6 +1223,17 @@
 }
 
 /**
+ * Called when course category deleted - cleanup gradebook
+ * @param int $categoryid course category id
+ * @param int $newparentid empty means everything deleted, otherwise id of category where content moved
+ * @param bool $showfeedback print feedback
+ */
+function grade_course_category_delete($categoryid, $newparentid, $showfeedback) {
+    $context = get_context_instance(CONTEXT_COURSECAT, $categoryid);
+    delete_records('grade_letters', 'contextid', $context->id);
+}
+
+/**
  * Does gradebook cleanup when module uninstalled.
  */
 function grade_uninstalled_module($modname) {
Index: course/delete_category_form.php
===================================================================
RCS file: course/delete_category_form.php
diff -N course/delete_category_form.php
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ course/delete_category_form.php	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,106 @@
+<?php  //$Id: $
+
+require_once($CFG->libdir.'/formslib.php');
+
+class delete_category_form extends moodleform {
+
+    var $_category;
+
+    function definition() {
+        global $CFG;
+
+        $mform    =& $this->_form;
+        $category = $this->_customdata;
+        $this->_category = $category;
+
+        $mform->addElement('header','general', get_string('delete'));
+
+        $mform->addElement('static', 'categorylabel', get_string('category'), format_string($category->name));
+
+        $displaylist = array();
+        $parentlist = array();
+        $children = array();
+        make_categories_list($displaylist, $parentlist);
+        unset($displaylist[$category->id]);
+        foreach ($displaylist as $catid=>$unused) {
+            // remove all children of $category
+            if (isset($parentlist[$catid]) and in_array($category->id, $parentlist[$catid])) {
+                $children[] = $catid;
+                unset($displaylist[$catid]);
+                continue;
+            }
+            if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $catid))) {
+                unset($displaylist[$catid]);
+            }
+        }
+
+        $candeletecontent = true;
+        foreach ($children as $catid) {
+            $context = get_context_instance(CONTEXT_COURSECAT, $catid);
+            if (!has_capability('moodle/category:delete', $context)) {
+                $candeletecontent = false;
+                break;
+            }
+        }
+
+        $options = array();
+
+        if ($displaylist) {
+            $options[0] = get_string('move');
+        }
+
+        if ($candeletecontent) {
+            $options[1] =get_string('delete');
+        }
+
+        if (empty($options)) {
+            print_error('nocategorydelete', 'error', 'index.php', format_string($category->name));
+        }
+
+        $mform->addElement('select', 'fulldelete', get_string('categorycontent'), $options);
+        $mform->disabledIf('newparent', 'fulldelete', 'eq', '1');
+        $mform->setDefault('newparent', 0);
+
+        if ($displaylist) {
+            $mform->addElement('select', 'newparent', get_string('movecategorycontentto'), $displaylist);
+            if (in_array($category->parent, $displaylist)) {
+                $mform->setDefault('newparent', $category->parent);
+            }
+        }
+
+        $mform->addElement('select', 'confirmdelete', get_string('deletecategoryconfirm'), array(0=>get_string('no'), 1=>get_string('yes')));
+        $mform->disabledIf('confirmdelete', 'fulldelete', 'noteq', '1');
+        
+
+        $mform->addElement('hidden', 'delete');
+        $mform->addElement('hidden', 'sure');
+        $mform->setDefault('sure', md5(serialize($category)));
+
+//--------------------------------------------------------------------------------
+        $this->add_action_buttons(true, get_string('delete'));
+
+    }
+
+/// perform some extra moodle validation
+    function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        if (!empty($data['fulldelete'])) {
+            if (empty($data['confirmdelete'])) {
+                $errors['confirmdelete'] = get_string('required');
+            }
+            // already verified
+        } else {
+            if (empty($data['newparent'])) {
+                $errors['newparent'] = get_string('required');
+            }
+        }
+
+        if ($data['sure'] != md5(serialize($this->_category))) {
+            $errors['categorylabel'] = get_string('categorymodifiedcancel');
+        }
+
+        return $errors;
+    }
+}
+?>
