uploadcsv.php 11.7 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
<?php
/**
 * This program is part of Mahara
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * @package    mahara
 * @subpackage admin
 * @author     Nigel McNie <nigel@catalyst.net.nz>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 2006,2007 Catalyst IT Ltd http://catalyst.net.nz
 *
 */

define('INTERNAL', 1);
define('ADMIN', 1);
29
define('MENUITEM', 'configusers');
30
define('SUBMENUITEM', 'uploadcsv');
31
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
Martyn Smith's avatar
Martyn Smith committed
32
define('TITLE', get_string('uploadcsv', 'admin'));
33
require_once('pieforms/pieform.php');
Nigel McNie's avatar
Nigel McNie committed
34
safe_require('artefact', 'internal');
35

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

Nigel McNie's avatar
Nigel McNie committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
$FORMAT = array();
$ALLOWEDKEYS = array(
    'username',
    'password',
    'email',
    'firstname',
    'lastname',
    'preferredname',
    'studentid',
    'introduction',
    'officialwebsite',
    'personalwebsite',
    'blogaddress',
    'address',
    'town',
    'city',
    'country',
    'homenumber',
    'businessnumber',
    'mobilenumber',
    'faxnumber',
    'icqnumber',
    'msnnumber',
    'aimscreenname',
    'yahoochat',
    'skypeusername',
    'jabberusername',
    'occupation',
    'industry',
68
69
    'institution',
    'authinstance'
Nigel McNie's avatar
Nigel McNie committed
70
71
);

72
73
74
75
76
77
78
79
80
81
82
/**
 * TODO: do we want to keep this function? Then it should be in auth/lib.php
 * Given an institution, returns the authentication methods used by it, sorted 
 * by priority.
 *
 * @param  string   $institution     Name of the institution
 * @return array                     Array of auth instance records
 */
function auth_get_auth_instances() {
    static $cache = array();

83
    if (count($cache) > 0) {
84
85
86
87
88
89
        return $cache;
    }

    $sql ='
        SELECT DISTINCT
            i.id,
90
91
            inst.name,
            inst.displayname,
92
93
            i.instancename
        FROM 
94
95
            {institution} inst,
            {auth_instance} i
96
        WHERE 
97
            i.institution = inst.name
98
        ORDER BY
99
            inst.displayname,
100
101
102
103
            i.instancename';

    $cache = get_records_sql_array($sql, array());

104
    if (empty($cache)) {
105
106
107
108
109
110
        return array();
    }

    return $cache;
}

111
112
$authinstances = auth_get_auth_instances();
if (count($authinstances) > 1) {
113
    $options = array();
114
115
116

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

    $authinstanceelement = array(
121
122
        'type' => 'select',
        'title' => get_string('institution'),
123
124
        'description' => get_string('uploadcsvinstitution', 'admin'),
        'options' => $options,
125
        'defaultvalue' => $default
126
127
128
    );
}
else {
129
130
131
132
    foreach ($authinstances as $authinstance) {
        $string = $authinstance->id .'_'. $authinstance->name;
    }

133
    $authinstanceelement = array(
134
        'type' => 'hidden',
135
        'value' => $string
136
137
138
139
140
141
    );
}

$form = array(
    'name' => 'uploadcsv',
    'elements' => array(
142
        'authinstance' => $authinstanceelement,
143
144
145
146
147
148
149
150
151
152
        'file' => array(
            'type' => 'file',
            'title' => get_string('csvfile', 'admin'),
            'description' => get_string('csvfiledescription', 'admin'),
            'rules' => array(
                'required' => true
            )
        ),
        'submit' => array(
            'type' => 'submit',
Nigel McNie's avatar
Nigel McNie committed
153
            'value' => get_string('uploadcsv', 'admin')
154
155
156
157
158
159
160
161
162
        )
    )
);

/**
 * 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>$CVSDATA</var>
 * array so it can be accessed by the submit function
 *
163
164
 * @param Pieform  $form   The form to validate
 * @param array    $values The values submitted
165
 */
