bulkimport.php 13.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
<?php
/**
 * Mahara: Electronic portfolio, weblog, resume builder and social networking
 * Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
 *                         http://wiki.mahara.org/Contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @package    mahara
 * @subpackage admin
 * @author     Catalyst IT Ltd
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 2006-2009 Catalyst IT Ltd http://catalyst.net.nz
 *
 */

define('INTERNAL', 1);
define('ADMIN', 1);
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require_once('pieforms/pieform.php');
require_once('institution.php');
33
require_once(get_config('docroot') . '/lib/htmloutput.php');
34 35
safe_require('artefact', 'internal');
safe_require('artefact', 'file');
36 37
raise_memory_limit('1024M');
set_time_limit(300); // 5 minutes
38 39 40 41 42 43

define('TITLE', get_string('bulkleap2aimport', 'admin'));

// Turn on autodetecting of line endings, so mac newlines (\r) will work
ini_set('auto_detect_line_endings', 1);

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
$ADDEDUSERS = $SESSION->get('bulkimport_addedusers');
if (empty($ADDEDUSERS)) {
    $ADDEDUSERS = array();
}
$FAILEDUSERS = $SESSION->get('bulkimport_failedusers');
if (empty($FAILEDUSERS)) {
    $FAILEDUSERS = array();
}
$LEAP2AFILES = $SESSION->get('bulkimport_leap2afiles');
if (empty($LEAP2AFILES)) {
    $LEAP2AFILES = array();
}
$AUTHINSTANCE = $SESSION->get('bulkimport_authinstance');
$EMAILUSERS = $SESSION->get('bulkimport_emailusers');

// Import in progress
if (!empty($LEAP2AFILES)) {
    import_next_user();
}
elseif (!empty($ADDEDUSERS) or !empty($FAILEDUSERS)) {
    finish_import();
}

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
$authinstances = auth_get_auth_instances();

if (count($authinstances) > 0) {
    $options = array();

    foreach ($authinstances as $authinstance) {
        $options[$authinstance->id] = $authinstance->displayname. ': '.$authinstance->instancename;
    }
    $default = key($options);

    $authinstanceelement = array(
        'type' => 'select',
        'title' => get_string('institution'),
        'description' => get_string('uploadcsvinstitution', 'admin'),
        'options' => $options,
        'defaultvalue' => $default
    );
}

$form = array(
    'name' => 'bulkimport',
    'elements' => array(
        'authinstance' => $authinstanceelement,
90
        'file' => array(
91
            'type' => 'text',
92 93 94
            'title' => get_string('importfile', 'admin'),
            'size' => 40,
            'description' => get_string('bulkleap2aimportfiledescription', 'admin'),
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
            'rules' => array(
                'required' => true
            )
        ),
        'emailusers' => array(
            'type' => 'checkbox',
            'title' => get_string('emailusersaboutnewaccount', 'admin'),
            'description' => get_string('emailusersaboutnewaccountdescription', 'admin'),
            'defaultvalue' => true,
        ),
        'submit' => array(
            'type' => 'submit',
            'value' => get_string('Import', 'admin')
        )
    )
);

112 113 114 115 116
/**
 * Work-around the redirection limit of Firefox (http://kb.mozillazine.org/Network.http.redirection-limit)
 */
function meta_redirect() {
    $url = get_config('wwwroot') . '/admin/users/bulkimport.php';
117
    print_meta_redirect($url);
118 119 120
    exit;
}

121 122 123 124 125 126 127 128 129
/**
 * The CSV file is parsed here so validation errors can be returned to the
 * user. The data from a successful parsing is stored in the <var>$LEAP2AFILES</var>
 * array so it can be accessed by the submit function
 *
 * @param Pieform  $form   The form to validate
 * @param array    $values The values submitted
 */
