MDL-11996 bulk user upload - improvements, fixes and cleanup - part2

This commit is contained in:
skodak 2007-11-02 08:41:05 +00:00
parent 1adbae673c
commit df7ecfe486
3 changed files with 332 additions and 100 deletions

View File

@ -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);
@set_time_limit(0);
@raise_memory_limit('192M');
if (!defined('UP_LINE_MAX_SIZE')) {
define('UP_LINE_MAX_SIZE', 4096);
}
@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 ($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;
$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);
// 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);
foreach ($line as $key => $value) {
$value = trim($value); // remove whitespace
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), $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
}
$line = strtok("\n");
}
// 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->get_data() ) {
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;
// 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,
);
$fields['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);
$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 = 0;
$line = strtok($text, "\n");
while ($line !== false) {
$linenum++;
$line = trim($line);
if ($line == '') {
//ignore empty lines
$line = strtok("\n");
continue;
}
$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
!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);
}
$headers[$key] = $value;
}
$line = false; // found header line
$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));
}
?>

View File

@ -1,7 +1,46 @@
<?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;
$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;
@ -13,14 +52,6 @@ class admin_uploaduser_form extends moodleform {
// upload settings and file
$mform->addElement('header', 'settingsheader', get_string('settings'));
$mform->addElement('file', 'userfile', get_string('file'));
$mform->addRule('userfile', null, 'required');
$textlib = textlib_get_instance();
$choices = $textlib->get_encodings();
$mform->addElement('select', 'encoding', get_string('encoding', 'admin'), $choices);
$mform->setDefault('encoding', 'UTF-8');
$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);
}
}
}
}
}
?>

View 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';