Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
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/>. 16 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 */ 24 25 namespace tool_uploaduser; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use context_system; 30 use tool_uploaduser\local\field_value_validators; 31 32 require_once($CFG->dirroot.'/user/profile/lib.php'); 33 require_once($CFG->dirroot.'/user/lib.php'); 34 require_once($CFG->dirroot.'/group/lib.php'); 35 require_once($CFG->dirroot.'/cohort/lib.php'); 36 require_once($CFG->libdir.'/csvlib.class.php'); 37 require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/uploaduser/locallib.php'); 38 39 /** 40 * Process CSV file with users data, this will create/update users, enrol them into courses, etc 41 * 42 * @package tool_uploaduser 43 * @copyright 2020 Moodle 44 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 45 */ 46 class process { 47 48 /** @var \csv_import_reader */ 49 protected $cir; 50 /** @var \stdClass */ 51 protected $formdata; 52 /** @var \uu_progress_tracker */ 53 protected $upt; 54 /** @var array */ 55 protected $filecolumns = null; 56 /** @var int */ 57 protected $today; 58 /** @var \enrol_plugin|null */ 59 protected $manualenrol = null; 60 /** @var array */ 61 protected $standardfields = []; 62 /** @var array */ 63 protected $profilefields = []; 64 /** @var \profile_field_base[] */ 65 protected $allprofilefields = []; 66 /** @var string|\uu_progress_tracker|null */ 67 protected $progresstrackerclass = null; 68 69 /** @var int */ 70 protected $usersnew = 0; 71 /** @var int */ 72 protected $usersupdated = 0; 73 /** @var int /not printed yet anywhere */ 74 protected $usersuptodate = 0; 75 /** @var int */ 76 protected $userserrors = 0; 77 /** @var int */ 78 protected $deletes = 0; 79 /** @var int */ 80 protected $deleteerrors = 0; 81 /** @var int */ 82 protected $renames = 0; 83 /** @var int */ 84 protected $renameerrors = 0; 85 /** @var int */ 86 protected $usersskipped = 0; 87 /** @var int */ 88 protected $weakpasswords = 0; 89 90 /** @var array course cache - do not fetch all courses here, we will not probably use them all anyway */ 91 protected $ccache = []; 92 /** @var array */ 93 protected $cohorts = []; 94 /** @var array Course roles lookup cache. */ 95 protected $rolecache = []; 96 /** @var array System roles lookup cache. */ 97 protected $sysrolecache = []; 98 /** @var array cache of used manual enrol plugins in each course */ 99 protected $manualcache = []; 100 /** @var array officially supported plugins that are enabled */ 101 protected $supportedauths = []; 102 103 /** 104 * process constructor. 105 * 106 * @param \csv_import_reader $cir 107 * @param string|null $progresstrackerclass 108 * @throws \coding_exception 109 */ 110 public function __construct(\csv_import_reader $cir, string $progresstrackerclass = null) { 111 $this->cir = $cir; 112 if ($progresstrackerclass) { 113 if (!class_exists($progresstrackerclass) || !is_subclass_of($progresstrackerclass, \uu_progress_tracker::class)) { 114 throw new \coding_exception('Progress tracker class must extend \uu_progress_tracker'); 115 } 116 $this->progresstrackerclass = $progresstrackerclass; 117 } else { 118 $this->progresstrackerclass = \uu_progress_tracker::class; 119 } 120 121 // Keep timestamp consistent. 122 $today = time(); 123 $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0); 124 $this->today = $today; 125 126 $this->sysrolecache = uu_allowed_sysroles_cache(); // System roles lookup cache. 127 $this->supportedauths = uu_supported_auths(); // Officially supported plugins that are enabled. 128 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 } 133 134 $this->find_profile_fields(); 135 $this->find_standard_fields(); 136 } 137 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', 'phone1', 'phone2', 'address', 146 'description', 'descriptionformat', 'password', 147 'auth', // Watch out when changing auth type or using external auth plugins! 148 'oldusername', // Use when renaming users - this is the original username. 149 'suspended', // 1 means suspend user account, 0 means activate user account, nothing means keep as is. 150 'theme', // Define a theme for user when 'allowuserthemes' is enabled. 151 'deleted', // 1 means delete user 152 'mnethostid', // Can not be used for adding, updating or deleting of users - only for enrolments, 153 // groups, cohorts and suspending. 154 'interests', 155 ); 156 // Include all name fields. 157 $this->standardfields = array_merge($this->standardfields, \core_user\fields::get_name_fields()); 158 } 159 160 /** 161 * Profile fields 162 */ 163 protected function find_profile_fields(): void { 164 global $CFG; 165 require_once($CFG->dirroot . '/user/profile/lib.php'); 166 $this->allprofilefields = profile_get_user_fields_with_data(0); 167 $this->profilefields = []; 168 if ($proffields = $this->allprofilefields) { 169 foreach ($proffields as $key => $proffield) { 170 $profilefieldname = 'profile_field_'.$proffield->get_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 } 180 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 } 194 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; 203 204 // Clear bulk selection. 205 if ($this->get_bulk()) { 206 $SESSION->bulk_users = array(); 207 } 208 } 209 210 /** 211 * Operation type 212 * @return int 213 */ 214 protected function get_operation_type(): int { 215 return (int)$this->formdata->uutype; 216 } 217 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 } 226 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 } 235 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 } 243 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 } 251 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 } 262 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 } 271 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 } 279 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 } 287 288 /** 289 * Setting to allow suspends 290 * @return bool 291 */ 292 protected function get_allow_suspends(): bool { 293 return !empty($this->formdata->uuallowsuspends); 294 } 295 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 } 303 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 } 313 314 /** 315 * Process the CSV file 316 */ 317 public function process() { 318 // Init csv import helper. 319 $this->cir->init(); 320 321 $classname = $this->progresstrackerclass; 322 $this->upt = new $classname(); 323 $this->upt->start(); // Start table. 324 325 $linenum = 1; // Column header is first line. 326 while ($line = $this->cir->next()) { 327 $this->upt->flush(); 328 $linenum++; 329 330 $this->upt->track('line', $linenum); 331 $this->process_line($line); 332 } 333 334 $this->upt->close(); // Close table. 335 $this->cir->close(); 336 $this->cir->cleanup(true); 337 } 338 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; 347 348 $user = new \stdClass(); 349 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 } 370 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 } 380 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 } 405 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 } 411 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 } 423 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 } 429 430 if (empty($user->mnethostid)) { 431 $user->mnethostid = $CFG->mnet_localhost_id; 432 } 433 434 return $user; 435 } 436 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; 447 448 if (!$user = $this->prepare_user_record($line)) { 449 return; 450 } 451 452 if ($existinguser = $DB->get_record('user', ['username' => $user->username, 'mnethostid' => $user->mnethostid])) { 453 $this->upt->track('id', $existinguser->id, 'normal', false); 454 } 455 456 if ($user->mnethostid == $CFG->mnet_localhost_id) { 457 $remoteuser = false; 458 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 } 464 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 } 471 472 $remoteuser = true; 473 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 } 494 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); 503 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 } 513 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 foreach ($this->allprofilefields as $field => $profilefield) { 533 if (isset($user->$field)) { 534 continue; 535 } 536 if (isset($this->formdata->$field)) { 537 // Process templates. 538 $user->$field = uu_process_template($this->formdata->$field, $user); 539 540 // Form contains key and later code expects value. 541 // Convert key to value for required profile fields. 542 if (method_exists($profilefield, 'convert_external_data')) { 543 $user->$field = $profilefield->edit_save_data_preprocess($user->$field, null); 544 } 545 546 $formdefaults[$field] = true; 547 } 548 } 549 } 550 551 // Delete user. 552 if (!empty($user->deleted)) { 553 if (!$this->get_allow_deletes() or $remoteuser or 554 !has_capability('moodle/user:delete', context_system::instance())) { 555 $this->usersskipped++; 556 $this->upt->track('status', get_string('usernotdeletedoff', 'error'), 'warning'); 557 return; 558 } 559 if ($existinguser) { 560 if (is_siteadmin($existinguser->id)) { 561 $this->upt->track('status', get_string('usernotdeletedadmin', 'error'), 'error'); 562 $this->deleteerrors++; 563 return; 564 } 565 if (delete_user($existinguser)) { 566 $this->upt->track('status', get_string('userdeleted', 'tool_uploaduser')); 567 $this->deletes++; 568 } else { 569 $this->upt->track('status', get_string('usernotdeletederror', 'error'), 'error'); 570 $this->deleteerrors++; 571 } 572 } else { 573 $this->upt->track('status', get_string('usernotdeletedmissing', 'error'), 'error'); 574 $this->deleteerrors++; 575 } 576 return; 577 } 578 // We do not need the deleted flag anymore. 579 unset($user->deleted); 580 581 // Renaming requested? 582 if (!empty($user->oldusername) ) { 583 if (!$this->get_allow_renames()) { 584 $this->usersskipped++; 585 $this->upt->track('status', get_string('usernotrenamedoff', 'error'), 'warning'); 586 return; 587 } 588 589 if ($existinguser) { 590 $this->upt->track('status', get_string('usernotrenamedexists', 'error'), 'error'); 591 $this->renameerrors++; 592 return; 593 } 594 595 if ($user->username === 'guest') { 596 $this->upt->track('status', get_string('guestnoeditprofileother', 'error'), 'error'); 597 $this->renameerrors++; 598 return; 599 } 600 601 if ($this->get_normalise_user_names()) { 602 $oldusername = \core_user::clean_field($user->oldusername, 'username'); 603 } else { 604 $oldusername = $user->oldusername; 605 } 606 607 // No guessing when looking for old username, it must be exact match. 608 if ($olduser = $DB->get_record('user', 609 ['username' => $oldusername, 'mnethostid' => $CFG->mnet_localhost_id])) { 610 $this->upt->track('id', $olduser->id, 'normal', false); 611 if (is_siteadmin($olduser->id)) { 612 $this->upt->track('status', get_string('usernotrenamedadmin', 'error'), 'error'); 613 $this->renameerrors++; 614 return; 615 } 616 $DB->set_field('user', 'username', $user->username, ['id' => $olduser->id]); 617 $this->upt->track('username', '', 'normal', false); // Clear previous. 618 $this->upt->track('username', s($oldusername).'-->'.s($user->username), 'info'); 619 $this->upt->track('status', get_string('userrenamed', 'tool_uploaduser')); 620 $this->renames++; 621 } else { 622 $this->upt->track('status', get_string('usernotrenamedmissing', 'error'), 'error'); 623 $this->renameerrors++; 624 return; 625 } 626 $existinguser = $olduser; 627 $existinguser->username = $user->username; 628 } 629 630 // Can we process with update or insert? 631 $skip = false; 632 switch ($this->get_operation_type()) { 633 case UU_USER_ADDNEW: 634 if ($existinguser) { 635 $this->usersskipped++; 636 $this->upt->track('status', get_string('usernotaddedregistered', 'error'), 'warning'); 637 $skip = true; 638 } 639 break; 640 641 case UU_USER_ADDINC: 642 if ($existinguser) { 643 // This should not happen! 644 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error'); 645 $this->userserrors++; 646 $skip = true; 647 } 648 break; 649 650 case UU_USER_ADD_UPDATE: 651 break; 652 653 case UU_USER_UPDATE: 654 if (!$existinguser) { 655 $this->usersskipped++; 656 $this->upt->track('status', get_string('usernotupdatednotexists', 'error'), 'warning'); 657 $skip = true; 658 } 659 break; 660 661 default: 662 // Unknown type. 663 $skip = true; 664 } 665 666 if ($skip) { 667 return; 668 } 669 670 if ($existinguser) { 671 $user->id = $existinguser->id; 672 673 $this->upt->track('username', \html_writer::link( 674 new \moodle_url('/user/profile.php', ['id' => $existinguser->id]), s($existinguser->username)), 'normal', false); 675 $this->upt->track('suspended', $this->get_string_yes_no($existinguser->suspended) , 'normal', false); 676 $this->upt->track('auth', $existinguser->auth, 'normal', false); 677 678 if (is_siteadmin($user->id)) { 679 $this->upt->track('status', get_string('usernotupdatedadmin', 'error'), 'error'); 680 $this->userserrors++; 681 return; 682 } 683 684 $existinguser->timemodified = time(); 685 // Do NOT mess with timecreated or firstaccess here! 686 687 // Load existing profile data. 688 profile_load_data($existinguser); 689 690 $doupdate = false; 691 $dologout = false; 692 693 if ($this->get_update_type() != UU_UPDATE_NOCHANGES and !$remoteuser) { 694 695 // Handle 'auth' column separately, the field can never be missing from a user. 696 if (!empty($user->auth) && ($user->auth !== $existinguser->auth) && 697 ($this->get_update_type() != UU_UPDATE_MISSING)) { 698 699 $this->upt->track('auth', s($existinguser->auth).'-->'.s($user->auth), 'info', false); 700 $existinguser->auth = $user->auth; 701 if (!isset($this->supportedauths[$user->auth])) { 702 $this->upt->track('auth', get_string('userauthunsupported', 'error'), 'warning'); 703 } 704 $doupdate = true; 705 if ($existinguser->auth === 'nologin') { 706 $dologout = true; 707 } 708 } 709 $allcolumns = array_merge($this->standardfields, $this->profilefields); 710 foreach ($allcolumns as $column) { 711 if ($column === 'username' or $column === 'password' or $column === 'auth' or $column === 'suspended') { 712 // These can not be changed here. 713 continue; 714 } 715 if (!property_exists($user, $column) or !property_exists($existinguser, $column)) { 716 continue; 717 } 718 if ($this->get_update_type() == UU_UPDATE_MISSING) { 719 if (!is_null($existinguser->$column) and $existinguser->$column !== '') { 720 continue; 721 } 722 } else if ($this->get_update_type() == UU_UPDATE_ALLOVERRIDE) { 723 // We override everything. 724 null; 725 } else if ($this->get_update_type() == UU_UPDATE_FILEOVERRIDE) { 726 if (!empty($formdefaults[$column])) { 727 // Do not override with form defaults. 728 continue; 729 } 730 } 731 if ($existinguser->$column !== $user->$column) { 732 if ($column === 'email') { 733 $select = $DB->sql_like('email', ':email', false, true, false, '|'); 734 $params = array('email' => $DB->sql_like_escape($user->email, '|')); 735 if ($DB->record_exists_select('user', $select , $params)) { 736 737 $changeincase = \core_text::strtolower($existinguser->$column) === \core_text::strtolower( 738 $user->$column); 739 740 if ($changeincase) { 741 // If only case is different then switch to lower case and carry on. 742 $user->$column = \core_text::strtolower($user->$column); 743 continue; 744 } else if (!$this->get_allow_email_duplicates()) { 745 $this->upt->track('email', get_string('useremailduplicate', 'error'), 'error'); 746 $this->upt->track('status', get_string('usernotupdatederror', 'error'), 'error'); 747 $this->userserrors++; 748 return; 749 } else { 750 $this->upt->track('email', get_string('useremailduplicate', 'error'), 'warning'); 751 } 752 } 753 if (!validate_email($user->email)) { 754 $this->upt->track('email', get_string('invalidemail'), 'warning'); 755 } 756 } 757 758 if ($column === 'lang') { 759 if (empty($user->lang)) { 760 // Do not change to not-set value. 761 continue; 762 } else if (\core_user::clean_field($user->lang, 'lang') === '') { 763 $this->upt->track('status', get_string('cannotfindlang', 'error', $user->lang), 'warning'); 764 continue; 765 } 766 } 767 768 if (in_array($column, $this->upt->columns)) { 769 $this->upt->track($column, s($existinguser->$column).'-->'.s($user->$column), 'info', false); 770 } 771 $existinguser->$column = $user->$column; 772 $doupdate = true; 773 } 774 } 775 } 776 777 try { 778 $auth = get_auth_plugin($existinguser->auth); 779 } catch (\Exception $e) { 780 $this->upt->track('auth', get_string('userautherror', 'error', s($existinguser->auth)), 'error'); 781 $this->upt->track('status', get_string('usernotupdatederror', 'error'), 'error'); 782 $this->userserrors++; 783 return; 784 } 785 $isinternalauth = $auth->is_internal(); 786 787 // Deal with suspending and activating of accounts. 788 if ($this->get_allow_suspends() and isset($user->suspended) and $user->suspended !== '') { 789 $user->suspended = $user->suspended ? 1 : 0; 790 if ($existinguser->suspended != $user->suspended) { 791 $this->upt->track('suspended', '', 'normal', false); 792 $this->upt->track('suspended', 793 $this->get_string_yes_no($existinguser->suspended).'-->'.$this->get_string_yes_no($user->suspended), 794 'info', false); 795 $existinguser->suspended = $user->suspended; 796 $doupdate = true; 797 if ($existinguser->suspended) { 798 $dologout = true; 799 } 800 } 801 } 802 803 // Changing of passwords is a special case 804 // do not force password changes for external auth plugins! 805 $oldpw = $existinguser->password; 806 807 if ($remoteuser) { 808 // Do not mess with passwords of remote users. 809 null; 810 } else if (!$isinternalauth) { 811 $existinguser->password = AUTH_PASSWORD_NOT_CACHED; 812 $this->upt->track('password', '-', 'normal', false); 813 // Clean up prefs. 814 unset_user_preference('create_password', $existinguser); 815 unset_user_preference('auth_forcepasswordchange', $existinguser); 816 817 } else if (!empty($user->password)) { 818 if ($this->get_update_passwords()) { 819 // Check for passwords that we want to force users to reset next 820 // time they log in. 821 $errmsg = null; 822 $weak = !check_password_policy($user->password, $errmsg, $user); 823 if ($this->get_reset_passwords() == UU_PWRESET_ALL or 824 ($this->get_reset_passwords() == UU_PWRESET_WEAK and $weak)) { 825 if ($weak) { 826 $this->weakpasswords++; 827 $this->upt->track('password', get_string('invalidpasswordpolicy', 'error'), 'warning'); 828 } 829 set_user_preference('auth_forcepasswordchange', 1, $existinguser); 830 } else { 831 unset_user_preference('auth_forcepasswordchange', $existinguser); 832 } 833 unset_user_preference('create_password', $existinguser); // No need to create password any more. 834 835 // Use a low cost factor when generating bcrypt hash otherwise 836 // hashing would be slow when uploading lots of users. Hashes 837 // will be automatically updated to a higher cost factor the first 838 // time the user logs in. 839 $existinguser->password = hash_internal_user_password($user->password, true); 840 $this->upt->track('password', $user->password, 'normal', false); 841 } else { 842 // Do not print password when not changed. 843 $this->upt->track('password', '', 'normal', false); 844 } 845 } 846 847 if ($doupdate or $existinguser->password !== $oldpw) { 848 // We want only users that were really updated. 849 user_update_user($existinguser, false, false); 850 851 $this->upt->track('status', get_string('useraccountupdated', 'tool_uploaduser')); 852 $this->usersupdated++; 853 854 if (!$remoteuser) { 855 // Pre-process custom profile menu fields data from csv file. 856 $existinguser = uu_pre_process_custom_profile_data($existinguser); 857 // Save custom profile fields data from csv file. 858 profile_save_data($existinguser); 859 } 860 861 if ($this->get_bulk() == UU_BULK_UPDATED or $this->get_bulk() == UU_BULK_ALL) { 862 if (!in_array($user->id, $SESSION->bulk_users)) { 863 $SESSION->bulk_users[] = $user->id; 864 } 865 } 866 867 // Trigger event. 868 \core\event\user_updated::create_from_userid($existinguser->id)->trigger(); 869 870 } else { 871 // No user information changed. 872 $this->upt->track('status', get_string('useraccountuptodate', 'tool_uploaduser')); 873 $this->usersuptodate++; 874 875 if ($this->get_bulk() == UU_BULK_ALL) { 876 if (!in_array($user->id, $SESSION->bulk_users)) { 877 $SESSION->bulk_users[] = $user->id; 878 } 879 } 880 } 881 882 if ($dologout) { 883 \core\session\manager::kill_user_sessions($existinguser->id); 884 } 885 886 } else { 887 // Save the new user to the database. 888 $user->confirmed = 1; 889 $user->timemodified = time(); 890 $user->timecreated = time(); 891 $user->mnethostid = $CFG->mnet_localhost_id; // We support ONLY local accounts here, sorry. 892 893 if (!isset($user->suspended) or $user->suspended === '') { 894 $user->suspended = 0; 895 } else { 896 $user->suspended = $user->suspended ? 1 : 0; 897 } 898 $this->upt->track('suspended', $this->get_string_yes_no($user->suspended), 'normal', false); 899 900 if (empty($user->auth)) { 901 $user->auth = 'manual'; 902 } 903 $this->upt->track('auth', $user->auth, 'normal', false); 904 905 // Do not insert record if new auth plugin does not exist! 906 try { 907 $auth = get_auth_plugin($user->auth); 908 } catch (\Exception $e) { 909 $this->upt->track('auth', get_string('userautherror', 'error', s($user->auth)), 'error'); 910 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error'); 911 $this->userserrors++; 912 return; 913 } 914 if (!isset($this->supportedauths[$user->auth])) { 915 $this->upt->track('auth', get_string('userauthunsupported', 'error'), 'warning'); 916 } 917 918 $isinternalauth = $auth->is_internal(); 919 920 if (empty($user->email)) { 921 $this->upt->track('email', get_string('invalidemail'), 'error'); 922 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error'); 923 $this->userserrors++; 924 return; 925 926 } else if ($DB->record_exists('user', ['email' => $user->email])) { 927 if (!$this->get_allow_email_duplicates()) { 928 $this->upt->track('email', get_string('useremailduplicate', 'error'), 'error'); 929 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error'); 930 $this->userserrors++; 931 return; 932 } else { 933 $this->upt->track('email', get_string('useremailduplicate', 'error'), 'warning'); 934 } 935 } 936 if (!validate_email($user->email)) { 937 $this->upt->track('email', get_string('invalidemail'), 'warning'); 938 } 939 940 if (empty($user->lang)) { 941 $user->lang = ''; 942 } else if (\core_user::clean_field($user->lang, 'lang') === '') { 943 $this->upt->track('status', get_string('cannotfindlang', 'error', $user->lang), 'warning'); 944 $user->lang = ''; 945 } 946 947 $forcechangepassword = false; 948 949 if ($isinternalauth) { 950 if (empty($user->password)) { 951 if ($this->get_create_paswords()) { 952 $user->password = 'to be generated'; 953 $this->upt->track('password', '', 'normal', false); 954 $this->upt->track('password', get_string('uupasswordcron', 'tool_uploaduser'), 'warning', false); 955 } else { 956 $this->upt->track('password', '', 'normal', false); 957 $this->upt->track('password', get_string('missingfield', 'error', 'password'), 'error'); 958 $this->upt->track('status', get_string('usernotaddederror', 'error'), 'error'); 959 $this->userserrors++; 960 return; 961 } 962 } else { 963 $errmsg = null; 964 $weak = !check_password_policy($user->password, $errmsg, $user); 965 if ($this->get_reset_passwords() == UU_PWRESET_ALL or 966 ($this->get_reset_passwords() == UU_PWRESET_WEAK and $weak)) { 967 if ($weak) { 968 $this->weakpasswords++; 969 $this->upt->track('password', get_string('invalidpasswordpolicy', 'error'), 'warning'); 970 } 971 $forcechangepassword = true; 972 } 973 // Use a low cost factor when generating bcrypt hash otherwise 974 // hashing would be slow when uploading lots of users. Hashes 975 // will be automatically updated to a higher cost factor the first 976 // time the user logs in. 977 $user->password = hash_internal_user_password($user->password, true); 978 } 979 } else { 980 $user->password = AUTH_PASSWORD_NOT_CACHED; 981 $this->upt->track('password', '-', 'normal', false); 982 } 983 984 $user->id = user_create_user($user, false, false); 985 $this->upt->track('username', \html_writer::link( 986 new \moodle_url('/user/profile.php', ['id' => $user->id]), s($user->username)), 'normal', false); 987 988 // Pre-process custom profile menu fields data from csv file. 989 $user = uu_pre_process_custom_profile_data($user); 990 // Save custom profile fields data. 991 profile_save_data($user); 992 993 if ($forcechangepassword) { 994 set_user_preference('auth_forcepasswordchange', 1, $user); 995 } 996 if ($user->password === 'to be generated') { 997 set_user_preference('create_password', 1, $user); 998 } 999 1000 // Trigger event. 1001 \core\event\user_created::create_from_userid($user->id)->trigger(); 1002 1003 $this->upt->track('status', get_string('newuser')); 1004 $this->upt->track('id', $user->id, 'normal', false); 1005 $this->usersnew++; 1006 1007 // Make sure user context exists. 1008 \context_user::instance($user->id); 1009 1010 if ($this->get_bulk() == UU_BULK_NEW or $this->get_bulk() == UU_BULK_ALL) { 1011 if (!in_array($user->id, $SESSION->bulk_users)) { 1012 $SESSION->bulk_users[] = $user->id; 1013 } 1014 } 1015 } 1016 1017 // Update user interests. 1018 if (isset($user->interests) && strval($user->interests) !== '') { 1019 useredit_update_interests($user, preg_split('/\s*,\s*/', $user->interests, -1, PREG_SPLIT_NO_EMPTY)); 1020 } 1021 1022 // Add to cohort first, it might trigger enrolments indirectly - do NOT create cohorts here! 1023 foreach ($this->get_file_columns() as $column) { 1024 if (!preg_match('/^cohort\d+$/', $column)) { 1025 continue; 1026 } 1027 1028 if (!empty($user->$column)) { 1029 $addcohort = $user->$column; 1030 if (!isset($this->cohorts[$addcohort])) { 1031 if (is_number($addcohort)) { 1032 // Only non-numeric idnumbers! 1033 $cohort = $DB->get_record('cohort', ['id' => $addcohort]); 1034 } else { 1035 $cohort = $DB->get_record('cohort', ['idnumber' => $addcohort]); 1036 if (empty($cohort) && has_capability('moodle/cohort:manage', \context_system::instance())) { 1037 // Cohort was not found. Create a new one. 1038 $cohortid = cohort_add_cohort((object)array( 1039 'idnumber' => $addcohort, 1040 'name' => $addcohort, 1041 'contextid' => \context_system::instance()->id 1042 )); 1043 $cohort = $DB->get_record('cohort', ['id' => $cohortid]); 1044 } 1045 } 1046 1047 if (empty($cohort)) { 1048 $this->cohorts[$addcohort] = get_string('unknowncohort', 'core_cohort', s($addcohort)); 1049 } else if (!empty($cohort->component)) { 1050 // Cohorts synchronised with external sources must not be modified! 1051 $this->cohorts[$addcohort] = get_string('external', 'core_cohort'); 1052 } else { 1053 $this->cohorts[$addcohort] = $cohort; 1054 } 1055 } 1056 1057 if (is_object($this->cohorts[$addcohort])) { 1058 $cohort = $this->cohorts[$addcohort]; 1059 if (!$DB->record_exists('cohort_members', ['cohortid' => $cohort->id, 'userid' => $user->id])) { 1060 cohort_add_member($cohort->id, $user->id); 1061 // We might add special column later, for now let's abuse enrolments. 1062 $this->upt->track('enrolments', get_string('useradded', 'core_cohort', s($cohort->name)), 'info'); 1063 } 1064 } else { 1065 // Error message. 1066 $this->upt->track('enrolments', $this->cohorts[$addcohort], 'error'); 1067 } 1068 } 1069 } 1070 1071 // Find course enrolments, groups, roles/types and enrol periods 1072 // this is again a special case, we always do this for any updated or created users. 1073 foreach ($this->get_file_columns() as $column) { 1074 if (preg_match('/^sysrole\d+$/', $column)) { 1075 1076 if (!empty($user->$column)) { 1077 $sysrolename = $user->$column; 1078 if ($sysrolename[0] == '-') { 1079 $removing = true; 1080 $sysrolename = substr($sysrolename, 1); 1081 } else { 1082 $removing = false; 1083 } 1084 1085 if (array_key_exists($sysrolename, $this->sysrolecache)) { 1086 $sysroleid = $this->sysrolecache[$sysrolename]->id; 1087 } else { 1088 $this->upt->track('enrolments', get_string('unknownrole', 'error', s($sysrolename)), 'error'); 1089 continue; 1090 } 1091 1092 if ($removing) { 1093 if (user_has_role_assignment($user->id, $sysroleid, SYSCONTEXTID)) { 1094 role_unassign($sysroleid, $user->id, SYSCONTEXTID); 1095 $this->upt->track('enrolments', get_string('unassignedsysrole', 1096 'tool_uploaduser', $this->sysrolecache[$sysroleid]->name), 'info'); 1097 } 1098 } else { 1099 if (!user_has_role_assignment($user->id, $sysroleid, SYSCONTEXTID)) { 1100 role_assign($sysroleid, $user->id, SYSCONTEXTID); 1101 $this->upt->track('enrolments', get_string('assignedsysrole', 1102 'tool_uploaduser', $this->sysrolecache[$sysroleid]->name), 'info'); 1103 } 1104 } 1105 } 1106 1107 continue; 1108 } 1109 if (!preg_match('/^course\d+$/', $column)) { 1110 continue; 1111 } 1112 $i = substr($column, 6); 1113 1114 if (empty($user->{'course'.$i})) { 1115 continue; 1116 } 1117 $shortname = $user->{'course'.$i}; 1118 if (!array_key_exists($shortname, $this->ccache)) { 1119 if (!$course = $DB->get_record('course', ['shortname' => $shortname], 'id, shortname')) { 1120 $this->upt->track('enrolments', get_string('unknowncourse', 'error', s($shortname)), 'error'); 1121 continue; 1122 } 1123 $this->ccache[$shortname] = $course; 1124 $this->ccache[$shortname]->groups = null; 1125 } 1126 $courseid = $this->ccache[$shortname]->id; 1127 $coursecontext = \context_course::instance($courseid); 1128 if (!isset($this->manualcache[$courseid])) { 1129 $this->manualcache[$courseid] = false; 1130 if ($this->manualenrol) { 1131 if ($instances = enrol_get_instances($courseid, false)) { 1132 foreach ($instances as $instance) { 1133 if ($instance->enrol === 'manual') { 1134 $this->manualcache[$courseid] = $instance; 1135 break; 1136 } 1137 } 1138 } 1139 } 1140 } 1141 1142 if (!array_key_exists($courseid, $this->rolecache)) { 1143 $this->rolecache[$courseid] = uu_allowed_roles_cache(null, (int)$courseid); 1144 } 1145 1146 if ($courseid == SITEID) { 1147 // Technically frontpage does not have enrolments, but only role assignments, 1148 // let's not invent new lang strings here for this rarely used feature. 1149 1150 if (!empty($user->{'role'.$i})) { 1151 $rolename = $user->{'role'.$i}; 1152 if (array_key_exists($rolename, $this->rolecache[$courseid]) ) { 1153 $roleid = $this->rolecache[$courseid][$rolename]->id; 1154 } else { 1155 $this->upt->track('enrolments', get_string('unknownrole', 'error', s($rolename)), 'error'); 1156 continue; 1157 } 1158 1159 role_assign($roleid, $user->id, \context_course::instance($courseid)); 1160 1161 $a = new \stdClass(); 1162 $a->course = $shortname; 1163 $a->role = $this->rolecache[$courseid][$roleid]->name; 1164 $this->upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a), 'info'); 1165 } 1166 1167 } else if ($this->manualenrol and $this->manualcache[$courseid]) { 1168 1169 // Find role. 1170 $roleid = false; 1171 if (!empty($user->{'role'.$i})) { 1172 $rolename = $user->{'role'.$i}; 1173 if (array_key_exists($rolename, $this->rolecache[$courseid])) { 1174 $roleid = $this->rolecache[$courseid][$rolename]->id; 1175 } else { 1176 $this->upt->track('enrolments', get_string('unknownrole', 'error', s($rolename)), 'error'); 1177 continue; 1178 } 1179 1180 } else if (!empty($user->{'type'.$i})) { 1181 // If no role, then find "old" enrolment type. 1182 $addtype = $user->{'type'.$i}; 1183 if ($addtype < 1 or $addtype > 3) { 1184 $this->upt->track('enrolments', get_string('error').': typeN = 1|2|3', 'error'); 1185 continue; 1186 } else if (empty($this->formdata->{'uulegacy'.$addtype})) { 1187 continue; 1188 } else { 1189 $roleid = $this->formdata->{'uulegacy'.$addtype}; 1190 } 1191 } else { 1192 // No role specified, use the default from manual enrol plugin. 1193 $defaultenrolroleid = (int)$this->manualcache[$courseid]->roleid; 1194 // Validate the current user can assign this role. 1195 if (array_key_exists($defaultenrolroleid, $this->rolecache[$courseid]) ) { 1196 $roleid = $defaultenrolroleid; 1197 } else { 1198 $role = $DB->get_record('role', ['id' => $defaultenrolroleid]); 1199 $this->upt->track('enrolments', get_string('unknownrole', 'error', s($role->shortname)), 'error'); 1200 continue; 1201 } 1202 } 1203 1204 if ($roleid) { 1205 // Find duration and/or enrol status. 1206 $timeend = 0; 1207 $timestart = $this->today; 1208 $status = null; 1209 1210 if (isset($user->{'enrolstatus'.$i})) { 1211 $enrolstatus = $user->{'enrolstatus'.$i}; 1212 if ($enrolstatus == '') { 1213 $status = null; 1214 } else if ($enrolstatus === (string)ENROL_USER_ACTIVE) { 1215 $status = ENROL_USER_ACTIVE; 1216 } else if ($enrolstatus === (string)ENROL_USER_SUSPENDED) { 1217 $status = ENROL_USER_SUSPENDED; 1218 } else { 1219 debugging('Unknown enrolment status.'); 1220 } 1221 } 1222 1223 if (!empty($user->{'enroltimestart'.$i})) { 1224 $parsedtimestart = strtotime($user->{'enroltimestart'.$i}); 1225 if ($parsedtimestart !== false) { 1226 $timestart = $parsedtimestart; 1227 } 1228 } 1229 1230 if (!empty($user->{'enrolperiod'.$i})) { 1231 $duration = (int)$user->{'enrolperiod'.$i} * 60 * 60 * 24; // Convert days to seconds. 1232 if ($duration > 0) { // Sanity check. 1233 $timeend = $timestart + $duration; 1234 } 1235 } else if ($this->manualcache[$courseid]->enrolperiod > 0) { 1236 $timeend = $timestart + $this->manualcache[$courseid]->enrolperiod; 1237 } 1238 1239 $this->manualenrol->enrol_user($this->manualcache[$courseid], $user->id, $roleid, 1240 $timestart, $timeend, $status); 1241 1242 $a = new \stdClass(); 1243 $a->course = $shortname; 1244 $a->role = $this->rolecache[$courseid][$roleid]->name; 1245 $this->upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a), 'info'); 1246 } 1247 } 1248 1249 // Find group to add to. 1250 if (!empty($user->{'group'.$i})) { 1251 // Make sure user is enrolled into course before adding into groups. 1252 if (!is_enrolled($coursecontext, $user->id)) { 1253 $this->upt->track('enrolments', get_string('addedtogroupnotenrolled', '', $user->{'group'.$i}), 'error'); 1254 continue; 1255 } 1256 // Build group cache. 1257 if (is_null($this->ccache[$shortname]->groups)) { 1258 $this->ccache[$shortname]->groups = array(); 1259 if ($groups = groups_get_all_groups($courseid)) { 1260 foreach ($groups as $gid => $group) { 1261 $this->ccache[$shortname]->groups[$gid] = new \stdClass(); 1262 $this->ccache[$shortname]->groups[$gid]->id = $gid; 1263 $this->ccache[$shortname]->groups[$gid]->name = $group->name; 1264 if (!is_numeric($group->name)) { // Only non-numeric names are supported!!! 1265 $this->ccache[$shortname]->groups[$group->name] = new \stdClass(); 1266 $this->ccache[$shortname]->groups[$group->name]->id = $gid; 1267 $this->ccache[$shortname]->groups[$group->name]->name = $group->name; 1268 } 1269 } 1270 } 1271 } 1272 // Group exists? 1273 $addgroup = $user->{'group'.$i}; 1274 if (!array_key_exists($addgroup, $this->ccache[$shortname]->groups)) { 1275 // If group doesn't exist, create it. 1276 $newgroupdata = new \stdClass(); 1277 $newgroupdata->name = $addgroup; 1278 $newgroupdata->courseid = $this->ccache[$shortname]->id; 1279 $newgroupdata->description = ''; 1280 $gid = groups_create_group($newgroupdata); 1281 if ($gid) { 1282 $this->ccache[$shortname]->groups[$addgroup] = new \stdClass(); 1283 $this->ccache[$shortname]->groups[$addgroup]->id = $gid; 1284 $this->ccache[$shortname]->groups[$addgroup]->name = $newgroupdata->name; 1285 } else { 1286 $this->upt->track('enrolments', get_string('unknowngroup', 'error', s($addgroup)), 'error'); 1287 continue; 1288 } 1289 } 1290 $gid = $this->ccache[$shortname]->groups[$addgroup]->id; 1291 $gname = $this->ccache[$shortname]->groups[$addgroup]->name; 1292 1293 try { 1294 if (groups_add_member($gid, $user->id)) { 1295 $this->upt->track('enrolments', get_string('addedtogroup', '', s($gname)), 'info'); 1296 } else { 1297 $this->upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error'); 1298 } 1299 } catch (\moodle_exception $e) { 1300 $this->upt->track('enrolments', get_string('addedtogroupnot', '', s($gname)), 'error'); 1301 continue; 1302 } 1303 } 1304 } 1305 if (($invalid = \core_user::validate($user)) !== true) { 1306 $this->upt->track('status', get_string('invaliduserdata', 'tool_uploaduser', s($user->username)), 'warning'); 1307 } 1308 } 1309 1310 /** 1311 * Summary about the whole process (how many users created, skipped, updated, etc) 1312 * 1313 * @return array 1314 */ 1315 public function get_stats() { 1316 $lines = []; 1317 1318 if ($this->get_operation_type() != UU_USER_UPDATE) { 1319 $lines[] = get_string('userscreated', 'tool_uploaduser').': '.$this->usersnew; 1320 } 1321 if ($this->get_operation_type() == UU_USER_UPDATE or $this->get_operation_type() == UU_USER_ADD_UPDATE) { 1322 $lines[] = get_string('usersupdated', 'tool_uploaduser').': '.$this->usersupdated; 1323 } 1324 if ($this->get_allow_deletes()) { 1325 $lines[] = get_string('usersdeleted', 'tool_uploaduser').': '.$this->deletes; 1326 $lines[] = get_string('deleteerrors', 'tool_uploaduser').': '.$this->deleteerrors; 1327 } 1328 if ($this->get_allow_renames()) { 1329 $lines[] = get_string('usersrenamed', 'tool_uploaduser').': '.$this->renames; 1330 $lines[] = get_string('renameerrors', 'tool_uploaduser').': '.$this->renameerrors; 1331 } 1332 if ($usersskipped = $this->usersskipped) { 1333 $lines[] = get_string('usersskipped', 'tool_uploaduser').': '.$usersskipped; 1334 } 1335 $lines[] = get_string('usersweakpassword', 'tool_uploaduser').': '.$this->weakpasswords; 1336 $lines[] = get_string('errors', 'tool_uploaduser').': '.$this->userserrors; 1337 1338 return $lines; 1339 } 1340 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body