--- enrol/database/enrol.php	2008-08-27 19:17:55.000000000 -0500
+++ enrol/database/enrol.php	2009-06-17 14:59:56.000000000 -0500
@@ -6,6 +6,29 @@
 
     var $log;
 
+    /**
+     * Courses with enrollments that
+     * have been processed during the
+     * sync_enrolments() method
+     *     * @var array
+     **/
+    var $processed = array();
+
+    /**
+     * Backup files created
+     *     * @var array
+     **/
+    var $backupfiles = array();
+
+/**
+ * Run any cleanup routines
+ *
+ * @return void
+ **/
+function __destruct() {
+    $this->delete_backup_files();
+}
+
 /*
  * For the given user, let's go out and look in an external database
  * for an authoritative list of enrolments, and then adjust the
@@ -30,8 +53,8 @@
         : array(null);
 
     //error_log('[ENROL_DB] found ' . count($roles) . ' roles:');
-
-    foreach($roles as $role) {
+	
+     foreach($roles as $role) {
 
         //error_log('[ENROL_DB] setting up enrolments for '.$role->shortname);
 
@@ -61,8 +84,10 @@
             error_log('[ENROL_DB] Using config default for roles: '.$role->shortname);
         }*/
 
-        if ($rs = $enroldb->Execute("SELECT {$CFG->enrol_remotecoursefield} as enrolremotecoursefield
-                                       FROM {$CFG->enrol_dbtable}
+        if ($rs = $enroldb->Execute("SELECT {$CFG->enrol_remotecoursefield} as enrolremotecoursefield".
+//                                            (!empty($CFG->enrol_db_remotetimestartfield) ? ", $CFG->enrol_db_remotetimestartfield as timestart" : '').
+//                                            (!empty($CFG->enrol_db_remotetimeendfield) ? ", $CFG->enrol_db_remotetimeendfield as timeend" : '').
+                                     " FROM {$CFG->enrol_dbtable}
                                       WHERE {$CFG->enrol_remoteuserfield} = " . $useridfield .
                                         (isset($remote_role_name, $remote_role_value) ? ' AND '.$remote_role_name.' = '.$remote_role_value : ''))) {
 
@@ -77,24 +102,25 @@
             if (!$existing) {
                 $existing = array();
             }
-
-
             if (!$rs->EOF) {   // We found some courses
-
-                //$count = 0;
-                $courselist = array();
+                $courselist = $enroltimes = array();
                 while ($fields_obj = rs_fetch_next_record($rs)) {         // Make a nice little array of courses to process
                     $fields_obj = (object)array_change_key_case((array)$fields_obj , CASE_LOWER);
                     $courselist[] = $fields_obj->enrolremotecoursefield;
-                    //$count++;
+                    $enroltimes[$fields_obj->enrolremotecoursefield]['timestart'] = 0;
+                    $enroltimes[$fields_obj->enrolremotecoursefield]['timeend'] = 0;
                 }
                 rs_close($rs);
-
                 //error_log('[ENROL_DB] Found '.count($existing).' existing roles and '.$count.' in external database');
-
                 foreach ($courselist as $coursefield) {   /// Check the list of courses against existing
                     $course = get_record('course', $CFG->enrol_localcoursefield, $coursefield);
                     if (!is_object($course)) {
+                        if (!empty($CFG->enrol_restore_coursetemplate)) {
+                            if (debugging('',DEBUG_ALL)) {
+                                error_log( "Course $coursefield does not exist and restore is enabled, but not allowed on login, skipping") ;
+                            }
+                            continue; // next foreach course
+                        }
                         if (empty($CFG->enrol_db_autocreate)) { // autocreation not allowed
                             if (debugging('',DEBUG_ALL)) {
                                 error_log( "Course $coursefield does not exist, skipping") ;
@@ -102,16 +128,16 @@
                             continue; // next foreach course
                         }
                         // ok, now then let's create it!
-                        // prepare any course properties we actually have
-                        $course = new StdClass;
-                        $course->{$CFG->enrol_localcoursefield} = $coursefield;
-                        $course->fullname  = $coursefield;
-                        $course->shortname = $coursefield;
-                        if (!($newcourseid = $this->create_course($course, true)
-                            and $course = get_record( 'course', 'id', $newcourseid))) {
-                            error_log( "Creating course $coursefield failed");
+                        error_log("Creating Course $coursefield...");
+                        if ($courseid = $this->create_course($enroldb, $coursefield, true)
+                            and $course = get_record( 'course', 'id', $courseid)) {
+                            error_log("created.");
+                        } else {
+                            error_log("failed.");
                             continue; // nothing left to do...
                         }
+                    } else if (!empty($CFG->enrol_db_autoupdate)) {
+                        $this->update_course($enroldb, $course, $coursefield);
                     }
 
                     // if the course is hidden and we don't want to enrol in hidden courses
@@ -132,6 +158,8 @@
                         continue;
                     }
 
+                    $times = $enroltimes[$coursefield];
+
                     // Search the role assignments to see if this user
                     // already has this role in this context.  If it is, we
                     // skip to the next course.
@@ -139,13 +167,26 @@
                         if ($role_assignment->roleid == $role->id
                             && $role_assignment->contextid == $context->id) {
                             unset($existing[$key]);
-                            //error_log('[ENROL_DB] User is already enroled in course '.$course->idnumber);
-                            continue 2;
+                            if (!$CFG->enrol_db_disableunenrol and $times['timeend'] != 0 and $times['timeend'] < time()) {
+                                // Timeend expired, unenrol
+                                role_unassign($role->id, $user->id, 0, $context->id);
+                                continue 2;
+                            }
+                            if (round($times['timestart'], -2) == $role_assignment->timestart and $times['timeend'] == $role_assignment->timeend) {
+                                // No updates needed, skip out of this one
+                                //error_log('[ENROL_DB] User is already enroled in course '.$course->idnumber);
+                                continue 2;
+                            } else {
+                                // Perform an update by performing the role_assign below
+                                break;
+                            }
                         }
                     }
 
                     //error_log('[ENROL_DB] Enrolling user in course '.$course->idnumber);
-                    role_assign($role->id, $user->id, 0, $context->id, 0, 0, 0, 'database');
+                    if ($times['timeend'] == 0 or $times['timeend'] > time()) {
+                        role_assign($role->id, $user->id, 0, $context->id, $times['timestart'], $times['timeend'], 0, 'database');
+                    }
                 }
             } // We've processed all external courses found
 
@@ -157,7 +198,7 @@
                     if ($role_assignment->enrol == 'database') {
                         //error_log('[ENROL_DB] Removing user from context '.$role_assignment->contextid);
                         role_unassign($role_assignment->roleid, $user->id, '', $role_assignment->contextid);
-                    } 
+                    }
                 }
             }
         } else {
@@ -221,18 +262,17 @@
     }
 
     begin_sql();
