Completed
Pull Request — development (#3246)
by Emanuele
16:38
created

Profile_Controller::_build_profile_linktree()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4.1574

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 0
dl 0
loc 25
ccs 11
cts 14
cp 0.7856
crap 4.1574
rs 9.52
c 0
b 0
f 0
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
 * @name      ElkArte Forum
9
 * @copyright ElkArte Forum contributors
10
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
11
 *
12
 * This file contains code covered by:
13
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
14
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
15
 *
16
 * @version 2.0 dev
17
 *
18
 */
19
20
/**
21
 * Profile_Controller class
22
 * Has the job of showing and editing people's profiles.
23
 */
24
class Profile_Controller extends Action_Controller
25
{
26
	/**
27
	 * If the save was successful or not
28
	 * @var boolean
29
	 */
30
	private $_completed_save = false;
31
32
	/**
33
	 * If this was a request to save an update
34
	 * @var null
35
	 */
36
	private $_saving = null;
37
38
	/**
39
	 * What it says, on completion
40
	 * @var bool
41
	 */
42
	private $_force_redirect;
43
44
	/**
45
	 * Holds the output of createMenu for the profile areas
46
	 * @var array|boolean
47
	 */
48
	private $_profile_include_data;
49
50
	/**
51
	 * The current area chosen from the menu
52
	 * @var string
53
	 */
54
	private $_current_area;
55
56
	/**
57
	 * Member id for the history being viewed
58
	 * @var int
59
	 */
60
	private $_memID = 0;
61
62
	/**
63
	 * The array from $user_profile stored here to avoid some global
64
	 * @var mixed[]
65
	 */
66
	private $_profile = [];
67
68
	/**
69
	 * Called before all other methods when coming from the dispatcher or
70
	 * action class.
71
	 */
72 1 View Code Duplication
	public function pre_dispatch()
73
	{
74 1
		global $user_profile;
75
76 1
		require_once(SUBSDIR . '/Menu.subs.php');
77 1
		require_once(SUBSDIR . '/Profile.subs.php');
78
79 1
		$this->_memID = currentMemberID();
80 1
		$this->_profile = $user_profile[$this->_memID];
81 1
	}
82
83
	/**
84
	 * Allow the change or view of profiles.
85
	 *
86
	 * - Fires the pre_load event
87
	 *
88
	 * @see Action_Controller::action_index()
89
	 */
90 1
	public function action_index()
91
	{
92 1
		global $txt, $user_info, $context, $cur_profile, $memberContext;
93 1
		global $profile_vars, $post_errors;
94
95
		// Don't reload this as we may have processed error strings.
96 1
		if (empty($post_errors))
97 1
			theme()->getTemplates()->loadLanguageFile('Profile');
98 1
		theme()->getTemplates()->load('Profile');
99
100
		// Trigger profile pre-load event
101 1
		$this->_events->trigger('pre_load', array('post_errors' => $post_errors));
102
103
		// A little bit about this member
104 1
		$context['id_member'] = $this->_memID;
105 1
		$cur_profile = $this->_profile;
106
107
		// Let's have some information about this member ready, too.
108 1
		loadMemberContext($this->_memID);
109 1
		$context['member'] = $memberContext[$this->_memID];
110
111
		// Is this the profile of the user himself or herself?
112 1
		$context['user']['is_owner'] = (int) $this->_memID === (int) $user_info['id'];
113
114
		// Create the menu of profile options
115 1
		$this->_define_profile_menu();
116
117
		// Is there an updated message to show?
118 1
		if (isset($this->_req->query->updated))
119
			$context['profile_updated'] = $txt['profile_updated_own'];
120
121
		// If it said no permissions that meant it wasn't valid!
122 1
		if ($this->_profile_include_data && empty($this->_profile_include_data['permission']))
123
			$this->_profile_include_data['enabled'] = false;
124
125
		// No menu and guest? A warm welcome to register
126 1
		if (!$this->_profile_include_data && $user_info['is_guest'])
127
			is_not_guest();
128
129
		// No menu means no access at all.
130 1
		if (!$this->_profile_include_data || (isset($this->_profile_include_data['enabled']) && $this->_profile_include_data['enabled'] === false))
131
			throw new Elk_Exception('no_access', false);
132
133
		// Make a note of the Unique ID for this menu.
134 1
		$context['profile_menu_id'] = $context['max_menu_id'];
135 1
		$context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id'];
136
137
		// Set the selected item - now it's been validated.
138 1
		$this->_current_area = $this->_profile_include_data['current_area'];
139 1
		$context['menu_item_selected'] = $this->_current_area;
140
141
		// Before we go any further, let's work on the area we've said is valid.
142
		// Note this is done here just in case we ever compromise the menu function in error!
143 1
		$this->_completed_save = false;
144 1
		$context['do_preview'] = isset($this->_req->post->preview_signature);
145
146
		// Are we saving data in a valid area?
147 1
		$this->_saving = $this->_req->getPost('save', 'trim', $this->_req->getQuery('save', 'trim', null));
148 1
		if (isset($this->_profile_include_data['sc']) && (isset($this->_saving) || $context['do_preview']))
149
		{
150
			checkSession($this->_profile_include_data['sc']);
151
			$this->_completed_save = true;
152
		}
153
154
		// Permissions for good measure.
155 1
		if (!empty($this->_profile_include_data['permission']))
156 1
			isAllowedTo($this->_profile_include_data['permission'][$context['user']['is_owner'] ? 'own' : 'any']);
157
158
		// Session validation and/or Token Checks
159 1
		$this->_check_access();
160
161
		// Build the link tree.
162 1
		$this->_build_profile_linktree();
163
164
		// Set the template for this area... if you still can :P
165
		// and add the profile layer.
166 1
		$context['sub_template'] = $this->_profile_include_data['function'];
167 1
		theme()->getLayers()->add('profile');
168
169
		// Need JS if we made it this far
170 1
		loadJavascriptFile('profile.js');
171
172
		// Right - are we saving - if so let's save the old data first.
173 1
		$this->_save_updates();
174
175
		// Have some errors for some reason?
176
		// @todo check that this can be safely removed.
177 1
		if (!empty($post_errors))
178
		{
179
			// Set all the errors so the template knows what went wrong.
180
			foreach ($post_errors as $error_type)
181
				$context['modify_error'][$error_type] = true;
182
		}
183
		// If it's you then we should redirect upon save.
184 1
		elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview'])
185
			redirectexit('action=profile;area=' . $this->_current_area . ';updated');
186 1
		elseif (!empty($this->_force_redirect))
187
			redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $this->_memID) . ';area=' . $this->_current_area);
188
189
		// Let go to the right place
190 1
		if (isset($this->_profile_include_data['file']))
191
			require_once($this->_profile_include_data['file']);
192
193 1
		callMenu($this->_profile_include_data);
0 ignored issues
show
Bug introduced by
It seems like $this->_profile_include_data can also be of type boolean; however, callMenu() does only seem to accept array|string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
194
195
		// Set the page title if it's not already set...
196 1
		if (!isset($context['page_title']))
197
			$context['page_title'] = $txt['profile'] . (isset($txt[$this->_current_area]) ? ' - ' . $txt[$this->_current_area] : '');
198 1
	}