function bulkimport_validate(Pieform $form, $values) {
130
    global $LEAP2AFILES, $USER;
131 132 133 134 135 136 137 138

    // Don't even start attempting to parse if there are previous errors
    if ($form->has_errors()) {
        return;
    }

    require_once('csvfile.php');

139 140 141
    $zipfile = $values['file'];
    if (!is_file($zipfile)) {
        $form->set_error('file', get_string('importfilenotafile', 'admin'));
142 143
        return;
    }
144 145
    if (!is_readable($zipfile)) {
        $form->set_error('file', get_string('importfilenotreadable', 'admin'));
146 147 148
        return;
    }

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    // Create temporary directory
    $importdir = get_config('dataroot') . 'import/'
        . $USER->get('id')  . '/' . time() .  '/';
    if (!check_dir_exists($importdir)) {
        throw new SystemException("Couldn't create the temporary export directory $importdir");
    }

    $command = sprintf('%s %s %s',
                       escapeshellcmd(get_config('pathtounzip')),
                       escapeshellarg($zipfile),
                       '-d ' . escapeshellarg($importdir)
                       );
    $output = array();
    exec($command, $output, $returnvar);
    if ($returnvar != 0) {
        log_debug("unzip command failed with return value $returnvar");
        // Let's make it obvious if the cause is obvious :)
        if ($returnvar == 127) {
            log_debug("This means that 'unzip' isn't installed, or the config var \$cfg->pathtounzip is not"
                      . " pointing at unzip (see Mahara's file lib/config-defaults.php)");
        }
        throw new SystemException(get_string('unzipfailed', 'admin', hsc($zipfile)));
    }
    else {
        log_debug("Unzipped $zipfile into $importdir");
    }
175

176
    $csvfilename = $importdir . '/usernames.csv';
177
    if (!is_readable($csvfilename)) {
178
        $form->set_error('file', get_string('importfilemissinglisting', 'admin'));
179 180 181 182 183 184 185 186 187
        return;
    }

    $csvusers = new CsvFile($csvfilename);
    $csvusers->set('headerExists', false);
    $csvusers->set('format', array('username', 'filename'));
    $csvdata = $csvusers->get_data();

    if (!empty($csvdata->errors['file'])) {
188
        $form->set_error('file', get_string('invalidlistingfile', 'admin'));
189 190 191
        return;
    }

192 193 194 195
    foreach ($csvdata->data as $user) {
        $username = $user[0];
        $filename = $user[1];
        $LEAP2AFILES[$username] = "$importdir/users/$filename";
196 197 198 199 200 201 202 203 204
    }
}

/**
 * Add the users to the system.
 */
function bulkimport_submit(Pieform $form, $values) {
    global $SESSION, $LEAP2AFILES;

Francois Marier's avatar
Francois Marier committed
205
    log_info('Attempting to import ' . count($LEAP2AFILES) . ' users from Leap2A files');
206 207 208 209 210 211 212 213 214 215 216 217 218

    $SESSION->set('bulkimport_leap2afiles', $LEAP2AFILES);
    $SESSION->set('bulkimport_authinstance', (int)$values['authinstance']);
    $SESSION->set('bulkimport_emailusers', $values['emailusers']);
    $SESSION->set('bulkimport_addedusers', '');
    $SESSION->set('bulkimport_failedusers', '');

    redirect(get_config('wwwroot') . '/admin/users/bulkimport.php');
}

function import_next_user() {
    global $SESSION, $ADDEDUSERS, $FAILEDUSERS, $LEAP2AFILES, $AUTHINSTANCE;

219 220 221
    require_once(get_config('docroot') . 'import/lib.php');
    safe_require('import', 'leap');

222 223 224 225 226 227 228 229
    // Pop the last element off of the LEAP2AFILES array
    $filename = end($LEAP2AFILES);
    $username = key($LEAP2AFILES);
    unset($LEAP2AFILES[$username]);

    log_debug('adding user ' . $username . ' from ' . $filename);

    $authobj = get_record('auth_instance', 'id', $AUTHINSTANCE);
230 231
    $institution = new Institution($authobj->institution);

232 233 234
    $date = time();
    $nicedate = date('Y/m/d h:i:s', $date);
    $niceuser = preg_replace('/[^a-zA-Z0-9_-]/', '-', $username);
235

236
    $uploaddir = get_config('dataroot') . 'import/' . $niceuser . '-' . $date . '/';
237

238
    check_dir_exists($uploaddir);
239

240 241 242 243 244 245 246 247 248 249 250 251 252 253
    // Unzip the file
    $command = sprintf('%s %s %s %s',
                       escapeshellcmd(get_config('pathtounzip')),
                       escapeshellarg($filename),
                       get_config('unzipdirarg'),
                       escapeshellarg($uploaddir)
                       );
    $output = array();
    exec($command, $output, $returnvar);
    if ($returnvar != 0) {
        $FAILEDUSERS[$username] = get_string('unzipfailed', 'admin', hsc($filename));
        log_debug("unzip command failed with return value $returnvar");
        continue;
    }
254

255 256 257 258 259 260
    $leap2afilename = $uploaddir . 'leap2a.xml';
    if (!is_file($leap2afilename)) {
        $FAILEDUSERS[$username] = get_string('noleap2axmlfiledetected', 'admin');
        log_debug($FAILEDUSERS[$username]);
        continue;
    }
261

262 263 264 265
    // If the username is already taken, append something to the end
    while (get_record('usr', 'username', $username)) {
        $username .= "_";
    }
266

267 268 269 270 271 272 273 274
    $user = (object)array(
                          'authinstance'   => $AUTHINSTANCE,
                          'username'       => $username,
                          'firstname'      => 'Imported',
                          'lastname'       => 'User',
                          'password'       => get_random_key(6),
                          'passwordchange' => 1,
                          );
275

276
    db_begin();
277

278 279 280 281 282 283
    try {
        $user->id = create_user($user, array(), $institution, $authobj);
    }
    catch (EmailException $e) {
        // Suppress any emails (e.g. new institution membership) sent out
        // during user creation, becuase the user doesn't have an email
Francois Marier's avatar
Francois Marier committed
284
        // address until we've imported them from the Leap2A file.
285 286
        log_debug("Failed sending email during user import");
    }
287

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
    $importerfilename = substr($leap2afilename, strlen(get_config('dataroot')));
    $logfile          = dirname($leap2afilename) . '/import.log';

    $importer = PluginImport::create_importer(null, (object)array(
        'token'      => '',
        'usr'        => $user->id,
        'queue'      => (int)!(PluginImport::import_immediately_allowed()), // import allowed straight away? Then don't queue
        'ready'      => 0, // maybe 1?
        'expirytime' => db_format_timestamp(time()+(60*60*24)),
        'format'     => 'leap',
        'data'       => array('filename' => $importerfilename),
        'loglevel'   => PluginImportLeap::LOG_LEVEL_VERBOSE,
        'logtargets' => LOG_TARGET_FILE,
        'logfile'    => $logfile,
        'profile'    => true,
    ));

    try {
        $importer->process();
Francois Marier's avatar
Francois Marier committed
307
        log_info("Imported user account $user->id from Leap2A file, see $logfile for a full log");
308 309
    }
    catch (ImportException $e) {
Francois Marier's avatar
Francois Marier committed
310
        log_info("Leap2A import failed: " . $e->getMessage());
311 312 313
        $FAILEDUSERS[$username] = get_string("leap2aimportfailed");
        db_rollback();
        continue;
314 315
    }

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
    db_commit();

    // Reload the user details, as various fields are changed by the
    // importer when importing (e.g. firstname/lastname)
    $ADDEDUSERS[] = get_record('usr', 'id', $user->id);

    $SESSION->set('bulkimport_leap2afiles', $LEAP2AFILES);
    $SESSION->set('bulkimport_addedusers', $ADDEDUSERS);
    $SESSION->set('bulkimport_failedusers', $FAILEDUSERS);

    meta_redirect();
}