166
function uploadcsv_validate(Pieform $form, $values) {
Nigel McNie's avatar
Nigel McNie committed
167
    global $CSVDATA, $ALLOWEDKEYS, $FORMAT;
168
169
170
171
172
173
174

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

    if ($values['file']['size'] == 0) {
175
        $form->set_error('file', $form->i18n('rule', 'required', 'required', array()));
176
177
178
179
180
181
        return;
    }

    require_once('pear/File.php');
    require_once('pear/File/CSV.php');

182
183
184
185
    // Don't be tempted to use 'explode' here. There may be > 1 underscore.
    $break = strpos($values['authinstance'], '_');
    $authinstance = substr($values['authinstance'], 0, $break);
    $institution  = substr($values['authinstance'], $break+1);
186
187
188

    $conf = File_CSV::discoverFormat($values['file']['tmp_name']);
    $i = 0;
189
    while ($line = File_CSV::readQuoted($values['file']['tmp_name'], $conf)) {
190
        $i++;
Nigel McNie's avatar
Nigel McNie committed
191
192
193
194
        if (!is_array($line)) {
            // Note: the CSV parser returns true on some errors and false on
            // others! Yes that's retarded. No I didn't write it :(
            $form->set_error('file', get_string('uploadcsverrorincorrectnumberoffields', 'admin', $i));
195
196
197
            return;
        }

Nigel McNie's avatar
Nigel McNie committed
198
199
        // Get the format of the file
        if ($i == 1) {
200
201
            foreach ($line as &$potentialkey) {
                $potentialkey = trim($potentialkey);
Nigel McNie's avatar
Nigel McNie committed
202
203
204
205
206
                if (!in_array($potentialkey, $ALLOWEDKEYS)) {
                    $form->set_error('file', get_string('uploadcsverrorinvalidfieldname', 'admin', $potentialkey));
                    return;
                }
            }
207

Nigel McNie's avatar
Nigel McNie committed
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
            // Now we know all of the field names are valid, we need to make
            // sure that the required fields are included
            $mandatoryfields = array(
                'username',
                'password'
            );
            $mandatoryfields = array_merge($mandatoryfields, array_keys(ArtefactTypeProfile::get_mandatory_fields()));
            if ($lockedprofilefields = get_column('institution_locked_profile_field', 'profilefield', 'name', $institution)) {
                $mandatoryfields = array_merge($mandatoryfields, $lockedprofilefields);
            }
            
            // Add in the locked profile fields for this institution
            foreach ($mandatoryfields as $field) {
                if (!in_array($field, $line)) {
                    $form->set_error('file', get_string('uploadcsverrorrequiredfieldnotspecified', 'admin', $field));
                    return;
                }
            }

            // The format line is valid
            $FORMAT = $line;
            log_info('FORMAT:');
            log_info($FORMAT);
231
        }
Nigel McNie's avatar
Nigel McNie committed
232
233
234
235
236
237
238
239
240
        else {
            // We have a line with the correct number of fields, but should validate these fields
            // Note: This validation should really be methods on each profile class, that way
            // it can be used in the profile screen as well.

            $formatkeylookup = array_flip($FORMAT);
            $username = $line[$formatkeylookup['username']];
            $password = $line[$formatkeylookup['password']];
            $email    = $line[$formatkeylookup['email']];
241

242
243
244
            $authobj = AuthFactory::create($authinstance);

            if (method_exists($authobj, 'is_username_valid') && !$authobj->is_username_valid($username)) {
Nigel McNie's avatar
Nigel McNie committed
245
246
247
                $form->set_error('file', get_string('uploadcsverrorinvalidusername', 'admin', $i));
                return;
            }
248
            if (record_exists('usr', 'username', $username, 'authinstance', $authinstance)) {
Nigel McNie's avatar
Nigel McNie committed
249
                $form->set_error('file', get_string('uploadcsverroruseralreadyexists', 'admin', $i, $username));
250
251
                return;
            }
252
253
254
            if (record_exists('usr', 'email', $email)) {
                $form->set_error('file', get_string('uploadcsverroremailaddresstaken', 'admin', $i, $email));
            }
255

Nigel McNie's avatar
Nigel McNie committed
256
257
258
            // Note: only checks for valid form are done here, none of the checks
            // like whether the password is too easy. The user is going to have to
            // change their password on first login anyway.
259
            if (method_exists($authobj, 'is_password_valid') && !$authobj->is_password_valid($password)) {
Nigel McNie's avatar
Nigel McNie committed
260
261
262
263
264
265
                $form->set_error('file', get_string('uploadcsverrorinvalidpassword', 'admin', $i));
                return;
            }

            // All OK!
            $CSVDATA[] = $line;
266
267
        }

Nigel McNie's avatar
Nigel McNie committed
268
269
270
271
272
273
    }

    if ($i == 1) {
        // There was only the title row :(
        $form->set_error('file', get_string('uploadcsverrornorecords', 'admin'));
        return;
274
    }
275
276
277
278
279

    if ($CSVDATA === null) {
        // Oops! Couldn't get CSV data for some reason
        $form->set_error('file', get_string('uploadcsverrorunspecifiedproblem', 'admin'));
    }
280
281
282
283
284
285
}

