loadCustomFields()   D
last analyzed

Complexity

Conditions 56

Size

Total Lines 142
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 56
eloc 85
nop 2
dl 0
loc 142
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file has the primary job of showing and editing people's profiles.
5
 * It also allows the user to change some of their or another's preferences,
6
 * and such things.
7
 *
8
 * Simple Machines Forum (SMF)
9
 *
10
 * @package SMF
11
 * @author Simple Machines https://www.simplemachines.org
12
 * @copyright 2022 Simple Machines and individual contributors
13
 * @license https://www.simplemachines.org/about/smf/license.php BSD
14
 *
15
 * @version 2.1.0
16
 */
17
18
if (!defined('SMF'))
19
	die('No direct access...');
20
21
/**
22
 * The main designating function for modifying profiles. Loads up info, determins what to do, etc.
23
 *
24
 * @param array $post_errors Any errors that occurred
25
 */
26
function ModifyProfile($post_errors = array())
27
{
28
	global $txt, $scripturl, $user_info, $context, $sourcedir, $user_profile, $cur_profile;
29
	global $modSettings, $memberContext, $profile_vars, $post_errors, $smcFunc;
30
31
	// Don't reload this as we may have processed error strings.
32
	if (empty($post_errors))
33
		loadLanguage('Profile+Drafts');
34
	loadTemplate('Profile');
35
36
	require_once($sourcedir . '/Subs-Menu.php');
37
38
	// Did we get the user by name...
39
	if (isset($_REQUEST['user']))
40
		$memberResult = loadMemberData($_REQUEST['user'], true, 'profile');
41
	// ... or by id_member?
42
	elseif (!empty($_REQUEST['u']))
43
		$memberResult = loadMemberData((int) $_REQUEST['u'], false, 'profile');
44
	// If it was just ?action=profile, edit your own profile, but only if you're not a guest.
45
	else
46
	{
47
		// Members only...
48
		is_not_guest();
49
		$memberResult = loadMemberData($user_info['id'], false, 'profile');
50
	}
51
52
	// Check if loadMemberData() has returned a valid result.
53
	if (!$memberResult)
54
		fatal_lang_error('not_a_user', false, 404);
55
56
	// If all went well, we have a valid member ID!
57
	list ($memID) = $memberResult;
58
	$memID = (int) $memID;
59
	$context['id_member'] = $memID;
60
	$cur_profile = $user_profile[$memID];
61
62
	// Let's have some information about this member ready, too.
63
	loadMemberContext($memID);
64
	$context['member'] = $memberContext[$memID];
65
66
	// Is this the profile of the user himself or herself?
67
	$context['user']['is_owner'] = $memID == $user_info['id'];
68
69
	// Group management isn't actually a permission. But we need it to be for this, so we need a phantom permission.
70
	// And we care about what the current user can do, not what the user whose profile it is.
71
	if ($user_info['mod_cache']['gq'] != '0=1')
72
		$user_info['permissions'][] = 'approve_group_requests';
73
74
	// If paid subscriptions are enabled, make sure we actually have at least one subscription available...
75
	$context['subs_available'] = false;
76
77
	if (!empty($modSettings['paid_enabled']))
78
	{
79
		$get_active_subs = $smcFunc['db_query']('', '
80
			SELECT COUNT(*)
81
			FROM {db_prefix}subscriptions
82
			WHERE active = {int:active}', array(
83
				'active' => 1,
84
			)
85
		);
86
87
		list ($num_subs) = $smcFunc['db_fetch_row']($get_active_subs);
88
89
		$context['subs_available'] = ($num_subs > 0);
90
91
		$smcFunc['db_free_result']($get_active_subs);
92
	}
93
94
	/* Define all the sections within the profile area!
95
		We start by defining the permission required - then SMF takes this and turns it into the relevant context ;)
96
		Possible fields:
97
			For Section:
98
				string $title:		Section title.
99
				array $areas:		Array of areas within this section.
100
101
			For Areas:
102
				string $label:		Text string that will be used to show the area in the menu.
103
				string $file:		Optional text string that may contain a file name that's needed for inclusion in order to display the area properly.
104
				string $custom_url:	Optional href for area.
105
				string $function:	Function to execute for this section. Can be a call to an static method: class::method
106
				string $class		If your function is a method, set the class field with your class's name and SMF will create a new instance for it.
107
				bool $enabled:		Should area be shown?
108
				string $sc:			Session check validation to do on save - note without this save will get unset - if set.
109
				bool $hidden:		Does this not actually appear on the menu?
110
				bool $password:		Whether to require the user's password in order to save the data in the area.
111
				array $subsections:	Array of subsections, in order of appearance.
112
				array $permission:	Array of permissions to determine who can access this area. Should contain arrays $own and $any.
113
	*/
