account.php 15.6 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	Description: Controller for user account page


	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.

	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
*/

Scott committed
22
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
23
	header('Location: ../../');
Scott committed
24 25
	exit;
}
Scott committed
26

Scott committed
27 28 29 30 31
require_once QA_INCLUDE_DIR . 'db/users.php';
require_once QA_INCLUDE_DIR . 'app/format.php';
require_once QA_INCLUDE_DIR . 'app/users.php';
require_once QA_INCLUDE_DIR . 'db/selects.php';
require_once QA_INCLUDE_DIR . 'util/image.php';
Scott committed
32 33


Scott committed
34
// Check we're not using single-sign on integration, that we're logged in
Scott committed
35

Scott committed
36 37
if (QA_FINAL_EXTERNAL_USERS)
	qa_fatal_error('User accounts are handled by external code');
Scott committed
38

Scott committed
39
$userid = qa_get_logged_in_userid();
Scott committed
40

Scott committed
41 42
if (!isset($userid))
	qa_redirect('login');
Scott committed
43 44


Scott committed
45
// Get current information on user
Scott committed
46

Scott committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
list($useraccount, $userprofile, $userpoints, $userfields) = qa_db_select_with_pending(
	qa_db_user_account_selectspec($userid, true),
	qa_db_user_profile_selectspec($userid, true),
	qa_db_user_points_selectspec($userid, true),
	qa_db_userfields_selectspec()
);

$changehandle = qa_opt('allow_change_usernames') || (!$userpoints['qposts'] && !$userpoints['aposts'] && !$userpoints['cposts']);
$doconfirms = qa_opt('confirm_user_emails') && $useraccount['level'] < QA_USER_LEVEL_EXPERT;
$isconfirmed = ($useraccount['flags'] & QA_USER_FLAGS_EMAIL_CONFIRMED) ? true : false;

$haspasswordold = isset($useraccount['passsalt']) && isset($useraccount['passcheck']);
if (QA_PASSWORD_HASH) {
	$haspassword = isset($useraccount['passhash']);
} else {
	$haspassword = $haspasswordold;
}
$permit_error = qa_user_permit_error();
$isblocked = $permit_error !== false;
66
$pending_confirmation = $doconfirms && !$isconfirmed;
Scott committed
67

Scott committed
68
// Process profile if saved
69

Scott committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
// If the post_max_size is exceeded then the $_POST array is empty so no field processing can be done
if (qa_post_limit_exceeded())
	$errors['avatar'] = qa_lang('main/file_upload_limit_exceeded');
