MDL-70143 tool_uploaduser: fix wrong cache reference
[moodle.git] / admin / tool / uploaduser / classes / process.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  * Class process
19  *
20  * @package     tool_uploaduser
21  * @copyright   2020 Moodle
22  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace tool_uploaduser;
27 defined('MOODLE_INTERNAL') || die();
29 use tool_uploaduser\local\field_value_validators;
31 require_once($CFG->dirroot.'/user/profile/lib.php');
32 require_once($CFG->dirroot.'/user/lib.php');
33 require_once($CFG->dirroot.'/group/lib.php');
34 require_once($CFG->dirroot.'/cohort/lib.php');
35 require_once($CFG->libdir.'/csvlib.class.php');
36 require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/uploaduser/locallib.php');
38 /**
39  * Process CSV file with users data, this will create/update users, enrol them into courses, etc
40  *
41  * @package     tool_uploaduser
42  * @copyright   2020 Moodle
43  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44  */
45 class process {
47     /** @var \csv_import_reader  */
48     protected $cir;
49     /** @var \stdClass  */
50     protected $formdata;
51     /** @var \uu_progress_tracker  */
52     protected $upt;
53     /** @var array  */
54     protected $filecolumns = null;
55     /** @var int  */
56     protected $today;
57     /** @var \enrol_plugin|null */
58     protected $manualenrol = null;
59     /** @var array */
60     protected $standardfields = [];
61     /** @var array */
62     protected $profilefields = [];
63     /** @var array */
64     protected $allprofilefields = [];
65     /** @var string|\uu_progress_tracker|null  */
66     protected $progresstrackerclass = null;
68     /** @var int */
69     protected $usersnew      = 0;
70     /** @var int */
71     protected $usersupdated  = 0;
72     /** @var int /not printed yet anywhere */
73     protected $usersuptodate = 0;
74     /** @var int */
75     protected $userserrors   = 0;
76     /** @var int */
77     protected $deletes       = 0;
78     /** @var int */
79     protected $deleteerrors  = 0;
80     /** @var int */
81     protected $renames       = 0;
82     /** @var int */
83     protected $renameerrors  = 0;
84     /** @var int */
85     protected $usersskipped  = 0;
86     /** @var int */
87     protected $weakpasswords = 0;
89     /** @var array course cache - do not fetch all courses here, we  will not probably use them all anyway */
90     protected $ccache         = [];
91     /** @var array */
92     protected $cohorts        = [];
93     /** @var array  Course roles lookup cache. */
94     protected $rolecache      = [];
95     /** @var array System roles lookup cache. */
96     protected $sysrolecache   = [];
97     /** @var array cache of used manual enrol plugins in each course */
98     protected $manualcache    = [];
99     /** @var array officially supported plugins that are enabled */
100     protected $supportedauths = [];
102     /**
103      * process constructor.
104      *
105      * @param \csv_import_reader $cir
106      * @param string|null $progresstrackerclass
107      * @throws \coding_exception
108      */
109     public function __construct(\csv_import_reader $cir, string $progresstrackerclass = null) {
110         $this->cir = $cir;
111         if ($progresstrackerclass) {
112             if (!class_exists($progresstrackerclass) || !is_subclass_of($progresstrackerclass, \uu_progress_tracker::class)) {
113                 throw new \coding_exception('Progress tracker class must extend \uu_progress_tracker');
114             }
115             $this->progresstrackerclass = $progresstrackerclass;
116         } else {
117             $this->progresstrackerclass = \uu_progress_tracker::class;
118         }
120         // Keep timestamp consistent.
121         $today = time();
122         $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
123         $this->today = $today;
125         $this->rolecache      = uu_allowed_roles_cache(); // Course roles lookup cache.
126         $this->sysrolecache   = uu_allowed_sysroles_cache(); // System roles lookup cache.
127         $this->supportedauths = uu_supported_auths(); // Officially supported plugins that are enabled.
129         if (enrol_is_enabled('manual')) {
130             // We use only manual enrol plugin here, if it is disabled no enrol is done.
131             $this->manualenrol = enrol_get_plugin('manual');
132         }
134         $this->find_profile_fields();
135         $this->find_standard_fields();
136     }
138     /**
139      * Standard user fields.
140      */
141     protected function find_standard_fields(): void {
142         $this->standardfields = array('id', 'username', 'email', 'emailstop',
143             'city', 'country', 'lang', 'timezone', 'mailformat',
144             'maildisplay', 'maildigest', 'htmleditor', 'autosubscribe',
145             'institution', 'department', 'idnumber', 'skype',
146             'msn', 'aim', 'yahoo', 'icq', 'phone1', 'phone2', 'address',
147             'url', 'description', 'descriptionformat', 'password',
148             'auth',        // Watch out when changing auth type or using external auth plugins!
149             'oldusername', // Use when renaming users - this is the original username.
150             'suspended',   // 1 means suspend user account, 0 means activate user account, nothing means keep as is.
151             'theme',       // Define a theme for user when 'allowuserthemes' is enabled.
152             'deleted',     // 1 means delete user
153             'mnethostid',  // Can not be used for adding, updating or deleting of users - only for enrolments,
154                            // groups, cohorts and suspending.
155             'interests',
156         );
157         // Include all name fields.
158         $this->standardfields = array_merge($this->standardfields, get_all_user_name_fields());
159     }
161     /**
162      * Profile fields
163      */
164     protected function find_profile_fields(): void {
165         global $DB;
166         $this->allprofilefields = $DB->get_records('user_info_field');
167         $this->profilefields = [];
168         if ($proffields = $this->allprofilefields) {
169             foreach ($proffields as $key => $proffield) {
170                 $profilefieldname = 'profile_field_'.$proffield->shortname;
171                 $this->profilefields[] = $profilefieldname;
172                 // Re-index $proffields with key as shortname. This will be
173                 // used while checking if profile data is key and needs to be converted (eg. menu profile field).
174                 $proffields[$profilefieldname] = $proffield;
175                 unset($proffields[$key]);
176             }
177             $this->allprofilefields = $proffields;
178         }
179     }
181     /**
182      * Returns the list of columns in the file
183      *
184      * @return array
185      */
186     public function get_file_columns(): array {
187         if ($this->filecolumns === null) {
188             $returnurl = new \moodle_url('/admin/tool/uploaduser/index.php');
189             $this->filecolumns = uu_validate_user_upload_columns($this->cir,
190                 $this->standardfields, $this->profilefields, $returnurl);
191         }
192         return $this->filecolumns;
193     }
195     /**
196      * Set data from the form (or from CLI options)
197      *
198      * @param \stdClass $formdata
199      */
200     public function set_form_data(\stdClass $formdata): void {
201         global $SESSION;
202         $this->formdata = $formdata;
204         // Clear bulk selection.
205         if ($this->get_bulk()) {
206             $SESSION->bulk_users = array();
207         }
208     }
210     /**
211      * Operation type
212      * @return int
213      */
214     protected function get_operation_type(): int {
215         return (int)$this->formdata->uutype;
216     }
218     /**
219      * Setting to allow deletes
220      * @return bool
221      */
222     protected function get_allow_deletes(): bool {
223         $optype = $this->get_operation_type();
224         return (!empty($this->formdata->uuallowdeletes) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC);
225     }
227     /**
228      * Setting to allow deletes
229      * @return bool
230      */
231     protected function get_allow_renames(): bool {
232         $optype = $this->get_operation_type();
233         return (!empty($this->formdata->uuallowrenames) and $optype != UU_USER_ADDNEW and $optype != UU_USER_ADDINC);
234     }
236     /**
237      * Setting to select for bulk actions (not available in CLI)
238      * @return bool
239      */
240     public function get_bulk(): bool {
241         return $this->formdata->uubulk ?? false;
242     }
244     /**
245      * Setting for update type
246      * @return int
247      */
248     protected function get_update_type(): int {
249         return isset($this->formdata->uuupdatetype) ? $this->formdata->uuupdatetype : 0;
250     }
252     /**
253      * Setting to allow update passwords
254      * @return bool
255      */
256     protected function get_update_passwords(): bool {
257         return !empty($this->formdata->uupasswordold)
258             and $this->get_operation_type() != UU_USER_ADDNEW
259             and $this->get_operation_type() != UU_USER_ADDINC
260             and ($this->get_update_type() == UU_UPDATE_FILEOVERRIDE or $this->get_update_type() == UU_UPDATE_ALLOVERRIDE);
261     }
263     /**
264      * Setting to allow email duplicates
265      * @return bool
266      */
267     protected function get_allow_email_duplicates(): bool {
268         global $CFG;
269         return !(empty($CFG->allowaccountssameemail) ? 1 : $this->formdata->uunoemailduplicates);
270     }
272     /**
273      * Setting for reset password
274      * @return int UU_PWRESET_NONE, UU_PWRESET_WEAK, UU_PWRESET_ALL
275      */
276     protected function get_reset_passwords(): int {
277         return isset($this->formdata->uuforcepasswordchange) ? $this->formdata->uuforcepasswordchange : UU_PWRESET_NONE;
278     }
280     /**
281      * Setting to allow create passwords
282      * @return bool
283      */
284     protected function get_create_paswords(): bool {
285         return (!empty($this->formdata->uupasswordnew) and $this->get_operation_type() != UU_USER_UPDATE);
286     }
288     /**
289      * Setting to allow suspends
290      * @return bool
291      */
292     protected function get_allow_suspends(): bool {
293         return !empty($this->formdata->uuallowsuspends);
294     }
296     /**
297      * Setting to normalise user names
298      * @return bool
299      */
300     protected function get_normalise_user_names(): bool {
301         return !empty($this->formdata->uustandardusernames);
302     }
304     /**
305      * Helper method to return Yes/No string
306      *
307      * @param bool $value
308      * @return string
309      */
310     protected function get_string_yes_no($value): string {
311         return $value ? get_string('yes') : get_string('no');
312     }
314     /**
315      * Process the CSV file
316      */
317     public function process() {
318         // Init csv import helper.
319         $this->cir->init();
321         $classname = $this->progresstrackerclass;
322         $this->upt = new $classname();
323         $this->upt->start(); // Start table.
325         $linenum = 1; // Column header is first line.
326         while ($line = $this->cir->next()) {
327             $this->upt->flush();
328             $linenum++;
330             $this->upt->track('line', $linenum);
331             $this->process_line($line);
332         }
334         $this->upt->close(); // Close table.
335         $this->cir->close();
336         $this->cir->cleanup(true);
337     }
339     /**
340      * Prepare one line from CSV file as a user record
341      *
342      * @param array $line
343      * @return \stdClass|null
344      */
345     protected function prepare_user_record(array $line): ?\stdClass {
346         global $CFG, $USER;
348         $user = new \stdClass();
350         // Add fields to user object.
351         foreach ($line as $keynum => $value) {
352             if (!isset($this->get_file_columns()[$keynum])) {
353                 // This should not happen.
354                 continue;
355             }
356             $key = $this->get_file_columns()[$keynum];
357             if (strpos($key, 'profile_field_') === 0) {
358                 // NOTE: bloody mega hack alert!!
359                 if (isset($USER->$key) and is_array($USER->$key)) {
360                     // This must be some hacky field that is abusing arrays to store content and format.
361                     $user->$key = array();
362                     $user->{$key['text']}   = $value;
363                     $user->{$key['format']} = FORMAT_MOODLE;
364                 } else {
365                     $user->$key = trim($value);
366                 }
367             } else {
368                 $user->$key = trim($value);
369             }
371             if (in_array($key, $this->upt->columns)) {
372                 // Default value in progress tracking table, can be changed later.
373                 $this->upt->track($key, s($value), 'normal');
374             }
375         }
376         if (!isset($user->username)) {
377             // Prevent warnings below.
378             $user->username = '';
379         }
381         if ($this->get_operation_type() == UU_USER_ADDNEW or $this->get_operation_type() == UU_USER_ADDINC) {
382             // User creation is a special case - the username may be constructed from templates using firstname and lastname
383             // better never try this in mixed update types.
384             $error = false;
385             if (!isset($user->firstname) or $user->firstname === '') {
386                 $this->upt->track('status', get_string('missingfield', 'error', 'firstname'), 'error');
387                 $this->upt->track('firstname', get_string('error'), 'error');
388                 $error = true;
389             }
390             if (!isset($user->lastname) or $user->lastname === '') {
391                 $this->upt->track('status', get_string('missingfield', 'error', 'lastname'), 'error');
392                 $this->upt->track('lastname', get_string('error'), 'error');
393                 $error = true;
394             }
395             if ($error) {
396                 $this->userserrors++;
397                 return null;
398             }
399             // We require username too - we might use template for it though.
400             if (empty($user->username) and !empty($this->formdata->username)) {
401                 $user->username = uu_process_template($this->formdata->username, $user);
402                 $this->upt->track('username', s($user->username));
403             }
404         }
406         // Normalize username.
407         $user->originalusername = $user->username;
408         if ($this->get_normalise_user_names()) {
409             $user->username = \core_user::clean_field($user->username, 'username');
410         }
412         // Make sure we really have username.
413         if (empty($user->username)) {
414             $this->upt->track('status', get_string('missingfield', 'error', 'username'), 'error');
415             $this->upt->track('username', get_string('error'), 'error');
416             $this->userserrors++;
417             return null;
418         } else if ($user->username === 'guest') {
419             $this->upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error');
420             $this->userserrors++;
421             return null;
422         }
424         if ($user->username !== \core_user::clean_field($user->username, 'username')) {
425             $this->upt->track('status', get_string('invalidusername', 'error', 'username'), 'error');
426             $this->upt->track('username', get_string('error'), 'error');
427             $this->userserrors++;
428         }
430         if (empty($user->mnethostid)) {
431             $user->mnethostid = $CFG->mnet_localhost_id;
432         }
434         return $user;
435     }
437     /**
438      * Process one line from CSV file
439      *
440      * @param array $line
441      * @throws \coding_exception
442      * @throws \dml_exception
443      * @throws \moodle_exception
444      */
445     public function process_line(array $line) {
446         global $DB, $CFG, $SESSION;
448         if (!$user = $this->prepare_user_record($line)) {
449             return;
450         }
452         if ($existinguser = $DB->get_record('user', ['username' => $user->username, 'mnethostid' => $user->mnethostid])) {
453             $this->upt->track('id', $existinguser->id, 'normal', false);
454         }
456         if ($user->mnethostid == $CFG->mnet_localhost_id) {
457             $remoteuser = false;
459             // Find out if username incrementing required.
460             if ($existinguser and $this->get_operation_type() == UU_USER_ADDINC) {
461                 $user->username = uu_increment_username($user->username);
462                 $existinguser = false;
463             }
465         } else {
466             if (!$existinguser or $this->get_operation_type() == UU_USER_ADDINC) {
467                 $this->upt->track('status', get_string('errormnetadd', 'tool_uploaduser'), 'error');
468                 $this->userserrors++;
469                 return;
470             }
472             $remoteuser = true;
474             // Make sure there are no changes of existing fields except the suspended status.
475             foreach ((array)$existinguser as $k => $v) {
476                 if ($k === 'suspended') {
477                     continue;
478                 }
479                 if (property_exists($user, $k)) {
480                     $user->$k = $v;
481                 }
482                 if (in_array($k, $this->upt->columns)) {
483                     if ($k === 'password' or $k === 'oldusername' or $k === 'deleted') {
484                         $this->upt->track($k, '', 'normal', false);
485                     } else {
486                         $this->upt->track($k, s($v), 'normal', false);
487                     }
488                 }
489             }
490             unset($user->oldusername);
491             unset($user->password);
492             $user->auth = $existinguser->auth;
493         }
495         // Notify about nay username changes.
496         if ($user->originalusername !== $user->username) {
497             $this->upt->track('username', '', 'normal', false); // Clear previous.
498             $this->upt->track('username', s($user->originalusername).'-->'.s($user->username), 'info');
499         } else {
500             $this->upt->track('username', s($user->username), 'normal', false);
501         }
502         unset($user->originalusername);
504         // Verify if the theme is valid and allowed to be set.
505         if (isset($user->theme)) {
506             list($status, $message) = field_value_validators::validate_theme($user->theme);
507             if ($status !== 'normal' && !empty($message)) {
508                 $this->upt->track('status', $message, $status);
509                 // Unset the theme when validation fails.
510                 unset($user->theme);
511             }
512         }
514         // Add default values for remaining fields.
515         $formdefaults = array();
516         if (!$existinguser ||
517                 ($this->get_update_type() != UU_UPDATE_FILEOVERRIDE && $this->get_update_type() != UU_UPDATE_NOCHANGES)) {
518             foreach ($this->standardfields as $field) {
519                 if (isset($user->$field)) {
520                     continue;
521                 }
522                 // All validation moved to form2.
523                 if (isset($this->formdata->$field)) {
524                     // Process templates.
525                     $user->$field = uu_process_template($this->formdata->$field, $user);
526                     $formdefaults[$field] = true;
527                     if (in_array($field, $this->upt->columns)) {
528                         $this->upt->track($field, s($user->$field), 'normal');
529                     }
530                 }
531             }
532             $proffields = $this->allprofilefields;
533             foreach ($this->profilefields as $field) {
534                 if (isset($user->$field)) {
535                     continue;
536                 }
537                 if (isset($this->formdata->$field)) {
538                     // Process templates.
539                     $user->$field = uu_process_template($this->formdata->$field, $user);
541                     // Form contains key and later code expects value.
542                     // Convert key to value for required profile fields.
543                     require_once($CFG->dirroot.'/user/profile/field/'.$proffields[$field]->datatype.'/field.class.php');
544                     $profilefieldclass = 'profile_field_'.$proffields[$field]->datatype;
545                     $profilefield = new $profilefieldclass($proffields[$field]->id);
546                     if (method_exists($profilefield, 'convert_external_data')) {
547                         $user->$field = $profilefield->edit_save_data_preprocess($user->$field, null);
548                     }
550                     $formdefaults[$field] = true;
551                 }
552             }
553         }
555         // Delete user.
556         if (!empty($user->deleted)) {
557             if (!$this->get_allow_deletes() or $remoteuser) {
558                 $this->usersskipped++;
559                 $this->upt->track('status', get_string('usernotdeletedoff', 'error'), 'warning');
560                 return;
561             }
562             if ($existinguser) {
563                 if (is_siteadmin($existinguser->id)) {
564                     $this->upt->track('status', get_string('usernotdeletedadmin', 'error'), 'error');
565                     $this->deleteerrors++;
566                     return;
567                 }
568                 if (delete_user($existinguser)) {
569                     $this->upt->track('status', get_string('userdeleted', 'tool_uploaduser'));
570                     $this->deletes++;
571                 } else {
572                     $this->upt->track('status', get_string('usernotdeletederror', 'error'), 'error');
573                     $this->deleteerrors++;
574                 }
575             } else {
576                 $this->upt->track('status', get_string('usernotdeletedmissing', 'error'), 'error');
577                 $this->deleteerrors++;
578             }
579             return;
580         }
581         // We do not need the deleted flag anymore.
582         unset($user->deleted);
584         // Renaming requested?
585         if (!empty($user->oldusername) ) {
586             if (!$this->get_allow_renames()) {
587                 $this->usersskipped++;
588                 $this->upt->track('status', get_string('usernotrenamedoff', 'error'), 'warning');
589                 return;
590             }
592             if ($existinguser) {
593                 $this->upt->track('status', get_string('usernotrenamedexists', 'error'), 'error');
594                 $this->renameerrors++;
595                 return;
596             }
598             if ($user->username === 'guest') {
599                 $this->upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error');
600                 $this->renameerrors++;
601                 return;
602             }
604             if ($this->get_normalise_user_names()) {
605                 $oldusername = \core_user::clean_field($user->oldusername, 'username');
606             } else {
607                 $oldusername = $user->oldusername;
608             }
610             // No guessing when looking for old username, it must be exact match.
611             if ($olduser = $DB->get_record('user',
612                     ['username' => $oldusername, 'mnethostid' => $CFG->mnet_localhost_id])) {
613                 $this->upt->track('id', $olduser->id, 'normal', false);
614                 if (is_siteadmin($olduser->id)) {
615                     $this->upt->track('status', get_string('usernotrenamedadmin', 'error'), 'error');
616                     $this->renameerrors++;
617                     return;
618                 }
619                 $DB->set_field('user', 'username', $user->username, ['id' => $olduser->id]);
620                 $this->upt->track('username', '', 'normal', false); // Clear previous.
621                 $this->upt->track('username', s($oldusername).'-->'.s($user->username), 'info');
622                 $this->upt->track('status', get_string('userrenamed', 'tool_uploaduser'));
623                 $this->renames++;
624             } else {
625                 $this->upt->track('status', get_string('usernotrenamedmissing', 'error'), 'error');
626                 $this->renameerrors++;
627                 return;
628             }
629             $existinguser = $olduser;
630             $existinguser->username = $user->username;
631         }
633         // Can we process with update or insert?
634         $skip = false;
635         switch ($this->get_operation_type()) {
636             case UU_USER_ADDNEW:
637                 if ($existinguser) {
638                     $this->usersskipped++;
639                     $this->upt->track('status', get_string('usernotaddedregistered', 'error'), 'warning');
640                     $skip = true;
641                 }
642                 break;
644             case UU_USER_ADDINC:
645                 if ($existinguser) {
646                     // This should not happen!
647                     $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error');
648                     $this->userserrors++;
649                     $skip = true;
650                 }
651                 break;
653             case UU_USER_ADD_UPDATE:
654                 break;
656             case UU_USER_UPDATE:
657                 if (!$existinguser) {
658                     $this->usersskipped++;
659                     $this->upt->track('status', get_string('usernotupdatednotexists', 'error'), 'warning');
660                     $skip = true;
661                 }
662                 break;
664             default:
665                 // Unknown type.
666                 $skip = true;
667         }
669         if ($skip) {
670             return;
671         }
673         if ($existinguser) {
674             $user->id = $existinguser->id;
676             $this->upt->track('username', \html_writer::link(
677                 new \moodle_url('/user/profile.php', ['id' => $existinguser->id]), s($existinguser->username)), 'normal', false);
678             $this->upt->track('suspended', $this->get_string_yes_no($existinguser->suspended) , 'normal', false);
679             $this->upt->track('auth', $existinguser->auth, 'normal', false);
681             if (is_siteadmin($user->id)) {
682                 $this->upt->track('status', get_string('usernotupdatedadmin', 'error'), 'error');
683                 $this->userserrors++;
684                 return;
685             }
687             $existinguser->timemodified = time();
688             // Do NOT mess with timecreated or firstaccess here!
690             // Load existing profile data.
691             profile_load_data($existinguser);
693             $doupdate = false;
694             $dologout = false;
696             if ($this->get_update_type() != UU_UPDATE_NOCHANGES and !$remoteuser) {
697                 if (!empty($user->auth) and $user->auth !== $existinguser->auth) {
698                     $this->upt->track('auth', s($existinguser->auth).'-->'.s($user->auth), 'info', false);
699                     $existinguser->auth = $user->auth;
700                     if (!isset($this->supportedauths[$user->auth])) {
701                         $this->upt->track('auth', get_string('userauthunsupported', 'error'), 'warning');
702                     }
703                     $doupdate = true;
704                     if ($existinguser->auth === 'nologin') {
705                         $dologout = true;
706                     }
707                 }
708                 $allcolumns = array_merge($this->standardfields, $this->profilefields);
709                 foreach ($allcolumns as $column) {
710                     if ($column === 'username' or $column === 'password' or $column === 'auth' or $column === 'suspended') {
711                         // These can not be changed here.
712                         continue;
713                     }
714                     if (!property_exists($user, $column) or !property_exists($existinguser, $column)) {
715                         continue;
716                     }
717                     if ($this->get_update_type() == UU_UPDATE_MISSING) {
718                         if (!is_null($existinguser->$column) and $existinguser->$column !== '') {
719                             continue;
720                         }
721                     } else if ($this->get_update_type() == UU_UPDATE_ALLOVERRIDE) {
722                         // We override everything.
723                         null;
724                     } else if ($this->get_update_type() == UU_UPDATE_FILEOVERRIDE) {
725                         if (!empty($formdefaults[$column])) {
726                             // Do not override with form defaults.
727                             continue;
728                         }
729                     }
730                     if ($existinguser->$column !== $user->$column) {
731                         if ($column === 'email') {
732                             $select = $DB->sql_like('email', ':email', false, true, false, '|');
733                             $params = array('email' => $DB->sql_like_escape($user->email, '|'));
734                             if ($DB->record_exists_select('user', $select , $params)) {
736                                 $changeincase = \core_text::strtolower($existinguser->$column) === \core_text::strtolower(
737                                         $user->$column);
739                                 if ($changeincase) {
740                                     // If only case is different then switch to lower case and carry on.
741                                     $user->$column = \core_text::strtolower($user->$column);
742                                     continue;
743                                 } else if (!$this->get_allow_email_duplicates()) {
744                                     $this->upt->track('email', get_string('useremailduplicate', 'error'), 'error');
745                                     $this->upt->track('status', get_string('usernotupdatederror', 'error'), 'error');
746                                     $this->userserrors++;
747                                     return;
748                                 } else {
749                                     $this->upt->track('email', get_string('useremailduplicate', 'error'), 'warning');
750                                 }
751                             }
752                             if (!validate_email($user->email)) {
753                                 $this->upt->track('email', get_string('invalidemail'), 'warning');
754                             }
755                         }
757                         if ($column === 'lang') {
758                             if (empty($user->lang)) {
759                                 // Do not change to not-set value.
760                                 continue;
761                             } else if (\core_user::clean_field($user->lang, 'lang') === '') {
762                                 $this->upt->track('status', get_string('cannotfindlang', 'error', $user->lang), 'warning');
763                                 continue;
764                             }
765                         }
767                         if (in_array($column, $this->upt->columns)) {
768                             $this->upt->track($column, s($existinguser->$column).'-->'.s($user->$column), 'info', false);
769                         }
770                         $existinguser->$column = $user->$column;
771                         $doupdate = true;
772                     }
773                 }
774             }
776             try {
777                 $auth = get_auth_plugin($existinguser->auth);
778             } catch (\Exception $e) {
779                 $this->upt->track('auth', get_string('userautherror', 'error', s($existinguser->auth)), 'error');
780                 $this->upt->track('status', get_string('usernotupdatederror', 'error'), 'error');
781                 $this->userserrors++;
782                 return;
783             }
784             $isinternalauth = $auth->is_internal();
786             // Deal with suspending and activating of accounts.
787             if ($this->get_allow_suspends() and isset($user->suspended) and $user->suspended !== '') {
788                 $user->suspended = $user->suspended ? 1 : 0;
789                 if ($existinguser->suspended != $user->suspended) {
790                     $this->upt->track('suspended', '', 'normal', false);
791                     $this->upt->track('suspended',
792                         $this->get_string_yes_no($existinguser->suspended).'-->'.$this->get_string_yes_no($user->suspended),
793                         'info', false);
794                     $existinguser->suspended = $user->suspended;
795                     $doupdate = true;
796                     if ($existinguser->suspended) {
797                         $dologout = true;
798                     }
799                 }
800             }
802             // Changing of passwords is a special case
803             // do not force password changes for external auth plugins!
804             $oldpw = $existinguser->password;
806             if ($remoteuser) {
807                 // Do not mess with passwords of remote users.
808                 null;
809             } else if (!$isinternalauth) {
810                 $existinguser->password = AUTH_PASSWORD_NOT_CACHED;
811                 $this->upt->track('password', '-', 'normal', false);
812                 // Clean up prefs.
813                 unset_user_preference('create_password', $existinguser);
814                 unset_user_preference('auth_forcepasswordchange', $existinguser);
816             } else if (!empty($user->password)) {
817                 if ($this->get_update_passwords()) {
818                     // Check for passwords that we want to force users to reset next
819                     // time they log in.
820                     $errmsg = null;
821                     $weak = !check_password_policy($user->password, $errmsg, $user);
822                     if ($this->get_reset_passwords() == UU_PWRESET_ALL or
823                             ($this->get_reset_passwords() == UU_PWRESET_WEAK and $weak)) {
824                         if ($weak) {
825                             $this->weakpasswords++;
826                             $this->upt->track('password', get_string('invalidpasswordpolicy', 'error'), 'warning');
827                         }
828                         set_user_preference('auth_forcepasswordchange', 1, $existinguser);
829                     } else {
830                         unset_user_preference('auth_forcepasswordchange', $existinguser);
831                     }
832                     unset_user_preference('create_password', $existinguser); // No need to create password any more.
834                     // Use a low cost factor when generating bcrypt hash otherwise
835                     // hashing would be slow when uploading lots of users. Hashes
836                     // will be automatically updated to a higher cost factor the first
837                     // time the user logs in.
838                     $existinguser->password = hash_internal_user_password($user->password, true);
839                     $this->upt->track('password', $user->password, 'normal', false);
840                 } else {
841                     // Do not print password when not changed.
842                     $this->upt->track('password', '', 'normal', false);
843                 }
844             }
846             if ($doupdate or $existinguser->password !== $oldpw) {
847                 // We want only users that were really updated.
848                 user_update_user($existinguser, false, false);
850                 $this->upt->track('status', get_string('useraccountupdated', 'tool_uploaduser'));
851                 $this->usersupdated++;
853                 if (!$remoteuser) {
854                     // Pre-process custom profile menu fields data from csv file.
855                     $existinguser = uu_pre_process_custom_profile_data($existinguser);
856                     // Save custom profile fields data from csv file.
857                     profile_save_data($existinguser);
858                 }
860                 if ($this->get_bulk() == UU_BULK_UPDATED or $this->get_bulk() == UU_BULK_ALL) {
861                     if (!in_array($user->id, $SESSION->bulk_users)) {
862                         $SESSION->bulk_users[] = $user->id;
863                     }
864                 }
866                 // Trigger event.
867                 \core\event\user_updated::create_from_userid($existinguser->id)->trigger();
869             } else {
870                 // No user information changed.
871                 $this->upt->track('status', get_string('useraccountuptodate', 'tool_uploaduser'));
872                 $this->usersuptodate++;
874                 if ($this->get_bulk() == UU_BULK_ALL) {
875                     if (!in_array($user->id, $SESSION->bulk_users)) {
876                         $SESSION->bulk_users[] = $user->id;
877                     }
878                 }
879             }
881             if ($dologout) {
882                 \core\session\manager::kill_user_sessions($existinguser->id);
883             }
885         } else {
886             // Save the new user to the database.
887             $user->confirmed    = 1;
888             $user->timemodified = time();
889             $user->timecreated  = time();
890             $user->mnethostid   = $CFG->mnet_localhost_id; // We support ONLY local accounts here, sorry.
892             if (!isset($user->suspended) or $user->suspended === '') {
893                 $user->suspended = 0;
894             } else {
895                 $user->suspended = $user->suspended ? 1 : 0;
896             }
897             $this->upt->track('suspended', $this->get_string_yes_no($user->suspended), 'normal', false);
899             if (empty($user->auth)) {
900                 $user->auth = 'manual';
901             }
902             $this->upt->track('auth', $user->auth, 'normal', false);
904             // Do not insert record if new auth plugin does not exist!
905             try {
906                 $auth = get_auth_plugin($user->auth);
907             } catch (\Exception $e) {
908                 $this->upt->track('auth', get_string('userautherror', 'error', s($user->auth)), 'error');
909                 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error');
910                 $this->userserrors++;
911                 return;
912             }
913             if (!isset($this->supportedauths[$user->auth])) {
914                 $this->upt->track('auth', get_string('userauthunsupported', 'error'), 'warning');
915             }
917             $isinternalauth = $auth->is_internal();
919             if (empty($user->email)) {
920                 $this->upt->track('email', get_string('invalidemail'), 'error');
921                 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error');
922                 $this->userserrors++;
923                 return;
925             } else if ($DB->record_exists('user', ['email' => $user->email])) {
926                 if (!$this->get_allow_email_duplicates()) {
927                     $this->upt->track('email', get_string('useremailduplicate', 'error'), 'error');
928                     $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error');
929                     $this->userserrors++;
930                     return;
931                 } else {
932                     $this->upt->track('email', get_string('useremailduplicate', 'error'), 'warning');
933                 }
934             }
935             if (!validate_email($user->email)) {
936                 $this->upt->track('email', get_string('invalidemail'), 'warning');
937             }
939             if (empty($user->lang)) {
940                 $user->lang = '';
941             } else if (\core_user::clean_field($user->lang, 'lang') === '') {
942                 $this->upt->track('status', get_string('cannotfindlang', 'error', $user->lang), 'warning');
943                 $user->lang = '';
944             }
946             $forcechangepassword = false;
948             if ($isinternalauth) {
949                 if (empty($user->password)) {
950                     if ($this->get_create_paswords()) {
951                         $user->password = 'to be generated';
952                         $this->upt->track('password', '', 'normal', false);
953                         $this->upt->track('password', get_string('uupasswordcron', 'tool_uploaduser'), 'warning', false);
954                     } else {
955                         $this->upt->track('password', '', 'normal', false);
956                         $this->upt->track('password', get_string('missingfield', 'error', 'password'), 'error');
957                         $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error');
958                         $this->userserrors++;
959                         return;
960                     }
961                 } else {
962                     $errmsg = null;
963                     $weak = !check_password_policy($user->password, $errmsg, $user);
964                     if ($this->get_reset_passwords() == UU_PWRESET_ALL or
965                             ($this->get_reset_passwords() == UU_PWRESET_WEAK and $weak)) {
966                         if ($weak) {
967                             $this->weakpasswords++;
968                             $this->upt->track('password', get_string('invalidpasswordpolicy', 'error'), 'warning');
969                         }
970                         $forcechangepassword = true;
971                     }
972                     // Use a low cost factor when generating bcrypt hash otherwise
973                     // hashing would be slow when uploading lots of users. Hashes
974                     // will be automatically updated to a higher cost factor the first
975                     // time the user logs in.
976                     $user->password = hash_internal_user_password($user->password, true);
977                 }
978             } else {
979                 $user->password = AUTH_PASSWORD_NOT_CACHED;
980                 $this->upt->track('password', '-', 'normal', false);
981             }
983             $user->id = user_create_user($user, false, false);
984             $this->upt->track('username', \html_writer::link(
985                 new \moodle_url('/user/profile.php', ['id' => $user->id]), s($user->username)), 'normal', false);
987             // Pre-process custom profile menu fields data from csv file.
988             $user = uu_pre_process_custom_profile_data($user);
989             // Save custom profile fields data.
990             profile_save_data($user);
992             if ($forcechangepassword) {
993                 set_user_preference('auth_forcepasswordchange', 1, $user);
994             }
995             if ($user->password === 'to be generated') {
996                 set_user_preference('create_password', 1, $user);
997             }
999             // Trigger event.
1000             \core\event\user_created::create_from_userid($user->id)->trigger();
1002             $this->upt->track('status', get_string('newuser'));
1003             $this->upt->track('id', $user->id, 'normal', false);
1004             $this->usersnew++;
1006             // Make sure user context exists.
1007             \context_user::instance($user->id);
1009             if ($this->get_bulk() == UU_BULK_NEW or $this->get_bulk() == UU_BULK_ALL) {
1010                 if (!in_array($user->id, $SESSION->bulk_users)) {
1011                     $SESSION->bulk_users[] = $user->id;
1012                 }
1013             }
1014         }
1016         // Update user interests.
1017         if (isset($user->interests) && strval($user->interests) !== '') {
1018             useredit_update_interests($user, preg_split('/\s*,\s*/', $user->interests, -1, PREG_SPLIT_NO_EMPTY));
1019         }
1021         // Add to cohort first, it might trigger enrolments indirectly - do NOT create cohorts here!
1022         foreach ($this->get_file_columns() as $column) {
1023             if (!preg_match('/^cohort\d+$/', $column)) {
1024                 continue;
1025             }
1027             if (!empty($user->$column)) {
1028                 $addcohort = $user->$column;
1029                 if (!isset($this->cohorts[$addcohort])) {
1030                     if (is_number($addcohort)) {
1031                         // Only non-numeric idnumbers!
1032                         $cohort = $DB->get_record('cohort', ['id' => $addcohort]);
1033                     } else {
1034                         $cohort = $DB->get_record('cohort', ['idnumber' => $addcohort]);
1035                         if (empty($cohort) && has_capability('moodle/cohort:manage', \context_system::instance())) {
1036                             // Cohort was not found. Create a new one.
1037                             $cohortid = cohort_add_cohort((object)array(
1038                                 'idnumber' => $addcohort,
1039                                 'name' => $addcohort,
1040                                 'contextid' => \context_system::instance()->id
1041                             ));
1042                             $cohort = $DB->get_record('cohort', ['id' => $cohortid]);
1043                         }
1044                     }
1046                     if (empty($cohort)) {
1047                         $this->cohorts[$addcohort] = get_string('unknowncohort', 'core_cohort', s($addcohort));
1048                     } else if (!empty($cohort->component)) {
1049                         // Cohorts synchronised with external sources must not be modified!
1050                         $this->cohorts[$addcohort] = get_string('external', 'core_cohort');
1051                     } else {
1052                         $this->cohorts[$addcohort] = $cohort;
1053                     }
1054                 }
1056                 if (is_object($this->cohorts[$addcohort])) {
1057                     $cohort = $this->cohorts[$addcohort];
1058                     if (!$DB->record_exists('cohort_members', ['cohortid' => $cohort->id, 'userid' => $user->id])) {
1059                         cohort_add_member($cohort->id, $user->id);
1060                         // We might add special column later, for now let's abuse enrolments.
1061                         $this->upt->track('enrolments', get_string('useradded', 'core_cohort', s($cohort->name)), 'info');
1062                     }
1063                 } else {
1064                     // Error message.
1065                     $this->upt->track('enrolments', $this->cohorts[$addcohort], 'error');
1066                 }
1067             }
1068         }
1070         // Find course enrolments, groups, roles/types and enrol periods
1071         // this is again a special case, we always do this for any updated or created users.
1072         foreach ($this->get_file_columns() as $column) {
1073             if (preg_match('/^sysrole\d+$/', $column)) {
1075                 if (!empty($user->$column)) {
1076                     $sysrolename = $user->$column;
1077                     if ($sysrolename[0] == '-') {
1078                         $removing = true;
1079                         $sysrolename = substr($sysrolename, 1);
1080                     } else {
1081                         $removing = false;
1082                     }
1084                     if (array_key_exists($sysrolename, $this->sysrolecache)) {
1085                         $sysroleid = $this->sysrolecache[$sysrolename]->id;
1086                     } else {
1087                         $this->upt->track('enrolments', get_string('unknownrole', 'error', s($sysrolename)), 'error');
1088                         continue;
1089                     }
1091                     if ($removing) {
1092                         if (user_has_role_assignment($user->id, $sysroleid, SYSCONTEXTID)) {
1093                             role_unassign($sysroleid, $user->id, SYSCONTEXTID);
1094                             $this->upt->track('enrolments', get_string('unassignedsysrole',
1095                                 'tool_uploaduser', $this->sysrolecache[$sysroleid]->name), 'info');
1096                         }
1097                     } else {
1098                         if (!user_has_role_assignment($user->id, $sysroleid, SYSCONTEXTID)) {
1099                             role_assign($sysroleid, $user->id, SYSCONTEXTID);
1100                             $this->upt->track('enrolments', get_string('assignedsysrole',
1101                                 'tool_uploaduser', $this->sysrolecache[$sysroleid]->name), 'info');
1102                         }
1103                     }
1104                 }
1106                 continue;
1107             }
1108             if (!preg_match('/^course\d+$/', $column)) {
1109                 continue;
1110             }
1111             $i = substr($column, 6);
1113             if (empty($user->{'course'.$i})) {
1114                 continue;
1115             }
1116             $shortname = $user->{'course'.$i};
1117             if (!array_key_exists($shortname, $this->ccache)) {
1118                 if (!$course = $DB->get_record('course', ['shortname' => $shortname], 'id, shortname')) {
1119                     $this->upt->track('enrolments', get_string('unknowncourse', 'error', s($shortname)), 'error');
1120                     continue;
1121                 }
1122                 $this->ccache[$shortname] = $course;
1123                 $this->ccache[$shortname]->groups = null;
1124             }
1125             $courseid      = $this->ccache[$shortname]->id;
1126             $coursecontext = \context_course::instance($courseid);
1127             if (!isset($this->manualcache[$courseid])) {
1128                 $this->manualcache[$courseid] = false;
1129                 if ($this->manualenrol) {
1130                     if ($instances = enrol_get_instances($courseid, false)) {
1131                         foreach ($instances as $instance) {
1132                             if ($instance->enrol === 'manual') {
1133                                 $this->manualcache[$courseid] = $instance;
1134                                 break;
1135                             }
1136                         }
1137                     }
1138                 }
1139             }
1141             if ($courseid == SITEID) {
1142                 // Technically frontpage does not have enrolments, but only role assignments,
1143                 // let's not invent new lang strings here for this rarely used feature.
1145                 if (!empty($user->{'role'.$i})) {
1146                     $rolename = $user->{'role'.$i};
1147                     if (array_key_exists($rolename, $this->rolecache)) {
1148                         $roleid = $this->rolecache[$rolename]->id;
1149                     } else {
1150                         $this->upt->track('enrolments', get_string('unknownrole', 'error', s($rolename)), 'error');
1151                         continue;
1152                     }
1154                     role_assign($roleid, $user->id, \context_course::instance($courseid));
1156                     $a = new \stdClass();
1157                     $a->course = $shortname;
1158                     $a->role   = $this->rolecache[$roleid]->name;
1159                     $this->upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a), 'info');
1160                 }
1162             } else if ($this->manualenrol and $this->manualcache[$courseid]) {
1164                 // Find role.
1165                 $roleid = false;
1166                 if (!empty($user->{'role'.$i})) {
1167                     $rolename = $user->{'role'.$i};
1168                     if (array_key_exists($rolename, $this->rolecache)) {
1169                         $roleid = $this->rolecache[$rolename]->id;
1170                     } else {
1171                         $this->upt->track('enrolments', get_string('unknownrole', 'error', s($rolename)), 'error');
1172                         continue;
1173                     }
1175                 } else if (!empty($user->{'type'.$i})) {
1176                     // If no role, then find "old" enrolment type.
1177                     $addtype = $user->{'type'.$i};
1178                     if ($addtype < 1 or $addtype > 3) {
1179                         $this->upt->track('enrolments', get_string('error').': typeN = 1|2|3', 'error');
1180                         continue;
1181                     } else if (empty($this->formdata->{'uulegacy'.$addtype})) {
1182                         continue;
1183                     } else {
1184                         $roleid = $this->formdata->{'uulegacy'.$addtype};
1185                     }
1186                 } else {
1187                     // No role specified, use the default from manual enrol plugin.
1188                     $roleid = $this->manualcache[$courseid]->roleid;
1189                 }
1191                 if ($roleid) {
1192                     // Find duration and/or enrol status.
1193                     $timeend = 0;
1194                     $timestart = $this->today;
1195                     $status = null;
1197                     if (isset($user->{'enrolstatus'.$i})) {
1198                         $enrolstatus = $user->{'enrolstatus'.$i};
1199                         if ($enrolstatus == '') {
1200                             $status = null;
1201                         } else if ($enrolstatus === (string)ENROL_USER_ACTIVE) {
1202                             $status = ENROL_USER_ACTIVE;
1203                         } else if ($enrolstatus === (string)ENROL_USER_SUSPENDED) {
1204                             $status = ENROL_USER_SUSPENDED;
1205                         } else {
1206                             debugging('Unknown enrolment status.');
1207                         }
1208                     }
1210                     if (!empty($user->{'enroltimestart'.$i})) {
1211                         $parsedtimestart = strtotime($user->{'enroltimestart'.$i});
1212                         if ($parsedtimestart !== false) {
1213                             $timestart = $parsedtimestart;
1214                         }
1215                     }
1217                     if (!empty($user->{'enrolperiod'.$i})) {
1218                         $duration = (int)$user->{'enrolperiod'.$i} * 60 * 60 * 24; // Convert days to seconds.
1219                         if ($duration > 0) { // Sanity check.
1220                             $timeend = $timestart + $duration;
1221                         }
1222                     } else if ($this->manualcache[$courseid]->enrolperiod > 0) {
1223                         $timeend = $timestart + $this->manualcache[$courseid]->enrolperiod;
1224                     }
1226                     $this->manualenrol->enrol_user($this->manualcache[$courseid], $user->id, $roleid,
1227                         $timestart, $timeend, $status);
1229                     $a = new \stdClass();
1230                     $a->course = $shortname;
1231                     $a->role   = $this->rolecache[$roleid]->name;
1232                     $this->upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a), 'info');
1233                 }
1234             }
1236             // Find group to add to.
1237             if (!empty($user->{'group'.$i})) {
1238                 // Make sure user is enrolled into course before adding into groups.
1239                 if (!is_enrolled($coursecontext, $user->id)) {
1240                     $this->upt->track('enrolments', get_string('addedtogroupnotenrolled', '', $user->{'group'.$i}), 'error');
1241                     continue;
1242                 }
1243                 // Build group cache.
1244                 if (is_null($this->ccache[$shortname]->groups)) {
1245                     $this->ccache[$shortname]->groups = array();
1246                     if ($groups = groups_get_all_groups($courseid)) {
1247                         foreach ($groups as $gid => $group) {
1248                             $this->ccache[$shortname]->groups[$gid] = new \stdClass();
1249                             $this->ccache[$shortname]->groups[$gid]->id   = $gid;
1250                             $this->ccache[$shortname]->groups[$gid]->name = $group->name;
1251                             if (!is_numeric($group->name)) { // Only non-numeric names are supported!!!
1252                                 $this->ccache[$shortname]->groups[$group->name] = new \stdClass();
1253                                 $this->ccache[$shortname]->groups[$group->name]->id   = $gid;
1254                                 $this->ccache[$shortname]->groups[$group->name]->name = $group->name;
1255                             }
1256                         }
1257                     }
1258                 }
1259                 // Group exists?
1260                 $addgroup = $user->{'group'.$i};
1261                 if (!array_key_exists($addgroup, $this->ccache[$shortname]->groups)) {
1262                     // If group doesn't exist,  create it.
1263                     $newgroupdata = new \stdClass();
1264                     $newgroupdata->name = $addgroup;
1265                     $newgroupdata->courseid = $this->ccache[$shortname]->id;
1266                     $newgroupdata->description = '';
1267                     $gid = groups_create_group($newgroupdata);
1268                     if ($gid) {
1269                         $this->ccache[$shortname]->groups[$addgroup] = new \stdClass();
1270                         $this->ccache[$shortname]->groups[$addgroup]->id   = $gid;
1271                         $this->ccache[$shortname]->groups[$addgroup]->name = $newgroupdata->name;
1272                     } else {
1273                         $this->upt->track('enrolments', get_string('unknowngroup', 'error', s($addgroup)), 'error');
1274                         continue;
1275                     }
1276                 }
1277                 $gid   = $this->ccache[$shortname]->groups[$addgroup]->id;
1278                 $gname = $this->ccache[$shortname]->groups[$addgroup]->name;
1280                 try {
1281                     if (groups_add_member($gid, $user->id)) {
1282                         $this->upt->track('enrolments', get_string('addedtogroup', '', s($gname)), 'info');
1283                     } else {
1284                         $this->upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error');
1285                     }
1286                 } catch (\moodle_exception $e) {
1287                     $this->upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error');
1288                     continue;
1289                 }
1290             }
1291         }
1292         if (($invalid = \core_user::validate($user)) !== true) {
1293             $this->upt->track('status', get_string('invaliduserdata', 'tool_uploaduser', s($user->username)), 'warning');
1294         }
1295     }
1297     /**
1298      * Summary about the whole process (how many users created, skipped, updated, etc)
1299      *
1300      * @return array
1301      */
1302     public function get_stats() {
1303         $lines = [];
1305         if ($this->get_operation_type() != UU_USER_UPDATE) {
1306             $lines[] = get_string('userscreated', 'tool_uploaduser').': '.$this->usersnew;
1307         }
1308         if ($this->get_operation_type() == UU_USER_UPDATE or $this->get_operation_type() == UU_USER_ADD_UPDATE) {
1309             $lines[] = get_string('usersupdated', 'tool_uploaduser').': '.$this->usersupdated;
1310         }
1311         if ($this->get_allow_deletes()) {
1312             $lines[] = get_string('usersdeleted', 'tool_uploaduser').': '.$this->deletes;
1313             $lines[] = get_string('deleteerrors', 'tool_uploaduser').': '.$this->deleteerrors;
1314         }
1315         if ($this->get_allow_renames()) {
1316             $lines[] = get_string('usersrenamed', 'tool_uploaduser').': '.$this->renames;
1317             $lines[] = get_string('renameerrors', 'tool_uploaduser').': '.$this->renameerrors;
1318         }
1319         if ($usersskipped = $this->usersskipped) {
1320             $lines[] = get_string('usersskipped', 'tool_uploaduser').': '.$usersskipped;
1321         }
1322         $lines[] = get_string('usersweakpassword', 'tool_uploaduser').': '.$this->weakpasswords;
1323         $lines[] = get_string('errors', 'tool_uploaduser').': '.$this->userserrors;
1325         return $lines;
1326     }