+    $i = 1;
     $extcourses = array();
     while ($extcourse_obj = rs_fetch_next_record($rs)) { // there are more course records
         $extcourse_obj = (object)array_change_key_case((array)$extcourse_obj , CASE_LOWER);
         $extcourse = $extcourse_obj->{strtolower($CFG->enrol_remotecoursefield)};
         array_push($extcourses, $extcourse);
-
         // does the course exist in moodle already?
         $course = false;
         $course = get_record( 'course',
                               $CFG->enrol_localcoursefield,
                               $extcourse );
-
         if (!is_object($course)) {
             if (empty($CFG->enrol_db_autocreate)) { // autocreation not allowed
                 if (debugging('', DEBUG_ALL)) {
@@ -240,18 +280,24 @@
                 }
                 continue; // next foreach course
             }
+            // Add to list - don't care if it fails
+            $this->processed[$extcourse] = $extcourse;
+
             // ok, now then let's create it!
-            // prepare any course properties we actually have
-            $course = new StdClass;
-            $course->{$CFG->enrol_localcoursefield} = $extcourse;
-            $course->fullname  = $extcourse;
-            $course->shortname = $extcourse;
-            if (!($newcourseid = $this->create_course($course, true)
-             and $course = get_record( 'course', 'id', $newcourseid))) {
-                error_log( "Creating course $extcourse failed");
+            error_log("Creating Course $extcourse...");
+            if ($courseid = $this->create_course($enroldb, $extcourse, true, true)
+                and $course = get_record( 'course', 'id', $courseid)) {
+                // we are skipping fix_course_sortorder()
+                print "created ". $i++." Course ". $extcourse." \n";
+            } else {
+                print "failed ". $i++." Course ". $extcourse." \n";
                 continue; // nothing left to do...
             }
+        } else if (!empty($CFG->enrol_db_autoupdate)) {
+            $this->update_course($enroldb, $course, $extcourse);
 
+            // Course updated, add to list
+            $this->processed[$extcourse] = $extcourse;
         }
 
         $context = get_context_instance(CONTEXT_COURSE, $course->id);
@@ -265,7 +311,7 @@
         // get a list of the student ids the are enrolled
         // in the external db -- hopefully it'll fit in memory...
         $extenrolments = array();
-        $sql = "SELECT {$CFG->enrol_remoteuserfield} " .
+        $sql = "SELECT {$CFG->enrol_remoteuserfield}" . 
             " FROM {$CFG->enrol_dbtable} " .
             " WHERE {$CFG->enrol_remotecoursefield} = " . $enroldb->quote($extcourse) .
                 ($have_role ? ' AND '.$remote_role_name.' = '.$remote_role_value : '');
@@ -279,10 +325,16 @@
             continue;
         }
 
-        // slurp results into an array
+        // slurp results into an arrays
+        $enroltimes = array();
         while ($crs_obj = rs_fetch_next_record($crs)) {
             $crs_obj = (object)array_change_key_case((array)$crs_obj , CASE_LOWER);
-            array_push($extenrolments, $crs_obj->{strtolower($CFG->enrol_remoteuserfield)});
+            $extenrolment = $crs_obj->{strtolower($CFG->enrol_remoteuserfield)};
+            array_push($extenrolments, $extenrolment);
+
+            $enroltimes[$extenrolment] = array();
+            $enroltimes[$extenrolment]['timestart'] = 0;
+            $enroltimes[$extenrolment]['timeend'] = 0;
         }
         rs_close($crs); // release the handle
 
@@ -327,7 +379,7 @@
         foreach ($extenrolments as $member) {
             // Get the user id and whether is enrolled in one fell swoop
             $sql = "
-                SELECT u.id AS userid, ra.id AS enrolmentid
+                SELECT u.id AS userid, ra.id AS enrolmentid, ra.timestart, ra.timeend, ra.contextid
                 FROM {$CFG->prefix}user u
                  LEFT OUTER JOIN {$CFG->prefix}role_assignments ra ON u.id = ra.userid
                   AND ra.roleid = {$role->id}
@@ -342,25 +394,42 @@
             }
             if ( $ers->EOF ) { // if this returns empty, it means we don't have the student record.
                                               // should not happen -- but skip it anyway
-                trigger_error('weird! no user record entry?');
+                trigger_error('weird! no user record entry?');		
                 continue;
             }
             $user_obj = rs_fetch_record($ers);
             $userid      = $user_obj->userid;
             $enrolmentid = $user_obj->enrolmentid;
+            $times       = $enroltimes[$member];
             rs_close($ers); // release the handle
 
-            if ($enrolmentid) { // already enrolled - skip
+            if ($times['timeend'] != 0 and $times['timeend'] < time()) {
+                // Timeend expired - don't assign a role
+                if (!$CFG->enrol_db_disableunenrol and $enrolmentid) {
+                    // Enrolled and timeend has expired - unenrol
+                    if (role_unassign($role->id, $user_obj->userid, 0, $user_obj->contextid)){
+                        error_log( "Unassigned {$role->shortname} assignment #$enrolmentid for course {$course->id} (" . format_string($course->shortname) . "); user {$user_obj->userid}");
+                    } else {
+                        error_log( "Failed to unassign {$role->shortname} assignment #$enrolmentid for course {$course->id} (" . format_string($course->shortname) . "); user {$user_obj->userid}");
+                    }
+                }
+                continue;
+            }
+            if ($enrolmentid and round($times['timestart'], -2) == $user_obj->timestart and $times['timeend'] == $user_obj->timeend) {
+                // already enrolled and enrollment dates have not changed - skip (we round timestart because role_assign does)
                 continue;
             }
 
-            if (role_assign($role->id, $userid, 0, $context->id, 0, 0, 0, 'database')){
+            if (role_assign($role->id, $userid, 0, $context->id, $times['timestart'], $times['timeend'], 0, 'database')){
                 error_log( "Assigned role {$role->shortname} to user {$userid} in course {$course->id} (" . format_string($course->shortname) . ")");
             } else {
                 error_log( "Failed to assign role {$role->shortname} to user {$userid} in course {$course->id} (" . format_string($course->shortname) . ")");
             }
 
         } // end foreach member
+
+        // Update cron time
+        $this->cron_set_start(false);
     } // end while course records
     rs_close($rs); //Close the main course recordset
 
@@ -404,6 +473,9 @@
                 } else {
                     error_log( "Failed unassign role {$roleid} from user $user in context $contextid");
                 }