114
	$profile_areas = array(
115
		'info' => array(
116
			'title' => $txt['profileInfo'],
117
			'areas' => array(
118
				'summary' => array(
119
					'label' => $txt['summary'],
120
					'file' => 'Profile-View.php',
121
					'function' => 'summary',
122
					'icon' => 'administration',
123
					'permission' => array(
124
						'own' => 'is_not_guest',
125
						'any' => 'profile_view',
126
					),
127
				),
128
				'popup' => array(
129
					'function' => 'profile_popup',
130
					'permission' => array(
131
						'own' => 'is_not_guest',
132
						'any' => array(),
133
					),
134
					'select' => 'summary',
135
				),
136
				'alerts_popup' => array(
137
					'function' => 'alerts_popup',
138
					'permission' => array(
139
						'own' => 'is_not_guest',
140
						'any' => array(),
141
					),
142
					'select' => 'summary',
143
				),
144
				'statistics' => array(
145
					'label' => $txt['statPanel'],
146
					'file' => 'Profile-View.php',
147
					'function' => 'statPanel',
148
					'icon' => 'stats',
149
					'permission' => array(
150
						'own' => 'is_not_guest',
151
						'any' => 'profile_view',
152
					),
153
				),
154
				'showposts' => array(
155
					'label' => $txt['showPosts'],
156
					'file' => 'Profile-View.php',
157
					'function' => 'showPosts',
158
					'icon' => 'posts',
159
					'subsections' => array(
160
						'messages' => array($txt['showMessages'], array('is_not_guest', 'profile_view')),
161
						'topics' => array($txt['showTopics'], array('is_not_guest', 'profile_view')),
162
						'unwatchedtopics' => array($txt['showUnwatched'], array('is_not_guest', 'profile_view'), 'enabled' => $context['user']['is_owner']),
163
						'attach' => array($txt['showAttachments'], array('is_not_guest', 'profile_view')),
164
					),
165
					'permission' => array(
166
						'own' => 'is_not_guest',
167
						'any' => 'profile_view',
168
					),
169
				),
170
				'showdrafts' => array(
171
					'label' => $txt['drafts_show'],
172
					'file' => 'Drafts.php',
173
					'function' => 'showProfileDrafts',
174
					'icon' => 'drafts',
175
					'enabled' => !empty($modSettings['drafts_post_enabled']) && $context['user']['is_owner'],
176
					'permission' => array(
177
						'own' => 'is_not_guest',
178
						'any' => array(),
179
					),
180
				),
181
				'showalerts' => array(
182
					'label' => $txt['alerts_show'],
183
					'file' => 'Profile-View.php',
184
					'function' => 'showAlerts',
185
					'icon' => 'alerts',
186
					'permission' => array(
187
						'own' => 'is_not_guest',
188
						'any' => array(),
189
					),
190
				),
191
				'permissions' => array(
192
					'label' => $txt['showPermissions'],
193
					'file' => 'Profile-View.php',
194
					'function' => 'showPermissions',
195
					'icon' => 'permissions',
196
					'permission' => array(
197
						'own' => 'manage_permissions',
198
						'any' => 'manage_permissions',
199
					),
200
				),
201
				'tracking' => array(
202
					'label' => $txt['trackUser'],
203
					'file' => 'Profile-View.php',
204
					'function' => 'tracking',
205
					'icon' => 'logs',
206
					'subsections' => array(
207
						'activity' => array($txt['trackActivity'], 'moderate_forum'),
208
						'ip' => array($txt['trackIP'], 'moderate_forum'),
209
						'edits' => array($txt['trackEdits'], 'moderate_forum', 'enabled' => !empty($modSettings['userlog_enabled'])),
210
						'groupreq' => array($txt['trackGroupRequests'], 'approve_group_requests', 'enabled' => !empty($modSettings['show_group_membership'])),
211
						'logins' => array($txt['trackLogins'], 'moderate_forum', 'enabled' => !empty($modSettings['loginHistoryDays'])),
212
					),
213
					'permission' => array(
214
						'own' => array('moderate_forum', 'approve_group_requests'),
215
						'any' => array('moderate_forum', 'approve_group_requests'),
216
					),
217
				),
218
				'viewwarning' => array(
219
					'label' => $txt['profile_view_warnings'],
220
					'enabled' => $modSettings['warning_settings'][0] == 1 && $cur_profile['warning'],
221
					'file' => 'Profile-View.php',
222
					'function' => 'viewWarning',
223
					'icon' => 'warning',
224
					'permission' => array(
225
						'own' => array('view_warning_own', 'view_warning_any', 'issue_warning', 'moderate_forum'),
226
						'any' => array('view_warning_any', 'issue_warning', 'moderate_forum'),
227
					),
228
				),
229
			),
230
		),
231
		'edit_profile' => array(
232
			'title' => $txt['forumprofile'],
233
			'areas' => array(
234
				'account' => array(
235
					'label' => $txt['account'],
236
					'file' => 'Profile-Modify.php',
237
					'function' => 'account',
238
					'icon' => 'maintain',
239
					'enabled' => $context['user']['is_admin'] || ($cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups']))),
240
					'sc' => 'post',
241
					'token' => 'profile-ac%u',
242
					'password' => true,
243
					'permission' => array(
244
						'own' => array('profile_identity_any', 'profile_identity_own', 'profile_password_any', 'profile_password_own', 'manage_membergroups'),
245
						'any' => array('profile_identity_any', 'profile_password_any', 'manage_membergroups'),
246
					),
247
				),
248
				'tfasetup' => array(
249
					'label' => $txt['account'],
250
					'file' => 'Profile-Modify.php',
251
					'function' => 'tfasetup',
252
					'token' => 'profile-tfa%u',
253
					'enabled' => !empty($modSettings['tfa_mode']),
254
					'hidden' => true,
255
					'select' => 'account',
256
					'permission' => array(
257
						'own' => array('profile_password_own'),
258
						'any' => array('profile_password_any'),
259
					),
260
				),
261
				'tfadisable' => array(
262
					'label' => $txt['account'],
263
					'file' => 'Profile-Modify.php',
264
					'function' => 'tfadisable',
265
					'token' => 'profile-tfa%u',
266
					'sc' => 'post',
267
					'password' => true,
268
					'enabled' => !empty($modSettings['tfa_mode']),
269
					'hidden' => true,
270
					'select' => 'account',
271
					'permission' => array(
272
						'own' => array('profile_password_own'),
273
						'any' => array('profile_password_any'),
274
					),
275
				),
276
				'forumprofile' => array(
277
					'label' => $txt['forumprofile'],
278
					'file' => 'Profile-Modify.php',
279
					'function' => 'forumProfile',
280
					'icon' => 'members',
281
					'sc' => 'post',
282
					'token' => 'profile-fp%u',
283
					'permission' => array(
284
						'own' => array('profile_forum_any', 'profile_forum_own'),
285
						'any' => array('profile_forum_any'),
286
					),
287
				),
288
				'theme' => array(
289
					'label' => $txt['theme'],
290
					'file' => 'Profile-Modify.php',
291
					'function' => 'theme',
292
					'icon' => 'features',
293
					'sc' => 'post',
294
					'token' => 'profile-th%u',
295
					'permission' => array(
296
						'own' => array('profile_extra_any', 'profile_extra_own'),
297
						'any' => array('profile_extra_any'),
298
					),
299
				),
300
				'notification' => array(
301
					'label' => $txt['notification'],
302
					'file' => 'Profile-Modify.php',
303
					'function' => 'notification',
304
					'icon' => 'alerts',
305
					'sc' => 'post',
306
					//'token' => 'profile-nt%u', This is not checked here. We do it in the function itself - but if it was checked, this is what it'd be.
307
					'subsections' => array(
308
						'alerts' => array($txt['alert_prefs'], array('is_not_guest', 'profile_extra_any')),
309
						'topics' => array($txt['watched_topics'], array('is_not_guest', 'profile_extra_any')),
310
						'boards' => array($txt['watched_boards'], array('is_not_guest', 'profile_extra_any')),
311
					),
312
					'permission' => array(
313
						'own' => array('is_not_guest'),
314
						'any' => array('profile_extra_any'), // If you change this, update it in the functions themselves; we delegate all saving checks there.
315
					),
316
				),
317
				'ignoreboards' => array(
318
					'label' => $txt['ignoreboards'],
319
					'file' => 'Profile-Modify.php',
320
					'function' => 'ignoreboards',
321
					'icon' => 'boards',
322
					'enabled' => !empty($modSettings['allow_ignore_boards']),
323
					'sc' => 'post',
324
					'token' => 'profile-ib%u',
325
					'permission' => array(
326
						'own' => array('profile_extra_any', 'profile_extra_own'),
327
						'any' => array('profile_extra_any'),
328
					),
329
				),
330
				'lists' => array(
331
					'label' => $txt['editBuddyIgnoreLists'],
332
					'file' => 'Profile-Modify.php',
333
					'function' => 'editBuddyIgnoreLists',
334
					'icon' => 'frenemy',
335
					'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'],
336
					'sc' => 'post',
337
					'subsections' => array(
338
						'buddies' => array($txt['editBuddies']),
339
						'ignore' => array($txt['editIgnoreList']),
340
					),
341
					'permission' => array(
342
						'own' => array('profile_extra_any', 'profile_extra_own'),
343
						'any' => array(),
344
					),
345
				),
346
				'groupmembership' => array(
347
					'label' => $txt['groupmembership'],
348
					'file' => 'Profile-Modify.php',
349
					'function' => 'groupMembership',
350
					'icon' => 'people',
351
					'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'],
352
					'sc' => 'request',
353
					'token' => 'profile-gm%u',
354
					'token_type' => 'request',
355
					'permission' => array(
356
						'own' => array('is_not_guest'),
357
						'any' => array('manage_membergroups'),
358
					),
359
				),
360
			),
361
		),
