qa-app-users-edit.php 14.7 KB
Newer Older
Gideon Greenspan committed
1 2 3 4 5 6 7
<?php

/*
	Question2Answer (c) Gideon Greenspan

	http://www.question2answer.org/

Scott Vivian committed
8

Gideon Greenspan committed
9 10 11 12 13 14 15 16 17
	File: qa-include/qa-app-users-edit.php
	Version: See define()s at top of qa-include/qa-base.php
	Description: User management (application level) for creating/modifying users


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.
Scott Vivian committed
18

Gideon Greenspan committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

	if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
		header('Location: ../');
		exit;
	}

	@define('QA_MIN_PASSWORD_LEN', 4);
	@define('QA_NEW_PASSWORD_LEN', 8); // when resetting password


	function qa_handle_email_filter(&$handle, &$email, $olduser=null)
/*
	Return $errors fields for any invalid aspect of user-entered $handle (username) and $email. Works by calling through
	to all filter modules and also rejects existing values in database unless they belongs to $olduser (if set).
*/
	{
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
Scott Vivian committed
43

Gideon Greenspan committed
44
		$errors=array();
Scott Vivian committed
45

Gideon Greenspan committed
46
		$filtermodules=qa_load_modules_with('filter', 'filter_handle');
Scott Vivian committed
47

Gideon Greenspan committed
48 49 50 51 52 53 54 55 56 57 58 59 60
		foreach ($filtermodules as $filtermodule) {
			$error=$filtermodule->filter_handle($handle, $olduser);
			if (isset($error)) {
				$errors['handle']=$error;
				break;
			}
		}

		if (!isset($errors['handle'])) { // first test through filters, then check for duplicates here
			$handleusers=qa_db_user_find_by_handle($handle);
			if (count($handleusers) && ( (!isset($olduser['userid'])) || (array_search($olduser['userid'], $handleusers)===false) ) )
				$errors['handle']=qa_lang('users/handle_exists');
		}
Scott Vivian committed
61

Gideon Greenspan committed
62
		$filtermodules=qa_load_modules_with('filter', 'filter_email');
Scott Vivian committed
63

Gideon Greenspan committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77
		$error=null;
		foreach ($filtermodules as $filtermodule) {
			$error=$filtermodule->filter_email($email, $olduser);
			if (isset($error)) {
				$errors['email']=$error;
				break;
			}
		}

		if (!isset($errors['email'])) {
			$emailusers=qa_db_user_find_by_email($email);
			if (count($emailusers) && ( (!isset($olduser['userid'])) || (array_search($olduser['userid'], $emailusers)===false) ) )
				$errors['email']=qa_lang('users/email_exists');
		}
Scott Vivian committed
78

Gideon Greenspan committed
79 80
		return $errors;
	}
Scott Vivian committed
81 82


Gideon Greenspan committed
83 84 85 86 87 88 89
	function qa_handle_make_valid($handle)
/*
	Make $handle valid and unique in the database - if $allowuserid is set, allow it to match that user only
*/
	{
		require_once QA_INCLUDE_DIR.'qa-util-string.php';
		require_once QA_INCLUDE_DIR.'qa-db-maxima.php';
Gideon Greenspan committed
90
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
Scott Vivian committed
91

Gideon Greenspan committed
92 93 94 95 96 97 98 99 100 101 102
		if (!strlen($handle))
			$handle=qa_lang('users/registered_user');

		$handle=preg_replace('/[\\@\\+\\/]/', ' ', $handle);

		for ($attempt=0; $attempt<=99; $attempt++) {
			$suffix=$attempt ? (' '.$attempt) : '';
			$tryhandle=qa_substr($handle, 0, QA_DB_MAX_HANDLE_LENGTH-strlen($suffix)).$suffix;

			$filtermodules=qa_load_modules_with('filter', 'filter_handle');
			foreach ($filtermodules as $filtermodule)
Gideon Greenspan committed
103
				$filtermodule->filter_handle($tryhandle, null); // filter first without worrying about errors, since our goal is to get a valid one
Scott Vivian committed
104

Gideon Greenspan committed
105
			$haderror=false;
Scott Vivian committed
106

Gideon Greenspan committed
107
			foreach ($filtermodules as $filtermodule) {
Gideon Greenspan committed
108
				$error=$filtermodule->filter_handle($tryhandle, null); // now check for errors after we've filtered
Gideon Greenspan committed
109 110 111
				if (isset($error))
					$haderror=true;
			}
Scott Vivian committed
112

Gideon Greenspan committed
113 114 115 116 117 118
			if (!$haderror) {
				$handleusers=qa_db_user_find_by_handle($tryhandle);
				if (!count($handleusers))
					return $tryhandle;
			}
		}
Scott Vivian committed
119

Gideon Greenspan committed
120
		qa_fatal_error('Could not create a valid and unique handle from: '.$handle);
Gideon Greenspan committed
121 122 123 124 125 126 127 128 129 130 131
	}


	function qa_password_validate($password, $olduser=null)
