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