199
200
	/**
201
	 * Define all the sections within the profile area!
202
	 *
203
	 * We start by defining the permission required - then we take this and turn
204
	 * it into the relevant context ;)
205
	 *
206
	 * Possible fields:
207
	 *   For Section:
208
	 *    - string $title: Section title.
209
	 *    - array $areas:  Array of areas within this section.
210
	 *
211
	 *   For Areas:
212
	 *    - string $label:      Text string that will be used to show the area in the menu.
213
	 *    - string $file:       Optional text string that may contain a file name that's needed for inclusion in order to display the area properly.
214
	 *    - string $custom_url: Optional href for area.
215
	 *    - string $function:   Function to execute for this section.
216
	 *    - bool $enabled:      Should area be shown?
217
	 *    - string $sc:         Session check validation to do on save - note without this save will get unset - if set.
218
	 *    - bool $hidden:       Does this not actually appear on the menu?
219
	 *    - bool $password:     Whether to require the user's password in order to save the data in the area.
220
	 *    - array $subsections: Array of subsections, in order of appearance.
221
	 *    - array $permission:  Array of permissions to determine who can access this area. Should contain arrays $own and $any.
222
	 */
223 1
	private function _define_profile_menu()
224
	{
225 1
		global $txt, $context, $modSettings;
226
227
		$profile_areas = array(
228 1
			'info' => array(
229 1
				'title' => $txt['profileInfo'],
230
				'areas' => array(
231
					'summary' => array(
232 1
						'label' => $txt['summary'],
233 1
						'controller' => 'ProfileInfo_Controller',
234 1
						'function' => 'action_summary',
235
						// From the summary it's possible to activate an account, so we need the token
236 1
						'token' => 'profile-aa%u',
237 1
						'token_type' => 'get',
238
						'permission' => array(
239
							'own' => 'profile_view_own',
240
							'any' => 'profile_view_any',
241
						),
242
					),
243
					'statistics' => array(
244 1
						'label' => $txt['statPanel'],
245 1
						'controller' => 'ProfileInfo_Controller',
246 1
						'function' => 'action_statPanel',
247
						'permission' => array(
248
							'own' => 'profile_view_own',
249
							'any' => 'profile_view_any',
250
						),
251
					),
252
					'showposts' => array(
253 1
						'label' => $txt['showPosts'],
254 1
						'controller' => 'ProfileInfo_Controller',
255 1
						'function' => 'action_showPosts',
256
						'subsections' => array(
257 1
							'messages' => array($txt['showMessages'], array('profile_view_own', 'profile_view_any')),
258 1
							'topics' => array($txt['showTopics'], array('profile_view_own', 'profile_view_any')),
259 1
							'unwatchedtopics' => array($txt['showUnwatched'], array('profile_view_own', 'profile_view_any'), 'enabled' => $modSettings['enable_unwatch'] && $context['user']['is_owner']),
260 1
							'attach' => array($txt['showAttachments'], array('profile_view_own', 'profile_view_any')),
261
						),
262
						'permission' => array(
263
							'own' => 'profile_view_own',
264
							'any' => 'profile_view_any',
265
						),
266
					),
267
					'showlikes' => array(
268 1
						'label' => $txt['likes_show'],
269 1
						'controller' => 'Likes_Controller',
270 1
						'function' => 'action_showProfileLikes',
271 1
						'enabled' => !empty($modSettings['likes_enabled']) && $context['user']['is_owner'],
272
						'subsections' => array(
273 1
							'given' => array($txt['likes_given'], array('profile_view_own')),
274 1
							'received' => array($txt['likes_received'], array('profile_view_own')),
275
						),
276
						'permission' => array(
277
							'own' => 'profile_view_own',
278
							'any' => array(),
279
						),
280
					),
281
					'permissions' => array(
282 1
						'label' => $txt['showPermissions'],
283 1
						'controller' => 'ProfileInfo_Controller',
284 1
						'function' => 'action_showPermissions',
285
						'permission' => array(
286
							'own' => 'manage_permissions',
287
							'any' => 'manage_permissions',
288
						),
289
					),
290
					'history' => array(
291 1
						'label' => $txt['history'],
292 1
						'controller' => 'ProfileHistory_Controller',
293 1
						'function' => 'action_index',
294
						'subsections' => array(
295 1
							'activity' => array($txt['trackActivity'], 'moderate_forum'),
296 1
							'ip' => array($txt['trackIP'], 'moderate_forum'),
297 1
							'edits' => array($txt['trackEdits'], 'moderate_forum'),
298 1
							'logins' => array($txt['trackLogins'], array('profile_view_own', 'moderate_forum')),
299
						),
300
						'permission' => array(
301
							'own' => 'moderate_forum',
302
							'any' => 'moderate_forum',
303
						),
304
					),
305
					'viewwarning' => array(
306 1
						'label' => $txt['profile_view_warnings'],
307 1
						'enabled' => in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']) && $this->_profile['warning'] && (!empty($modSettings['warning_show']) && ($context['user']['is_owner'] || $modSettings['warning_show'] == 2)),
308 1
						'controller' => 'ProfileInfo_Controller',
309 1
						'function' => 'action_viewWarning',
310
						'permission' => array(
311
							'own' => 'profile_view_own',
312
							'any' => 'issue_warning',
313
						),
314
					),
315
				),
316
			),
317
			'edit_profile' => array(
318 1
				'title' => $txt['profileEdit'],
319
				'areas' => array(
320
					'account' => array(
321 1
						'label' => $txt['account'],
322 1
						'controller' => 'ProfileOptions_Controller',
323 1
						'function' => 'action_account',
324 1
						'enabled' => $context['user']['is_admin'] || ($this->_profile['id_group'] != 1 && !in_array(1, explode(',', $this->_profile['additional_groups']))),
325 1
						'sc' => 'post',
326 1
						'token' => 'profile-ac%u',
327
						'password' => true,
328
						'permission' => array(
329
							'own' => array('profile_identity_any', 'profile_identity_own', 'manage_membergroups'),
330
							'any' => array('profile_identity_any', 'manage_membergroups'),
331
						),
332
					),
333
					'forumprofile' => array(
334 1
						'label' => $txt['forumprofile'],
335 1
						'controller' => 'ProfileOptions_Controller',
336 1
						'function' => 'action_forumProfile',
337 1
						'sc' => 'post',
338 1
						'token' => 'profile-fp%u',
339
						'permission' => array(
340
							'own' => array('profile_extra_any', 'profile_extra_own', 'profile_title_own', 'profile_title_any'),
341
							'any' => array('profile_extra_any', 'profile_title_any'),
342
						),
343
					),
344
					'theme' => array(
345 1
						'label' => $txt['theme'],
346 1
						'controller' => 'ProfileOptions_Controller',
347 1
						'function' => 'action_themepick',
348 1
						'sc' => 'post',
349 1
						'token' => 'profile-th%u',
350
						'permission' => array(
351
							'own' => array('profile_extra_any', 'profile_extra_own'),
352
							'any' => array('profile_extra_any'),
353
						),
354
					),
355
					'authentication' => array(
356 1
						'label' => $txt['authentication'],
357 1
						'controller' => 'ProfileOptions_Controller',
358 1
						'function' => 'action_authentication',
359 1
						'enabled' => !empty($modSettings['enableOpenID']) || !empty($this->_profile['openid_uri']),
360 1
						'sc' => 'post',
361 1
						'token' => 'profile-au%u',
362 1
						'hidden' => empty($modSettings['enableOpenID']) && empty($this->_profile['openid_uri']),
363
						'password' => true,
364
						'permission' => array(
365
							'own' => array('profile_identity_any', 'profile_identity_own'),
366
							'any' => array('profile_identity_any'),
367
						),
368
					),
369
					'notification' => array(
370 1
						'label' => $txt['notifications'],
371 1
						'controller' => 'ProfileOptions_Controller',
372 1
						'function' => 'action_notification',
373 1
						'sc' => 'post',
374 1
						'token' => 'profile-nt%u',
375
						'permission' => array(
376
							'own' => array('profile_extra_any', 'profile_extra_own'),
377
							'any' => array('profile_extra_any'),
378
						),
379
					),
380
					// Without profile_extra_own, settings are accessible from the PM section.
381
					// @todo at some point decouple it from PMs
382
					'contactprefs' => array(
383 1
						'label' => $txt['contactprefs'],
384 1
						'controller' => 'ProfileOptions_Controller',
385 1
						'function' => 'action_pmprefs',
386 1
						'enabled' => allowedTo(array('profile_extra_own', 'profile_extra_any')),
387 1
						'sc' => 'post',
388 1
						'token' => 'profile-pm%u',
389
						'permission' => array(
390
							'own' => array('pm_read'),
391
							'any' => array('profile_extra_any'),
392
						),
393
					),
394
					'ignoreboards' => array(
395 1
						'label' => $txt['ignoreboards'],
396 1
						'controller' => 'ProfileOptions_Controller',
397 1
						'function' => 'action_ignoreboards',
398 1
						'enabled' => !empty($modSettings['allow_ignore_boards']),
399 1
						'sc' => 'post',
400 1
						'token' => 'profile-ib%u',
401
						'permission' => array(
402
							'own' => array('profile_extra_any', 'profile_extra_own'),
403
							'any' => array('profile_extra_any'),
404
						),
405
					),
406
					'lists' => array(
407 1
						'label' => $txt['editBuddyIgnoreLists'],
408 1
						'controller' => 'ProfileOptions_Controller',
409 1
						'function' => 'action_editBuddyIgnoreLists',
410 1
						'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'],
411 1
						'sc' => 'post',
412 1
						'token' => 'profile-bl%u',
413
						'subsections' => array(
414 1
							'buddies' => array($txt['editBuddies']),
415 1
							'ignore' => array($txt['editIgnoreList']),
416
						),
417
						'permission' => array(
418
							'own' => array('profile_extra_any', 'profile_extra_own'),
419
							'any' => array(),
420
						),
421
					),
422
					'groupmembership' => array(
423 1
						'label' => $txt['groupmembership'],
424 1
						'controller' => 'ProfileOptions_Controller',
425 1
						'function' => 'action_groupMembership',
426 1
						'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'],
427 1
						'sc' => 'request',
428 1
						'token' => 'profile-gm%u',
429 1
						'token_type' => 'request',
430
						'permission' => array(
431
							'own' => array('profile_view_own'),
432
							'any' => array('manage_membergroups'),
433
						),
434
					),
435
				),
436
			),