else {
	require_once QA_INCLUDE_DIR . 'app/users-edit.php';

	if (qa_clicked('dosaveprofile') && !$isblocked) {
		$inhandle = $changehandle ? qa_post_text('handle') : $useraccount['handle'];
		$inemail = qa_post_text('email');
		$inmessages = qa_post_text('messages');
		$inwallposts = qa_post_text('wall');
		$inmailings = qa_post_text('mailings');
		$inavatar = qa_post_text('avatar');

		$inprofile = array();
		foreach ($userfields as $userfield)
			$inprofile[$userfield['fieldid']] = qa_post_text('field_' . $userfield['fieldid']);

		if (!qa_check_form_security_code('account', qa_post_text('code')))
			$errors['page'] = qa_lang_html('misc/form_security_again');
		else {
			$errors = qa_handle_email_filter($inhandle, $inemail, $useraccount);

			if (!isset($errors['handle']))
				qa_db_user_set($userid, 'handle', $inhandle);

			if (!isset($errors['email']) && $inemail !== $useraccount['email']) {
				qa_db_user_set($userid, 'email', $inemail);
				qa_db_user_set_flag($userid, QA_USER_FLAGS_EMAIL_CONFIRMED, false);
				$isconfirmed = false;

				if ($doconfirms)
					qa_send_new_confirm($userid);
			}
Scott committed
104

Scott committed
105 106
			if (qa_opt('allow_private_messages'))
				qa_db_user_set_flag($userid, QA_USER_FLAGS_NO_MESSAGES, !$inmessages);
Scott committed
107

Scott committed
108 109
			if (qa_opt('allow_user_walls'))
				qa_db_user_set_flag($userid, QA_USER_FLAGS_NO_WALL_POSTS, !$inwallposts);
Scott committed
110

Scott committed
111 112
			if (qa_opt('mailing_enabled'))
				qa_db_user_set_flag($userid, QA_USER_FLAGS_NO_MAILINGS, !$inmailings);
Scott committed
113

Scott committed
114 115
			qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_AVATAR, ($inavatar == 'uploaded'));
			qa_db_user_set_flag($userid, QA_USER_FLAGS_SHOW_GRAVATAR, ($inavatar == 'gravatar'));
Scott committed
116

Scott committed
117 118
			if (is_array(@$_FILES['file'])) {
				$avatarfileerror = $_FILES['file']['error'];
Scott committed
119

Scott committed
120 121 122 123 124
				// Note if $_FILES['file']['error'] === 1 then upload_max_filesize has been exceeded
				if ($avatarfileerror === 1)
					$errors['avatar'] = qa_lang('main/file_upload_limit_exceeded');
				elseif ($avatarfileerror === 0 && $_FILES['file']['size'] > 0) {
					require_once QA_INCLUDE_DIR . 'app/limits.php';
Scott committed
125

Scott committed
126 127 128 129
					switch (qa_user_permit_error(null, QA_LIMIT_UPLOADS)) {
						case 'limit':
							$errors['avatar'] = qa_lang('main/upload_limit');
							break;
Scott committed
130

Scott committed
131 132 133
						default:
							$errors['avatar'] = qa_lang('users/no_permission');
							break;
Scott committed
134

Scott committed
135 136 137
						case false:
							qa_limits_increment($userid, QA_LIMIT_UPLOADS);
							$toobig = qa_image_file_too_big($_FILES['file']['tmp_name'], qa_opt('avatar_store_size'));
Scott committed
138

Scott committed
139 140 141 142 143 144 145 146
							if ($toobig)
								$errors['avatar'] = qa_lang_sub('main/image_too_big_x_pc', (int)($toobig * 100));
							elseif (!qa_set_user_avatar($userid, file_get_contents($_FILES['file']['tmp_name']), $useraccount['avatarblobid']))
								$errors['avatar'] = qa_lang_sub('main/image_not_read', implode(', ', qa_gd_image_formats()));
							break;
					}
				}  // There shouldn't be any need to catch any other error
			}
Scott committed
147

Scott committed
148 149 150 151 152
			if (count($inprofile)) {
				$filtermodules = qa_load_modules_with('filter', 'filter_profile');
				foreach ($filtermodules as $filtermodule)
					$filtermodule->filter_profile($inprofile, $errors, $useraccount, $userprofile);
			}
Scott committed
153

Scott committed
154 155 156 157
			foreach ($userfields as $userfield) {
				if (!isset($errors[$userfield['fieldid']]))
					qa_db_user_profile_set($userid, $userfield['title'], $inprofile[$userfield['fieldid']]);
			}
Scott committed
158

Scott committed
159 160 161
			list($useraccount, $userprofile) = qa_db_select_with_pending(
				qa_db_user_account_selectspec($userid, true), qa_db_user_profile_selectspec($userid, true)
			);
Scott committed
162

Scott committed
163
			qa_report_event('u_save', $userid, $useraccount['handle'], qa_cookie_get());
Scott committed
164

Scott committed
165 166 167 168 169
			if (empty($errors))
				qa_redirect('account', array('state' => 'profile-saved'));

			qa_logged_in_user_flush();
		}
170
	} elseif (qa_clicked('dosaveprofile') && $pending_confirmation) {
Scott committed
171 172
		// only allow user to update email if they are not confirmed yet
		$inemail = qa_post_text('email');
Scott committed
173

Scott committed
174 175
		if (!qa_check_form_security_code('account', qa_post_text('code')))
			$errors['page'] = qa_lang_html('misc/form_security_again');
Scott committed
176

Scott committed
177 178
		else {
			$errors = qa_handle_email_filter($useraccount['handle'], $inemail, $useraccount);
Scott committed
179

Scott committed
180 181 182 183
			if (!isset($errors['email']) && $inemail !== $useraccount['email']) {
				qa_db_user_set($userid, 'email', $inemail);
				qa_db_user_set_flag($userid, QA_USER_FLAGS_EMAIL_CONFIRMED, false);
				$isconfirmed = false;
184

Scott committed
185 186
				if ($doconfirms)
					qa_send_new_confirm($userid);
187 188
			}

Scott committed
189
			qa_report_event('u_save', $userid, $useraccount['handle'], qa_cookie_get());
190

Scott committed
191 192
			if (empty($errors))
				qa_redirect('account', array('state' => 'profile-saved'));
193

Scott committed
194 195 196 197 198
			qa_logged_in_user_flush();
		}
	}