/*
	Return an array with a single element (key 'password') if user-entered $password is valid, otherwise an empty array.
	Works by calling through to all filter modules.
*/
	{
		$error=null;
		$filtermodules=qa_load_modules_with('filter', 'validate_password');
Scott Vivian committed
132

Gideon Greenspan committed
133 134 135 136 137
		foreach ($filtermodules as $filtermodule) {
			$error=$filtermodule->validate_password($password, $olduser);
			if (isset($error))
				break;
		}
Scott Vivian committed
138

Gideon Greenspan committed
139 140 141 142
		if (!isset($error)) {
			$minpasslen=max(QA_MIN_PASSWORD_LEN, 1);
			if (qa_strlen($password)<$minpasslen)
				$error=qa_lang_sub('users/password_min', $minpasslen);
Scott Vivian committed
143
		}
Gideon Greenspan committed
144 145 146 147 148 149 150

		if (isset($error))
			return array('password' => $error);

		return array();
	}

Scott Vivian committed
151

Gideon Greenspan committed
152 153 154 155 156 157 158
	function qa_create_new_user($email, $password, $handle, $level=QA_USER_LEVEL_BASIC, $confirmed=false)
/*
	Create a new user (application level) with $email, $password, $handle and $level.
	Set $confirmed to true if the email address has been confirmed elsewhere.
	Handles user points, notification and optional email confirmation.
*/
	{
Gideon Greenspan committed
159
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
160

Gideon Greenspan committed
161 162 163 164 165 166 167 168
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
		require_once QA_INCLUDE_DIR.'qa-db-points.php';
		require_once QA_INCLUDE_DIR.'qa-app-options.php';
		require_once QA_INCLUDE_DIR.'qa-app-emails.php';
		require_once QA_INCLUDE_DIR.'qa-app-cookies.php';

		$userid=qa_db_user_create($email, $password, $handle, $level, qa_remote_ip_address());
		qa_db_points_update_ifuser($userid, null);
Gideon Greenspan committed
169
		qa_db_uapprovecount_update();
Scott Vivian committed
170

Gideon Greenspan committed
171 172
		if ($confirmed)
			qa_db_user_set_flag($userid, QA_USER_FLAGS_EMAIL_CONFIRMED, true);
Scott Vivian committed
173

Gideon Greenspan committed
174 175
		if (qa_opt('show_notice_welcome'))
			qa_db_user_set_flag($userid, QA_USER_FLAGS_WELCOME_NOTICE, true);
Scott Vivian committed
176

Gideon Greenspan committed
177
		$custom=qa_opt('show_custom_welcome') ? trim(qa_opt('custom_welcome')) : '';
Scott Vivian committed
178

Gideon Greenspan committed
179 180 181 182
		if (qa_opt('confirm_user_emails') && ($level<QA_USER_LEVEL_EXPERT) && !$confirmed) {
			$confirm=strtr(qa_lang('emails/welcome_confirm'), array(
				'^url' => qa_get_new_confirm_url($userid, $handle)
			));
Scott Vivian committed
183

Gideon Greenspan committed
184 185
			if (qa_opt('confirm_user_required'))
				qa_db_user_set_flag($userid, QA_USER_FLAGS_MUST_CONFIRM, true);
Scott Vivian committed
186

Gideon Greenspan committed
187 188
		} else
			$confirm='';
Scott Vivian committed
189

Gideon Greenspan committed
190 191
		if (qa_opt('moderate_users') && qa_opt('approve_user_required') && ($level<QA_USER_LEVEL_EXPERT))
			qa_db_user_set_flag($userid, QA_USER_FLAGS_MUST_APPROVE, true);
Scott Vivian committed
192

Gideon Greenspan committed
193
		qa_send_notification($userid, $email, $handle, qa_lang('emails/welcome_subject'), qa_lang('emails/welcome_body'), array(
Gideon Greenspan committed
194
			'^password' => isset($password) ? qa_lang('main/hidden') : qa_lang('users/password_to_set'), // v 1.6.3: no longer email out passwords
Gideon Greenspan committed
195 196 197 198
			'^url' => qa_opt('site_url'),
			'^custom' => strlen($custom) ? ($custom."\n\n") : '',
			'^confirm' => $confirm,
		));
Scott Vivian committed
199

Gideon Greenspan committed
200 201 202 203
		qa_report_event('u_register', $userid, $handle, qa_cookie_get(), array(
			'email' => $email,
			'level' => $level,
		));
Scott Vivian committed
204

Gideon Greenspan committed
205 206
		return $userid;
	}