437
			'profile_action' => array(
438 1
				'title' => $txt['profileAction'],
439
				'areas' => array(
440
					'sendpm' => array(
441 1
						'label' => $txt['profileSendIm'],
442 1
						'custom_url' => getUrl('action', ['action' => 'pm', 'sa' => 'send']),
443
						'permission' => array(
444
							'own' => array(),
445
							'any' => array('pm_send'),
446
						),
447
					),
448
					'issuewarning' => array(
449 1
						'label' => $txt['profile_issue_warning'],
450 1
						'enabled' => in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']) && (!$context['user']['is_owner'] || $context['user']['is_admin']),
451 1
						'controller' => 'ProfileAccount_Controller',
452 1
						'function' => 'action_issuewarning',
453 1
						'token' => 'profile-iw%u',
454
						'permission' => array(
455
							'own' => array(),
456
							'any' => array('issue_warning'),
457
						),
458
					),
459
					'banuser' => array(
460 1
						'label' => $txt['profileBanUser'],
461 1
						'custom_url' => getUrl('admin', ['action' => 'admin', 'area' => 'ban', 'sa' => 'add']),
462 1
						'enabled' => $this->_profile['id_group'] != 1 && !in_array(1, explode(',', $this->_profile['additional_groups'])),
463
						'permission' => array(
464
							'own' => array(),
465
							'any' => array('manage_bans'),
466
						),
467
					),
468
					'subscriptions' => array(
469 1
						'label' => $txt['subscriptions'],
470 1
						'controller' => 'ProfileSubscriptions_Controller',
471 1
						'function' => 'action_subscriptions',
472 1
						'enabled' => !empty($modSettings['paid_enabled']),
473
						'permission' => array(
474
							'own' => array('profile_view_own'),
475
							'any' => array('moderate_forum'),
476
						),
477
					),
478
					'deleteaccount' => array(
479 1
						'label' => $txt['deleteAccount'],
480 1
						'controller' => 'ProfileAccount_Controller',
481 1
						'function' => 'action_deleteaccount',
482 1
						'sc' => 'post',
483 1
						'token' => 'profile-da%u',
484
						'password' => true,
485
						'permission' => array(
486
							'own' => array('profile_remove_any', 'profile_remove_own'),
487
							'any' => array('profile_remove_any'),
488
						),
489
					),
490
					'activateaccount' => array(
491
						'controller' => 'ProfileAccount_Controller',
492
						'function' => 'action_activateaccount',
493
						'sc' => 'get',
494
						'token' => 'profile-aa%u',
495
						'token_type' => 'get',
496
						'permission' => array(
497
							'own' => array(),
498
							'any' => array('moderate_forum'),
499
						),
500
					),
501
				),
502
			),