362
		'profile_action' => array(
363
			'title' => $txt['profileAction'],
364
			'areas' => array(
365
				'sendpm' => array(
366
					'label' => $txt['profileSendIm'],
367
					'custom_url' => $scripturl . '?action=pm;sa=send',
368
					'icon' => 'personal_message',
369
					'enabled' => allowedTo('profile_view'),
370
					'permission' => array(
371
						'own' => array(),
372
						'any' => array('pm_send'),
373
					),
374
				),
375
				'report' => array(
376
					'label' => $txt['report_profile'],
377
					'custom_url' => $scripturl . '?action=reporttm;' . $context['session_var'] . '=' . $context['session_id'],
378
					'icon' => 'warning',
379
					'enabled' => allowedTo('profile_view'),
380
					'permission' => array(
381
						'own' => array(),
382
						'any' => array('report_user'),
383
					),
384
				),
385
				'issuewarning' => array(
386
					'label' => $txt['profile_issue_warning'],
387
					'enabled' => $modSettings['warning_settings'][0] == 1,
388
					'file' => 'Profile-Actions.php',
389
					'function' => 'issueWarning',
390
					'icon' => 'warning',
391
					'token' => 'profile-iw%u',
392
					'permission' => array(
393
						'own' => array(),
394
						'any' => array('issue_warning'),
395
					),
396
				),
397
				'banuser' => array(
398
					'label' => $txt['profileBanUser'],
399
					'custom_url' => $scripturl . '?action=admin;area=ban;sa=add',
400
					'icon' => 'ban',
401
					'enabled' => $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])),
402
					'permission' => array(
403
						'own' => array(),
404
						'any' => array('manage_bans'),
405
					),
406
				),
407
				'subscriptions' => array(
408
					'label' => $txt['subscriptions'],
409
					'file' => 'Profile-Actions.php',
410
					'function' => 'subscriptions',
411
					'icon' => 'paid',
412
					'enabled' => !empty($modSettings['paid_enabled']) && $context['subs_available'],
413
					'permission' => array(
414
						'own' => array('is_not_guest'),
415
						'any' => array('moderate_forum'),
416
					),
417
				),
418
				'getprofiledata' => array(
419
					'label' => $txt['export_profile_data'],
420
					'file' => 'Profile-Export.php',
421
					'function' => 'export_profile_data',
422
					'icon' => 'packages',
423
					// 'token' => 'profile-ex%u', // This is not checked here. We do it in the function itself - but if it was checked, this is what it'd be.
424
					'permission' => array(
425
						'own' => array('profile_view_own'),
426
						'any' => array('moderate_forum'),
427
					),
428
				),
