MDL-29020 add CLI verbose mode and stop requiring course idnumber when external colum...
authorPetr Skoda <commits@skodak.org>
Sat, 20 Aug 2011 09:22:32 +0000 (11:22 +0200)
committerPetr Skoda <commits@skodak.org>
Sat, 20 Aug 2011 10:59:23 +0000 (12:59 +0200)
enrol/database/cli/sync.php
enrol/database/lib.php

index 79af47d..0054c0a 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
 define('CLI_SCRIPT', true);
 
 require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
+require_once($CFG->libdir.'/clilib.php');
 
-if (!enrol_is_enabled('database')) {
-     die('enrol_database plugin is disabled, sync is disabled');
+// now get cli options
+list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
+
+if ($unrecognized) {
+    $unrecognized = implode("\n  ", $unrecognized);
+    cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
 }
 
+if ($options['help']) {
+    $help =
+"Execute enrol sync with external database.
+The enrol_database plugin must be enabled and properly configured.
+
+Options:
+-v, --verbose         Print verbose progess information
+-h, --help            Print out this help
 
+Example:
+\$sudo -u www-data /usr/bin/php enrol/database/cli/sync.php
+
+Sample cron entry:
+# 5 minutes past 4am
+5 4 * * * \$sudo -u www-data /usr/bin/php /var/www/moodle/enrol/database/cli/sync.php
+";
+
+    echo $help;
+    die;
+}
+
+if (!enrol_is_enabled('database')) {
+    echo('enrol_database plugin is disabled, sync is disabled'."\n");
+    exit(1);
+}
+
+$verbose = !empty($options['verbose']);
 $enrol = enrol_get_plugin('database');
-$enrol->sync_courses();
-$enrol->sync_enrolments();
+$result = 0;
+
+$result = $result | $enrol->sync_courses($verbose);
+$result = $result | $enrol->sync_enrolments($verbose);
+
+exit($result);
\ No newline at end of file
index 987e0e7..814e723 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -103,7 +102,10 @@ class enrol_database_plugin extends enrol_plugin {
         $enrols = array();
         $instances = array();
 
-        $extdb = $this->db_init();
+        if (!$extdb = $this->db_init()) {
+            // can not connect to database, sorry
+            return;
+        }
 
         // read remote enrols and create instances
         $sql = $this->db_get_sql($table, array($userfield=>$user->$localuserfield), array(), false);
@@ -242,22 +244,33 @@ class enrol_database_plugin extends enrol_plugin {
     /**
      * Forces synchronisation of all enrolments with external database.
      *
-     * @return void
+     * @param bool $verbose
+     * @return int 0 means success, 1 db connect failure, 2 db read failure
      */
-    public function sync_enrolments() {
+    public function sync_enrolments($verbose = false) {
         global $CFG, $DB;
 
         // we do not create courses here intentionally because it requires full sync and is slow
         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
-            return;
+            if ($verbose) {
+                mtrace('User enrolment synchronisation skipped.');
+            }
+            return 0;
+        }
+
+        if ($verbose) {
+            mtrace('Starting user enrolment synchronisation...');
+        }
+
+        if (!$extdb = $this->db_init()) {
+            mtrace('Error while communicating with external enrolment database');
+            return 1;
         }
 
         // we may need a lot of memory here
         @set_time_limit(0);
         raise_memory_limit(MEMORY_HUGE);
 
-        $extdb = $this->db_init();
-
         // second step is to sync instances and users
         $table            = $this->get_config('remoteenroltable');
         $coursefield      = strtolower($this->get_config('remotecoursefield'));
@@ -298,18 +311,20 @@ class enrol_database_plugin extends enrol_plugin {
             }
             $rs->Close();
         } else {
-            debugging('Error while communicating with external enrolment database');
+            mtrace('Error reading data from the external enrolment table');
             $extdb->Close();
-            return;
+            return 2;
         }
         $preventfullunenrol = empty($externalcourses);
         if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
-            debugging('Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.');
+            if ($verbose) {
+                mtrace('  Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.');
+            }
         }
 
         // first find all existing courses with enrol instance
         $existing = array();
-        $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, e.id AS enrolid
+        $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, e.id AS enrolid, c.shortname
                   FROM {course} c
                   JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')";
         $rs = $DB->get_recordset_sql($sql); // watch out for idnumber duplicates
@@ -328,7 +343,7 @@ class enrol_database_plugin extends enrol_plugin {
             $localnotempty =  "AND c.$localcoursefield <> :lcfe";
             $params['lcfe'] = $DB->sql_empty();
         }
-        $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping
+        $sql = "SELECT c.id, c.visible, c.$localcoursefield AS mapping, c.shortname
                   FROM {course} c
              LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')
                  WHERE e.id IS NULL $localnotempty";
@@ -430,7 +445,7 @@ class enrol_database_plugin extends enrol_plugin {
                 }
                 $rs->Close();
             } else {
-                debugging('Error while communicating with external enrolment database');
+                mtrace('Error while communicating with external enrolment database');
                 $extdb->Close();
                 return;
             }
@@ -443,6 +458,9 @@ class enrol_database_plugin extends enrol_plugin {
                         $this->enrol_user($instance, $userid, $roleid);
                         $current_roles[$userid][$roleid] = $roleid;
                         $current_status[$userid] = ENROL_USER_ACTIVE;
+                        if ($verbose) {
+                            mtrace("  enrolling: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname);
+                        }
                     }
                 }
 
@@ -451,12 +469,18 @@ class enrol_database_plugin extends enrol_plugin {
                     if (empty($userroles[$cr])) {
                         role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
                         unset($current_roles[$userid][$cr]);
+                        if ($verbose) {
+                            mtrace("  unsassigning roles: $userid ==> $course->shortname");
+                        }
                     }
                 }
 
                 // reenable enrolment when previously disable enrolment refreshed
                 if ($current_status[$userid] == ENROL_USER_SUSPENDED) {
                     $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid'=>$instance->id, 'userid'=>$userid));
+                    if ($verbose) {
+                        mtrace("  unsuspending: $userid ==> $course->shortname");
+                    }
                 }
             }
 
