]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-11996 bulk user upload - improvements, fixes and cleanup - part2
authorskodak <skodak>
Fri, 2 Nov 2007 08:41:05 +0000 (08:41 +0000)
committerskodak <skodak>
Fri, 2 Nov 2007 08:41:05 +0000 (08:41 +0000)
admin/uploaduser.php
admin/uploaduser_form.php
lang/en_utf8/admin.php

index 2cc4e6fbc8a73a72a47b1ed120490bc6a6fc8224..246951b318cb630599280fd9a0dfeda51bd94968 100755 (executable)
@@ -1,29 +1,32 @@
-<?php
+<?php  // $Id$
 
 /// Bulk user registration script from a comma separated file
 /// Returns list of users with their user ids
 
 require_once('../config.php');
 require_once($CFG->libdir.'/adminlib.php');
-require_once($CFG->libdir.'/textlib.class.php');
 require_once('uploaduser_form.php');
-define('LINE_MAX_SIZE', 1024);
 
-//Note: commas within a field should be encoded as &#44 (for comma separated csv files)
-//Note: semicolon within a field should be encoded as &#59 (for semicolon separated csv files)
-$csv_delimiter = isset($CFG->CSV_DELIMITER) ? $CFG->CSV_DELIMITER : ',';
-$csv_encode = '&#' . (isset($CFG->CSV_ENCODE) ? $CFG->CSV_ENCODE : ord($csv_delimiter));
+$uplid       = optional_param('uplid', '', PARAM_FILE);
+$previewrows = optional_param('previewrows', 10, PARAM_INT);
+$separator   = optional_param('separator', 'comma', PARAM_ALPHA);
+
+if (!defined('UP_LINE_MAX_SIZE')) {
+    define('UP_LINE_MAX_SIZE', 4096);
+}
 
-@set_time_limit(0);
-@raise_memory_limit('192M');
+@set_time_limit(3600); // 1 hour should be enough
+@raise_memory_limit('256M');
+if (function_exists('apache_child_terminate')) {
+    // if we are running from Apache, give httpd a hint that 
+    // it can recycle the process after it's done. Apache's 
+    // memory management is truly awful but we can help it.
+    @apache_child_terminate();
+}
 
 admin_externalpage_setup('uploadusers');
 require_capability('moodle/site:uploadusers', get_context_instance(CONTEXT_SYSTEM));
 
-if (! $site = get_site()) {
-    error('Could not find site-level course');
-}
-
 $textlib = textlib_get_instance();
 
 $struserrenamed = get_string('userrenamed', 'admin');
@@ -45,85 +48,158 @@ $strcannotassignrole = get_string('cannotassignrole', 'error');
 $strduplicateusername = get_string('duplicateusername', 'error');
 $strindent = '-->';
 
-$mform = new admin_uploaduser_form();
+$return = $CFG->wwwroot.'/'.$CFG->admin.'/uploaduser.php';
 
-// Print the header
+// make arrays of valid fields for error checking
+// the value associated to each field is: 0 = optional field, 1 = field required either in default values or in data file
+$fields = array(
+    'firstname' => 1,
+    'lastname' => 1,
+    'username' => 1,
+    'email' => 1,
+    'city' => 1,
+    'country' => 1,
+    'lang' => 1,
+    'auth' => 1,
+    'timezone' => 1,
+    'mailformat' => 1,
+    'maildisplay' => 1,
+    'htmleditor' => 0,
+    'ajax' => 0,
+    'autosubscribe' => 1,
+    'mnethostid' => 0,
+    'institution' => 0,
+    'department' => 0,
+    'idnumber' => 0,
+    'icq' => 0,
+    'phone1' => 0,
+    'phone2' => 0,
+    'address' => 0,
+    'url' => 0,
+    'description' => 0,
+    'icq' => 0,
+    'oldusername' => 0,
+    'emailstop' => 1,
+    'deleted' => 0,
+    'password' => 0, // changed later
+);
 