429
				'download' => array(
430
					'label' => $txt['export_profile_data'],
431
					'file' => 'Profile-Export.php',
432
					'function' => 'download_export_file',
433
					'icon' => 'packages',
434
					'hidden' => true,
435
					'select' => 'getprofiledata',
436
					'permission' => array(
437
						'own' => array('profile_view_own'),
438
						'any' => array('moderate_forum'),
439
					),
440
				),
441
				'dlattach' => array(
442
					'label' => $txt['export_profile_data'],
443
					'file' => 'Profile-Export.php',
444
					'function' => 'export_attachment',
445
					'icon' => 'packages',
446
					'hidden' => true,
447
					'select' => 'getprofiledata',
448
					'permission' => array(
449
						'own' => array('profile_view_own'),
450
						'any' => array(),
451
					),
452
				),
453
				'deleteaccount' => array(
454
					'label' => $txt['deleteAccount'],
455
					'file' => 'Profile-Actions.php',
456
					'function' => 'deleteAccount',
457
					'icon' => 'members_delete',
458
					'sc' => 'post',
459
					'token' => 'profile-da%u',
460
					'password' => true,
461
					'permission' => array(
462
						'own' => array('profile_remove_any', 'profile_remove_own'),
463
						'any' => array('profile_remove_any'),
464
					),
465
				),
466
				'activateaccount' => array(
467
					'file' => 'Profile-Actions.php',
468
					'function' => 'activateAccount',
469
					'icon' => 'regcenter',
470
					'sc' => 'get',
471
					'token' => 'profile-aa%u',
472
					'token_type' => 'get',
473
					'permission' => array(
474
						'own' => array(),
475
						'any' => array('moderate_forum'),
476
					),
477
				),
478
				// A logout link just for the popup menu.
479
				'logout' => array(
480
					'label' => $txt['logout'],
481
					'custom_url' => $scripturl . '?action=logout;%1$s=%2$s',
482
					'icon' => 'logout',
483
					'enabled' => !empty($_REQUEST['area']) && $_REQUEST['area'] === 'popup',
484
					'permission' => array(
485
						'own' => array('is_not_guest'),
486
						'any' => array(),
487
					),
488
				),
489
			),
490
		),
491
	);
492
493
	// Let them modify profile areas easily.
494
	call_integration_hook('integrate_pre_profile_areas', array(&$profile_areas));
495
496
	// Do some cleaning ready for the menu function.
497
	$context['password_areas'] = array();
498
	$current_area = isset($_REQUEST['area']) ? $_REQUEST['area'] : '';
499
500
	foreach ($profile_areas as $section_id => $section)
501
	{
502
		// Do a bit of spring cleaning so to speak.
503
		foreach ($section['areas'] as $area_id => $area)
504
		{
505
			// If it said no permissions that meant it wasn't valid!
506
			if (empty($area['permission'][$context['user']['is_owner'] ? 'own' : 'any']))
507
				$profile_areas[$section_id]['areas'][$area_id]['enabled'] = false;
508
			// Otherwise pick the right set.
509
			else
510
				$profile_areas[$section_id]['areas'][$area_id]['permission'] = $area['permission'][$context['user']['is_owner'] ? 'own' : 'any'];
511
512
			// Password required in most cases
513
			if (!empty($area['password']))
514
				$context['password_areas'][] = $area_id;
515
		}
516
	}
517
518
	// Is there an updated message to show?
519
	if (isset($_GET['updated']))
520
		$context['profile_updated'] = $txt['profile_updated_own'];
521
522
	// Set a few options for the menu.
523
	$menuOptions = array(
524
		'disable_url_session_check' => true,
525
		'current_area' => $current_area,
526
		'extra_url_parameters' => array(
527
			'u' => $context['id_member'],
528
		),
529
	);
530
531
	// Logging out requires the session id in the url.
532
	$profile_areas['profile_action']['areas']['logout']['custom_url'] = sprintf($profile_areas['profile_action']['areas']['logout']['custom_url'], $context['session_var'], $context['session_id']);
533
534
	// Actually create the menu!
535
	$profile_include_data = createMenu($profile_areas, $menuOptions);
536
537
	// No menu means no access.
538
	if (!$profile_include_data && (!$user_info['is_guest'] || validateSession()))
539
		fatal_lang_error('no_access', false);
540
541
	// Make a note of the Unique ID for this menu.
542
	$context['profile_menu_id'] = $context['max_menu_id'];
543
	$context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id'];
544
545
	// Set the selected item - now it's been validated.
546
	$current_area = $profile_include_data['current_area'];
547
	$current_sa = $profile_include_data['current_subsection'];
548
	$context['menu_item_selected'] = $current_area;
549
550
	// Before we go any further, let's work on the area we've said is valid. Note this is done here just in case we ever compromise the menu function in error!
551
	$context['completed_save'] = false;
552
	$context['do_preview'] = isset($_REQUEST['preview_signature']);
553
554
	$security_checks = array();
555
	$found_area = false;
556
	foreach ($profile_areas as $section_id => $section)
