PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/admin/tool/uploaduser/locallib.php

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