Scott Vivian committed
207 208


Gideon Greenspan committed
209 210 211 212 213 214
	function qa_delete_user($userid)
/*
	Delete $userid and all their votes and flags. Their posts will become anonymous.
	Handles recalculations of votes and flags for posts this user has affected.
*/
	{
Gideon Greenspan committed
215
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
216

Gideon Greenspan committed
217 218 219 220
		require_once QA_INCLUDE_DIR.'qa-db-votes.php';
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
		require_once QA_INCLUDE_DIR.'qa-db-post-update.php';
		require_once QA_INCLUDE_DIR.'qa-db-points.php';
Scott Vivian committed
221

Gideon Greenspan committed
222
		$postids=qa_db_uservoteflag_user_get($userid); // posts this user has flagged or voted on, whose counts need updating
Scott Vivian committed
223

Gideon Greenspan committed
224
		qa_db_user_delete($userid);
Gideon Greenspan committed
225
		qa_db_uapprovecount_update();
Scott Vivian committed
226

Gideon Greenspan committed
227 228 229 230
		foreach ($postids as $postid) { // hoping there aren't many of these - saves a lot of new SQL code...
			qa_db_post_recount_votes($postid);
			qa_db_post_recount_flags($postid);
		}
Scott Vivian committed
231

Gideon Greenspan committed
232
		$postuserids=qa_db_posts_get_userids($postids);
Scott Vivian committed
233

Gideon Greenspan committed
234 235 236 237
		foreach ($postuserids as $postuserid)
			qa_db_points_update_ifuser($postuserid, array('avoteds','qvoteds', 'upvoteds', 'downvoteds'));
	}

Scott Vivian committed
238

Gideon Greenspan committed
239 240 241 242 243
	function qa_send_new_confirm($userid)
/*
	Set a new email confirmation code for the user and send it out
*/
	{
Gideon Greenspan committed
244
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
245

Gideon Greenspan committed
246 247 248 249 250
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
		require_once QA_INCLUDE_DIR.'qa-db-selects.php';
		require_once QA_INCLUDE_DIR.'qa-app-emails.php';

		$userinfo=qa_db_select_with_pending(qa_db_user_account_selectspec($userid, true));
Scott Vivian committed
251

Gideon Greenspan committed
252 253 254 255 256 257
		if (!qa_send_notification($userid, $userinfo['email'], $userinfo['handle'], qa_lang('emails/confirm_subject'), qa_lang('emails/confirm_body'), array(
			'^url' => qa_get_new_confirm_url($userid, $userinfo['handle']),
		)))
			qa_fatal_error('Could not send email confirmation');
	}

Scott Vivian committed
258

Gideon Greenspan committed
259 260 261 262 263
	function qa_get_new_confirm_url($userid, $handle)
/*
	Set a new email confirmation code for the user and return the corresponding link
*/
	{
Gideon Greenspan committed
264
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
265

Gideon Greenspan committed
266
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
Scott Vivian committed
267

Gideon Greenspan committed
268 269
		$emailcode=qa_db_user_rand_emailcode();
		qa_db_user_set($userid, 'emailcode', $emailcode);
Scott Vivian committed
270

Gideon Greenspan committed
271
		return qa_path_absolute('confirm', array('c' => $emailcode, 'u' => $handle));
Gideon Greenspan committed
272 273
	}

Scott Vivian committed
274