@@ -469,6 +493,9 @@ class enrol_database_plugin extends enrol_plugin {
                             continue;
                         }
                         $this->unenrol_user($instance, $userid);
+                        if ($verbose) {
+                            mtrace("  unenrolling: $userid ==> $course->shortname");
+                        }
                     }
                 }
 
@@ -483,9 +510,15 @@ class enrol_database_plugin extends enrol_plugin {
                     }
                     if ($status != ENROL_USER_SUSPENDED) {
                         $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid'=>$instance->id, 'userid'=>$userid));
+                        if ($verbose) {
+                            mtrace("  suspending: $userid ==> $course->shortname");
+                        }
                     }
                     if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
                         role_unassign_all(array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_database', 'itemid'=>$instance->id));
+                        if ($verbose) {
+                            mtrace("  unsassigning all roles: $userid ==> $course->shortname");
+                        }
                     }
                 }
             }
@@ -493,6 +526,12 @@ class enrol_database_plugin extends enrol_plugin {
 
         // close db connection
         $extdb->Close();
+
+        if ($verbose) {
+            mtrace('...user enrolment synchronisation finished.');
+        }
+
+        return 0;
     }
 
     /**
@@ -500,21 +539,33 @@ class enrol_database_plugin extends enrol_plugin {
      *
      * First it creates new courses if necessary, then
      * enrols and unenrols users.
-     * @return void
+     *
+     * @param bool $verbose
+     * @return int 0 means success, 1 db connect failure, 4 db read failure
      */