+
+                // Update cron time
+                $this->cron_set_start(false);
             }
             rs_close($ers); // release the handle
         }
@@ -415,6 +487,7 @@
     fix_course_sortorder();
 
     $this->enrol_disconnect($enroldb);
+    $this->delete_backup_files();
     return true;
 }
 
@@ -431,15 +504,24 @@
                   'enrol_dbname', 'enrol_dbtable',
                   'enrol_localcoursefield', 'enrol_localuserfield',
                   'enrol_remotecoursefield', 'enrol_remoteuserfield',
-                  'enrol_db_autocreate', 'enrol_db_category', 'enrol_db_template',
+                  'enrol_db_autocreate', 'enrol_db_category',
+                  'enrol_autocreate_category', 'enrol_category_separator',
                   'enrol_db_localrolefield', 'enrol_db_remoterolefield',
                   'enrol_remotecoursefield', 'enrol_remoteuserfield',
+                  'enrol_coursetable', 'enrol_coursefullname',
+                  'enrol_courseshortname', 'enrol_courseid',
+		  'enrol_coursecategory',
                   'enrol_db_ignorehiddencourse', 'enrol_db_defaultcourseroleid',
-                  'enrol_db_disableunenrol');
+                  'enrol_db_disableunenrol', 
+                  'enrol_db_autoupdate', 'enrol_restore_coursetemplate',
+                  'enrol_restore_usetemplate');
 
     foreach ($vars as $var) {
         if (!isset($frm->$var)) {
             $frm->$var = '';
+//	    print_r($frm);
+//	    print_r($var);
+	    
         }
     }
     include("$CFG->dirroot/enrol/database/config.html");
@@ -503,15 +585,63 @@
     }
     set_config('enrol_db_autocreate', $config->enrol_db_autocreate);
 
+    if (!isset($config->enrol_db_autoupdate)) {
+        $config->enrol_db_autoupdate = 0;
+    }
+    set_config('enrol_db_autoupdate', $config->enrol_db_autoupdate);
+
     if (!isset($config->enrol_db_category)) {
         $config->enrol_db_category = '';
     }
     set_config('enrol_db_category', $config->enrol_db_category);
 