557
	{
558
		// Do a bit of spring cleaning so to speak.
559
		foreach ($section['areas'] as $area_id => $area)
560
		{
561
			// Is this our area?
562
			if ($current_area == $area_id)
563
			{
564
				// This can't happen - but is a security check.
565
				if ((isset($section['enabled']) && $section['enabled'] == false) || (isset($area['enabled']) && $area['enabled'] == false))
566
					fatal_lang_error('no_access', false);
567
568
				// Are we saving data in a valid area?
569
				if (isset($area['sc']) && (isset($_REQUEST['save']) || $context['do_preview']))
570
				{
571
					$security_checks['session'] = $area['sc'];
572
					$context['completed_save'] = true;
573
				}
574
575
				// Do we need to perform a token check?
576
				if (!empty($area['token']))
577
				{
578
					$security_checks[isset($_REQUEST['save']) ? 'validateToken' : 'needsToken'] = $area['token'];
579
					$token_name = $area['token'] !== true ? str_replace('%u', $context['id_member'], $area['token']) : 'profile-u' . $context['id_member'];
580
581
					$token_type = isset($area['token_type']) && in_array($area['token_type'], array('request', 'post', 'get')) ? $area['token_type'] : 'post';
582
				}
583
584
				// Does this require session validating?
585
				if (!empty($area['validate']) || (isset($_REQUEST['save']) && !$context['user']['is_owner'] && ($area_id != 'issuewarning' || empty($modSettings['securityDisable_moderate']))))
586
					$security_checks['validate'] = true;
587
588
				// Permissions for good measure.
589
				if (!empty($profile_include_data['permission']))
590
					$security_checks['permission'] = $profile_include_data['permission'];
591
592
				// Either way got something.
593
				$found_area = true;
594
			}
595
		}
596
	}
597
598
	// Oh dear, some serious security lapse is going on here... we'll put a stop to that!
599
	if (!$found_area)
600
		fatal_lang_error('no_access', false);
601
602
	// Release this now.
603
	unset($profile_areas);
604
605
	// Now the context is setup have we got any security checks to carry out additional to that above?
606
	if (isset($security_checks['session']))
607
		checkSession($security_checks['session']);
608
	if (isset($security_checks['validate']))
609
		validateSession();
610
	if (isset($security_checks['validateToken']))
611
		validateToken($token_name, $token_type);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $token_type does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $token_name does not seem to be defined for all execution paths leading up to this point.
Loading history...
612
	if (isset($security_checks['permission']))
613
		isAllowedTo($security_checks['permission']);
614
615
	// Create a token if needed.
616
	if (isset($security_checks['needsToken']) || isset($security_checks['validateToken']))
617
	{
618
		createToken($token_name, $token_type);
619
		$context['token_check'] = $token_name;
620
	}
621
622
	// File to include?
623
	if (isset($profile_include_data['file']))
624
		require_once($sourcedir . '/' . $profile_include_data['file']);
625
626
	// Build the link tree.
627
	$context['linktree'][] = array(
628
		'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : ''),
629
		'name' => sprintf($txt['profile_of_username'], $context['member']['name']),
630
	);
631
632
	if (!empty($profile_include_data['label']))
633
		$context['linktree'][] = array(
634
			'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'],
635
			'name' => $profile_include_data['label'],
636
		);
637
638
	if (!empty($profile_include_data['current_subsection']) && $profile_include_data['subsections'][$profile_include_data['current_subsection']][0] != $profile_include_data['label'])
639
		$context['linktree'][] = array(
640
			'url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'] . ';sa=' . $profile_include_data['current_subsection'],
641
			'name' => $profile_include_data['subsections'][$profile_include_data['current_subsection']][0],
642
		);
643
644
	// Set the template for this area and add the profile layer.
645
	$context['sub_template'] = $profile_include_data['function'];
646
	$context['template_layers'][] = 'profile';
647
648
	// All the subactions that require a user password in order to validate.
649
	$check_password = $context['user']['is_owner'] && in_array($profile_include_data['current_area'], $context['password_areas']);
650
	$context['require_password'] = $check_password;
651
652
	loadJavaScriptFile('profile.js', array('defer' => false, 'minimize' => true), 'smf_profile');
653
654
	// These will get populated soon!
655
	$post_errors = array();
656
	$profile_vars = array();
657
658
	// Right - are we saving - if so let's save the old data first.
659
	if ($context['completed_save'])
660
	{
661
		// Clean up the POST variables.
662
		$_POST = htmltrim__recursive($_POST);
663
		$_POST = htmlspecialchars__recursive($_POST);
664
665
		if ($check_password)
666
		{
667
			// Check to ensure we're forcing SSL for authentication
668
			if (!empty($modSettings['force_ssl']) && empty($maintenance) && !httpsOn())
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $maintenance seems to never exist and therefore empty should always be true.
Loading history...
669
				fatal_lang_error('login_ssl_required', false);
670
671
			$password = isset($_POST['oldpasswrd']) ? $_POST['oldpasswrd'] :  '';
672
673
			// You didn't even enter a password!
674
			if (trim($password) == '')
675
				$post_errors[] = 'no_password';
676
677
			// Since the password got modified due to all the $_POST cleaning, lets undo it so we can get the correct password
678
			$password = un_htmlspecialchars($password);
679
680
			// Does the integration want to check passwords?
681
			$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($cur_profile['member_name'], $password, false)), true);
682
683
			// Bad password!!!
684
			if (!$good_password && !hash_verify_password($user_profile[$memID]['member_name'], $password, $user_info['passwd']))
685
				$post_errors[] = 'bad_password';
686
687
			// Warn other elements not to jump the gun and do custom changes!
688
			if (in_array('bad_password', $post_errors))
689
				$context['password_auth_failed'] = true;
690
		}
691
692
		// Change the IP address in the database.
693
		if ($context['user']['is_owner'] && $menuOptions['current_area'] != 'tfasetup')
694
			$profile_vars['member_ip'] = $user_info['ip'];
695
696
		// Now call the sub-action function...
697
		if ($current_area == 'activateaccount')
698
		{
699
			if (empty($post_errors))
700
				activateAccount($memID);
701
		}
