See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 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 * Bulk user registration functions 19 * 20 * @package tool 21 * @subpackage uploaduser 22 * @copyright 2004 onwards Martin Dougiamas (http://dougiamas.com) 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 define('UU_USER_ADDNEW', 0); 29 define('UU_USER_ADDINC', 1); 30 define('UU_USER_ADD_UPDATE', 2); 31 define('UU_USER_UPDATE', 3); 32 33 define('UU_UPDATE_NOCHANGES', 0); 34 define('UU_UPDATE_FILEOVERRIDE', 1); 35 define('UU_UPDATE_ALLOVERRIDE', 2); 36 define('UU_UPDATE_MISSING', 3); 37 38 define('UU_BULK_NONE', 0); 39 define('UU_BULK_NEW', 1); 40 define('UU_BULK_UPDATED', 2); 41 define('UU_BULK_ALL', 3); 42 43 define('UU_PWRESET_NONE', 0); 44 define('UU_PWRESET_WEAK', 1); 45 define('UU_PWRESET_ALL', 2); 46 47 /** 48 * Tracking of processed users. 49 * 50 * This class prints user information into a html table. 51 * 52 * @package core 53 * @subpackage admin 54 * @copyright 2007 Petr Skoda {@link http://skodak.org} 55 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 56 */ 57 class uu_progress_tracker { 58 /** @var array */ 59 protected $_row; 60 61 /** 62 * The columns shown on the table. 63 * @var array 64 */ 65 public $columns = []; 66 /** @var array column headers */ 67 protected $headers = []; 68 69 /** 70 * uu_progress_tracker constructor. 71 */ 72 public function __construct() { 73 $this->headers = [ 74 'status' => get_string('status'), 75 'line' => get_string('uucsvline', 'tool_uploaduser'), 76 'id' => 'ID', 77 'username' => get_string('username'), 78 'firstname' => get_string('firstname'), 79 'lastname' => get_string('lastname'), 80 'email' => get_string('email'), 81 'password' => get_string('password'), 82 'auth' => get_string('authentication'), 83 'enrolments' => get_string('enrolments', 'enrol'), 84 'suspended' => get_string('suspended', 'auth'), 85 'theme' => get_string('theme'), 86 'deleted' => get_string('delete'), 87 ]; 88 $this->columns = array_keys($this->headers); 89 } 90 91 /** 92 * Print table header. 93 * @return void 94 */ 95 public function start() { 96 $ci = 0; 97 echo '<table id="uuresults" class="generaltable boxaligncenter flexible-wrap" summary="'.get_string('uploadusersresult', 'tool_uploaduser').'">'; 98 echo '<tr class="heading r0">'; 99 foreach ($this->headers as $key => $header) { 100 echo '<th class="header c'.$ci++.'" scope="col">'.$header.'</th>'; 101 } 102 echo '</tr>'; 103 $this->_row = null; 104 } 105 106 /** 107 * Flush previous line and start a new one. 108 * @return void 109 */ 110 public function flush() { 111 if (empty($this->_row) or empty($this->_row['line']['normal'])) { 112 // Nothing to print - each line has to have at least number 113 $this->_row = array(); 114 foreach ($this->columns as $col) { 115 $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>''); 116 } 117 return; 118 } 119 $ci = 0; 120 $ri = 1; 121 echo '<tr class="r'.$ri.'">'; 122 foreach ($this->_row as $key=>$field) { 123 foreach ($field as $type=>$content) { 124 if ($field[$type] !== '') { 125 $field[$type] = '<span class="uu'.$type.'">'.$field[$type].'</span>'; 126 } else { 127 unset($field[$type]); 128 } 129 } 130 echo '<td class="cell c'.$ci++.'">'; 131 if (!empty($field)) { 132 echo implode('<br />', $field); 133 } else { 134 echo ' '; 135 } 136 echo '</td>'; 137 } 138 echo '</tr>'; 139 foreach ($this->columns as $col) { 140 $this->_row[$col] = array('normal'=>'', 'info'=>'', 'warning'=>'', 'error'=>''); 141 } 142 } 143 144 /** 145 * Add tracking info 146 * @param string $col name of column 147 * @param string $msg message 148 * @param string $level 'normal', 'warning' or 'error' 149 * @param bool $merge true means add as new line, false means override all previous text of the same type 150 * @return void 151 */ 152 public function track($col, $msg, $level = 'normal', $merge = true) { 153 if (empty($this->_row)) { 154 $this->flush(); //init arrays 155 } 156 if (!in_array($col, $this->columns)) { 157 debugging('Incorrect column:'.$col); 158 return; 159 } 160 if ($merge) { 161 if ($this->_row[$col][$level] != '') { 162 $this->_row[$col][$level] .='<br />'; 163 } 164 $this->_row[$col][$level] .= $msg; 165 } else { 166 $this->_row[$col][$level] = $msg; 167 } 168 } 169 170 /** 171 * Print the table end 172 * @return void 173 */ 174 public function close() { 175 $this->flush(); 176 echo '</table>'; 177 } 178 } 179 180 /** 181 * Validation callback function - verified the column line of csv file. 182 * Converts standard column names to lowercase. 183 * @param csv_import_reader $cir 184 * @param array $stdfields standard user fields 185 * @param array $profilefields custom profile fields 186 * @param moodle_url $returnurl return url in case of any error 187 * @return array list of fields 188 */ 189 function uu_validate_user_upload_columns(csv_import_reader $cir, $stdfields, $profilefields, moodle_url $returnurl) { 190 $columns = $cir->get_columns(); 191 192 if (empty($columns)) { 193 $cir->close(); 194 $cir->cleanup(); 195 throw new \moodle_exception('cannotreadtmpfile', 'error', $returnurl); 196 } 197 if (count($columns) < 2) { 198 $cir->close(); 199 $cir->cleanup(); 200 throw new \moodle_exception('csvfewcolumns', 'error', $returnurl); 201 } 202 203 // test columns 204 $processed = array(); 205 $acceptedfields = [ 206 'category', 207 'categoryrole', 208 'cohort', 209 'course', 210 'enrolperiod', 211 'enrolstatus', 212 'enroltimestart', 213 'group', 214 'role', 215 'sysrole', 216 'type', 217 ]; 218 $specialfieldsregex = "/^(" . implode('|', $acceptedfields) . ")\d+$/"; 219 220 foreach ($columns as $key=>$unused) { 221 $field = $columns[$key]; 222 $field = trim($field); 223 $lcfield = core_text::strtolower($field); 224 if (in_array($field, $stdfields) or in_array($lcfield, $stdfields)) { 225 // standard fields are only lowercase 226 $newfield = $lcfield; 227 228 } else if (in_array($field, $profilefields)) { 229 // exact profile field name match - these are case sensitive 230 $newfield = $field; 231 232 } else if (in_array($lcfield, $profilefields)) { 233 // hack: somebody wrote uppercase in csv file, but the system knows only lowercase profile field 234 $newfield = $lcfield; 235 236 } else if (preg_match($specialfieldsregex, $lcfield)) { 237 // special fields for enrolments 238 $newfield = $lcfield; 239 240 } else { 241 $cir->close(); 242 $cir->cleanup(); 243 throw new \moodle_exception('invalidfieldname', 'error', $returnurl, $field); 244 } 245 if (in_array($newfield, $processed)) { 246 $cir->close(); 247 $cir->cleanup(); 248 throw new \moodle_exception('duplicatefieldname', 'error', $returnurl, $newfield); 249 } 250 $processed[$key] = $newfield; 251 } 252 253 return $processed; 254 } 255 256 /** 257 * Increments username - increments trailing number or adds it if not present. 258 * Varifies that the new username does not exist yet 259 * @param string $username 260 * @return incremented username which does not exist yet 261 */ 262 function uu_increment_username($username) { 263 global $DB, $CFG; 264 265 if (!preg_match_all('/(.*?)([0-9]+)$/', $username, $matches)) { 266 $username = $username.'2'; 267 } else { 268 $username = $matches[1][0].($matches[2][0]+1); 269 } 270 271 if ($DB->record_exists('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) { 272 return uu_increment_username($username); 273 } else { 274 return $username; 275 } 276 } 277 278 /** 279 * Check if default field contains templates and apply them. 280 * @param string template - potential tempalte string 281 * @param object user object- we need username, firstname and lastname 282 * @return string field value 283 */ 284 function uu_process_template($template, $user) { 285 if (is_array($template)) { 286 // hack for for support of text editors with format 287 $t = $template['text']; 288 } else { 289 $t = $template; 290 } 291 if (strpos($t, '%') === false) { 292 return $template; 293 } 294 295 $username = isset($user->username) ? $user->username : ''; 296 $firstname = isset($user->firstname) ? $user->firstname : ''; 297 $lastname = isset($user->lastname) ? $user->lastname : ''; 298 299 $callback = partial('uu_process_template_callback', $username, $firstname, $lastname); 300 301 $result = preg_replace_callback('/(?<!%)%([+-~])?(\d)*([flu])/', $callback, $t); 302 303 if (is_null($result)) { 304 return $template; //error during regex processing?? 305 } 306 307 if (is_array($template)) { 308 $template['text'] = $result; 309 return $t; 310 } else { 311 return $result; 312 } 313 } 314 315 /** 316 * Internal callback function. 317 */ 318 function uu_process_template_callback($username, $firstname, $lastname, $block) { 319 switch ($block[3]) { 320 case 'u': 321 $repl = $username; 322 break; 323 case 'f': 324 $repl = $firstname; 325 break; 326 case 'l': 327 $repl = $lastname; 328 break; 329 default: 330 return $block[0]; 331 } 332 333 switch ($block[1]) { 334 case '+': 335 $repl = core_text::strtoupper($repl); 336 break; 337 case '-': 338 $repl = core_text::strtolower($repl); 339 break; 340 case '~': 341 $repl = core_text::strtotitle($repl); 342 break; 343 } 344 345 if (!empty($block[2])) { 346 $repl = core_text::substr($repl, 0 , $block[2]); 347 } 348 349 return $repl; 350 } 351 352 /** 353 * Returns list of auth plugins that are enabled and known to work. 354 * 355 * If ppl want to use some other auth type they have to include it 356 * in the CSV file next on each line. 357 * 358 * @return array type=>name 359 */ 360 function uu_supported_auths() { 361 // Get all the enabled plugins. 362 $plugins = get_enabled_auth_plugins(); 363 $choices = array(); 364 foreach ($plugins as $plugin) { 365 $objplugin = get_auth_plugin($plugin); 366 // If the plugin can not be manually set skip it. 367 if (!$objplugin->can_be_manually_set()) { 368 continue; 369 } 370 $choices[$plugin] = get_string('pluginname', "auth_{$plugin}"); 371 } 372 373 return $choices; 374 } 375 376 /** 377 * Returns list of roles that are assignable in courses 378 * @return array 379 */ 380 function uu_allowed_roles() { 381 // let's cheat a bit, frontpage is guaranteed to exist and has the same list of roles ;-) 382 $roles = get_assignable_roles(context_course::instance(SITEID), ROLENAME_ORIGINALANDSHORT); 383 return array_reverse($roles, true); 384 } 385 386 /** 387 * Returns assignable roles for current user using short role name and role ID as index. 388 * This function is no longer called without parameters. 389 * 390 * @param int|null $categoryid Id of the category to get roles for. 391 * @param int|null $courseid Id of the course to get roles for. 392 * @return array 393 */ 394 function uu_allowed_roles_cache(?int $categoryid = null, ?int $courseid = null): array { 395 if (!is_null($categoryid) && !is_null($courseid)) { 396 return []; 397 } else if (is_null($categoryid) && !is_null($courseid)) { 398 $allowedroles = get_assignable_roles(context_course::instance($courseid), ROLENAME_SHORT); 399 } else if (is_null($courseid) && !is_null($categoryid)) { 400 $allowedroles = get_assignable_roles(context_coursecat::instance($categoryid), ROLENAME_SHORT); 401 } else { 402 $allowedroles = get_assignable_roles(context_course::instance(SITEID), ROLENAME_SHORT); 403 } 404 405 $rolecache = []; 406 // A role can be searched for by its ID or by its shortname. 407 foreach ($allowedroles as $rid=>$rname) { 408 $rolecache[$rid] = new stdClass(); 409 $rolecache[$rid]->id = $rid; 410 $rolecache[$rid]->name = $rname; 411 // Since numeric short names are allowed, to avoid replacement of another role, we only accept non-numeric values. 412 if (!is_numeric($rname)) { 413 $rolecache[$rname] = new stdClass(); 414 $rolecache[$rname]->id = $rid; 415 $rolecache[$rname]->name = $rname; 416 } 417 } 418 return $rolecache; 419 } 420 421 /** 422 * Returns mapping of all system roles using short role name as index. 423 * @return array 424 */ 425 function uu_allowed_sysroles_cache() { 426 $allowedroles = get_assignable_roles(context_system::instance(), ROLENAME_SHORT); 427 $rolecache = []; 428 foreach ($allowedroles as $rid => $rname) { 429 $rolecache[$rid] = new stdClass(); 430 $rolecache[$rid]->id = $rid; 431 $rolecache[$rid]->name = $rname; 432 if (!is_numeric($rname)) { // Only non-numeric shortnames are supported! 433 $rolecache[$rname] = new stdClass(); 434 $rolecache[$rname]->id = $rid; 435 $rolecache[$rname]->name = $rname; 436 } 437 } 438 return $rolecache; 439 } 440 441 /** 442 * Pre process custom profile data, and update it with corrected value 443 * 444 * @param stdClass $data user profile data 445 * @return stdClass pre-processed custom profile data 446 */ 447 function uu_pre_process_custom_profile_data($data) { 448 global $CFG; 449 require_once($CFG->dirroot . '/user/profile/lib.php'); 450 $fields = profile_get_user_fields_with_data(0); 451 452 // find custom profile fields and check if data needs to converted. 453 foreach ($data as $key => $value) { 454 if (preg_match('/^profile_field_/', $key)) { 455 $shortname = str_replace('profile_field_', '', $key); 456 if ($fields) { 457 foreach ($fields as $formfield) { 458 if ($formfield->get_shortname() === $shortname && method_exists($formfield, 'convert_external_data')) { 459 $data->$key = $formfield->convert_external_data($value); 460 } 461 } 462 } 463 } 464 } 465 return $data; 466 } 467 468 /** 469 * Checks if data provided for custom fields is correct 470 * Currently checking for custom profile field or type menu 471 * 472 * @param array $data user profile data 473 * @param array $profilefieldvalues Used to track previous profile field values to ensure uniqueness is observed 474 * @return bool true if no error else false 475 */ 476 function uu_check_custom_profile_data(&$data, array &$profilefieldvalues = []) { 477 global $CFG; 478 require_once($CFG->dirroot.'/user/profile/lib.php'); 479 480 $noerror = true; 481 $testuserid = null; 482 483 if (!empty($data['username'])) { 484 if (preg_match('/id=(.*)"/i', $data['username'], $result)) { 485 $testuserid = $result[1]; 486 } 487 } 488 $profilefields = profile_get_user_fields_with_data(0); 489 // Find custom profile fields and check if data needs to converted. 490 foreach ($data as $key => $value) { 491 if (preg_match('/^profile_field_/', $key)) { 492 $shortname = str_replace('profile_field_', '', $key); 493 foreach ($profilefields as $formfield) { 494 if ($formfield->get_shortname() === $shortname) { 495 if (method_exists($formfield, 'convert_external_data') && 496 is_null($formfield->convert_external_data($value))) { 497 $data['status'][] = get_string('invaliduserfield', 'error', $shortname); 498 $noerror = false; 499 } 500 501 // Ensure unique field value doesn't already exist in supplied data. 502 $formfieldunique = $formfield->is_unique() && ($value !== '' || $formfield->is_required()); 503 if ($formfieldunique && array_key_exists($shortname, $profilefieldvalues) && 504 (array_search($value, $profilefieldvalues[$shortname]) !== false)) { 505 506 $data['status'][] = get_string('valuealreadyused') . " ({$key})"; 507 $noerror = false; 508 } 509 510 // Check for duplicate value. 511 if (method_exists($formfield, 'edit_validate_field') ) { 512 $testuser = new stdClass(); 513 $testuser->{$key} = $value; 514 $testuser->id = $testuserid; 515 $err = $formfield->edit_validate_field($testuser); 516 if (!empty($err[$key])) { 517 $data['status'][] = $err[$key].' ('.$key.')'; 518 $noerror = false; 519 } 520 } 521 522 // Record value of unique field, so it can be compared for duplicates. 523 if ($formfieldunique) { 524 $profilefieldvalues[$shortname][] = $value; 525 } 526 } 527 } 528 } 529 } 530 return $noerror; 531 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body