503
		);
504
505
		// Set a few options for the menu.
506
		$menuOptions = array(
507 1
			'disable_url_session_check' => true,
508 1
			'hook' => 'profile',
509
			'extra_url_parameters' => array(
510 1
				'u' => $context['id_member'],
511
			),
512
			'default_include_dir' => CONTROLLERDIR,
513
		);
514
515
		// Actually create the menu!
516 1
		$this->_profile_include_data = createMenu($profile_areas, $menuOptions);
517 1
		unset($profile_areas);
518 1
	}
519
520
	/**
521
	 * Does session and token checks for the areas that require those
522
	 */
523 1
	private function _check_access()
524
	{
525 1
		global $context;
526
527
		// Does this require session validating?
528 1
		if (!empty($this->_profile_include_data['validate'])
529 1
			|| (isset($this->_saving) && !$context['user']['is_owner']))
530
		{
531
			validateSession();
532
		}
533
534
		// Do we need to perform a token check?
535 1
		if (!empty($this->_profile_include_data['token']))
536
		{
537 1
			if ($this->_profile_include_data['token'] !== true)
538
			{
539 1
				$token_name = str_replace('%u', $context['id_member'], $this->_profile_include_data['token']);
540
			}
541
			else
542
			{
543
				$token_name = 'profile-u' . $context['id_member'];
544
			}
545
546 1
			if (isset($this->_profile_include_data['token_type']) && in_array($this->_profile_include_data['token_type'], array('request', 'post', 'get')))
547
			{
548 1
				$token_type = $this->_profile_include_data['token_type'];
549
			}
550
			else
551
			{
552
				$token_type = 'post';
553
			}
554
555 1
			if (isset($this->_saving))
556
			{
557
				validateToken($token_name, $token_type);
558
			}
559
560 1
			createToken($token_name, $token_type);
561 1
			$context['token_check'] = $token_name;
562
		}
563 1
	}