-    public function sync_courses() {
+    public function sync_courses($verbose = false) {
         global $CFG, $DB;
 
         // make sure we sync either enrolments or courses
         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
-            return;
+            if ($verbose) {
+                mtrace('Course synchronisation skipped.');
+            }
+            return 0;
+        }
+
+        if ($verbose) {
+            mtrace('Starting course synchronisation...');
         }
 
         // we may need a lot of memory here
         @set_time_limit(0);
         raise_memory_limit(MEMORY_HUGE);
 
-        $extdb = $this->db_init();
+        if (!$extdb = $this->db_init()) {
+            mtrace('Error while communicating with external enrolment database');
+            return 1;
+        }
 
         // first create new courses
         $table     = $this->get_config('newcoursetable');
@@ -534,24 +585,30 @@ class enrol_database_plugin extends enrol_plugin {
         $createcourses = array();
         if ($rs = $extdb->Execute($sql)) {
             if (!$rs->EOF) {
-                $courselist = array();
                 while ($fields = $rs->FetchRow()) {
                     $fields = array_change_key_case($fields, CASE_LOWER);
+                    $fields = $this->db_decode($fields);
                     if (empty($fields[$shortname]) or empty($fields[$fullname])) {
-                        //invalid record - these two are mandatory
+                        if ($verbose) {
+                            mtrace('  error: invalid external course record, shortname and fullname are mandatory: ' . json_encode($fields)); // hopefully every geek can read JS, right?
+                        }
                         continue;
                     }
-                    $fields = $this->db_decode($fields);
                     if ($DB->record_exists('course', array('shortname'=>$fields[$shortname]))) {
                         // already exists
                         continue;
                     }
-                    if ($idnumber and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber]))) {
-                        // idnumber duplicates are not allowed
+                    // allow empty idnumber but not duplicates
+                    if ($idnumber and $fields[$idnumber] !== '' and $fields[$idnumber] !== null and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber]))) {
+                        if ($verbose) {
+                            mtrace('  error: duplicate idnumber, can not create course: '.$fields[$shortname].' ['.$fields[$idnumber].']');
+                        }
                         continue;
                     }
                     if ($category and !$DB->record_exists('course_categories', array('id'=>$fields[$category]))) {
-                        // invalid category id, better to skip
+                        if ($verbose) {
+                            mtrace('  error: invalid category id, can not create course: '.$fields[$shortname]);
+                        }
                         continue;
                     }
                     $course = new stdClass();
@@ -564,9 +621,9 @@ class enrol_database_plugin extends enrol_plugin {
             }
             $rs->Close();
         } else {
-            debugging('Error while communicating with external enrolment database');
+            mtrace('Error reading data from the external course table');
             $extdb->Close();
-            return;
+            return 4;
         }
         if ($createcourses) {
             require_once("$CFG->dirroot/course/lib.php");
@@ -599,7 +656,10 @@ class enrol_database_plugin extends enrol_plugin {
                 $newcourse->idnumber  = $fields->idnumber;
                 $newcourse->category  = $fields->category ? $fields->category : $defaultcategory;
 
-                create_course($newcourse);
+                $c = create_course($newcourse);
+                if ($verbose) {
+                    mtrace("  creating course: $c->id, $c->fullname, $c->shortname, $c->idnumber, $c->category");
+                }
             }
 
             unset($createcourses);
@@ -608,6 +668,12 @@ class enrol_database_plugin extends enrol_plugin {
 
         // close db connection
         $extdb->Close();
+
+        if ($verbose) {
+            mtrace('...course synchronisation finished.');
+        }
+
+        return 0;
     }
 
     protected function db_get_sql($table, array $conditions, array $fields, $distinct = false, $sort = "") {
@@ -631,6 +697,11 @@ class enrol_database_plugin extends enrol_plugin {
         return $sql;
     }
 
+    /**
+     * Tries to make connection to the external database.
+     *
+     * @return null|ADONewConnection
+     */
     protected function db_init() {
         global $CFG;
 
@@ -643,7 +714,11 @@ class enrol_database_plugin extends enrol_plugin {
             ob_start(); //start output buffer to allow later use of the page headers
         }
 
-        $extdb->Connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), true);
+        $result = $extdb->Connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), true);
+        if (!$result) {
+            return null;
+        }
+
         $extdb->SetFetchMode(ADODB_FETCH_ASSOC);
         if ($this->get_config('dbsetupsql')) {
             $extdb->Execute($this->get_config('dbsetupsql'));