Gideon Greenspan committed
275 276 277 278 279
	function qa_complete_confirm($userid, $email, $handle)
/*
	Complete the email confirmation process for the user
*/
	{
Gideon Greenspan committed
280
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
281

Gideon Greenspan committed
282 283
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
		require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
Scott Vivian committed
284

Gideon Greenspan committed
285 286 287 288 289 290 291 292
		qa_db_user_set_flag($userid, QA_USER_FLAGS_EMAIL_CONFIRMED, true);
		qa_db_user_set_flag($userid, QA_USER_FLAGS_MUST_CONFIRM, false);
		qa_db_user_set($userid, 'emailcode', ''); // to prevent re-use of the code

		qa_report_event('u_confirmed', $userid, $handle, qa_cookie_get(), array(
			'email' => $email,
		));
	}
Scott Vivian committed
293 294


Gideon Greenspan committed
295
	function qa_set_user_level($userid, $handle, $level, $oldlevel)
Gideon Greenspan committed
296 297 298 299
/*
	Set the user level of user $userid with $handle to $level (one of the QA_USER_LEVEL_* constraints in qa-app-users.php)
	Pass the previous user level in $oldlevel. Reports the appropriate event, assumes change performed by the logged in user.
*/
Gideon Greenspan committed
300 301 302
	{
		require_once QA_INCLUDE_DIR.'qa-db-users.php';

Gideon Greenspan committed
303 304
		qa_db_user_set($userid, 'level', $level);
		qa_db_uapprovecount_update();
Scott Vivian committed
305

Gideon Greenspan committed
306 307
		if ($level>=QA_USER_LEVEL_APPROVED)
			qa_db_user_set_flag($userid, QA_USER_FLAGS_MUST_APPROVE, false);
Gideon Greenspan committed
308

Gideon Greenspan committed
309 310 311 312 313 314
		qa_report_event('u_level', qa_get_logged_in_userid(), qa_get_logged_in_handle(), qa_cookie_get(), array(
			'userid' => $userid,
			'handle' => $handle,
			'level' => $level,
			'oldlevel' => $oldlevel,
		));
Gideon Greenspan committed
315
	}
Scott Vivian committed
316 317


Gideon Greenspan committed
318
	function qa_set_user_blocked($userid, $handle, $blocked)
Gideon Greenspan committed
319 320 321 322
/*
	Set the status of user $userid with $handle to blocked if $blocked is true, otherwise to unblocked. Reports the appropriate
	event, assumes change performed by the logged in user.
*/
Gideon Greenspan committed
323 324
	{
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
Scott Vivian committed
325

Gideon Greenspan committed
326 327
		qa_db_user_set_flag($userid, QA_USER_FLAGS_USER_BLOCKED, $blocked);
		qa_db_uapprovecount_update();
Scott Vivian committed
328

Gideon Greenspan committed
329 330 331 332 333
		qa_report_event($blocked ? 'u_block' : 'u_unblock', qa_get_logged_in_userid(), qa_get_logged_in_handle(), qa_cookie_get(), array(
			'userid' => $userid,
			'handle' => $handle,
		));
	}
Gideon Greenspan committed
334

Scott Vivian committed
335

Gideon Greenspan committed
336 337 338 339 340
	function qa_start_reset_user($userid)
/*
	Start the 'I forgot my password' process for $userid, sending reset code
*/
	{
Gideon Greenspan committed
341
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
342

Gideon Greenspan committed
343 344 345 346 347 348 349 350 351 352 353
		require_once QA_INCLUDE_DIR.'qa-db-users.php';
		require_once QA_INCLUDE_DIR.'qa-app-options.php';
		require_once QA_INCLUDE_DIR.'qa-app-emails.php';
		require_once QA_INCLUDE_DIR.'qa-db-selects.php';

		qa_db_user_set($userid, 'emailcode', qa_db_user_rand_emailcode());

		$userinfo=qa_db_select_with_pending(qa_db_user_account_selectspec($userid, true));

		if (!qa_send_notification($userid, $userinfo['email'], $userinfo['handle'], qa_lang('emails/reset_subject'), qa_lang('emails/reset_body'), array(
			'^code' => $userinfo['emailcode'],
Gideon Greenspan committed
354
			'^url' => qa_path_absolute('reset', array('c' => $userinfo['emailcode'], 'e' => $userinfo['email'])),
Gideon Greenspan committed
355 356 357 358
		)))
			qa_fatal_error('Could not send reset password email');
	}