564
565
	/**
566
	 * Just builds the link tree based on where were are in the profile section
567
	 * and who's profile is being viewed, etc.
568
	 */
569 1
	private function _build_profile_linktree()
570
	{
571 1
		global $context, $txt, $user_info;
572
573 1
		$context['linktree'][] = array(
574 1
			'url' => getUrl('profile', ['action' => 'profile', 'u' => $this->_memID, 'name' => $this->_profile['real_name']]),
575 1
			'name' => sprintf($txt['profile_of_username'], $context['member']['name']),
576
		);
577
578 1
		if (!empty($this->_profile_include_data['label']))
579
		{
580 1
			$context['linktree'][] = array(
581 1
				'url' => getUrl('profile', ['action' => 'profile', 'area' => $this->_profile_include_data['current_area'], 'u' => $this->_memID, 'name' => $this->_profile['real_name']]),
582 1
				'name' => $this->_profile_include_data['label'],
583
			);
584
		}
585
586 1
		if (!empty($this->_profile_include_data['current_subsection']) && $this->_profile_include_data['subsections'][$this->_profile_include_data['current_subsection']][0] != $this->_profile_include_data['label'])
587
		{
588
			$context['linktree'][] = array(
589
				'url' => getUrl('profile', ['action' => 'profile', 'area' => $this->_profile_include_data['current_area'], 'sa' => $this->_profile_include_data['current_subsection'], 'u' => $this->_memID, 'name' => $this->_profile['real_name']]),
590
				'name' => $this->_profile_include_data['subsections'][$this->_profile_include_data['current_subsection']][0],
591
			);
592
		}
593 1
	}