-admin_externalpage_print_header();
+if (empty($uplid)) {
+    $mform = new admin_uploaduser_form1();
 
-// If a file has been uploaded, then process it
-if ( $formdata = $mform->get_data() ) {
-    $createpassword = $formdata->createpassword;
-    $updateaccounts = $formdata->updateaccounts;
-    $allowrenames   = $formdata->allowrenames;
-    $skipduplicates = $formdata->duplicatehandling;
+    if ($formdata = $mform->get_data()) {
+        if (!$filename = make_upload_directory('temp/uploaduser/'.$USER->id, true)) {
+            error('Can not create temporary upload directory!', $return);
+        }
+        // use current (non-conflicting) time stamp
+        $uplid = time();
+        while (file_exists($filename.'/'.$uplid)) {
+            $uplid--;
+        }
+        $filename = $filename.'/'.$uplid;
 
-    // make arrays of valid fields for error checking
-    // the value associated to each field is: 0 = optional field, 1 = field required either in default values or in data file
-    $fields = array(
-        'firstname' => 1,
-        'lastname' => 1,
-        'username' => 1,
-        'email' => 1,
-        'city' => 1,
-        'country' => 1,
-        'lang' => 1,
-        'auth' => 1,
-        'timezone' => 1,
-        'mailformat' => 1,
-        'maildisplay' => 1,
-        'htmleditor' => 0,
-        'ajax' => 0,
-        'autosubscribe' => 1,
-        'mnethostid' => 0,
-        'institution' => 0,
-        'department' => 0,
-        'idnumber' => 0,
-        'icq' => 0,
-        'phone1' => 0,
-        'phone2' => 0,
-        'address' => 0,
-        'url' => 0,
-        'description' => 0,
-        'icq' => 0,
-        'oldusername' => 0,
-        'emailstop' => 1,
-        'deleted' => 0,
-        'password' => !$createpassword,
-    );
-
-    $text = $mform->get_file_content('userfile');
-    // convert to utf-8 encoding
-    $text = $textlib->convert($text, $formdata->encoding, 'utf-8');
-    // remove Unicode BOM from first line
-    $text = $textlib->trim_utf8_bom($text);
-    // Fix mac/dos newlines
-    $text = preg_replace('!\r\n?!', "\n", $text);
+        $text = $mform->get_file_content('userfile');
+        // convert to utf-8 encoding
+        $text = $textlib->convert($text, $formdata->encoding, 'utf-8');
+        // remove Unicode BOM from first line
+        $text = $textlib->trim_utf8_bom($text);
+        // Fix mac/dos newlines
+        $text = preg_replace('!\r\n?!', "\n", $text);
+        //remove empty lines at the beginning and end
+        $text = trim($text);
 
-    // find header row
-    $headers = array();
-    $linenum = 0;
-    $line = strtok($text, "\n");
-    while ($line !== false) {
-        $linenum++;
-        $line = trim($line);
-        if ($line == '') {
-            //ignore empty lines
-            $line = strtok("\n");
-            continue;
+        // verify each line has the same number of separators - this detects major breakage in files
+        $line = strtok($text, "\n");
+        if ($line === false) {
+            error('Empty file', $return); //TODO: localize
+        }
+
+        // test headers
+        $csv_delimiter = get_upload_csv_delimiter($separator);
+        $col_count = substr_count($line, $csv_delimiter);
+        if ($col_count < 2) {
+            error('Not enough columns, please verify the separator setting!', $return); //TODO: localize
         }
+
         $line = explode($csv_delimiter, $line);
-        // check for valid field names
         foreach ($line as $key => $value) {
             $value = trim($value); // remove whitespace
-            if (!in_array($value, $fields) && // if not a standard field and not an enrolment field, then we have an error
+            if (!array_key_exists($value, $fields) && // if not a standard field and not an enrolment field, then we have an error
                 !preg_match('/^course\d+$/', $value) && !preg_match('/^group\d+$/', $value) &&
                 !preg_match('/^type\d+$/', $value) && !preg_match('/^role\d+$/', $value)) {
-                error(get_string('invalidfieldname', 'error', $value), 'uploaduser.php?sesskey='.$USER->sesskey);
+                error(get_string('invalidfieldname', 'error', $value), $return);
+            }
+        }
+
+        $line = strtok("\n");
+        if ($line === false) {
+            error('Only one row present, can not continue!', $return); //TODO: localize
+        }
+
+        while ($line !== false) {
+            if (substr_count($line, $csv_delimiter) !== $col_count) {
+                error('Incorrect file format - number of columns is not constant!', $return); //TODO: localize
             }
-            $headers[$key] = $value;
+            $line = strtok("\n");
         }
-        $line = false; // found header line
+
+        // store file
+        $fp = fopen($filename, "w");
+        fwrite($fp,$text);
+        fclose($fp);
+        // continue to second form
+
+    } else {
+        admin_externalpage_print_header();
+        print_heading_with_help(get_string('uploadusers'), 'uploadusers2');
+        $mform->display();
+        admin_externalpage_print_footer();
+        die;
+    }
+}
+
+$mform = new admin_uploaduser_form2();
+// set initial date from form1
+$mform->set_data(array('separator'=>$separator, 'uplid'=>$uplid, 'previewrows'=>$previewrows));
+
+// If a file has been uploaded, then process it
+if ($formdata = $mform->is_cancelled()) {
+    user_upload_cleanup($uplid);
+    redirect($return);
+
+} else if ($formdata = $mform->get_data()) {
+    // Print the header
+    admin_externalpage_print_header();
+    print_heading(get_string('uploadusers'));
+
+    $createpassword = $formdata->createpassword;
+    $updateaccounts = $formdata->updateaccounts;
+    $allowrenames   = $formdata->allowrenames;
+    $skipduplicates = $formdata->duplicatehandling;
+
+    $fields['password'] = !$createpassword;
+
+    $filename = $CFG->dataroot.'/temp/uploaduser/'.$USER->id.'/'.$uplid;
+    if (!file_exists($filename)) {
+        user_upload_cleanup($uplid);
+        error('Error reading temporary file!', $return); //TODO: localize
+    }
+    if (!$fp = fopen($filename, "r")) {
+        user_upload_cleanup($uplid);
+        error('Error reading temporary file!', $return); //TODO: localize
+    }
+    
+    $csv_delimiter = get_upload_csv_delimiter($separator);
+    $csv_encode    = get_upload_csv_encode($csv_delimiter);
+
+    // find header row
+    $headers = array();
+    $linenum = 1;
+    $line = explode($csv_delimiter, fgets($fp, UP_LINE_MAX_SIZE));
+
+    // prepare headers
+    foreach ($line as $key => $value) {
+        $headers[$key] = trim($value);
     }
 
     // check that required fields are present or a default value for them exists
@@ -155,16 +231,9 @@ if ( $formdata = $mform->get_data() ) {
         unset($tmp);
 
         echo '<p id="results">';
-        $line = strtok("\n");
-        while ($line !== false) {
+        while (!feof($fp)) {
             $linenum++;
-            $line = trim($line);
-            if ($line == '') {
-                //empty line??
-                $line = strtok("\n");
-                continue;
-            }
-            $line = explode($csv_delimiter, $line);
+            $line = explode($csv_delimiter, fgets($fp, UP_LINE_MAX_SIZE));
             $errors = '';
             $user = new object();
             // by default, use the local mnet id (this may be changed in the file)
@@ -416,8 +485,6 @@ if ( $formdata = $mform->get_data() ) {
                     }
                 }
             }
-            //read next line
-            $line = strtok("\n");
         }
         echo '</p>';
         notify(get_string('userscreated', 'admin') . ': ' . $usersnew);
@@ -430,11 +497,119 @@ if ( $formdata = $mform->get_data() ) {
         }
         notify(get_string('errors', 'admin') . ': ' . $userserrors);
     }
+    fclose($fp);
+    user_upload_cleanup($uplid);
     echo '<hr />';
+    print_continue($return);
+    admin_externalpage_print_footer();
+    die;
 }
 
+// Print the header
+admin_externalpage_print_header();
+
 /// Print the form
 print_heading_with_help(get_string('uploadusers'), 'uploadusers2');
+
+/// Print csv file preview
+$filename = $CFG->dataroot.'/temp/uploaduser/'.$USER->id.'/'.$uplid;
+if (!file_exists($filename)) {
+    error('Error reading temporary file!', $return); //TODO: localize
+}
+if (!$fp = fopen($filename, "r")) {
+    error('Error reading temporary file!', $return); //TODO: localize
+}
+
+$csv_delimiter = get_upload_csv_delimiter($separator);
+$csv_encode    = get_upload_csv_encode($csv_delimiter);
+
+$header = explode($csv_delimiter, fgets($fp, UP_LINE_MAX_SIZE));
+
+$width = count($header);
+$columncount = 0;
+$rowcount = 0;
+echo '<table class="flexible boxaligncenter generaltable">';
+echo '<tr class="heading r'.$rowcount++.'">';
+foreach ($header as $h) {
+    echo '<th class="header c'.$columncount++.'">'.trim($h).'</th>';
+}
+echo '</tr>';
+
+while (!feof($fp) and $rowcount <= $previewrows+1) {
+    $columncount = 0;
+    $fields = explode($csv_delimiter, fgets($fp, UP_LINE_MAX_SIZE));
+    echo '<tr class="r'.$rowcount++.'">';
+    foreach ($fields as $field) {
+        echo '<td class=" c'.$columncount++.'">'.trim(str_replace($csv_encode, $csv_delimiter, $field)).'</td>';;
+    }
+    echo '</tr>';
+}
+if ($rowcount > $previewrows+1) {
+    echo '<tr class="r'.$rowcount++.'">';
+    foreach ($fields as $field) {
+        echo '<td class=" c'.$columncount++.'">...</td>';;
+    }
+}
+echo '</table>';
+fclose($fp);
+
 $mform->display();
 admin_externalpage_print_footer();
+die;
+
+/////////////////////////
+/// Utility functions ///
+/////////////////////////
+
+function user_upload_cleanup($uplid) {
+    global $USER, $CFG;
+    if (empty($uplid)) {
+        return;
+    }
+    $filename = $CFG->dataroot.'/temp/uploaduser/'.$USER->id.'/'.$uplid;
+    if (file_exists($filename)) {
+        @unlink($filename);
+    }
+}
+
+function get_uf_headers($uplid, $separator) {
+    global $USER, $CFG;
+
+    $filename = $CFG->dataroot.'/temp/uploaduser/'.$USER->id.'/'.$uplid;
+    if (!file_exists($filename)) {
+        return false;
+    }
+    $fp = fopen($filename, "r");
+    $line = fgets($fp, 2048);
+    fclose($fp);
+    if ($line === false) {
+        return false;
+    }
+
+    $csv_delimiter = get_upload_csv_delimiter($separator);
+    $headers = explode($csv_delimiter, $line);
+    foreach($headers as $key=>$val) {
+        $headers[$key] = trim($val);
+    }
+    return $headers;
+}
+
+function get_upload_csv_delimiter($separator) {
+    global $CFG;
+
+    switch ($separator) {
+        case 'semicolon' : return ';';
+        case 'colon'     : return ':';
+        case 'tab'       : return "\t";
+        case 'cfg'       : return isset($CFG->CSV_DELIMITER) ? $CFG->CSV_DELIMITER : ',';
+        default          : return ',';
+    }
+}
+
+function get_upload_csv_encode($delimiter) {
+//Note: commas within a field should be encoded as &#44 (for comma separated csv files)
+//Note: semicolon within a field should be encoded as &#59 (for semicolon separated csv files)
+    global $CFG;
+    return '&#' . (isset($CFG->CSV_ENCODE) ? $CFG->CSV_ENCODE : ord($delimiter));
+}
 ?>
index 6edfdbc6a01a438f0cf445762966fd93ae3af9bd..140a17685b9599b7691dee741fa1c4c785eb8372 100644 (file)
@@ -1,26 +1,57 @@
 <?php // $Id$
 require_once $CFG->libdir.'/formslib.php';
 
-class admin_uploaduser_form extends moodleform {
+class admin_uploaduser_form1 extends moodleform {
     function definition (){
         global $CFG, $USER;
 
+        $this->set_upload_manager(new upload_manager('userfile', false, false, null, false, 0, true, true, false));
+
         $mform =& $this->_form;
 
-        // I am the tamplate user
-        $templateuser = $USER;
-
-// upload settings and file
-        $mform->addElement('header', 'settingsheader', get_string('settings'));
+        $mform->addElement('header', 'settingsheader', get_string('upload'));
 
         $mform->addElement('file', 'userfile', get_string('file'));
         $mform->addRule('userfile', null, 'required');
 
+        $choices = array('comma'=>',', 'semicolon'=>';', 'colon'=>':', 'tab'=>'\\t');
+        if (isset($CFG->CSV_DELIMITER) and !in_array($CFG->CSV_DELIMITER, $choices)) {
+            $choices['cfg'] = $CFG->CSV_DELIMITER; 
+        }
+        $mform->addElement('select', 'separator', get_string('csvseparator', 'admin'), $choices);
+        if (array_key_exists('cfg', $choices)) {
+            $mform->setDefault('separator', 'cfg');
+        } else if (get_string('listsep') == ';') {
+            $mform->setDefault('separator', 'semicolon');
+        } else {
+            $mform->setDefault('separator', 'comma');
+        }
+
         $textlib = textlib_get_instance();
         $choices = $textlib->get_encodings();
         $mform->addElement('select', 'encoding', get_string('encoding', 'admin'), $choices);
         $mform->setDefault('encoding', 'UTF-8');
 
+        $choices = array('10'=>10, '20'=>20, '100'=>100, '1000'=>1000, '100000'=>100000);
+        $mform->addElement('select', 'previewrows', get_string('rowpreviewnum', 'admin'), $choices);
+        $mform->setType('previewrows', PARAM_INT);
+
+        $this->add_action_buttons(false, get_string('uploadusers'));
+    }
+}
+
+class admin_uploaduser_form2 extends moodleform {
+    function definition (){
+        global $CFG, $USER;
+
+        $mform =& $this->_form;
+
+        // I am the tamplate user
+        $templateuser = $USER;
+
+// upload settings and file
+        $mform->addElement('header', 'settingsheader', get_string('settings'));
+
         $choices = array(0 => get_string('infilefield', 'auth'), 1 => get_string('createpasswordifneeded', 'auth'));
         $mform->addElement('select', 'createpassword', get_string('passwordhandling', 'auth'), $choices);
 
@@ -131,8 +162,32 @@ class admin_uploaduser_form extends moodleform {
         $mform->setType('address', PARAM_MULTILANG);
         $mform->setAdvanced('address');
 
-        $this->add_action_buttons(false, get_string('uploadusers'));
+// hidden fields
+        $mform->addElement('hidden', 'uplid');
+        $mform->setType('uplid', PARAM_FILE);
+
+        $mform->addElement('hidden', 'separator');
+        $mform->setType('separator', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'previewrows');
+        $mform->setType('previewrows', PARAM_ALPHA);
+
+        $this->add_action_buttons(true, get_string('uploadusers'));
     }
 
+    function definition_after_data() {
+        $mform =& $this->_form;
+
+        $separator = $mform->getElementValue('separator');
+        $uplid     = $mform->getElementValue('uplid');
+        
+        if ($headers = get_uf_headers($uplid, $separator)) {
+            foreach ($headers as $header) {
+                if ($mform->elementExists($header)) {
+                    $mform->removeElement($header);
+                }
+            }
+        }
+    }
 }
 ?>
index f2834b780d75acdc5bf3eba1f40669ba13a80330..1635f73cf89b5fcc3040346f386cca6b7e649bbd 100644 (file)
@@ -231,6 +231,7 @@ $string['cronerrorclionly'] = 'Sorry, internet access to this page has been disa
 $string['cronerrorpassword'] = 'Sorry, you have not provided a valid password to access this page';
 $string['cronremotepassword'] = 'Cron password for remote access';
 $string['cronwarning'] = 'The <a href=\"cron.php\">cron.php maintenance script</a> has not been run for at least 24 hours.';
+$string['csvseparator'] = 'CSV separator';
 $string['curlrecommended'] = 'Installing the optional Curl library is highly recommended in order to enable Moodle Networking functionality.';
 $string['customcheck'] = 'Other Checks';
 $string['datarootsecuritywarning'] = 'Your site configuration might not be secure. Please make sure that your dataroot directory ($a) is not directly accessible via web.';
@@ -547,6 +548,7 @@ $string['riskspam'] = 'Users could send spam to site users or others';
 $string['riskspamshort'] = 'Spam risk';
 $string['riskxss'] = 'Users could add files and texts that allow cross-site scripting (XSS)';
 $string['riskxssshort'] = 'XSS risk';
+$string['rowpreviewnum'] = 'Preview rows';
 $string['runclamavonupload'] = 'Use clam AV on uploaded files';
 $string['savechanges'] = 'Save Changes';
 $string['search'] = 'Search';