Scott committed
199
	// Process change password if clicked
200

Scott committed
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
	if (qa_clicked('dochangepassword')) {
		$inoldpassword = qa_post_text('oldpassword');
		$innewpassword1 = qa_post_text('newpassword1');
		$innewpassword2 = qa_post_text('newpassword2');

		if (!qa_check_form_security_code('password', qa_post_text('code')))
			$errors['page'] = qa_lang_html('misc/form_security_again');
		else {
			$errors = array();
			$legacyPassError = !hash_equals(strtolower($useraccount['passcheck']), strtolower(qa_db_calc_passcheck($inoldpassword, $useraccount['passsalt'])));

			if (QA_PASSWORD_HASH) {
				$passError = !password_verify($inoldpassword, $useraccount['passhash']);
				if (($haspasswordold && $legacyPassError) || (!$haspasswordold && $haspassword && $passError)) {
					$errors['oldpassword'] = qa_lang('users/password_wrong');
216
				}
Scott committed
217 218 219 220 221 222 223 224 225 226 227
			} else {
				if ($haspassword && $legacyPassError) {
					$errors['oldpassword'] = qa_lang('users/password_wrong');
				}
			}

			$useraccount['password'] = $inoldpassword;
			$errors = $errors + qa_password_validate($innewpassword1, $useraccount); // array union

			if ($innewpassword1 != $innewpassword2)
				$errors['newpassword2'] = qa_lang('users/password_mismatch');
228

Scott committed
229 230 231 232
			if (empty($errors)) {
				qa_db_user_set_password($userid, $innewpassword1);
				qa_db_user_set($userid, 'sessioncode', ''); // stop old 'Remember me' style logins from still working
				qa_set_logged_in_user($userid, $useraccount['handle'], false, $useraccount['sessionsource']); // reinstate this specific session
233

Scott committed
234
				qa_report_event('u_password', $userid, $useraccount['handle'], qa_cookie_get());
Scott committed
235

Scott committed
236
				qa_redirect('account', array('state' => 'password-changed'));
Scott committed
237 238
			}
		}
Scott committed
239 240
	}
}
Scott committed
241

Scott committed
242
// Prepare content for theme
Scott committed
243

Scott committed
244
$qa_content = qa_content_prepare();
Scott committed
245

Scott committed
246 247
$qa_content['title'] = qa_lang_html('profile/my_account_title');
$qa_content['error'] = @$errors['page'];
Scott committed
248

