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