594
595
	/**
596
	 * Save profile updates
597
	 */
598 1
	private function _save_updates()
599
	{
600 1
		global $txt, $user_info, $context, $modSettings, $user_settings, $post_errors, $profile_vars;
601
602
		// All the subactions that require a user password in order to validate.
603 1
		$check_password = $context['user']['is_owner'] && !empty($this->_profile_include_data['password']);
604 1
		$context['require_password'] = $check_password && empty($user_settings['openid_uri']);
605
606
		// These will get populated soon!
607 1
		$post_errors = array();
608 1
		$profile_vars = array();
609
610 1
		if ($this->_completed_save)
611
		{
612
			// Clean up the POST variables.
613
			$post = htmltrim__recursive((array) $this->_req->post);
614
			$post = htmlspecialchars__recursive($post);
615
			$this->_req->post = new ArrayObject($post, ArrayObject::ARRAY_AS_PROPS);
616
617
			// Does the change require the current password as well?
618
			$this->_check_password($check_password);
619
620
			// Change the IP address in the database.
621
			if ($context['user']['is_owner'])
622
			{
623
				$profile_vars['member_ip'] = $user_info['ip'];
624
			}
625
626
			// Now call the sub-action function...
627
			if ($this->_current_area === 'activateaccount' && empty($post_errors))
628
			{
629
				$controller = new ProfileAccount_Controller(new Event_Manager());
630
				$controller->pre_dispatch();
631
				$controller->action_activateaccount();
632
			}
633
			elseif ($this->_current_area === 'deleteaccount' && empty($post_errors))
634
			{
635
				$controller = new ProfileAccount_Controller(new Event_Manager());
636
				$controller->pre_dispatch();
637
				$controller->action_deleteaccount2();
638
639
				// Done
640
				redirectexit();
641
			}
642
			elseif ($this->_current_area === 'groupmembership' && empty($post_errors))
643
			{
644
				$controller = new ProfileOptions_Controller(new Event_Manager());
645
				$controller->pre_dispatch();
646
				$msg = $controller->action_groupMembership2();
647
648
				// Whatever we've done, we have nothing else to do here...
649
				redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $this->_memID) . ';area=groupmembership' . (!empty($msg) ? ';msg=' . $msg : ''));
650
			}