702
		elseif ($current_area == 'deleteaccount')
703
		{
704
			if (empty($post_errors))
705
			{
706
				deleteAccount2($memID);
707
				redirectexit();
708
			}
709
		}
710
		elseif ($menuOptions['current_area'] == 'tfadisable')
711
		{
712
			// Already checked the password, token, permissions, and session.
713
			$profile_vars += array(
714
				'tfa_secret' => '',
715
				'tfa_backup' => '',
716
			);
717
		}
718
		elseif ($current_area == 'groupmembership' && empty($post_errors))
719
		{
720
			$msg = groupMembership2($profile_vars, $post_errors, $memID);
721
722
			// Whatever we've done, we have nothing else to do here...
723
			redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=groupmembership' . (!empty($msg) ? ';msg=' . $msg : ''));
724
		}
725
		elseif (in_array($current_area, array('account', 'forumprofile', 'theme')))
726
			saveProfileFields();
727
		else
728
		{
729
			$force_redirect = true;
730
			// Ensure we include this.
731
			require_once($sourcedir . '/Profile-Modify.php');
732
			saveProfileChanges($profile_vars, $post_errors, $memID);
733
		}
734
735
		call_integration_hook('integrate_profile_save', array(&$profile_vars, &$post_errors, $memID, $cur_profile, $current_area));
736
737
		// There was a problem, let them try to re-enter.
738
		if (!empty($post_errors))
739
		{
740
			// Load the language file so we can give a nice explanation of the errors.
741
			loadLanguage('Errors');
742
			$context['post_errors'] = $post_errors;
743
		}
744
		elseif (!empty($profile_vars))
745
		{
746
			// If we've changed the password, notify any integration that may be listening in.
747
			if (isset($profile_vars['passwd']))
748
				call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd2']));
749
750
			updateMemberData($memID, $profile_vars);
751
752
			// What if this is the newest member?
753
			if ($modSettings['latestMember'] == $memID)
754
				updateStats('member');
755
			elseif (isset($profile_vars['real_name']))
756
				updateSettings(array('memberlist_updated' => time()));
757
758
			// If the member changed his/her birthdate, update calendar statistics.
759
			if (isset($profile_vars['birthdate']) || isset($profile_vars['real_name']))
760
				updateSettings(array(
761
					'calendar_updated' => time(),
762
				));
763
764
			// Anything worth logging?
765
			if (!empty($context['log_changes']) && !empty($modSettings['modlog_enabled']))
766
			{
767
				$log_changes = array();
768
				require_once($sourcedir . '/Logging.php');
769
				foreach ($context['log_changes'] as $k => $v)
770
					$log_changes[] = array(
771
						'action' => $k,
772
						'log_type' => 'user',
773
						'extra' => array_merge($v, array(
774
							'applicator' => $user_info['id'],
775
							'member_affected' => $memID,
776
						)),
777
					);
778
779
				logActions($log_changes);
780
			}
781
782
			// Have we got any post save functions to execute?
783
			if (!empty($context['profile_execute_on_save']))
784
				foreach ($context['profile_execute_on_save'] as $saveFunc)
785
					$saveFunc();
786
787
			// Let them know it worked!
788
			$context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : sprintf($txt['profile_updated_else'], $cur_profile['member_name']);
789
790
			// Invalidate any cached data.
791
			cache_put_data('member_data-profile-' . $memID, null, 0);
792
		}
793
	}
794
795
	// Have some errors for some reason?
796
	if (!empty($post_errors))
797
	{
798
		// Set all the errors so the template knows what went wrong.
799
		foreach ($post_errors as $error_type)
800
			$context['modify_error'][$error_type] = true;
801
	}
802
	// If it's you then we should redirect upon save.
803
	elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview'])
804
		redirectexit('action=profile;area=' . $current_area . (!empty($current_sa) ? ';sa=' . $current_sa : '') . ';updated');
805
	elseif (!empty($force_redirect))
806
		redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area);
807
808
	// Get the right callable.
809
	$call = call_helper($profile_include_data['function'], true);
810
811
	// Is it valid?
812
	if (!empty($call))
813
		call_user_func($call, $memID);
814
815
	// Set the page title if it's not already set...
816
	if (!isset($context['page_title']))
817
		$context['page_title'] = $txt['profile'] . (isset($txt[$current_area]) ? ' - ' . $txt[$current_area] : '');
818
}
819
820
/**
821
 * Set up the requirements for the profile popup - the area that is shown as the popup menu for the current user.
822
 *
823
 * @param int $memID The ID of the member
824
 */