Scott committed
249 250
$qa_content['form_profile'] = array(
	'tags' => 'enctype="multipart/form-data" method="post" action="' . qa_self_html() . '"',
251

Scott committed
252
	'style' => 'wide',
Scott committed
253

Scott committed
254 255 256 257 258 259
	'fields' => array(
		'duration' => array(
			'type' => 'static',
			'label' => qa_lang_html('users/member_for'),
			'value' => qa_time_to_string(qa_opt('db_time') - $useraccount['created']),
		),
Scott committed
260

Scott committed
261 262 263 264 265 266
		'type' => array(
			'type' => 'static',
			'label' => qa_lang_html('users/member_type'),
			'value' => qa_html(qa_user_level_string($useraccount['level'])),
			'note' => $isblocked ? qa_lang_html('users/user_blocked') : null,
		),
Scott committed
267

Scott committed
268 269 270 271 272 273 274
		'handle' => array(
			'label' => qa_lang_html('users/handle_label'),
			'tags' => 'name="handle"',
			'value' => qa_html(isset($inhandle) ? $inhandle : $useraccount['handle']),
			'error' => qa_html(@$errors['handle']),
			'type' => ($changehandle && !$isblocked) ? 'text' : 'static',
		),
Scott committed
275

Scott committed
276 277 278 279 280 281
		'email' => array(
			'label' => qa_lang_html('users/email_label'),
			'tags' => 'name="email"',
			'value' => qa_html(isset($inemail) ? $inemail : $useraccount['email']),
			'error' => isset($errors['email']) ? qa_html($errors['email']) :
				($pending_confirmation ? qa_insert_login_links(qa_lang_html('users/email_please_confirm')) : null),
282
			'type' => $pending_confirmation ? 'email' : ($isblocked ? 'static' : 'email'),
Scott committed
283
		),
Scott committed
284

Scott committed
285 286 287 288 289 290 291
		'messages' => array(
			'label' => qa_lang_html('users/private_messages'),
			'tags' => 'name="messages"' . ($pending_confirmation ? ' disabled' : ''),
			'type' => 'checkbox',
			'value' => !($useraccount['flags'] & QA_USER_FLAGS_NO_MESSAGES),
			'note' => qa_lang_html('users/private_messages_explanation'),
		),
Scott committed
292

Scott committed
293 294 295 296 297 298 299
		'wall' => array(
			'label' => qa_lang_html('users/wall_posts'),
			'tags' => 'name="wall"' . ($pending_confirmation ? ' disabled' : ''),
			'type' => 'checkbox',
			'value' => !($useraccount['flags'] & QA_USER_FLAGS_NO_WALL_POSTS),
			'note' => qa_lang_html('users/wall_posts_explanation'),
		),
Scott committed
300

Scott committed
301 302 303 304 305 306
		'mailings' => array(
			'label' => qa_lang_html('users/mass_mailings'),
			'tags' => 'name="mailings"',
			'type' => 'checkbox',
			'value' => !($useraccount['flags'] & QA_USER_FLAGS_NO_MAILINGS),
			'note' => qa_lang_html('users/mass_mailings_explanation'),
Scott committed
307 308
		),

Scott committed
309 310 311 312 313 314 315
		'avatar' => null, // for positioning
	),

	'buttons' => array(
		'save' => array(
			'tags' => 'onclick="qa_show_waiting_after(this, false);"',
			'label' => qa_lang_html('users/save_profile'),
Scott committed
316
		),
Scott committed
317
	),
Scott committed
318

Scott committed
319 320 321 322
	'hidden' => array(
		'dosaveprofile' => array(
			'tags' => 'name="dosaveprofile"',
			'value' => '1',
Scott committed
323
		),
Scott committed
324 325 326 327 328 329
		'code' => array(
			'tags' => 'name="code"',
			'value' => qa_get_form_security_code('account'),
		),
	),
);
Scott committed
330

Scott committed
331 332
if (qa_get_state() == 'profile-saved')
	$qa_content['form_profile']['ok'] = qa_lang_html('users/profile_saved');
Scott committed
333

Scott committed
334 335
if (!qa_opt('allow_private_messages'))
	unset($qa_content['form_profile']['fields']['messages']);
Scott committed
336

Scott committed
337 338
if (!qa_opt('allow_user_walls'))
	unset($qa_content['form_profile']['fields']['wall']);
Scott committed
339

Scott committed
340 341
if (!qa_opt('mailing_enabled'))
	unset($qa_content['form_profile']['fields']['mailings']);
Scott committed
342

Scott committed
343 344 345 346
if ($isblocked && !$pending_confirmation) {
	unset($qa_content['form_profile']['buttons']['save']);
	$qa_content['error'] = qa_lang_html('users/no_permission');
}
Scott committed
347

Scott committed
348
// Avatar upload stuff
Scott committed
349

Scott committed
350 351
if (qa_opt('avatar_allow_gravatar') || qa_opt('avatar_allow_upload')) {
	$avataroptions = array();
Scott committed
352

Scott committed
353 354 355 356 357 358
	if (qa_opt('avatar_default_show') && strlen(qa_opt('avatar_default_blobid'))) {
		$avataroptions[''] = '<span style="margin:2px 0; display:inline-block;">' .
			qa_get_avatar_blob_html(qa_opt('avatar_default_blobid'), qa_opt('avatar_default_width'), qa_opt('avatar_default_height'), 32) .
			'</span> ' . qa_lang_html('users/avatar_default');
	} else
		$avataroptions[''] = qa_lang_html('users/avatar_none');
Scott committed
359

Scott committed
360
	$avatarvalue = $avataroptions[''];
Scott committed
361

Scott committed
362 363
	if (qa_has_gd_image() && qa_opt('avatar_allow_upload') && !$pending_confirmation) {
		$avataroptions['uploaded'] = '<input name="file" type="file">';
Scott committed
364

Scott committed
365 366 367 368
		if (isset($useraccount['avatarblobid']))
			$avataroptions['uploaded'] = '<span style="margin:2px 0; display:inline-block;">' .
				qa_get_avatar_blob_html($useraccount['avatarblobid'], $useraccount['avatarwidth'], $useraccount['avatarheight'], 32) .
				'</span>' . $avataroptions['uploaded'];
Scott committed
369

Scott committed
370 371 372
		if ($useraccount['flags'] & QA_USER_FLAGS_SHOW_AVATAR)
			$avatarvalue = $avataroptions['uploaded'];
	}
Scott committed
373

Scott committed
374 375 376 377 378 379 380 381
	$qa_content['form_profile']['fields']['avatar'] = array(
		'type' => 'select-radio',
		'label' => qa_lang_html('users/avatar_label'),
		'tags' => 'name="avatar"',
		'options' => $avataroptions,
		'value' => $avatarvalue,
		'error' => qa_html(@$errors['avatar']),
	);
Scott committed
382

Scott committed
383 384 385
} else {
	unset($qa_content['form_profile']['fields']['avatar']);
}
Scott committed
386 387