function finish_import() {
    global $SESSION, $ADDEDUSERS, $FAILEDUSERS, $EMAILUSERS;

    $totalusers = count($ADDEDUSERS) + count($FAILEDUSERS);
333

334 335 336 337
    log_info('Imported ' . count($ADDEDUSERS) . '/' . $totalusers . ' users successfully');

    if (!empty($ADDEDUSERS)) {
        $SESSION->add_ok_msg(get_string('importednuserssuccessfully', 'admin', count($ADDEDUSERS), $totalusers));
338 339 340 341
    }

    // Only send e-mail to users after we're sure they have been inserted
    // successfully
342 343
    if ($EMAILUSERS && $ADDEDUSERS) {
        foreach ($ADDEDUSERS as $user) {
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
            $noemailusers = array();
            try {
                email_user($user, null, get_string('accountcreated', 'mahara', get_config('sitename')),
                    get_string('accountcreatedchangepasswordtext', 'mahara', $user->firstname, get_config('sitename'), $user->username, $user->password, get_config('wwwroot'), get_config('sitename')),
                    get_string('accountcreatedchangepasswordhtml', 'mahara', $user->firstname, get_config('wwwroot'), get_config('sitename'), $user->username, $user->password, get_config('wwwroot'), get_config('wwwroot'), get_config('sitename'))
                );
            }
            catch (EmailException $e) {
                log_info($e->getMessage());
                $noemailusers[] = $user;
            }
        }

        if ($noemailusers) {
            $message = get_string('uploadcsvsomeuserscouldnotbeemailed', 'admin') . "\n<ul>\n";
            foreach ($noemailusers as $user) {
                $message .= '<li>' . full_name($user) . ' &lt;' . hsc($user->email) . "&gt;</li>\n";
            }
            $message .= "</ul>\n";
            $SESSION->add_info_msg($message, false);
        }
    }

367 368 369
    if (!empty($FAILEDUSERS)) {
        $message = get_string('importfailedfornusers', 'admin', count($FAILEDUSERS), $totalusers) . "\n<ul>\n";
        foreach ($FAILEDUSERS as $username => $error) {
370 371 372 373 374 375
            $message .= '<li>' . hsc($username) . ': ' . hsc($error) . "</li>\n";
        }
        $message .= "</ul>\n";
        $SESSION->add_err_msg($message, false);
    }

376 377 378 379 380 381
    $SESSION->set('bulkimport_leap2afiles', '');
    $SESSION->set('bulkimport_authinstance', '');
    $SESSION->set('bulkimport_emailusers', '');
    $SESSION->set('bulkimport_addedusers', '');
    $SESSION->set('bulkimport_failedusers', '');

382
    redirect(get_config('wwwroot') . '/admin/users/bulkimport.php');
383 384 385 386 387 388 389 390
}

$form = pieform($form);

$smarty = smarty();
$smarty->assign('form', $form);
$smarty->assign('PAGEHEADING', hsc(TITLE));
$smarty->display('admin/users/bulkimport.tpl');