MDL-62599 mod_lti: fix coding style issues
[moodle.git] / admin / tool / uploaduser / index.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Bulk user registration script from a comma separated file
19  *
20  * @package    tool
21  * @subpackage uploaduser
22  * @copyright  2004 onwards Martin Dougiamas (http://dougiamas.com)
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 require('../../../config.php');
27 require_once($CFG->libdir.'/adminlib.php');
28 require_once($CFG->libdir.'/csvlib.class.php');
29 require_once($CFG->dirroot.'/user/profile/lib.php');
30 require_once($CFG->dirroot.'/user/lib.php');
31 require_once($CFG->dirroot.'/group/lib.php');
32 require_once($CFG->dirroot.'/cohort/lib.php');
33 require_once('locallib.php');
34 require_once('user_form.php');
36 $iid         = optional_param('iid', '', PARAM_INT);
37 $previewrows = optional_param('previewrows', 10, PARAM_INT);
39 core_php_time_limit::raise(60*60); // 1 hour should be enough
40 raise_memory_limit(MEMORY_HUGE);
42 admin_externalpage_setup('tooluploaduser');
43 require_capability('moodle/site:uploadusers', context_system::instance());
45 $struserrenamed             = get_string('userrenamed', 'tool_uploaduser');
46 $strusernotrenamedexists    = get_string('usernotrenamedexists', 'error');
47 $strusernotrenamedmissing   = get_string('usernotrenamedmissing', 'error');
48 $strusernotrenamedoff       = get_string('usernotrenamedoff', 'error');
49 $strusernotrenamedadmin     = get_string('usernotrenamedadmin', 'error');
51 $struserupdated             = get_string('useraccountupdated', 'tool_uploaduser');
52 $strusernotupdated          = get_string('usernotupdatederror', 'error');
53 $strusernotupdatednotexists = get_string('usernotupdatednotexists', 'error');
54 $strusernotupdatedadmin     = get_string('usernotupdatedadmin', 'error');
56 $struseruptodate            = get_string('useraccountuptodate', 'tool_uploaduser');
58 $struseradded               = get_string('newuser');
59 $strusernotadded            = get_string('usernotaddedregistered', 'error');
60 $strusernotaddederror       = get_string('usernotaddederror', 'error');
62 $struserdeleted             = get_string('userdeleted', 'tool_uploaduser');
63 $strusernotdeletederror     = get_string('usernotdeletederror', 'error');
64 $strusernotdeletedmissing   = get_string('usernotdeletedmissing', 'error');
65 $strusernotdeletedoff       = get_string('usernotdeletedoff', 'error');
66 $strusernotdeletedadmin     = get_string('usernotdeletedadmin', 'error');
68 $strcannotassignrole        = get_string('cannotassignrole', 'error');
70 $struserauthunsupported     = get_string('userauthunsupported', 'error');
71 $stremailduplicate          = get_string('useremailduplicate', 'error');
73 $strinvalidpasswordpolicy   = get_string('invalidpasswordpolicy', 'error');
74 $errorstr                   = get_string('error');
76 $stryes                     = get_string('yes');
77 $strno                      = get_string('no');
78 $stryesnooptions = array(0=>$strno, 1=>$stryes);
80 $returnurl = new moodle_url('/admin/tool/uploaduser/index.php');
81 $bulknurl  = new moodle_url('/admin/user/user_bulk.php');
83 $today = time();
84 $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
86 // array of all valid fields for validation
87 $STD_FIELDS = array('id', 'username', 'email',
88         'city', 'country', 'lang', 'timezone', 'mailformat',
89         'maildisplay', 'maildigest', 'htmleditor', 'autosubscribe',
90         'institution', 'department', 'idnumber', 'skype',
91         'msn', 'aim', 'yahoo', 'icq', 'phone1', 'phone2', 'address',
92         'url', 'description', 'descriptionformat', 'password',
93         'auth',        // watch out when changing auth type or using external auth plugins!
94         'oldusername', // use when renaming users - this is the original username
95         'suspended',   // 1 means suspend user account, 0 means activate user account, nothing means keep as is for existing users
96         'deleted',     // 1 means delete user
97         'mnethostid',  // Can not be used for adding, updating or deleting of users - only for enrolments, groups, cohorts and suspending.
98         'interests',
99     );
100 // Include all name fields.
101 $STD_FIELDS = array_merge($STD_FIELDS, get_all_user_name_fields());
103 $PRF_FIELDS = array();
105 if ($proffields = $DB->get_records('user_info_field')) {
106     foreach ($proffields as $key => $proffield) {
107         $profilefieldname = 'profile_field_'.$proffield->shortname;
108         $PRF_FIELDS[] = $profilefieldname;
109         // Re-index $proffields with key as shortname. This will be
110         // used while checking if profile data is key and needs to be converted (eg. menu profile field)
111         $proffields[$profilefieldname] = $proffield;
112         unset($proffields[$key]);
113     }
116 if (empty($iid)) {
117     $mform1 = new admin_uploaduser_form1();
119     if ($formdata = $mform1->get_data()) {
120         $iid = csv_import_reader::get_new_iid('uploaduser');
121         $cir = new csv_import_reader($iid, 'uploaduser');
123         $content = $mform1->get_file_content('userfile');
125         $readcount = $cir->load_csv_content($content, $formdata->encoding, $formdata->delimiter_name);
126         $csvloaderror = $cir->get_error();
127         unset($content);
129         if (!is_null($csvloaderror)) {
130             print_error('csvloaderror', '', $returnurl, $csvloaderror);
131         }
132         // test if columns ok
133         $filecolumns = uu_validate_user_upload_columns($cir, $STD_FIELDS, $PRF_FIELDS, $returnurl);
134         // continue to form2
136     } else {
137         echo $OUTPUT->header();
139         echo $OUTPUT->heading_with_help(get_string('uploadusers', 'tool_uploaduser'), 'uploadusers', 'tool_uploaduser');
141         $mform1->display();
142         echo $OUTPUT->footer();
143         die;
144     }
145 } else {
146     $cir = new csv_import_reader($iid, 'uploaduser');
147     $filecolumns = uu_validate_user_upload_columns($cir, $STD_FIELDS, $PRF_FIELDS, $returnurl);
150 $mform2 = new admin_uploaduser_form2(null, array('columns'=>$filecolumns, 'data'=>array('iid'=>$iid, 'previewrows'=>$previewrows)));
152 // If a file has been uploaded, then process it
153 if ($formdata = $mform2->is_cancelled()) {
154     $cir->cleanup(true);
155     redirect($returnurl);
157 } else if ($formdata = $mform2->get_data()) {
158     // Print the header
159     echo $OUTPUT->header();
160     echo $OUTPUT->heading(get_string('uploadusersresult', 'tool_uploaduser'));
162     $optype = $formdata->uutype;
164     $updatetype        = isset($formdata->uuupdatetype) ? $formdata->uuupdatetype : 0;
165     $createpasswords   = (!empty($formdata->uupasswordnew) and $optype != UU_USER_UPDATE);
166     $updatepasswords   = (!empty($formdata->uupasswordold)  and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC and ($updatetype == UU_UPDATE_FILEOVERRIDE or $updatetype == UU_UPDATE_ALLOVERRIDE));
167     $allowrenames      = (!empty($formdata->uuallowrenames) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC);
168     $allowdeletes      = (!empty($formdata->uuallowdeletes) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC);
169     $allowsuspends     = (!empty($formdata->uuallowsuspends));
170     $bulk              = $formdata->uubulk;
171     $noemailduplicates = empty($CFG->allowaccountssameemail) ? 1 : $formdata->uunoemailduplicates;
172     $standardusernames = $formdata->uustandardusernames;
173     $resetpasswords    = isset($formdata->uuforcepasswordchange) ? $formdata->uuforcepasswordchange : UU_PWRESET_NONE;
175     // verification moved to two places: after upload and into form2
176     $usersnew      = 0;
177     $usersupdated  = 0;
178     $usersuptodate = 0; //not printed yet anywhere
179     $userserrors   = 0;
180     $deletes       = 0;
181     $deleteerrors  = 0;
182     $renames       = 0;
183     $renameerrors  = 0;
184     $usersskipped  = 0;
185     $weakpasswords = 0;
187     // caches
188     $ccache         = array(); // course cache - do not fetch all courses here, we  will not probably use them all anyway!
189     $cohorts        = array();
190     $rolecache      = uu_allowed_roles_cache(); // Course roles lookup cache.
191     $sysrolecache   = uu_allowed_sysroles_cache(); // System roles lookup cache.
192     $manualcache    = array(); // cache of used manual enrol plugins in each course
193     $supportedauths = uu_supported_auths(); // officially supported plugins that are enabled
195     // we use only manual enrol plugin here, if it is disabled no enrol is done
196     if (enrol_is_enabled('manual')) {
197         $manual = enrol_get_plugin('manual');
198     } else {
199         $manual = NULL;
200     }
202     // clear bulk selection
203     if ($bulk) {
204         $SESSION->bulk_users = array();
205     }
207     // init csv import helper
208     $cir->init();
209     $linenum = 1; //column header is first line
211     // init upload progress tracker
212     $upt = new uu_progress_tracker();
213     $upt->start(); // start table
214     $validation = array();
215     while ($line = $cir->next()) {
216         $upt->flush();
217         $linenum++;
219         $upt->track('line', $linenum);
221         $user = new stdClass();
223         // add fields to user object
224         foreach ($line as $keynum => $value) {
225             if (!isset($filecolumns[$keynum])) {
226                 // this should not happen
227                 continue;
228             }
229             $key = $filecolumns[$keynum];
230             if (strpos($key, 'profile_field_') === 0) {
231                 //NOTE: bloody mega hack alert!!
232                 if (isset($USER->$key) and is_array($USER->$key)) {
233                     // this must be some hacky field that is abusing arrays to store content and format
234                     $user->$key = array();
235                     $user->{$key['text']}   = $value;
236                     $user->{$key['format']} = FORMAT_MOODLE;
237                 } else {
238                     $user->$key = trim($value);
239                 }
240             } else {
241                 $user->$key = trim($value);
242             }
244             if (in_array($key, $upt->columns)) {
245                 // default value in progress tracking table, can be changed later
246                 $upt->track($key, s($value), 'normal');
247             }
248         }
249         if (!isset($user->username)) {
250             // prevent warnings below
251             $user->username = '';
252         }
254         if ($optype == UU_USER_ADDNEW or $optype == UU_USER_ADDINC) {
255             // user creation is a special case - the username may be constructed from templates using firstname and lastname
256             // better never try this in mixed update types
257             $error = false;
258             if (!isset($user->firstname) or $user->firstname === '') {
259                 $upt->track('status', get_string('missingfield', 'error', 'firstname'), 'error');
260                 $upt->track('firstname', $errorstr, 'error');
261                 $error = true;
262             }
263             if (!isset($user->lastname) or $user->lastname === '') {
264                 $upt->track('status', get_string('missingfield', 'error', 'lastname'), 'error');
265                 $upt->track('lastname', $errorstr, 'error');
266                 $error = true;
267             }
268             if ($error) {
269                 $userserrors++;
270                 continue;
271             }
272             // we require username too - we might use template for it though
273             if (empty($user->username) and !empty($formdata->username)) {
274                 $user->username = uu_process_template($formdata->username, $user);
275                 $upt->track('username', s($user->username));
276             }
277         }
279         // normalize username
280         $originalusername = $user->username;
281         if ($standardusernames) {
282             $user->username = core_user::clean_field($user->username, 'username');
283         }
285         // make sure we really have username
286         if (empty($user->username)) {
287             $upt->track('status', get_string('missingfield', 'error', 'username'), 'error');
288             $upt->track('username', $errorstr, 'error');
289             $userserrors++;
290             continue;
291         } else if ($user->username === 'guest') {
292             $upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error');
293             $userserrors++;
294             continue;
295         }
297         if ($user->username !== core_user::clean_field($user->username, 'username')) {
298             $upt->track('status', get_string('invalidusername', 'error', 'username'), 'error');
299             $upt->track('username', $errorstr, 'error');
300             $userserrors++;
301         }
303         if (empty($user->mnethostid)) {
304             $user->mnethostid = $CFG->mnet_localhost_id;
305         }
307         if ($existinguser = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$user->mnethostid))) {
308             $upt->track('id', $existinguser->id, 'normal', false);
309         }
311         if ($user->mnethostid == $CFG->mnet_localhost_id) {
312             $remoteuser = false;
314             // Find out if username incrementing required.
315             if ($existinguser and $optype == UU_USER_ADDINC) {
316                 $user->username = uu_increment_username($user->username);
317                 $existinguser = false;
318             }
320         } else {
321             if (!$existinguser or $optype == UU_USER_ADDINC) {
322                 $upt->track('status', get_string('errormnetadd', 'tool_uploaduser'), 'error');
323                 $userserrors++;
324                 continue;
325             }
327             $remoteuser = true;
329             // Make sure there are no changes of existing fields except the suspended status.
330             foreach ((array)$existinguser as $k => $v) {
331                 if ($k === 'suspended') {
332                     continue;
333                 }
334                 if (property_exists($user, $k)) {
335                     $user->$k = $v;
336                 }
337                 if (in_array($k, $upt->columns)) {
338                     if ($k === 'password' or $k === 'oldusername' or $k === 'deleted') {
339                         $upt->track($k, '', 'normal', false);
340                     } else {
341                         $upt->track($k, s($v), 'normal', false);
342                     }
343                 }
344             }
345             unset($user->oldusername);
346             unset($user->password);
347             $user->auth = $existinguser->auth;
348         }
350         // notify about nay username changes
351         if ($originalusername !== $user->username) {
352             $upt->track('username', '', 'normal', false); // clear previous
353             $upt->track('username', s($originalusername).'-->'.s($user->username), 'info');
354         } else {
355             $upt->track('username', s($user->username), 'normal', false);
356         }
358         // add default values for remaining fields
359         $formdefaults = array();
360         if (!$existinguser || ($updatetype != UU_UPDATE_FILEOVERRIDE && $updatetype != UU_UPDATE_NOCHANGES)) {
361             foreach ($STD_FIELDS as $field) {
362                 if (isset($user->$field)) {
363                     continue;
364                 }
365                 // all validation moved to form2
366                 if (isset($formdata->$field)) {
367                     // process templates
368                     $user->$field = uu_process_template($formdata->$field, $user);
369                     $formdefaults[$field] = true;
370                     if (in_array($field, $upt->columns)) {
371                         $upt->track($field, s($user->$field), 'normal');
372                     }
373                 }
374             }
375             foreach ($PRF_FIELDS as $field) {
376                 if (isset($user->$field)) {
377                     continue;
378                 }
379                 if (isset($formdata->$field)) {
380                     // process templates
381                     $user->$field = uu_process_template($formdata->$field, $user);
383                     // Form contains key and later code expects value.
384                     // Convert key to value for required profile fields.
385                     require_once($CFG->dirroot.'/user/profile/field/'.$proffields[$field]->datatype.'/field.class.php');
386                     $profilefieldclass = 'profile_field_'.$proffields[$field]->datatype;
387                     $profilefield = new $profilefieldclass($proffields[$field]->id);
388                     if (method_exists($profilefield, 'convert_external_data')) {
389                         $user->$field = $profilefield->edit_save_data_preprocess($user->$field, null);
390                     }
392                     $formdefaults[$field] = true;
393                 }
394             }
395         }
397         // delete user
398         if (!empty($user->deleted)) {
399             if (!$allowdeletes or $remoteuser) {
400                 $usersskipped++;
401                 $upt->track('status', $strusernotdeletedoff, 'warning');
402                 continue;
403             }
404             if ($existinguser) {
405                 if (is_siteadmin($existinguser->id)) {
406                     $upt->track('status', $strusernotdeletedadmin, 'error');
407                     $deleteerrors++;
408                     continue;
409                 }
410                 if (delete_user($existinguser)) {
411                     $upt->track('status', $struserdeleted);
412                     $deletes++;
413                 } else {
414                     $upt->track('status', $strusernotdeletederror, 'error');
415                     $deleteerrors++;
416                 }
417             } else {
418                 $upt->track('status', $strusernotdeletedmissing, 'error');
419                 $deleteerrors++;
420             }
421             continue;
422         }
423         // we do not need the deleted flag anymore
424         unset($user->deleted);
426         // renaming requested?
427         if (!empty($user->oldusername) ) {
428             if (!$allowrenames) {
429                 $usersskipped++;
430                 $upt->track('status', $strusernotrenamedoff, 'warning');
431                 continue;
432             }
434             if ($existinguser) {
435                 $upt->track('status', $strusernotrenamedexists, 'error');
436                 $renameerrors++;
437                 continue;
438             }
440             if ($user->username === 'guest') {
441                 $upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error');
442                 $renameerrors++;
443                 continue;
444             }
446             if ($standardusernames) {
447                 $oldusername = core_user::clean_field($user->oldusername, 'username');
448             } else {
449                 $oldusername = $user->oldusername;
450             }
452             // no guessing when looking for old username, it must be exact match
453             if ($olduser = $DB->get_record('user', array('username'=>$oldusername, 'mnethostid'=>$CFG->mnet_localhost_id))) {
454                 $upt->track('id', $olduser->id, 'normal', false);
455                 if (is_siteadmin($olduser->id)) {
456                     $upt->track('status', $strusernotrenamedadmin, 'error');
457                     $renameerrors++;
458                     continue;
459                 }
460                 $DB->set_field('user', 'username', $user->username, array('id'=>$olduser->id));
461                 $upt->track('username', '', 'normal', false); // clear previous
462                 $upt->track('username', s($oldusername).'-->'.s($user->username), 'info');
463                 $upt->track('status', $struserrenamed);
464                 $renames++;
465             } else {
466                 $upt->track('status', $strusernotrenamedmissing, 'error');
467                 $renameerrors++;
468                 continue;
469             }
470             $existinguser = $olduser;
471             $existinguser->username = $user->username;
472         }
474         // can we process with update or insert?
475         $skip = false;
476         switch ($optype) {
477             case UU_USER_ADDNEW:
478                 if ($existinguser) {
479                     $usersskipped++;
480                     $upt->track('status', $strusernotadded, 'warning');
481                     $skip = true;
482                 }
483                 break;
485             case UU_USER_ADDINC:
486                 if ($existinguser) {
487                     //this should not happen!
488                     $upt->track('status', $strusernotaddederror, 'error');
489                     $userserrors++;
490                     $skip = true;
491                 }
492                 break;
494             case UU_USER_ADD_UPDATE:
495                 break;
497             case UU_USER_UPDATE:
498                 if (!$existinguser) {
499                     $usersskipped++;
500                     $upt->track('status', $strusernotupdatednotexists, 'warning');
501                     $skip = true;
502                 }
503                 break;
505             default:
506                 // unknown type
507                 $skip = true;
508         }
510         if ($skip) {
511             continue;
512         }
514         if ($existinguser) {
515             $user->id = $existinguser->id;
517             $upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$existinguser->id)), s($existinguser->username)), 'normal', false);
518             $upt->track('suspended', $stryesnooptions[$existinguser->suspended] , 'normal', false);
519             $upt->track('auth', $existinguser->auth, 'normal', false);
521             if (is_siteadmin($user->id)) {
522                 $upt->track('status', $strusernotupdatedadmin, 'error');
523                 $userserrors++;
524                 continue;
525             }
527             $existinguser->timemodified = time();
528             // do NOT mess with timecreated or firstaccess here!
530             //load existing profile data
531             profile_load_data($existinguser);
533             $doupdate = false;
534             $dologout = false;
536             if ($updatetype != UU_UPDATE_NOCHANGES and !$remoteuser) {
537                 if (!empty($user->auth) and $user->auth !== $existinguser->auth) {
538                     $upt->track('auth', s($existinguser->auth).'-->'.s($user->auth), 'info', false);
539                     $existinguser->auth = $user->auth;
540                     if (!isset($supportedauths[$user->auth])) {
541                         $upt->track('auth', $struserauthunsupported, 'warning');
542                     }
543                     $doupdate = true;
544                     if ($existinguser->auth === 'nologin') {
545                         $dologout = true;
546                     }
547                 }
548                 $allcolumns = array_merge($STD_FIELDS, $PRF_FIELDS);
549                 foreach ($allcolumns as $column) {
550                     if ($column === 'username' or $column === 'password' or $column === 'auth' or $column === 'suspended') {
551                         // these can not be changed here
552                         continue;
553                     }
554                     if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
555                         continue;
556                     }
557                     if ($updatetype == UU_UPDATE_MISSING) {
558                         if (!is_null($existinguser->$column) and $existinguser->$column !== '') {
559                             continue;
560                         }
561                     } else if ($updatetype == UU_UPDATE_ALLOVERRIDE) {
562                         // we override everything
564                     } else if ($updatetype == UU_UPDATE_FILEOVERRIDE) {
565                         if (!empty($formdefaults[$column])) {
566                             // do not override with form defaults
567                             continue;
568                         }
569                     }
570                     if ($existinguser->$column !== $user->$column) {
571                         if ($column === 'email') {
572                             $select = $DB->sql_like('email', ':email', false, true, false, '|');
573                             $params = array('email' => $DB->sql_like_escape($user->email, '|'));
574                             if ($DB->record_exists_select('user', $select , $params)) {
576                                 $changeincase = core_text::strtolower($existinguser->$column) === core_text::strtolower(
577                                                 $user->$column);
579                                 if ($changeincase) {
580                                     // If only case is different then switch to lower case and carry on.
581                                     $user->$column = core_text::strtolower($user->$column);
582                                     continue;
583                                 } else if ($noemailduplicates) {
584                                     $upt->track('email', $stremailduplicate, 'error');
585                                     $upt->track('status', $strusernotupdated, 'error');
586                                     $userserrors++;
587                                     continue 2;
588                                 } else {
589                                     $upt->track('email', $stremailduplicate, 'warning');
590                                 }
591                             }
592                             if (!validate_email($user->email)) {
593                                 $upt->track('email', get_string('invalidemail'), 'warning');
594                             }
595                         }
597                         if ($column === 'lang') {
598                             if (empty($user->lang)) {
599                                 // Do not change to not-set value.
600                                 continue;
601                             } else if (core_user::clean_field($user->lang, 'lang') === '') {
602                                 $upt->track('status', get_string('cannotfindlang', 'error', $user->lang), 'warning');
603                                 continue;
604                             }
605                         }
607                         if (in_array($column, $upt->columns)) {
608                             $upt->track($column, s($existinguser->$column).'-->'.s($user->$column), 'info', false);
609                         }
610                         $existinguser->$column = $user->$column;
611                         $doupdate = true;
612                     }
613                 }
614             }
616             try {
617                 $auth = get_auth_plugin($existinguser->auth);
618             } catch (Exception $e) {
619                 $upt->track('auth', get_string('userautherror', 'error', s($existinguser->auth)), 'error');
620                 $upt->track('status', $strusernotupdated, 'error');
621                 $userserrors++;
622                 continue;
623             }
624             $isinternalauth = $auth->is_internal();
626             // deal with suspending and activating of accounts
627             if ($allowsuspends and isset($user->suspended) and $user->suspended !== '') {
628                 $user->suspended = $user->suspended ? 1 : 0;
629                 if ($existinguser->suspended != $user->suspended) {
630                     $upt->track('suspended', '', 'normal', false);
631                     $upt->track('suspended', $stryesnooptions[$existinguser->suspended].'-->'.$stryesnooptions[$user->suspended], 'info', false);
632                     $existinguser->suspended = $user->suspended;
633                     $doupdate = true;
634                     if ($existinguser->suspended) {
635                         $dologout = true;
636                     }
637                 }
638             }
640             // changing of passwords is a special case
641             // do not force password changes for external auth plugins!
642             $oldpw = $existinguser->password;
644             if ($remoteuser) {
645                 // Do not mess with passwords of remote users.
647             } else if (!$isinternalauth) {
648                 $existinguser->password = AUTH_PASSWORD_NOT_CACHED;
649                 $upt->track('password', '-', 'normal', false);
650                 // clean up prefs
651                 unset_user_preference('create_password', $existinguser);
652                 unset_user_preference('auth_forcepasswordchange', $existinguser);
654             } else if (!empty($user->password)) {
655                 if ($updatepasswords) {
656                     // Check for passwords that we want to force users to reset next
657                     // time they log in.
658                     $errmsg = null;
659                     $weak = !check_password_policy($user->password, $errmsg);
660                     if ($resetpasswords == UU_PWRESET_ALL or ($resetpasswords == UU_PWRESET_WEAK and $weak)) {
661                         if ($weak) {
662                             $weakpasswords++;
663                             $upt->track('password', $strinvalidpasswordpolicy, 'warning');
664                         }
665                         set_user_preference('auth_forcepasswordchange', 1, $existinguser);
666                     } else {
667                         unset_user_preference('auth_forcepasswordchange', $existinguser);
668                     }
669                     unset_user_preference('create_password', $existinguser); // no need to create password any more
671                     // Use a low cost factor when generating bcrypt hash otherwise
672                     // hashing would be slow when uploading lots of users. Hashes
673                     // will be automatically updated to a higher cost factor the first
674                     // time the user logs in.
675                     $existinguser->password = hash_internal_user_password($user->password, true);
676                     $upt->track('password', $user->password, 'normal', false);
677                 } else {
678                     // do not print password when not changed
679                     $upt->track('password', '', 'normal', false);
680                 }
681             }
683             if ($doupdate or $existinguser->password !== $oldpw) {
684                 // We want only users that were really updated.
685                 user_update_user($existinguser, false, false);
687                 $upt->track('status', $struserupdated);
688                 $usersupdated++;
690                 if (!$remoteuser) {
691                     // pre-process custom profile menu fields data from csv file
692                     $existinguser = uu_pre_process_custom_profile_data($existinguser);
693                     // save custom profile fields data from csv file
694                     profile_save_data($existinguser);
695                 }
697                 if ($bulk == UU_BULK_UPDATED or $bulk == UU_BULK_ALL) {
698                     if (!in_array($user->id, $SESSION->bulk_users)) {
699                         $SESSION->bulk_users[] = $user->id;
700                     }
701                 }
703                 // Trigger event.
704                 \core\event\user_updated::create_from_userid($existinguser->id)->trigger();
706             } else {
707                 // no user information changed
708                 $upt->track('status', $struseruptodate);
709                 $usersuptodate++;
711                 if ($bulk == UU_BULK_ALL) {
712                     if (!in_array($user->id, $SESSION->bulk_users)) {
713                         $SESSION->bulk_users[] = $user->id;
714                     }
715                 }
716             }
718             if ($dologout) {
719                 \core\session\manager::kill_user_sessions($existinguser->id);
720             }
722         } else {
723             // save the new user to the database
724             $user->confirmed    = 1;
725             $user->timemodified = time();
726             $user->timecreated  = time();
727             $user->mnethostid   = $CFG->mnet_localhost_id; // we support ONLY local accounts here, sorry
729             if (!isset($user->suspended) or $user->suspended === '') {
730                 $user->suspended = 0;
731             } else {
732                 $user->suspended = $user->suspended ? 1 : 0;
733             }
734             $upt->track('suspended', $stryesnooptions[$user->suspended], 'normal', false);
736             if (empty($user->auth)) {
737                 $user->auth = 'manual';
738             }
739             $upt->track('auth', $user->auth, 'normal', false);
741             // do not insert record if new auth plugin does not exist!
742             try {
743                 $auth = get_auth_plugin($user->auth);
744             } catch (Exception $e) {
745                 $upt->track('auth', get_string('userautherror', 'error', s($user->auth)), 'error');
746                 $upt->track('status', $strusernotaddederror, 'error');
747                 $userserrors++;
748                 continue;
749             }
750             if (!isset($supportedauths[$user->auth])) {
751                 $upt->track('auth', $struserauthunsupported, 'warning');
752             }
754             $isinternalauth = $auth->is_internal();
756             if (empty($user->email)) {
757                 $upt->track('email', get_string('invalidemail'), 'error');
758                 $upt->track('status', $strusernotaddederror, 'error');
759                 $userserrors++;
760                 continue;
762             } else if ($DB->record_exists('user', array('email'=>$user->email))) {
763                 if ($noemailduplicates) {
764                     $upt->track('email', $stremailduplicate, 'error');
765                     $upt->track('status', $strusernotaddederror, 'error');
766                     $userserrors++;
767                     continue;
768                 } else {
769                     $upt->track('email', $stremailduplicate, 'warning');
770                 }
771             }
772             if (!validate_email($user->email)) {
773                 $upt->track('email', get_string('invalidemail'), 'warning');
774             }
776             if (empty($user->lang)) {
777                 $user->lang = '';
778             } else if (core_user::clean_field($user->lang, 'lang') === '') {
779                 $upt->track('status', get_string('cannotfindlang', 'error', $user->lang), 'warning');
780                 $user->lang = '';
781             }
783             $forcechangepassword = false;
785             if ($isinternalauth) {
786                 if (empty($user->password)) {
787                     if ($createpasswords) {
788                         $user->password = 'to be generated';
789                         $upt->track('password', '', 'normal', false);
790                         $upt->track('password', get_string('uupasswordcron', 'tool_uploaduser'), 'warning', false);
791                     } else {
792                         $upt->track('password', '', 'normal', false);
793                         $upt->track('password', get_string('missingfield', 'error', 'password'), 'error');
794                         $upt->track('status', $strusernotaddederror, 'error');
795                         $userserrors++;
796                         continue;
797                     }
798                 } else {
799                     $errmsg = null;
800                     $weak = !check_password_policy($user->password, $errmsg);
801                     if ($resetpasswords == UU_PWRESET_ALL or ($resetpasswords == UU_PWRESET_WEAK and $weak)) {
802                         if ($weak) {
803                             $weakpasswords++;
804                             $upt->track('password', $strinvalidpasswordpolicy, 'warning');
805                         }
806                         $forcechangepassword = true;
807                     }
808                     // Use a low cost factor when generating bcrypt hash otherwise
809                     // hashing would be slow when uploading lots of users. Hashes
810                     // will be automatically updated to a higher cost factor the first
811                     // time the user logs in.
812                     $user->password = hash_internal_user_password($user->password, true);
813                 }
814             } else {
815                 $user->password = AUTH_PASSWORD_NOT_CACHED;
816                 $upt->track('password', '-', 'normal', false);
817             }
819             $user->id = user_create_user($user, false, false);
820             $upt->track('username', html_writer::link(new moodle_url('/user/profile.php', array('id'=>$user->id)), s($user->username)), 'normal', false);
822             // pre-process custom profile menu fields data from csv file
823             $user = uu_pre_process_custom_profile_data($user);
824             // save custom profile fields data
825             profile_save_data($user);
827             if ($forcechangepassword) {
828                 set_user_preference('auth_forcepasswordchange', 1, $user);
829             }
830             if ($user->password === 'to be generated') {
831                 set_user_preference('create_password', 1, $user);
832             }
834             // Trigger event.
835             \core\event\user_created::create_from_userid($user->id)->trigger();
837             $upt->track('status', $struseradded);
838             $upt->track('id', $user->id, 'normal', false);
839             $usersnew++;
841             // make sure user context exists
842             context_user::instance($user->id);
844             if ($bulk == UU_BULK_NEW or $bulk == UU_BULK_ALL) {
845                 if (!in_array($user->id, $SESSION->bulk_users)) {
846                     $SESSION->bulk_users[] = $user->id;
847                 }
848             }
849         }
851         // Update user interests.
852         if (isset($user->interests) && strval($user->interests) !== '') {
853             useredit_update_interests($user, preg_split('/\s*,\s*/', $user->interests, -1, PREG_SPLIT_NO_EMPTY));
854         }
856         // add to cohort first, it might trigger enrolments indirectly - do NOT create cohorts here!
857         foreach ($filecolumns as $column) {
858             if (!preg_match('/^cohort\d+$/', $column)) {
859                 continue;
860             }
862             if (!empty($user->$column)) {
863                 $addcohort = $user->$column;
864                 if (!isset($cohorts[$addcohort])) {
865                     if (is_number($addcohort)) {
866                         // only non-numeric idnumbers!
867                         $cohort = $DB->get_record('cohort', array('id'=>$addcohort));
868                     } else {
869                         $cohort = $DB->get_record('cohort', array('idnumber'=>$addcohort));
870                         if (empty($cohort) && has_capability('moodle/cohort:manage', context_system::instance())) {
871                             // Cohort was not found. Create a new one.
872                             $cohortid = cohort_add_cohort((object)array(
873                                 'idnumber' => $addcohort,
874                                 'name' => $addcohort,
875                                 'contextid' => context_system::instance()->id
876                             ));
877                             $cohort = $DB->get_record('cohort', array('id'=>$cohortid));
878                         }
879                     }
881                     if (empty($cohort)) {
882                         $cohorts[$addcohort] = get_string('unknowncohort', 'core_cohort', s($addcohort));
883                     } else if (!empty($cohort->component)) {
884                         // cohorts synchronised with external sources must not be modified!
885                         $cohorts[$addcohort] = get_string('external', 'core_cohort');
886                     } else {
887                         $cohorts[$addcohort] = $cohort;
888                     }
889                 }
891                 if (is_object($cohorts[$addcohort])) {
892                     $cohort = $cohorts[$addcohort];
893                     if (!$DB->record_exists('cohort_members', array('cohortid'=>$cohort->id, 'userid'=>$user->id))) {
894                         cohort_add_member($cohort->id, $user->id);
895                         // we might add special column later, for now let's abuse enrolments
896                         $upt->track('enrolments', get_string('useradded', 'core_cohort', s($cohort->name)));
897                     }
898                 } else {
899                     // error message
900                     $upt->track('enrolments', $cohorts[$addcohort], 'error');
901                 }
902             }
903         }
906         // find course enrolments, groups, roles/types and enrol periods
907         // this is again a special case, we always do this for any updated or created users
908         foreach ($filecolumns as $column) {
909             if (preg_match('/^sysrole\d+$/', $column)) {
911                 if (!empty($user->$column)) {
912                     $sysrolename = $user->$column;
913                     if ($sysrolename[0] == '-') {
914                         $removing = true;
915                         $sysrolename = substr($sysrolename, 1);
916                     } else {
917                         $removing = false;
918                     }
920                     if (array_key_exists($sysrolename, $sysrolecache)) {
921                         $sysroleid = $sysrolecache[$sysrolename]->id;
922                     } else {
923                         $upt->track('enrolments', get_string('unknownrole', 'error', s($sysrolename)), 'error');
924                         continue;
925                     }
927                     if ($removing) {
928                         if (user_has_role_assignment($user->id, $sysroleid, SYSCONTEXTID)) {
929                             role_unassign($sysroleid, $user->id, SYSCONTEXTID);
930                             $upt->track('enrolments', get_string('unassignedsysrole',
931                                     'tool_uploaduser', $sysrolecache[$sysroleid]->name));
932                         }
933                     } else {
934                         if (!user_has_role_assignment($user->id, $sysroleid, SYSCONTEXTID)) {
935                             role_assign($sysroleid, $user->id, SYSCONTEXTID);
936                             $upt->track('enrolments', get_string('assignedsysrole',
937                                     'tool_uploaduser', $sysrolecache[$sysroleid]->name));
938                         }
939                     }
940                 }
942                 continue;
943             }
944             if (!preg_match('/^course\d+$/', $column)) {
945                 continue;
946             }
947             $i = substr($column, 6);
949             if (empty($user->{'course'.$i})) {
950                 continue;
951             }
952             $shortname = $user->{'course'.$i};
953             if (!array_key_exists($shortname, $ccache)) {
954                 if (!$course = $DB->get_record('course', array('shortname'=>$shortname), 'id, shortname')) {
955                     $upt->track('enrolments', get_string('unknowncourse', 'error', s($shortname)), 'error');
956                     continue;
957                 }
958                 $ccache[$shortname] = $course;
959                 $ccache[$shortname]->groups = null;
960             }
961             $courseid      = $ccache[$shortname]->id;
962             $coursecontext = context_course::instance($courseid);
963             if (!isset($manualcache[$courseid])) {
964                 $manualcache[$courseid] = false;
965                 if ($manual) {
966                     if ($instances = enrol_get_instances($courseid, false)) {
967                         foreach ($instances as $instance) {
968                             if ($instance->enrol === 'manual') {
969                                 $manualcache[$courseid] = $instance;
970                                 break;
971                             }
972                         }
973                     }
974                 }
975             }
977             if ($courseid == SITEID) {
978                 // Technically frontpage does not have enrolments, but only role assignments,
979                 // let's not invent new lang strings here for this rarely used feature.
981                 if (!empty($user->{'role'.$i})) {
982                     $rolename = $user->{'role'.$i};
983                     if (array_key_exists($rolename, $rolecache)) {
984                         $roleid = $rolecache[$rolename]->id;
985                     } else {
986                         $upt->track('enrolments', get_string('unknownrole', 'error', s($rolename)), 'error');
987                         continue;
988                     }
990                     role_assign($roleid, $user->id, context_course::instance($courseid));
992                     $a = new stdClass();
993                     $a->course = $shortname;
994                     $a->role   = $rolecache[$roleid]->name;
995                     $upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a));
996                 }
998             } else if ($manual and $manualcache[$courseid]) {
1000                 // find role
1001                 $roleid = false;
1002                 if (!empty($user->{'role'.$i})) {
1003                     $rolename = $user->{'role'.$i};
1004                     if (array_key_exists($rolename, $rolecache)) {
1005                         $roleid = $rolecache[$rolename]->id;
1006                     } else {
1007                         $upt->track('enrolments', get_string('unknownrole', 'error', s($rolename)), 'error');
1008                         continue;
1009                     }
1011                 } else if (!empty($user->{'type'.$i})) {
1012                     // if no role, then find "old" enrolment type
1013                     $addtype = $user->{'type'.$i};
1014                     if ($addtype < 1 or $addtype > 3) {
1015                         $upt->track('enrolments', $strerror.': typeN = 1|2|3', 'error');
1016                         continue;
1017                     } else if (empty($formdata->{'uulegacy'.$addtype})) {
1018                         continue;
1019                     } else {
1020                         $roleid = $formdata->{'uulegacy'.$addtype};
1021                     }
1022                 } else {
1023                     // no role specified, use the default from manual enrol plugin
1024                     $roleid = $manualcache[$courseid]->roleid;
1025                 }
1027                 if ($roleid) {
1028                     // Find duration and/or enrol status.
1029                     $timeend = 0;
1030                     $status = null;
1032                     if (isset($user->{'enrolstatus'.$i})) {
1033                         $enrolstatus = $user->{'enrolstatus'.$i};
1034                         if ($enrolstatus == '') {
1035                             $status = null;
1036                         } else if ($enrolstatus === (string)ENROL_USER_ACTIVE) {
1037                             $status = ENROL_USER_ACTIVE;
1038                         } else if ($enrolstatus === (string)ENROL_USER_SUSPENDED) {
1039                             $status = ENROL_USER_SUSPENDED;
1040                         } else {
1041                             debugging('Unknown enrolment status.');
1042                         }
1043                     }
1045                     if (!empty($user->{'enrolperiod'.$i})) {
1046                         $duration = (int)$user->{'enrolperiod'.$i} * 60*60*24; // convert days to seconds
1047                         if ($duration > 0) { // sanity check
1048                             $timeend = $today + $duration;
1049                         }
1050                     } else if ($manualcache[$courseid]->enrolperiod > 0) {
1051                         $timeend = $today + $manualcache[$courseid]->enrolperiod;
1052                     }
1054                     $manual->enrol_user($manualcache[$courseid], $user->id, $roleid, $today, $timeend, $status);
1056                     $a = new stdClass();
1057                     $a->course = $shortname;
1058                     $a->role   = $rolecache[$roleid]->name;
1059                     $upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a));
1060                 }
1061             }
1063             // find group to add to
1064             if (!empty($user->{'group'.$i})) {
1065                 // make sure user is enrolled into course before adding into groups
1066                 if (!is_enrolled($coursecontext, $user->id)) {
1067                     $upt->track('enrolments', get_string('addedtogroupnotenrolled', '', $user->{'group'.$i}), 'error');
1068                     continue;
1069                 }
1070                 //build group cache
1071                 if (is_null($ccache[$shortname]->groups)) {
1072                     $ccache[$shortname]->groups = array();
1073                     if ($groups = groups_get_all_groups($courseid)) {
1074                         foreach ($groups as $gid=>$group) {
1075                             $ccache[$shortname]->groups[$gid] = new stdClass();
1076                             $ccache[$shortname]->groups[$gid]->id   = $gid;
1077                             $ccache[$shortname]->groups[$gid]->name = $group->name;
1078                             if (!is_numeric($group->name)) { // only non-numeric names are supported!!!
1079                                 $ccache[$shortname]->groups[$group->name] = new stdClass();
1080                                 $ccache[$shortname]->groups[$group->name]->id   = $gid;
1081                                 $ccache[$shortname]->groups[$group->name]->name = $group->name;
1082                             }
1083                         }
1084                     }
1085                 }
1086                 // group exists?
1087                 $addgroup = $user->{'group'.$i};
1088                 if (!array_key_exists($addgroup, $ccache[$shortname]->groups)) {
1089                     // if group doesn't exist,  create it
1090                     $newgroupdata = new stdClass();
1091                     $newgroupdata->name = $addgroup;
1092                     $newgroupdata->courseid = $ccache[$shortname]->id;
1093                     $newgroupdata->description = '';
1094                     $gid = groups_create_group($newgroupdata);
1095                     if ($gid){
1096                         $ccache[$shortname]->groups[$addgroup] = new stdClass();
1097                         $ccache[$shortname]->groups[$addgroup]->id   = $gid;
1098                         $ccache[$shortname]->groups[$addgroup]->name = $newgroupdata->name;
1099                     } else {
1100                         $upt->track('enrolments', get_string('unknowngroup', 'error', s($addgroup)), 'error');
1101                         continue;
1102                     }
1103                 }
1104                 $gid   = $ccache[$shortname]->groups[$addgroup]->id;
1105                 $gname = $ccache[$shortname]->groups[$addgroup]->name;
1107                 try {
1108                     if (groups_add_member($gid, $user->id)) {
1109                         $upt->track('enrolments', get_string('addedtogroup', '', s($gname)));
1110                     }  else {
1111                         $upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error');
1112                     }
1113                 } catch (moodle_exception $e) {
1114                     $upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error');
1115                     continue;
1116                 }
1117             }
1118         }
1119         $validation[$user->username] = core_user::validate($user);
1120     }
1121     $upt->close(); // close table
1122     if (!empty($validation)) {
1123         foreach ($validation as $username => $result) {
1124             if ($result !== true) {
1125                 \core\notification::warning(get_string('invaliduserdata', 'tool_uploaduser', s($username)));
1126             }
1127         }
1128     }
1129     $cir->close();
1130     $cir->cleanup(true);
1132     echo $OUTPUT->box_start('boxwidthnarrow boxaligncenter generalbox', 'uploadresults');
1133     echo '<p>';
1134     if ($optype != UU_USER_UPDATE) {
1135         echo get_string('userscreated', 'tool_uploaduser').': '.$usersnew.'<br />';
1136     }
1137     if ($optype == UU_USER_UPDATE or $optype == UU_USER_ADD_UPDATE) {
1138         echo get_string('usersupdated', 'tool_uploaduser').': '.$usersupdated.'<br />';
1139     }
1140     if ($allowdeletes) {
1141         echo get_string('usersdeleted', 'tool_uploaduser').': '.$deletes.'<br />';
1142         echo get_string('deleteerrors', 'tool_uploaduser').': '.$deleteerrors.'<br />';
1143     }
1144     if ($allowrenames) {
1145         echo get_string('usersrenamed', 'tool_uploaduser').': '.$renames.'<br />';
1146         echo get_string('renameerrors', 'tool_uploaduser').': '.$renameerrors.'<br />';
1147     }
1148     if ($usersskipped) {
1149         echo get_string('usersskipped', 'tool_uploaduser').': '.$usersskipped.'<br />';
1150     }
1151     echo get_string('usersweakpassword', 'tool_uploaduser').': '.$weakpasswords.'<br />';
1152     echo get_string('errors', 'tool_uploaduser').': '.$userserrors.'</p>';
1153     echo $OUTPUT->box_end();
1155     if ($bulk) {
1156         echo $OUTPUT->continue_button($bulknurl);
1157     } else {
1158         echo $OUTPUT->continue_button($returnurl);
1159     }
1160     echo $OUTPUT->footer();
1161     die;
1164 // Print the header
1165 echo $OUTPUT->header();
1167 echo $OUTPUT->heading(get_string('uploaduserspreview', 'tool_uploaduser'));
1169 // NOTE: this is JUST csv processing preview, we must not prevent import from here if there is something in the file!!
1170 //       this was intended for validation of csv formatting and encoding, not filtering the data!!!!
1171 //       we definitely must not process the whole file!
1173 // preview table data
1174 $data = array();
1175 $cir->init();
1176 $linenum = 1; //column header is first line
1177 $noerror = true; // Keep status of any error.
1178 while ($linenum <= $previewrows and $fields = $cir->next()) {
1179     $linenum++;
1180     $rowcols = array();
1181     $rowcols['line'] = $linenum;
1182     foreach($fields as $key => $field) {
1183         $rowcols[$filecolumns[$key]] = s(trim($field));
1184     }
1185     $rowcols['status'] = array();
1187     if (isset($rowcols['username'])) {
1188         $stdusername = core_user::clean_field($rowcols['username'], 'username');
1189         if ($rowcols['username'] !== $stdusername) {
1190             $rowcols['status'][] = get_string('invalidusernameupload');
1191         }
1192         if ($userid = $DB->get_field('user', 'id', array('username'=>$stdusername, 'mnethostid'=>$CFG->mnet_localhost_id))) {
1193             $rowcols['username'] = html_writer::link(new moodle_url('/user/profile.php', array('id'=>$userid)), $rowcols['username']);
1194         }
1195     } else {
1196         $rowcols['status'][] = get_string('missingusername');
1197     }
1199     if (isset($rowcols['email'])) {
1200         if (!validate_email($rowcols['email'])) {
1201             $rowcols['status'][] = get_string('invalidemail');
1202         }
1204         $select = $DB->sql_like('email', ':email', false, true, false, '|');
1205         $params = array('email' => $DB->sql_like_escape($rowcols['email'], '|'));
1206         if ($DB->record_exists_select('user', $select , $params)) {
1207             $rowcols['status'][] = $stremailduplicate;
1208         }
1209     }
1211     if (isset($rowcols['city'])) {
1212         $rowcols['city'] = $rowcols['city'];
1213     }
1214     // Check if rowcols have custom profile field with correct data and update error state.
1215     $noerror = uu_check_custom_profile_data($rowcols) && $noerror;
1216     $rowcols['status'] = implode('<br />', $rowcols['status']);
1217     $data[] = $rowcols;
1219 if ($fields = $cir->next()) {
1220     $data[] = array_fill(0, count($fields) + 2, '...');
1222 $cir->close();
1224 $table = new html_table();
1225 $table->id = "uupreview";
1226 $table->attributes['class'] = 'generaltable';
1227 $table->tablealign = 'center';
1228 $table->summary = get_string('uploaduserspreview', 'tool_uploaduser');
1229 $table->head = array();
1230 $table->data = $data;
1232 $table->head[] = get_string('uucsvline', 'tool_uploaduser');
1233 foreach ($filecolumns as $column) {
1234     $table->head[] = $column;
1236 $table->head[] = get_string('status');
1238 echo html_writer::tag('div', html_writer::table($table), array('class'=>'flexible-wrap'));
1240 // Print the form if valid values are available
1241 if ($noerror) {
1242     $mform2->display();
1244 echo $OUTPUT->footer();
1245 die;