Scott Vivian committed
359

Gideon Greenspan committed
360 361 362 363 364
	function qa_complete_reset_user($userid)
/*
	Successfully finish the 'I forgot my password' process for $userid, sending new password
*/
	{
Gideon Greenspan committed
365
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
366

Gideon Greenspan committed
367 368 369 370 371
		require_once QA_INCLUDE_DIR.'qa-util-string.php';
		require_once QA_INCLUDE_DIR.'qa-app-options.php';
		require_once QA_INCLUDE_DIR.'qa-app-emails.php';
		require_once QA_INCLUDE_DIR.'qa-app-cookies.php';
		require_once QA_INCLUDE_DIR.'qa-db-selects.php';
Scott Vivian committed
372

Gideon Greenspan committed
373
		$password=qa_random_alphanum(max(QA_MIN_PASSWORD_LEN, QA_NEW_PASSWORD_LEN));
Scott Vivian committed
374

Gideon Greenspan committed
375
		$userinfo=qa_db_select_with_pending(qa_db_user_account_selectspec($userid, true));
Scott Vivian committed
376

Gideon Greenspan committed
377 378 379 380 381
		if (!qa_send_notification($userid, $userinfo['email'], $userinfo['handle'], qa_lang('emails/new_password_subject'), qa_lang('emails/new_password_body'), array(
			'^password' => $password,
			'^url' => qa_opt('site_url'),
		)))
			qa_fatal_error('Could not send new password - password not reset');
Scott Vivian committed
382

Gideon Greenspan committed
383 384 385 386 387 388 389 390
		qa_db_user_set_password($userid, $password); // do this last, to be safe
		qa_db_user_set($userid, 'emailcode', ''); // so can't be reused

		qa_report_event('u_reset', $userid, $userinfo['handle'], qa_cookie_get(), array(
			'email' => $userinfo['email'],
		));
	}

Scott Vivian committed
391

Gideon Greenspan committed
392 393 394 395 396 397
	function qa_logged_in_user_flush()
/*
	Flush any information about the currently logged in user, so it is retrieved from database again
*/
	{
		global $qa_cached_logged_in_user;
Scott Vivian committed
398

Gideon Greenspan committed
399 400
		$qa_cached_logged_in_user=null;
	}
Scott Vivian committed
401 402


Gideon Greenspan committed
403 404 405 406 407
	function qa_set_user_avatar($userid, $imagedata, $oldblobid=null)
/*
	Set the avatar of $userid to the image in $imagedata, and remove $oldblobid from the database if not null
*/
	{
Gideon Greenspan committed
408
		if (qa_to_override(__FUNCTION__)) { $args=func_get_args(); return qa_call_override(__FUNCTION__, $args); }
Scott Vivian committed
409

Gideon Greenspan committed
410
		require_once QA_INCLUDE_DIR.'qa-util-image.php';
Scott Vivian committed
411

Gideon Greenspan committed
412
		$imagedata=qa_image_constrain_data($imagedata, $width, $height, qa_opt('avatar_store_size'));
Scott Vivian committed
413

Gideon Greenspan committed
414
		if (isset($imagedata)) {
Gideon Greenspan committed
415
			require_once QA_INCLUDE_DIR.'qa-app-blobs.php';
Gideon Greenspan committed
416

Gideon Greenspan committed
417
			$newblobid=qa_create_blob($imagedata, 'jpeg', null, $userid, null, qa_remote_ip_address());
Scott Vivian committed
418

Gideon Greenspan committed
419 420 421 422 423 424 425 426
			if (isset($newblobid)) {
				qa_db_user_set($userid, 'avatarblobid', $newblobid);
				qa_db_user_set($userid, 'avatarwidth', $width);
				qa_db_user_set($userid, 'avatarheight', $height);
				qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_AVATAR, true);
				qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_GRAVATAR, false);

				if (isset($oldblobid))
Gideon Greenspan committed
427
					qa_delete_blob($oldblobid);
Gideon Greenspan committed
428 429 430 431

				return true;
			}
		}
Scott Vivian committed
432

Gideon Greenspan committed
433 434 435 436 437 438 439
		return false;
	}


/*
	Omit PHP closing tag to help avoid accidental output
*/