651
			// Authentication changes?
652
			elseif ($this->_current_area === 'authentication')
653
			{
654
				$controller = new ProfileOptions_Controller(new Event_Manager());
655
				$controller->pre_dispatch();
656
				$controller->action_authentication(true);
657
			}
658
			elseif (in_array($this->_current_area, array('account', 'forumprofile', 'theme', 'contactprefs')))
659
			{
660
				// @todo yes this is ugly, but saveProfileFields needs to be updated first
661
				$_POST = (array) $this->_req->post;
662
663
				require_once(CONTROLLERDIR . '/ProfileOptions.controller.php');
664
				if ($this->_current_area === 'account' && !empty($modSettings['enableOTP']))
665
				{
666
					$fields = ProfileOptions_Controller::getFields('account_otp');
667
				}
668
				else
669
				{
670
					$fields = ProfileOptions_Controller::getFields($this->_current_area);
671
				}
672
673
				saveProfileFields($fields['fields'], $fields['hook']);
674
			}
675
			elseif (empty($post_errors))
676
			{
677
				// @todo yes this is also ugly, but saveProfileChanges needs to be updated first
678
				$_POST = (array) $this->_req->post;
679
680
				$this->_force_redirect = true;
681
				saveProfileChanges($profile_vars, $this->_memID);
682
			}
683
684
			call_integration_hook('integrate_profile_save', array(&$profile_vars, &$post_errors, $this->_memID));
685
686
			// There was a problem, let them try to re-enter.
687
			if (!empty($post_errors))
688
			{
689
				// Load the language file so we can give a nice explanation of the errors.
690
				theme()->getTemplates()->loadLanguageFile('Errors');
691
				$context['post_errors'] = $post_errors;
692
			}
693
			elseif (!empty($profile_vars))