825
function profile_popup($memID)
826
{
827
	global $context, $scripturl, $txt, $db_show_debug;
828
829
	// We do not want to output debug information here.
830
	$db_show_debug = false;
831
832
	// We only want to output our little layer here.
833
	$context['template_layers'] = array();
834
835
	// This list will pull from the master list wherever possible. Hopefully it should be clear what does what.
836
	$profile_items = array(
837
		array(
838
			'menu' => 'edit_profile',
839
			'area' => 'account',
840
		),
841
		array(
842
			'menu' => 'edit_profile',
843
			'area' => 'forumprofile',
844
			'title' => $txt['popup_forumprofile'],
845
		),
846
		array(
847
			'menu' => 'edit_profile',
848
			'area' => 'theme',
849
			'title' => $txt['theme'],
850
		),
851
		array(
852
			'menu' => 'edit_profile',
853
			'area' => 'notification',
854
		),
855
		array(
856
			'menu' => 'edit_profile',
857
			'area' => 'ignoreboards',
858
		),
859
		array(
860
			'menu' => 'edit_profile',
861
			'area' => 'lists',
862
			'url' => $scripturl . '?action=profile;area=lists;sa=ignore',
863
			'title' => $txt['popup_ignore'],
864
		),
865
		array(
866
			'menu' => 'info',
867
			'area' => 'showposts',
868
			'title' => $txt['popup_showposts'],
869
		),
870
		array(
871
			'menu' => 'info',
872
			'area' => 'showdrafts',
873
			'title' => $txt['popup_showdrafts'],
874
		),
875
		array(
876
			'menu' => 'edit_profile',
877
			'area' => 'groupmembership',
878
			'title' => $txt['popup_groupmembership'],
879
		),
880
		array(
881
			'menu' => 'profile_action',
882
			'area' => 'subscriptions',
883
			'title' => $txt['popup_subscriptions'],
884
		),
885
		array(
886
			'menu' => 'profile_action',
887
			'area' => 'logout',
888
		),
889
	);
890
891
	call_integration_hook('integrate_profile_popup', array(&$profile_items));
892
893
	// Now check if these items are available
894
	$context['profile_items'] = array();
895
	$menu_context = &$context[$context['profile_menu_name']]['sections'];
896
	foreach ($profile_items as $item)
897
	{
898
		if (isset($menu_context[$item['menu']]['areas'][$item['area']]))
899
		{
900
			$context['profile_items'][] = $item;
901
		}
902
	}
903
}
904
905
/**
906
 * Set up the requirements for the alerts popup - the area that shows all the alerts just quickly for the current user.
907
 *
908
 * @param int $memID The ID of the member
909
 */
