1<?php 2 3/** 4 * @package tikiwiki 5 */ 6// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project 7// 8// All Rights Reserved. See copyright.txt for details and a complete list of authors. 9// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 10// $Id$ 11 12$errors = []; 13 14$inputConfiguration = [ 15 [ 16 'staticKeyFilters' => [ 17 'offset' => 'int', 18 'numrows' => 'int', 19 'find' => 'text', 20 'filterEmail' => 'xss', 21 'sort_mode' => 'alnumdash', 22 'initial' => 'alpha', 23 'filterGroup' => 'text', 24 ] 25 ] 26]; 27 28 29require_once('tiki-setup.php'); 30// temporary patch: tiki_p_admin includes tiki_p_admin_users but if you don't 31// clean the temp/cache each time you sqlupgrade the perms setting is not 32// synchronous with the cache 33$access = TikiLib::lib('access'); 34$access->check_permission(['tiki_p_admin_users']); 35 36if ($tiki_p_admin != 'y') { 37 $userGroups = $userlib->get_user_groups_inclusion($user); 38 $smarty->assign_by_ref('userGroups', $userGroups); 39} else { 40 $userGroups = []; 41} 42 43/** 44 * @param $u 45 * @param $reason 46 * @return mixed 47 */ 48function discardUser($u, $reason) 49{ 50 $u['reason'] = $reason; 51 return $u; 52} 53 54function batchImportUsers() 55{ 56 global $tiki_p_admin, $prefs, $userGroups; 57 $userlib = TikiLib::lib('user'); 58 $tikilib = TikiLib::lib('tiki'); 59 $smarty = TikiLib::lib('smarty'); 60 $logslib = TikiLib::lib('logs'); 61 $access = TikiLib::lib('access'); 62 63 $fname = $_FILES['csvlist']['tmp_name']; 64 $fhandle = fopen($fname, 'r'); 65 $fields = fgetcsv($fhandle, 1000); 66 67 if (!$fields[0]) { 68 $errors[] = tra('The file has incorrect syntax or is not a CSV file'); 69 } 70 if (!in_array('login', $fields) || !in_array('email', $fields) || !in_array('password', $fields)) { 71 $errors[] = tra('The file does not have the required header:') . ' login, password, email'; 72 } 73 if (!empty($errors)) { 74 Feedback::error(['mes' => $errors]); 75 $access->redirect('tiki-adminusers.php'); 76 die; 77 } 78 79 while (!feof($fhandle)) { 80 $data = fgetcsv($fhandle, 1000); 81 if (empty($data)) { 82 continue; 83 } 84 $temp_max = count($fields); 85 for ($i = 0; $i < $temp_max; $i++) { 86 if ( 87 $fields[$i] == 'login' 88 && function_exists('mb_detect_encoding') 89 && mb_detect_encoding($data[$i], 'ASCII, UTF-8, ISO-8859-1') == 'ISO-8859-1' 90 ) { 91 $data[$i] = utf8_encode($data[$i]); 92 } 93 @$ar[$fields[$i]] = $data[$i]; 94 } 95 $userrecs[] = $ar; 96 } 97 fclose($fhandle); 98 99 if (empty($userrecs) or !is_array($userrecs)) { 100 Feedback::error(tra('No records were found. Check the file please!')); 101 $access->redirect('tiki-adminusers.php'); 102 die; 103 } 104 // whether to force password change on first login or not 105 $pass_first_login = (isset($_REQUEST['forcePasswordChange']) && $_REQUEST['forcePasswordChange'] == 'on'); 106 107 $added = 0; 108 $errors = []; 109 $discarded = []; 110 111 foreach ($userrecs as $u) { 112 $local = []; 113 $exist = false; 114 115 if ($prefs['feature_intertiki'] == 'y' && !empty($prefs['feature_intertiki_mymaster'])) { 116 if (empty($u['login']) && empty($u['email'])) { 117 $local[] = discardUser($u, tra('User login or email is required')); 118 } else { // pick up the info on the master 119 $info = $userlib->interGetUserInfo( 120 $prefs['interlist'][$prefs['feature_intertiki_mymaster']], 121 empty($u['login']) ? '' : $u['login'], 122 empty($u['email']) ? '' : $u['email'] 123 ); 124 125 if (empty($info)) { 126 $local[] = discardUser($u, tra('User does not exist on master')); 127 } else { 128 $u['login'] = $info['login']; 129 $u['email'] = $info['email']; 130 } 131 } 132 } else { 133 if (empty($u['login'])) { 134 $local[] = discardUser($u, tra('User login is required')); 135 } 136 137 if (empty($u['password'])) { 138 $u['password'] = $tikilib->genPass(); 139 } 140 if (empty($u['email'])) { 141 $local[] = discardUser($u, tra('Email is required')); 142 } 143 } 144 145 if (!empty($local)) { 146 $discarded = array_merge($discarded, $local); 147 continue; 148 } 149 150 if ($userlib->user_exists($u['login'])) { // exist on local 151 $exist = true; 152 } 153 154 if ($exist && $_REQUEST['overwrite'] == 'n') { 155 $discarded[] = discardUser($u, tra('User is duplicated')); 156 continue; 157 } 158 159 if (!$exist) { 160 if (!empty($_REQUEST['notification'])) { 161 $apass = md5($tikilib->genPass()); 162 } else { 163 $apass = ''; 164 } 165 166 $u['login'] = $userlib->add_user( 167 $u['login'], 168 $u['password'], 169 $u['email'], 170 $pass_first_login ? $u['password'] : '', 171 $pass_first_login, 172 $apass, 173 null, 174 (!empty($_REQUEST['notification']) ? 'u' : null) 175 ); 176 177 global $user; 178 $logslib->add_log('adminusers', sprintf(tra('Created account %s <%s>'), $u['login'], $u['email']), $user); 179 if (!empty($_REQUEST['notification'])) { 180 $realpass = $pass_first_login ? '' : $u['password']; 181 $userlib->send_validation_email($u['login'], $apass, $u['email'], '', '', '', 'user_creation_validation_mail', $realpass); 182 } 183 } 184 185 $userlib->set_user_fields($u); 186 if ($exist && isset($_REQUEST['overwriteGroup'])) { 187 $userlib->remove_user_from_all_groups($u['login']); 188 } 189 190 if (!empty($u['groups'])) { 191 $grps = preg_split('/(?<!,),(?!,)/', $u['groups']); 192 foreach ($grps as $grp) { 193 $grp = preg_replace('/,,/', ',', preg_replace('/^ *(.*) *$/u', "$1", $grp)); 194 $existg = false; 195 if ($userlib->group_exists($grp)) { 196 $existg = true; 197 } elseif (!empty($_REQUEST['createGroup']) && $userlib->add_group($grp)) { 198 $existg = true; 199 } 200 201 if (!$existg) { 202 $err = tra('Unknown') . ": $grp"; 203 if (!in_array($err, $errors)) { 204 $errors[] = $err; 205 } 206 } elseif ($tiki_p_admin != 'y' && !array_key_exists($grp, $userGroups)) { 207 $smarty->assign('errortype', 401); 208 $err = tra('Permission denied') . ": $grp"; 209 if (!in_array($err, $errors)) { 210 $errors[] = $err; 211 } 212 } else { 213 $userlib->assign_user_to_group($u['login'], $grp); 214 $logslib->add_log('perms', sprintf(tra('Assigned %s in group %s'), $u['login'], $grp), $user); 215 } 216 } 217 } 218 219 if (!empty($u['default_group'])) { 220 $userlib->set_default_group($u['login'], $u['default_group']); 221 } 222 223 if (!empty($u['realName'])) { 224 $tikilib->set_user_preference($u['login'], 'realName', $u['realName']); 225 } 226 $added++; 227 } 228 Feedback::success(tr('Users added:') . ' ' . $added); 229 230 if (count($discarded)) { 231 foreach ($discarded as $key => $value) { 232 $df[] = $discarded[$key]['login'] . ' (' . $discarded[$key]['reason'] . ')'; 233 } 234 Feedback::warning(['mes' => $df, 'title' => tr('%0 users not added', count($discarded))]); 235 } 236 237 if (count($errors)) { 238 array_unique($errors); 239 Feedback::error(['mes' => $errors]); 240 } 241} 242 243$auto_query_args = [ 244 'offset', 245 'numrows', 246 'find', 247 'filterEmail', 248 'sort_mode', 249 'initial', 250 'filterGroup' 251]; 252if (isset($_REQUEST['batch']) && is_uploaded_file($_FILES['csvlist']['tmp_name']) && $access->checkCsrf()) { 253 batchImportUsers(); 254 // Process the form to add a user here 255} elseif (isset($_REQUEST['newuser']) && $access->checkCsrf()) { 256 $AddUser = true;; 257 // if email validation set check if email addr is set 258 if ( 259 $prefs['login_is_email'] != 'y' && isset($_REQUEST['need_email_validation']) && 260 empty($_REQUEST['email']) 261 ) { 262 $errors[] = tra('Email validation requested but email address not set'); 263 $AddUser = false; 264 } 265 if ($_REQUEST['pass'] != $_REQUEST['passAgain']) { 266 $errors[] = tra('The passwords do not match'); 267 $AddUser = false; 268 } elseif (empty($_REQUEST['pass']) && empty($_REQUEST['genepass'])) { 269 $errors[] = tra('Password not set'); 270 $AddUser = false; 271 } 272 273 $newPass = $_POST['pass'] ? $_POST['pass'] : $_POST['genepass']; 274 // Check if the user already exists 275 276 if ($userlib->user_exists($_REQUEST['login'])) { 277 $errors[] = sprintf(tra('User %s already exists'), $_REQUEST['login']); 278 $AddUser = false; 279 } 280 if ($prefs['login_is_email'] == 'y' && !validate_email($_REQUEST['login'])) { 281 $errors[] = tra('Invalid email') . ' ' . $_REQUEST['login']; 282 $AddUser = false; 283 } 284 if (!empty($prefs['username_pattern']) && !preg_match($prefs['username_pattern'], $_REQUEST['login'])) { 285 $errors[] = tra('User login contains invalid characters.'); 286 $AddUser = false; 287 } 288 // end verify newuser info 289 if ($AddUser) { 290 $pass_first_login = (isset($_REQUEST['pass_first_login']) && $_REQUEST['pass_first_login'] == 'on'); 291 $polerr = $userlib->check_password_policy($newPass); 292 if (strlen($polerr) > 0) { 293 Feedback::error(['mes' => $polerr]); 294 } else { 295 if ($prefs['login_is_email'] == 'y' and empty($_REQUEST['email'])) { 296 $_REQUEST['email'] = $_REQUEST['login']; 297 } 298 299 $send_validation_email = false; 300 301 if (isset($_REQUEST['need_email_validation']) && $_REQUEST['need_email_validation'] == 'on') { 302 $send_validation_email = true; 303 $apass = md5($tikilib->genPass()); 304 } else { 305 $apass = ''; 306 } 307 308 if ($_REQUEST['login'] = $userlib->add_user( 309 $_REQUEST['login'], 310 $newPass, 311 $_REQUEST['email'], 312 $pass_first_login ? $newPass : '', 313 $pass_first_login, 314 $apass, 315 null, 316 ($send_validation_email ? 'u' : null) 317 )) { 318 $feedback = sprintf(tra('New user created with username %s.'), $_REQUEST['login']); 319 Feedback::success($feedback); 320 $logslib->add_log('adminusers', $feedback, $user); 321 322 if ($send_validation_email) { 323 // No need to send credentials in mail if the user is forced to choose a new password after validation 324 $realpass = $pass_first_login ? '' : $newPass; 325 $userlib->send_validation_email( 326 $_REQUEST['login'], 327 $apass, 328 $_REQUEST['email'], 329 '', 330 '', 331 '', 332 'user_creation_validation_mail', 333 $realpass 334 ); 335 } 336 337 if ($prefs['userTracker'] === 'y' && !empty($_REQUEST['insert_user_tracker_item'])) { 338 TikiLib::lib('header')->add_jq_onready('setTimeout(function () { $(".insert-usertracker").click(); });'); 339 $_REQUEST['user'] = $userlib->get_user_id($_REQUEST['login']); 340 $cookietab = '2'; 341 } else { 342 $cookietab = '1'; 343 $_REQUEST['find'] = $_REQUEST['login']; 344 } 345 } else { 346 $errors[] = sprintf( 347 tra('Impossible to create new %s with %s %s.'), 348 tra('user'), 349 tra('username'), 350 $_REQUEST['login'] 351 ); 352 } 353 } 354 } 355 356 $cookietab = 1; 357} elseif (isset($_REQUEST['action'])) { 358 if ($_REQUEST['action'] == 'email_due' && isset($_REQUEST['user']) && $access->checkCsrf()) { 359 $result = $userlib->reset_email_due($_REQUEST['user']); 360 if ($result->numRows()) { 361 Feedback::success(tr('User account %0 has been invalidated by the admin', $_REQUEST['user'])); 362 } else { 363 Feedback::error(tr('An error occurred - the user account %0 has not been invalidated by the admin', $_REQUEST['user'])); 364 } 365 } 366 367 if ( 368 $_REQUEST['action'] == 'remove_openid' && isset($_REQUEST['userId']) 369 && $access->checkCsrfForm(tra('Remove link with OpenID for this user?')) 370 ) { 371 $result = $userlib->remove_openid_link($_REQUEST['userId']); 372 if ($result->numRows()) { 373 Feedback::success(tr('Link to OpenID for user %0 has been removed', $_REQUEST['user'])); 374 } else { 375 Feedback::error(tr('An error occurred - the link to OpenID for user %0 has not been removed', $_REQUEST['user'])); 376 } 377 } 378 379 $_REQUEST['user'] = ''; 380} 381 382if (!isset($_REQUEST['sort_mode'])) { 383 $sort_mode = 'login_asc'; 384} else { 385 $sort_mode = $_REQUEST['sort_mode']; 386} 387$smarty->assign_by_ref('sort_mode', $sort_mode); 388 389if (empty($_REQUEST['numrows'])) { 390 $numrows = $maxRecords; 391} else { 392 $numrows = $_REQUEST['numrows']; 393} 394$smarty->assign_by_ref('numrows', $numrows); 395 396if (empty($_REQUEST['offset'])) { 397 $offset = 0; 398} else { 399 $offset = $_REQUEST['offset']; 400} 401$smarty->assign_by_ref('offset', $offset); 402 403if (isset($_REQUEST['initial'])) { 404 $initial = $_REQUEST['initial']; 405} else { 406 $initial = ''; 407} 408$smarty->assign('initial', $initial); 409 410if (isset($_REQUEST['find'])) { 411 $find = $_REQUEST['find']; 412} else { 413 $find = ''; 414} 415$smarty->assign('find', $find); 416 417if (isset($_REQUEST['filterGroup'])) { 418 $filterGroup = $_REQUEST['filterGroup']; 419} else { 420 $filterGroup = ''; 421} 422$smarty->assign('filterGroup', $filterGroup); 423 424if (isset($_REQUEST['filterEmail'])) { 425 $filterEmail = $_REQUEST['filterEmail']; 426} else { 427 $filterEmail = ''; 428} 429$smarty->assign('filterEmail', $filterEmail); 430 431list($username, $usermail, $usersTrackerId, $chlogin) = ['', '', '', false]; 432$trklib = TikiLib::lib('trk'); 433 434if (isset($_REQUEST['user']) and $_REQUEST['user']) { 435 if (!is_numeric($_REQUEST['user'])) { 436 $_REQUEST['user'] = $userlib->get_user_id($_REQUEST['user']); 437 } 438 $userinfo = $userlib->get_userid_info($_REQUEST["user"]); 439 $cookietab = '2'; 440 441 // If login is e-mail, email field needs to be the same as name (and is generally not send) 442 if ($prefs['login_is_email'] == 'y' && isset($_POST['login'])) { 443 $_POST['email'] = $_POST['login']; 444 } 445 446 if ( 447 isset($_POST['edituser']) and isset($_POST['login']) and isset($_POST['email']) 448 && $access->checkCsrfForm(tra('Modify this user\'s data?')) 449 ) { 450 if (!empty($_POST['login'])) { 451 if ($userinfo['login'] != $_POST['login'] && $userinfo['login'] != 'admin') { 452 if ($userlib->user_exists($_POST['login'])) { 453 $errors[] = tra('User already exists'); 454 } elseif (!empty($prefs['username_pattern']) && !preg_match($prefs['username_pattern'], $_POST['login'])) { 455 $errors[] = tra('User login contains invalid characters.'); 456 } elseif ($userlib->change_login($userinfo['login'], $_POST['login'])) { 457 Feedback::success(sprintf( 458 tra('%s changed from %s to %s'), 459 tra('Username'), 460 $userinfo['login'], 461 $_POST['login'] 462 )); 463 $logslib->add_log( 464 'adminusers', 465 'changed login for ' . $_POST['login'] . ' from ' . $userinfo['login'] . ' to ' . $_POST['login'], 466 $user 467 ); 468 469 $userinfo['login'] = $_POST['login']; 470 } else { 471 $errors[] = sprintf( 472 tra("Unable to change %s from %s to %s"), 473 tra('login'), 474 $userinfo['login'], 475 $_POST['login'] 476 ); 477 } 478 } 479 } 480 481 $pass_first_login = (isset($_REQUEST['pass_first_login']) && $_REQUEST['pass_first_login'] == 'on'); 482 if ((isset($_POST['pass']) && $_POST["pass"]) || $pass_first_login || (isset($_POST['genepass']) && $_POST['genepass'])) { 483 if ($_POST['pass'] != $_POST['passAgain']) { 484 Feedback::error(tra('The passwords do not match')); 485 } 486 487 if ($tiki_p_admin == 'y' || $tiki_p_admin_users == 'y' || $userinfo['login'] == $user) { 488 $newPass = $_POST['pass'] ? $_POST['pass'] : $_POST['genepass']; 489 $polerr = $userlib->check_password_policy($newPass); 490 if (strlen($polerr) > 0 && !$pass_first_login) { 491 Feedback::error($polerr); 492 } else { 493 if ($userlib->change_user_password($userinfo['login'], $newPass, $pass_first_login)) { 494 Feedback::success(sprintf(tra('%s modified successfully.'), tra('password'))); 495 $logslib->add_log('adminusers', 'changed password for ' . $_POST['login'], $user); 496 } else { 497 $errors[] = sprintf(tra('%s modification failed.'), tra('password')); 498 } 499 } 500 } 501 } 502 503 if ($userinfo['email'] != $_POST['email']) { 504 if ($userlib->change_user_email($userinfo['login'], $_POST['email'], '')) { 505 if ($prefs['login_is_email'] != 'y') { 506 Feedback::success(sprintf( 507 tra('%s changed from %s to %s'), 508 tra('Email'), 509 $userinfo['email'], 510 $_POST['email'] 511 )); 512 $logslib->add_log('adminusers', 'changed email for' . $_POST['login'] . ' from ' . $userinfo['email'] . ' to ' . $_POST['email'], $user); 513 } 514 $userinfo['email'] = $_POST['email']; 515 } else { 516 $errors[] = sprintf(tra('Impossible to change %s from %s to %s'), tra('email'), $userinfo['email'], $_POST['email']); 517 } 518 } 519 // check need_email_validation 520 if (!empty($_POST['login']) && !empty($_POST['email']) && !empty($_POST['need_email_validation'])) { 521 $userlib->invalidate_account($_POST['login']); 522 $userinfo = $userlib->get_user_info($_POST['login']); 523 $userlib->send_validation_email($_POST['login'], $userinfo['valid'], $_POST['email'], 'y'); 524 } 525 526 $cookietab = '1'; 527 } 528 529 if ($prefs['userTracker'] == 'y') { 530 $re = $userlib->get_usertracker($_REQUEST['user']); 531 if ($re['usersTrackerId']) { 532 $trklib = TikiLib::lib('trk'); 533 $userstrackerid = $re['usersTrackerId']; 534 $smarty->assign('userstrackerid', $userstrackerid); 535 $usersFields = $trklib->list_tracker_fields($usersTrackerId, 0, -1, 'position_asc', ''); 536 $smarty->assign_by_ref('usersFields', $usersFields['data']); 537 if (isset($re['usersFieldId']) and $re['usersFieldId']) { 538 $usersfieldid = $re['usersFieldId']; 539 $smarty->assign('usersfieldid', $usersfieldid); 540 541 $usersitemid = $trklib->get_item_id($userstrackerid, $usersfieldid, $re['user']); 542 $smarty->assign('usersitemid', $usersitemid); 543 544 if (empty($usersitemid)) { // calculate the user field forced value for item insert dialog 545 $usersfield = $trklib->get_tracker_field($usersfieldid); 546 $usersTrackerForced = [$usersfield['permName'] => $userinfo['login']]; 547 $smarty->assign('usersTrackerForced', $usersTrackerForced); 548 } 549 } 550 } 551 } 552 553 if ($prefs['email_due'] > 0) { 554 $userinfo['daysSinceEmailConfirm'] = floor(($userlib->now - $userinfo['email_confirm']) / (60 * 60 * 24)); 555 } 556} else { 557 //For to get informations entered and placed in the fields 558 if (isset($_REQUEST['login'])) { 559 $userinfo['login'] = strip_tags((trim($_REQUEST['login']))); 560 } 561 562 if (isset($_REQUEST['email'])) { 563 $userinfo['email'] = strip_tags((trim($_REQUEST['email']))); 564 } 565 566 $userinfo['created'] = $tikilib->now; 567 $userinfo['registrationDate'] = ''; 568 $userinfo['age'] = ''; 569 $userinfo['currentLogin'] = ''; 570 $userinfo['editable'] = true; 571 572 $_REQUEST['user'] = 0; 573} 574 575if ($tiki_p_admin == 'y') { 576 $all_groups = $userlib->list_all_groups(); 577} else { 578 foreach ($userGroups as $g => $t) { 579 $all_groups[] = $g; 580 } 581} 582//get users 583$users = $userlib->get_users( 584 $offset, 585 $numrows, 586 $sort_mode, 587 $find, 588 $initial, 589 true, 590 $filterGroup, 591 $filterEmail, 592 !empty($_REQUEST['filterEmailNotConfirmed']), 593 !empty($_REQUEST['filterNotValidated']), 594 !empty($_REQUEST['filterNeverLoggedIn']) 595); 596if ($prefs['userTracker'] === 'y') { 597 foreach ($users['data'] as &$u) { 598 $userTrackerInfo = $userlib->get_usertracker($u['userId']); 599 if ($userTrackerInfo && $userTrackerInfo['usersTrackerId']) { 600 $u['itemId'] = $trklib->get_item_id($userTrackerInfo['usersTrackerId'], $userTrackerInfo['usersFieldId'], $u['login']); 601 } 602 } 603} 604$smarty->assign_by_ref('users', $users['data']); 605$smarty->assign_by_ref('cant', $users['cant']); 606 607if (isset($_REQUEST['add'])) { 608 $cookietab = '2'; 609} 610 611//add tablesorter sorting and filtering 612$ts = Table_Check::setVars('adminusers', true); 613if ($ts['enabled'] && !$ts['ajax']) { 614 //delete anonymous out of group list used for dropdown 615 $ts_groups = array_flip($all_groups); 616 unset($ts_groups['Anonymous']); 617 $ts_groups = array_flip($ts_groups); 618 //set tablesorter code 619 Table_Factory::build( 620 'TikiAdminusers', 621 [ 622 'id' => $ts['tableid'], 623 'total' => $users['cant'], 624 'columns' => [ 625 '#groups' => [ 626 'filter' => [ 627 'options' => $ts_groups 628 ] 629 ] 630 ], 631 ] 632 ); 633} 634 635if (count($errors) > 0) { 636 Feedback::error(['mes' => $errors]); 637} 638 639$smarty->assign_by_ref('all_groups', $all_groups); 640$smarty->assign('userinfo', $userinfo); 641$smarty->assign('userId', $_REQUEST['user']); 642$smarty->assign('username', $username); 643$smarty->assign('usermail', $usermail); 644 645// disallow robots to index page: 646$smarty->assign('metatag_robots', 'NOINDEX, NOFOLLOW'); 647$smarty->assign('mid', 'tiki-adminusers.tpl'); 648if ($ts['ajax']) { 649 $smarty->display('tiki-adminusers.tpl'); 650} else { 651 $smarty->display('tiki.tpl'); 652} 653