694
			{
695
				// If we've changed the password, notify any integration that may be listening in.
696
				if (isset($profile_vars['passwd']))
697
				{
698
					call_integration_hook('integrate_reset_pass', array($this->_profile['member_name'], $this->_profile['member_name'], $this->_req->post->passwrd2));
0 ignored issues
show
Bug introduced by
The property passwrd2 does not seem to exist in ArrayObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
699
				}
700
701
				require_once(SUBSDIR . '/Members.subs.php');
702
				updateMemberData($this->_memID, $profile_vars);
703
704
				// What if this is the newest member?
705
				if ($modSettings['latestMember'] == $this->_memID)
706
				{
707
					require_once(SUBSDIR . '/Members.subs.php');
708
					updateMemberStats();
709
				}
710
				elseif (isset($profile_vars['real_name']))
711
				{
712
					updateSettings(array('memberlist_updated' => time()));
713
				}
714
715
				// If the member changed his/her birth date, update calendar statistics.
716
				if (isset($profile_vars['birthdate']) || isset($profile_vars['real_name']))
717
				{
718
					updateSettings(array(
719
						'calendar_updated' => time(),
720
					));
721
				}
722
723
				// Anything worth logging?
724
				if (!empty($context['log_changes']) && !empty($modSettings['modlog_enabled']))
725
				{
726
					$log_changes = array();
727
					foreach ($context['log_changes'] as $k => $v)
0 ignored issues
show
Bug introduced by
The expression $context['log_changes'] of type boolean is not traversable.
Loading history...
728
					{
729
						$log_changes[] = array(
730
							'action' => $k,
731
							'log_type' => 'user',
732
							'extra' => array_merge($v, array(
733
								'applicator' => $user_info['id'],
734
								'member_affected' => $this->_memID,
735
							)),
736
						);
737
					}
738
739
					logActions($log_changes);
740
				}
741
742
				// Have we got any post save functions to execute?
743
				if (!empty($context['profile_execute_on_save']))
744
				{
745
					foreach ($context['profile_execute_on_save'] as $saveFunc)
0 ignored issues
show
Bug introduced by
The expression $context['profile_execute_on_save'] of type boolean is not traversable.
Loading history...
746
						$saveFunc();
747
				}
748
749
				// Let them know it worked!
750
				$context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : sprintf($txt['profile_updated_else'], $this->_profile['member_name']);
751
752
				// Invalidate any cached data.
753
				Cache::instance()->remove('member_data-profile-' . $this->_memID);
754
			}
755
		}
756 1
	}
757
758
	/**
759
	 * If a password validation before a change is needed, this is the function to do it
760
	 *
761
	 * @param bool $check_password if this profile update requires a password verification
762
	 * @throws Elk_Exception
763
	 */
764
	private function _check_password($check_password)
765
	{
766
		global $user_settings, $user_info, $post_errors, $context;
767
768
		if ($check_password)
769
		{
770
			// If we're using OpenID try to re-validate.
771
			if (!empty($user_settings['openid_uri']))
772
			{
773
				require_once(SUBSDIR . '/OpenID.subs.php');
774
				$openID = new OpenID();
775
				$openID->revalidate();
776
			}
777
			else
778
			{
779
				// You didn't even enter a password!
780
				if (trim($this->_req->post->oldpasswrd) === '')
781
				{
782
					$post_errors[] = 'no_password';
783
				}
784
785
				// Since the password got modified due to all the $_POST cleaning, lets undo it so we can get the correct password
786
				$this->_req->post->oldpasswrd = un_htmlspecialchars($this->_req->post->oldpasswrd);
787
788
				// Does the integration want to check passwords?
789
				$good_password = in_array(true, call_integration_hook('integrate_verify_password', array($this->_profile['member_name'], $this->_req->post->oldpasswrd, false)), true);
790
791
				// Start up the password checker, we have work to do
792
				require_once(SUBSDIR . '/Auth.subs.php');
793
794
				// Bad password!!!
795
				if (!$good_password && !validateLoginPassword($this->_req->post->oldpasswrd, $user_info['passwd'], $this->_profile['member_name']))
796
				{
797
					$post_errors[] = 'bad_password';
798
				}
799
800
				// Warn other elements not to jump the gun and do custom changes!
801
				if (in_array('bad_password', $post_errors))
802
				{
803
					$context['password_auth_failed'] = true;
804
				}
805
			}
806
		}
807
	}
808
}
809