-    if (!isset($config->enrol_db_template)) {
-        $config->enrol_db_template = '';
+    if (!isset($config->enrol_autocreate_category)) {
+        $config->enrol_autocreate_category = '';
+    }
+    set_config('enrol_autocreate_category', $config->enrol_autocreate_category);
+
+    if (!isset($config->enrol_category_separator)) {
+        $config->enrol_category_separator = '';
     }
-    set_config('enrol_db_template', $config->enrol_db_template);
+    set_config('enrol_category_separator', $config->enrol_category_separator);
+
+    if (!isset($config->enrol_coursetable)) {
+        $config->enrol_coursetable = '';
+    }
+    set_config('enrol_coursetable', $config->enrol_coursetable);
+
+    if (!isset($config->enrol_coursefullname)) {
+        $config->enrol_coursefullname = '';
+    }
+    set_config('enrol_coursefullname', $config->enrol_coursefullname);
+
+    if (!isset($config->enrol_courseshortname)) {
+        $config->enrol_courseshortname = '';
+    }
+    set_config('enrol_courseshortname',
+               $config->enrol_courseshortname);
+
+    if (!isset($config->enrol_courseid)) {
+        $config->enrol_courseid = '';
+    }
+    set_config('enrol_courseid',
+               $config->enrol_courseid);
+
+    if (!isset($config->enrol_coursecategory)) {
+        $config->enrol_coursecategory = '';
+    }
+    set_config('enrol_coursecategory',
+               $config->enrol_coursecategory);
+
+    if (!isset($config->enrol_restore_coursetemplate)) {
+        $config->enrol_restore_coursetemplate = '';
+    }
+    set_config('enrol_restore_coursetemplate', $config->enrol_restore_coursetemplate);
+
+    if (!isset($config->enrol_restore_usetemplate)) {
+        $config->enrol_restore_usetemplate = '';
+    }
+    set_config('enrol_restore_usetemplate', $config->enrol_restore_usetemplate);
 
     if (!isset($config->enrol_db_defaultcourseroleid)) {
         $config->enrol_db_defaultcourseroleid = '';
@@ -546,14 +676,33 @@
 // NOTE: if you pass true for $skip_fix_course_sortorder
 // you will want to call fix_course_sortorder() after your are done
 // with course creation
-function create_course ($course,$skip_fix_course_sortorder=0){
-    global $CFG;
-
-    // define a template
-    if(!empty($CFG->enrol_db_template)){
-        $template = get_record("course", 'shortname', $CFG->enrol_db_template);
-        $template = (array)$template;
+function create_course($enroldb, $coursefield, $skip_fix_course_sortorder = 0, $enablerestore = false) {
+    global $CFG;    
+    if (!empty($CFG->enrol_coursetable)) {
+        // Using external course information table
+        if (!$course = $this->get_course_details($enroldb, $coursefield)) {
+            return false;
+        }
     } else {
+        // Using most basic data
+        $course = new StdClass;
+        $course->{$CFG->enrol_localcoursefield} = $coursefield;
+        $course->fullname  = $coursefield;
+        $course->shortname = $coursefield;
+    }
+    //print_r($course);
+    // define a template
+//    if (!empty($CFG->enrol_coursetemplate) and !empty($course->template)) {
+    if (!empty($course->template)) {
+        // We stored the course template in $course->template in get_course_details()
+        // so use it and unset() it once we are done with it.
+        if ($template = get_record("course", 'shortname', addslashes($course->template))) {
+            $templatecourse = clone($template);
+            $template = (array)$template;
+            unset ($course->template);
+        }
+    }
+    if (empty($template)) {
         $site = get_site();
         $template = array(
                           'startdate'      => time() + 3600 * 24,
@@ -574,28 +723,38 @@
                           'teachers' => $site->teachers,
                           );
     }
+
     // overlay template
     foreach (array_keys($template) AS $key) {
-        if (empty($course->$key)) {
+        if (!isset($course->$key)) {
             $course->$key = $template[$key];
         }
     }
 
-    $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;
+    // Only use default categories if the course doesn't already have one.
+    if (empty($course->category)) {
+        // the misc 'catch-all' category
+        $course->category = 1;
+
+        if (!empty($CFG->enrol_db_category)) {
+            //category = 0 or undef will break moodle
+            $course->category = $CFG->enrol_db_category;
+        }
     }
 
     // define the sortorder
     $sort = get_field_sql('SELECT COALESCE(MAX(sortorder)+1, 100) AS max ' .
                           ' FROM ' . $CFG->prefix . 'course ' .
                           ' WHERE category=' . $course->category);
-    $course->sortorder = $sort;
-
-    // override with local data
-    $course->startdate   = time() + 3600 * 24;
+    $course->sortorder   = $sort;
     $course->timecreated = time();
-    $course->visible     = 1;
+
+    if (!isset($course->startdate)) {
+        $course->startdate   = time() + 3600 * 24;
+    }
+    if (!isset($course->visible)) {
+        $course->visible     = 1;
+    }
 
     // clear out id just in case
     unset($course->id);
@@ -603,23 +762,26 @@
     // truncate a few key fields
     $course->idnumber  = substr($course->idnumber, 0, 100);
     $course->shortname = substr($course->shortname, 0, 100);
-
+    //print_r($course);
     // store it and log
     if ($newcourseid = insert_record("course", addslashes_object($course))) {  // Set up new course
-        $section = NULL;
-        $section->course = $newcourseid;   // Create a default section.
-        $section->section = 0;
-        $section->id = insert_record("course_sections", $section);
-        $page = page_create_object(PAGE_COURSE_VIEW, $newcourseid);
-        blocks_repopulate_page($page); // Return value no
-
+        if ($enablerestore and !empty($CFG->enrol_restore_coursetemplate) and isset($templatecourse)) {
+            $this->backup_and_restore_into_course($templatecourse, $newcourseid);
+        } else {
+            $section = NULL;
+            $section->course = $newcourseid;   // Create a default section.
+            $section->section = 0;
+            $section->id = 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){
             fix_course_sortorder();
         }
         add_to_log($newcourseid, "course", "new", "view.php?id=$newcourseid", "enrol/database auto-creation");
     } else {
-        trigger_error("Could not create new course $extcourse from  from database");
+        trigger_error("Could not create new course $extcourse from database");
         notify("Serious Error! Could not create the new course!");
         return false;
     }
@@ -627,6 +789,115 @@
     return $newcourseid;
 }
 
+/**
+ * Update a Moodle course settings based
+ * on the external course settings.
+ *
+ * @param object $enroldb External database connection
+ * @param object $course Moodle course to update
+ * @param string $extcourseid External course id to sync the Moodle course to
+ * @return void
+ **/
+function update_course($enroldb, $course, $extcourseid) {
+    if ($extcourse = $this->get_course_details($enroldb, $extcourseid)) {
+        $update = false;
+        $record = new stdClass;
+        foreach ($extcourse as $key => $value) {
+            if ($key != 'id' and isset($course->$key) and $course->$key != $value) {
+                switch ($key) {
+                    case 'idnumber':
+                    case 'shortname':
+                        $record->$key = substr($value, 0, 100);
+                        break;
+                    default:
+                        $record->$key = $value;
+                        break;		
+                }
+                $update = true;
+		//print_r($record);
+		//print_r($course);
+            }
+        }
+
+        // Explicitly set
+        $record->id = $course->id;
+	//print_r($record);
+	//print_r($course);
+        if ($update) {
+            if (!update_record('course', addslashes_object($record))) {
+                error_log("Failed to update course with id = $course->id");
+            }
+        }
+    }
+}
+
+/**
+ * Processes courses that have no enrolments.
+ *
+ * Best to be called after {@link sync_enrolments()}
+ * as $this->processed is built so we can skip courses
+ * already processed via enrolments.
+ *
+ * @return boolean
+ **/
+function process_courses_without_enrolments() {
+    global $CFG;
+
+    // Check required config
+    if (!empty($CFG->enrol_courseid) and
+        !empty($CFG->enrol_coursetable) and
+        !empty($CFG->enrol_localcoursefield)) {
+
+        $enroldb = $this->enrol_connect();
+        if (!$enroldb) {
+            error_log('[ENROL_DB] Could not make a connection');
+            return;
+        }
+
+        begin_sql();
+
+        if ($rs = $enroldb->Execute("SELECT {$CFG->enrol_courseid} as enrolremotecoursefield
+                                       FROM {$CFG->enrol_coursetable}")) {
+
+            while ($fields_obj = rs_fetch_next_record($rs)) {
+                $fields_obj  = (object)array_change_key_case((array)$fields_obj , CASE_LOWER);
+                $coursefield = $fields_obj->enrolremotecoursefield;
+
+                if (!in_array($coursefield, $this->processed)) {
+                    $course = get_record('course', $CFG->enrol_localcoursefield, $coursefield);
+
+                    if (!is_object($course)) {
+                        if (empty($CFG->enrol_db_autocreate)) { // autocreation not allowed
+                            if (debugging('', DEBUG_ALL)) {
+                                error_log( "Course $extcourse does not exist, skipping");
+                            }
+                            continue; // next foreach course
+                        }
+                        // ok, now then let's create it!
+                        error_log("Creating Course $coursefield...");
+                        if ($this->create_course($enroldb, $coursefield, true, true)) {
+                            // we are skipping fix_course_sortorder()
+                            error_log("created.");
+                        } else {
+                            error_log("failed.");
+                            continue; // nothing left to do...
+                        }
+                    } else if (!empty($CFG->enrol_db_autoupdate)) {
+                        $this->update_course($enroldb, $course, $coursefield);
+                    }
+                }
+            }
+            rs_close($rs);
+        }
+        commit_sql();
+        fix_course_sortorder();
+        $this->enrol_disconnect($enroldb);
+        $this->delete_backup_files();
+    }
+
+    return true;
+}
+
 /// DB Connect
 /// NOTE: You MUST remember to disconnect
 /// when you stop using it -- as this call will
@@ -677,6 +948,374 @@
     return array($have_role, $remote_role_name, $remote_role_value);
 }
 
+// get_course_details
+//
+// get_course_details Returns false if the course doesn't exist
+// or if there is more than one course with this courseid in the
+// enrolment database
+//
+// @param $enroldb
+// @param $courseid
+// @param $log_errors boolean (default 'true')
+function get_course_details($enroldb, $courseid, $log_errors = true){
+    global $CFG;
+
+    $columns = $CFG->enrol_courseshortname;
+    if (!empty($CFG->enrol_coursefullname)) {
+        $columns = $columns . "," . $CFG->enrol_coursefullname;
+    }
+
+    if (!empty($CFG->enrol_coursecategory)) {
+        $columns = $columns . "," . $CFG->enrol_coursecategory;
+    }
+    $courseid = addslashes($courseid);
+    $query = "SELECT $columns FROM {$CFG->enrol_coursetable} " .
+             "WHERE {$CFG->enrol_courseid} = '$courseid' LIMIT 1";
+    //print_r($query);
+    if ($rs = $enroldb->Execute($query)) {
+        //print_r($rs);
+	/*print $query ."\n";
+	print $rs->RecordCount()."\n";
+	print_r($rs->fields[$CFG->enrol_courseshortname]);
+	print "\n";
+	print $rs->fields[$CFG->enrol_courseshortname]."-".$rs->fields[$CFG->enrol_coursecategory]."\n";*/
+        if ($rs->RecordCount() == 1) {
+            $course = new StdClass;
+            $course->shortname = $rs->fields[$CFG->enrol_courseshortname];
+	    //print "<b>". $course."</b>\n";
+	    //print $course->shortname."</b>\n";
+	    //print_r($course);
+            if (!empty($CFG->enrol_coursefullname)) {
+                $course->fullname = $rs->fields[$CFG->enrol_coursefullname];
+            }
+
+            if (!empty($CFG->enrol_coursecategory)) {
+                $category = $rs->fields[$CFG->enrol_coursecategory];
+		//print "<b>".$category."</b>\n";
+                if(!empty($category) and $categoryid = $this->get_category($category)) {
+                    $course->category = $categoryid;
+                }
+                // If we don't find a categoryid, don't set the course
+                // category so it gets the default one.
+            }
+            // Make this assignment last, in case the user chooses
+            // one of the above fields as $CFG->enrol_localcoursefield
+            $course->{$CFG->enrol_localcoursefield} = $courseid;
+            return $course;
+
+        } else if ($rs->RecordCount() > 1) {
+            // Woops! There is more than one course with this ID.
+            // We cannot tell which is the right one! Log this
+            // and return with error.
+	    
+            if ($log_errors) {
+                error_log("User enrolled to course $courseid, but there "
+                          . "is more than one course with that value in "
+                          . "the enrolment database (in field "
+                          . "{$CFG->enrol_courseid}) \n");
+            }
+
+        } else {
+            // We didn't find the course in the external enrolment
+            // database. Log it and return with error.
+            if ($log_errors) {
+                error_log("User enrolled to a nonexistant course $courseid "
+                          . "(course not found in enrolment database) ");
+            }
+        }
+    }
+    return false;
+}
+
+// get_category
+//
+// get_category returns the id of a given course category. If
+// $CFG->enrol_db_autocreate_category is set and the category doesn't
+// exist, it creates it and returns the new id. Otherwise it returns
+// false.
+//
+// if $CFG->enrol_category_separator is set, it can handle
+// subcategories of any depth. You just need to specify the 'path' of
+// the subcategory as the names of the categories separated by the
+// value of the separator. For example, if we use '/' as the
+// separator, we can specify 'category1/category2/category3' if we are
+// interested in a category called 'category3' that is inside a
+// category called 'category2' that is inside a category called
+// 'category1' that is a top level category.
+//
+// @param $category the name of the category
+// @return category id (int) or false.
+// @uses $CFG
+function get_category($category) {
+    global $CFG;
+
+    if(empty($CFG->enrol_category_separator)) {
+        if (empty($CFG->enrol_autocreate_category)) {
+            if ($id = get_field('course_categories', 'id', 'name', addslashes($category))) {
+                return $id;
+            }
+            error_log("Category '$category' not found (and not autocreating categories). " .
+                      "Using default category");
+            return false;
+        } else {
+            $categories = array($category);
+        }
+    } else {
+        if ((strpos($category, $CFG->enrol_category_separator) === 0)
+            || (strrpos($category, $CFG->enrol_category_separator) === (strlen($category) - 1))) {
+            error_log('Category name syntax invalid (cannot start or end with category ' .
+                      'separator): '.$category);
+            return false;
+        }
+        $categories = explode($CFG->enrol_category_separator, $category);
+    }
+
+    // Start checking/creating categories at the top level.
+    $parentid = 0;
+    $parentpath = '';
+    foreach ($categories as $depth => $categoryname) {
+        if ($category = get_record('course_categories', 'name', addslashes($categoryname),
+                                   'parent', $parentid)) {
+            $categoryid = $category->id;
+            $parentid = $category->id;
+            $parentpath = $category->path;
+            continue;
+        }
+        if (empty($CFG->enrol_autocreate_category)) {
+            error_log("Category '$categoryname' not found (and not autocreating categories). " .
+                      "Using default category");
+            return false;
+        }
+        $newcategory = new stdClass();
+        $newcategory->name = addslashes($categoryname);
+        $newcategory->description = addslashes($categoryname);
+        $newcategory->sortorder = 999;
+        $newcategory->parent = $parentid;
+        $newcategory->depth = $depth + 1;
+        if (!$newcategory->id = insert_record('course_categories', $newcategory)) {
+            error_log("Could not create the new category: '$newcategory->name'");
+            return false;
+        } else {
+            $newcategory->path = $parentpath . '/' . $newcategory->id;
+            update_record ('course_categories', $newcategory);
+            $newcategory->context = get_context_instance(CONTEXT_COURSECAT, $newcategory->id);
+            mark_context_dirty($newcategory->context->path);
+            $parentid = $newcategory->id;
+            $parentpath = $newcategory->path;
+            $categoryid = $newcategory->id;
+        }
+    }
+    return $categoryid;
+}
+
+/**
+ * Backups a course and then restores
+ * that backup into a destination course.
+ *
+ * @param object $fromcourse The course to backup and restore
+ * @param int $destcourseid The destination for the restore
+ * @return boolean
+ **/
+function backup_and_restore_into_course($fromcourse, $destcourseid) {
+    global $CFG, $USER;
+
+    require_once($CFG->libdir.'/adminlib.php');
+    require_once($CFG->libdir.'/blocklib.php');
+    require_once($CFG->libdir.'/wiki_to_markdown.php');
+    require_once($CFG->libdir.'/xmlize.php');
+    require_once($CFG->dirroot.'/course/lib.php');
+    require_once($CFG->dirroot.'/backup/lib.php');
+    require_once($CFG->dirroot.'/backup/backuplib.php');
+    require_once($CFG->dirroot.'/backup/restorelib.php');
+    require_once($CFG->dirroot.'/backup/bb/restore_bb.php');
+
+    // Same setup as in admin/cron.php
+    $USER = get_admin();
+    $USER->timezone = $CFG->timezone;
+    $USER->lang = '';
+    $USER->theme = '';
+
+    // Look in backupfiles for the backup file or generate a new one
+    if (array_key_exists($fromcourse->id, $this->backupfiles) and $this->backupfiles[$fromcourse->id] === false) {
+        error_log("Failed to restore course with id = $fromcourse->id into course with id = $destcourseid because backup has failed once already");
+        return false;
+
+    } else if (!array_key_exists($fromcourse->id, $this->backupfiles)) {
+        // Grab backup file (may actually trigger a backup)
+        if (!$this->backupfiles[$fromcourse->id] = $this->get_backup_file($fromcourse)) {
+            return false;
+        }
+    }
+
+    // Should have backup file now - import it into destination course
+    if (!@import_backup_file_silently($this->backupfiles[$fromcourse->id], $destcourseid, true, false)) {
+        error_log("Failed to restore course with id = $fromcourse->id into course with id = $destcourseid because import failed");
+        return false;
+    }
+
+    // Update the time since backup/restores can take a long time
+    $this->cron_set_start();
+
+    return true;
+}
+
+/**
+ * Look for a backup file in
+ * backupdata/template or create
+ * a new backup
+ *
+ * @param object $course Find a backup file for this course
+ * @return mixed
+ **/
+function get_backup_file($course) {
+    global $CFG;
+
+    $templatedir = "$CFG->dataroot/$course->id/backupdata/template";
+
+    // If we are using template dir, check there for a backup file first
+    if (!empty($CFG->enrol_restore_usetemplate) and is_dir($templatedir)) {
+        $files = get_directory_list($templatedir, '', false);
+        foreach ($files as $file) {
+            if (pathinfo($file, PATHINFO_EXTENSION) != 'zip') {
+                continue;
+            }
+            if (strpos(strtolower($file), strtolower($course->shortname)) !== false) {
+                // Found what we hope is a backup file
+                return "$templatedir/$file";
+            }
+        }
+    }
+
+    $errorstring = '';
+    $backupprefs = array();
+    if ($backupfile = @backup_course_silently($course->id, $backupprefs, $errorstring)) {
+        // If using templates dir, then save backup file to it
+        if (!empty($CFG->enrol_restore_usetemplate) and make_upload_directory("$course->id/backupdata/template", false)) {
+            $name = pathinfo($backupfile, PATHINFO_BASENAME);
+
+            if (rename($backupfile, "$templatedir/$name")) {
+                return "$templatedir/$name";
+            }
+        }
+        return $backupfile;
+    } else {
+        error_log("Failed to backup course with id = $course->id.  Error string returned from backup: $errorstring");
+        return false;
+    }
+}
+
+/**
+ * Remove all backup files generated
+ * during execution except the ones
+ * stored in backupdata/template.
+ *
+ * @return boolean
+ **/
+function delete_backup_files() {
+    global $CFG;
+
+    require_once($CFG->libdir.'/filelib.php');
+
+    foreach ($this->backupfiles as $backupfile) {
+        if (strpos($backupfile, 'backupdata/template') === false) {
+            fulldelete($backupfile);
+        }
+    }
+    $this->backupfiles = array();
+
+    return true;
+}
+
+/**
+ * Cron hook
+ *
+ * @return void
+ **/
+function cron() {
+    global $CFG;
+
+    require_once($CFG->dirroot.'/course/lib.php');
+    require_once($CFG->dirroot.'/lib/blocklib.php');
+
+    if ($this->cron_running()) {
+        error_log("Cron is still running or has not expired yet.  Will try again next cron.");
+        return;
+    }
+    $this->cron_set_start();
+
+    // If we have settings to handle roles individually, through each type of
+    // role and update it.  Otherwise, just got through once (with no role
+    // specified).
+    $roles = !empty($CFG->enrol_db_remoterolefield) && !empty($CFG->enrol_db_localrolefield)
+        ? get_records('role')
+        : array(null);
+
+    foreach ($roles as $role) {
+        $this->sync_enrolments($role);
+    }
+
+    $this->process_courses_without_enrolments();
+
+    // sync metacourses
+    if (function_exists('sync_metacourses')) {
+        sync_metacourses();
+    }
+
+    $info = get_performance_info();
+    error_log('Performance info from enrol/database cron: '.$info['txt']);
+
+    $this->cron_set_end();
+}
+
+/**
+ * Determine if the cron is still running
+ *
+ * @return boolean
+ **/
+function cron_running() {
+    if ($started = get_config(NULL, 'enrol_db_cronstarted')) {
+        $timetocheck = time() - HOURSECS;
+
+        if ($started != 0 and $started > $timetocheck) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * Set the time for the cronstarted
+ *
+ * If not forced, then will only actually
+ * set the config every 100 calls - this is
+ * nice for calling within loops.
+ *
+ * @return boolean
+ **/
+function cron_set_start($force = true) {
+    static $count = 1;
+
+    $return = true;
+
+    if (!$force) {
+        $count++;
+    }
+    if ($force or $count % 100 == 0) {
+        $return = set_config('enrol_db_cronstarted', time());
+    }
+    return $return;
+}
+
+/**
+ * End the cron time
+ *
+ * @return boolean
+ **/
+function cron_set_end() {
+    return unset_config('enrol_db_cronstarted');
+}
+
 } // end of class
 
 ?>

--- enrol/database/config.html	2008-05-02 19:16:23.000000000 -0500
+++ enrol/database/config.html	2009-06-17 15:00:02.000000000 -0500
@@ -188,6 +188,16 @@
 </tr>
 
 <tr>
+    <td align="right">enrol_db_autoupdate:</td>
+    <td>
+    <?php choose_from_menu($yesno, "enrol_db_autoupdate", $frm->enrol_db_autoupdate, ""); ?>
+    </td>
+    <td>
+    <?php print_string('autoupdate', 'enrol_database') ?>
+    </td>
+</tr>
+
+<tr>
         <td align="right">enrol_db_category:</td>
         <td>
     <?php
@@ -204,19 +214,96 @@
 </tr>
 
 <tr>
-    <td align="right">enrol_db_template:</td>
+    <td align="right">enrol_autocreate_category:</td>
+    <td>
+    <?php
+       choose_from_menu($yesno, "enrol_autocreate_category", $frm->enrol_autocreate_category, "");
+           if (isset($err["enrol_autocreate_category"])) formerr($err["enrol_autocreate_category"]); 
+    ?>
+    </td>
+    <td>
+    <?php  print_string("autocreatecategory","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_coursetable:</td>
+    <td>
+    <input size="15" type="text" name="enrol_coursetable" value="<?php echo $frm->enrol_coursetable ?>" />
+    </td>
+    <td>
+    <?php print_string("coursetable","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_coursefullname:</td>
+    <td>
+    <input size="15" type="text" name="enrol_coursefullname" value="<?php echo $frm->enrol_coursefullname ?>" />
+    </td>
+    <td>
+    <?php  print_string("coursefullname","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_courseshortname:</td>
+    <td>
+    <input size="15" type="text" name="enrol_courseshortname" value="<?php echo $frm->enrol_courseshortname ?>" />
+    </td>
+    <td>
+    <?php  print_string("courseshortname","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_courseid:</td>
+    <td>
+    <input size="15" type="text" name="enrol_courseid" value="<?php echo $frm->enrol_courseid ?>" />
+    </td>
+    <td>
+    <?php  print_string("courseid","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_coursecategory:</td>
+    <td>
+    <input size="15" type="text" name="enrol_coursecategory" value="<?php echo $frm->enrol_coursecategory ?>" />
+    </td>
+    <td>
+    <?php  print_string("coursecategory","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_category_separator:</td>
         <td>
-    <input name="enrol_db_template" type="text" size="30" value="<?php echo $frm->enrol_db_template?>" />
-    <?php  if (isset($err["enrol_db_template"])) formerr($err["enrol_db_template"]); ?>
+    <input name="enrol_category_separator" type="text" size="15" value="<?php echo $frm->enrol_category_separator?>" />
+    <?php  if (isset($err["enrol_category_separator"])) formerr($err["enrol_category_separator"]); ?>
     </td><td>
-    <?php  print_string("template","enrol_database") ?>
+    <?php  print_string("categoryseparator","enrol_database") ?>
     </td>
 </tr>
 
 <tr>
-   <th colspan="2" scope="col">
-        <?php print_string("general_options", "enrol_database") ?>
-   </th>
+    <td align="right">enrol_restore_coursetemplate:</td>
+    <td>
+        <?php choose_from_menu($yesno, "enrol_restore_coursetemplate", $frm->enrol_restore_coursetemplate, ""); ?>
+    </td>
+    <td>
+    <?php  print_string("restore_coursetemplate","enrol_database") ?>
+    </td>
+</tr>
+
+<tr>
+    <td align="right">enrol_restore_usetemplate:</td>
+    <td>
+        <?php choose_from_menu($yesno, "enrol_restore_usetemplate", $frm->enrol_restore_usetemplate, ""); ?>
+    </td>
+    <td>
+    <?php  print_string("restore_usetemplate","enrol_database") ?>
+    </td>
 </tr>
 
 <tr>
