Skip to content
Snippets Groups Projects
Commit ae1afd2f authored by Christopher Smith's avatar Christopher Smith
Browse files

add csv import functionality to the user manager

parent 5c967d3d
No related branches found
No related tags found
No related merge requests found
......@@ -30,6 +30,7 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
var $_edit_user = ''; // set to user selected for editing
var $_edit_userdata = array();
var $_disabled = ''; // if disabled set to explanatory string
var $_import_failures = array();
/**
* Constructor
......@@ -49,6 +50,11 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
$this->_auth = & $auth;
}
// attempt to retrieve any import failures from the session
if ($_SESSION['import_failures']){
$this->_import_failures = $_SESSION['import_failures'];
}
}
/**
......@@ -102,6 +108,8 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
$this->_start = 0;
break;
case "export" : $this->_export(); break;
case "import" : $this->_import(); break;
case "importfails" : $this->_downloadImportFailures(); break;
}
$this->_user_total = $this->_auth->canDo('getUserCount') ? $this->_auth->getUserCount($this->_filter) : -1;
......@@ -238,6 +246,10 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
ptln(" </div>");
ptln("</div>");
}
if ($this->_auth->canDo('addUser')) {
$this->_htmlImportForm();
}
ptln("</div>");
}
......@@ -352,6 +364,59 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
}
}
function _htmlImportForm($indent=0) {
global $ID;
$failure_download_link = wl($ID,array('do'=>'admin','page'=>'usermanager','fn[importfails]'=>1));
ptln('<div class="level2 import_users">',$indent);
print $this->locale_xhtml('import');
ptln(' <form action="'.wl($ID).'" method="post" enctype="multipart/form-data">',$indent);
formSecurityToken();
ptln(' <label>User list file (csv): <input type="file" name="import" /></label>',$indent);
ptln(' <input type="submit" name="fn[import]" value="'.$this->lang['import'].'" />',$indent);
ptln(' <input type="hidden" name="do" value="admin" />',$indent);
ptln(' <input type="hidden" name="page" value="usermanager" />',$indent);
$this->_htmlFilterSettings($indent+4);
ptln(' </form>',$indent);
ptln('</div>');
// list failures from the previous import
if ($this->_import_failures) {
$digits = strlen(count($this->_import_failures));
ptln('<div class="level3 import_failures">',$indent);
ptln(' <h3>Most Recent Import - Failures</h3>');
ptln(' <table class="import_failures">',$indent);
ptln(' <thead>',$indent);
ptln(' <tr>',$indent);
ptln(' <th class="line">'.$this->lang['line'].'</th>',$indent);
ptln(' <th class="error">'.$this->lang['error'].'</th>',$indent);
ptln(' <th class="userid">'.$this->lang['user_id'].'</th>',$indent);
ptln(' <th class="username">'.$this->lang['user_name'].'</th>',$indent);
ptln(' <th class="usermail">'.$this->lang['user_mail'].'</th>',$indent);
ptln(' <th class="usergroups">'.$this->lang['user_groups'].'</th>',$indent);
ptln(' </tr>',$indent);
ptln(' </thead>',$indent);
ptln(' <tbody>',$indent);
foreach ($this->_import_failures as $line => $failure) {
ptln(' <tr>',$indent);
ptln(' <td class="lineno"> '.sprintf('%0'.$digits.'d',$line).' </td>',$indent);
ptln(' <td class="error">' .$failure['error'].' </td>', $indent);
ptln(' <td class="field userid"> '.hsc($failure['user'][0]).' </td>',$indent);
ptln(' <td class="field username"> '.hsc($failure['user'][2]).' </td>',$indent);
ptln(' <td class="field usermail"> '.hsc($failure['user'][3]).' </td>',$indent);
ptln(' <td class="field usergroups"> '.hsc($failure['user'][4]).' </td>',$indent);
ptln(' </tr>',$indent);
}
ptln(' </tbody>',$indent);
ptln(' </table>',$indent);
ptln(' <p><a href="'.$failure_download_link.'">Download Failures as CSV for correction</a></p>');
ptln('</div>');
}
}
function _addUser(){
global $INPUT;
if (!checkSecurityToken()) return false;
......@@ -666,4 +731,126 @@ class admin_plugin_usermanager extends DokuWiki_Admin_Plugin {
fclose($fd);
die;
}
/*
* import a file of users in csv format
*
* csv file should have 4 columns, user_id, full name, email, groups (comma separated)
*/
function _import() {
// check we are allowed to add users
if (!checkSecurityToken()) return false;
if (!$this->_auth->canDo('addUser')) return false;
// check file uploaded ok.
if (empty($_FILES['import']['size']) || !empty($FILES['import']['error']) && is_uploaded_file($FILES['import']['tmp_name'])) {
msg($this->lang['import_error_upload'],-1);
return false;
}
// retrieve users from the file
$this->_import_failures = array();
$import_success_count = 0;
$import_fail_count = 0;
$line = 0;
$fd = fopen($_FILES['import']['tmp_name'],'r');
if ($fd) {
while($csv = fgets($fd)){
$raw = str_getcsv($csv);
$error = ''; // clean out any errors from the previous line
// data checks...
if (1 == ++$line) {
if ($raw[0] == 'user_id' || $raw[0] == $this->lang['user_id']) continue; // skip headers
}
if (count($raw) < 4) { // need at least four fields
$import_fail_count++;
$error = sprintf($this->lang['import_error_fields'], count($raw));
$this->_import_failures[$line] = array('error' => $error, 'user' => $raw, 'orig' => $csv);
continue;
}
array_splice($raw,1,0,auth_pwgen()); // splice in a generated password
$clean = $this->_cleanImportUser($raw, $error);
if ($clean && $this->_addImportUser($clean, $error)) {
# $this->_notifyUser($clean[0],$clean[1]);
$import_success_count++;
} else {
$import_fail_count++;
$this->_import_failures[$line] = array('error' => $error, 'user' => $raw, 'orig' => $csv);
}
}
msg(sprintf($this->lang['import_success_count'], ($import_success_count+$import_fail_count), $import_success_count),($import_success_count ? 1 : -1));
if ($import_fail_count) {
msg(sprintf($this->lang['import_failure_count'], $import_fail_count),-1);
}
} else {
msg($this->lang['import_error_readfail'],-1);
}
// save import failures into the session
if (!headers_sent()) {
session_start();
$_SESSION['import_failures'] = $this->_import_failures;
session_write_close();
}
}
function _cleanImportUser($candidate, & $error){
global $INPUT;
// kludgy ....
$INPUT->set('userid', $candidate[0]);
$INPUT->set('userpass', $candidate[1]);
$INPUT->set('username', $candidate[2]);
$INPUT->set('usermail', $candidate[3]);
$INPUT->set('usergroups', $candidate[4]);
$cleaned = $this->_retrieveUser();
list($user,$pass,$name,$mail,$grps) = $cleaned;
if (empty($user)) {
$error = $this->lang['import_error_baduserid'];
return false;
}
// no need to check password, handled elsewhere
if (!($this->_auth->canDo('modName') xor empty($name))){
$error = $this->lang['import_error_badname'];
return false;
}
if (!($this->_auth->canDo('modMail') xor empty($mail))){
$error = $this->lang['import_error_badmail'];
return false;
}
return $cleaned;
}
function _addImportUser($user, & $error){
if (!$this->_auth->triggerUserMod('create', $user)) {
$error = $this->lang['import_error_create'];
return false;
}
return true;
}
function _downloadImportFailures(){
// ==============================================================================================
// GENERATE OUTPUT
// normal headers for downloading...
header('Content-type: text/csv;charset=utf-8');
header('Content-Disposition: attachment; filename="importfails.csv"');
# // for debugging assistance, send as text plain to the browser
# header('Content-type: text/plain;charset=utf-8');
// output the csv
$fd = fopen('php://output','w');
foreach ($this->_import_failures as $line => $fail) {
fputs($fd, $fail['orig']);
}
fclose($fd);
die;
}
}
===== Bulk User Import =====
Requires a CSV file of users with at least four columns.
The columns must contain, in order: user-id, full name, email address and groups.
The CSV fields should be separated by commas (,) and strings delimited by quotation marks (""). Backslash (\) can be used for escaping.
For an example of a suitable file, try the "Export Users" function above.
Duplicate user-ids will be ignored.
A password will be generated and emailed to each successfully imported user.
......@@ -33,6 +33,9 @@ $lang['clear'] = 'Reset Search Filter';
$lang['filter'] = 'Filter';
$lang['export_all'] = 'Export All Users (CSV)';
$lang['export_filtered'] = 'Export Filtered User list (CSV)';
$lang['import'] = 'Import New Users';
$lang['line'] = 'Line no.';
$lang['error'] = 'Error message';
$lang['summary'] = 'Displaying users %1$d-%2$d of %3$d found. %4$d users total.';
$lang['nonefound'] = 'No users found. %d users total.';
......@@ -58,3 +61,15 @@ $lang['add_fail'] = 'User addition failed';
$lang['notify_ok'] = 'Notification email sent';
$lang['notify_fail'] = 'Notification email could not be sent';
// import errors
$lang['import_success_count'] = 'User Import: %d users found, %d imported successfully.';
$lang['import_failure_count'] = 'User Import: %d failed. Failures are listed below.';
$lang['import_error_fields'] = "Insufficient fields, found %d, require 4.";
$lang['import_error_baduserid'] = "User-id missing";
$lang['import_error_badname'] = 'Bad name';
$lang['import_error_badmail'] = 'Bad mail';
$lang['import_error_upload'] = 'Import Failed. The csv file could not be uploaded or is empty.';
$lang['import_error_readfail'] = 'Import Failed. Unable to read uploaded file.';
$lang['import_error_create'] = 'Unable to create the user';
......@@ -17,4 +17,7 @@
color: #ccc!important;
border-color: #ccc!important;
}
#user__manager .import_failures {
margin-top: 1em;
}
/* IE won't understand but doesn't require it */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment