adding current groupid to grade_export class - soon to be used in plugins
[moodle-pu.git] / admin / uploaduser.php
blob5ec3dcf66614275411bf5011b94774517943dc4d
1 <?php
3 /// Bulk user registration script from a comma separated file
4 /// Returns list of users with their user ids
6 require_once('../config.php');
7 require_once($CFG->libdir.'/adminlib.php');
8 require_once($CFG->libdir.'/textlib.class.php');
9 require_once('uploaduser_form.php');
10 define('LINE_MAX_SIZE', 1024);
12 //Note: commas within a field should be encoded as &#44 (for comma separated csv files)
13 //Note: semicolon within a field should be encoded as &#59 (for semicolon separated csv files)
14 $csv_delimiter = isset($CFG->CSV_DELIMITER) ? $CFG->CSV_DELIMITER : ',';
15 $csv_encode = '&#' . (isset($CFG->CSV_ENCODE) ? $CFG->CSV_ENCODE : ord($csv_delimiter));
17 @set_time_limit(0);
18 @raise_memory_limit('192M');
20 admin_externalpage_setup('uploadusers');
21 require_capability('moodle/site:uploadusers', get_context_instance(CONTEXT_SYSTEM));
23 if (! $site = get_site()) {
24 error('Could not find site-level course');
27 $textlib =& textlib_get_instance();
29 $struserrenamed = get_string('userrenamed', 'admin');
30 $strusernotrenamedexists = get_string('usernotrenamedexists', 'error');
31 $strusernotrenamedmissing = get_string('usernotrenamedmissing', 'error');
33 $struserupdated = get_string('useraccountupdated', 'admin');
34 $strusernotupdated = get_string('usernotupdatederror', 'error');
36 $struseradded = get_string('newuser');
37 $strusernotadded = get_string('usernotaddedregistered', 'error');
38 $strusernotaddederror = get_string('usernotaddederror', 'error');
40 $struserdeleted = get_string('userdeleted', 'admin');
41 $strusernotdeletederror = get_string('usernotdeletederror', 'error');
42 $strusernotdeletedmissing = get_string('usernotdeletedmissing', 'error');
44 $strcannotassignrole = get_string('cannotassignrole', 'error');
45 $strduplicateusername = get_string('duplicateusername', 'error');
46 $strindent = '-->';
48 $mform = new admin_uploaduser_form();
50 // Print the header
52 admin_externalpage_print_header();
54 // If a file has been uploaded, then process it
55 if ( $formdata = $mform->get_data() ) {
56 $createpassword = $formdata->createpassword;
57 $updateaccounts = $formdata->updateaccounts;
58 $allowrenames = $formdata->allowrenames;
59 $skipduplicates = $formdata->duplicatehandling;
61 // make arrays of valid fields for error checking
62 // the value associated to each field is: 0 = optional field, 1 = field required either in default values or in data file
63 $fields = array(
64 'firstname' => 1,
65 'lastname' => 1,
66 'username' => 1,
67 'email' => 1,
68 'city' => 1,
69 'country' => 1,
70 'lang' => 1,
71 'auth' => 1,
72 'timezone' => 1,
73 'mailformat' => 1,
74 'maildisplay' => 1,
75 'htmleditor' => 1,
76 'autosubscribe' => 1,
77 'mnethostid' => 0,
78 'institution' => 0,
79 'department' => 0,
80 'idnumber' => 0,
81 'icq' => 0,
82 'phone1' => 0,
83 'phone2' => 0,
84 'address' => 0,
85 'url' => 0,
86 'description' => 0,
87 'icq' => 0,
88 'oldusername' => 0,
89 'emailstop' => 1,
90 'deleted' => 0,
91 'password' => !$createpassword,
94 $fp = fopen($mform->get_userfile_name(), 'r');
95 $linenum = 1; // since header is line 1
96 // get header (field names) and remove Unicode BOM from first line, if any
97 $line = explode($csv_delimiter, $textlib->trim_utf8_bom(fgets($fp,LINE_MAX_SIZE)));
98 // check for valid field names
99 $headers = array();
100 foreach ($line as $key => $value) {
101 $value = trim($value); // remove whitespace
102 if (!in_array($value, $fields) && // if not a standard field and not an enrolment field, then we have an error
103 !preg_match('/^course\d+$/', $value) && !preg_match('/^group\d+$/', $value) &&
104 !preg_match('/^type\d+$/', $value) && !preg_match('/^role\d+$/', $value)) {
105 error(get_string('invalidfieldname', 'error', $value), 'uploaduser.php?sesskey='.$USER->sesskey);
107 $headers[$key] = $value;
110 // check that required fields are present or a default value for them exists
111 $headersOk = true;
112 // disable the check if we also have deleting information (ie. deleted column)
113 if (!in_array('deleted', $headers)) {
114 foreach ($fields as $key => $required) {
115 if($required && !in_array($key, $headers) && (!isset($formdata->$key) || $formdata->$key==='')) {
116 notify(get_string('missingfield', 'error', $key));
117 $headersOk = false;
121 if($headersOk) {
122 $usersnew = 0;
123 $usersupdated = 0;
124 $userserrors = 0;
125 $usersdeleted = 0;
126 $renames = 0;
127 $renameerrors = 0;
128 $deleteerrors = 0;
129 $newusernames = array();
130 // We'll need courses a lot, so fetch it early and keep it in memory, indexed by their shortname
131 $tmp =& get_courses('all','','id,shortname,visible');
132 $courses = array();
133 foreach ($tmp as $c) {
134 $courses[$c->shortname] = $c;
136 unset($tmp);
138 echo '<p id="results">';
139 while (!feof ($fp)) {
140 $errors = '';
141 $user = new object();
142 // by default, use the local mnet id (this may be changed in the file)
143 $user->mnethostid = $CFG->mnet_localhost_id;
144 $line = explode($csv_delimiter, fgets($fp,LINE_MAX_SIZE));
145 ++$linenum;
146 // add fields to user object
147 foreach ($line as $key => $value) {
148 if($value !== '') {
149 $key = $headers[$key];
150 //decode encoded commas
151 $value = str_replace($csv_encode,$csv_delimiter,trim($value));
152 // special fields: password and username
153 if ($key == 'password' && !empty($value)) {
154 $user->$key = hash_internal_user_password($value);
155 } else if($key == 'username') {
156 $value = $textlib->strtolower(addslashes($value));
157 if(empty($CFG->extendedusernamechars)) {
158 $value = eregi_replace('[^(-\.[:alnum:])]', '', $value);
160 @$newusernames[$value]++;
161 $user->$key = $value;
162 } else {
163 $user->$key = addslashes($value);
168 // add default values for remaining fields
169 foreach ($fields as $key => $required) {
170 if(isset($user->$key)) {
171 continue;
173 if(!isset($formdata->$key) || $formdata->$key==='') { // no default value was submited
174 // if the field is required, give an error only if we are adding the user or deleting a user with unkown username
175 if($required && (empty($user->deleted) || $key == 'username')) {
176 $errors .= get_string('missingfield', 'error', $key) . ' ';
178 continue;
180 // process templates
181 $template = $formdata->$key;
182 $templatelen = strlen($template);
183 $value = '';
184 for ($i = 0 ; $i < $templatelen; ++$i) {
185 if($template[$i] == '%') {
186 $case = 0; // 1=lowercase, 2=uppercase
187 $len = 0; // number of characters to keep
188 $info = null; // data to process
189 for($j = $i + 1; is_null($info) && $j < $templatelen; ++$j) {
190 $car = $template[$j];
191 if ($car >= '0' && $car <= '9') {
192 $len = $len * 10 + (int)$car;
193 } else if($car == '-') {
194 $case = 1;
195 } else if($car == '+') {
196 $case = 2;
197 } else if($car == 'f') { // first name
198 $info = @$user->firstname;
199 } else if($car == 'l') { // last name
200 $info = @$user->lastname;
201 } else if($car == 'u') { // username
202 $info = @$user->username;
203 } else if($car == '%' && $j == $i+1) {
204 $info = '%';
205 } else { // invalid character
206 $info = '';
209 if($info==='' || is_null($info)) { // invalid template
210 continue;
212 $i = $j - 1;
213 // change case
214 if($case == 1) {
215 $info = $textlib->strtolower($info);
216 } else if($case == 2) {
217 $info = $textlib->strtoupper($info);
219 if($len) { // truncate data
220 $info = $textlib->substr($info, 0, $len);
222 $value .= $info;
223 } else {
224 $value .= $template[$i];
228 if($key == 'username') {
229 $value = $textlib->strtolower($value);
230 if(empty($CFG->extendedusernamechars)) {
231 $value = eregi_replace('[^(-\.[:alnum:])]', '', $value);
233 @$newusernames[$value]++;
234 // check for new username duplicates
235 if($newusernames[$value] > 1) {
236 if($skipduplicates) {
237 $errors .= $strduplicateusername . ' (' . stripslashes($value) . '). ';
238 continue;
239 } else {
240 $value .= $newusernames[$value];
244 $user->$key = $value;
246 if($errors) {
247 notify(get_string('erroronline', 'error', $linenum). ': ' . $errors);
248 ++$userserrors;
249 continue;
252 // delete user
253 if(@$user->deleted) {
254 $info = ': ' . stripslashes($user->username) . '. ';
255 if($user =& get_record('user', 'username', $user->username, 'mnethostid', $user->mnethostid)) {
256 $user->timemodified = time();
257 $user->username = addslashes($user->email . $user->timemodified); // Remember it just in case
258 $user->deleted = 1;
259 $user->email = ''; // Clear this field to free it up
260 $user->idnumber = ''; // Clear this field to free it up
261 if (update_record('user', $user)) {
262 // not sure if this is needed. unenrol_student($user->id); // From all courses
263 delete_records('role_assignments', 'userid', $user->id); // unassign all roles
264 // remove all context assigned on this user?
265 echo $struserdeleted . $info . '<br />';
266 ++$usersdeleted;
267 } else {
268 notify(get_string('erroronline', 'error', $linenum). ': ' . $strusernotdeletederror . $info);
269 ++$deleteerrors;
271 } else {
272 notify(get_string('erroronline', 'error', $linenum). ': ' . $strusernotdeletedmissing . $info);
273 ++$deleteerrors;
275 continue;
278 // save the user to the database
279 $user->confirmed = 1;
280 $user->timemodified = time();
282 // before insert/update, check whether we should be updating an old record instead
283 if ($allowrenames && !empty($user->oldusername) ) {
284 $user->oldusername = $textlib->strtolower($user->oldusername);
285 $info = ': ' . stripslashes($user->oldusername) . '-->' . stripslashes($user->username) . '. ';
286 if ($olduser =& get_record('user', 'username', $user->oldusername, 'mnethostid', $user->mnethostid)) {
287 if (set_field('user', 'username', $user->username, 'id', $olduser->id)) {
288 echo $struserrenamed . $info;
289 $renames++;
290 } else {
291 notify(get_string('erroronline', 'error', $linenum). ': ' . $strusernotrenamedexists . $info);
292 $renameerrors++;
293 continue;
295 } else {
296 notify(get_string('erroronline', 'error', $linenum). ': ' . $strusernotrenamedmissing . $info);
297 $renameerrors++;
298 continue;
302 // save the information
303 if ($olduser =& get_record('user', 'username', $user->username, 'mnethostid', $user->mnethostid)) {
304 $user->id = $olduser->id;
305 $info = ': ' . stripslashes($user->username) .' (ID = ' . $user->id . ')';
306 if ($updateaccounts) {
307 // Record is being updated
308 if (update_record('user', $user)) {
309 echo $struserupdated . $info . '<br />';
310 $usersupdated++;
311 } else {
312 notify(get_string('erroronline', 'error', $linenum). ': ' . $strusernotupdated . $info);
313 $userserrors++;
314 continue;
316 } else {
317 //Record not added - user is already registered
318 //In this case, output userid from previous registration
319 //This can be used to obtain a list of userids for existing users
320 echo $strusernotadded . $info . '<br />';
321 $userserrors++;
323 } else { // new user
324 if ($user->id = insert_record('user', $user)) {
325 $info = ': ' . stripslashes($user->username) .' (ID = ' . $user->id . ')';
326 echo $struseradded . $info . '<br />';
327 $usersnew++;
328 if (empty($user->password) && $createpassword) {
329 // passwords will be created and sent out on cron
330 insert_record('user_preferences', array( 'userid' => $user->id, 'name' => 'create_password', 'value' => 1));
331 insert_record('user_preferences', array( 'userid' => $user->id, 'name' => 'auth_forcepasswordchange', 'value' => 1));
333 } else {
334 // Record not added -- possibly some other error
335 notify(get_string('erroronline', 'error', $linenum). ': ' . $strusernotaddederror . ': ' . stripslashes($user->username));
336 $userserrors++;
337 continue;
341 // find course enrolments, groups and roles/types
342 for($ncourses = 1; $addcourse = @$user->{'course' . $ncourses}; ++$ncourses) {
343 // find course
344 if(!$course = @$courses[$addcourse]) {
345 notify(get_string('erroronline', 'error', $linenum). ': ' . get_string('unknowncourse', 'error', $addcourse));
346 continue;
348 // find role
349 if ($addrole = @$user->{'role' . $ncourses}) {
350 $coursecontext =& get_context_instance(CONTEXT_COURSE, $course->id);
351 if (!$ok = role_assign($addrole, $user->id, 0, $coursecontext->id)) {
352 echo $strindent . $strcannotassignrole . '<br >';
354 } else {
355 // if no role, then find "old" enrolment type
356 switch ($addtype = @$user->{'type' . $ncourses}) {
357 case 2: // teacher
358 $ok = add_teacher($user->id, $course->id, 1);
359 break;
360 case 3: // non-editing teacher
361 $ok = add_teacher($user->id, $course->id, 0);
362 break;
363 case 1: // student
364 default:
365 $ok = enrol_student($user->id, $course->id);
366 break;
369 if ($ok) { // OK
370 echo $strindent . get_string('enrolledincourse', '', $addcourse) . '<br />';
371 } else {
372 notify(get_string('erroronline', 'error', $linenum). ': ' . get_string('enrolledincoursenot', '', $addcourse));
375 // find group to add to
376 if ($addgroup = @$user->{'group' . $ncourses}) {
377 if ($gid =& groups_get_group_by_name($course->id, $addgroup)) {
378 $coursecontext =& get_context_instance(CONTEXT_COURSE, $course->id);
379 if (count(get_user_roles($coursecontext, $user->id))) {
380 if (groups_add_member($gid, $user->id)) {
381 echo $strindent . get_string('addedtogroup','',$addgroup) . '<br />';
382 } else {
383 notify(get_string('erroronline', 'error', $linenum). ': ' . get_string('addedtogroupnot','',$addgroup));
385 } else {
386 notify(get_string('erroronline', 'error', $linenum). ': ' . get_string('addedtogroupnotenrolled','',$addgroup));
388 } else {
389 notify(get_string('erroronline', 'error', $linenum). ': ' . get_string('groupunknown','error',$addgroup));
394 echo '</p>';
395 notify(get_string('userscreated', 'admin') . ': ' . $usersnew);
396 notify(get_string('usersupdated', 'admin') . ': ' . $usersupdated);
397 notify(get_string('usersdeleted', 'admin') . ': ' . $usersdeleted);
398 notify(get_string('deleteerrors', 'admin') . ': ' . $deleteerrors);
399 if ($allowrenames) {
400 notify(get_string('usersrenamed', 'admin') . ': ' . $renames);
401 notify(get_string('renameerrors', 'admin') . ': ' . $renameerrors);
403 notify(get_string('errors', 'admin') . ': ' . $userserrors);
405 fclose($fp);
406 echo '<hr />';
409 /// Print the form
410 print_heading_with_help(get_string('uploadusers'), 'uploadusers2');
411 $mform->display();
412 admin_externalpage_print_footer();