PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/admin/tool/uploaduser/locallib.php

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