910
function alerts_popup($memID)
911
{
912
	global $context, $sourcedir, $db_show_debug, $cur_profile, $modSettings;
913
914
	// Load the Alerts language file.
915
	loadLanguage('Alerts');
916
917
	// We do not want to output debug information here.
918
	$db_show_debug = false;
919
920
	// We only want to output our little layer here.
921
	$context['template_layers'] = array();
922
923
	// No funny business allowed
924
	$counter = isset($_REQUEST['counter']) ? max(0, (int) $_REQUEST['counter']) : 0;
925
	$limit = !empty($modSettings['alerts_per_page']) && (int) $modSettings['alerts_per_page'] < 1000 ? min((int) $modSettings['alerts_per_page'], 1000) : 25;
926
927
	$context['unread_alerts'] = array();
928
	if ($counter < $cur_profile['alerts'])
929
	{
930
		// Now fetch me my unread alerts, pronto!
931
		require_once($sourcedir . '/Profile-View.php');
932
		$context['unread_alerts'] = fetch_alerts($memID, false, !empty($counter) ? $cur_profile['alerts'] - $counter : $limit, 0, !isset($_REQUEST['counter']));
0 ignored issues
show
Bug introduced by
It seems like ! empty($counter) ? $cur...s'] - $counter : $limit can also be of type integer; however, parameter $limit of fetch_alerts() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

932
		$context['unread_alerts'] = fetch_alerts($memID, false, /** @scrutinizer ignore-type */ !empty($counter) ? $cur_profile['alerts'] - $counter : $limit, 0, !isset($_REQUEST['counter']));
Loading history...
Bug introduced by
0 of type integer is incompatible with the type array expected by parameter $offset of fetch_alerts(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

932
		$context['unread_alerts'] = fetch_alerts($memID, false, !empty($counter) ? $cur_profile['alerts'] - $counter : $limit, /** @scrutinizer ignore-type */ 0, !isset($_REQUEST['counter']));
Loading history...
933
934
		// This shouldn't happen, but just in case...
935
		if (empty($counter) && $cur_profile['alerts'] != count($context['unread_alerts']))
936
			updateMemberData($memID, array('alerts' => count($context['unread_alerts'])));
937
	}
938
}
939
940
/**
941
 * Load any custom fields for this area... no area means load all, 'summary' loads all public ones.
942
 *
943
 * @param int $memID The ID of the member
944
 * @param string $area Which area to load fields for
945
 */
946
function loadCustomFields($memID, $area = 'summary')
947
{
948
	global $context, $txt, $user_profile, $smcFunc, $user_info, $settings, $scripturl;
949
950
	// Get the right restrictions in place...
951
	$where = 'active = 1';
952
	if (!allowedTo('admin_forum') && $area != 'register')
953
	{
954
		// If it's the owner they can see two types of private fields, regardless.
955
		if ($memID == $user_info['id'])
956
			$where .= $area == 'summary' ? ' AND private < 3' : ' AND (private = 0 OR private = 2)';
957
		else
958
			$where .= $area == 'summary' ? ' AND private < 2' : ' AND private = 0';
959
	}
960
961
	if ($area == 'register')
962
		$where .= ' AND show_reg != 0';
963
	elseif ($area != 'summary')
964
		$where .= ' AND show_profile = {string:area}';
965
966
	// Load all the relevant fields - and data.
967
	$request = $smcFunc['db_query']('', '
968
		SELECT
969
			col_name, field_name, field_desc, field_type, field_order, show_reg, field_length, field_options,
970
			default_value, bbc, enclose, placement
971
		FROM {db_prefix}custom_fields
972
		WHERE ' . $where . '
973
		ORDER BY field_order',
974
		array(
975
			'area' => $area,
976
		)
977
	);
978
	$context['custom_fields'] = array();
979
	$context['custom_fields_required'] = false;
980
	while ($row = $smcFunc['db_fetch_assoc']($request))
981
	{
982
		// Shortcut.
983
		$exists = $memID && isset($user_profile[$memID], $user_profile[$memID]['options'][$row['col_name']]);
984
		$value = $exists ? $user_profile[$memID]['options'][$row['col_name']] : '';
985
986
		$currentKey = 0;
987
		if (!empty($row['field_options']))
988
		{
989
			$fieldOptions = explode(',', $row['field_options']);
990
			foreach ($fieldOptions as $k => $v)
991
			{
992
				if (empty($currentKey))
993
					$currentKey = $v === $value ? $k : 0;
994
			}
995
		}
996
997
		// If this was submitted already then make the value the posted version.
998
		if (isset($_POST['customfield']) && isset($_POST['customfield'][$row['col_name']]))
999
		{
1000
			$value = $smcFunc['htmlspecialchars']($_POST['customfield'][$row['col_name']]);
1001
			if (in_array($row['field_type'], array('select', 'radio')))
1002
				$value = ($options = explode(',', $row['field_options'])) && isset($options[$value]) ? $options[$value] : '';
1003
		}
1004
1005
		// Don't show the "disabled" option for the "gender" field if we are on the "summary" area.
1006
		if ($area == 'summary' && $row['col_name'] == 'cust_gender' && $value == '{gender_0}')
1007
			continue;
1008
1009
		// HTML for the input form.
1010
		$output_html = $value;
1011
		if ($row['field_type'] == 'check')
1012
		{
1013
			$true = (!$exists && $row['default_value']) || $value;
1014
			$input_html = '<input type="checkbox" name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"' . ($true ? ' checked' : '') . '>';
1015
			$output_html = $true ? $txt['yes'] : $txt['no'];
1016
		}
1017
		elseif ($row['field_type'] == 'select')
1018
		{
1019
			$input_html = '<select name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"><option value="-1"></option>';
1020
			$options = explode(',', $row['field_options']);
1021
			foreach ($options as $k => $v)
1022
			{
1023
				$true = (!$exists && $row['default_value'] == $v) || $value == $v;
1024
				$input_html .= '<option value="' . $k . '"' . ($true ? ' selected' : '') . '>' . tokenTxtReplace($v) . '</option>';
1025
				if ($true)
1026
					$output_html = $v;
1027
			}
1028
1029
			$input_html .= '</select>';
1030
		}
1031
		elseif ($row['field_type'] == 'radio')
1032
		{
1033
			$input_html = '<fieldset>';
1034
			$options = explode(',', $row['field_options']);
1035
			foreach ($options as $k => $v)
1036
			{
1037
				$true = (!$exists && $row['default_value'] == $v) || $value == $v;
1038
				$input_html .= '<label for="customfield_' . $row['col_name'] . '_' . $k . '"><input type="radio" name="customfield[' . $row['col_name'] . ']" id="customfield_' . $row['col_name'] . '_' . $k . '" value="' . $k . '"' . ($true ? ' checked' : '') . '>' . tokenTxtReplace($v) . '</label><br>';
1039
				if ($true)
1040
					$output_html = $v;
1041
			}
1042
			$input_html .= '</fieldset>';
1043
		}
1044
		elseif ($row['field_type'] == 'text')
1045
		{
1046
			$input_html = '<input type="text" name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"' . ($row['field_length'] != 0 ? ' maxlength="' . $row['field_length'] . '"' : '') . ' size="' . ($row['field_length'] == 0 || $row['field_length'] >= 50 ? 50 : ($row['field_length'] > 30 ? 30 : ($row['field_length'] > 10 ? 20 : 10))) . '" value="' . un_htmlspecialchars($value) . '"' . ($row['show_reg'] == 2 ? ' required' : '') . '>';
1047
		}
1048
		else
1049
		{
1050
			@list ($rows, $cols) = @explode(',', $row['default_value']);
1051
			$input_html = '<textarea name="customfield[' . $row['col_name'] . ']" id="customfield[' . $row['col_name'] . ']"' . ($row['field_length'] != 0 ? ' maxlength="' . $row['field_length'] . '"' : '') . (!empty($rows) ? ' rows="' . $rows . '"' : '') . (!empty($cols) ? ' cols="' . $cols . '"' : '') . ($row['show_reg'] == 2 ? ' required' : '') . '>' . un_htmlspecialchars($value) . '</textarea>';
1052
		}
1053
1054
		// Parse BBCode
1055
		if ($row['bbc'])
1056
			$output_html = parse_bbc($output_html);
1057
		elseif ($row['field_type'] == 'textarea')
1058
			// Allow for newlines at least
1059
			$output_html = strtr($output_html, array("\n" => '<br>'));
1060
1061
		// Enclosing the user input within some other text?
1062
		if (!empty($row['enclose']) && !empty($output_html))
1063
			$output_html = strtr($row['enclose'], array(
1064
				'{SCRIPTURL}' => $scripturl,
1065
				'{IMAGES_URL}' => $settings['images_url'],
1066
				'{DEFAULT_IMAGES_URL}' => $settings['default_images_url'],
1067
				'{INPUT}' => un_htmlspecialchars($output_html),
1068
				'{KEY}' => $currentKey
1069
			));
1070
1071
		$context['custom_fields'][] = array(
1072
			'name' => tokenTxtReplace($row['field_name']),
1073
			'desc' => tokenTxtReplace($row['field_desc']),
1074
			'type' => $row['field_type'],
1075
			'order' => $row['field_order'],
1076
			'input_html' => $input_html,
1077
			'output_html' => tokenTxtReplace($output_html),
1078
			'placement' => $row['placement'],
1079
			'colname' => $row['col_name'],
1080
			'value' => $value,
1081
			'show_reg' => $row['show_reg'],
1082
		);
1083
		$context['custom_fields_required'] = $context['custom_fields_required'] || $row['show_reg'] == 2;
1084
	}
1085
	$smcFunc['db_free_result']($request);
1086
1087
	call_integration_hook('integrate_load_custom_profile_fields', array($memID, $area));
1088
}
1089
1090
?>