/**
 * Add the users to the system. Make sure that they have to change their
 * password on next login also.
 */
286
function uploadcsv_submit(Pieform $form, $values) {
Nigel McNie's avatar
Nigel McNie committed
287
    global $SESSION, $CSVDATA, $FORMAT;
288
    log_info('Inserting users from the CSV file');
289
    db_begin();
Nigel McNie's avatar
Nigel McNie committed
290
    $formatkeylookup = array_flip($FORMAT);
291
292
293
294
295
296

    // Don't be tempted to use 'explode' here. There may be > 1 underscore.
    $break = strpos($values['authinstance'], '_');
    $authinstance = substr($values['authinstance'], 0, $break);
    $institution  = substr($values['authinstance'], $break+1);

297
    foreach ($CSVDATA as $record) {
Nigel McNie's avatar
Nigel McNie committed
298
        log_debug('adding user ' . $record[$formatkeylookup['username']]);
299
        $user = new StdClass;
300
301
302
303
304
305
        $user->institution  = $institution;
        $user->authinstance = $authinstance;
        $user->username     = $record[$formatkeylookup['username']];
        $user->password     = $record[$formatkeylookup['password']];
        $user->email        = $record[$formatkeylookup['email']];

Nigel McNie's avatar
Nigel McNie committed
306
307
308
309
310
311
        if (isset($formatkeylookup['studentid'])) {
            $user->studentid = $record[$formatkeylookup['studentid']];
        }
        if (isset($formatkeylookup['preferredname'])) {
            $user->preferredname = $record[$formatkeylookup['preferredname']];
        }
312
313
        $user->passwordchange = 1;
        $id = insert_record('usr', $user, 'id', true);
314
        $user->id = $id;
315

Nigel McNie's avatar
Nigel McNie committed
316
317
318
319
320
        foreach ($FORMAT as $field) {
            if ($field == 'username' || $field == 'password') {
                continue;
            }
            set_profile_field($id, $field, $record[$formatkeylookup[$field]]);
321
        }
Nigel McNie's avatar
Nigel McNie committed
322
323

        handle_event('createuser', $user);
324
    }
325
    db_commit();
326
327
    log_info('Inserted ' . count($CSVDATA) . ' records');

328
    $SESSION->add_ok_msg(get_string('uploadcsvusersaddedsuccessfully', 'admin'));
329
    redirect('/admin/users/uploadcsv.php');
330
331
}

Nigel McNie's avatar
Nigel McNie committed
332
333
334
335
336
337
338
339
340
341
342
// Get a list of all profile fields, to inform the user on what fields they can
// put in their file.
$fields = "<ul>\n";
foreach (array_keys(ArtefactTypeProfile::get_all_fields()) as $type) {
    if ($type == 'firstname' || $type == 'lastname' || $type == 'email') {
        continue;
    }
    $fields .= '<li>' . hsc($type) . "</li>\n";
}
$fields .= "</ul>\n";

343
$smarty = smarty();
Nigel McNie's avatar
Nigel McNie committed
344
345
346
347
348
$smarty->assign('uploadcsvpagedescription', get_string('uploadcsvpagedescription', 'admin',
    get_config('wwwroot') . 'admin/extensions/pluginconfig.php?plugintype=artefact&pluginname=internal&type=profile',
    get_config('wwwroot') . 'admin/users/institutions.php',
    $fields
));
349
$smarty->assign('uploadcsvform', pieform($form));
350
$smarty->display('admin/users/uploadcsv.tpl');
351
352

?>