account.php 16.1 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 22
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	File: qa-include/qa-page-account.php
	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
23 24 25 26
if (!defined('QA_VERSION')) { // don't allow this page to be requested directly from browser
	header('Location: ../');
	exit;
}
Scott committed
27

Scott committed
28 29 30 31 32
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
33 34 35 36


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

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

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

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


//	Get current information on user

Scott committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
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;
$pending_confirmation = $doconfirms && $permit_error == 'confirm';
Scott committed
68

Scott committed
69
//	Process profile if saved
70

Scott committed
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 104
// 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
105

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

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

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

Scott committed
115 116
			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
117

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

Scott committed
121 122 123 124 125
				// 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
126

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

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

Scott committed
136 137 138
						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
139

Scott committed
140 141 142 143 144 145 146 147
							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
148

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

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

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

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

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

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

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

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

Scott committed
181 182 183 184
			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;
185

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

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

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

Scott committed
195 196 197 198 199 200
			qa_logged_in_user_flush();
		}
	}


	//	Process change password if clicked
201

Scott committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
	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');
217
				}
Scott committed
218 219 220 221 222 223 224 225 226 227 228
			} 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');
229

Scott committed
230 231 232 233
			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
234

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

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

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

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

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

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

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

Scott committed
255 256 257 258 259 260
	'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
261

Scott committed
262 263 264 265 266 267
		'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
268

Scott committed
269 270 271 272 273 274 275
		'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
276

Scott committed
277 278 279 280 281 282 283 284
		'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),
			'type' => $pending_confirmation ? 'text' : ($isblocked ? 'static' : 'text'),
		),
Scott committed
285

Scott committed
286 287 288 289 290 291 292
		'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
293

Scott committed
294 295 296 297 298 299 300
		'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
301

Scott committed
302 303 304 305 306 307
		'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
308 309
		),

Scott committed
310 311 312 313 314 315 316
		'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
317
		),
Scott committed
318
	),
Scott committed
319

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

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

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

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

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

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

//	Avatar upload stuff

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

Scott committed
354 355 356 357 358 359
	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
360

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

Scott committed
363 364 365 366 367 368
	if (qa_opt('avatar_allow_gravatar') && !$pending_confirmation) {
		$avataroptions['gravatar'] = '<span style="margin:2px 0; display:inline-block;">' .
			qa_get_gravatar_html($useraccount['email'], 32) . ' ' . strtr(qa_lang_html('users/avatar_gravatar'), array(
				'^1' => '<a href="http://www.gravatar.com/" target="_blank">',
				'^2' => '</a>',
			)) . '</span>';
Scott committed
369

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

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

Scott committed
377 378 379 380
		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
381

Scott committed
382 383 384
		if ($useraccount['flags'] & QA_USER_FLAGS_SHOW_AVATAR)
			$avatarvalue = $avataroptions['uploaded'];
	}
Scott committed
385

Scott committed
386 387 388 389 390 391 392 393
	$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
394

Scott committed
395 396 397
} else {
	unset($qa_content['form_profile']['fields']['avatar']);
}
Scott committed
398 399 400 401


//	Other profile fields

Scott committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
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
420 421 422 423


//	Raw information for plugin layers to access

Scott committed
424 425 426
$qa_content['raw']['account'] = $useraccount;
$qa_content['raw']['profile'] = $userprofile;
$qa_content['raw']['points'] = $userpoints;
Scott committed
427 428 429 430


//	Change password form

Scott committed
431 432 433 434 435 436 437 438 439 440 441 442 443 444
$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
445 446
		),

Scott committed
447 448 449 450 451
		'new_1' => array(
			'label' => qa_lang_html('users/new_password_1'),
			'tags' => 'name="newpassword1"',
			'type' => 'password',
			'error' => qa_html(@$errors['password']),
Scott committed
452 453
		),

Scott committed
454 455 456 457 458
		'new_2' => array(
			'label' => qa_lang_html('users/new_password_2'),
			'tags' => 'name="newpassword2"',
			'type' => 'password',
			'error' => qa_html(@$errors['newpassword2']),
Scott committed
459
		),
Scott committed
460
	),
Scott committed
461

Scott committed
462 463 464 465 466
	'buttons' => array(
		'change' => array(
			'label' => qa_lang_html('users/change_password'),
		),
	),
Scott committed
467

Scott committed
468 469 470 471 472 473 474 475 476 477 478
	'hidden' => array(
		'dochangepassword' => array(
			'tags' => 'name="dochangepassword"',
			'value' => '1',
		),
		'code' => array(
			'tags' => 'name="code"',
			'value' => qa_get_form_security_code('password'),
		),
	),
);
Scott committed
479

Scott committed
480 481 482 483
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
484

Scott committed
485 486
if (qa_get_state() == 'password-changed')
	$qa_content['form_profile']['ok'] = qa_lang_html('users/password_changed');
Scott committed
487 488


Scott committed
489
$qa_content['navigation']['sub'] = qa_user_sub_navigation($useraccount['handle'], 'account', true);
Scott committed
490 491


Scott committed
492
return $qa_content;