$page = page_create_object(PAGE_COURSE_VIEW, $newid);
blocks_repopulate_page($page); // Return value not checked because you can always edit later
- $cat = new Object();
- $cat->name = get_string('miscellaneous');
- $cat->depth = 1;
- if ($catid = $DB->insert_record('course_categories', $cat)) {
- // make sure category context exists
- get_context_instance(CONTEXT_COURSECAT, $catid);
- mark_context_dirty('/'.SYSCONTEXTID);
- // do nothing
- } else {
- print_error('cannotsetupcategory', 'error');
- }
+ // create default course category
+ $cat = get_course_category();
+
} else {
print_error('cannotsetupsite', 'error');
}
$page = page_create_object(PAGE_COURSE_VIEW, $newid);
blocks_repopulate_page($page); // Return value not checked because you can always edit later
- $cat = new object();
- $cat->name = get_string('miscellaneous');
- $cat->depth = 1;
- if (!$catid = $DB->insert_record('course_categories', $cat)) {
- print_error('cannotsetupcategory', 'error');
- }
- // make sure category context exists
- get_context_instance(CONTEXT_COURSECAT, $catid);
- mark_context_dirty('/'.SYSCONTEXTID);
+ // create default course category
+ $cat = get_course_category();
redirect('index.php');
}
$category = $DB->get_record("course_categories", array("name"=>$course_header->category->name));
}
- //If no exists, get category id 1
+ //If no exists, get default category
if (!$category) {
- $category = $DB->get_record("course_categories", array("id"=>"1"));
+ $category = get_course_category();
}
- //If category 1 doesn'exists, lets create the course category (get it from backup file)
- if (!$category) {
- $ins_category = new object();
- $ins_category->name = $course_header->category->name;
- $ins_category->parent = 0;
- $ins_category->sortorder = 0;
- $ins_category->coursecount = 0;
- $ins_category->visible = 0; //To avoid interferences with the rest of the site
- $ins_category->timemodified = time();
- $newid = $DB->insert_record("course_categories",$ins_category);
- $category->id = $newid;
- $category->name = $course_header->category->name;
- }
- //If exists, put new category id
- if ($category) {
- $course_header->category->id = $category->id;
- $course_header->category->name = $category->name;
- //Error, cannot locate category
- } else {
- $course_header->category->id = 0;
- $course_header->category->name = get_string("unknowncategory");
- $status = false;
- }
+ $course_header->category->id = $category->id;
+ $course_header->category->name = $category->name;
//Create the course_object
if ($status) {
$course->enrolenddate += $restore->course_startdateoffset;
}
$course->enrolperiod = $course_header->course_enrolperiod;
- //Calculate sortorder field
- $sortmax = $DB->get_record_sql('SELECT MAX(sortorder) AS max
- FROM {course}
- WHERE category=?', array($course->category));
- if (!empty($sortmax->max)) {
- $course->sortorder = $sortmax->max + 1;
- unset($sortmax);
- } else {
- $course->sortorder = 100;
- }
+ //Put as last course in category
+ $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - 1;
//Now, recode some languages (Moodle 1.5)
if ($course->lang == 'ma_nt') {
if ($resort and confirm_sesskey()) {
if ($courses = get_courses($category->id, "fullname ASC", 'c.id,c.fullname,c.sortorder')) {
- // move it off the range
- $count = $DB->get_record_sql('SELECT MAX(sortorder) AS max, 1 FROM {course} WHERE category= ?', array($category->id));
- $count = $count->max + 100;
- $DB->begin_sql();
+ $i = 1;
foreach ($courses as $course) {
- $DB->set_field('course', 'sortorder', $count, array('id'=>$course->id));
- $count++;
+ $DB->set_field('course', 'sortorder', $category->sortorder+$i, array('id'=>$course->id));
+ $i++;
}
- $DB->commit_sql();
- fix_course_sortorder($category->id);
+ fix_course_sortorder(); // should not be needed
}
}
}
if ((!empty($moveup) or !empty($movedown)) and confirm_sesskey()) {
require_capability('moodle/category:update', $context);
- $movecourse = NULL;
- $swapcourse = NULL;
-
- // ensure the course order has no gaps
- // and isn't at 0
- fix_course_sortorder($category->id);
- // we are going to need to know the range
- $max = $DB->get_record_sql('SELECT MAX(sortorder) AS max, 1 FROM {course} WHERE category=?', array($category->id));
- $max = $max->max + 100;
+ // ensure the course order has continuous ordering
+ fix_course_sortorder();
+ $swapcourse = NULL;
if (!empty($moveup)) {
- $movecourse = $DB->get_record('course', array('id'=>$moveup));
- $swapcourse = $DB->get_record('course', array('category'=>$category->id, 'sortorder'=>($movecourse->sortorder-1)));
+ if ($movecourse = $DB->get_record('course', array('id'=>$moveup))) {
+ $swapcourse = $DB->get_record('course', array('sortorder'=>$movecourse->sortorder-1));
+ }
} else {
- $movecourse = $DB->get_record('course', array('id'=>$movedown));
- $swapcourse = $DB->get_record('course', array('category'=>$category->id, 'sortorder'=>($movecourse->sortorder+1)));
- }
-
- if ($swapcourse and $movecourse) { // Renumber everything for robustness
- $DB->begin_sql();
- if (!( $DB->set_field("course", "sortorder", $max, array("id"=>$swapcourse->id))
- && $DB->set_field("course", "sortorder", $swapcourse->sortorder, array("id"=>$movecourse->id))
- && $DB->set_field("course", "sortorder", $movecourse->sortorder, array("id"=>$swapcourse->id))
- )) {
- notify("Could not update that course!");
+ if ($movecourse = $DB->get_record('course', array('id'=>$movedown))) {
+ $swapcourse = $DB->get_record('course', array('sortorder'=>$movecourse->sortorder+1));
}
- $DB->commit_sql();
}
-
+ if ($swapcourse and $movecourse) {
+ $DB->set_field('course', 'sortorder', $swapcourse->sortorder, array('id'=>$movecourse->id));
+ $DB->set_field('course', 'sortorder', $movecourse->sortorder, array('id'=>$swapcourse->id));
+ }
}
} // End of editing stuff
$newcategory = new stdClass();
$newcategory->name = $data->name;
$newcategory->description = $data->description;
- $newcategory->sortorder = 999;
$newcategory->parent = $data->parent; // if $id = 0, the new category will be a top-level category
if (!empty($data->theme) && !empty($CFG->allowcategorythemes)) {
theme_setup();
}
- if (empty($category) && has_capability('moodle/category:create', $context)) { // Create a new category
+ if (empty($category) && has_capability('moodle/category:create', $context)) { // Create a new category
+ $newcategory->sortorder = MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES; // put as last category in any parent cat
if (!$newcategory->id = $DB->insert_record('course_categories', $newcategory)) {
notify( "Could not insert the new category '$newcategory->name' ");
} else {
$newcategory->context = get_context_instance(CONTEXT_COURSECAT, $newcategory->id);
mark_context_dirty($newcategory->context->path);
+ fix_course_sortorder();
redirect('index.php?categoryedit=on');
}
} elseif (has_capability('moodle/category:update', $context)) {
if ($newcategory->parent != $category->parent) {
$parent_cat = $DB->get_record('course_categories', array('id'=>$newcategory->parent));
- move_category($newcategory, $parent_cat);
+ move_category($newcategory, $parent_cat); // includes sortorder fix
}
if (!$DB->update_record('course_categories', $newcategory)) {
} else {
$redirect_link = 'category.php?id='.$newcategory->id.'&categoryedit=on';
}
- fix_course_sortorder();
redirect($redirect_link);
}
}
if ((!empty($moveup) or !empty($movedown)) and confirm_sesskey()) {
+ fix_course_sortorder();
$swapcategory = NULL;
- $movecategory = NULL;
if (!empty($moveup)) {
if ($movecategory = $DB->get_record('course_categories', array('id'=>$moveup))) {
- $categories = get_categories($movecategory->parent);
-
- foreach ($categories as $category) {
- if ($category->id == $movecategory->id) {
- break;
- }
- $swapcategory = $category;
+ if ($swapcategory = $DB->get_records_select('course_categories', "sortorder<? AND parent=?", array($movecategory->sortorder, $movecategory->parent), 'sortorder ASC', '*', 0, 1)) {
+ $swapcategory = reset($swapcategory);
}
- unset($category);
}
- }
- if (!empty($movedown)) {
+ } else {
if ($movecategory = $DB->get_record('course_categories', array('id'=>$movedown))) {
- $categories = get_categories($movecategory->parent);
-
- $choosenext = false;
- foreach ($categories as $category) {
- if ($choosenext) {
- $swapcategory = $category;
- break;
- }
- if ($category->id == $movecategory->id) {
- $choosenext = true;
- }
+ if ($swapcategory = $DB->get_records_select('course_categories', "sortorder>? AND parent=?", array($movecategory->sortorder, $movecategory->parent), 'sortorder ASC', '*', 0, 1)) {
+ $swapcategory = reset($swapcategory);
}
- unset($category);
}
}
- if ($swapcategory and $movecategory) { // Renumber everything for robustness
- $count=0;
- foreach ($categories as $category) {
- $count++;
- if ($category->id == $swapcategory->id) {
- $category = $movecategory;
- } else if ($category->id == $movecategory->id) {
- $category = $swapcategory;
- }
- if (!$DB->set_field('course_categories', 'sortorder', $count, array('id'=>$category->id))) {
- notify('Could not update that category!');
- }
- }
- unset($category);
+ if ($swapcategory and $movecategory) {
+ $DB->set_field('course_categories', 'sortorder', $swapcategory->sortorder, array('id'=>$movecategory->id));
+ $DB->set_field('course_categories', 'sortorder', $movecategory->sortorder, array('id'=>$swapcategory->id));
}
- }
-/// Find any orphan courses that don't yet have a valid category and set to default
- fix_coursecategory_orphans();
+ // finally reorder courses
+ fix_course_sortorder();
+ }
-/// Should be a no-op 99% of the cases
- fix_course_sortorder();
+/// This should not be needed anymore
+ //fix_course_sortorder();
/// Print out the categories with all the knobs
function move_courses($courseids, $categoryid) {
global $CFG, $DB;
- if (!empty($courseids)) {
+ if (!empty($courseids) and $category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
+ $courseids = array_reverse($courseids);
+ $i = 1;
- $courseids = array_reverse($courseids);
-
- foreach ($courseids as $courseid) {
-
- if (! $course = $DB->get_record("course", array("id"=>$courseid))) {
- notify("Error finding course $courseid");
- } else {
- // figure out a sortorder that we can use in the destination category
- $sortorder = $DB->get_field_sql('SELECT MIN(sortorder)-1 AS min
- FROM {course} WHERE category=?', array($categoryid));
- if (is_null($sortorder) || $sortorder === false) {
- // the category is empty
- // rather than let the db default to 0
- // set it to > 100 and avoid extra work in fix_coursesortorder()
- $sortorder = 200;
- } else if ($sortorder < 10) {
- fix_course_sortorder($categoryid);
- }
-
- $course->category = $categoryid;
- $course->sortorder = $sortorder;
- $course->fullname = $course->fullname;
- $course->shortname = $course->shortname;
- $course->summary = $course->summary;
- $course->password = $course->password;
- $course->teacher = $course->teacher;
- $course->teachers = $course->teachers;
- $course->student = $course->student;
- $course->students = $course->students;
-
- if (!$DB->update_record('course', $course)) {
- notify("An error occurred - course not moved!");
- }
+ foreach ($courseids as $courseid) {
+ if (!$course = $DB->get_record("course", array("id"=>$courseid))) {
+ notify("Error finding course $courseid");
+ } else {
+ $course->category = $categoryid;
+ $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
- $context = get_context_instance(CONTEXT_COURSE, $course->id);
- $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
- context_moved($context, $newparent);
+ if (!$DB->update_record('course', $course)) {
+ notify("An error occurred - course not moved!");
}
+
+ $context = get_context_instance(CONTEXT_COURSE, $course->id);
+ $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
+ context_moved($context, $newparent);
}
- fix_course_sortorder();
}
+ fix_course_sortorder();
+ }
return true;
}
if (!$DB->set_field('course_categories', 'parent', 0, array('id'=>$category->id))) {
return false;
}
+
$newparent = get_context_instance(CONTEXT_SYSTEM);
+
} else {
if (!$DB->set_field('course_categories', 'parent', $newparentcat->id, array('id'=>$category->id))) {
return false;
context_moved($context, $newparent);
- // The most effective thing would be to find the common parent,
- // until then, do it sitewide...
+ // now make it last in new category
+ $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('id'=>$category->id));
+
+ // and fix the sortorders
fix_course_sortorder();
return true;
$data->timecreated = time();
- // place at beginning of category
- fix_course_sortorder();
- $data->sortorder = $DB->get_field_sql("SELECT MIN(sortorder)-1 FROM {course} WHERE category=?", array($data->category));
- if (empty($data->sortorder)) {
- $data->sortorder = 100;
- }
+ // place at beginning of any category
+ $data->sortorder = 0;
if ($newcourseid = $DB->insert_record('course', $data)) { // Set up new course
if (!empty($approve) and confirm_sesskey()) {
if ($course = $DB->get_record("course_request", array("id"=>$approve))) {
- // place at beginning of category
- fix_course_sortorder();
-
- if (empty($CFG->defaultrequestcategory) or !$DB->record_exists('course_categories', array('id'=>$CFG->defaultrequestcategory))) {
- // default to first top level directory, hacky but means things don't break
- $CFG->defaultrequestcategory = $DB->get_field('course_categories', 'id', array('parent'=>'0'));
- }
+ $category = get_course_category($CFG->defaultrequestcategory);
$course->category = $CFG->defaultrequestcategory;
- $course->sortorder = $DB->get_field_sql("SELECT min(sortorder)-1 FROM {course} WHERE category=?", array($course->category));
- if (empty($course->sortorder)) {
- $course->sortorder = 1000;
- }
+ $course->sortorder = $category->sortorder; // place as the first in category
$course->requested = 1;
unset($course->reason);
unset($course->id);
}
$DB->delete_records('course_request', array('id'=>$approve));
$success = 1;
+ fix_course_sortorder();
}
if (!empty($success)) {
$user = $DB->get_record('user', array('id'=>$teacherid));
global $CFG, $DB;
// define a template
- if(!empty($CFG->enrol_db_template)){
+ if (!empty($CFG->enrol_db_template)){
$template = $DB->get_record("course", array('shortname'=>$CFG->enrol_db_template));
$template = (array)$template;
} else {
}
}
- $course->category = 1; // the misc 'catch-all' category
- if (!empty($CFG->enrol_db_category)){ //category = 0 or undef will break moodle
- $course->category = $CFG->enrol_db_category;
- }
+ $category = get_course_category($CFG->enrol_db_category);
- // define the sortorder
- $sort = $DB->get_field_sql('SELECT COALESCE(MAX(sortorder)+1, 100) AS max FROM {course} WHERE category= ?', array($course->category));
- $course->sortorder = $sort;
+ // put at the end of category
+ $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - 1;
// override with local data
$course->startdate = time() + 3600 * 24;
// store it and log
if ($newcourseid = $DB->insert_record("course", $course)) { // Set up new course
- $section = NULL;
- $section->course = $newcourseid; // Create a default section.
+ $section = new object();
+ $section->course = $newcourseid; // Create a default section.
$section->section = 0;
$section->id = $DB->insert_record("course_sections", $section);
$page = page_create_object(PAGE_COURSE_VIEW, $newcourseid);
blocks_repopulate_page($page); // Return value no
-
- if(!$skip_fix_course_sortorder){
+ if (!$skip_fix_course_sortorder){
fix_course_sortorder();
}
add_to_log($newcourseid, "course", "new", "view.php?id=$newcourseid", "enrol/database auto-creation");
$course->startdate = time();
$course->numsections = 1;
// Choose a sort order that puts us at the start of the list!
- $sortinfo = $DB->get_record_sql('SELECT MIN(sortorder) AS min, MAX(sortorder) AS max FROM {course} WHERE category<>0');
- if (is_object($sortinfo)) { // no courses?
- $max = $sortinfo->max;
- $min = $sortinfo->min;
- unset($sortinfo);
- $course->sortorder = $min - 1;
- }else{
- $course->sortorder = 1000;
- }
+ $course->sortorder = 0;
+
if($course->id = $DB->insert_record('course', $course)){
// Setup the blocks
global $CFG, $DB;
// override defaults with template course
- if(!empty($CFG->enrol_ldap_template)){
+ if (!empty($CFG->enrol_ldap_template)){
$course = $DB->get_record("course", array('shortname'=>$CFG->enrol_ldap_template));
unset($course->id); // so we are clear to reinsert the record
unset($course->sortorder);
$course->summary = empty($CFG->enrol_ldap_course_summary) || empty($course_ext[$CFG->enrol_ldap_course_summary][0])
? ''
: $course_ext[$CFG->enrol_ldap_course_summary][0];
-
- if(!empty($CFG->enrol_ldap_category)){ // optional ... but ensure it is set!
- $course->category = $CFG->enrol_ldap_category;
- }
- if ($course->category == 0){ // must be avoided as it'll break moodle
- $course->category = 1; // the misc 'catch-all' category
- }
- // define the sortorder (yuck)
- $sort = $DB->get_record_sql('SELECT MAX(sortorder) AS max, 1 FROM {course} WHERE category=?', array($course->category));
- $sort = $sort->max;
- $sort++;
- $course->sortorder = $sort;
+ $category = get_course_category($CFG->enrol_db_category);
+
+ // put at the end of category
+ $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - 1;
// override with local data
$course->startdate = time();
// store it and log
if ($newcourseid = $DB->insert_record("course", $course)) { // Set up new course
$section = new object();
- $section->course = $newcourseid; // Create a default section.
+ $section->course = $newcourseid; // Create a default section.
$section->section = 0;
$section->id = $DB->insert_record("course_sections", $section);
$page = page_create_object(PAGE_COURSE_VIEW, $newcourseid);
blocks_repopulate_page($page); // Return value no
-
- if(!$skip_fix_course_sortorder){
+ if (!$skip_fix_course_sortorder){
fix_course_sortorder();
}
add_to_log($newcourseid, "course", "new", "view.php?id=$newcourseid", "enrol/ldap auto-creation");
return ($cap->component != $comp || $cap->contextlevel != $contextlevel);
}
+/**
+ * Rebuild all related context depth and path caches
+ * @param array $fixcontexts array of contexts
+ */
+function rebuild_contexts(array $fixcontexts) {
+ global $DB;
+
+ foreach ($fixcontexts as $context) {
+ if ($context->path) {
+ mark_context_dirty($context->path);
+ }
+ $DB->set_field_select('context', 'depth', 0, "path LIKE '%/$context->id/%'");
+ $DB->set_field('context', 'depth', 0, array('id'=>$context->id));
+ }
+ build_context_path(false);
+}
+
/**
* Populate context.path and context.depth where missing.
* @param bool $force force a complete rebuild of the path and depth fields.
* @package moodlecore
*/
+define('MAX_COURSES_IN_CATEGORY', 10000); // MAX_COURSES_IN_CATEGORY * MAX_COURSE_CATEGORIES must not be more than max integer!
+define('MAX_COURSE_CATEGORIES', 10000);
+
/**
* Sets up global $DB moodle_database instance
* @return void
return $subcats;
}
-
/**
-* This recursive function makes sure that the courseorder is consecutive
-*
-* @param type description
-*
-* $n is the starting point, offered only for compatilibity -- will be ignored!
-* $safe (bool) prevents it from assuming category-sortorder is unique, used to upgrade
-* safely from 1.4 to 1.5
-*/
-function fix_course_sortorder($categoryid=0, $n=0, $safe=0, $depth=0, $path='') {
- global $CFG, $DB;
+ * Return specified category, default if given does not exist
+ * @param int $catid course category id
+ * @return object caregory
+ */
+function get_course_category($catid=0) {
+ global $DB;
+
+ $category = false;
+
+ if (!empty($catid)) {
+ $category = $DB->get_record('course_categories', array('id'=>$catid));
+ }
- $count = 0;
-
- $catgap = 1000; // "standard" category gap
- $tolerance = 200; // how "close" categories can get
-
- if ($categoryid > 0){
- // update depth and path
- $cat = $DB->get_record('course_categories', array('id'=>$categoryid));
- if ($cat->parent == 0) {
- $depth = 0;
- $path = '';
- } else if ($depth == 0 ) { // doesn't make sense; get from DB
- // this is only called if the $depth parameter looks dodgy
- $parent = $DB->get_record('course_categories', array('id'=>$cat->parent));
- $path = $parent->path;
- $depth = $parent->depth;
+ if (!$category) {
+ // the first category is considered default for now
+ if ($category = $DB->get_records('course_categories', null, 'sortorder', '*', 0, 1)) {
+ $category = reset($category);
+
+ } else {
+ $cat = new object();
+ $cat->name = get_string('miscellaneous');
+ $cat->depth = 1;
+ $cat->sortorder = MAX_COURSES_IN_CATEGORY;
+ $cat->timemodified = time();
+ if (!$catid = $DB->insert_record('course_categories', $cat)) {
+ print_error('cannotsetupcategory', 'error');
+ }
+ // make sure category context exists
+ get_context_instance(CONTEXT_COURSECAT, $catid);
+ mark_context_dirty('/'.SYSCONTEXTID);
+ $category = $DB->get_record('course_categories', array('id'=>$catid));
}
- $path = $path . '/' . $categoryid;
- $depth = $depth + 1;
+ }
+
+ return $category;
+}
+
+/**
+ * Fixes course category and course sortorder, also verifies category and course parents and paths.
+ * (circular references are not fixed)
+ */
+function fix_course_sortorder() {
+ global $DB, $SITE;
+
+ //WARNING: this is PHP5 only code!
+
+ if ($unsorted = $DB->get_records('course_categories', array('sortorder'=>0))) {
+ //move all categories that are not sorted yet to the end
+ $DB->set_field('course_categories', 'sortorder', MAX_COURSES_IN_CATEGORY*MAX_COURSE_CATEGORIES, array('sortorder'=>0));
+ }
- if ($cat->path !== $path) {
- $DB->set_field('course_categories', 'path', $path, array('id'=>$categoryid));
+ $allcats = $DB->get_records('course_categories', null, 'sortorder, id', 'id, sortorder, parent, depth, path');
+ $topcats = array();
+ $brokencats = array();
+ foreach ($allcats as $cat) {
+ $sortorder = (int)$cat->sortorder;
+ if (!$cat->parent) {
+ while(isset($topcats[$sortorder])) {
+ $sortorder++;
+ }
+ $topcats[$sortorder] = $cat;
+ continue;
}
- if ($cat->depth != $depth) {
- $DB->set_field('course_categories', 'depth', $depth, array('id'=>$categoryid));
+ if (!isset($allcats[$cat->parent])) {
+ $brokencats[] = $cat;
+ continue;
}
+ if (!isset($allcats[$cat->parent]->children)) {
+ $allcats[$cat->parent]->children = array();
+ }
+ while(isset($allcats[$cat->parent]->children[$sortorder])) {
+ $sortorder++;
+ }
+ $allcats[$cat->parent]->children[$sortorder] = $cat;
}
+ unset($allcats);
- // get some basic info about courses in the category
- $info = $DB->get_record_sql("SELECT MIN(sortorder) AS min,
- MAX(sortorder) AS max,
- COUNT(sortorder) AS count
- FROM {course}
- WHERE category=?", array($categoryid));
- if ($info) { // no courses?
- $max = $info->max;
- $count = $info->count;
- $min = $info->min;
- unset($info);
+ // add broken cats to category tree
+ if ($brokencats) {
+ $defaultcat = reset($topcats);
+ foreach ($brokencats as $cat) {
+ $topcats[] = $cat;
+ }
}
- if ($categoryid > 0 && $n==0) { // only passed category so don't shift it
- $n = $min;
+ // now walk recursively the tree and fix any problems found
+ $sortorder = 0;
+ $fixcontexts = array();
+ _fix_course_cats($topcats, $sortorder, 0, 0, '', $fixcontexts);
+
+ // detect if there are "multiple" frontpage courses and fix them if needed
+ $frontcourses = $DB->get_records('course', array('category'=>0), 'id');
+ if (count($frontcourses) > 1) {
+ if (isset($frontcourses[SITEID])) {
+ $frontcourse = $frontcourses[SITEID];
+ unset($frontcourses[SITEID]);
+ } else {
+ $frontcourse = array_shift($frontcourses);
+ }
+ $defaultcat = reset($topcats);
+ foreach ($frontcourses as $course) {
+ $DB->set_field('course', 'category', $defaultcat->id, array('id'=>$course->id));
+ $context = get_context_instance(CONTEXT_COURSE, $course->id);
+ $fixcontexts[$context->id] = $context;
+ }
+ unset($frontcourses);
+ } else {
+ $frontcourse = reset($frontcourses);
}
- // $hasgap flag indicates whether there's a gap in the sequence
- $hasgap = false;
- if ($max-$min+1 != $count) {
- $hasgap = true;
+ // now fix the paths and depths in context table if needed
+ if ($fixcontexts) {
+ rebuild_contexts($fixcontexts);
}
- // $mustshift indicates whether the sequence must be shifted to
- // meet its range
- $mustshift = false;
- if ($min < $n+$tolerance || $min > $n+$tolerance+$catgap ) {
- $mustshift = true;
+ // release memory
+ unset($topcats);
+ unset($brokencats);
+ unset($fixcontexts);
+
+ // fix frontpage course sortorder
+ if ($frontcourse->sortorder != 1) {
+ $DB->set_field('course', 'sortorder', 1, array('id'=>$frontcourse->id));
}
- // actually sort only if there are courses,
- // and we meet one ofthe triggers:
- // - safe flag
- // - they are not in a continuos block
- // - they are too close to the 'bottom'
- if ($count && ( $safe || $hasgap || $mustshift ) ) {
- // special, optimized case where all we need is to shift
- if ( $mustshift && !$safe && !$hasgap) {
- $shift = $n + $catgap - $min;
- if ($shift < $count) {
- $shift = $count + $catgap;
- }
- // UPDATE course SET sortorder=sortorder+$shift
- $DB->execute("UPDATE {course}
- SET sortorder=sortorder+?
- WHERE category=?", array($shift, $categoryid));
- $n = $n + $catgap + $count;
-
- } else { // do it slowly
- $n = $n + $catgap;
- // if the new sequence overlaps the current sequence, lack of transactions
- // will stop us -- shift things aside for a moment...
- if ($safe || ($n >= $min && $n+$count+1 < $min && $CFG->dbfamily==='mysql')) {
- $shift = $max + $n + 1000;
- $DB->execute("UPDATE {course}
- SET sortorder=sortorder+?
- WHERE category=?", array($shift, $categoryid));
- }
+ // now fix the course counts in category records if needed
+ $sql = "SELECT cc.id, cc.coursecount, COUNT(c.id) AS newcount
+ FROM {course_categories} cc
+ LEFT JOIN {course} c ON c.category = cc.id
+ GROUP BY cc.id, cc.coursecount
+ HAVING cc.coursecount <> COUNT(c.id)";
- $courses = get_courses($categoryid, 'c.sortorder ASC', 'c.id,c.sortorder');
- $DB->begin_sql();
- $tx = true; // transaction sanity
- foreach ($courses as $course) {
- if ($tx && $course->sortorder != $n ) { // save db traffic
- $tx = $tx && $DB->set_field('course', 'sortorder', $n, array('id'=>$course->id));
- }
- $n++;
- }
- if ($tx) {
- $DB->commit_sql();
- } else {
- $DB->rollback_sql();
- if (!$safe) {
- // if we failed when called with !safe, try
- // to recover calling self with safe=true
- return fix_course_sortorder($categoryid, $n, true, $depth, $path);
- }
- }
- }
+ if ($updatecounts = $DB->get_records_sql($sql)) {
+ foreach ($updatecounts as $cat) {
+ $cat->coursecount = $cat->newcount;
+ unset($cat->newcount);
+ $DB->update_record_raw('course_categories', $cat, true);
+ }
}
- $DB->set_field('course_categories', 'coursecount', $count, array('id'=>$categoryid));
- // $n could need updating
- $max = $DB->get_field_sql("SELECT MAX(sortorder) FROM {course} WHERE category=?", array($categoryid));
- if ($max > $n) {
- $n = $max;
+ // now make sure that sortorders in course table are withing the category sortorder ranges
+ $sql = "SELECT cc.id, cc.sortorder
+ FROM {course_categories} cc
+ JOIN {course} c ON c.category = cc.id
+ WHERE c.sortorder < cc.sortorder OR c.sortorder > cc.sortorder + ".MAX_COURSES_IN_CATEGORY;
+
+ if ($fixcategories = $DB->get_records_sql($sql)) {
+ //fix the course sortorder ranges
+ foreach ($fixcategories as $cat) {
+ $sql = "UPDATE {course}
+ SET sortorder = (sortorder % ".MAX_COURSES_IN_CATEGORY.") + ?
+ WHERE category = ?";
+ $DB->execute($sql, array($cat->sortorder, $cat->id));
+ }
}
+ unset($fixcategories);
+
+ // categories having courses with sortorder duplicates or having gaps in sortorder
+ $sql = "SELECT DISTINCT c1.category AS id , cc.sortorder
+ FROM {course} c1
+ JOIN {course} c2 ON c1.sortorder = c2.sortorder
+ JOIN {course_categories} cc ON (c1.category = cc.id)
+ WHERE c1.id <> c2.id";
+ $fixcategories = $DB->get_records_sql($sql);
+
+ $sql = "SELECT cc.id, cc.sortorder, cc.coursecount, MAX(c.sortorder) AS maxsort, MIN(c.sortorder) AS minsort
+ FROM {course_categories} cc
+ JOIN {course} c ON c.category = cc.id
+ GROUP BY cc.id, cc.sortorder, cc.coursecount
+ HAVING (MAX(c.sortorder) <> cc.sortorder + cc.coursecount) OR (MIN(c.sortorder) <> cc.sortorder + 1)";
+ $gapcategories = $DB->get_records_sql($sql);
+
+ foreach ($gapcategories as $cat) {
+ if (isset($fixcategories[$cat->id])) {
+ // duplicates detected already
+
+ } else if ($cat->minsort == $cat->sortorder and $cat->maxsort == $cat->sortorder + $cat->coursecount - 1) {
+ // easy - new course inserted with sortorder 0, the rest is ok
+ $sql = "UPDATE {course}
+ SET sortorder = sortorder + 1
+ WHERE category = ?";
+ $DB->execute($sql, array($cat->id));
- if ($categories = get_categories($categoryid)) {
- foreach ($categories as $category) {
- $n = fix_course_sortorder($category->id, $n, $safe, $depth, $path);
+ } else {
+ // it needs full resorting
+ $fixcategories[$cat->id] = $cat;
}
}
+ unset($gapcategories);
- return $n+1;
+ // fix course sortorders in problematic categories only
+ foreach ($fixcategories as $cat) {
+ $i = 1;
+ $courses = $DB->get_records('course', array('category'=>$cat->id), 'sortorder ASC, id DESC', 'id, sortorder');
+ foreach ($courses as $course) {
+ if ($course->sortorder != $cat->sortorder + $i) {
+ $course->sortorder = $cat->sortorder + $i;
+ $DB->update_record_raw('course', $course, true);
+ }
+ $i++;
+ }
+ }
}
/**
- * Ensure all courses have a valid course category
- * useful if a category has been removed manually
- **/
-function fix_coursecategory_orphans() {
+ * Internal recursive category verification function, do not use directly!
+ */
+function _fix_course_cats($children, &$sortorder, $parent, $depth, $path, &$fixcontexts) {
global $DB;
- // Note: the handling of sortorder here is arguably
- // open to race conditions. Hard to fix here, unlikely
- // to hit anyone in production.
-
- $sql = "SELECT c.id, c.category, c.shortname
- FROM {course} c
- LEFT OUTER JOIN {course_categories} cc ON c.category=cc.id
- WHERE cc.id IS NULL AND c.id <> " . SITEID;
-
- if (!$rs = $DB->get_recordset_sql($sql)) {
- return;
- }
-
- if ($rs->valid()) { // we have some orphans
-
- // the "default" category is the lowest numbered...
- $default = $DB->get_field_sql("SELECT MIN(id)
- FROM {course_categories}");
- $sortorder = $DB->get_field_sql("SELECT MAX(sortorder)
- FROM {course}
- WHERE category=?", array($default));
+ $depth++;
+ foreach ($children as $cat) {
+ $sortorder = $sortorder + MAX_COURSES_IN_CATEGORY;
+ $update = false;
+ if ($parent != $cat->parent or $depth != $cat->depth or $path.'/'.$cat->id != $cat->path) {
+ $cat->parent = $parent;
+ $cat->depth = $depth;
+ $cat->path = $path.'/'.$cat->id;
+ $update = true;
- $DB->begin_sql();
- foreach ($rs as $course) {
- if (!$DB->set_field('course', 'category', $default, array('id'=>$course->id))
- or !$DB->set_field('course', 'sortorder', ++$sortorder, array('id'=>$course->id))) {
- $DB->rollback_sql();
- return;
- }
+ // make sure context caches are rebuild and dirty contexts marked
+ $context = get_context_instance(CONTEXT_COURSECAT, $cat->id);
+ $fixcontexts[$context->id] = $context;
+ }
+ if ($cat->sortorder != $sortorder) {
+ $cat->sortorder = $sortorder;
+ $update = true;
+ }
+ if ($update) {
+ $DB->update_record('course_categories', $cat, true);
+ }
+ if (isset($cat->children)) {
+ _fix_course_cats($cat->children, $sortorder, $cat->id, $cat->depth, $cat->path, $fixcontexts);
}
- $DB->commit_sql();
}
- $rs->close();
}
/**