Scott committed
388
// Other profile fields
Scott committed
389

Scott committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
foreach ($userfields as $userfield) {
	$value = @$inprofile[$userfield['fieldid']];
	if (!isset($value))
		$value = @$userprofile[$userfield['title']];

	$label = trim(qa_user_userfield_label($userfield), ':');
	if (strlen($label))
		$label .= ':';

	$qa_content['form_profile']['fields'][$userfield['title']] = array(
		'label' => qa_html($label),
		'tags' => 'name="field_' . $userfield['fieldid'] . '"',
		'value' => qa_html($value),
		'error' => qa_html(@$errors[$userfield['fieldid']]),
		'rows' => ($userfield['flags'] & QA_FIELD_FLAGS_MULTI_LINE) ? 8 : null,
		'type' => $isblocked ? 'static' : 'text',
	);
}
Scott committed
408 409


Scott committed
410
// Raw information for plugin layers to access
Scott committed
411

Scott committed
412 413 414
$qa_content['raw']['account'] = $useraccount;
$qa_content['raw']['profile'] = $userprofile;
$qa_content['raw']['points'] = $userpoints;
Scott committed
415 416


Scott committed
417
// Change password form
Scott committed
418

Scott committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432
$qa_content['form_password'] = array(
	'tags' => 'method="post" action="' . qa_self_html() . '"',

	'style' => 'wide',

	'title' => qa_lang_html('users/change_password'),

	'fields' => array(
		'old' => array(
			'label' => qa_lang_html('users/old_password'),
			'tags' => 'name="oldpassword"',
			'value' => qa_html(@$inoldpassword),
			'type' => 'password',
			'error' => qa_html(@$errors['oldpassword']),
Scott committed
433 434
		),

Scott committed
435 436 437 438 439
		'new_1' => array(
			'label' => qa_lang_html('users/new_password_1'),
			'tags' => 'name="newpassword1"',
			'type' => 'password',
			'error' => qa_html(@$errors['password']),
Scott committed
440 441
		),

Scott committed
442 443 444 445 446
		'new_2' => array(
			'label' => qa_lang_html('users/new_password_2'),
			'tags' => 'name="newpassword2"',
			'type' => 'password',
			'error' => qa_html(@$errors['newpassword2']),
Scott committed
447
		),
Scott committed
448
	),
Scott committed
449

Scott committed
450 451 452 453 454
	'buttons' => array(
		'change' => array(
			'label' => qa_lang_html('users/change_password'),
		),
	),
Scott committed
455

Scott committed
456 457 458 459 460 461 462 463 464 465 466
	'hidden' => array(
		'dochangepassword' => array(
			'tags' => 'name="dochangepassword"',
			'value' => '1',
		),
		'code' => array(
			'tags' => 'name="code"',
			'value' => qa_get_form_security_code('password'),
		),
	),
);
Scott committed
467

Scott committed
468 469 470 471
if (!$haspassword && !$haspasswordold) {
	$qa_content['form_password']['fields']['old']['type'] = 'static';
	$qa_content['form_password']['fields']['old']['value'] = qa_lang_html('users/password_none');
}
Scott committed
472

Scott committed
473 474
if (qa_get_state() == 'password-changed')
	$qa_content['form_profile']['ok'] = qa_lang_html('users/password_changed');
Scott committed
475 476


Scott committed
477
$qa_content['navigation']['sub'] = qa_user_sub_navigation($useraccount['handle'], 'account', true);
Scott committed
478 479


Scott committed
480
return $qa_content;